diff --git a/ble.go b/ble.go index df083a4..9956ed2 100644 --- a/ble.go +++ b/ble.go @@ -173,6 +173,25 @@ func (b *BLE) StopScan() { b.stopScan() } +func CancelConnection(p Peripheral) { + b := peripheralLookup(p) + if b == nil { + return + } + b.connections.Lock() + var ch chan string; + for _, item := range b.connections.items { + if item.p.Identifier == p.Identifier { + ch = item.state + break + } + } + b.connections.Unlock() + if ch != nil { + ch <- "cancel" + } +} + func connectTracker(b *BLE, x ConnectionListItem) bool { fmt.Printf("connectTracker(): %s\n", x.p.Name) b.connections.Lock() diff --git a/ble_darwin.go b/ble_darwin.go index 4c7db40..4f5fa86 100644 --- a/ble_darwin.go +++ b/ble_darwin.go @@ -3,7 +3,6 @@ package ble import "C" import ( - "encoding/binary" "fmt" "runtime" "time" @@ -13,6 +12,8 @@ import ( "git.wow.st/gmp/ble/ns" ) +// Types required for ble.go + type bleState ns.CBManagerState type bleHandle struct { @@ -24,12 +25,6 @@ var cdLookup map[unsafe.Pointer]*BLE var pdLookup map[unsafe.Pointer]*BLE var pcache map[unsafe.Pointer]*Peripheral -func peripheralLookup(p Peripheral) *BLE { - return pdLookup[p.p.Ptr()] -} - -type State string - type Peripheral struct { Name string RSSI int @@ -41,30 +36,19 @@ type Peripheral struct { type Service *ns.CBService type Characteristic *ns.CBCharacteristic +// Functions required by API + +func peripheralLookup(p Peripheral) *BLE { + return pdLookup[p.p.Ptr()] +} + //Init needs to be called before the BLE library can be used. No setup needed //on Darwin. func Init() { } -func (x *Peripheral) Retain() { -//NOTE: ns.ObjectAtIndex() calls SetFinalizer for us, which will be a problem -//later when we call GC(), so we always first clear the finalizer before -//setting a new one. - runtime.SetFinalizer(x.p, nil) - x.p.Retain() - x.p.GC() -} - -func peripheralName(p *ns.CBPeripheral) string { - var ret string - nsname := p.Name() - if nsname.Ptr() != nil { - ret = nsname.String() - } - return ret -} - +//newPeripheral creates a new Peripheral struct func newPeripheral(x *ns.CBPeripheral) Peripheral { if pcache == nil { pcache = make(map[unsafe.Pointer]*Peripheral) @@ -82,6 +66,7 @@ func newPeripheral(x *ns.CBPeripheral) Peripheral { return ret } +//stringState returns a string version of the BLE state func (b *BLE) stringState() string { x := b.state switch (ns.NSInteger)(x) { @@ -102,57 +87,35 @@ func (b *BLE) stringState() string { } } +//readyToScan returns true if the hardware is ready to initiate a scan func (b *BLE) readyToScan() bool { return b.state == (bleState)(ns.CBManagerStatePoweredOn) } +//scan puts the BLE hardware into scanning mode func (b *BLE) scan() { b.handle.cm.ScanForPeripheralsWithServices(nil, nil) } +//stopScan stops a scan in progress func (b *BLE) stopScan() { b.handle.cm.StopScan() } -func (b *BLE) setState(x ns.CBManagerState) { - b.Lock() - defer b.Unlock() - b.state = (bleState)(x) - if b.ready && b.wantScan { - fmt.Printf("Go: Scanning\n") - b.scan() - b.wantScan = false - } -} +//connectPeripheral attempts to connect to a Peripheral func (b *BLE) connectPeripheral(x Peripheral) { fmt.Printf("BLE.Connect(): calling cm.ConnectPeripheral(%p)\n", x.p.Ptr()) b.handle.cm.ConnectPeripheral(x.p, nil) } -func CancelConnection(p Peripheral) { - b := peripheralLookup(p) - if b == nil { - return - } - b.connections.Lock() - var ch chan string; - for _, item := range b.connections.items { - if item.p.Identifier == p.Identifier { - ch = item.state - break - } - } - b.connections.Unlock() - if ch != nil { - ch <- "cancel" - } -} - +//cancelConnection cancels an in-progress connection attempt func (b *BLE) cancelConnection(p Peripheral) { b.handle.cm.CancelPeripheralConnection(p.p) } +//knownPeripheral returns a Peripheral that is known to the system without +//scanning func (b *BLE) knownPeripheral(p Peripheral) (Peripheral, bool) { fmt.Printf("RetrievePeripheralsWithIdentifiers\n") ps := b.handle.cm.RetrievePeripheralsWithIdentifiers(ns.NSArrayWithObjects(ns.NSUUIDAlloc().InitWithUUIDString(ns.NSStringWithGoString(p.Identifier)))) @@ -162,10 +125,72 @@ func (b *BLE) knownPeripheral(p Peripheral) (Peripheral, bool) { return newPeripheral(cbp), true } else { fmt.Printf("--none found\n") - return Peripheral{}, false + return p, false } } +//DiscoverServices asks a Peripheral for its Services +func (x Peripheral) DiscoverServices() { + fmt.Printf("Discovering services on %s\n", x.Name) + + // discover all services on this device + x.p.DiscoverServices(nil) +} + +//DiscoverCharacteristics asks a Peripheral for the Characteristics related +//to a Service +func (p Peripheral) DiscoverCharacteristics(serv Service) { + p.p.DiscoverCharacteristics(nil, serv) +} + +//SetNotifyValue subscribes to a characteristic +func (p Peripheral) SetNotifyValue(c Characteristic) { + p.p.SetNotifyValue(1, c) +} + +//NewBLE returns a pointer to a BLE struct after setting up the OS +//Bluetooth API. +func NewBLE() *BLE { + ps := Peripherals{items: make([]PeripheralListItem, 0)} + ble := &BLE{events: make(chan interface{}), peripherals: ps} + + queue := ns.DispatchQueueCreate(ns.CharWithGoString("go_hrm_queue"), nil) + + cd := ns.CBDelegateAlloc() + + cd.CentralManagerDidUpdateStateCallback(didUpdateState) + cd.CentralManagerDidDiscoverPeripheralCallback(didDiscoverPeripheral) + cd.CentralManagerDidConnectPeripheralCallback(didConnectPeripheral) + cd.PeripheralDidDiscoverServicesCallback(didDiscoverServices) + cd.PeripheralDidDiscoverCharacteristicsForServiceCallback(didDiscoverCharacteristics) + cd.PeripheralDidUpdateValueForCharacteristicCallback(didUpdateValue) + + + ble.handle.cd = cd + if cdLookup == nil { + cdLookup = make(map[unsafe.Pointer]*BLE, 0) + } + if pdLookup == nil { + pdLookup = make(map[unsafe.Pointer]*BLE, 0) + } + + // We defined our own queue because this won't work on the main queue. + ble.handle.cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(cd, queue) + cdLookup[ble.handle.cm.Ptr()] = ble + + // For debugging purposes, run GC every second to make sure things are + // not over-released. + go func() { + for { + runtime.GC() + time.Sleep(time.Second * 30) + } + }() + return ble +} + +// Core Bluetooth callback functions + func didUpdateState(c *ns.CBCentralManager) { b := cdLookup[c.Ptr()] st := c.CBManager.State() @@ -239,15 +264,6 @@ func didConnectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) { fmt.Printf("Go: didConnectPeripheral returning\n") } -func (x Peripheral) DiscoverServices() { - fmt.Printf("Discovering services on %s\n", x.Name) - p := x.p - - // discover all services on this device - - p.DiscoverServices(nil) -} - func didDiscoverServices(p *ns.CBPeripheral, e *ns.NSError) { b := pdLookup[p.Ptr()] fmt.Printf("Did discover services\n") @@ -264,23 +280,6 @@ func didDiscoverServices(p *ns.CBPeripheral, e *ns.NSError) { fmt.Printf("Go: didDiscoverServices returning\n") } -func hr(d *ns.NSData) int { - if l := int(d.Length()); l < 4 { - return 0 - } - x := C.GoBytes(d.Bytes(), 4) - flags := x[0] - if flags&0x80 != 0 { // uint16 format - return int(binary.BigEndian.Uint16(x[1:2])) - } else { - return int(x[1]) - } -} - -func (p Peripheral) DiscoverCharacteristics(serv *ns.CBService) { - p.p.DiscoverCharacteristics(nil, serv) -} - func didDiscoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) { b := pdLookup[p.Ptr()] fmt.Printf("Did discover characteristics\n") @@ -299,10 +298,6 @@ func didDiscoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSErr fmt.Printf("Go: didDiscoverCharacteristics returning\n") } -func (p Peripheral) SetNotifyValue(c *ns.CBCharacteristic) { - p.p.SetNotifyValue(1, c) -} - func didUpdateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) { b := pdLookup[p.Ptr()] v := chr.Value() @@ -313,41 +308,34 @@ func didUpdateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) } } -func NewBLE() *BLE { - ps := Peripherals{items: make([]PeripheralListItem, 0)} - ble := &BLE{events: make(chan interface{}), peripherals: ps} +// internal convenience functions - queue := ns.DispatchQueueCreate(ns.CharWithGoString("go_hrm_queue"), nil) - - cd := ns.CBDelegateAlloc() - - cd.CentralManagerDidUpdateStateCallback(didUpdateState) - cd.CentralManagerDidDiscoverPeripheralCallback(didDiscoverPeripheral) - cd.CentralManagerDidConnectPeripheralCallback(didConnectPeripheral) - cd.PeripheralDidDiscoverServicesCallback(didDiscoverServices) - cd.PeripheralDidDiscoverCharacteristicsForServiceCallback(didDiscoverCharacteristics) - cd.PeripheralDidUpdateValueForCharacteristicCallback(didUpdateValue) - - - ble.handle.cd = cd - if cdLookup == nil { - cdLookup = make(map[unsafe.Pointer]*BLE, 0) +func peripheralName(p *ns.CBPeripheral) string { + var ret string + nsname := p.Name() + if nsname.Ptr() != nil { + ret = nsname.String() } - if pdLookup == nil { - pdLookup = make(map[unsafe.Pointer]*BLE, 0) - } - - // We defined our own queue because this won't work on the main queue. - ble.handle.cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(cd, queue) - cdLookup[ble.handle.cm.Ptr()] = ble - - // For debugging purposes, run GC every second to make sure things are - // not over-released. - go func() { - for { - runtime.GC() - time.Sleep(time.Second * 30) - } - }() - return ble + return ret } + +func (x *Peripheral) Retain() { +//NOTE: ns.ObjectAtIndex() calls SetFinalizer for us, which will be a problem +//later when we call GC(), so we always first clear the finalizer before +//setting a new one. + runtime.SetFinalizer(x.p, nil) + x.p.Retain() + x.p.GC() +} + +func (b *BLE) setState(x ns.CBManagerState) { + b.Lock() + defer b.Unlock() + b.state = (bleState)(x) + if b.ready && b.wantScan { + fmt.Printf("Go: Scanning\n") + b.scan() + b.wantScan = false + } +} +