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 }