all: add RequestConnectionParams to request new connection parameters
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.
This commit is contained in:
parent
5d805a929c
commit
d74f6a1009
8 changed files with 170 additions and 1 deletions
3
Makefile
3
Makefile
|
@ -11,6 +11,8 @@ smoketest-tinygo:
|
||||||
@md5sum test.hex
|
@md5sum test.hex
|
||||||
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/circuitplay
|
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/circuitplay
|
||||||
@md5sum test.hex
|
@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
|
$(TINYGO) build -o test.uf2 -size=short -target=circuitplay-bluefruit ./examples/discover
|
||||||
@md5sum test.hex
|
@md5sum test.hex
|
||||||
$(TINYGO) build -o test.hex -size=short -target=pca10040-s132v6 ./examples/heartrate
|
$(TINYGO) build -o test.hex -size=short -target=pca10040-s132v6 ./examples/heartrate
|
||||||
|
@ -42,6 +44,7 @@ smoketest-tinygo:
|
||||||
smoketest-linux:
|
smoketest-linux:
|
||||||
# Test on 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/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
|
||||||
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate-monitor
|
GOOS=linux go build -o /tmp/go-build-discard ./examples/heartrate-monitor
|
||||||
GOOS=linux go build -o /tmp/go-build-discard ./examples/nusserver
|
GOOS=linux go build -o /tmp/go-build-discard ./examples/nusserver
|
||||||
|
|
84
examples/connparams/main.go
Normal file
84
examples/connparams/main.go
Normal file
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
8
gap.go
8
gap.go
|
@ -391,7 +391,8 @@ func (buf *rawAdvertisementPayload) addServiceUUID(uuid UUID) (ok bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectionParams are used when connecting to a peripherals.
|
// ConnectionParams are used when connecting to a peripherals or when changing
|
||||||
|
// the parameters of an active connection.
|
||||||
type ConnectionParams struct {
|
type ConnectionParams struct {
|
||||||
// The timeout for the connection attempt. Not used during the rest of the
|
// The timeout for the connection attempt. Not used during the rest of the
|
||||||
// connection. If no duration is specified, a default timeout will be used.
|
// connection. If no duration is specified, a default timeout will be used.
|
||||||
|
@ -403,4 +404,9 @@ type ConnectionParams struct {
|
||||||
// will be used.
|
// will be used.
|
||||||
MinInterval Duration
|
MinInterval Duration
|
||||||
MaxInterval Duration
|
MaxInterval Duration
|
||||||
|
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,6 +176,18 @@ func (d Device) Disconnect() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestConnectionParams 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.
|
||||||
|
//
|
||||||
|
// This call has not yet been implemented on macOS.
|
||||||
|
func (d Device) RequestConnectionParams(params ConnectionParams) error {
|
||||||
|
// TODO: implement this using setDesiredConnectionLatency, see:
|
||||||
|
// https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393277-setdesiredconnectionlatency
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Peripheral delegate functions
|
// Peripheral delegate functions
|
||||||
|
|
||||||
type peripheralDelegate struct {
|
type peripheralDelegate struct {
|
||||||
|
|
11
gap_linux.go
11
gap_linux.go
|
@ -366,3 +366,14 @@ func (d Device) Disconnect() error {
|
||||||
// property change in `watchForConnect` and cancel things then
|
// property change in `watchForConnect` and cancel things then
|
||||||
return d.device.Call("org.bluez.Device1.Disconnect", 0).Err
|
return d.device.Call("org.bluez.Device1.Disconnect", 0).Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestConnectionParams 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) RequestConnectionParams(params ConnectionParams) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -230,6 +230,16 @@ func (d Device) Disconnect() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestConnectionParams 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 NINA, this call hasn't been implemented yet.
|
||||||
|
func (d Device) RequestConnectionParams(params ConnectionParams) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d Device) findNotificationRegistration(handle uint16) *notificationRegistration {
|
func (d Device) findNotificationRegistration(handle uint16) *notificationRegistration {
|
||||||
for _, n := range d.notificationRegistrations {
|
for _, n := range d.notificationRegistrations {
|
||||||
if n.handle == handle {
|
if n.handle == handle {
|
||||||
|
|
|
@ -188,3 +188,34 @@ func (d Device) Disconnect() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestConnectionParams 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) RequestConnectionParams(params ConnectionParams) 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 params.MinInterval != 0 {
|
||||||
|
connParams.min_conn_interval = C.uint16_t(params.MinInterval) / 2
|
||||||
|
}
|
||||||
|
if params.MaxInterval != 0 {
|
||||||
|
connParams.max_conn_interval = C.uint16_t(params.MaxInterval) / 2
|
||||||
|
}
|
||||||
|
if params.Timeout != 0 {
|
||||||
|
connParams.conn_sup_timeout = C.uint16_t(params.Timeout) / 16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send them to peer device.
|
||||||
|
errCode := C.sd_ble_gap_conn_param_update(d.connectionHandle, &connParams)
|
||||||
|
return makeError(errCode)
|
||||||
|
}
|
||||||
|
|
|
@ -247,3 +247,15 @@ func (d Device) Disconnect() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestConnectionParams 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) RequestConnectionParams(params ConnectionParams) error {
|
||||||
|
// TODO: implement this using
|
||||||
|
// BluetoothLEDevice.RequestPreferredConnectionParameters.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue