microbit: add DigitalWriter, DigitalReader, and AnalogReader support
Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
parent
8eedc24c75
commit
ca2854bdf0
|
@ -0,0 +1,36 @@
|
|||
// +build example
|
||||
//
|
||||
// Do not build by default.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/gpio"
|
||||
"gobot.io/x/gobot/platforms/ble"
|
||||
"gobot.io/x/gobot/platforms/microbit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bleAdaptor := ble.NewClientAdaptor(os.Args[1])
|
||||
|
||||
ubit := microbit.NewIOPinDriver(bleAdaptor)
|
||||
led := gpio.NewLedDriver(ubit, "0")
|
||||
|
||||
work := func() {
|
||||
gobot.Every(1*time.Second, func() {
|
||||
led.Toggle()
|
||||
})
|
||||
}
|
||||
|
||||
robot := gobot.NewRobot("bot",
|
||||
[]gobot.Connection{bleAdaptor, ubit},
|
||||
[]gobot.Device{ubit, led},
|
||||
work,
|
||||
)
|
||||
|
||||
robot.Start()
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
package microbit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/platforms/ble"
|
||||
)
|
||||
|
||||
// IOPinDriver is the Gobot driver for the Microbit's built-in digital and
|
||||
// analog I/O
|
||||
type IOPinDriver struct {
|
||||
name string
|
||||
adMask int
|
||||
ioMask int
|
||||
connection gobot.Connection
|
||||
gobot.Eventer
|
||||
}
|
||||
|
||||
const (
|
||||
// BLE services
|
||||
ioPinService = "e95d127b251d470aa062fa1922dfa9a8"
|
||||
|
||||
// BLE characteristics
|
||||
pinDataCharacteristic = "e95d8d00251d470aa062fa1922dfa9a8"
|
||||
pinADConfigCharacteristic = "e95d5899251d470aa062fa1922dfa9a8"
|
||||
pinIOConfigCharacteristic = "e95db9fe251d470aa062fa1922dfa9a8"
|
||||
)
|
||||
|
||||
// PinData has the read data for a specific digital pin
|
||||
type PinData struct {
|
||||
pin uint8
|
||||
value uint8
|
||||
}
|
||||
|
||||
// NewIOPinDriver creates a Microbit IOPinDriver
|
||||
func NewIOPinDriver(a ble.BLEConnector) *IOPinDriver {
|
||||
n := &IOPinDriver{
|
||||
name: gobot.DefaultName("Microbit IO Pins"),
|
||||
connection: a,
|
||||
Eventer: gobot.NewEventer(),
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// Connection returns the BLE connection
|
||||
func (b *IOPinDriver) Connection() gobot.Connection { return b.connection }
|
||||
|
||||
// Name returns the Driver Name
|
||||
func (b *IOPinDriver) Name() string { return b.name }
|
||||
|
||||
// SetName sets the Driver Name
|
||||
func (b *IOPinDriver) SetName(n string) { b.name = n }
|
||||
|
||||
// adaptor returns BLE adaptor
|
||||
func (b *IOPinDriver) adaptor() ble.BLEConnector {
|
||||
return b.Connection().(ble.BLEConnector)
|
||||
}
|
||||
|
||||
// Start tells driver to get ready to do work
|
||||
func (b *IOPinDriver) Start() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Halt stops driver (void)
|
||||
func (b *IOPinDriver) Halt() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ReadAllPinData reads and returns the pin data for all pins
|
||||
func (b *IOPinDriver) ReadAllPinData() (pins []PinData) {
|
||||
c, _ := b.adaptor().ReadCharacteristic(pinDataCharacteristic)
|
||||
buf := bytes.NewBuffer(c)
|
||||
pinsData := make([]PinData, buf.Len()/2)
|
||||
|
||||
for i := 0; i < buf.Len()/2; i++ {
|
||||
pinData := PinData{}
|
||||
pinData.pin, _ = buf.ReadByte()
|
||||
pinData.value, _ = buf.ReadByte()
|
||||
pinsData[i] = pinData
|
||||
}
|
||||
|
||||
return pinsData
|
||||
}
|
||||
|
||||
// WritePinData writes the pin data for a single pin
|
||||
func (b *IOPinDriver) WritePinData(pin string, data byte) (err error) {
|
||||
i, err := strconv.Atoi(pin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := []byte{byte(i), data}
|
||||
err = b.adaptor().WriteCharacteristic(pinDataCharacteristic, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadPinADConfig reads and returns the pin A/D config mask for all pins
|
||||
func (b *IOPinDriver) ReadPinADConfig() (config uint32, err error) {
|
||||
var result uint32
|
||||
c, e := b.adaptor().ReadCharacteristic(pinADConfigCharacteristic)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
buf := bytes.NewBuffer(c)
|
||||
binary.Read(buf, binary.LittleEndian, result)
|
||||
b.adMask = int(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// WritePinADConfig writes the pin A/D config mask for all pins
|
||||
func (b *IOPinDriver) WritePinADConfig(config int) (err error) {
|
||||
b.adMask = config
|
||||
data := &bytes.Buffer{}
|
||||
binary.Write(data, binary.LittleEndian, uint32(config))
|
||||
err = b.adaptor().WriteCharacteristic(pinADConfigCharacteristic, data.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// ReadPinIOConfig reads and returns the pin IO config mask for all pins
|
||||
func (b *IOPinDriver) ReadPinIOConfig() (config int, err error) {
|
||||
var result uint32
|
||||
c, e := b.adaptor().ReadCharacteristic(pinIOConfigCharacteristic)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
buf := bytes.NewBuffer(c)
|
||||
binary.Read(buf, binary.LittleEndian, result)
|
||||
b.ioMask = int(result)
|
||||
return int(result), nil
|
||||
}
|
||||
|
||||
// WritePinIOConfig writes the pin I/O config mask for all pins
|
||||
func (b *IOPinDriver) WritePinIOConfig(config int) (err error) {
|
||||
b.ioMask = config
|
||||
data := &bytes.Buffer{}
|
||||
binary.Write(data, binary.LittleEndian, uint32(config))
|
||||
err = b.adaptor().WriteCharacteristic(pinIOConfigCharacteristic, data.Bytes())
|
||||
return
|
||||
}
|
||||
|
||||
// Connect here to allow Driver to also act as an Adaptor
|
||||
func (b *IOPinDriver) Connect() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize here to allow Driver to also act as an Adaptor
|
||||
func (b *IOPinDriver) Finalize() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DigitalRead reads from a pin
|
||||
func (b *IOPinDriver) DigitalRead(pin string) (val int, err error) {
|
||||
p, err := strconv.Atoi(pin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.ensureDigital(p)
|
||||
b.ensureInput(p)
|
||||
|
||||
pins := b.ReadAllPinData()
|
||||
return int(pins[p].value), nil
|
||||
}
|
||||
|
||||
// DigitalWrite writes to a pin
|
||||
func (b *IOPinDriver) DigitalWrite(pin string, level byte) (err error) {
|
||||
p, err := strconv.Atoi(pin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.ensureDigital(p)
|
||||
b.ensureOutput(p)
|
||||
|
||||
return b.WritePinData(pin, level)
|
||||
}
|
||||
|
||||
// AnalogRead reads from a pin
|
||||
func (b *IOPinDriver) AnalogRead(pin string) (val int, err error) {
|
||||
p, err := strconv.Atoi(pin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.ensureAnalog(p)
|
||||
b.ensureInput(p)
|
||||
|
||||
pins := b.ReadAllPinData()
|
||||
return int(pins[p].value), nil
|
||||
}
|
||||
|
||||
func (b *IOPinDriver) ensureDigital(pin int) {
|
||||
if hasBit(b.adMask, pin) {
|
||||
b.WritePinADConfig(clearBit(b.adMask, pin))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IOPinDriver) ensureAnalog(pin int) {
|
||||
if !hasBit(b.adMask, pin) {
|
||||
b.WritePinADConfig(setBit(b.adMask, pin))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IOPinDriver) ensureInput(pin int) {
|
||||
if !hasBit(b.ioMask, pin) {
|
||||
b.WritePinIOConfig(setBit(b.ioMask, pin))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *IOPinDriver) ensureOutput(pin int) {
|
||||
if hasBit(b.ioMask, pin) {
|
||||
b.WritePinIOConfig(clearBit(b.ioMask, pin))
|
||||
}
|
||||
}
|
||||
|
||||
// via http://stackoverflow.com/questions/23192262/how-would-you-set-and-clear-a-single-bit-in-go
|
||||
|
||||
// Sets the bit at pos in the integer n.
|
||||
func setBit(n int, pos int) int {
|
||||
n |= (1 << uint(pos))
|
||||
return n
|
||||
}
|
||||
|
||||
// Test if the bit at pos is set in the integer n.
|
||||
func hasBit(n int, pos int) bool {
|
||||
val := n & (1 << uint(pos))
|
||||
return (val > 0)
|
||||
}
|
||||
|
||||
// Clears the bit at pos in n.
|
||||
func clearBit(n int, pos int) int {
|
||||
return n &^ (1 << uint(pos))
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package microbit
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gobot.io/x/gobot"
|
||||
"gobot.io/x/gobot/drivers/aio"
|
||||
"gobot.io/x/gobot/drivers/gpio"
|
||||
"gobot.io/x/gobot/gobottest"
|
||||
)
|
||||
|
||||
// the IOPinDriver is a Driver
|
||||
var _ gobot.Driver = (*IOPinDriver)(nil)
|
||||
|
||||
// and is also an Adaptor
|
||||
var _ gobot.Adaptor = (*IOPinDriver)(nil)
|
||||
|
||||
// that supports the DigitalReader, DigitalWriter, & AnalogReader interfaces
|
||||
var _ gpio.DigitalReader = (*IOPinDriver)(nil)
|
||||
var _ gpio.DigitalWriter = (*IOPinDriver)(nil)
|
||||
var _ aio.AnalogReader = (*IOPinDriver)(nil)
|
||||
|
||||
func initTestIOPinDriver() *IOPinDriver {
|
||||
d := NewIOPinDriver(NewBleTestAdaptor())
|
||||
return d
|
||||
}
|
||||
|
||||
func TestIOPinDriver(t *testing.T) {
|
||||
d := initTestIOPinDriver()
|
||||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "Microbit IO Pin"), true)
|
||||
d.SetName("NewName")
|
||||
gobottest.Assert(t, d.Name(), "NewName")
|
||||
}
|
||||
|
||||
func TestIOPinDriverStartAndHalt(t *testing.T) {
|
||||
d := initTestIOPinDriver()
|
||||
gobottest.Assert(t, d.Start(), nil)
|
||||
gobottest.Assert(t, d.Halt(), nil)
|
||||
}
|
||||
|
||||
func TestIOPinDriverDigitalRead(t *testing.T) {
|
||||
a := NewBleTestAdaptor()
|
||||
d := NewIOPinDriver(a)
|
||||
a.TestReadCharacteristic(func(cUUID string) ([]byte, error) {
|
||||
return []byte{0, 1, 1, 0, 2, 1}, nil
|
||||
})
|
||||
|
||||
val, _ := d.DigitalRead("0")
|
||||
gobottest.Assert(t, val, 1)
|
||||
|
||||
val, _ = d.DigitalRead("1")
|
||||
gobottest.Assert(t, val, 0)
|
||||
}
|
||||
|
||||
func TestIOPinDriverDigitalWrite(t *testing.T) {
|
||||
a := NewBleTestAdaptor()
|
||||
d := NewIOPinDriver(a)
|
||||
|
||||
// TODO: a better test
|
||||
gobottest.Assert(t, d.DigitalWrite("0", 1), nil)
|
||||
}
|
||||
|
||||
func TestIOPinDriverAnalogRead(t *testing.T) {
|
||||
a := NewBleTestAdaptor()
|
||||
d := NewIOPinDriver(a)
|
||||
a.TestReadCharacteristic(func(cUUID string) ([]byte, error) {
|
||||
return []byte{0, 0, 1, 128, 2, 1}, nil
|
||||
})
|
||||
|
||||
val, _ := d.AnalogRead("0")
|
||||
gobottest.Assert(t, val, 0)
|
||||
|
||||
val, _ = d.AnalogRead("1")
|
||||
gobottest.Assert(t, val, 128)
|
||||
}
|
Loading…
Reference in New Issue