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:
commit
b3e1bb09ee
34
examples/ble.go
Normal file
34
examples/ble.go
Normal 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
146
examples/gatt_explorer.go
Normal 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
13
platforms/ble/LICENSE
Normal 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
34
platforms/ble/README.md
Normal 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
61
platforms/ble/battery.go
Normal 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
|
||||
}
|
242
platforms/ble/ble_adaptor.go
Normal file
242
platforms/ble/ble_adaptor.go
Normal 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")
|
||||
}
|
||||
|
21
platforms/ble/ble_adaptor_test.go
Normal file
21
platforms/ble/ble_adaptor_test.go
Normal 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")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user