Skip to content

Commit

Permalink
all: add SetConnectionLatency to update connection parameters
Browse files Browse the repository at this point in the history
This allows changing the connection latency, slave latency, and
connection timeout of an active connection - whether in the central or
peripheral role. This is especially helpful on battery operated BLE
devices that don't have a lot of power and need to lower the connection
latency for improved speed. It might also be useful for devices that
need high speed, as the defaults might be too low.
  • Loading branch information
aykevl committed Jan 4, 2024
1 parent 1682161 commit 5c15b8e
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 0 deletions.
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
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.SetConnectionLatency(bluetooth.ConnectionLatency{
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())
}
}
13 changes: 13 additions & 0 deletions gap.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,16 @@ type ConnectionParams struct {
MinInterval Duration
MaxInterval Duration
}

// Connection latency and timeout for a connection to a peripheral or central.
type ConnectionLatency struct {
// Connection Supervision Timeout. After this time has passed with no
// communication, the connection is considered lost. If no timeout is
// specified, the timeout will be unchanged.
Timeout Duration

// Minimum and maximum connection interval to be used for this connection.
// Fields that are unset will be unchanged.
MinInterval Duration
MaxInterval Duration
}
11 changes: 11 additions & 0 deletions gap_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,14 @@ func (d Device) Disconnect() error {
// property change in `watchForConnect` and cancel things then
return d.device.Call("org.bluez.Device1.Disconnect", 0).Err
}

// SetConnectionLatency requests a different connection latency and timeout of
// the given device connection. Fields that are unset will be left alone.
// Whether or not the device will actually honor this, depends on the device and
// on the specific parameters.
//
// On Linux, this call doesn't do anything because BlueZ doesn't support
// changing the connection latency.
func (d Device) SetConnectionLatency(latency ConnectionLatency) error {
return nil
}
31 changes: 31 additions & 0 deletions gap_nrf528xx-central.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,34 @@ func (d Device) Disconnect() error {

return nil
}

// SetConnectionLatency requests a different connection latency and timeout of
// the given device connection. Fields that are unset will be left alone.
// Whether or not the device will actually honor this, depends on the device and
// on the specific parameters.
//
// On the Nordic SoftDevice, this call will also set the slave latency to 0.
func (d Device) SetConnectionLatency(latency ConnectionLatency) error {
// The default parameters if no specific parameters are picked.
connParams := C.ble_gap_conn_params_t{
min_conn_interval: C.BLE_GAP_CP_MIN_CONN_INTVL_NONE,
max_conn_interval: C.BLE_GAP_CP_MAX_CONN_INTVL_NONE,
slave_latency: 0,
conn_sup_timeout: C.BLE_GAP_CP_CONN_SUP_TIMEOUT_NONE,
}

// Use specified parameters if available.
if latency.MinInterval != 0 {
connParams.min_conn_interval = C.uint16_t(latency.MinInterval) / 2
}
if latency.MaxInterval != 0 {
connParams.max_conn_interval = C.uint16_t(latency.MaxInterval) / 2
}
if latency.Timeout != 0 {
connParams.conn_sup_timeout = C.uint16_t(latency.Timeout) / 16
}

// Send them to peer device.
errCode := C.sd_ble_gap_conn_param_update(d.connectionHandle, &connParams)
return makeError(errCode)
}
12 changes: 12 additions & 0 deletions gap_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,15 @@ func (d Device) Disconnect() error {

return nil
}

// SetConnectionLatency requests a different connection latency and timeout of
// the given device connection. Fields that are unset will be left alone.
// Whether or not the device will actually honor this, depends on the device and
// on the specific parameters.
//
// On Windows, this call doesn't do anything.
func (d Device) SetConnectionLatency(latency ConnectionLatency) error {
// TODO: implement this using
// BluetoothLEDevice.RequestPreferredConnectionParameters.
return nil
}

0 comments on commit 5c15b8e

Please sign in to comment.