Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection params #211

Merged
merged 5 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ smoketest-tinygo:
@md5sum test.hex
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/circuitplay
@md5sum test.hex
$(TINYGO) build -o test.hex -size=short -target=circuitplay-bluefruit ./examples/connparams
@md5sum test.hex
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/discover
@md5sum test.hex
$(TINYGO) build -o test.hex -size=short -target=pca10040-s132v6 ./examples/heartrate
Expand Down Expand Up @@ -40,6 +42,7 @@ smoketest-tinygo:
smoketest-linux:
# Test on Linux.
GOOS=linux go build -o /tmp/go-build-discard ./examples/advertisement
GOOS=linux go build -o /tmp/go-build-discard ./examples/connparams
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate-monitor
GOOS=linux go build -o /tmp/go-build-discard ./examples/nusserver
Expand Down
2 changes: 1 addition & 1 deletion adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ const debug = false
// SetConnectHandler sets a handler function to be called whenever the adaptor connects
// or disconnects. You must call this before you call adaptor.Connect() for centrals
// or adaptor.Start() for peripherals in order for it to work.
func (a *Adapter) SetConnectHandler(c func(device Address, connected bool)) {
func (a *Adapter) SetConnectHandler(c func(device Device, connected bool)) {
a.connectHandler = c
}
6 changes: 3 additions & 3 deletions adapter_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Adapter struct {
// used to allow multiple callers to call Connect concurrently.
connectMap sync.Map

connectHandler func(device Address, connected bool)
connectHandler func(device Device, connected bool)
}

// DefaultAdapter is the default adapter on the system.
Expand All @@ -35,7 +35,7 @@ var DefaultAdapter = &Adapter{
pm: cbgo.NewPeripheralManager(nil),
connectMap: sync.Map{},

connectHandler: func(device Address, connected bool) {
connectHandler: func(device Device, connected bool) {
return
},
}
Expand Down Expand Up @@ -106,7 +106,7 @@ func (cmd *centralManagerDelegate) DidDisconnectPeripheral(cmgr cbgo.CentralMana
addr := Address{}
uuid, _ := ParseUUID(id)
addr.UUID = uuid
cmd.a.connectHandler(addr, false)
cmd.a.connectHandler(Device{Address: addr}, false)

// like with DidConnectPeripheral, check if we have a chan allocated for this and send through the peripheral
// this will only be true if the receiving side is still waiting for a connection to complete
Expand Down
4 changes: 2 additions & 2 deletions adapter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type Adapter struct {
address string
defaultAdvertisement *Advertisement

connectHandler func(device Address, connected bool)
connectHandler func(device Device, connected bool)
}

// DefaultAdapter is the default adapter on the system. On Linux, it is the
Expand All @@ -32,7 +32,7 @@ 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) {
connectHandler: func(device Device, connected bool) {
},
}

Expand Down
14 changes: 7 additions & 7 deletions adapter_ninafw.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ type Adapter struct {
isDefault bool
scanning bool

connectHandler func(device Address, connected bool)
connectHandler func(device Device, connected bool)

connectedDevices []*Device
connectedDevices []Device
notificationsStarted bool
}

Expand All @@ -30,10 +30,10 @@ type Adapter struct {
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
isDefault: true,
connectHandler: func(device Address, connected bool) {
connectHandler: func(device Device, connected bool) {
return
},
connectedDevices: make([]*Device, 0, maxConnections),
connectedDevices: make([]Device, 0, maxConnections),
}

// Enable configures the BLE stack. It must be called before any
Expand Down Expand Up @@ -170,7 +170,7 @@ func (a *Adapter) startNotifications() {
}

d := a.findDevice(not.connectionHandle)
if d == nil {
if d.deviceInternal == nil {
if _debug {
println("no device found for handle", not.connectionHandle)
}
Expand All @@ -197,7 +197,7 @@ func (a *Adapter) startNotifications() {
}()
}

func (a *Adapter) findDevice(handle uint16) *Device {
func (a *Adapter) findDevice(handle uint16) Device {
for _, d := range a.connectedDevices {
if d.handle == handle {
if _debug {
Expand All @@ -208,5 +208,5 @@ func (a *Adapter) findDevice(handle uint16) *Device {
}
}

return nil
return Device{}
}
20 changes: 18 additions & 2 deletions adapter_nrf51.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ func handleEvent() {
switch id {
case C.BLE_GAP_EVT_CONNECTED:
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
DefaultAdapter.connectHandler(Address{}, true)
connectEvent := gapEvent.params.unionfield_connected()
device := Device{
Address: Address{makeMACAddress(connectEvent.peer_addr)},
connectionHandle: gapEvent.conn_handle,
}
DefaultAdapter.connectHandler(device, true)
case C.BLE_GAP_EVT_DISCONNECTED:
if defaultAdvertisement.isAdvertising.Get() != 0 {
// The advertisement was running but was automatically stopped
Expand All @@ -55,7 +60,10 @@ func handleEvent() {
defaultAdvertisement.start()
}
currentConnection.handle.Reg = C.BLE_CONN_HANDLE_INVALID
DefaultAdapter.connectHandler(Address{}, false)
device := Device{
connectionHandle: gapEvent.conn_handle,
}
DefaultAdapter.connectHandler(device, false)
case C.BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
// Respond with the default PPCP connection parameters by passing
// nil:
Expand Down Expand Up @@ -111,3 +119,11 @@ func (a *Adapter) Address() (MACAddress, error) {
}
return MACAddress{MAC: makeAddress(addr.addr)}, nil
}

// Convert a C.ble_gap_addr_t to a MACAddress struct.
func makeMACAddress(addr C.ble_gap_addr_t) MACAddress {
return MACAddress{
MAC: makeAddress(addr.addr),
isRandom: addr.addr_type != 0,
}
}
24 changes: 19 additions & 5 deletions adapter_nrf528xx-full.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,24 @@ func handleEvent() {
switch id {
case C.BLE_GAP_EVT_CONNECTED:
connectEvent := gapEvent.params.unionfield_connected()
device := Device{
Address: Address{makeMACAddress(connectEvent.peer_addr)},
connectionHandle: gapEvent.conn_handle,
}
switch connectEvent.role {
case C.BLE_GAP_ROLE_PERIPH:
if debug {
println("evt: connected in peripheral role")
}
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
DefaultAdapter.connectHandler(Address{}, true)
DefaultAdapter.connectHandler(device, true)
case C.BLE_GAP_ROLE_CENTRAL:
if debug {
println("evt: connected in central role")
}
connectionAttempt.connectionHandle = gapEvent.conn_handle
connectionAttempt.state.Set(2) // connection was successful
DefaultAdapter.connectHandler(Address{}, true)
DefaultAdapter.connectHandler(device, true)
}
case C.BLE_GAP_EVT_DISCONNECTED:
if debug {
Expand All @@ -61,7 +65,18 @@ func handleEvent() {
// necessary.
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
}
DefaultAdapter.connectHandler(Address{}, false)
device := Device{
connectionHandle: gapEvent.conn_handle,
}
DefaultAdapter.connectHandler(device, false)
case C.BLE_GAP_EVT_CONN_PARAM_UPDATE:
if debug {
// Print connection parameters for easy debugging.
params := gapEvent.params.unionfield_conn_param_update().conn_params
interval_ms := params.min_conn_interval * 125 / 100 // min and max are the same here
print("conn param update interval=", interval_ms, "ms latency=", params.slave_latency, " timeout=", params.conn_sup_timeout*10, "ms")
println()
}
case C.BLE_GAP_EVT_ADV_REPORT:
advReport := gapEvent.params.unionfield_adv_report()
if debug && &scanReportBuffer.data[0] != (*byte)(unsafe.Pointer(advReport.data.p_data)) {
Expand All @@ -73,8 +88,7 @@ func handleEvent() {
scanReportBuffer.len = byte(advReport.data.len)
globalScanResult.RSSI = int16(advReport.rssi)
globalScanResult.Address = Address{
MACAddress{MAC: makeAddress(advReport.peer_addr.addr),
isRandom: advReport.peer_addr.bitfield_addr_type() != 0},
makeMACAddress(advReport.peer_addr),
}
globalScanResult.AdvertisementPayload = &scanReportBuffer
// Signal to the main thread that there was a scan report.
Expand Down
12 changes: 10 additions & 2 deletions adapter_nrf528xx-peripheral.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ func handleEvent() {
println("evt: connected in peripheral role")
}
currentConnection.handle.Reg = uint16(gapEvent.conn_handle)
DefaultAdapter.connectHandler(Address{}, true)
connectEvent := gapEvent.params.unionfield_connected()
device := Device{
Address: Address{makeMACAddress(connectEvent.peer_addr)},
connectionHandle: gapEvent.conn_handle,
}
DefaultAdapter.connectHandler(device, true)
case C.BLE_GAP_EVT_DISCONNECTED:
if debug {
println("evt: disconnected")
Expand All @@ -44,7 +49,10 @@ func handleEvent() {
// necessary.
C.sd_ble_gap_adv_start(defaultAdvertisement.handle, C.BLE_CONN_CFG_TAG_DEFAULT)
}
DefaultAdapter.connectHandler(Address{}, false)
device := Device{
connectionHandle: gapEvent.conn_handle,
}
DefaultAdapter.connectHandler(device, false)
case C.BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST:
// We need to respond with sd_ble_gap_data_length_update. Setting
// both parameters to nil will make sure we send the default values.
Expand Down
8 changes: 8 additions & 0 deletions adapter_nrf528xx.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@ func (a *Adapter) Address() (MACAddress, error) {
}
return MACAddress{MAC: makeAddress(addr.addr)}, nil
}

// Convert a C.ble_gap_addr_t to a MACAddress struct.
func makeMACAddress(addr C.ble_gap_addr_t) MACAddress {
return MACAddress{
MAC: makeAddress(addr.addr),
isRandom: addr.bitfield_addr_type() != 0,
}
}
4 changes: 2 additions & 2 deletions adapter_sd.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ type Adapter struct {
scanning bool
charWriteHandlers []charWriteHandler

connectHandler func(device Address, connected bool)
connectHandler func(device Device, connected bool)
}

// DefaultAdapter is the default adapter on the current system. On Nordic chips,
// it will return the SoftDevice interface.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{isDefault: true,
connectHandler: func(device Address, connected bool) {
connectHandler: func(device Device, connected bool) {
return
}}

Expand Down
4 changes: 2 additions & 2 deletions adapter_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import (
type Adapter struct {
watcher *advertisement.BluetoothLEAdvertisementWatcher

connectHandler func(device Address, connected bool)
connectHandler func(device Device, connected bool)
}

// DefaultAdapter is the default adapter on the system.
//
// Make sure to call Enable() before using it to initialize the adapter.
var DefaultAdapter = &Adapter{
connectHandler: func(device Address, connected bool) {
connectHandler: func(device Device, connected bool) {
return
},
}
Expand Down
2 changes: 1 addition & 1 deletion examples/circuitplay/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func main() {
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
ws = ws2812.New(neo)

adapter.SetConnectHandler(func(d bluetooth.Address, c bool) {
adapter.SetConnectHandler(func(d bluetooth.Device, c bool) {
connected = c

if !connected && !disconnected {
Expand Down
84 changes: 84 additions & 0 deletions examples/connparams/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Test for setting connection parameters.
//
// To test this feature, run this either on a desktop OS or by flashing it to a
// device with TinyGo. Then connect to it from a BLE connection debugger, for
// example nRF Connect on Android. After a second, you should see in the log of
// the BLE app that the connection latency has been updated. It might look
// something like this:
//
// Connection parameters updated (interval: 510.0ms, latency: 0, timeout: 10000ms)
package main

import (
"time"

"tinygo.org/x/bluetooth"
)

var (
adapter = bluetooth.DefaultAdapter
newDevice chan bluetooth.Device
)

func main() {
must("enable BLE stack", adapter.Enable())

newDevice = make(chan bluetooth.Device, 1)
adapter.SetConnectHandler(func(device bluetooth.Device, connected bool) {
// If this is a new device, signal it to the separate goroutine.
if connected {
select {
case newDevice <- device:
default:
}
}
})

// Start advertising, so we can be found.
const name = "Go BLE test"
adv := adapter.DefaultAdvertisement()
adv.Configure(bluetooth.AdvertisementOptions{
LocalName: name,
})
adv.Start()
println("advertising:", name)

for device := range newDevice {
println("connection from device:", device.Address.String())

// Discover services and characteristics.
svcs, err := device.DiscoverServices(nil)
if err != nil {
println(" failed to resolve services:", err)
}
for _, svc := range svcs {
println(" service:", svc.UUID().String())
chars, err := svc.DiscoverCharacteristics(nil)
if err != nil {
println(" failed to resolve characteristics:", err)
}
for _, char := range chars {
println(" characteristic:", char.UUID().String())
}
}

// Update connection parameters (as a test).
time.Sleep(time.Second)
err = device.RequestConnectionParams(bluetooth.ConnectionParams{
MinInterval: bluetooth.NewDuration(495 * time.Millisecond),
MaxInterval: bluetooth.NewDuration(510 * time.Millisecond),
Timeout: bluetooth.NewDuration(10 * time.Second),
})
if err != nil {
println(" failed to update connection parameters:", err)
continue
}
println(" updated connection parameters")
}
}

func must(action string, err error) {
if err != nil {
panic("failed to " + action + ": " + err.Error())
}
}
2 changes: 1 addition & 1 deletion examples/discover/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func main() {
}
})

var device *bluetooth.Device
var device bluetooth.Device
select {
case result := <-ch:
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
Expand Down
2 changes: 1 addition & 1 deletion examples/heartrate-monitor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func main() {
}
})

var device *bluetooth.Device
var device bluetooth.Device
select {
case result := <-ch:
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
Expand Down
Loading
Loading