gattc: use GetUUID() to allow for bare metal use of short UUID. (#14)
* gattc: use UUID() to allow for bare metal to permit clean use of short UUIDs Signed-off-by: deadprogram <ron@hybridgroup.com> * gattc/macos: correct usage of UUID wrapper type alias Signed-off-by: Ron Evans <ron@hybridgroup.com> * gattc/sd: correct usage of UUID wrapper type alias Signed-off-by: Ron Evans <ron@hybridgroup.com> * gattc/sd, uuid/sd: changes intended to reduce memory allocations for service and characteristic discovery Signed-off-by: deadprogram <ron@hybridgroup.com> * gattc/sd: partial improvements to DiscoverServices/DiscoverCharacteristics Signed-off-by: deadprogram <ron@hybridgroup.com> * gattc/sd: mostly getting uuid back for services in DiscoverServices Signed-off-by: deadprogram <ron@hybridgroup.com> * uuid/sd: correct way to calculate UUID from shortUUID Signed-off-by: deadprogram <ron@hybridgroup.com> * gattc/sd: able to discover services and characteristics Signed-off-by: deadprogram <ron@hybridgroup.com> * examples: updated discover example that can run with OS or bare metal Signed-off-by: deadprogram <ron@hybridgroup.com> * gattc/sd: ensure safe casts for length of returned struct when converting short UUID Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
parent
6dc1dff711
commit
ef90e5d337
9 changed files with 301 additions and 104 deletions
2
Makefile
2
Makefile
|
@ -33,6 +33,7 @@ smoketest-linux:
|
||||||
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/nusserver
|
GOOS=linux go build -o /tmp/go-build-discard ./examples/nusserver
|
||||||
GOOS=linux go build -o /tmp/go-build-discard ./examples/scanner
|
GOOS=linux go build -o /tmp/go-build-discard ./examples/scanner
|
||||||
|
GOOS=linux go build -o /tmp/go-build-discard ./examples/discover
|
||||||
|
|
||||||
smoketest-windows:
|
smoketest-windows:
|
||||||
# Test on Windows.
|
# Test on Windows.
|
||||||
|
@ -41,3 +42,4 @@ smoketest-windows:
|
||||||
smoketest-macos:
|
smoketest-macos:
|
||||||
# Test on 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/scanner
|
||||||
|
GOOS=darwin CGO_ENABLED=1 go build -o /tmp/go-build-discard ./examples/discover
|
||||||
|
|
|
@ -177,6 +177,7 @@ func handleEvent() {
|
||||||
// one discovered service. Use the first as a sensible fallback.
|
// one discovered service. Use the first as a sensible fallback.
|
||||||
discoveringService.startHandle.Set(discoveryEvent.services[0].handle_range.start_handle)
|
discoveringService.startHandle.Set(discoveryEvent.services[0].handle_range.start_handle)
|
||||||
discoveringService.endHandle.Set(discoveryEvent.services[0].handle_range.end_handle)
|
discoveringService.endHandle.Set(discoveryEvent.services[0].handle_range.end_handle)
|
||||||
|
discoveringService.uuid = discoveryEvent.services[0].uuid
|
||||||
} else {
|
} else {
|
||||||
// No service found.
|
// No service found.
|
||||||
discoveringService.startHandle.Set(0)
|
discoveringService.startHandle.Set(0)
|
||||||
|
@ -192,6 +193,9 @@ func handleEvent() {
|
||||||
discoveringCharacteristic.handle_value.Set(discoveryEvent.chars[0].handle_value)
|
discoveringCharacteristic.handle_value.Set(discoveryEvent.chars[0].handle_value)
|
||||||
discoveringCharacteristic.char_props = discoveryEvent.chars[0].char_props
|
discoveringCharacteristic.char_props = discoveryEvent.chars[0].char_props
|
||||||
discoveringCharacteristic.uuid = discoveryEvent.chars[0].uuid
|
discoveringCharacteristic.uuid = discoveryEvent.chars[0].uuid
|
||||||
|
} else {
|
||||||
|
// zero indicates we received no characteristic, set handle_value to last
|
||||||
|
discoveringCharacteristic.handle_value.Set(0xffff)
|
||||||
}
|
}
|
||||||
case C.BLE_GATTC_EVT_DESC_DISC_RSP:
|
case C.BLE_GATTC_EVT_DESC_DISC_RSP:
|
||||||
discoveryEvent := gattcEvent.params.unionfield_desc_disc_rsp()
|
discoveryEvent := gattcEvent.params.unionfield_desc_disc_rsp()
|
||||||
|
|
|
@ -1,7 +1,23 @@
|
||||||
|
// This example scans and then connects to a specific Bluetooth peripheral
|
||||||
|
// and then displays all of the services and characteristics.
|
||||||
|
//
|
||||||
|
// To run this on a desktop system:
|
||||||
|
//
|
||||||
|
// go run ./examples/discover EE:74:7D:C9:2A:68
|
||||||
|
//
|
||||||
|
// To run this on a microcontroller, change the constant value in the file
|
||||||
|
// "mcu.go" to set the MAC address of the device you want to discover.
|
||||||
|
// Then, flash to the microcontroller board like this:
|
||||||
|
//
|
||||||
|
// tinygo flash -o circuitplay-bluefruit ./examples/discover
|
||||||
|
//
|
||||||
|
// Once the program is flashed to the board, connect to the USB port
|
||||||
|
// via serial to view the output.
|
||||||
|
//
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"time"
|
||||||
|
|
||||||
"tinygo.org/x/bluetooth"
|
"tinygo.org/x/bluetooth"
|
||||||
)
|
)
|
||||||
|
@ -9,13 +25,9 @@ import (
|
||||||
var adapter = bluetooth.DefaultAdapter
|
var adapter = bluetooth.DefaultAdapter
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
time.Sleep(3 * time.Second)
|
||||||
println("usage: discover [local name]")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for device with specific name
|
println("enabling")
|
||||||
name := os.Args[1]
|
|
||||||
|
|
||||||
// Enable BLE interface.
|
// Enable BLE interface.
|
||||||
must("enable BLE stack", adapter.Enable())
|
must("enable BLE stack", adapter.Enable())
|
||||||
|
@ -26,7 +38,7 @@ func main() {
|
||||||
println("scanning...")
|
println("scanning...")
|
||||||
err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {
|
err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) {
|
||||||
println("found device:", result.Address.String(), result.RSSI, result.LocalName())
|
println("found device:", result.Address.String(), result.RSSI, result.LocalName())
|
||||||
if result.LocalName() == name {
|
if result.Address.String() == connectAddress() {
|
||||||
adapter.StopScan()
|
adapter.StopScan()
|
||||||
ch <- result
|
ch <- result
|
||||||
}
|
}
|
||||||
|
@ -38,25 +50,30 @@ func main() {
|
||||||
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
|
device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
os.Exit(1)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
println("connected to ", result.LocalName())
|
println("connected to ", result.Address.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// get services
|
// get services
|
||||||
println("discovering services/characteristics")
|
println("discovering services/characteristics")
|
||||||
srvcs, err := device.DiscoverServices(nil)
|
srvcs, err := device.DiscoverServices(nil)
|
||||||
for _, srvc := range srvcs {
|
must("discover services", err)
|
||||||
println("- service", srvc.UUID.String())
|
|
||||||
|
|
||||||
chars, _ := srvc.DiscoverCharacteristics(nil)
|
for _, srvc := range srvcs {
|
||||||
|
println("- service", srvc.UUID().String())
|
||||||
|
|
||||||
|
chars, err := srvc.DiscoverCharacteristics(nil)
|
||||||
|
if err != nil {
|
||||||
|
println(err)
|
||||||
|
}
|
||||||
for _, char := range chars {
|
for _, char := range chars {
|
||||||
println("-- characteristic", char.UUID.String())
|
println("-- characteristic", char.UUID().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
must("start scan", err)
|
done()
|
||||||
}
|
}
|
||||||
|
|
||||||
func must(action string, err error) {
|
func must(action string, err error) {
|
||||||
|
|
21
examples/discover/mcu.go
Normal file
21
examples/discover/mcu.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// +build baremetal
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// replace this with the MAC address of the Bluetooth peripheral you want to connect to.
|
||||||
|
const deviceAddress = "E4:B7:F4:11:8D:33"
|
||||||
|
|
||||||
|
func connectAddress() string {
|
||||||
|
return deviceAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// done just blocks forever, allows USB CDC reset for flashing new software.
|
||||||
|
func done() {
|
||||||
|
println("Done.")
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Hour)
|
||||||
|
}
|
22
examples/discover/os.go
Normal file
22
examples/discover/os.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// +build !baremetal
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func connectAddress() string {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
println("usage: discover [address]")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for device with specific name
|
||||||
|
address := os.Args[1]
|
||||||
|
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
// done just prints a message and allows program to exit.
|
||||||
|
func done() {
|
||||||
|
println("Done.")
|
||||||
|
}
|
|
@ -33,12 +33,12 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
for _, dsvc := range d.prph.Services() {
|
for _, dsvc := range d.prph.Services() {
|
||||||
uuid, _ := ParseUUID(dsvc.UUID().String())
|
uuid, _ := ParseUUID(dsvc.UUID().String())
|
||||||
svc := DeviceService{
|
svc := DeviceService{
|
||||||
UUID: uuid,
|
uuidWrapper: uuid,
|
||||||
device: d,
|
device: d,
|
||||||
service: dsvc,
|
service: dsvc,
|
||||||
}
|
}
|
||||||
svcs = append(svcs, svc)
|
svcs = append(svcs, svc)
|
||||||
d.services[svc.UUID] = &svc
|
d.services[svc.uuidWrapper] = &svc
|
||||||
}
|
}
|
||||||
return svcs, nil
|
return svcs, nil
|
||||||
case <-time.NewTimer(10 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
|
@ -46,15 +46,24 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// uuidWrapper is a type alias for UUID so we ensure no conflicts with
|
||||||
|
// struct method of the same name.
|
||||||
|
type uuidWrapper = UUID
|
||||||
|
|
||||||
// DeviceService is a BLE service on a connected peripheral device.
|
// DeviceService is a BLE service on a connected peripheral device.
|
||||||
type DeviceService struct {
|
type DeviceService struct {
|
||||||
UUID
|
uuidWrapper
|
||||||
|
|
||||||
device *Device
|
device *Device
|
||||||
|
|
||||||
service cbgo.Service
|
service cbgo.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceService.
|
||||||
|
func (s *DeviceService) UUID() UUID {
|
||||||
|
return s.uuidWrapper
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoverCharacteristics discovers characteristics in this service. Pass a
|
// DiscoverCharacteristics discovers characteristics in this service. Pass a
|
||||||
// list of characteristic UUIDs you are interested in to this function. Either a
|
// list of characteristic UUIDs you are interested in to this function. Either a
|
||||||
// list of all requested services is returned, or if some services could not be
|
// list of all requested services is returned, or if some services could not be
|
||||||
|
@ -83,12 +92,12 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
||||||
for _, dchar := range s.service.Characteristics() {
|
for _, dchar := range s.service.Characteristics() {
|
||||||
uuid, _ := ParseUUID(dchar.UUID().String())
|
uuid, _ := ParseUUID(dchar.UUID().String())
|
||||||
char := DeviceCharacteristic{
|
char := DeviceCharacteristic{
|
||||||
UUID: uuid,
|
uuidWrapper: uuid,
|
||||||
service: s,
|
service: s,
|
||||||
characteristic: dchar,
|
characteristic: dchar,
|
||||||
}
|
}
|
||||||
chars = append(chars, char)
|
chars = append(chars, char)
|
||||||
s.device.characteristics[char.UUID] = &char
|
s.device.characteristics[char.uuidWrapper] = &char
|
||||||
}
|
}
|
||||||
return chars, nil
|
return chars, nil
|
||||||
case <-time.NewTimer(10 * time.Second).C:
|
case <-time.NewTimer(10 * time.Second).C:
|
||||||
|
@ -99,7 +108,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
||||||
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
||||||
// device.
|
// device.
|
||||||
type DeviceCharacteristic struct {
|
type DeviceCharacteristic struct {
|
||||||
UUID
|
uuidWrapper
|
||||||
|
|
||||||
service *DeviceService
|
service *DeviceService
|
||||||
|
|
||||||
|
@ -107,6 +116,11 @@ type DeviceCharacteristic struct {
|
||||||
callback func(buf []byte)
|
callback func(buf []byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceCharacteristic.
|
||||||
|
func (c *DeviceCharacteristic) UUID() UUID {
|
||||||
|
return c.uuidWrapper
|
||||||
|
}
|
||||||
|
|
||||||
// WriteWithoutResponse replaces the characteristic value with a new value. The
|
// WriteWithoutResponse replaces the characteristic value with a new value. The
|
||||||
// call will return before all data has been written. A limited number of such
|
// call will return before all data has been written. A limited number of such
|
||||||
// writes can be in flight at any given time. This call is also known as a
|
// writes can be in flight at any given time. This call is also known as a
|
||||||
|
|
|
@ -11,13 +11,22 @@ import (
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/gatt"
|
"github.com/muka/go-bluetooth/bluez/profile/gatt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// UUIDWrapper is a type alias for UUID so we ensure no conflicts with
|
||||||
|
// struct method of the same name.
|
||||||
|
type uuidWrapper = UUID
|
||||||
|
|
||||||
// DeviceService is a BLE service on a connected peripheral device.
|
// DeviceService is a BLE service on a connected peripheral device.
|
||||||
type DeviceService struct {
|
type DeviceService struct {
|
||||||
UUID
|
uuidWrapper
|
||||||
|
|
||||||
service *gatt.GattService1
|
service *gatt.GattService1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceService.
|
||||||
|
func (s *DeviceService) UUID() UUID {
|
||||||
|
return s.uuidWrapper
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoverServices starts a service discovery procedure. Pass a list of service
|
// DiscoverServices starts a service discovery procedure. Pass a list of service
|
||||||
// UUIDs you are interested in to this function. Either a slice of all services
|
// UUIDs you are interested in to this function. Either a slice of all services
|
||||||
// is returned (of the same length as the requested UUIDs and in the same
|
// is returned (of the same length as the requested UUIDs and in the same
|
||||||
|
@ -89,7 +98,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid, _ := ParseUUID(service.Properties.UUID)
|
uuid, _ := ParseUUID(service.Properties.UUID)
|
||||||
ds := DeviceService{UUID: uuid,
|
ds := DeviceService{uuidWrapper: uuid,
|
||||||
service: service,
|
service: service,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +117,16 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
||||||
// device.
|
// device.
|
||||||
type DeviceCharacteristic struct {
|
type DeviceCharacteristic struct {
|
||||||
UUID
|
uuidWrapper
|
||||||
|
|
||||||
characteristic *gatt.GattCharacteristic1
|
characteristic *gatt.GattCharacteristic1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceCharacteristic.
|
||||||
|
func (c *DeviceCharacteristic) UUID() UUID {
|
||||||
|
return c.uuidWrapper
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoverCharacteristics discovers characteristics in this service. Pass a
|
// DiscoverCharacteristics discovers characteristics in this service. Pass a
|
||||||
// list of characteristic UUIDs you are interested in to this function. Either a
|
// list of characteristic UUIDs you are interested in to this function. Either a
|
||||||
// list of all requested services is returned, or if some services could not be
|
// list of all requested services is returned, or if some services could not be
|
||||||
|
@ -171,7 +185,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid, _ := ParseUUID(char.Properties.UUID)
|
uuid, _ := ParseUUID(char.Properties.UUID)
|
||||||
dc := DeviceCharacteristic{UUID: uuid,
|
dc := DeviceCharacteristic{uuidWrapper: uuid,
|
||||||
characteristic: char,
|
characteristic: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
234
gattc_sd.go
234
gattc_sd.go
|
@ -17,6 +17,11 @@ import (
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxDefaultServicesToDiscover = 6
|
||||||
|
maxDefaultCharacteristicsToDiscover = 8
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errAlreadyDiscovering = errors.New("bluetooth: already discovering a service or characteristic")
|
errAlreadyDiscovering = errors.New("bluetooth: already discovering a service or characteristic")
|
||||||
errNotFound = errors.New("bluetooth: not found")
|
errNotFound = errors.New("bluetooth: not found")
|
||||||
|
@ -29,23 +34,30 @@ var discoveringService struct {
|
||||||
state volatile.Register8 // 0 means nothing happening, 1 means in progress, 2 means found something
|
state volatile.Register8 // 0 means nothing happening, 1 means in progress, 2 means found something
|
||||||
startHandle volatile.Register16
|
startHandle volatile.Register16
|
||||||
endHandle volatile.Register16
|
endHandle volatile.Register16
|
||||||
|
uuid C.ble_uuid_t
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceService is a BLE service on a connected peripheral device. It is only
|
// DeviceService is a BLE service on a connected peripheral device. It is only
|
||||||
// valid as long as the device remains connected.
|
// valid as long as the device remains connected.
|
||||||
type DeviceService struct {
|
type DeviceService struct {
|
||||||
|
uuid shortUUID
|
||||||
|
|
||||||
connectionHandle uint16
|
connectionHandle uint16
|
||||||
startHandle uint16
|
startHandle uint16
|
||||||
endHandle uint16
|
endHandle uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceService.
|
||||||
|
func (s *DeviceService) UUID() UUID {
|
||||||
|
return s.uuid.UUID()
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoverServices starts a service discovery procedure. Pass a list of service
|
// DiscoverServices starts a service discovery procedure. Pass a list of service
|
||||||
// UUIDs you are interested in to this function. Either a slice of all services
|
// UUIDs you are interested in to this function. Either a slice of all services
|
||||||
// is returned (of the same length as the requested UUIDs and in the same
|
// is returned (of the same length as the requested UUIDs and in the same
|
||||||
// order), or if some services could not be discovered an error is returned.
|
// order), or if some services could not be discovered an error is returned.
|
||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will currently result in zero services being
|
// Passing a nil slice of UUIDs will return a complete list of
|
||||||
// returned, but this may be changed in the future to return a complete list of
|
|
||||||
// services.
|
// services.
|
||||||
//
|
//
|
||||||
// On the Nordic SoftDevice, only one service discovery procedure may be done at
|
// On the Nordic SoftDevice, only one service discovery procedure may be done at
|
||||||
|
@ -56,15 +68,46 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
return nil, errAlreadyDiscovering
|
return nil, errAlreadyDiscovering
|
||||||
}
|
}
|
||||||
|
|
||||||
services := make([]DeviceService, len(uuids))
|
sz := maxDefaultServicesToDiscover
|
||||||
for i, uuid := range uuids {
|
if len(uuids) > 0 {
|
||||||
// Start discovery of this service.
|
sz = len(uuids)
|
||||||
shortUUID, errCode := uuid.shortUUID()
|
}
|
||||||
if errCode != 0 {
|
services := make([]DeviceService, 0, sz)
|
||||||
return nil, Error(errCode)
|
|
||||||
|
var shortUUIDs []C.ble_uuid_t
|
||||||
|
|
||||||
|
// Make a map of UUIDs in SoftDevice short form, for easier comparing.
|
||||||
|
if len(uuids) > 0 {
|
||||||
|
shortUUIDs = make([]C.ble_uuid_t, sz)
|
||||||
|
for i, uuid := range uuids {
|
||||||
|
var errCode uint32
|
||||||
|
shortUUIDs[i], errCode = uuid.shortUUID()
|
||||||
|
if errCode != 0 {
|
||||||
|
return nil, Error(errCode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numFound := 0
|
||||||
|
|
||||||
|
var startHandle uint16 = 1
|
||||||
|
|
||||||
|
for i := 0; i < sz; i++ {
|
||||||
|
var suuid C.ble_uuid_t
|
||||||
|
if len(uuids) > 0 {
|
||||||
|
suuid = shortUUIDs[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start discovery of this service.
|
||||||
discoveringService.state.Set(1)
|
discoveringService.state.Set(1)
|
||||||
errCode = C.sd_ble_gattc_primary_services_discover(d.connectionHandle, 0, &shortUUID)
|
var errCode uint32
|
||||||
|
if len(uuids) > 0 {
|
||||||
|
errCode = C.sd_ble_gattc_primary_services_discover(d.connectionHandle, startHandle, &suuid)
|
||||||
|
} else {
|
||||||
|
// calling with nil searches for all primary services.
|
||||||
|
// TODO: need a way to set suuid from the returned data
|
||||||
|
errCode = C.sd_ble_gattc_primary_services_discover(d.connectionHandle, startHandle, nil)
|
||||||
|
}
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
discoveringService.state.Set(0)
|
discoveringService.state.Set(0)
|
||||||
return nil, Error(errCode)
|
return nil, Error(errCode)
|
||||||
|
@ -78,8 +121,9 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
arm.Asm("wfe")
|
arm.Asm("wfe")
|
||||||
}
|
}
|
||||||
// Retrieve values, and mark the global as unused.
|
// Retrieve values, and mark the global as unused.
|
||||||
startHandle := discoveringService.startHandle.Get()
|
startHandle = discoveringService.startHandle.Get()
|
||||||
endHandle := discoveringService.endHandle.Get()
|
endHandle := discoveringService.endHandle.Get()
|
||||||
|
suuid = discoveringService.uuid
|
||||||
discoveringService.state.Set(0)
|
discoveringService.state.Set(0)
|
||||||
|
|
||||||
if startHandle == 0 {
|
if startHandle == 0 {
|
||||||
|
@ -89,11 +133,26 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the discovered service.
|
// Store the discovered service.
|
||||||
services[i] = DeviceService{
|
svc := DeviceService{
|
||||||
|
uuid: suuid,
|
||||||
connectionHandle: d.connectionHandle,
|
connectionHandle: d.connectionHandle,
|
||||||
startHandle: startHandle,
|
startHandle: startHandle,
|
||||||
endHandle: endHandle,
|
endHandle: endHandle,
|
||||||
}
|
}
|
||||||
|
services = append(services, svc)
|
||||||
|
|
||||||
|
numFound++
|
||||||
|
if numFound >= sz {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// last entry
|
||||||
|
if endHandle == 0xffff {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with the next handle
|
||||||
|
startHandle = endHandle + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return services, nil
|
return services, nil
|
||||||
|
@ -102,12 +161,19 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) {
|
||||||
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
// DeviceCharacteristic is a BLE characteristic on a connected peripheral
|
||||||
// device. It is only valid as long as the device remains connected.
|
// device. It is only valid as long as the device remains connected.
|
||||||
type DeviceCharacteristic struct {
|
type DeviceCharacteristic struct {
|
||||||
|
uuid shortUUID
|
||||||
|
|
||||||
connectionHandle uint16
|
connectionHandle uint16
|
||||||
valueHandle uint16
|
valueHandle uint16
|
||||||
cccdHandle uint16
|
cccdHandle uint16
|
||||||
permissions CharacteristicPermissions
|
permissions CharacteristicPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the UUID for this DeviceCharacteristic.
|
||||||
|
func (c *DeviceCharacteristic) UUID() UUID {
|
||||||
|
return c.uuid.UUID()
|
||||||
|
}
|
||||||
|
|
||||||
// A global used to pass information from the event handler back to the
|
// A global used to pass information from the event handler back to the
|
||||||
// DiscoverCharacteristics function below.
|
// DiscoverCharacteristics function below.
|
||||||
var discoveringCharacteristic struct {
|
var discoveringCharacteristic struct {
|
||||||
|
@ -123,40 +189,44 @@ var discoveringCharacteristic struct {
|
||||||
// slice has the same length as the UUID slice with characteristics in the same
|
// slice has the same length as the UUID slice with characteristics in the same
|
||||||
// order in the slice as in the requested UUID list.
|
// order in the slice as in the requested UUID list.
|
||||||
//
|
//
|
||||||
// Passing a nil slice of UUIDs will currently result in zero characteristics
|
// Passing a nil slice of UUIDs will return a complete
|
||||||
// being returned, but this may be changed in the future to return a complete
|
|
||||||
// list of characteristics.
|
// list of characteristics.
|
||||||
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) {
|
func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) {
|
||||||
if len(uuids) == 0 {
|
|
||||||
// Nothing to do. This behavior might change in the future (if a nil
|
|
||||||
// uuids slice is passed).
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if discoveringCharacteristic.handle_value.Get() != 0 {
|
if discoveringCharacteristic.handle_value.Get() != 0 {
|
||||||
return nil, errAlreadyDiscovering
|
return nil, errAlreadyDiscovering
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a list of UUIDs in SoftDevice short form, for easier comparing.
|
sz := maxDefaultCharacteristicsToDiscover
|
||||||
shortUUIDs := make([]C.ble_uuid_t, len(uuids))
|
if len(uuids) > 0 {
|
||||||
for i, uuid := range uuids {
|
sz = len(uuids)
|
||||||
var errCode uint32
|
}
|
||||||
shortUUIDs[i], errCode = uuid.shortUUID()
|
characteristics := make([]DeviceCharacteristic, 0, sz)
|
||||||
if errCode != 0 {
|
|
||||||
return nil, Error(errCode)
|
var shortUUIDs []C.ble_uuid_t
|
||||||
|
|
||||||
|
// Make a map of UUIDs in SoftDevice short form, for easier comparing.
|
||||||
|
if len(uuids) > 0 {
|
||||||
|
shortUUIDs = make([]C.ble_uuid_t, sz)
|
||||||
|
for i, uuid := range uuids {
|
||||||
|
var errCode uint32
|
||||||
|
shortUUIDs[i], errCode = uuid.shortUUID()
|
||||||
|
if errCode != 0 {
|
||||||
|
return nil, Error(errCode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request characteristics one by one, until all are found.
|
// Request characteristics one by one, until all are found.
|
||||||
numFound := 0
|
numFound := 0
|
||||||
characteristics := make([]DeviceCharacteristic, len(uuids))
|
|
||||||
startHandle := s.startHandle
|
startHandle := s.startHandle
|
||||||
for numFound < len(uuids) && startHandle < s.endHandle {
|
|
||||||
|
for startHandle < s.endHandle {
|
||||||
// Discover the next characteristic in this service.
|
// Discover the next characteristic in this service.
|
||||||
handles := C.ble_gattc_handle_range_t{
|
handles := C.ble_gattc_handle_range_t{
|
||||||
start_handle: startHandle,
|
start_handle: startHandle,
|
||||||
end_handle: s.endHandle,
|
end_handle: startHandle + 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
errCode := C.sd_ble_gattc_characteristics_discover(s.connectionHandle, &handles)
|
errCode := C.sd_ble_gattc_characteristics_discover(s.connectionHandle, &handles)
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
return nil, Error(errCode)
|
return nil, Error(errCode)
|
||||||
|
@ -171,64 +241,74 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter
|
||||||
foundCharacteristicHandle := discoveringCharacteristic.handle_value.Get()
|
foundCharacteristicHandle := discoveringCharacteristic.handle_value.Get()
|
||||||
discoveringCharacteristic.handle_value.Set(0)
|
discoveringCharacteristic.handle_value.Set(0)
|
||||||
|
|
||||||
|
// was it last characteristic?
|
||||||
|
if foundCharacteristicHandle == 0xffff {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// Start the next request from the handle right after this one.
|
// Start the next request from the handle right after this one.
|
||||||
startHandle = foundCharacteristicHandle + 1
|
startHandle = foundCharacteristicHandle + 1
|
||||||
|
|
||||||
// Look whether we found a requested handle.
|
// not one of the characteristics we are looking for
|
||||||
for i, shortUUID := range shortUUIDs {
|
if len(shortUUIDs) > 0 && !shortUUID(discoveringCharacteristic.uuid).IsIn(shortUUIDs) {
|
||||||
if discoveringCharacteristic.uuid == shortUUID {
|
continue
|
||||||
// Found a characteristic.
|
}
|
||||||
permissions := CharacteristicPermissions(0)
|
|
||||||
rawPermissions := discoveringCharacteristic.char_props
|
|
||||||
if rawPermissions.bitfield_broadcast() != 0 {
|
|
||||||
permissions |= CharacteristicBroadcastPermission
|
|
||||||
}
|
|
||||||
if rawPermissions.bitfield_read() != 0 {
|
|
||||||
permissions |= CharacteristicReadPermission
|
|
||||||
}
|
|
||||||
if rawPermissions.bitfield_write_wo_resp() != 0 {
|
|
||||||
permissions |= CharacteristicWriteWithoutResponsePermission
|
|
||||||
}
|
|
||||||
if rawPermissions.bitfield_write() != 0 {
|
|
||||||
permissions |= CharacteristicWritePermission
|
|
||||||
}
|
|
||||||
if rawPermissions.bitfield_notify() != 0 {
|
|
||||||
permissions |= CharacteristicNotifyPermission
|
|
||||||
}
|
|
||||||
if rawPermissions.bitfield_indicate() != 0 {
|
|
||||||
permissions |= CharacteristicIndicatePermission
|
|
||||||
}
|
|
||||||
characteristics[i].permissions = permissions
|
|
||||||
characteristics[i].valueHandle = foundCharacteristicHandle
|
|
||||||
|
|
||||||
if permissions&CharacteristicNotifyPermission != 0 {
|
// Found a characteristic.
|
||||||
// This characteristic has the notify permission, so most
|
permissions := CharacteristicPermissions(0)
|
||||||
// likely it also supports notifications.
|
rawPermissions := discoveringCharacteristic.char_props
|
||||||
errCode := C.sd_ble_gattc_descriptors_discover(s.connectionHandle, &C.ble_gattc_handle_range_t{
|
if rawPermissions.bitfield_broadcast() != 0 {
|
||||||
start_handle: startHandle,
|
permissions |= CharacteristicBroadcastPermission
|
||||||
end_handle: s.endHandle,
|
}
|
||||||
})
|
if rawPermissions.bitfield_read() != 0 {
|
||||||
if errCode != 0 {
|
permissions |= CharacteristicReadPermission
|
||||||
return nil, Error(errCode)
|
}
|
||||||
}
|
if rawPermissions.bitfield_write_wo_resp() != 0 {
|
||||||
|
permissions |= CharacteristicWriteWithoutResponsePermission
|
||||||
|
}
|
||||||
|
if rawPermissions.bitfield_write() != 0 {
|
||||||
|
permissions |= CharacteristicWritePermission
|
||||||
|
}
|
||||||
|
if rawPermissions.bitfield_notify() != 0 {
|
||||||
|
permissions |= CharacteristicNotifyPermission
|
||||||
|
}
|
||||||
|
if rawPermissions.bitfield_indicate() != 0 {
|
||||||
|
permissions |= CharacteristicIndicatePermission
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until the descriptor handle is found.
|
dc := DeviceCharacteristic{uuid: discoveringCharacteristic.uuid}
|
||||||
for discoveringCharacteristic.handle_value.Get() == 0 {
|
dc.permissions = permissions
|
||||||
arm.Asm("wfe")
|
dc.valueHandle = foundCharacteristicHandle
|
||||||
}
|
|
||||||
foundDescriptorHandle := discoveringCharacteristic.handle_value.Get()
|
|
||||||
discoveringCharacteristic.handle_value.Set(0)
|
|
||||||
|
|
||||||
characteristics[i].cccdHandle = foundDescriptorHandle
|
if permissions&CharacteristicNotifyPermission != 0 {
|
||||||
}
|
// This characteristic has the notify permission, so most
|
||||||
|
// likely it also supports notifications.
|
||||||
numFound++
|
errCode := C.sd_ble_gattc_descriptors_discover(s.connectionHandle, &C.ble_gattc_handle_range_t{
|
||||||
break
|
start_handle: startHandle,
|
||||||
|
end_handle: startHandle + 1,
|
||||||
|
})
|
||||||
|
if errCode != 0 {
|
||||||
|
return nil, Error(errCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait until the descriptor handle is found.
|
||||||
|
for discoveringCharacteristic.handle_value.Get() == 0 {
|
||||||
|
arm.Asm("wfe")
|
||||||
|
}
|
||||||
|
foundDescriptorHandle := discoveringCharacteristic.handle_value.Get()
|
||||||
|
discoveringCharacteristic.handle_value.Set(0)
|
||||||
|
|
||||||
|
dc.cccdHandle = foundDescriptorHandle
|
||||||
|
}
|
||||||
|
|
||||||
|
characteristics = append(characteristics, dc)
|
||||||
|
numFound++
|
||||||
|
if numFound >= sz {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if numFound != len(uuids) {
|
if len(uuids) > 0 && numFound != len(uuids) {
|
||||||
return nil, errNotFound
|
return nil, errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
uuid_sd.go
23
uuid_sd.go
|
@ -12,6 +12,8 @@ package bluetooth
|
||||||
import "C"
|
import "C"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
|
type shortUUID C.ble_uuid_t
|
||||||
|
|
||||||
func (uuid UUID) shortUUID() (C.ble_uuid_t, uint32) {
|
func (uuid UUID) shortUUID() (C.ble_uuid_t, uint32) {
|
||||||
var short C.ble_uuid_t
|
var short C.ble_uuid_t
|
||||||
short.uuid = uint16(uuid[3])
|
short.uuid = uint16(uuid[3])
|
||||||
|
@ -22,3 +24,24 @@ func (uuid UUID) shortUUID() (C.ble_uuid_t, uint32) {
|
||||||
errCode := C.sd_ble_uuid_vs_add((*C.ble_uuid128_t)(unsafe.Pointer(&uuid[0])), &short._type)
|
errCode := C.sd_ble_uuid_vs_add((*C.ble_uuid128_t)(unsafe.Pointer(&uuid[0])), &short._type)
|
||||||
return short, errCode
|
return short, errCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UUID returns the full length UUID for this short UUID.
|
||||||
|
func (s shortUUID) UUID() UUID {
|
||||||
|
if s._type == C.BLE_UUID_TYPE_BLE {
|
||||||
|
return New16BitUUID(s.uuid)
|
||||||
|
}
|
||||||
|
var outLen C.uint8_t
|
||||||
|
var outUUID UUID
|
||||||
|
C.sd_ble_uuid_encode(((*C.ble_uuid_t)(unsafe.Pointer(&s))), &outLen, ((*C.uint8_t)(unsafe.Pointer(&outUUID))))
|
||||||
|
return outUUID
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIn checks the passed in slice of short UUIDs to see if this uuid is in it.
|
||||||
|
func (s shortUUID) IsIn(uuids []C.ble_uuid_t) bool {
|
||||||
|
for _, u := range uuids {
|
||||||
|
if u == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue