diff --git a/ble_darwin.go b/ble_darwin.go index 154872f..3d1e59c 100644 --- a/ble_darwin.go +++ b/ble_darwin.go @@ -14,31 +14,30 @@ import ( ) type BLE struct { - events chan interface{} - - state ns.CBManagerState - + state ns.CBManagerState + events chan interface{} peripherals Peripherals hr int + cd *ns.CBDelegate + cm *ns.CBCentralManager ready, wantScan bool sync.Mutex - cd *ns.CBDelegate - cm *ns.CBCentralManager + connections Connections } -var cdLookup map[unsafe.Pointer] *BLE -var pdLookup map[unsafe.Pointer] *BLE +var cdLookup map[unsafe.Pointer]*BLE +var pdLookup map[unsafe.Pointer]*BLE type State string type Peripheral struct { - Name string - RSSI int + Name string + RSSI int Identifier string - p *ns.CBPeripheral + p *ns.CBPeripheral } func peripheralName(p *ns.CBPeripheral) string { @@ -52,14 +51,14 @@ func peripheralName(p *ns.CBPeripheral) string { func newPeripheral(x *ns.CBPeripheral) Peripheral { return Peripheral{ - Name: peripheralName(x), + Name: peripheralName(x), Identifier: x.Identifier().UUIDString().String(), - p: x, + p: x, } } type PeripheralListItem struct { - p Peripheral + p Peripheral seen time.Time } @@ -68,11 +67,34 @@ type Peripherals struct { sync.Mutex } +type ConnectionListItem struct { + p Peripheral + state chan string +} + +type Connections struct { + items []ConnectionListItem + close chan struct{} + sync.Mutex +} + +func (c *Connections) UpdateState(p Peripheral, s string) { + var ch chan string + c.Lock() + for _, item := range c.items { + if p.Identifier == item.p.Identifier { + ch = item.state + } + } + c.Unlock() + ch <- s +} + func (ps *Peripherals) Add(x Peripheral) bool { ps.Lock() defer ps.Unlock() found := false - for n,item := range ps.items { + for n, item := range ps.items { if item.p.Identifier == x.Identifier { item.p = x item.seen = time.Now() @@ -87,11 +109,28 @@ func (ps *Peripherals) Add(x Peripheral) bool { //take ownership of this Objective-C object x.p.Retain() x.p.GC() - item := PeripheralListItem {p: x, seen: time.Now()} + item := PeripheralListItem{p: x, seen: time.Now()} ps.items = append(ps.items, item) return true } +func (cs *Connections) Add(x ConnectionListItem) bool { + cs.Lock() + defer cs.Unlock() + found := false + for _, item := range cs.items { + if item.p.Identifier == x.p.Identifier { + // are we allowed to have multiple connections to the same peripheral? + return false + } + } + if found { + return false + } + cs.items = append(cs.items, x) + return true +} + type UpdateStateEvent struct { State string } @@ -103,6 +142,9 @@ type DiscoverEvent struct { type ConnectEvent struct { Peripheral Peripheral } +type ConnectTimeoutEvent struct { + Peripheral Peripheral +} func (b *BLE) Events() chan interface{} { return b.events @@ -174,6 +216,31 @@ func (b *BLE) StopScan() { b.cm.StopScan() } +func connectTracker(b *BLE, x ConnectionListItem) { + tick := time.NewTicker(time.Second * 30) + select { + case <-b.connections.close: + fmt.Printf("Closing connection to %s\n", x.p.Name) + b.cm.CancelPeripheralConnection(x.p.p) + case <-tick.C: + fmt.Printf("Connection to %s timed out\n", x.p.Name) + b.cm.CancelPeripheralConnection(x.p.p) + b.connections.Lock() + for n, item := range b.connections.items { + if item.p.Identifier == x.p.Identifier { + b.connections.items = append(b.connections.items[:n], b.connections.items[n+1:]...) + } + } + b.connections.Unlock() + b.events <- ConnectTimeoutEvent{x.p} + case state := <-x.state: + fmt.Printf("connectTracker: state\n") + if state == "connected" { + fmt.Printf("--connected") + } + } +} + func (b *BLE) Connect(p Peripheral) { b.Lock() if !b.ready { @@ -182,40 +249,56 @@ func (b *BLE) Connect(p Peripheral) { } b.Unlock() if p.p == nil { + fmt.Printf("RetrievePeripheralsWithIdentifiers\n") ps := b.cm.RetrievePeripheralsWithIdentifiers(ns.NSArrayWithObjects(ns.NSUUIDAlloc().InitWithUUIDString(ns.NSStringWithGoString(p.Identifier)))) - if (int)(ps.Count()) > 0 { + if x := (int)(ps.Count()); x > 0 { + fmt.Printf("--found %d\n", x) p.p = ps.ObjectAtIndex(0).CBPeripheral() } else { + fmt.Printf("--none found\n") return } } + item := ConnectionListItem{p, make(chan string)} + b.connections.Add(item) + go connectTracker(b, item) + //time.Sleep(time.Second/10) b.cm.ConnectPeripheral(p.p, nil) fmt.Printf("cm.ConnectPeripheral() returned\n") } func updateState(c *ns.CBCentralManager) { - ble := cdLookup[c.Ptr()] + b := cdLookup[c.Ptr()] fmt.Printf("Go: did update state\n") st := c.CBManager.State() if st == (ns.CBManagerState)(ns.CBManagerStatePoweredOn) { - ble.ready = true + b.ready = true + b.connections.close = make(chan struct{}) } else { - ble.ready = false + if b.ready { + close(b.connections.close) + for _, item := range b.connections.items { + fmt.Printf("Closing connection to %s\n", item.p.Name) + b.cm.CancelPeripheralConnection(item.p.p) + } + b.connections.items = b.connections.items[:0] + b.ready = false + } } - ble.setState(st) - ble.events <- UpdateStateEvent{State: stringState(st)} + b.setState(st) + b.events <- UpdateStateEvent{State: stringState(st)} } func discoverPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral, d *ns.NSDictionary, rssi *ns.NSNumber) { - ble := cdLookup[c.Ptr()] + b := cdLookup[c.Ptr()] peripheral := newPeripheral(p) peripheral.RSSI = (int)(rssi.IntValue()) if peripheral.Name == "" { return } - if ok := ble.peripherals.Add(peripheral); ok { - pdLookup[p.Ptr()] = ble - ble.events <- DiscoverEvent{Peripheral: peripheral} + if ok := b.peripherals.Add(peripheral); ok { + pdLookup[p.Ptr()] = b + b.events <- DiscoverEvent{Peripheral: peripheral} } } @@ -247,6 +330,7 @@ func connectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) { } } + b.connections.UpdateState(peripheral, "connected") b.events <- ConnectEvent{peripheral} fmt.Printf("Go: connectPeripheral returning\n") } @@ -260,7 +344,6 @@ func DiscoverServices(x Peripheral) { p.DiscoverServices(nil) } - func discoverServices(p *ns.CBPeripheral, e *ns.NSError) { fmt.Printf("Did discover services\n") p.Services().ObjectEnumerator().ForIn(func(o *ns.Id) bool { @@ -327,15 +410,15 @@ func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) { } var ( - hrm_uuid *ns.CBUUID - hrv_uuid *ns.CBUUID - info_uuid *ns.CBUUID + hrm_uuid *ns.CBUUID + hrv_uuid *ns.CBUUID + info_uuid *ns.CBUUID gble *BLE ) func NewBLE() *BLE { - ps := Peripherals{items: make([]PeripheralListItem,0)} + ps := Peripherals{items: make([]PeripheralListItem, 0)} ble := &BLE{events: make(chan interface{}), peripherals: ps} gble = ble @@ -356,10 +439,10 @@ func NewBLE() *BLE { ble.cd = cd if cdLookup == nil { - cdLookup = make(map[unsafe.Pointer]*BLE,0) + cdLookup = make(map[unsafe.Pointer]*BLE, 0) } if pdLookup == nil { - pdLookup = make(map[unsafe.Pointer]*BLE,0) + pdLookup = make(map[unsafe.Pointer]*BLE, 0) } // We defined our own queue because this won't work on the main queue.