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:
Ron Evans 2020-09-13 20:21:38 +02:00 committed by GitHub
parent 6dc1dff711
commit ef90e5d337
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 301 additions and 104 deletions

View file

@ -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

View file

@ -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()

View file

@ -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
View 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
View 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.")
}

View file

@ -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

View file

@ -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,
} }

View file

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

View file

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