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()) + } +} 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{ 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 a97fb0a2..fa48f2ff 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 } @@ -21,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 @@ -40,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 @@ -65,23 +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].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") } @@ -91,6 +108,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 } @@ -101,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 @@ -130,23 +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].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") }