hybridgroup.gobot/platforms/ble/ble_adaptor.go

222 lines
5.6 KiB
Go
Raw Normal View History

2015-06-08 03:38:19 +08:00
package ble
import (
"fmt"
"github.com/currantlabs/gatt"
"github.com/hybridgroup/gobot"
2016-03-03 14:00:05 +08:00
"log"
"strings"
2015-06-08 03:38:19 +08:00
)
var _ gobot.Adaptor = (*BLEAdaptor)(nil)
// Represents a Connection to a BLE Peripheral
type BLEAdaptor struct {
name string
uuid string
device gatt.Device
peripheral gatt.Peripheral
services map[string]*BLEService
connected bool
ready chan struct{}
2015-06-08 03:38:19 +08:00
}
// NewBLEAdaptor returns a new BLEAdaptor given a name and uuid
func NewBLEAdaptor(name string, uuid string) *BLEAdaptor {
return &BLEAdaptor{
2016-03-03 14:00:05 +08:00
name: name,
uuid: uuid,
2015-06-30 00:47:18 +08:00
connected: false,
ready: make(chan struct{}),
services: make(map[string]*BLEService),
2015-06-08 03:38:19 +08:00
}
}
2016-03-03 14:00:05 +08:00
func (b *BLEAdaptor) Name() string { return b.name }
func (b *BLEAdaptor) UUID() string { return b.uuid }
func (b *BLEAdaptor) Peripheral() gatt.Peripheral { return b.peripheral }
2015-06-08 03:38:19 +08:00
// Connect initiates a connection to the BLE peripheral. Returns true on successful connection.
func (b *BLEAdaptor) Connect() (errs []error) {
device, err := gatt.NewDevice(DefaultClientOptions...)
if err != nil {
log.Fatalf("Failed to open BLE device, err: %s\n", err)
return
}
b.device = device
// Register handlers.
device.Handle(
gatt.PeripheralDiscovered(b.DiscoveryHandler),
gatt.PeripheralConnected(b.ConnectHandler),
gatt.PeripheralDisconnected(b.DisconnectHandler),
2015-06-08 03:38:19 +08:00
)
device.Init(b.StateChangeHandler)
2016-03-03 14:00:05 +08:00
<-b.ready
2015-06-08 03:38:19 +08:00
// TODO: make sure peripheral currently exists for this UUID before returning
return nil
}
// Reconnect attempts to reconnect to the BLE peripheral. If it has an active connection
// it will first close that connection and then establish a new connection.
// Returns true on Successful reconnection
func (b *BLEAdaptor) Reconnect() (errs []error) {
if b.connected {
b.Disconnect()
}
return b.Connect()
}
// Disconnect terminates the connection to the BLE peripheral. Returns true on successful disconnect.
func (b *BLEAdaptor) Disconnect() (errs []error) {
b.peripheral.Device().CancelConnection(b.peripheral)
2015-06-08 03:38:19 +08:00
return
}
// Finalize finalizes the BLEAdaptor
func (b *BLEAdaptor) Finalize() (errs []error) {
return b.Disconnect()
}
2016-03-03 14:00:05 +08:00
// ReadCharacteristic returns bytes from the BLE device for the
2015-06-08 03:38:19 +08:00
// requested service and characteristic
func (b *BLEAdaptor) ReadCharacteristic(sUUID string, cUUID string) (data []byte, err error) {
2015-06-30 00:47:18 +08:00
if !b.connected {
log.Fatalf("Cannot read from BLE device until connected")
return
}
2015-06-08 03:38:19 +08:00
2016-03-03 14:00:05 +08:00
characteristic := b.services[sUUID].characteristics[cUUID]
2015-06-30 00:47:18 +08:00
val, err := b.peripheral.ReadCharacteristic(characteristic)
if err != nil {
fmt.Printf("Failed to read characteristic, err: %s\n", err)
return nil, err
}
return val, nil
2015-06-08 03:38:19 +08:00
}
// WriteCharacteristic writes bytes to the BLE device for the
// requested service and characteristic
func (b *BLEAdaptor) WriteCharacteristic(sUUID string, cUUID string, data []byte) (err error) {
if !b.connected {
log.Fatalf("Cannot write to BLE device until connected")
return
}
characteristic := b.services[sUUID].characteristics[cUUID]
err = b.peripheral.WriteCharacteristic(characteristic, data, true)
if err != nil {
fmt.Printf("Failed to write characteristic, err: %s\n", err)
return err
}
return
}
// SubscribeNotify subscribes to the BLE device for the
// requested service and characteristic
func (b *BLEAdaptor) SubscribeNotify(sUUID string, cUUID string, f func([]byte, error)) (err error) {
if !b.connected {
log.Fatalf("Cannot subscribe to BLE device until connected")
return
}
characteristic := b.services[sUUID].characteristics[cUUID]
fn := func(c *gatt.Characteristic, b []byte, err error) {
f(b, err)
}
err = b.peripheral.SetNotifyValue(characteristic, fn)
if err != nil {
fmt.Printf("Failed to subscribe to characteristic, err: %s\n", err)
return err
}
return
}
func (b *BLEAdaptor) StateChangeHandler(d gatt.Device, s gatt.State) {
2015-06-08 03:38:19 +08:00
fmt.Println("State:", s)
switch s {
case gatt.StatePoweredOn:
fmt.Println("scanning...")
d.Scan([]gatt.UUID{}, false)
return
default:
d.StopScanning()
}
}
func (b *BLEAdaptor) DiscoveryHandler(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
2015-06-08 03:38:19 +08:00
id := strings.ToUpper(b.UUID())
if strings.ToUpper(p.ID()) != id {
return
}
// Stop scanning once we've got the peripheral we're looking for.
p.Device().StopScanning()
2016-03-03 14:00:05 +08:00
// and connect to it
p.Device().Connect(p)
2015-06-08 03:38:19 +08:00
}
func (b *BLEAdaptor) ConnectHandler(p gatt.Peripheral, err error) {
fmt.Printf("\nConnected Peripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
2016-03-03 14:00:05 +08:00
b.peripheral = p
2016-03-03 14:00:05 +08:00
if err := p.SetMTU(500); err != nil {
fmt.Printf("Failed to set MTU, err: %s\n", err)
}
2016-03-03 14:00:05 +08:00
ss, err := p.DiscoverServices(nil)
if err != nil {
fmt.Printf("Failed to discover services, err: %s\n", err)
return
}
2016-03-03 14:00:05 +08:00
for _, s := range ss {
b.services[s.UUID().String()] = NewBLEService(s.UUID().String(), s)
2016-03-03 14:00:05 +08:00
cs, err := p.DiscoverCharacteristics(nil, s)
if err != nil {
fmt.Printf("Failed to discover characteristics, err: %s\n", err)
continue
}
2016-03-03 14:00:05 +08:00
for _, c := range cs {
b.services[s.UUID().String()].characteristics[c.UUID().String()] = c
2016-03-03 14:00:05 +08:00
}
}
2016-03-03 14:00:05 +08:00
b.connected = true
close(b.ready)
//defer p.Device().CancelConnection(p)
2015-06-08 03:38:19 +08:00
}
func (b *BLEAdaptor) DisconnectHandler(p gatt.Peripheral, err error) {
2015-06-08 03:38:19 +08:00
fmt.Println("Disconnected")
}
2016-03-03 14:00:05 +08:00
// Represents a BLE Peripheral's Service
type BLEService struct {
uuid string
service *gatt.Service
characteristics map[string]*gatt.Characteristic
2016-03-03 14:00:05 +08:00
}
// NewBLEAdaptor returns a new BLEService given a uuid
func NewBLEService(sUuid string, service *gatt.Service) *BLEService {
return &BLEService{
uuid: sUuid,
service: service,
2016-03-03 14:00:05 +08:00
characteristics: make(map[string]*gatt.Characteristic),
}
}