gobot.io/x/gobot/v2@v2.1.0/platforms/firmata/firmata_adaptor.go (about)

     1  package firmata
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"strconv"
     7  	"time"
     8  
     9  	"go.bug.st/serial"
    10  	"gobot.io/x/gobot/v2"
    11  	"gobot.io/x/gobot/v2/drivers/i2c"
    12  	"gobot.io/x/gobot/v2/platforms/firmata/client"
    13  )
    14  
    15  type firmataBoard interface {
    16  	Connect(io.ReadWriteCloser) error
    17  	Disconnect() error
    18  	Pins() []client.Pin
    19  	AnalogWrite(int, int) error
    20  	SetPinMode(int, int) error
    21  	ReportAnalog(int, int) error
    22  	ReportDigital(int, int) error
    23  	DigitalWrite(int, int) error
    24  	I2cRead(int, int) error
    25  	I2cWrite(int, []byte) error
    26  	I2cConfig(int) error
    27  	ServoConfig(int, int, int) error
    28  	WriteSysex(data []byte) error
    29  	gobot.Eventer
    30  }
    31  
    32  type FirmataAdaptor interface {
    33  	Connect() (err error)
    34  	Finalize() (err error)
    35  	Name() string
    36  	SetName(n string)
    37  	WriteSysex(data []byte) error
    38  	gobot.Eventer
    39  }
    40  
    41  // Adaptor is the Gobot Adaptor for Firmata based boards
    42  type Adaptor struct {
    43  	name       string
    44  	port       string
    45  	Board      firmataBoard
    46  	conn       io.ReadWriteCloser
    47  	PortOpener func(port string) (io.ReadWriteCloser, error)
    48  	gobot.Eventer
    49  }
    50  
    51  // NewAdaptor returns a new Firmata Adaptor which optionally accepts:
    52  //
    53  //	string: port the Adaptor uses to connect to a serial port with a baude rate of 57600
    54  //	io.ReadWriteCloser: connection the Adaptor uses to communication with the hardware
    55  //
    56  // If an io.ReadWriteCloser is not supplied, the Adaptor will open a connection
    57  // to a serial port with a baude rate of 57600. If an io.ReadWriteCloser
    58  // is supplied, then the Adaptor will use the provided io.ReadWriteCloser and use the
    59  // string port as a label to be displayed in the log and api.
    60  func NewAdaptor(args ...interface{}) *Adaptor {
    61  	f := &Adaptor{
    62  		name:  gobot.DefaultName("Firmata"),
    63  		port:  "",
    64  		conn:  nil,
    65  		Board: client.New(),
    66  		PortOpener: func(port string) (io.ReadWriteCloser, error) {
    67  			return serial.Open(port, &serial.Mode{BaudRate: 57600})
    68  		},
    69  		Eventer: gobot.NewEventer(),
    70  	}
    71  
    72  	for _, arg := range args {
    73  		switch a := arg.(type) {
    74  		case string:
    75  			f.port = a
    76  		case io.ReadWriteCloser:
    77  			f.conn = a
    78  		}
    79  	}
    80  
    81  	return f
    82  }
    83  
    84  // Connect starts a connection to the board.
    85  func (f *Adaptor) Connect() (err error) {
    86  	if f.conn == nil {
    87  		sp, e := f.PortOpener(f.Port())
    88  		if e != nil {
    89  			return e
    90  		}
    91  		f.conn = sp
    92  	}
    93  	if err = f.Board.Connect(f.conn); err != nil {
    94  		return err
    95  	}
    96  
    97  	f.Board.On("SysexResponse", func(data interface{}) {
    98  		f.Publish("SysexResponse", data)
    99  	})
   100  
   101  	return
   102  }
   103  
   104  // Disconnect closes the io connection to the Board
   105  func (f *Adaptor) Disconnect() (err error) {
   106  	if f.Board != nil {
   107  		return f.Board.Disconnect()
   108  	}
   109  	return nil
   110  }
   111  
   112  // Finalize terminates the firmata connection
   113  func (f *Adaptor) Finalize() (err error) {
   114  	err = f.Disconnect()
   115  	return err
   116  }
   117  
   118  // Port returns the Firmata Adaptors port
   119  func (f *Adaptor) Port() string { return f.port }
   120  
   121  // Name returns the Firmata Adaptors name
   122  func (f *Adaptor) Name() string { return f.name }
   123  
   124  // SetName sets the Firmata Adaptors name
   125  func (f *Adaptor) SetName(n string) { f.name = n }
   126  
   127  // ServoConfig sets the pulse width in microseconds for a pin attached to a servo
   128  func (f *Adaptor) ServoConfig(pin string, min, max int) error {
   129  	p, err := strconv.Atoi(pin)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	return f.Board.ServoConfig(p, max, min)
   135  }
   136  
   137  // ServoWrite writes the 0-180 degree angle to the specified pin.
   138  func (f *Adaptor) ServoWrite(pin string, angle byte) (err error) {
   139  	p, err := strconv.Atoi(pin)
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	if f.Board.Pins()[p].Mode != client.Servo {
   145  		err = f.Board.SetPinMode(p, client.Servo)
   146  		if err != nil {
   147  			return err
   148  		}
   149  	}
   150  	err = f.Board.AnalogWrite(p, int(angle))
   151  	return
   152  }
   153  
   154  // PwmWrite writes the 0-254 value to the specified pin
   155  func (f *Adaptor) PwmWrite(pin string, level byte) (err error) {
   156  	p, err := strconv.Atoi(pin)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	if f.Board.Pins()[p].Mode != client.Pwm {
   162  		err = f.Board.SetPinMode(p, client.Pwm)
   163  		if err != nil {
   164  			return err
   165  		}
   166  	}
   167  	err = f.Board.AnalogWrite(p, int(level))
   168  	return
   169  }
   170  
   171  // DigitalWrite writes a value to the pin. Acceptable values are 1 or 0.
   172  func (f *Adaptor) DigitalWrite(pin string, level byte) (err error) {
   173  	p, err := strconv.Atoi(pin)
   174  	if err != nil {
   175  		return
   176  	}
   177  
   178  	if f.Board.Pins()[p].Mode != client.Output {
   179  		err = f.Board.SetPinMode(p, client.Output)
   180  		if err != nil {
   181  			return
   182  		}
   183  	}
   184  
   185  	err = f.Board.DigitalWrite(p, int(level))
   186  	return
   187  }
   188  
   189  // DigitalRead retrieves digital value from specified pin.
   190  // Returns -1 if the response from the board has timed out
   191  func (f *Adaptor) DigitalRead(pin string) (val int, err error) {
   192  	p, err := strconv.Atoi(pin)
   193  	if err != nil {
   194  		return
   195  	}
   196  
   197  	if f.Board.Pins()[p].Mode != client.Input {
   198  		if err = f.Board.SetPinMode(p, client.Input); err != nil {
   199  			return
   200  		}
   201  		if err = f.Board.ReportDigital(p, 1); err != nil {
   202  			return
   203  		}
   204  		<-time.After(10 * time.Millisecond)
   205  	}
   206  
   207  	return f.Board.Pins()[p].Value, nil
   208  }
   209  
   210  // AnalogRead retrieves value from analog pin.
   211  // Returns -1 if the response from the board has timed out
   212  func (f *Adaptor) AnalogRead(pin string) (val int, err error) {
   213  	p, err := strconv.Atoi(pin)
   214  	if err != nil {
   215  		return
   216  	}
   217  
   218  	p = f.digitalPin(p)
   219  
   220  	if f.Board.Pins()[p].Mode != client.Analog {
   221  		if err = f.Board.SetPinMode(p, client.Analog); err != nil {
   222  			return
   223  		}
   224  
   225  		if err = f.Board.ReportAnalog(p, 1); err != nil {
   226  			return
   227  		}
   228  		<-time.After(10 * time.Millisecond)
   229  	}
   230  
   231  	return f.Board.Pins()[p].Value, nil
   232  }
   233  
   234  func (f *Adaptor) WriteSysex(data []byte) error {
   235  	return f.Board.WriteSysex(data)
   236  }
   237  
   238  // digitalPin converts pin number to digital mapping
   239  func (f *Adaptor) digitalPin(pin int) int {
   240  	return pin + 14
   241  }
   242  
   243  // GetI2cConnection returns an i2c connection to a device on a specified bus.
   244  // Only supports bus number 0
   245  func (f *Adaptor) GetI2cConnection(address int, bus int) (connection i2c.Connection, err error) {
   246  	if bus != 0 {
   247  		return nil, fmt.Errorf("Invalid bus number %d, only 0 is supported", bus)
   248  	}
   249  	err = f.Board.I2cConfig(0)
   250  	return NewFirmataI2cConnection(f, address), err
   251  }
   252  
   253  // DefaultI2cBus returns the default i2c bus for this platform
   254  func (f *Adaptor) DefaultI2cBus() int {
   255  	return 0
   256  }