1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-05-08 19:29:16 +08:00

Merge branch 'feature/ble' into feature/ble-wip

This commit is contained in:
deadprogram 2016-02-20 19:56:42 -08:00
commit b3e1bb09ee
7 changed files with 551 additions and 0 deletions

34
examples/ble.go Normal file
View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"time"
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot/platforms/ble"
)
func main() {
gbot := gobot.NewGobot()
bleAdaptor := ble.NewBLEAdaptor("ble", "20:73:77:65:43:21")
battery := ble.NewBLEBatteryDriver(bleAdaptor, "battery")
work := func() {
fmt.Println("Working...")
gobot.After(3*time.Second, func() {
fmt.Println(battery.GetBatteryLevel())
})
}
robot := gobot.NewRobot("bleBot",
[]gobot.Connection{bleAdaptor},
[]gobot.Device{battery},
work,
)
gbot.AddRobot(robot)
gbot.Start()
}

146
examples/gatt_explorer.go Normal file
View File

@ -0,0 +1,146 @@
// +build
package main
import (
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/paypal/gatt"
"github.com/paypal/gatt/examples/option"
)
var done = make(chan struct{})
func onStateChanged(d gatt.Device, s gatt.State) {
fmt.Println("State:", s)
switch s {
case gatt.StatePoweredOn:
fmt.Println("Scanning...")
d.Scan([]gatt.UUID{}, false)
return
default:
d.StopScanning()
}
}
func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
id := strings.ToUpper(flag.Args()[0])
if strings.ToUpper(p.ID()) != id {
return
}
// Stop scanning once we've got the peripheral we're looking for.
p.Device().StopScanning()
fmt.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
fmt.Println(" Local Name =", a.LocalName)
fmt.Println(" TX Power Level =", a.TxPowerLevel)
fmt.Println(" Manufacturer Data =", a.ManufacturerData)
fmt.Println(" Service Data =", a.ServiceData)
fmt.Println("")
p.Device().Connect(p)
}
func onPeriphConnected(p gatt.Peripheral, err error) {
fmt.Println("Connected")
defer p.Device().CancelConnection(p)
// Discovery services
ss, err := p.DiscoverServices(nil)
if err != nil {
fmt.Printf("Failed to discover services, err: %s\n", err)
return
}
for _, s := range ss {
msg := "Service: " + s.UUID().String()
if len(s.Name()) > 0 {
msg += " (" + s.Name() + ")"
}
fmt.Println(msg)
// Discovery characteristics
cs, err := p.DiscoverCharacteristics(nil, s)
if err != nil {
fmt.Printf("Failed to discover characteristics, err: %s\n", err)
continue
}
for _, c := range cs {
msg := " Characteristic " + c.UUID().String()
if len(c.Name()) > 0 {
msg += " (" + c.Name() + ")"
}
msg += "\n properties " + c.Properties().String()
fmt.Println(msg)
// Read the characteristic, if possible.
if (c.Properties() & gatt.CharRead) != 0 {
b, err := p.ReadCharacteristic(c)
if err != nil {
fmt.Printf("Failed to read characteristic, err: %s\n", err)
continue
}
fmt.Printf(" value %x | %q\n", b, b)
}
// Discovery descriptors
ds, err := p.DiscoverDescriptors(nil, c)
if err != nil {
fmt.Printf("Failed to discover descriptors, err: %s\n", err)
continue
}
for _, d := range ds {
msg := " Descriptor " + d.UUID().String()
if len(d.Name()) > 0 {
msg += " (" + d.Name() + ")"
}
fmt.Println(msg)
// Read descriptor (could fail, if it's not readable)
b, err := p.ReadDescriptor(d)
if err != nil {
fmt.Printf("Failed to read descriptor, err: %s\n", err)
continue
}
fmt.Printf(" value %x | %q\n", b, b)
}
}
fmt.Println()
}
}
func onPeriphDisconnected(p gatt.Peripheral, err error) {
fmt.Println("Disconnected")
close(done)
}
func main() {
flag.Parse()
if len(flag.Args()) != 1 {
log.Fatalf("usage: %s [options] peripheral-id\n", os.Args[0])
}
d, err := gatt.NewDevice(option.DefaultClientOptions...)
if err != nil {
log.Fatalf("Failed to open device, err: %s\n", err)
return
}
// Register handlers.
d.Handle(
gatt.PeripheralDiscovered(onPeriphDiscovered),
gatt.PeripheralConnected(onPeriphConnected),
gatt.PeripheralDisconnected(onPeriphDisconnected),
)
d.Init(onStateChanged)
<-done
fmt.Println("Done")
}

