From 64ee33dfde7b50c80bb98e311bc851919724d438 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Sat, 29 Aug 2020 18:13:45 +0200 Subject: [PATCH 1/4] examples: add discover example that shows services/characteristics Signed-off-by: Ron Evans --- examples/discover/main.go | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 examples/discover/main.go diff --git a/examples/discover/main.go b/examples/discover/main.go new file mode 100644 index 00000000..dd388eaf --- /dev/null +++ b/examples/discover/main.go @@ -0,0 +1,66 @@ +package main + +import ( + "os" + + "github.com/tinygo-org/bluetooth" +) + +var adapter = bluetooth.DefaultAdapter + +func main() { + if len(os.Args) < 2 { + println("usage: discover [local name]") + os.Exit(1) + } + + // look for device with specific name + name := os.Args[1] + + // Enable BLE interface. + must("enable BLE stack", adapter.Enable()) + + ch := make(chan bluetooth.ScanResult, 1) + + // Start scanning. + println("scanning...") + err := adapter.Scan(func(adapter *bluetooth.Adapter, result bluetooth.ScanResult) { + println("found device:", result.Address.String(), result.RSSI, result.LocalName()) + if result.LocalName() == name { + adapter.StopScan() + ch <- result + } + }) + + var device *bluetooth.Device + select { + case result := <-ch: + device, err = adapter.Connect(result.Address, bluetooth.ConnectionParams{}) + if err != nil { + println(err.Error()) + os.Exit(1) + } + + println("connected to ", result.LocalName()) + } + + // get services + println("discovering services/characteristics") + srvcs, err := device.DiscoverServices(nil) + for _, srvc := range srvcs { + println("- service", srvc.UUID.String()) + + chars, _ := srvc.DiscoverCharacteristics(nil) + for _, char := range chars { + println("-- characteristic", char.UUID.String()) + } + } + + must("start scan", err) +} + +func must(action string, err error) { + if err != nil { + panic("failed to " + action + ": " + err.Error()) + } +} From b415f18f86df44b32ec68ff49b2b9ee759437d65 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 2 Sep 2020 19:15:52 +0200 Subject: [PATCH 2/4] linux: correct use of Addresser when scanning Signed-off-by: deadprogram --- gap_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gap_linux.go b/gap_linux.go index f1359f2d..78c4eba6 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -219,7 +219,7 @@ func makeScanResult(props *device.Device1Properties) ScanResult { serviceUUIDs = append(serviceUUIDs, parsedUUID) } - a := &Address{MACAddress{MAC: addr}} + a := Address{MACAddress{MAC: addr}} a.SetRandom(props.AddressType == "random") return ScanResult{ From 221f101b52540c0f6ad328679c6e238805838d99 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 2 Sep 2020 19:19:07 +0200 Subject: [PATCH 3/4] linux: add UUID to DeviceService and DeviceCharacteristic Signed-off-by: deadprogram --- gattc_linux.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gattc_linux.go b/gattc_linux.go index a97fb0a2..563bf5eb 100644 --- a/gattc_linux.go +++ b/gattc_linux.go @@ -13,6 +13,8 @@ import ( // DeviceService is a BLE service on a connected peripheral device. type DeviceService struct { + UUID + service *gatt.GattService1 } @@ -75,6 +77,7 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { // Don't overwrite it, to keep the servicesFound count correct. continue } + services[i].UUID = uuid services[i].service = service servicesFound++ break @@ -91,6 +94,8 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { // DeviceCharacteristic is a BLE characteristic on a connected peripheral // device. type DeviceCharacteristic struct { + UUID + characteristic *gatt.GattCharacteristic1 } @@ -140,6 +145,7 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter // Don't overwrite it, to keep the servicesFound count correct. continue } + chars[i].UUID = uuid chars[i].characteristic = char characteristicsFound++ break From 337e68c5074edb78333a6efa63ce35b07982ba1b Mon Sep 17 00:00:00 2001 From: deadprogram Date: Wed, 2 Sep 2020 20:44:17 +0200 Subject: [PATCH 4/4] linux: all for DiscoverServices and DiscoverCharacteristics to discover all when no specific list of UUIDs Signed-off-by: deadprogram --- gattc_darwin.go | 6 ++++ gattc_linux.go | 88 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/gattc_darwin.go b/gattc_darwin.go index 6beba58c..eb4f2fba 100644 --- a/gattc_darwin.go +++ b/gattc_darwin.go @@ -11,6 +11,9 @@ import ( // 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 // order), or if some services could not be discovered an error is returned. +// +// Passing a nil slice of UUIDs will return a complete list of +// services. func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { cbuuids := []cbgo.UUID{} for _, u := range uuids { @@ -58,6 +61,9 @@ type DeviceService struct { // discovered an error is returned. If there is no error, the characteristics // slice has the same length as the UUID slice with characteristics in the same // order in the slice as in the requested UUID list. +// +// Passing a nil slice of UUIDs will return a complete list of +// characteristics. func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) { cbuuids := []cbgo.UUID{} for _, u := range uuids { diff --git a/gattc_linux.go b/gattc_linux.go index 563bf5eb..fa48f2ff 100644 --- a/gattc_linux.go +++ b/gattc_linux.go @@ -23,8 +23,7 @@ type DeviceService struct { // 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. // -// Passing a nil slice of UUIDs will currently result in zero services being -// returned, but this may be changed in the future to return a complete list of +// Passing a nil slice of UUIDs will return a complete list of // services. // // On Linux with BlueZ, this just waits for the ServicesResolved signal (if @@ -42,7 +41,8 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { time.Sleep(10 * time.Millisecond) } - services := make([]DeviceService, len(uuids)) + services := []DeviceService{} + uuidServices := make(map[string]string) servicesFound := 0 // Iterate through all objects managed by BlueZ, hoping to find the services @@ -67,24 +67,38 @@ func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { if err != nil { return nil, err } - for i, uuid := range uuids { - if service.Properties.UUID != uuid.String() { - // Not one of the services we're looking for. - continue + + if len(uuids) > 0 { + found := false + for _, uuid := range uuids { + if service.Properties.UUID == uuid.String() { + // One of the services we're looking for. + found = true + break + } } - if services[i].service != nil { - // There is more than one service with the same UUID? - // Don't overwrite it, to keep the servicesFound count correct. + if !found { continue } - services[i].UUID = uuid - services[i].service = service - servicesFound++ - break } + + if _, ok := uuidServices[service.Properties.UUID]; ok { + // There is more than one service with the same UUID? + // Don't overwrite it, to keep the servicesFound count correct. + continue + } + + uuid, _ := ParseUUID(service.Properties.UUID) + ds := DeviceService{UUID: uuid, + service: service, + } + + services = append(services, ds) + servicesFound++ + uuidServices[service.Properties.UUID] = service.Properties.UUID } - if servicesFound != len(uuids) { + if servicesFound <= len(uuids) { return nil, errors.New("bluetooth: could not find some services") } @@ -106,11 +120,11 @@ type DeviceCharacteristic struct { // slice has the same length as the UUID slice with characteristics in the same // order in the slice as in the requested UUID list. // -// Passing a nil slice of UUIDs will currently result in zero characteristics -// being returned, but this may be changed in the future to return a complete +// Passing a nil slice of UUIDs will return a complete // list of characteristics. func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) { - chars := make([]DeviceCharacteristic, len(uuids)) + chars := []DeviceCharacteristic{} + uuidChars := make(map[string]string) characteristicsFound := 0 // Iterate through all objects managed by BlueZ, hoping to find the @@ -135,24 +149,38 @@ func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacter if err != nil { return nil, err } - for i, uuid := range uuids { - if char.Properties.UUID != uuid.String() { - // Not one of the characteristics we're looking for. - continue + + if len(uuids) > 0 { + found := false + for _, uuid := range uuids { + if char.Properties.UUID == uuid.String() { + // One of the services we're looking for. + found = true + break + } } - if chars[i].characteristic != nil { - // There is more than one characteristic with the same UUID? - // Don't overwrite it, to keep the servicesFound count correct. + if !found { continue } - chars[i].UUID = uuid - chars[i].characteristic = char - characteristicsFound++ - break } + + if _, ok := uuidChars[char.Properties.UUID]; ok { + // There is more than one characteristic with the same UUID? + // Don't overwrite it, to keep the servicesFound count correct. + continue + } + + uuid, _ := ParseUUID(char.Properties.UUID) + dc := DeviceCharacteristic{UUID: uuid, + characteristic: char, + } + + chars = append(chars, dc) + characteristicsFound++ + uuidChars[char.Properties.UUID] = char.Properties.UUID } - if characteristicsFound != len(uuids) { + if characteristicsFound < len(uuids) { return nil, errors.New("bluetooth: could not find some characteristics") }