├── .gitignore ├── LICENSE ├── README.md ├── client.go ├── constants.go ├── contrib └── ExtendedFirmata │ ├── ExtendedFirmata.ino │ └── Makefile ├── examples └── main.go ├── reply.go ├── serial_ext.go ├── spi_ext.go ├── sysex.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-firmata 2 | A Golang wrapper for [Firmata](https://www.arduino.cc/en/reference/firmata) on [Arduino](https://www.arduino.cc/) 3 | 4 | [![GoDoc](http://godoc.org/github.com/kraman/go-firmata?status.svg)](http://godoc.org/github.com/kraman/go-firmata) 5 | 6 | ## Installation 7 | 8 | ```bash 9 | go get github.com/kraman/go-firmata 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```go 15 | package main 16 | 17 | import ( 18 | "github.com/kraman/go-firmata" 19 | "time" 20 | ) 21 | 22 | var led uint8 = 13 23 | 24 | func main() { 25 | arduino, err := firmata.NewClient("COM1", 57600) 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | // arduino.Verbose = true 31 | 32 | myDelay := time.Millisecond * 250 33 | 34 | // Set led pin as output 35 | arduino.SetPinMode(led, firmata.Output) 36 | 37 | // Blink led 10 times 38 | for x := 0; x < 10; x++ { 39 | 40 | // Turn ON led 41 | arduino.DigitalWrite(led, true) 42 | arduino.Delay(myDelay) 43 | 44 | // Turn OFF led 45 | arduino.DigitalWrite(led, false) 46 | arduino.Delay(myDelay) 47 | 48 | } 49 | arduino.Close() 50 | } 51 | 52 | ``` -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | import ( 18 | "fmt" 19 | "github.com/tarm/serial" 20 | "io" 21 | "log" 22 | "os" 23 | "time" 24 | ) 25 | 26 | // Arduino Firmata client for golang 27 | type FirmataClient struct { 28 | serialDev string 29 | baud int 30 | conn *io.ReadWriteCloser 31 | Log *log.Logger 32 | 33 | protocolVersion []byte 34 | firmwareVersion []int 35 | firmwareName string 36 | 37 | ready bool 38 | analogMappingDone bool 39 | capabilityDone bool 40 | 41 | digitalPinState [8]byte 42 | 43 | analogPinsChannelMap map[int]byte 44 | analogChannelPinsMap map[byte]int 45 | pinModes []map[PinMode]interface{} 46 | 47 | valueChan chan FirmataValue 48 | serialChan chan string 49 | spiChan chan []byte 50 | 51 | Verbose bool 52 | } 53 | 54 | // Creates a new FirmataClient object and connects to the Arduino board 55 | // over specified serial port. This function blocks till a connection is 56 | // succesfullt established and pin mappings are retrieved. 57 | func NewClient(dev string, baud int) (client *FirmataClient, err error) { 58 | var conn io.ReadWriteCloser 59 | 60 | c := &serial.Config{Name: dev, Baud: baud} 61 | conn, err = serial.OpenPort(c) 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | client = &FirmataClient{ 67 | serialDev: dev, 68 | baud: baud, 69 | conn: &conn, 70 | Log: log.New(os.Stdout, "[go-firmata] ", log.Ltime), 71 | } 72 | go client.replyReader() 73 | 74 | conn.Write([]byte{byte(SystemReset)}) 75 | t := time.NewTicker(time.Second) 76 | 77 | for !(client.ready && client.analogMappingDone && client.capabilityDone) { 78 | select { 79 | case <-t.C: 80 | //no-op 81 | case <-time.After(time.Second * 15): 82 | client.Log.Print("No response in 30 seconds. Resetting arduino") 83 | conn.Write([]byte{byte(SystemReset)}) 84 | case <-time.After(time.Second * 30): 85 | client.Log.Print("Unable to initialize connection") 86 | conn.Close() 87 | client = nil 88 | } 89 | } 90 | 91 | client.Log.Print("Client ready to use") 92 | 93 | return 94 | } 95 | 96 | // Close the serial connection to properly clean up after ourselves 97 | // Usage: defer client.Close() 98 | func (c *FirmataClient) Delay(duration time.Duration) { 99 | time.Sleep(duration) 100 | } 101 | 102 | // Close the serial connection to properly clean up after ourselves 103 | // Usage: defer client.Close() 104 | func (c *FirmataClient) Close() { 105 | (*c.conn).Close() 106 | } 107 | 108 | // Sets the Pin mode (input, output, etc.) for the Arduino pin 109 | func (c *FirmataClient) SetPinMode(pin uint8, mode PinMode) error { 110 | if c.pinModes[pin][mode] == nil { 111 | return fmt.Errorf("Pin mode %v not supported by pin %v", mode, pin) 112 | } 113 | cmd := []byte{byte(SetPinMode), (pin & 0x7F), byte(mode)} 114 | if err := c.sendCommand(cmd); err != nil { 115 | return err 116 | } 117 | c.Log.Printf("SetPinMode: pin %d -> %s\r\n", pin, mode) 118 | return nil 119 | } 120 | 121 | // Specified if a digital Pin should be watched for input. 122 | // Values will be streamed back over a channel which can be retrieved by the GetValues() call 123 | func (c *FirmataClient) EnableDigitalInput(pin uint, val bool) (err error) { 124 | if pin < 0 || pin > uint(len(c.pinModes)) { 125 | err = fmt.Errorf("Invalid pin number %v\n", pin) 126 | return 127 | } 128 | port := (pin / 8) & 0x7F 129 | pin = pin % 8 130 | 131 | if val { 132 | cmd := []byte{byte(EnableDigitalInput) | byte(port), 0x01} 133 | err = c.sendCommand(cmd) 134 | } else { 135 | cmd := []byte{byte(EnableDigitalInput) | byte(port), 0x00} 136 | err = c.sendCommand(cmd) 137 | } 138 | 139 | return 140 | } 141 | 142 | // Set the value of a digital pin 143 | func (c *FirmataClient) DigitalWrite(pin uint8, val bool) error { 144 | if pin < 0 || pin > uint8(len(c.pinModes)) && c.pinModes[pin][Output] != nil { 145 | return fmt.Errorf("Invalid pin number %v\n", pin) 146 | } 147 | port := (pin / 8) & 0x7F 148 | portData := &c.digitalPinState[port] 149 | pin = pin % 8 150 | if val { 151 | (*portData) = (*portData) | (1 << pin) 152 | } else { 153 | (*portData) = (*portData) & ^(1 << pin) 154 | } 155 | data := to7Bit(*(portData)) 156 | cmd := []byte{byte(DigitalMessage) | byte(port), data[0], data[1]} 157 | if err := c.sendCommand(cmd); err != nil { 158 | return err 159 | } 160 | c.Log.Printf("DigitalWrite: pin %d -> %t\r\n", pin, val) 161 | return nil 162 | } 163 | 164 | // Specified if a analog Pin should be watched for input. 165 | // Values will be streamed back over a channel which can be retrieved by the GetValues() call 166 | func (c *FirmataClient) EnableAnalogInput(pin uint, val bool) (err error) { 167 | if pin < 0 || pin > uint(len(c.pinModes)) && c.pinModes[pin][Analog] != nil { 168 | err = fmt.Errorf("Invalid pin number %v\n", pin) 169 | return 170 | } 171 | 172 | ch := byte(c.analogPinsChannelMap[int(pin)]) 173 | c.Log.Printf("Enable analog inout on pin %v channel %v", pin, ch) 174 | if val { 175 | cmd := []byte{byte(EnableAnalogInput) | ch, 0x01} 176 | err = c.sendCommand(cmd) 177 | } else { 178 | cmd := []byte{byte(EnableAnalogInput) | ch, 0x00} 179 | err = c.sendCommand(cmd) 180 | } 181 | 182 | return 183 | } 184 | 185 | // Set the value of a analog pin 186 | func (c *FirmataClient) AnalogWrite(pin uint, pinData byte) (err error) { 187 | if pin < 0 || pin > uint(len(c.pinModes)) && c.pinModes[pin][Analog] != nil { 188 | err = fmt.Errorf("Invalid pin number %v\n", pin) 189 | return 190 | } 191 | 192 | data := to7Bit(pinData) 193 | cmd := []byte{byte(AnalogMessage) | byte(pin), data[0], data[1]} 194 | err = c.sendCommand(cmd) 195 | return 196 | } 197 | 198 | func (c *FirmataClient) sendCommand(cmd []byte) (err error) { 199 | bStr := "" 200 | for _, b := range cmd { 201 | bStr = bStr + fmt.Sprintf(" %#2x", b) 202 | } 203 | 204 | if c.Verbose { 205 | c.Log.Printf("Command send%v\n", bStr) 206 | } 207 | 208 | _, err = (*c.conn).Write(cmd) 209 | return 210 | } 211 | 212 | // Sets the polling interval in milliseconds for analog pin samples 213 | func (c *FirmataClient) SetAnalogSamplingInterval(ms byte) (err error) { 214 | data := to7Bit(ms) 215 | err = c.sendSysEx(SamplingInterval, data[0], data[1]) 216 | return 217 | } 218 | 219 | // Get the channel to retrieve analog and digital pin values 220 | func (c *FirmataClient) GetValues() <-chan FirmataValue { 221 | return c.valueChan 222 | } 223 | -------------------------------------------------------------------------------- /constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | import ( 18 | "fmt" 19 | ) 20 | 21 | type FirmataCommand byte 22 | type SysExCommand byte 23 | type PinMode byte 24 | type SerialPort byte 25 | 26 | const ( 27 | ProtocolMajorVersion = 2 28 | ProtocolMinorVersion = 3 29 | 30 | // max number of data bytes in non-Sysex messages 31 | MaxDataBytes = 32 32 | 33 | // message command bytes (128-255/0x80-0xFF) 34 | 35 | DigitalMessage FirmataCommand = 0x90 // send data for a digital pin 36 | AnalogMessage FirmataCommand = 0xE0 // send data for an analog pin (or PWM) 37 | EnableAnalogInput FirmataCommand = 0xC0 // enable analog input by pin # 38 | EnableDigitalInput FirmataCommand = 0xD0 // enable digital input by port pair 39 | SetPinMode FirmataCommand = 0xF4 // set a pin to INPUT/OUTPUT/PWM/etc 40 | ReportVersion FirmataCommand = 0xF9 // report protocol version 41 | SystemReset FirmataCommand = 0xFF // reset from MIDI 42 | StartSysEx FirmataCommand = 0xF0 // start a MIDI Sysex message 43 | EndSysEx FirmataCommand = 0xF7 // end a MIDI Sysex message 44 | 45 | // extended command set using sysex (0-127/0x00-0x7F) 46 | /* 0x00-0x0F reserved for user-defined commands */ 47 | ServoConfig SysExCommand = 0x70 // set max angle, minPulse, maxPulse, freq 48 | StringData SysExCommand = 0x71 // a string message with 14-bits per char 49 | ShiftData SysExCommand = 0x75 // a bitstream to/from a shift register 50 | I2CRequest SysExCommand = 0x76 // send an I2C read/write request 51 | I2CReply SysExCommand = 0x77 // a reply to an I2C read request 52 | I2CConfig SysExCommand = 0x78 // config I2C settings such as delay times and power pins 53 | ExtendedAnalog SysExCommand = 0x6F // analog write (PWM, Servo, etc) to any pin 54 | PinStateQuery SysExCommand = 0x6D // ask for a pin's current mode and value 55 | PinStateResponse SysExCommand = 0x6E // reply with pin's current mode and value 56 | CapabilityQuery SysExCommand = 0x6B // ask for supported modes and resolution of all pins 57 | CapabilityResponse SysExCommand = 0x6C // reply with supported modes and resolution 58 | AnalogMappingQuery SysExCommand = 0x69 // ask for mapping of analog to pin numbers 59 | AnalogMappingResponse SysExCommand = 0x6A // reply with mapping info 60 | ReportFirmware SysExCommand = 0x79 // report name and version of the firmware 61 | SamplingInterval SysExCommand = 0x7A // set the poll rate of the main loop 62 | SysExNonRealtime SysExCommand = 0x7E // MIDI Reserved for non-realtime messages 63 | SysExRealtime SysExCommand = 0x7F // MIDI Reserved for realtime messages 64 | Serial SysExCommand = 0x60 65 | SysExSPI SysExCommand = 0x80 66 | 67 | SerialConfig SerialSubCommand = 0x10 68 | SerialComm SerialSubCommand = 0x20 69 | SerialFlush SerialSubCommand = 0x30 70 | SerialClose SerialSubCommand = 0x40 71 | 72 | SPIConfig SPISubCommand = 0x10 73 | SPIComm SPISubCommand = 0x20 74 | 75 | SPI_MODE0 = 0x00 76 | SPI_MODE1 = 0x04 77 | SPI_MODE2 = 0x08 78 | SPI_MODE3 = 0x0C 79 | 80 | SoftSerial SerialPort = 0x00 81 | HardSerial1 SerialPort = 0x01 82 | HardSerial2 SerialPort = 0x02 83 | HardSerial3 SerialPort = 0x03 84 | 85 | // pin modes 86 | Input PinMode = 0x00 87 | Output PinMode = 0x01 88 | Analog PinMode = 0x02 89 | PWM PinMode = 0x03 90 | Servo PinMode = 0x04 91 | Shift PinMode = 0x05 92 | I2C PinMode = 0x06 93 | SPI PinMode = 0x07 94 | ) 95 | 96 | func (m PinMode) String() string { 97 | switch { 98 | case m == Input: 99 | return "INPUT" 100 | case m == Output: 101 | return "OUTPUT" 102 | case m == Analog: 103 | return "ANALOG" 104 | case m == PWM: 105 | return "PWM" 106 | case m == Servo: 107 | return "SERVO" 108 | case m == Shift: 109 | return "SHIFT" 110 | case m == I2C: 111 | return "I2C" 112 | } 113 | return "UNKNOWN" 114 | } 115 | 116 | func (c FirmataCommand) String() string { 117 | switch { 118 | case (c & 0xF0) == DigitalMessage: 119 | return fmt.Sprintf("DigitalMessage (0x%x)", byte(c)) 120 | case (c & 0xF0) == AnalogMessage: 121 | return fmt.Sprintf("AnalogMessage (0x%x)", byte(c)) 122 | case c == EnableAnalogInput: 123 | return fmt.Sprintf("EnableAnalogInput (0x%x)", byte(c)) 124 | case c == EnableDigitalInput: 125 | return fmt.Sprintf("EnableDigitalInput (0x%x)", byte(c)) 126 | case c == SetPinMode: 127 | return fmt.Sprintf("SetPinMode (0x%x)", byte(c)) 128 | case c == ReportVersion: 129 | return fmt.Sprintf("ReportVersion (0x%x)", byte(c)) 130 | case c == SystemReset: 131 | return fmt.Sprintf("SystemReset (0x%x)", byte(c)) 132 | case c == StartSysEx: 133 | return fmt.Sprintf("StartSysEx (0x%x)", byte(c)) 134 | case c == EndSysEx: 135 | return fmt.Sprintf("EndSysEx (0x%x)", byte(c)) 136 | } 137 | return fmt.Sprintf("Unexpected command (0x%x)", byte(c)) 138 | } 139 | 140 | func (c SysExCommand) String() string { 141 | switch { 142 | case c == ServoConfig: 143 | return fmt.Sprintf("ServoConfig (0x%x)", byte(c)) 144 | case c == StringData: 145 | return fmt.Sprintf("StringData (0x%x)", byte(c)) 146 | case c == ShiftData: 147 | return fmt.Sprintf("ShiftData (0x%x)", byte(c)) 148 | case c == I2CRequest: 149 | return fmt.Sprintf("I2CRequest (0x%x)", byte(c)) 150 | case c == I2CReply: 151 | return fmt.Sprintf("I2CReply (0x%x)", byte(c)) 152 | case c == I2CConfig: 153 | return fmt.Sprintf("I2CConfig (0x%x)", byte(c)) 154 | case c == ExtendedAnalog: 155 | return fmt.Sprintf("ExtendedAnalog (0x%x)", byte(c)) 156 | case c == PinStateQuery: 157 | return fmt.Sprintf("PinStateQuery (0x%x)", byte(c)) 158 | case c == PinStateResponse: 159 | return fmt.Sprintf("PinStateResponse (0x%x)", byte(c)) 160 | case c == CapabilityQuery: 161 | return fmt.Sprintf("CapabilityQuery (0x%x)", byte(c)) 162 | case c == CapabilityResponse: 163 | return fmt.Sprintf("CapabilityResponse (0x%x)", byte(c)) 164 | case c == AnalogMappingQuery: 165 | return fmt.Sprintf("AnalogMappingQuery (0x%x)", byte(c)) 166 | case c == AnalogMappingResponse: 167 | return fmt.Sprintf("AnalogMappingResponse (0x%x)", byte(c)) 168 | case c == ReportFirmware: 169 | return fmt.Sprintf("ReportFirmware (0x%x)", byte(c)) 170 | case c == SamplingInterval: 171 | return fmt.Sprintf("SamplingInterval (0x%x)", byte(c)) 172 | case c == SysExNonRealtime: 173 | return fmt.Sprintf("SysExNonRealtime (0x%x)", byte(c)) 174 | case c == SysExRealtime: 175 | return fmt.Sprintf("SysExRealtime (0x%x)", byte(c)) 176 | case c == Serial: 177 | return fmt.Sprintf("Serial (0x%x)", byte(c)) 178 | case c == SysExSPI: 179 | return fmt.Sprintf("SPI (0x%x)", byte(c)) 180 | } 181 | return fmt.Sprintf("Unexpected SysEx command (0x%x)", byte(c)) 182 | } 183 | -------------------------------------------------------------------------------- /contrib/ExtendedFirmata/ExtendedFirmata.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Firmata is a generic protocol for communicating with microcontrollers 3 | * from software on a host computer. It is intended to work with 4 | * any host computer software package. 5 | * 6 | * To download a host software package, please clink on the following link 7 | * to open the download page in your default browser. 8 | * 9 | * http://firmata.org/wiki/Download 10 | */ 11 | 12 | /* 13 | Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. 14 | Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. 15 | Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. 16 | Copyright (C) 2009-2011 Jeff Hoefs. All rights reserved. 17 | Copyright (C) 2014 Krishna Raman. All rights reserved. 18 | 19 | This library is free software; you can redistribute it and/or 20 | modify it under the terms of the GNU Lesser General Public 21 | License as published by the Free Software Foundation; either 22 | version 2.1 of the License, or (at your option) any later version. 23 | 24 | See file LICENSE.txt for further informations on licensing terms. 25 | 26 | formatted using the GNU C formatting and indenting 27 | */ 28 | 29 | /* 30 | * TODO: use Program Control to load stored profiles from EEPROM 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | // move the following defines to Firmata.h? 40 | #define I2C_WRITE B00000000 41 | #define I2C_READ B00001000 42 | #define I2C_READ_CONTINUOUSLY B00010000 43 | #define I2C_STOP_READING B00011000 44 | #define I2C_READ_WRITE_MODE_MASK B00011000 45 | #define I2C_10BIT_ADDRESS_MODE_MASK B00100000 46 | 47 | #define SYSEX_SERIAL 0x60 48 | 49 | #define SERIAL_CONFIG 0x10 50 | #define SERIAL_COMM 0x20 51 | #define SERIAL_FLUSH 0x30 52 | #define SERIAL_CLOSE 0x40 53 | 54 | #define SW_SERIAL 0x00 55 | #define HW_SERIAL1 0x01 56 | #define HW_SERIAL2 0x02 57 | #define HW_SERIAL3 0x03 58 | 59 | #define MAX_QUERIES 8 60 | #define MINIMUM_SAMPLING_INTERVAL 10 61 | 62 | #define REGISTER_NOT_SPECIFIED -1 63 | 64 | #define IS_PIN_SPI(p) ((p) == SS || (p) == MOSI || (p) == MISO || (p) == SCK) 65 | 66 | #define SYSEX_SPI 0x80 67 | #define SPI_CONFIG 0x10 68 | #define SPI_COMM 0x20 69 | 70 | #define PIN_SPI 0x08 // pin included in SPI setup 71 | #define TOTAL_PIN_MODES 8 72 | 73 | /*============================================================================== 74 | * GLOBAL VARIABLES 75 | *============================================================================*/ 76 | 77 | /* analog inputs */ 78 | int analogInputsToReport = 0; // bitwise array to store pin reporting 79 | 80 | /* digital input ports */ 81 | byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence 82 | byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent 83 | 84 | /* pins configuration */ 85 | byte pinConfig[TOTAL_PINS]; // configuration of every pin 86 | byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything 87 | // else 88 | int pinState[TOTAL_PINS]; // any value that has been written 89 | 90 | /* timer variables */ 91 | unsigned long currentMillis; // store the current value from millis() 92 | unsigned long previousMillis; // for comparison with currentMillis 93 | int samplingInterval = 19; // how often to run the main loop (in ms) 94 | 95 | /* i2c data */ 96 | struct i2c_device_info { 97 | byte addr; 98 | byte reg; 99 | byte bytes; 100 | }; 101 | 102 | /* for i2c read continuous more */ 103 | i2c_device_info query[MAX_QUERIES]; 104 | 105 | byte i2cRxData[32]; 106 | boolean isI2CEnabled = false; 107 | signed char queryIndex = -1; 108 | unsigned int i2cReadDelayTime = 109 | 0; // default delay time between i2c read request and Wire.requestFrom() 110 | 111 | Servo servos[MAX_SERVOS]; 112 | 113 | Stream *serialPort = NULL; 114 | byte *serialReadBuffer = NULL; 115 | int serialReadBufferPos = 0; 116 | byte serialReadTermChar = '\n'; 117 | int serialReadBufferLen = 0; 118 | 119 | /*============================================================================== 120 | * FUNCTIONS 121 | *============================================================================*/ 122 | 123 | void setPinModeCallback(byte pin, int mode); 124 | void reportAnalogCallback(byte analogPin, int value); 125 | void disableI2CPins(); 126 | void enableI2CPins(); 127 | 128 | void readAndReportData(byte address, int theRegister, byte numBytes) { 129 | // allow I2C requests that don't require a register read 130 | // for example, some devices using an interrupt pin to signify new data 131 | // available 132 | // do not always require the register read so upon interrupt you call 133 | // Wire.requestFrom() 134 | if (theRegister != REGISTER_NOT_SPECIFIED) { 135 | Wire.beginTransmission(address); 136 | #if ARDUINO >= 100 137 | Wire.write((byte)theRegister); 138 | #else 139 | Wire.send((byte)theRegister); 140 | #endif 141 | Wire.endTransmission(); 142 | delayMicroseconds(i2cReadDelayTime); // delay is necessary for some devices 143 | // such as WiiNunchuck 144 | } else { 145 | theRegister = 0; // fill the register with a dummy value 146 | } 147 | 148 | Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom 149 | 150 | // check to be sure correct number of bytes were returned by slave 151 | if (numBytes == Wire.available()) { 152 | i2cRxData[0] = address; 153 | i2cRxData[1] = theRegister; 154 | for (int i = 0; i < numBytes; i++) { 155 | #if ARDUINO >= 100 156 | i2cRxData[2 + i] = Wire.read(); 157 | #else 158 | i2cRxData[2 + i] = Wire.receive(); 159 | #endif 160 | } 161 | } else { 162 | if (numBytes > Wire.available()) { 163 | Firmata.sendString("I2C Read Error: Too many bytes received"); 164 | } else { 165 | Firmata.sendString("I2C Read Error: Too few bytes received"); 166 | } 167 | } 168 | 169 | // send slave address, register and received bytes 170 | Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); 171 | } 172 | 173 | void outputPort(byte portNumber, byte portValue, byte forceSend) { 174 | // pins not configured as INPUT are cleared to zeros 175 | portValue = portValue & portConfigInputs[portNumber]; 176 | // only send if the value is different than previously sent 177 | if (forceSend || previousPINs[portNumber] != portValue) { 178 | Firmata.sendDigitalPort(portNumber, portValue); 179 | previousPINs[portNumber] = portValue; 180 | } 181 | } 182 | 183 | /* ----------------------------------------------------------------------------- 184 | * check all the active digital inputs for change of state, then add any events 185 | * to the Serial output queue using Serial.print() */ 186 | void checkDigitalInputs(void) { 187 | /* Using non-looping code allows constants to be given to readPort(). 188 | * The compiler will apply substantial optimizations if the inputs 189 | * to readPort() are compile-time constants. */ 190 | if (TOTAL_PORTS > 0 && reportPINs[0]) 191 | outputPort(0, readPort(0, portConfigInputs[0]), false); 192 | if (TOTAL_PORTS > 1 && reportPINs[1]) 193 | outputPort(1, readPort(1, portConfigInputs[1]), false); 194 | if (TOTAL_PORTS > 2 && reportPINs[2]) 195 | outputPort(2, readPort(2, portConfigInputs[2]), false); 196 | if (TOTAL_PORTS > 3 && reportPINs[3]) 197 | outputPort(3, readPort(3, portConfigInputs[3]), false); 198 | if (TOTAL_PORTS > 4 && reportPINs[4]) 199 | outputPort(4, readPort(4, portConfigInputs[4]), false); 200 | if (TOTAL_PORTS > 5 && reportPINs[5]) 201 | outputPort(5, readPort(5, portConfigInputs[5]), false); 202 | if (TOTAL_PORTS > 6 && reportPINs[6]) 203 | outputPort(6, readPort(6, portConfigInputs[6]), false); 204 | if (TOTAL_PORTS > 7 && reportPINs[7]) 205 | outputPort(7, readPort(7, portConfigInputs[7]), false); 206 | if (TOTAL_PORTS > 8 && reportPINs[8]) 207 | outputPort(8, readPort(8, portConfigInputs[8]), false); 208 | if (TOTAL_PORTS > 9 && reportPINs[9]) 209 | outputPort(9, readPort(9, portConfigInputs[9]), false); 210 | if (TOTAL_PORTS > 10 && reportPINs[10]) 211 | outputPort(10, readPort(10, portConfigInputs[10]), false); 212 | if (TOTAL_PORTS > 11 && reportPINs[11]) 213 | outputPort(11, readPort(11, portConfigInputs[11]), false); 214 | if (TOTAL_PORTS > 12 && reportPINs[12]) 215 | outputPort(12, readPort(12, portConfigInputs[12]), false); 216 | if (TOTAL_PORTS > 13 && reportPINs[13]) 217 | outputPort(13, readPort(13, portConfigInputs[13]), false); 218 | if (TOTAL_PORTS > 14 && reportPINs[14]) 219 | outputPort(14, readPort(14, portConfigInputs[14]), false); 220 | if (TOTAL_PORTS > 15 && reportPINs[15]) 221 | outputPort(15, readPort(15, portConfigInputs[15]), false); 222 | } 223 | 224 | // ----------------------------------------------------------------------------- 225 | /* sets the pin mode to the correct state and sets the relevant bits in the 226 | * two bit-arrays that track Digital I/O and PWM status 227 | */ 228 | void setPinModeCallback(byte pin, int mode) { 229 | if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) { 230 | // disable i2c so pins can be used for other functions 231 | // the following if statements should reconfigure the pins properly 232 | disableI2CPins(); 233 | } 234 | if (IS_PIN_SERVO(pin) && mode != SERVO && 235 | servos[PIN_TO_SERVO(pin)].attached()) { 236 | servos[PIN_TO_SERVO(pin)].detach(); 237 | } 238 | if (IS_PIN_ANALOG(pin)) { 239 | reportAnalogCallback(PIN_TO_ANALOG(pin), 240 | mode == ANALOG ? 1 : 0); // turn on/off reporting 241 | } 242 | if (IS_PIN_DIGITAL(pin)) { 243 | if (mode == INPUT) { 244 | portConfigInputs[pin / 8] |= (1 << (pin & 7)); 245 | } else { 246 | portConfigInputs[pin / 8] &= ~(1 << (pin & 7)); 247 | } 248 | } 249 | pinState[pin] = 0; 250 | switch (mode) { 251 | case ANALOG: 252 | if (IS_PIN_ANALOG(pin)) { 253 | if (IS_PIN_DIGITAL(pin)) { 254 | pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver 255 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups 256 | } 257 | pinConfig[pin] = ANALOG; 258 | } 259 | break; 260 | case INPUT: 261 | if (IS_PIN_DIGITAL(pin)) { 262 | pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver 263 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups 264 | pinConfig[pin] = INPUT; 265 | } 266 | break; 267 | case OUTPUT: 268 | if (IS_PIN_DIGITAL(pin)) { 269 | digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM 270 | pinMode(PIN_TO_DIGITAL(pin), OUTPUT); 271 | pinConfig[pin] = OUTPUT; 272 | } 273 | break; 274 | case PWM: 275 | if (IS_PIN_PWM(pin)) { 276 | pinMode(PIN_TO_PWM(pin), OUTPUT); 277 | analogWrite(PIN_TO_PWM(pin), 0); 278 | pinConfig[pin] = PWM; 279 | } 280 | break; 281 | case SERVO: 282 | if (IS_PIN_SERVO(pin)) { 283 | pinConfig[pin] = SERVO; 284 | if (!servos[PIN_TO_SERVO(pin)].attached()) { 285 | servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin)); 286 | } 287 | } 288 | break; 289 | case I2C: 290 | if (IS_PIN_I2C(pin)) { 291 | // mark the pin as i2c 292 | // the user must call I2C_CONFIG to enable I2C for a device 293 | pinConfig[pin] = I2C; 294 | } 295 | break; 296 | case PIN_SPI: 297 | if (IS_PIN_SPI(pin)) { 298 | // mark pin as SPI 299 | // the user must call SPI_CONFIG to enable SPI for a device 300 | pinConfig[pin] = PIN_SPI; 301 | } 302 | default: 303 | Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM 304 | } 305 | // TODO: save status to EEPROM here, if changed 306 | } 307 | 308 | void analogWriteCallback(byte pin, int value) { 309 | if (pin < TOTAL_PINS) { 310 | switch (pinConfig[pin]) { 311 | case SERVO: 312 | if (IS_PIN_SERVO(pin)) 313 | servos[PIN_TO_SERVO(pin)].write(value); 314 | pinState[pin] = value; 315 | break; 316 | case PWM: 317 | if (IS_PIN_PWM(pin)) 318 | analogWrite(PIN_TO_PWM(pin), value); 319 | pinState[pin] = value; 320 | break; 321 | } 322 | } 323 | } 324 | 325 | void digitalWriteCallback(byte port, int value) { 326 | byte pin, lastPin, mask = 1, pinWriteMask = 0; 327 | 328 | if (port < TOTAL_PORTS) { 329 | // create a mask of the pins on this port that are writable. 330 | lastPin = port * 8 + 8; 331 | if (lastPin > TOTAL_PINS) 332 | lastPin = TOTAL_PINS; 333 | for (pin = port * 8; pin < lastPin; pin++) { 334 | // do not disturb non-digital pins (eg, Rx & Tx) 335 | if (IS_PIN_DIGITAL(pin)) { 336 | // only write to OUTPUT and INPUT (enables pullup) 337 | // do not touch pins in PWM, ANALOG, SERVO or other modes 338 | if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) { 339 | pinWriteMask |= mask; 340 | pinState[pin] = ((byte)value & mask) ? 1 : 0; 341 | } 342 | } 343 | mask = mask << 1; 344 | } 345 | writePort(port, (byte)value, pinWriteMask); 346 | } 347 | } 348 | 349 | // ----------------------------------------------------------------------------- 350 | /* sets bits in a bit array (int) to toggle the reporting of the analogIns 351 | */ 352 | // void FirmataClass::setAnalogPinReporting(byte pin, byte state) { 353 | //} 354 | void reportAnalogCallback(byte analogPin, int value) { 355 | if (analogPin < TOTAL_ANALOG_PINS) { 356 | if (value == 0) { 357 | analogInputsToReport = analogInputsToReport & ~(1 << analogPin); 358 | } else { 359 | analogInputsToReport = analogInputsToReport | (1 << analogPin); 360 | } 361 | } 362 | // TODO: save status to EEPROM here, if changed 363 | } 364 | 365 | void reportDigitalCallback(byte port, int value) { 366 | if (port < TOTAL_PORTS) { 367 | reportPINs[port] = (byte)value; 368 | } 369 | // do not disable analog reporting on these 8 pins, to allow some 370 | // pins used for digital, others analog. Instead, allow both types 371 | // of reporting to be enabled, but check if the pin is configured 372 | // as analog when sampling the analog inputs. Likewise, while 373 | // scanning digital pins, portConfigInputs will mask off values from any 374 | // pins configured as analog 375 | } 376 | 377 | /*============================================================================== 378 | * SYSEX-BASED commands 379 | *============================================================================*/ 380 | 381 | void sysexCallback(byte command, byte argc, byte *argv) { 382 | byte mode; 383 | byte slaveAddress; 384 | byte slaveRegister; 385 | byte data; 386 | unsigned int delayTime; 387 | 388 | switch (command) { 389 | case I2C_REQUEST: 390 | mode = argv[1] & I2C_READ_WRITE_MODE_MASK; 391 | if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { 392 | Firmata.sendString("10-bit addressing mode is not yet supported"); 393 | return; 394 | } else { 395 | slaveAddress = argv[0]; 396 | } 397 | 398 | switch (mode) { 399 | case I2C_WRITE: 400 | Wire.beginTransmission(slaveAddress); 401 | for (byte i = 2; i < argc; i += 2) { 402 | data = argv[i] + (argv[i + 1] << 7); 403 | #if ARDUINO >= 100 404 | Wire.write(data); 405 | #else 406 | Wire.send(data); 407 | #endif 408 | } 409 | Wire.endTransmission(); 410 | delayMicroseconds(70); 411 | break; 412 | case I2C_READ: 413 | if (argc == 6) { 414 | // a slave register is specified 415 | slaveRegister = argv[2] + (argv[3] << 7); 416 | data = argv[4] + (argv[5] << 7); // bytes to read 417 | readAndReportData(slaveAddress, (int)slaveRegister, data); 418 | } else { 419 | // a slave register is NOT specified 420 | data = argv[2] + (argv[3] << 7); // bytes to read 421 | readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data); 422 | } 423 | break; 424 | case I2C_READ_CONTINUOUSLY: 425 | if ((queryIndex + 1) >= MAX_QUERIES) { 426 | // too many queries, just ignore 427 | Firmata.sendString("too many queries"); 428 | break; 429 | } 430 | queryIndex++; 431 | query[queryIndex].addr = slaveAddress; 432 | query[queryIndex].reg = argv[2] + (argv[3] << 7); 433 | query[queryIndex].bytes = argv[4] + (argv[5] << 7); 434 | break; 435 | case I2C_STOP_READING: 436 | byte queryIndexToSkip; 437 | // if read continuous mode is enabled for only 1 i2c device, disable 438 | // read continuous reporting for that device 439 | if (queryIndex <= 0) { 440 | queryIndex = -1; 441 | } else { 442 | // if read continuous mode is enabled for multiple devices, 443 | // determine which device to stop reading and remove it's data from 444 | // the array, shifiting other array data to fill the space 445 | for (byte i = 0; i < queryIndex + 1; i++) { 446 | if (query[i].addr == slaveAddress) { 447 | queryIndexToSkip = i; 448 | break; 449 | } 450 | } 451 | 452 | for (byte i = queryIndexToSkip; i < queryIndex + 1; i++) { 453 | if (i < MAX_QUERIES) { 454 | query[i].addr = query[i + 1].addr; 455 | query[i].reg = query[i + 1].addr; 456 | query[i].bytes = query[i + 1].bytes; 457 | } 458 | } 459 | queryIndex--; 460 | } 461 | break; 462 | default: 463 | break; 464 | } 465 | break; 466 | case I2C_CONFIG: 467 | delayTime = (argv[0] + (argv[1] << 7)); 468 | 469 | if (delayTime > 0) { 470 | i2cReadDelayTime = delayTime; 471 | } 472 | 473 | if (!isI2CEnabled) { 474 | enableI2CPins(); 475 | } 476 | 477 | break; 478 | case SERVO_CONFIG: 479 | if (argc > 4) { 480 | // these vars are here for clarity, they'll optimized away by the compiler 481 | byte pin = argv[0]; 482 | int minPulse = argv[1] + (argv[2] << 7); 483 | int maxPulse = argv[3] + (argv[4] << 7); 484 | 485 | if (IS_PIN_SERVO(pin)) { 486 | if (servos[PIN_TO_SERVO(pin)].attached()) 487 | servos[PIN_TO_SERVO(pin)].detach(); 488 | servos[PIN_TO_SERVO(pin)] 489 | .attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse); 490 | setPinModeCallback(pin, SERVO); 491 | } 492 | } 493 | break; 494 | case SAMPLING_INTERVAL: 495 | if (argc > 1) { 496 | samplingInterval = argv[0] + (argv[1] << 7); 497 | if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { 498 | samplingInterval = MINIMUM_SAMPLING_INTERVAL; 499 | } 500 | } else { 501 | // Firmata.sendString("Not enough data"); 502 | } 503 | break; 504 | case EXTENDED_ANALOG: 505 | if (argc > 1) { 506 | int val = argv[1]; 507 | if (argc > 2) 508 | val |= (argv[2] << 7); 509 | if (argc > 3) 510 | val |= (argv[3] << 14); 511 | analogWriteCallback(argv[0], val); 512 | } 513 | break; 514 | case CAPABILITY_QUERY: 515 | Serial.write(START_SYSEX); 516 | Serial.write(CAPABILITY_RESPONSE); 517 | for (byte pin = 0; pin < TOTAL_PINS; pin++) { 518 | if (IS_PIN_DIGITAL(pin)) { 519 | Serial.write((byte)INPUT); 520 | Serial.write(1); 521 | Serial.write((byte)OUTPUT); 522 | Serial.write(1); 523 | } 524 | if (IS_PIN_ANALOG(pin)) { 525 | Serial.write(ANALOG); 526 | Serial.write(10); 527 | } 528 | if (IS_PIN_PWM(pin)) { 529 | Serial.write(PWM); 530 | Serial.write(8); 531 | } 532 | if (IS_PIN_SERVO(pin)) { 533 | Serial.write(SERVO); 534 | Serial.write(14); 535 | } 536 | if (IS_PIN_I2C(pin)) { 537 | Serial.write(I2C); 538 | Serial.write(1); // to do: determine appropriate value 539 | } 540 | Serial.write(127); 541 | } 542 | Serial.write(END_SYSEX); 543 | break; 544 | case PIN_STATE_QUERY: 545 | if (argc > 0) { 546 | byte pin = argv[0]; 547 | Serial.write(START_SYSEX); 548 | Serial.write(PIN_STATE_RESPONSE); 549 | Serial.write(pin); 550 | if (pin < TOTAL_PINS) { 551 | Serial.write((byte)pinConfig[pin]); 552 | Serial.write((byte)pinState[pin] & 0x7F); 553 | if (pinState[pin] & 0xFF80) 554 | Serial.write((byte)(pinState[pin] >> 7) & 0x7F); 555 | if (pinState[pin] & 0xC000) 556 | Serial.write((byte)(pinState[pin] >> 14) & 0x7F); 557 | } 558 | Serial.write(END_SYSEX); 559 | } 560 | break; 561 | case SYSEX_SERIAL: { 562 | byte subCommand = argv[0] & 0xF0; 563 | byte port = argv[0] & 0x0F; 564 | 565 | switch (subCommand) { 566 | case SERIAL_CONFIG: { 567 | if (serialPort != NULL) { 568 | Firmata.sendString( 569 | "Close existing serial connection before opening new one"); 570 | break; 571 | } 572 | long baud = 573 | ((long)argv[1]) | (((long)argv[2]) << 7) | (((long)argv[3]) << 14); 574 | serialReadBufferLen = (int)(argv[4] | argv[5] << 7 | argv[6] << 14); 575 | serialReadBuffer = (byte *)calloc(sizeof(byte), serialReadBufferLen); 576 | serialReadTermChar = (byte)(argv[7] | argv[8] << 7); 577 | serialReadBufferPos = 0; 578 | 579 | // byte txPin = argv[4]; 580 | // byte rxPin = argv[5]; 581 | switch (port) { 582 | case HW_SERIAL1: 583 | Serial1.begin(baud); 584 | serialPort = &Serial1; 585 | break; 586 | case HW_SERIAL2: 587 | Serial2.begin(baud); 588 | serialPort = &Serial2; 589 | break; 590 | case HW_SERIAL3: 591 | Serial3.begin(baud); 592 | serialPort = &Serial3; 593 | break; 594 | } 595 | break; 596 | } 597 | case SERIAL_COMM: { 598 | if (serialPort == NULL) { 599 | break; 600 | } 601 | byte data; 602 | // reassemble data bytes and forward to sw serial write buffer 603 | for (int i = 1; i < argc; i += 2) { 604 | data = argv[i] + (argv[i + 1] << 7); 605 | serialPort->write(data); 606 | } 607 | break; 608 | } 609 | case SERIAL_FLUSH: 610 | if (serialPort == NULL) { 611 | break; 612 | } 613 | serialPort->flush(); 614 | break; 615 | case SERIAL_CLOSE: 616 | if (serialPort == NULL) { 617 | break; 618 | } 619 | ((HardwareSerial *)serialPort)->end(); 620 | serialPort = NULL; 621 | free(serialReadBuffer); 622 | break; 623 | } 624 | break; 625 | } 626 | case SYSEX_SPI: { 627 | byte subCommand = argv[0] & 0xF0; 628 | switch (subCommand) { 629 | case SPI_CONFIG: { 630 | pinMode(MISO, INPUT); 631 | pinMode(MOSI, OUTPUT); 632 | pinMode(SCK, OUTPUT); 633 | pinMode(SS, OUTPUT); 634 | 635 | byte csPin = 40; //argv[1] | (argv[2] << 7); 636 | byte mode = SPI_MODE3; //argv[3] | (argv[4] << 7); 637 | pinMode(csPin, OUTPUT); 638 | digitalWrite(csPin, HIGH); 639 | SPI.begin(); 640 | SPI.setDataMode(mode); 641 | break; 642 | } 643 | case SPI_COMM: { 644 | byte csPin = 40; //argv[1] | (argv[2] << 7); 645 | Serial.write(START_SYSEX); 646 | Serial.write(SYSEX_SPI); 647 | Serial.write(SPI_COMM); 648 | Serial.write(csPin & 0x7F); 649 | Serial.write((csPin >> 7) & 0x7F); 650 | 651 | digitalWrite(csPin, LOW); 652 | for (int i = 3; i < argc; i += 2) { 653 | byte dataIn = argv[i] | (argv[i+1] << 7); 654 | byte dataOut = SPI.transfer(dataIn); 655 | Serial.write((byte)(dataOut & 0x7F)); 656 | Serial.write((byte)((dataOut >> 7) & 0x7F)); 657 | } 658 | digitalWrite(csPin, HIGH); 659 | 660 | Serial.write(END_SYSEX); 661 | break; 662 | } 663 | } 664 | break; 665 | } 666 | case ANALOG_MAPPING_QUERY: 667 | Serial.write(START_SYSEX); 668 | Serial.write(ANALOG_MAPPING_RESPONSE); 669 | for (byte pin = 0; pin < TOTAL_PINS; pin++) { 670 | Serial.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127); 671 | } 672 | Serial.write(END_SYSEX); 673 | break; 674 | } 675 | } 676 | 677 | void enableI2CPins() { 678 | byte i; 679 | // is there a faster way to do this? would probaby require importing 680 | // Arduino.h to get SCL and SDA pins 681 | for (i = 0; i < TOTAL_PINS; i++) { 682 | if (IS_PIN_I2C(i)) { 683 | // mark pins as i2c so they are ignore in non i2c data requests 684 | setPinModeCallback(i, I2C); 685 | } 686 | } 687 | 688 | isI2CEnabled = true; 689 | 690 | // is there enough time before the first I2C request to call this here? 691 | Wire.begin(); 692 | } 693 | 694 | /* disable the i2c pins so they can be used for other functions */ 695 | void disableI2CPins() { 696 | isI2CEnabled = false; 697 | // disable read continuous mode for all devices 698 | queryIndex = -1; 699 | // uncomment the following if or when the end() method is added to Wire 700 | // library 701 | // Wire.end(); 702 | } 703 | 704 | /*============================================================================== 705 | * SETUP() 706 | *============================================================================*/ 707 | 708 | void systemResetCallback() { 709 | // initialize a defalt state 710 | // TODO: option to load config from EEPROM instead of default 711 | if (isI2CEnabled) { 712 | disableI2CPins(); 713 | } 714 | for (byte i = 0; i < TOTAL_PORTS; i++) { 715 | reportPINs[i] = false; // by default, reporting off 716 | portConfigInputs[i] = 0; // until activated 717 | previousPINs[i] = 0; 718 | } 719 | // pins with analog capability default to analog input 720 | // otherwise, pins default to digital output 721 | for (byte i = 0; i < TOTAL_PINS; i++) { 722 | if (IS_PIN_ANALOG(i)) { 723 | // turns off pullup, configures everything 724 | setPinModeCallback(i, ANALOG); 725 | } else { 726 | // sets the output to 0, configures portConfigInputs 727 | setPinModeCallback(i, OUTPUT); 728 | } 729 | } 730 | // by default, do not report any analog inputs 731 | analogInputsToReport = 0; 732 | 733 | /* send digital inputs to set the initial state on the host computer, 734 | * since once in the loop(), this firmware will only send on change */ 735 | /* 736 | TODO: this can never execute, since no pins default to digital input 737 | but it will be needed when/if we support EEPROM stored config 738 | for (byte i=0; i < TOTAL_PORTS; i++) { 739 | outputPort(i, readPort(i, portConfigInputs[i]), true); 740 | } 741 | */ 742 | } 743 | 744 | void setup() { 745 | Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION); 746 | 747 | Firmata.attach(ANALOG_MESSAGE, analogWriteCallback); 748 | Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback); 749 | Firmata.attach(REPORT_ANALOG, reportAnalogCallback); 750 | Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); 751 | Firmata.attach(SET_PIN_MODE, setPinModeCallback); 752 | Firmata.attach(START_SYSEX, sysexCallback); 753 | Firmata.attach(SYSTEM_RESET, systemResetCallback); 754 | 755 | Firmata.begin(57600); 756 | systemResetCallback(); // reset to default config 757 | } 758 | 759 | /*============================================================================== 760 | * LOOP() 761 | *============================================================================*/ 762 | void loop() { 763 | byte pin, analogPin; 764 | 765 | /* DIGITALREAD - as fast as possible, check for changes and output them to 766 | * the 767 | * FTDI buffer using Serial.print() */ 768 | checkDigitalInputs(); 769 | 770 | /* SERIALREAD - processing incoming messagse as soon as possible, while 771 | * still 772 | * checking digital inputs. */ 773 | while (Firmata.available()) 774 | Firmata.processInput(); 775 | 776 | if (serialPort != NULL) { 777 | while (serialPort->available() > 0) { 778 | byte inChar = serialPort->read(); 779 | serialReadBuffer[serialReadBufferPos++] = inChar; 780 | if (inChar == serialReadTermChar || 781 | (serialReadBufferPos + 2) >= serialReadBufferLen) { 782 | Serial.write(START_SYSEX); 783 | Serial.write(SYSEX_SERIAL); 784 | Serial.write(SERIAL_COMM); 785 | for (int i = 0; i < serialReadBufferPos; i++) { 786 | Serial.write((byte)(serialReadBuffer[i] & 0x7F)); 787 | Serial.write((byte)((serialReadBuffer[i] >> 7) & 0x7F)); 788 | } 789 | Serial.write(END_SYSEX); 790 | serialReadBufferPos = 0; 791 | } 792 | } 793 | } 794 | 795 | /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over 796 | * 60 bytes. use a timer to sending an event character every 4 ms to 797 | * trigger the buffer to dump. */ 798 | 799 | currentMillis = millis(); 800 | if (currentMillis - previousMillis > samplingInterval) { 801 | previousMillis += samplingInterval; 802 | /* ANALOGREAD - do all analogReads() at the configured sampling interval 803 | */ 804 | for (pin = 0; pin < TOTAL_PINS; pin++) { 805 | if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) { 806 | analogPin = PIN_TO_ANALOG(pin); 807 | if (analogInputsToReport & (1 << analogPin)) { 808 | Firmata.sendAnalog(analogPin, analogRead(analogPin)); 809 | } 810 | } 811 | } 812 | // report i2c data for all device with read continuous mode enabled 813 | if (queryIndex > -1) { 814 | for (byte i = 0; i < queryIndex + 1; i++) { 815 | readAndReportData(query[i].addr, query[i].reg, query[i].bytes); 816 | } 817 | } 818 | } 819 | } -------------------------------------------------------------------------------- /contrib/ExtendedFirmata/Makefile: -------------------------------------------------------------------------------- 1 | #Copyright 2014 Krishna Raman 2 | # 3 | #Licensed under the Apache License, Version 2.0 (the "License"); 4 | #you may not use this file except in compliance with the License. 5 | #You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | #Unless required by applicable law or agreed to in writing, software 10 | #distributed under the License is distributed on an "AS IS" BASIS, 11 | #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | #See the License for the specific language governing permissions and 13 | #limitations under the License. 14 | 15 | AVR_GCC=/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-gcc 16 | AVR_GPP=/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-gcc 17 | AVR_AR=/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-ar 18 | AVR_OBJCOPY=/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-objcopy 19 | AVRDUDE=/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avrdude -C/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf 20 | 21 | ARDUINO_HW_INCLUDE=/Applications/Arduino.app/Contents/Resources/Java/hardware 22 | ARDUINO_LIB_INCLUDE=/Applications/Arduino.app/Contents/Resources/Java/libraries 23 | 24 | AVR_LIBC=obj/core/malloc.o obj/core/realloc.o 25 | HW_C_OBJECTS=obj/core/WInterrupts.o obj/core/wiring.o obj/core/wiring_analog.o obj/core/wiring_digital.o obj/core/wiring_pulse.o obj/core/wiring_shift.o 26 | HW_CPP_OBJECTS=obj/core/CDC.o obj/core/HardwareSerial.o obj/core/HID.o obj/core/IPAddress.o obj/core/main.o obj/core/new.o obj/core/Print.o obj/core/Stream.o \ 27 | obj/core/Tone.o obj/core/USBCore.o obj/core/WMath.o obj/core/WString.o 28 | 29 | INO=ExtendedFirmata 30 | SOURCES=obj/ExtendedFirmata.o 31 | LIBS=Servo Wire Firmata SPI 32 | 33 | # Programming support using avrdude. Settings and variables. 34 | MCU = atmega2560 35 | F_CPU = 16000000 36 | UPLOAD_RATE = 115200 37 | AVRDUDE_PROGRAMMER=wiring 38 | AVRDUDE_PORT=/dev/tty.usbmodem1411 39 | AVRDUDE_WRITE_FLASH=-U flash:w:obj/$(INO).hex:i 40 | AVRDUDE_FLAGS=-F -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) -b $(UPLOAD_RATE) -D 41 | 42 | build: dirs obj/core.a $(SOURCES) libraries cleanup 43 | $(AVR_GCC) -Os -Wl,--gc-sections,--relax -mmcu=atmega2560 -o obj/$(INO).elf obj/*.o obj/core/core.a -lm 44 | $(AVR_OBJCOPY) -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 obj/$(INO).elf obj/$(INO).eep 45 | $(AVR_OBJCOPY) -O ihex -R .eeprom obj/$(INO).elf obj/$(INO).hex 46 | 47 | dirs: 48 | mkdir -p obj/core 49 | ln -sf $(INO).ino $(INO).cpp 50 | 51 | cleanup: 52 | rm $(INO).cpp 53 | 54 | upload: build 55 | $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) 56 | 57 | libraries: 58 | pushd obj; \ 59 | for i in $(LIBS); do \ 60 | echo $$i; \ 61 | CFILES=`find $(ARDUINO_LIB_INCLUDE)/$$i -name \\*.c`; \ 62 | CPPFILES=`find $(ARDUINO_LIB_INCLUDE)/$$i -name \\*.cpp`; \ 63 | INC=""; \ 64 | for j in `find $(ARDUINO_LIB_INCLUDE)/$$i -type d | grep -v example`; do \ 65 | INC="$$INC -I$$j"; \ 66 | done; \ 67 | for j in $$CFILES; do \ 68 | $(AVR_GCC) -c -g -Os -Wall -ffunction-sections -fdata-sections -mmcu=atmega2560 \ 69 | -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 70 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 71 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 72 | $$INC $$j; \ 73 | done; \ 74 | for j in $$CPPFILES; do \ 75 | $(AVR_GPP) -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega2560 \ 76 | -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 77 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 78 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 79 | $$INC $$j; \ 80 | done; \ 81 | done; \ 82 | popd 83 | 84 | obj/core.a: $(AVR_LIBC) $(HW_C_OBJECTS) $(HW_CPP_OBJECTS) 85 | for i in $(AVR_LIBC) $(HW_C_OBJECTS) $(HW_CPP_OBJECTS); do \ 86 | $(AVR_AR) rcs obj/core/core.a $$i; \ 87 | done 88 | 89 | $(SOURCES): obj/%.o: %.cpp 90 | $(AVR_GPP) -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections \ 91 | -mmcu=atmega2560 -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 92 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 93 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 94 | -I$(ARDUINO_LIB_INCLUDE)/Servo \ 95 | -I$(ARDUINO_LIB_INCLUDE)/Wire \ 96 | -I$(ARDUINO_LIB_INCLUDE)/Firmata \ 97 | -I$(ARDUINO_LIB_INCLUDE)/SPI \ 98 | $< -o $@ 99 | 100 | $(AVR_LIBC): obj/core/%.o: $(ARDUINO_HW_INCLUDE)/arduino/cores/arduino/avr-libc/%.c 101 | $(AVR_GCC) -c -g -Os -Wall -ffunction-sections -fdata-sections -fpermissive\ 102 | -mmcu=atmega2560 -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 103 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 104 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 105 | $< -o $@ 106 | 107 | $(HW_C_OBJECTS): obj/core/%.o: $(ARDUINO_HW_INCLUDE)/arduino/cores/arduino/%.c 108 | $(AVR_GCC) -c -g -Os -Wall -ffunction-sections -fdata-sections \ 109 | -mmcu=atmega2560 -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 110 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 111 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 112 | $< -o $@ 113 | 114 | $(HW_CPP_OBJECTS): obj/core/%.o: $(ARDUINO_HW_INCLUDE)/arduino/cores/arduino/%.cpp 115 | $(AVR_GPP) -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections \ 116 | -mmcu=atmega2560 -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 \ 117 | -I$(ARDUINO_HW_INCLUDE)/arduino/cores/arduino \ 118 | -I$(ARDUINO_HW_INCLUDE)/arduino/variants/mega \ 119 | $< -o $@ 120 | -------------------------------------------------------------------------------- /examples/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/argandas/go-firmata" 5 | "time" 6 | ) 7 | 8 | var led uint8 = 13 9 | 10 | func main() { 11 | arduino, err := firmata.NewClient("COM24", 57600) 12 | if err != nil { 13 | panic(err) 14 | } 15 | 16 | // arduino.Verbose = true 17 | 18 | myDelay := time.Millisecond * 250 19 | 20 | arduino.SetPinMode(led, firmata.Output) 21 | for x := 0; x < 10; x++ { 22 | // Turn ON led 23 | arduino.DigitalWrite(led, true) 24 | arduino.Delay(myDelay) 25 | // Turn OFF led 26 | arduino.DigitalWrite(led, false) 27 | arduino.Delay(myDelay) 28 | 29 | } 30 | arduino.Close() 31 | } 32 | -------------------------------------------------------------------------------- /reply.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | import ( 18 | "bufio" 19 | "fmt" 20 | ) 21 | 22 | type FirmataValue struct { 23 | valueType FirmataCommand 24 | value int 25 | analogChannelPinsMap map[byte]int 26 | } 27 | 28 | func (v FirmataValue) IsAnalog() bool { 29 | return (v.valueType & 0xF0) == AnalogMessage 30 | } 31 | 32 | func (v FirmataValue) GetAnalogValue() (pin int, val int, err error) { 33 | if !v.IsAnalog() { 34 | err = fmt.Errorf("Cannot get analog value for digital pin") 35 | return 36 | } 37 | pin = v.analogChannelPinsMap[byte(v.valueType & ^AnalogMessage)] 38 | val = v.value 39 | return 40 | } 41 | 42 | func (v FirmataValue) GetDigitalValue() (port byte, val map[byte]interface{}, err error) { 43 | if v.IsAnalog() { 44 | err = fmt.Errorf("Cannot get digital value for analog pin") 45 | return 46 | } 47 | 48 | port = byte(v.valueType & ^DigitalMessage) 49 | val = make(map[byte]interface{}) 50 | mask := 0x01 51 | for i := byte(0); i < 8; i++ { 52 | val[port*8+i] = ((v.value & mask) > 0) 53 | mask = mask * 2 54 | } 55 | return 56 | } 57 | 58 | func (v FirmataValue) String() string { 59 | if v.IsAnalog() { 60 | p, v, _ := v.GetAnalogValue() 61 | return fmt.Sprintf("Analog value %v = %v", p, v) 62 | } else { 63 | p, v, _ := v.GetAnalogValue() 64 | return fmt.Sprintf("Digital port %v = %b", p, v) 65 | } 66 | } 67 | 68 | func (c *FirmataClient) replyReader() { 69 | r := bufio.NewReader(*c.conn) 70 | c.valueChan = make(chan FirmataValue) 71 | var init bool 72 | for { 73 | b, err := (r.ReadByte()) 74 | if err != nil { 75 | c.Log.Print(err) 76 | return 77 | } 78 | cmd := FirmataCommand(b) 79 | if c.Verbose { 80 | c.Log.Printf("Incoming cmd %v", cmd) 81 | } 82 | if !init { 83 | if cmd != ReportVersion { 84 | if c.Verbose { 85 | c.Log.Printf("Discarding unexpected command byte %0d (not initialized)\n", b) 86 | } 87 | continue 88 | } else { 89 | init = true 90 | } 91 | } 92 | 93 | switch { 94 | case cmd == ReportVersion: 95 | c.protocolVersion = make([]byte, 2) 96 | c.protocolVersion[0], err = r.ReadByte() 97 | c.protocolVersion[1], err = r.ReadByte() 98 | c.Log.Printf("Protocol version: %d.%d", c.protocolVersion[0], c.protocolVersion[1]) 99 | case cmd == StartSysEx: 100 | var sysExData []byte 101 | sysExData, err = r.ReadSlice(byte(EndSysEx)) 102 | if err == nil { 103 | c.parseSysEx(sysExData[0 : len(sysExData)-1]) 104 | } 105 | case (cmd&DigitalMessage) > 0 || byte(cmd&AnalogMessage) > 0: 106 | b1, _ := r.ReadByte() 107 | b2, _ := r.ReadByte() 108 | select { 109 | case c.valueChan <- FirmataValue{cmd, int(from7Bit(b1, b2)), c.analogChannelPinsMap}: 110 | } 111 | default: 112 | if c.Verbose { 113 | c.Log.Printf("Discarding unexpected command byte %0d\n", b) 114 | } 115 | } 116 | if err != nil { 117 | c.Log.Print(err) 118 | return 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /serial_ext.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | type SerialSubCommand byte 18 | 19 | // Configure a builtin or soft serial port. This command must be called before sending serial data. 20 | // Set txPin and rxPin to 0x00 for builtin serial ports. 21 | func (c *FirmataClient) SerialConfig(port SerialPort, baud int, txPin byte, rxPin byte) (err error) { 22 | baudBytes := intto7Bit(baud) 23 | bufferSize := intto7Bit(1024) 24 | termChar := to7Bit('\n') 25 | c.serialChan = make(chan string, 10) 26 | 27 | err = c.sendSysEx(Serial, byte(SerialConfig)|byte(port), 28 | baudBytes[0], baudBytes[1], baudBytes[2], 29 | bufferSize[0], bufferSize[1], bufferSize[2], 30 | termChar[0], termChar[1]) 31 | return 32 | } 33 | 34 | // Get channel for incoming serial data 35 | func (c *FirmataClient) GetSerialData() <-chan string { 36 | return c.serialChan 37 | } 38 | 39 | func (c *FirmataClient) parseSerialResponse(data7bit []byte) { 40 | 41 | data := make([]byte, 0) 42 | for i := 1; i < len(data7bit); i = i + 2 { 43 | data = append(data, byte(from7Bit(data7bit[i], data7bit[i+1]))) 44 | } 45 | select { 46 | case c.serialChan <- string(data): 47 | default: 48 | c.Log.Print("Serial data buffer overflow. No listener?") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spi_ext.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | type SPISubCommand byte 18 | 19 | // Enable SPI communication for selected chip-select pin 20 | func (c *FirmataClient) SPIConfig(csPin byte, spiMode byte) (err error) { 21 | csPinBytes := to7Bit(csPin) 22 | spiModeBytes := to7Bit(spiMode) 23 | c.spiChan = make(chan []byte) 24 | 25 | err = c.sendSysEx(SysExSPI, byte(SPIConfig), 26 | csPinBytes[0], csPinBytes[1], 27 | spiModeBytes[0], spiModeBytes[1]) 28 | return 29 | } 30 | 31 | // Read and write data to SPI device 32 | func (c *FirmataClient) SPIReadWrite(csPin byte, data []byte) (dataOut []byte, err error) { 33 | csPinBytes := to7Bit(csPin) 34 | data7Bit := []byte{byte(SPIComm)} 35 | 36 | data7Bit = append(data7Bit, csPinBytes...) 37 | for i := 0; i < len(data); i++ { 38 | bytes := to7Bit(data[i]) 39 | data7Bit = append(data7Bit, bytes...) 40 | } 41 | 42 | err = c.sendSysEx(SysExSPI, data7Bit...) 43 | dataOut = <-c.spiChan 44 | return 45 | } 46 | 47 | func (c *FirmataClient) parseSPIResponse(data7bit []byte) { 48 | data := make([]byte, 0) 49 | for i, _ := range data7bit { 50 | if i >= 3 && i%2 != 0 { 51 | data = append(data, from7Bit(data7bit[i], data7bit[i+1])) 52 | } 53 | } 54 | c.spiChan <- data 55 | } 56 | -------------------------------------------------------------------------------- /sysex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | ) 21 | 22 | func (c *FirmataClient) parseSysEx(data []byte) { 23 | var cmd SysExCommand 24 | 25 | cmd = SysExCommand(data[0]) 26 | 27 | if c.Verbose { 28 | c.Log.Printf("Processing sysex %v\n", cmd) 29 | } 30 | 31 | data = data[1:] 32 | 33 | bStr := "" 34 | for _, b := range data { 35 | bStr = bStr + fmt.Sprintf(" %#2x", b) 36 | } 37 | if c.Verbose { 38 | c.Log.Printf("SysEx recv %v\n", bStr) 39 | } 40 | 41 | switch { 42 | case cmd == StringData: 43 | c.Log.Printf("String data: %v", string(data)) 44 | case cmd == CapabilityResponse: 45 | dataBuf := bytes.NewBuffer(data) 46 | c.pinModes = make([]map[PinMode]interface{}, 0) 47 | 48 | pin := 0 49 | var err error 50 | var modes []byte 51 | for ; err == nil; modes, err = dataBuf.ReadBytes(127) { 52 | pinModes := make(map[PinMode]interface{}) 53 | if len(modes) < 2 { 54 | continue 55 | } 56 | 57 | modes = modes[0 : len(modes)-1] 58 | for i := 0; i < len(modes); i = i + 2 { 59 | pinModes[PinMode(modes[i])] = modes[i+1] 60 | } 61 | c.pinModes = append(c.pinModes, pinModes) 62 | pin = pin + 1 63 | } 64 | c.Log.Printf("Total pins: %v\n", pin-1) 65 | c.capabilityDone = true 66 | case cmd == AnalogMappingResponse: 67 | c.analogPinsChannelMap = make(map[int]byte) 68 | c.analogChannelPinsMap = make(map[byte]int) 69 | for pin, channel := range data { 70 | if channel != 127 { 71 | c.analogPinsChannelMap[pin] = channel 72 | c.analogChannelPinsMap[channel] = pin 73 | } 74 | } 75 | c.Log.Printf("pin -> channel: %v\n", c.analogPinsChannelMap) 76 | c.analogMappingDone = true 77 | case cmd == ReportFirmware: 78 | c.firmwareVersion = make([]int, 2) 79 | c.firmwareVersion[0] = int(data[0]) 80 | c.firmwareVersion[1] = int(data[1]) 81 | data = data[2:] 82 | c.Log.Printf("in %v", data[2:]) 83 | c.firmwareName = multibyteString(data) 84 | c.Log.Printf("Firmware: %v [%v.%v]", c.firmwareName, c.firmwareVersion[0], c.firmwareVersion[1]) 85 | c.ready = true 86 | c.sendSysEx(AnalogMappingQuery) 87 | c.sendSysEx(CapabilityQuery) 88 | case cmd == Serial: 89 | c.parseSerialResponse(data) 90 | case cmd == SysExSPI: 91 | c.parseSPIResponse(data) 92 | default: 93 | c.Log.Printf("Discarding unexpected SysEx command %v", cmd) 94 | } 95 | } 96 | 97 | func (c *FirmataClient) sendSysEx(cmd SysExCommand, data ...byte) (err error) { 98 | var b bytes.Buffer 99 | 100 | b.WriteByte(byte(StartSysEx)) 101 | b.WriteByte(byte(cmd)) 102 | b.Write(data) 103 | b.WriteByte(byte(EndSysEx)) 104 | 105 | bStr := "" 106 | for _, b := range b.Bytes() { 107 | bStr = bStr + fmt.Sprintf(" %#2x", b) 108 | } 109 | c.Log.Printf("SysEx send %v\n", bStr) 110 | 111 | _, err = b.WriteTo(*(c.conn)) 112 | return 113 | } 114 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Krishna Raman 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package firmata 16 | 17 | func from7Bit(b0 byte, b1 byte) byte { 18 | return (b0 & 0x7F) | ((b1 & 0x7F) << 7) 19 | } 20 | 21 | func to7Bit(i byte) []byte { 22 | return []byte{i & 0x7f, (i >> 7) & 0x7f} 23 | } 24 | 25 | func intto7Bit(i int) []byte { 26 | return []byte{byte(i & 0x7f), byte((i >> 7) & 0x7f), byte((i >> 14) & 0x7f)} 27 | } 28 | 29 | func multibyteString(data []byte) (str string) { 30 | 31 | if len(data)%2 != 0 { 32 | data = append(data, 0) 33 | } 34 | 35 | for i := 0; i < len(data); i = i + 2 { 36 | str = str + string(from7Bit(data[i], data[i+1])) 37 | } 38 | 39 | return 40 | } 41 | --------------------------------------------------------------------------------