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 Dec 25, 2023
1 parent 271f0d0 commit efb1820
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 0 deletions.
5 changes: 5 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 All @@ -36,6 +38,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 All @@ -45,12 +48,14 @@ smoketest-linux:
smoketest-windows:
# Test on Windows.
GOOS=windows go build -o /tmp/go-build-discard ./examples/scanner
GOOS=windows go build -o /tmp/go-build-discard ./examples/connparams
GOOS=windows go build -o /tmp/go-build-discard ./examples/discover
GOOS=windows go build -o /tmp/go-build-discard ./examples/heartrate-monitor

smoketest-macos:
# Test on macos.
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/scanner
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/connparams
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/discover
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/nusclient
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/heartrate-monitor
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()

Check failure on line 39 in examples/connparams/main.go

View workflow job for this annotation

GitHub Actions / build-windows

adapter.DefaultAdvertisement undefined (type *"tinygo.org/x/bluetooth".Adapter has no field or method DefaultAdvertisement)
adv.Configure(bluetooth.AdvertisementOptions{
LocalName: name,
})
adv.Start()
println("advertising:", name)

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

Check failure on line 47 in examples/connparams/main.go

View workflow job for this annotation

GitHub Actions / build-windows

device.Address undefined (type "tinygo.org/x/bluetooth".Device has no field or method Address)

// 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 @@ -326,6 +326,17 @@ func (d Device) Disconnect() error {
return d.device.Disconnect()
}

// 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
}

// watchForConnect watches for a signal from the bluez device interface that indicates a Connection/Disconnection.
//
// We can add extra signals to watch for here,
Expand Down
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 efb1820

Please sign in to comment.