Skip to content

Commit

Permalink
linux: rewrite everything to use DBus directly
Browse files Browse the repository at this point in the history
This is a big rewrite to use DBus calls directly instead of going
through go-bluetooth first.

This is a big change, but I believe it is an improvement. While the
go-bluetooth works for many cases, it's a layer in between that I
believe hurts more than it helps. Without it, we can just program
directly against the BlueZ D-Bus API. The end result is about 10% more
code.

With this rewrite, I fixed the following issues:

  * All MapToStruct warnings are gone, like in
    #193.
  * Advertisements can be restarted after they were stopped. Previously
    this resulted in a panic.
  * Looking at the source code of go-bluetooth, it appears that it
    includes devices from a different Bluetooth adapter than the one
    that's currently scanning. This is fixed with the rewrite.
  * Fix a bug in Adapter.AddService where it would only allow adding a
    single service. Multiple services can now be added.
    This was actually the motivating bug that led me down to rewrite the
    whole thing because I couldn't figure out where the bug was in
    go-bluetooth (it's many layers deep).
  * The `WriteEvent` callback in a characteristic now also gets the
    'offset' parameter which wasn't provided by go-bluetooth.

This rewrite also avoids go-bluetooth specific workarounds like
#74 and
#121.

I have tested all examples in the smoketest-linux Makefile target. They
all still work with this rewrite.
  • Loading branch information
aykevl committed Jan 3, 2024
1 parent ec80e01 commit cf80768
Show file tree
Hide file tree
Showing 7 changed files with 368 additions and 337 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func must(action string, err error) {

## Linux

Go Bluetooth support for Linux uses [BlueZ](http://www.bluez.org/) via the [D-Bus](https://en.wikipedia.org/wiki/D-Bus) interface thanks to the https://github.com/muka/go-bluetooth package. This should work with most distros that support BlueZ such as Ubuntu, Debian, Fedora, and Arch Linux, among others.
Go Bluetooth support for Linux uses [BlueZ](http://www.bluez.org/) via the [D-Bus](https://en.wikipedia.org/wiki/D-Bus) interface. This should work with most distros that support BlueZ such as Ubuntu, Debian, Fedora, and Arch Linux, among others.

Linux can be used both as a BLE Central or as a BLE Peripheral.

Expand Down
38 changes: 26 additions & 12 deletions adapter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ package bluetooth

import (
"errors"
"fmt"

"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile/adapter"
"github.com/godbus/dbus/v5"
)

const defaultAdapter = "hci0"

type Adapter struct {
adapter *adapter.Adapter1
id string
cancelChan chan struct{}
scanCancelChan chan struct{}
bus *dbus.Conn
bluez dbus.BusObject // object at /
adapter dbus.BusObject // object at /org/bluez/hciX
address string
defaultAdvertisement *Advertisement

connectHandler func(device Address, connected bool)
Expand All @@ -26,29 +31,38 @@ type Adapter struct {
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
id: defaultAdapter,
connectHandler: func(device Address, connected bool) {
return
},
}

// Enable configures the BLE stack. It must be called before any
// Bluetooth-related calls (unless otherwise indicated).
func (a *Adapter) Enable() (err error) {
if a.id == "" {
a.adapter, err = api.GetDefaultAdapter()
if err != nil {
return
bus, err := dbus.SystemBus()
if err != nil {
return err
}
a.bus = bus
a.bluez = a.bus.Object("org.bluez", dbus.ObjectPath("/"))
a.adapter = a.bus.Object("org.bluez", dbus.ObjectPath("/org/bluez/"+a.id))
addr, err := a.adapter.GetProperty("org.bluez.Adapter1.Address")
if err != nil {
if err, ok := err.(dbus.Error); ok && err.Name == "org.freedesktop.DBus.Error.UnknownObject" {
return fmt.Errorf("bluetooth: adapter %s does not exist", a.adapter.Path())
}
a.id, err = a.adapter.GetAdapterID()
return fmt.Errorf("could not activate BlueZ adapter: %w", err)
}
addr.Store(&a.address)

return nil
}

func (a *Adapter) Address() (MACAddress, error) {
if a.adapter == nil {
if a.address == "" {
return MACAddress{}, errors.New("adapter not enabled")
}
mac, err := ParseMAC(a.adapter.Properties.Address)
mac, err := ParseMAC(a.address)
if err != nil {
return MACAddress{}, err
}
Expand Down
Loading

0 comments on commit cf80768

Please sign in to comment.