13
platforms/ble/LICENSE Normal file
View File

@ -0,0 +1,13 @@
Copyright (c) 2015 The Hybrid Group
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

34
platforms/ble/README.md Normal file
View File

@ -0,0 +1,34 @@
# Bluetooth LE
The gobot-ble adaptor makes it easy to interact with Bluetooth LE using Go.
It is written using the [gatt](https://github.com/paypal/gatt) package from [Paypal](https://github.com/paypal). Thank you!
Learn more about Bluetooth LE [here](http://en.wikipedia.org/wiki/Bluetooth_low_energy).
## How to Install
```
go get github.com/hybridgroup/gobot && go install github.com/hybridgroup/gobot/platforms/ble
```
## How To Connect
### OSX
Info here...
### Ubuntu
Info here...
### Windows
Info here...
## How to Use
Example of a simple program...
```go
...
```

61
platforms/ble/battery.go Normal file
View File

@ -0,0 +1,61 @@
package ble
import (
"bytes"
"github.com/hybridgroup/gobot"
)
var _ gobot.Driver = (*BLEBatteryDriver)(nil)
type BLEBatteryDriver struct {
name string
connection gobot.Connection
gobot.Eventer
}
// NewBLEBatteryDriver creates a BLEBatteryDriver by name
func NewBLEBatteryDriver(a *BLEAdaptor, name string) *BLEBatteryDriver {
n := &BLEBatteryDriver{
name: name,
connection: a,
Eventer: gobot.NewEventer(),
}
// n.AddEvent("extended")
// n.AddEvent("signal")
// n.AddEvent("attention")
// n.AddEvent("meditation")
// n.AddEvent("blink")
// n.AddEvent("wave")
// n.AddEvent("eeg")
// n.AddEvent("error")
return n
}
func (b *BLEBatteryDriver) Connection() gobot.Connection { return b.connection }
func (b *BLEBatteryDriver) Name() string { return b.name }
// adaptor returns BLE adaptor
func (b *BLEBatteryDriver) adaptor() *BLEAdaptor {
return b.Connection().(*BLEAdaptor)
}
// Start tells driver to get ready to do work
func (b *BLEBatteryDriver) Start() (errs []error) {
return
}
// Halt stops battery driver (void)
func (b *BLEBatteryDriver) Halt() (errs []error) { return }
func (b *BLEBatteryDriver) GetBatteryLevel() (level uint8) {
var l uint8
c, _ := b.adaptor().ReadCharacteristic("180f", "2a19")
buf := bytes.NewBuffer(<-c)
val, _ := buf.ReadByte()
l = uint8(val)
return l
}

View File

@ -0,0 +1,242 @@
package ble
import (
"fmt"
"log"
"strings"
"github.com/hybridgroup/gobot"
"github.com/paypal/gatt"
)
// TODO: handle other OS defaults besides Linux
var DefaultClientOptions = []gatt.Option{
gatt.LnxMaxConnections(1),
gatt.LnxDeviceID(-1, false),
}
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
//sp io.ReadWriteCloser
connected bool
//connect func(string) (io.ReadWriteCloser, error)
}
// NewBLEAdaptor returns a new BLEAdaptor given a name and uuid
func NewBLEAdaptor(name string, uuid string) *BLEAdaptor {
return &BLEAdaptor{
name: name,
uuid: uuid,
connected: false,
// connect: func(port string) (io.ReadWriteCloser, error) {
// return serial.OpenPort(&serial.Config{Name: port, Baud: 115200})
// },
}
}
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 }
// 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.onDiscovered),
//gatt.PeripheralConnected(b.onConnected),
gatt.PeripheralDisconnected(b.onDisconnected),
)
device.Init(b.onStateChanged)
// 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) {
// if a.connected {
// if err := a.sp.Close(); err != nil {
// return []error{err}
// }
// a.connected = false
// }
return
}
// Finalize finalizes the BLEAdaptor
func (b *BLEAdaptor) Finalize() (errs []error) {
return b.Disconnect()
}
// ReadCharacteristic returns bytes from the BLE device for the
// requested service and characteristic
func (b *BLEAdaptor) ReadCharacteristic(sUUID string, cUUID string) (data chan []byte, err error) {
//defer b.peripheral.Device().CancelConnection(b.peripheral)
fmt.Println("ReadCharacteristic")
if !b.connected {
log.Fatalf("Cannot read from BLE device until connected")
return
}
c := make(chan []byte)
f := func(p gatt.Peripheral, e error) {
b.performRead(c, sUUID, cUUID)
}
b.device.Handle(
gatt.PeripheralConnected(f),
)
b.peripheral.Device().Connect(b.peripheral)
return c, nil
}
func (b *BLEAdaptor) performRead(c chan []byte, sUUID string, cUUID string) {
fmt.Println("performRead")
fmt.Printf("%x", b.Peripheral())
s := b.getService(sUUID)
characteristic := b.getCharacteristic(s, cUUID)
val, err := b.peripheral.ReadCharacteristic(characteristic)
if err != nil {
fmt.Printf("Failed to read characteristic, err: %s\n", err)
c <- []byte{}
}
c <- val
}
func (b *BLEAdaptor) getPeripheral() {
}
func (b *BLEAdaptor) getService(sUUID string) (service *gatt.Service) {
fmt.Println("getService")
ss, err := b.Peripheral().DiscoverServices(nil)
fmt.Println(ss)
fmt.Println("yo")
fmt.Println(err)
if err != nil {
fmt.Printf("Failed to discover services, err: %s\n", err)
return
}
fmt.Println("service")
for _, s := range ss {
msg := "Service: " + s.UUID().String()
if len(s.Name()) > 0 {
msg += " (" + s.Name() + ")"
}
fmt.Println(msg)
id := strings.ToUpper(s.UUID().String())
if strings.ToUpper(sUUID) != id {
continue
}
msg = "Found Service: " + s.UUID().String()
if len(s.Name()) > 0 {
msg += " (" + s.Name() + ")"
}
fmt.Println(msg)
return s
}
fmt.Println("getService: none found")
return
}
func (b *BLEAdaptor) getCharacteristic(s *gatt.Service, cUUID string) (c *gatt.Characteristic) {
fmt.Println("getCharacteristic")
cs, err := b.Peripheral().DiscoverCharacteristics(nil, s)
if err != nil {
fmt.Printf("Failed to discover characteristics, err: %s\n", err)
return
}
for _, char := range cs {
id := strings.ToUpper(char.UUID().String())
if strings.ToUpper(cUUID) != id {
continue
}
msg := " Found Characteristic " + char.UUID().String()
if len(char.Name()) > 0 {
msg += " (" + char.Name() + ")"
}
msg += "\n properties " + char.Properties().String()
fmt.Println(msg)
return char
}
return nil
}
func (b *BLEAdaptor) onStateChanged(d gatt.Device, s gatt.State) {
fmt.Println("State:", s)
switch s {
case gatt.StatePoweredOn:
fmt.Println("scanning...")
d.Scan([]gatt.UUID{}, false)
return
default:
d.StopScanning()
}
}
func (b *BLEAdaptor) onDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
fmt.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
id := strings.ToUpper(b.UUID())
if strings.ToUpper(p.ID()) != id {
return
}
b.connected = true
b.peripheral = p
// Stop scanning once we've got the peripheral we're looking for.
p.Device().StopScanning()
fmt.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name())
fmt.Println(" Local Name =", a.LocalName)
fmt.Println(" TX Power Level =", a.TxPowerLevel)
fmt.Println(" Manufacturer Data =", a.ManufacturerData)
fmt.Println(" Service Data =", a.ServiceData)
fmt.Println("")
}
func (b *BLEAdaptor) onConnected(p gatt.Peripheral, err error) {
fmt.Println("Connected")
defer p.Device().CancelConnection(p)
}
func (b *BLEAdaptor) onDisconnected(p gatt.Peripheral, err error) {
fmt.Println("Disconnected")
}

View File

@ -0,0 +1,21 @@
package ble
import (
"testing"
"github.com/hybridgroup/gobot"
)
func initTestBLEAdaptor() *BLEAdaptor {
a := NewBLEAdaptor("bot", "D7:99:5A:26:EC:38")
// a.connect = func(n *BLEAdaptor) (io.ReadWriteCloser, error) {
// return &NullReadWriteCloser{}, nil
// }
return a
}
func TestBLEAdaptor(t *testing.T) {
a := NewBLEAdaptor("bot", "D7:99:5A:26:EC:38")
gobot.Assert(t, a.Name(), "bot")
gobot.Assert(t, a.UUID(), "D7:99:5A:26:EC:38")
}