diff --git a/README.md b/README.md index 373d152..13222cc 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This package attempts to build a cross-platform Bluetooth Low Energy module for | API used | WinRT | BlueZ (over D-Bus) | SoftDevice | CoreBluetooth | | Scanning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Connect to peripheral | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | -| Write peripheral characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | +| Write peripheral characteristics | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Receive notifications | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | | Advertisement | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | | Local services | :x: | :heavy_check_mark: | :heavy_check_mark: | :x: | diff --git a/adapter_darwin.go b/adapter_darwin.go index 8067640..3f22b77 100644 --- a/adapter_darwin.go +++ b/adapter_darwin.go @@ -80,40 +80,6 @@ func (a *Adapter) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Perip a.connectChan <- prph } -// DidDisconnectPeripheral when peripheral is disconnected. -func (a *Adapter) DidDisconnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, err error) { -} - -// PeripheralManager delegate functions - -// PeripheralManagerDidUpdateState when state updated. -func (a *Adapter) PeripheralManagerDidUpdateState(pmgr cbgo.PeripheralManager) { -} - -// DidAddService when service added. -func (a *Adapter) DidAddService(pmgr cbgo.PeripheralManager, svc cbgo.Service, err error) { -} - -// DidStartAdvertising when advertising starts. -func (a *Adapter) DidStartAdvertising(pmgr cbgo.PeripheralManager, err error) { -} - -// DidReceiveReadRequest when read request received. -func (a *Adapter) DidReceiveReadRequest(pmgr cbgo.PeripheralManager, cbreq cbgo.ATTRequest) { -} - -// DidReceiveWriteRequests when write requests received. -func (a *Adapter) DidReceiveWriteRequests(pmgr cbgo.PeripheralManager, cbreqs []cbgo.ATTRequest) { -} - -// CentralDidSubscribe when central subscribed. -func (a *Adapter) CentralDidSubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, cbchr cbgo.Characteristic) { -} - -// CentralDidUnsubscribe when central unsubscribed. -func (a *Adapter) CentralDidUnsubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, chr cbgo.Characteristic) { -} - // makeScanResult creates a ScanResult when peripheral is found. func makeScanResult(prph cbgo.Peripheral, advFields cbgo.AdvFields, rssi int) ScanResult { uuid, _ := ParseUUID(prph.Identifier().String()) diff --git a/gap_darwin.go b/gap_darwin.go index e178441..9e1da79 100644 --- a/gap_darwin.go +++ b/gap_darwin.go @@ -81,7 +81,11 @@ func (a *Adapter) StopScan() error { type Device struct { cbgo.PeripheralDelegateBase - cm cbgo.CentralManager + cm cbgo.CentralManager + prph cbgo.Peripheral + + servicesChan chan error + charsChan chan error } // Connect starts a connection attempt to the given peripheral device address. @@ -101,7 +105,10 @@ func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, select { case p := <-a.connectChan: d := &Device{ - cm: a.cm, + cm: a.cm, + prph: p, + servicesChan: make(chan error), + charsChan: make(chan error), } p.SetDelegate(d) @@ -111,23 +118,27 @@ func (a *Adapter) Connect(address Addresser, params ConnectionParams) (*Device, } } +// Peripheral returns the Device's cbgo.Peripheral +func (d *Device) Peripheral() (prph cbgo.Peripheral) { + return d.prph +} + +// CharsChan returns the Device's charsChan channel used for +// blocking on discovering the characteristics for a service. +func (d *Device) CharsChan() chan error { + return d.charsChan +} + // Peripheral delegate functions +// DidDiscoverServices is called when the services for a Peripheral +// have been discovered. func (d *Device) DidDiscoverServices(prph cbgo.Peripheral, err error) { + d.servicesChan <- nil } + +// DidDiscoverCharacteristics is called when the characteristics for a Service +// for a Peripheral have been discovered. func (d *Device) DidDiscoverCharacteristics(prph cbgo.Peripheral, svc cbgo.Service, err error) { -} -func (d *Device) DidDiscoverDescriptors(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { -} -func (d *Device) DidUpdateValueForCharacteristic(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { -} -func (d *Device) DidUpdateValueForDescriptor(prph cbgo.Peripheral, dsc cbgo.Descriptor, err error) { -} -func (d *Device) DidWriteValueForCharacteristic(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { -} -func (d *Device) DidWriteValueForDescriptor(prph cbgo.Peripheral, dsc cbgo.Descriptor, err error) { -} -func (d *Device) DidUpdateNotificationState(prph cbgo.Peripheral, chr cbgo.Characteristic, err error) { -} -func (d *Device) DidReadRSSI(prph cbgo.Peripheral, rssi int, err error) { + d.charsChan <- nil } diff --git a/gattc_darwin.go b/gattc_darwin.go index 280f9a5..2f34562 100644 --- a/gattc_darwin.go +++ b/gattc_darwin.go @@ -1,15 +1,56 @@ package bluetooth +import ( + "errors" + "time" + + "github.com/JuulLabs-OSS/cbgo" +) + // 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 // 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. func (d *Device) DiscoverServices(uuids []UUID) ([]DeviceService, error) { - return nil, nil + cbuuids := []cbgo.UUID{} + for _, u := range uuids { + uuid, _ := cbgo.ParseUUID(u.String()) + cbuuids = append(cbuuids, uuid) + } + + d.prph.DiscoverServices(cbuuids) + + // wait on channel for service discovery + select { + case <-d.servicesChan: + svcs := []DeviceService{} + for _, dsvc := range d.prph.Services() { + uuid, _ := ParseUUID(dsvc.UUID().String()) + svc := DeviceService{ + UUID: uuid, + device: d, + service: dsvc, + } + svcs = append(svcs, svc) + } + return svcs, nil + case <-time.NewTimer(10 * time.Second).C: + return nil, errors.New("timeout on DiscoverServices") + } } // DeviceService is a BLE service on a connected peripheral device. type DeviceService struct { + UUID + + device *Device + + service cbgo.Service +} + +// Device returns the Device for this service. +func (s *DeviceService) Device() *Device { + return s.device } // DiscoverCharacteristics discovers characteristics in this service. Pass a @@ -19,12 +60,42 @@ type DeviceService 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. func (s *DeviceService) DiscoverCharacteristics(uuids []UUID) ([]DeviceCharacteristic, error) { - return nil, nil + cbuuids := []cbgo.UUID{} + for _, u := range uuids { + uuid, _ := cbgo.ParseUUID(u.String()) + cbuuids = append(cbuuids, uuid) + } + + s.Device().Peripheral().DiscoverCharacteristics(cbuuids, s.service) + + // wait on channel for characteristic discovery + select { + case <-s.Device().CharsChan(): + chars := []DeviceCharacteristic{} + for _, dchar := range s.service.Characteristics() { + uuid, _ := ParseUUID(dchar.UUID().String()) + char := DeviceCharacteristic{ + UUID: uuid, + service: s, + characteristic: dchar, + } + chars = append(chars, char) + } + return chars, nil + case <-time.NewTimer(10 * time.Second).C: + return nil, errors.New("timeout on DiscoverCharacteristics") + } } // DeviceCharacteristic is a BLE characteristic on a connected peripheral // device. type DeviceCharacteristic struct { + UUID + + service *DeviceService + + characteristic cbgo.Characteristic + callback func(buf []byte) } // WriteWithoutResponse replaces the characteristic value with a new value. The @@ -32,7 +103,9 @@ type DeviceCharacteristic struct { // writes can be in flight at any given time. This call is also known as a // "write command" (as opposed to a write request). func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) { - return 0, nil + c.service.Device().Peripheral().WriteCharacteristic(p, c.characteristic, false) + + return len(p), nil } // EnableNotifications enables notifications in the Client Characteristic @@ -40,5 +113,9 @@ func (c DeviceCharacteristic) WriteWithoutResponse(p []byte) (n int, err error) // notification with a new value every time the value of the characteristic // changes. func (c DeviceCharacteristic) EnableNotifications(callback func(buf []byte)) error { + if callback == nil { + return errors.New("must provide a callback for EnableNotifications") + } + return nil }