From a620bacdb8de411d61463a06c0b18b04e44471a9 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Fri, 28 Aug 2020 08:35:13 +0200 Subject: [PATCH] macos: able to retrieve some of the info when scanning Signed-off-by: Ron Evans --- adapter_darwin.go | 97 ++++++++++++++++++++++++++++++++++++++++++--- gap_darwin.go | 47 +++++++++++++++++++++- macbt/cmdelegate.go | 27 ------------- macbt/macbt.go | 2 - macbt/pmdelegate.go | 35 ---------------- 5 files changed, 138 insertions(+), 70 deletions(-) delete mode 100644 macbt/cmdelegate.go delete mode 100644 macbt/macbt.go delete mode 100644 macbt/pmdelegate.go diff --git a/adapter_darwin.go b/adapter_darwin.go index 237935a..7a0e5c0 100644 --- a/adapter_darwin.go +++ b/adapter_darwin.go @@ -2,22 +2,109 @@ package bluetooth import ( "github.com/JuulLabs-OSS/cbgo" - "github.com/tinygo-org/bluetooth/macbt" ) type Adapter struct { - cm cbgo.CentralManager - cmd macbt.CMDelegate + cbgo.CentralManagerDelegateBase + cbgo.PeripheralManagerDelegateBase + + cm cbgo.CentralManager + pm cbgo.PeripheralManager + + peripheralFoundHandler func(*Adapter, ScanResult) + cancelChan chan struct{} } // DefaultAdapter is the default adapter on the system. // // Make sure to call Enable() before using it to initialize the adapter. -var DefaultAdapter = &Adapter{} +var DefaultAdapter = &Adapter{ + cm: cbgo.NewCentralManager(nil), + pm: cbgo.NewPeripheralManager(nil), +} // Enable configures the BLE stack. It must be called before any // Bluetooth-related calls (unless otherwise indicated). func (a *Adapter) Enable() error { - a.cm = cbgo.NewCentralManager(nil) + a.cm.SetDelegate(a) + // TODO: wait until powered + a.pm.SetDelegate(a) + // TODO: wait until powered + return nil } + +// CentralManager delegate functions + +func (a *Adapter) CentralManagerDidUpdateState(cmgr cbgo.CentralManager) { +} + +// DidDiscoverPeripheral when peripheral is discovered. +func (a *Adapter) DidDiscoverPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, + advFields cbgo.AdvFields, rssi int) { + if a.peripheralFoundHandler != nil { + sr := makeScanResult(prph, advFields, rssi) + a.peripheralFoundHandler(a, sr) + } +} + +// DidConnectPeripheral when peripheral is connected. +func (a *Adapter) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) { +} + +// 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 { + // TODO: figure out the peripheral info. + + // TODO: create a list of serviceUUIDs. + + return ScanResult{ + RSSI: int16(rssi), + Address: Address{ + // TODO: fill in this info + //MAC: prph.Identifier(), + //IsRandom: prph.Identifier == "random", + }, + AdvertisementPayload: &advertisementFields{ + AdvertisementFields{ + LocalName: advFields.LocalName, + // TODO: fill in this info + //ServiceUUIDs: serviceUUIDs, + }, + }, + } +} diff --git a/gap_darwin.go b/gap_darwin.go index 4e829c3..8e30a04 100644 --- a/gap_darwin.go +++ b/gap_darwin.go @@ -1,14 +1,59 @@ package bluetooth +import ( + "errors" + + "github.com/JuulLabs-OSS/cbgo" +) + // Scan starts a BLE scan. It is stopped by a call to StopScan. A common pattern // is to cancel the scan when a particular device has been found. func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) (err error) { - return nil + if callback == nil { + return errors.New("must provide callback to Scan function") + } + + if a.cancelChan != nil { + return errors.New("already calling Scan function") + } + + a.peripheralFoundHandler = callback + + // Channel that will be closed when the scan is stopped. + // Detecting whether the scan is stopped can be done by doing a non-blocking + // read from it. If it succeeds, the scan is stopped. + cancelChan := make(chan struct{}) + a.cancelChan = cancelChan + + a.cm.Scan(nil, &cbgo.CentralManagerScanOpts{ + AllowDuplicates: true, + }) + + for { + // Check whether the scan is stopped. This is necessary to avoid a race + // condition between the signal channel and the cancelScan channel when + // the callback calls StopScan() (no new callbacks may be called after + // StopScan is called). + select { + case <-cancelChan: + // stop scanning here? + return nil + default: + } + } } // StopScan stops any in-progress scan. It can be called from within a Scan // callback to stop the current scan. If no scan is in progress, an error will // be returned. func (a *Adapter) StopScan() error { + if a.cancelChan != nil { + return errors.New("already calling Scan function") + } + + a.cm.StopScan() + close(a.cancelChan) + a.cancelChan = nil + return nil } diff --git a/macbt/cmdelegate.go b/macbt/cmdelegate.go deleted file mode 100644 index f5fc0cf..0000000 --- a/macbt/cmdelegate.go +++ /dev/null @@ -1,27 +0,0 @@ -// Implements the CentralManagerDelegate interface. CoreBluetooth -// communicates events asynchronously via callbacks. This file implements a -// synchronous interface by translating these callbacks into channel -// operations. - -package macbt - -import ( - "github.com/JuulLabs-OSS/cbgo" -) - -// CMDelegate to handle callbacks from CoreBluetooth. -type CMDelegate struct { -} - -func (d *CMDelegate) CentralManagerDidUpdateState(cmgr cbgo.CentralManager) { -} - -func (d *CMDelegate) DidDiscoverPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, - advFields cbgo.AdvFields, rssi int) { -} - -func (d *CMDelegate) DidConnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral) { -} - -func (d *CMDelegate) DidDisconnectPeripheral(cmgr cbgo.CentralManager, prph cbgo.Peripheral, err error) { -} diff --git a/macbt/macbt.go b/macbt/macbt.go deleted file mode 100644 index bdb7676..0000000 --- a/macbt/macbt.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package macbt is wrapper to CoreBluetooth. -package macbt diff --git a/macbt/pmdelegate.go b/macbt/pmdelegate.go deleted file mode 100644 index bac0221..0000000 --- a/macbt/pmdelegate.go +++ /dev/null @@ -1,35 +0,0 @@ -// Implements the PeripheralManagerDelegate interface. -// CoreBluetooth communicates events asynchronously via callbacks. This file -// implements a synchronous interface by translating these callbacks into -// channel operations. - -package macbt - -import ( - "github.com/JuulLabs-OSS/cbgo" -) - -// PMDelegate to handle callbacks from CoreBluetooth. -type PMDelegate struct { -} - -func (d *PMDelegate) PeripheralManagerDidUpdateState(pmgr cbgo.PeripheralManager) { -} - -func (d *PMDelegate) DidAddService(pmgr cbgo.PeripheralManager, svc cbgo.Service, err error) { -} - -func (d *PMDelegate) DidStartAdvertising(pmgr cbgo.PeripheralManager, err error) { -} - -func (d *PMDelegate) DidReceiveReadRequest(pmgr cbgo.PeripheralManager, cbreq cbgo.ATTRequest) { -} - -func (d *PMDelegate) DidReceiveWriteRequests(pmgr cbgo.PeripheralManager, cbreqs []cbgo.ATTRequest) { -} - -func (d *PMDelegate) CentralDidSubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, cbchr cbgo.Characteristic) { -} - -func (d *PMDelegate) CentralDidUnsubscribe(pmgr cbgo.PeripheralManager, cent cbgo.Central, chr cbgo.Characteristic) { -}