2023-10-27 21:06:07 +02:00
|
|
|
//go:build !windows
|
|
|
|
// +build !windows
|
|
|
|
|
2014-04-27 19:56:18 -07:00
|
|
|
package firmata
|
2014-04-26 03:11:51 -07:00
|
|
|
|
|
|
|
import (
|
2017-02-06 14:50:14 +01:00
|
|
|
"fmt"
|
2014-11-18 17:28:20 -08:00
|
|
|
"io"
|
2014-07-10 13:42:18 -07:00
|
|
|
"strconv"
|
2014-07-14 13:34:46 -07:00
|
|
|
"time"
|
2014-07-10 13:42:18 -07:00
|
|
|
|
2020-05-03 23:40:15 +03:00
|
|
|
"go.bug.st/serial"
|
2023-11-15 20:51:52 +01:00
|
|
|
|
2023-05-20 14:25:21 +02:00
|
|
|
"gobot.io/x/gobot/v2"
|
|
|
|
"gobot.io/x/gobot/v2/drivers/i2c"
|
|
|
|
"gobot.io/x/gobot/v2/platforms/firmata/client"
|
2014-04-26 03:11:51 -07:00
|
|
|
)
|
|
|
|
|
2015-01-05 04:22:12 -08:00
|
|
|
type firmataBoard interface {
|
2023-11-12 14:17:02 +01:00
|
|
|
Connect(conn io.ReadWriteCloser) error
|
2015-01-05 04:22:12 -08:00
|
|
|
Disconnect() error
|
|
|
|
Pins() []client.Pin
|
2023-11-15 20:51:52 +01:00
|
|
|
AnalogWrite(pin int, value int) error
|
2023-11-12 14:17:02 +01:00
|
|
|
SetPinMode(pin int, mode int) error
|
|
|
|
ReportAnalog(pin int, state int) error
|
|
|
|
ReportDigital(pin int, state int) error
|
|
|
|
DigitalWrite(pin int, value int) error
|
|
|
|
I2cRead(address int, numBytes int) error
|
|
|
|
I2cWrite(address int, data []byte) error
|
|
|
|
I2cConfig(delay int) error
|
2024-11-01 12:54:20 +01:00
|
|
|
ServoConfig(pin int, maximum int, minimum int) error
|
2017-06-14 00:14:00 +02:00
|
|
|
WriteSysex(data []byte) error
|
2017-02-07 20:50:26 +01:00
|
|
|
gobot.Eventer
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
|
|
|
|
2017-06-14 20:16:46 +02:00
|
|
|
type FirmataAdaptor interface {
|
2023-06-12 19:51:25 +02:00
|
|
|
Connect() error
|
|
|
|
Finalize() error
|
2017-06-14 20:16:46 +02:00
|
|
|
Name() string
|
|
|
|
SetName(n string)
|
|
|
|
WriteSysex(data []byte) error
|
|
|
|
gobot.Eventer
|
|
|
|
}
|
|
|
|
|
2016-09-25 20:49:20 +02:00
|
|
|
// Adaptor is the Gobot Adaptor for Firmata based boards
|
|
|
|
type Adaptor struct {
|
2017-01-15 13:51:07 +01:00
|
|
|
name string
|
|
|
|
port string
|
2017-06-14 13:44:03 +02:00
|
|
|
Board firmataBoard
|
2017-01-15 13:51:07 +01:00
|
|
|
conn io.ReadWriteCloser
|
|
|
|
PortOpener func(port string) (io.ReadWriteCloser, error)
|
2016-08-30 14:22:09 +02:00
|
|
|
gobot.Eventer
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2016-09-25 20:49:20 +02:00
|
|
|
// NewAdaptor returns a new Firmata Adaptor which optionally accepts:
|
2014-11-18 17:28:20 -08:00
|
|
|
//
|
2016-09-25 20:49:20 +02:00
|
|
|
// string: port the Adaptor uses to connect to a serial port with a baude rate of 57600
|
|
|
|
// io.ReadWriteCloser: connection the Adaptor uses to communication with the hardware
|
2014-11-18 17:28:20 -08:00
|
|
|
//
|
2016-09-25 20:49:20 +02:00
|
|
|
// If an io.ReadWriteCloser is not supplied, the Adaptor will open a connection
|
2014-11-18 17:28:20 -08:00
|
|
|
// to a serial port with a baude rate of 57600. If an io.ReadWriteCloser
|
2016-09-25 20:49:20 +02:00
|
|
|
// is supplied, then the Adaptor will use the provided io.ReadWriteCloser and use the
|
2014-11-18 17:28:20 -08:00
|
|
|
// string port as a label to be displayed in the log and api.
|
2016-09-25 20:49:20 +02:00
|
|
|
func NewAdaptor(args ...interface{}) *Adaptor {
|
|
|
|
f := &Adaptor{
|
2017-02-02 16:10:09 +01:00
|
|
|
name: gobot.DefaultName("Firmata"),
|
2015-01-05 04:22:12 -08:00
|
|
|
port: "",
|
|
|
|
conn: nil,
|
2017-06-14 13:44:03 +02:00
|
|
|
Board: client.New(),
|
2017-01-15 13:51:07 +01:00
|
|
|
PortOpener: func(port string) (io.ReadWriteCloser, error) {
|
2017-05-13 11:13:33 +02:00
|
|
|
return serial.Open(port, &serial.Mode{BaudRate: 57600})
|
2014-12-19 12:58:55 -08:00
|
|
|
},
|
2016-08-30 14:22:09 +02:00
|
|
|
Eventer: gobot.NewEventer(),
|
2014-12-19 12:58:55 -08:00
|
|
|
}
|
2014-11-18 17:28:20 -08:00
|
|
|
|
|
|
|
for _, arg := range args {
|
2023-05-19 14:16:22 +02:00
|
|
|
switch a := arg.(type) {
|
2014-11-18 17:28:20 -08:00
|
|
|
case string:
|
2023-05-19 14:16:22 +02:00
|
|
|
f.port = a
|
2014-11-18 17:28:20 -08:00
|
|
|
case io.ReadWriteCloser:
|
2023-05-19 14:16:22 +02:00
|
|
|
f.conn = a
|
2014-11-18 17:28:20 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-19 12:58:55 -08:00
|
|
|
return f
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// Connect starts a connection to the board.
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) Connect() error {
|
2014-12-19 12:58:55 -08:00
|
|
|
if f.conn == nil {
|
2023-06-12 19:51:25 +02:00
|
|
|
sp, err := f.PortOpener(f.Port())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-12-19 12:58:55 -08:00
|
|
|
}
|
2015-01-02 07:57:13 -08:00
|
|
|
f.conn = sp
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2023-06-12 19:51:25 +02:00
|
|
|
if err := f.Board.Connect(f.conn); err != nil {
|
2016-11-07 19:13:03 +01:00
|
|
|
return err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
2017-06-14 01:31:42 +02:00
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
return f.Board.On("SysexResponse", func(data interface{}) {
|
2017-06-14 01:31:42 +02:00
|
|
|
f.Publish("SysexResponse", data)
|
|
|
|
})
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
// Disconnect closes the io connection to the Board
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) Disconnect() error {
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board != nil {
|
|
|
|
return f.Board.Disconnect()
|
2014-11-19 23:21:19 -08:00
|
|
|
}
|
2015-01-05 04:22:12 -08:00
|
|
|
return nil
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
2014-10-17 12:42:07 -05:00
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// Finalize terminates the firmata connection
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) Finalize() error {
|
|
|
|
return f.Disconnect()
|
2014-11-19 23:21:19 -08:00
|
|
|
}
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2024-02-12 16:27:08 +01:00
|
|
|
// Port returns the Firmata adaptors port
|
2016-09-25 20:49:20 +02:00
|
|
|
func (f *Adaptor) Port() string { return f.port }
|
2015-01-02 07:57:13 -08:00
|
|
|
|
2024-02-12 16:27:08 +01:00
|
|
|
// Name returns the Firmata adaptors name
|
2016-09-25 20:49:20 +02:00
|
|
|
func (f *Adaptor) Name() string { return f.name }
|
|
|
|
|
2024-02-12 16:27:08 +01:00
|
|
|
// SetName sets the Firmata adaptors name
|
2016-09-25 20:49:20 +02:00
|
|
|
func (f *Adaptor) SetName(n string) { f.name = n }
|
2014-11-21 19:36:41 -08:00
|
|
|
|
2016-07-21 02:57:49 -07:00
|
|
|
// ServoConfig sets the pulse width in microseconds for a pin attached to a servo
|
2024-11-01 12:54:20 +01:00
|
|
|
func (f *Adaptor) ServoConfig(pin string, minimum, maximum int) error {
|
2016-07-21 02:48:53 -07:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-11-01 12:54:20 +01:00
|
|
|
return f.Board.ServoConfig(p, maximum, minimum)
|
2016-07-21 02:48:53 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// ServoWrite writes the 0-180 degree angle to the specified pin.
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) ServoWrite(pin string, angle byte) error {
|
2014-11-19 11:18:48 -08:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board.Pins()[p].Mode != client.Servo {
|
|
|
|
err = f.Board.SetPinMode(p, client.Servo)
|
2015-01-05 04:22:12 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2023-06-12 19:51:25 +02:00
|
|
|
|
|
|
|
return f.Board.AnalogWrite(p, int(angle))
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// PwmWrite writes the 0-254 value to the specified pin
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) PwmWrite(pin string, level byte) error {
|
2014-11-19 11:18:48 -08:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board.Pins()[p].Mode != client.Pwm {
|
|
|
|
err = f.Board.SetPinMode(p, client.Pwm)
|
2015-01-05 04:22:12 -08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2023-06-12 19:51:25 +02:00
|
|
|
|
|
|
|
return f.Board.AnalogWrite(p, int(level))
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// DigitalWrite writes a value to the pin. Acceptable values are 1 or 0.
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) DigitalWrite(pin string, level byte) error {
|
2014-11-19 11:18:48 -08:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
2023-06-12 19:51:25 +02:00
|
|
|
return err
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2014-04-26 03:11:51 -07:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board.Pins()[p].Mode != client.Output {
|
2023-06-12 19:51:25 +02:00
|
|
|
if err = f.Board.SetPinMode(p, client.Output); err != nil {
|
|
|
|
return err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
return f.Board.DigitalWrite(p, int(level))
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 07:57:13 -08:00
|
|
|
// DigitalRead retrieves digital value from specified pin.
|
|
|
|
// Returns -1 if the response from the board has timed out
|
2023-11-15 20:51:52 +01:00
|
|
|
func (f *Adaptor) DigitalRead(pin string) (int, error) {
|
2014-11-19 11:18:48 -08:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
2023-11-15 20:51:52 +01:00
|
|
|
return 0, err
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2014-07-13 13:54:41 -07:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board.Pins()[p].Mode != client.Input {
|
2023-11-15 20:51:52 +01:00
|
|
|
if err := f.Board.SetPinMode(p, client.Input); err != nil {
|
|
|
|
return 0, err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
2023-11-15 20:51:52 +01:00
|
|
|
if err := f.Board.ReportDigital(p, 1); err != nil {
|
|
|
|
return 0, err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
|
|
|
<-time.After(10 * time.Millisecond)
|
2014-07-14 13:34:46 -07:00
|
|
|
}
|
2015-01-05 04:22:12 -08:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
return f.Board.Pins()[p].Value, nil
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2014-10-17 12:42:07 -05:00
|
|
|
// AnalogRead retrieves value from analog pin.
|
2015-01-02 07:57:13 -08:00
|
|
|
// Returns -1 if the response from the board has timed out
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) AnalogRead(pin string) (int, error) {
|
2014-11-19 11:18:48 -08:00
|
|
|
p, err := strconv.Atoi(pin)
|
|
|
|
if err != nil {
|
2023-06-12 19:51:25 +02:00
|
|
|
return 0, err
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2015-01-05 04:22:12 -08:00
|
|
|
|
2014-04-27 19:56:18 -07:00
|
|
|
p = f.digitalPin(p)
|
2014-11-19 11:18:48 -08:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
if f.Board.Pins()[p].Mode != client.Analog {
|
2023-06-12 19:51:25 +02:00
|
|
|
if err := f.Board.SetPinMode(p, client.Analog); err != nil {
|
|
|
|
return 0, err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
2014-11-19 11:18:48 -08:00
|
|
|
|
2023-06-12 19:51:25 +02:00
|
|
|
if err := f.Board.ReportAnalog(p, 1); err != nil {
|
|
|
|
return 0, err
|
2015-01-05 04:22:12 -08:00
|
|
|
}
|
|
|
|
<-time.After(10 * time.Millisecond)
|
2014-11-19 11:18:48 -08:00
|
|
|
}
|
2014-07-13 13:54:41 -07:00
|
|
|
|
2017-06-14 13:44:03 +02:00
|
|
|
return f.Board.Pins()[p].Value, nil
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2017-06-14 00:14:00 +02:00
|
|
|
func (f *Adaptor) WriteSysex(data []byte) error {
|
2017-06-14 13:44:03 +02:00
|
|
|
return f.Board.WriteSysex(data)
|
2017-06-14 00:14:00 +02:00
|
|
|
}
|
|
|
|
|
2014-10-17 12:42:07 -05:00
|
|
|
// digitalPin converts pin number to digital mapping
|
2016-09-25 20:49:20 +02:00
|
|
|
func (f *Adaptor) digitalPin(pin int) int {
|
2014-04-26 03:11:51 -07:00
|
|
|
return pin + 14
|
|
|
|
}
|
|
|
|
|
2022-12-10 13:10:23 +01:00
|
|
|
// GetI2cConnection returns an i2c connection to a device on a specified bus.
|
2017-02-06 14:50:14 +01:00
|
|
|
// Only supports bus number 0
|
2023-06-12 19:51:25 +02:00
|
|
|
func (f *Adaptor) GetI2cConnection(address int, bus int) (i2c.Connection, error) {
|
2017-02-06 14:50:14 +01:00
|
|
|
if bus != 0 {
|
|
|
|
return nil, fmt.Errorf("Invalid bus number %d, only 0 is supported", bus)
|
2014-11-19 17:24:17 -08:00
|
|
|
}
|
2023-06-12 19:51:25 +02:00
|
|
|
if err := f.Board.I2cConfig(0); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return NewFirmataI2cConnection(f, address), nil
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|
|
|
|
|
2022-12-10 13:10:23 +01:00
|
|
|
// DefaultI2cBus returns the default i2c bus for this platform
|
|
|
|
func (f *Adaptor) DefaultI2cBus() int {
|
2017-02-06 14:50:14 +01:00
|
|
|
return 0
|
2014-04-26 03:11:51 -07:00
|
|
|
}
|