gobot.io/x/gobot@v1.16.0/platforms/firmata/client/client.go (about)

     1  // Package client provies a client for interacting with microcontrollers
     2  // using the Firmata protocol https://github.com/firmata/protocol.
     3  package client // import "gobot.io/x/gobot/platforms/firmata/client"
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"gobot.io/x/gobot"
    15  )
    16  
    17  // Pin Modes
    18  const (
    19  	Input  = 0x00
    20  	Output = 0x01
    21  	Analog = 0x02
    22  	Pwm    = 0x03
    23  	Servo  = 0x04
    24  )
    25  
    26  // Sysex Codes
    27  const (
    28  	ProtocolVersion          byte = 0xF9
    29  	SystemReset              byte = 0xFF
    30  	DigitalMessage           byte = 0x90
    31  	DigitalMessageRangeStart byte = 0x90
    32  	DigitalMessageRangeEnd   byte = 0x9F
    33  	AnalogMessage            byte = 0xE0
    34  	AnalogMessageRangeStart  byte = 0xE0
    35  	AnalogMessageRangeEnd    byte = 0xEF
    36  	ReportAnalog             byte = 0xC0
    37  	ReportDigital            byte = 0xD0
    38  	PinMode                  byte = 0xF4
    39  	StartSysex               byte = 0xF0
    40  	EndSysex                 byte = 0xF7
    41  	CapabilityQuery          byte = 0x6B
    42  	CapabilityResponse       byte = 0x6C
    43  	PinStateQuery            byte = 0x6D
    44  	PinStateResponse         byte = 0x6E
    45  	AnalogMappingQuery       byte = 0x69
    46  	AnalogMappingResponse    byte = 0x6A
    47  	StringData               byte = 0x71
    48  	I2CRequest               byte = 0x76
    49  	I2CReply                 byte = 0x77
    50  	I2CConfig                byte = 0x78
    51  	FirmwareQuery            byte = 0x79
    52  	I2CModeWrite             byte = 0x00
    53  	I2CModeRead              byte = 0x01
    54  	I2CModeContinuousRead    byte = 0x02
    55  	I2CModeStopReading       byte = 0x03
    56  	ServoConfig              byte = 0x70
    57  )
    58  
    59  // Errors
    60  var (
    61  	ErrConnected = errors.New("client is already connected")
    62  )
    63  
    64  // Client represents a client connection to a firmata board
    65  type Client struct {
    66  	pins            []Pin
    67  	FirmwareName    string
    68  	ProtocolVersion string
    69  	connecting      atomic.Value
    70  	connected       atomic.Value
    71  	connection      io.ReadWriteCloser
    72  	analogPins      []int
    73  	ConnectTimeout  time.Duration
    74  	initFunc        func() error
    75  	initMutex       sync.Mutex
    76  	gobot.Eventer
    77  }
    78  
    79  // Pin represents a pin on the firmata board
    80  type Pin struct {
    81  	SupportedModes []int
    82  	Mode           int
    83  	Value          int
    84  	State          int
    85  	AnalogChannel  int
    86  }
    87  
    88  // I2cReply represents the response from an I2cReply message
    89  type I2cReply struct {
    90  	Address  int
    91  	Register int
    92  	Data     []byte
    93  }
    94  
    95  // New returns a new Client
    96  func New() *Client {
    97  	c := &Client{
    98  		ProtocolVersion: "",
    99  		FirmwareName:    "",
   100  		connection:      nil,
   101  		ConnectTimeout:  15 * time.Second,
   102  		pins:            []Pin{},
   103  		analogPins:      []int{},
   104  		Eventer:         gobot.NewEventer(),
   105  	}
   106  
   107  	c.connecting.Store(false)
   108  	c.connected.Store(false)
   109  
   110  	for _, s := range []string{
   111  		"FirmwareQuery",
   112  		"CapabilityQuery",
   113  		"AnalogMappingQuery",
   114  		"ProtocolVersion",
   115  		"I2cReply",
   116  		"StringData",
   117  		"Error",
   118  	} {
   119  		c.AddEvent(s)
   120  	}
   121  
   122  	return c
   123  }
   124  
   125  func (b *Client) setConnecting(c bool) {
   126  	b.connecting.Store(c)
   127  }
   128  
   129  func (b *Client) setConnected(c bool) {
   130  	b.connected.Store(c)
   131  }
   132  
   133  // Disconnect disconnects the Client
   134  func (b *Client) Disconnect() (err error) {
   135  	b.setConnected(false)
   136  	return b.connection.Close()
   137  }
   138  
   139  // Connecting returns true when the client is connecting
   140  func (b *Client) Connecting() bool {
   141  	return b.connecting.Load().(bool)
   142  }
   143  
   144  // Connected returns the current connection state of the Client
   145  func (b *Client) Connected() bool {
   146  	return b.connected.Load().(bool)
   147  }
   148  
   149  // Pins returns all available pins
   150  func (b *Client) Pins() []Pin {
   151  	return b.pins
   152  }
   153  
   154  // Connect connects to the Client given conn. It first resets the firmata board
   155  // then continuously polls the firmata board for new information when it's
   156  // available.
   157  func (b *Client) Connect(conn io.ReadWriteCloser) (err error) {
   158  	if b.Connected() {
   159  		return ErrConnected
   160  	}
   161  
   162  	b.connection = conn
   163  	b.Reset()
   164  	connected := make(chan bool, 1)
   165  	connectError := make(chan error, 1)
   166  
   167  	b.Once(b.Event("ProtocolVersion"), func(data interface{}) {
   168  		e := b.FirmwareQuery()
   169  		if e != nil {
   170  			b.setConnecting(false)
   171  			connectError <- e
   172  		}
   173  	})
   174  
   175  	b.Once(b.Event("FirmwareQuery"), func(data interface{}) {
   176  		e := b.CapabilitiesQuery()
   177  		if e != nil {
   178  			b.setConnecting(false)
   179  			connectError <- e
   180  		}
   181  	})
   182  
   183  	b.Once(b.Event("CapabilityQuery"), func(data interface{}) {
   184  		e := b.AnalogMappingQuery()
   185  		if e != nil {
   186  			b.setConnecting(false)
   187  			connectError <- e
   188  		}
   189  	})
   190  
   191  	b.Once(b.Event("AnalogMappingQuery"), func(data interface{}) {
   192  		b.ReportDigital(0, 1)
   193  		b.ReportDigital(1, 1)
   194  		b.setConnecting(false)
   195  		b.setConnected(true)
   196  		connected <- true
   197  	})
   198  
   199  	// start it off...
   200  	b.setConnecting(true)
   201  	b.ProtocolVersionQuery()
   202  
   203  	go func() {
   204  		for {
   205  			if !b.Connecting() {
   206  				return
   207  			}
   208  			if e := b.process(); e != nil {
   209  				connectError <- e
   210  				return
   211  			}
   212  			time.Sleep(10 * time.Millisecond)
   213  		}
   214  	}()
   215  
   216  	select {
   217  	case <-connected:
   218  	case e := <-connectError:
   219  		return e
   220  	case <-time.After(b.ConnectTimeout):
   221  		return errors.New("unable to connect. Perhaps you need to flash your Arduino with Firmata?")
   222  	}
   223  
   224  	go func() {
   225  		for {
   226  			if !b.Connected() {
   227  				break
   228  			}
   229  
   230  			if err := b.process(); err != nil {
   231  				b.Publish(b.Event("Error"), err)
   232  			}
   233  		}
   234  	}()
   235  
   236  	return
   237  }
   238  
   239  // Reset sends the SystemReset sysex code.
   240  func (b *Client) Reset() error {
   241  	return b.write([]byte{SystemReset})
   242  }
   243  
   244  // SetPinMode sets the pin to mode.
   245  func (b *Client) SetPinMode(pin int, mode int) error {
   246  	b.pins[byte(pin)].Mode = mode
   247  	return b.write([]byte{PinMode, byte(pin), byte(mode)})
   248  }
   249  
   250  // DigitalWrite writes value to pin.
   251  func (b *Client) DigitalWrite(pin int, value int) error {
   252  	port := byte(math.Floor(float64(pin) / 8))
   253  	portValue := byte(0)
   254  
   255  	b.pins[pin].Value = value
   256  
   257  	for i := byte(0); i < 8; i++ {
   258  		if b.pins[8*port+i].Value != 0 {
   259  			portValue = portValue | (1 << i)
   260  		}
   261  	}
   262  	return b.write([]byte{DigitalMessage | port, portValue & 0x7F, (portValue >> 7) & 0x7F})
   263  }
   264  
   265  // ServoConfig sets the min and max pulse width for servo PWM range
   266  func (b *Client) ServoConfig(pin int, max int, min int) error {
   267  	ret := []byte{
   268  		ServoConfig,
   269  		byte(pin),
   270  		byte(min & 0x7F),
   271  		byte((min >> 7) & 0x7F),
   272  		byte(max & 0x7F),
   273  		byte((max >> 7) & 0x7F),
   274  	}
   275  	return b.WriteSysex(ret)
   276  }
   277  
   278  // AnalogWrite writes value to pin.
   279  func (b *Client) AnalogWrite(pin int, value int) error {
   280  	b.pins[pin].Value = value
   281  	return b.write([]byte{AnalogMessage | byte(pin), byte(value & 0x7F), byte((value >> 7) & 0x7F)})
   282  }
   283  
   284  // FirmwareQuery sends the FirmwareQuery sysex code.
   285  func (b *Client) FirmwareQuery() error {
   286  	return b.WriteSysex([]byte{FirmwareQuery})
   287  }
   288  
   289  // PinStateQuery sends a PinStateQuery for pin.
   290  func (b *Client) PinStateQuery(pin int) error {
   291  	return b.WriteSysex([]byte{PinStateQuery, byte(pin)})
   292  }
   293  
   294  // ProtocolVersionQuery sends the ProtocolVersion sysex code.
   295  func (b *Client) ProtocolVersionQuery() error {
   296  	return b.write([]byte{ProtocolVersion})
   297  }
   298  
   299  // CapabilitiesQuery sends the CapabilityQuery sysex code.
   300  func (b *Client) CapabilitiesQuery() error {
   301  	return b.WriteSysex([]byte{CapabilityQuery})
   302  }
   303  
   304  // AnalogMappingQuery sends the AnalogMappingQuery sysex code.
   305  func (b *Client) AnalogMappingQuery() error {
   306  	return b.WriteSysex([]byte{AnalogMappingQuery})
   307  }
   308  
   309  // ReportDigital enables or disables digital reporting for pin, a non zero
   310  // state enables reporting
   311  func (b *Client) ReportDigital(pin int, state int) error {
   312  	return b.togglePinReporting(pin, state, ReportDigital)
   313  }
   314  
   315  // ReportAnalog enables or disables analog reporting for pin, a non zero
   316  // state enables reporting
   317  func (b *Client) ReportAnalog(pin int, state int) error {
   318  	return b.togglePinReporting(pin, state, ReportAnalog)
   319  }
   320  
   321  // I2cRead reads numBytes from address once.
   322  func (b *Client) I2cRead(address int, numBytes int) error {
   323  	return b.WriteSysex([]byte{I2CRequest, byte(address), (I2CModeRead << 3),
   324  		byte(numBytes) & 0x7F, (byte(numBytes) >> 7) & 0x7F})
   325  }
   326  
   327  // I2cWrite writes data to address.
   328  func (b *Client) I2cWrite(address int, data []byte) error {
   329  	ret := []byte{I2CRequest, byte(address), (I2CModeWrite << 3)}
   330  	for _, val := range data {
   331  		ret = append(ret, byte(val&0x7F))
   332  		ret = append(ret, byte((val>>7)&0x7F))
   333  	}
   334  	return b.WriteSysex(ret)
   335  }
   336  
   337  // I2cConfig configures the delay in which a register can be read from after it
   338  // has been written to.
   339  func (b *Client) I2cConfig(delay int) error {
   340  	return b.WriteSysex([]byte{I2CConfig, byte(delay & 0xFF), byte((delay >> 8) & 0xFF)})
   341  }
   342  
   343  func (b *Client) togglePinReporting(pin int, state int, mode byte) (err error) {
   344  	if state != 0 {
   345  		state = 1
   346  	} else {
   347  		state = 0
   348  	}
   349  
   350  	err = b.write([]byte{byte(mode) | byte(pin), byte(state)})
   351  	return
   352  }
   353  
   354  // WriteSysex writes an arbitrary Sysex command to the microcontroller.
   355  func (b *Client) WriteSysex(data []byte) (err error) {
   356  	return b.write(append([]byte{StartSysex}, append(data, EndSysex)...))
   357  }
   358  
   359  func (b *Client) write(data []byte) (err error) {
   360  	_, err = b.connection.Write(data[:])
   361  	return
   362  }
   363  
   364  func (b *Client) read(n int) (buf []byte, err error) {
   365  	buf = make([]byte, n)
   366  	_, err = io.ReadFull(b.connection, buf)
   367  	return
   368  }
   369  
   370  func (b *Client) process() (err error) {
   371  	msgBuf, err := b.read(1)
   372  	if err != nil {
   373  		return err
   374  	}
   375  	messageType := msgBuf[0]
   376  	switch {
   377  	case ProtocolVersion == messageType:
   378  		buf, err := b.read(2)
   379  		if err != nil {
   380  			return err
   381  		}
   382  		b.ProtocolVersion = fmt.Sprintf("%v.%v", buf[0], buf[1])
   383  
   384  		b.Publish(b.Event("ProtocolVersion"), b.ProtocolVersion)
   385  	case AnalogMessageRangeStart <= messageType &&
   386  		AnalogMessageRangeEnd >= messageType:
   387  
   388  		buf, err := b.read(2)
   389  		if err != nil {
   390  			return err
   391  		}
   392  		value := uint(buf[0]) | uint(buf[1])<<7
   393  		pin := int((messageType & 0x0F))
   394  
   395  		if len(b.analogPins) > pin {
   396  			if len(b.pins) > b.analogPins[pin] {
   397  				b.pins[b.analogPins[pin]].Value = int(value)
   398  				b.Publish(b.Event(fmt.Sprintf("AnalogRead%v", pin)), b.pins[b.analogPins[pin]].Value)
   399  			}
   400  		}
   401  	case DigitalMessageRangeStart <= messageType &&
   402  		DigitalMessageRangeEnd >= messageType:
   403  
   404  		buf, err := b.read(2)
   405  		if err != nil {
   406  			return err
   407  		}
   408  		port := messageType & 0x0F
   409  		portValue := buf[0] | (buf[1] << 7)
   410  
   411  		for i := 0; i < 8; i++ {
   412  			pinNumber := int((8*byte(port) + byte(i)))
   413  			if len(b.pins) > pinNumber {
   414  				if b.pins[pinNumber].Mode == Input {
   415  					b.pins[pinNumber].Value = int((portValue >> (byte(i) & 0x07)) & 0x01)
   416  					b.Publish(b.Event(fmt.Sprintf("DigitalRead%v", pinNumber)), b.pins[pinNumber].Value)
   417  				}
   418  			}
   419  		}
   420  	case StartSysex == messageType:
   421  		buf, err := b.read(2)
   422  		if err != nil {
   423  			return err
   424  		}
   425  
   426  		currentBuffer := append(msgBuf, buf[0], buf[1])
   427  		for {
   428  			buf, err = b.read(1)
   429  			if err != nil {
   430  				return err
   431  			}
   432  			currentBuffer = append(currentBuffer, buf[0])
   433  			if buf[0] == EndSysex {
   434  				break
   435  			}
   436  		}
   437  		command := currentBuffer[1]
   438  		switch command {
   439  		case CapabilityResponse:
   440  			b.pins = []Pin{}
   441  			supportedModes := 0
   442  			n := 0
   443  
   444  			for _, val := range currentBuffer[2 : len(currentBuffer)-1] {
   445  				if val == 127 {
   446  					modes := []int{}
   447  					for _, mode := range []int{Input, Output, Analog, Pwm, Servo} {
   448  						if (supportedModes & (1 << byte(mode))) != 0 {
   449  							modes = append(modes, mode)
   450  						}
   451  					}
   452  
   453  					b.pins = append(b.pins, Pin{SupportedModes: modes, Mode: Output})
   454  					b.AddEvent(fmt.Sprintf("DigitalRead%v", len(b.pins)-1))
   455  					b.AddEvent(fmt.Sprintf("PinState%v", len(b.pins)-1))
   456  					supportedModes = 0
   457  					n = 0
   458  					continue
   459  				}
   460  
   461  				if n == 0 {
   462  					supportedModes = supportedModes | (1 << val)
   463  				}
   464  				n ^= 1
   465  			}
   466  			b.Publish(b.Event("CapabilityQuery"), nil)
   467  		case AnalogMappingResponse:
   468  			pinIndex := 0
   469  			b.analogPins = []int{}
   470  
   471  			for _, val := range currentBuffer[2 : len(currentBuffer)-1] {
   472  				b.pins[pinIndex].AnalogChannel = int(val)
   473  
   474  				if val != 127 {
   475  					b.analogPins = append(b.analogPins, pinIndex)
   476  				}
   477  				b.AddEvent(fmt.Sprintf("AnalogRead%v", pinIndex))
   478  				pinIndex++
   479  			}
   480  			b.Publish(b.Event("AnalogMappingQuery"), nil)
   481  		case PinStateResponse:
   482  			pin := currentBuffer[2]
   483  			b.pins[pin].Mode = int(currentBuffer[3])
   484  			b.pins[pin].State = int(currentBuffer[4])
   485  
   486  			if len(currentBuffer) > 6 {
   487  				b.pins[pin].State = int(uint(b.pins[pin].State) | uint(currentBuffer[5])<<7)
   488  			}
   489  			if len(currentBuffer) > 7 {
   490  				b.pins[pin].State = int(uint(b.pins[pin].State) | uint(currentBuffer[6])<<14)
   491  			}
   492  
   493  			b.Publish(b.Event(fmt.Sprintf("PinState%v", pin)), b.pins[pin])
   494  		case I2CReply:
   495  			reply := I2cReply{
   496  				Address:  int(byte(currentBuffer[2]) | byte(currentBuffer[3])<<7),
   497  				Register: int(byte(currentBuffer[4]) | byte(currentBuffer[5])<<7),
   498  				Data:     []byte{byte(currentBuffer[6]) | byte(currentBuffer[7])<<7},
   499  			}
   500  			for i := 8; i < len(currentBuffer); i = i + 2 {
   501  				if currentBuffer[i] == byte(0xF7) {
   502  					break
   503  				}
   504  				if i+2 > len(currentBuffer) {
   505  					break
   506  				}
   507  				reply.Data = append(reply.Data,
   508  					byte(currentBuffer[i])|byte(currentBuffer[i+1])<<7,
   509  				)
   510  			}
   511  			b.Publish(b.Event("I2cReply"), reply)
   512  		case FirmwareQuery:
   513  			name := []byte{}
   514  			for _, val := range currentBuffer[4:(len(currentBuffer) - 1)] {
   515  				if val != 0 {
   516  					name = append(name, val)
   517  				}
   518  			}
   519  			b.FirmwareName = string(name[:])
   520  			b.Publish(b.Event("FirmwareQuery"), b.FirmwareName)
   521  		case StringData:
   522  			str := currentBuffer[2:]
   523  			b.Publish(b.Event("StringData"), string(str[:len(str)-1]))
   524  		default:
   525  			data := make([]byte, len(currentBuffer))
   526  			copy(data, currentBuffer)
   527  			b.Publish("SysexResponse", data)
   528  		}
   529  	}
   530  	return
   531  }