├── .gitignore ├── README.md ├── LICENSE └── firmata.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | micropython-firmata 2 | =================== 3 | 4 | firmata implementation (in python) for the micropython project and board. 5 | As board not yet working neither is this implementation. 6 | Waiting for h/w and stabilisation of i2c and Serial comms. 7 | 8 | Implements 2.1.13 version 9 | * has virtual pin mapping for STMF4 chips which have many more pins than initial reference Arduino platform. 10 | * implements full protocol 11 | * allows for i2c comms, servo range, capability queries 12 | * does not implement Shift ops -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Neon22 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /firmata.py: -------------------------------------------------------------------------------- 1 | #! usr/bin/python 2 | 3 | ### Firmata implementation for Micro Python 4 | ### Protocol defined here: http://firmata.org/wiki/Protocol 5 | 6 | ### Custom for each hardware port 7 | ### Define the PINS and COMMS dictionaries below for each hardware device 8 | 9 | 10 | 11 | VERSION_BLINK_PIN = 24 # blink this pin to show version info. 12 | 13 | #------------------------------------------------ 14 | # micro python is likely to be running on chips with many more pins than originally envisioned by this protocol. 15 | # (The protocol maximum pin is 127. Special analog commands have a maximum of 15 pins. Special commands deal with indices > 15. 16 | # To solve this problem and make implementation and control easier - the physical pins are remapped to virtual pins. 17 | # - virtual pins are all numbered from 0 18 | # - analog pins are grouped into their own subset and labelled from 1 19 | # - They will be masked out of operations on digital pins 20 | # Digital Pins: 21 | # - a pin should appear only once in a single category - declaring your intention for its use. 22 | # - Alter this list so that the pins you want to control appear in it. 23 | # - Remove pins you do not want to make available. 24 | # Set the config up for the chip using !! pyb.?? 25 | # !!Could be an import? 26 | 27 | # potentially all defined pins can be configured as digital inputs or outputs 28 | 29 | # Indices used in PINS to keep structure small. 30 | # Using same values as Firmata pin mode values to make mode discovery easier 31 | INPUT = 0x00 # digital pin in input mode 32 | OUTPUT = 0x01 # digital pin in output mode 33 | ANALOG = 0x02 # analog pin in input mode 34 | PWM = 0x03 # digital pin in PWM output mode 35 | SERVO = 0x04 # digital pin in Servo output mode 36 | # to these we add shortcuts to make life simpler and 37 | # we use D=O to make Digital correspond with Output in Firmata 38 | #D,A,P,S,I = 1,2,3,4,5 # Digital, Analog, Pwm, Servo, I2c 39 | D,A,P,S = OUTPUT, ANALOG, PWM, SERVO 40 | # add extras for defining the Firmata PORTs 41 | PORTS, I2C, SPI = 252,253,254 # ... 42 | 43 | ### Define for specific hardware chip and project 44 | # The key is the virtual pin and the first item in the list is the physical pin. 45 | # followed by a list of capabilities. 46 | PINS = {1 : (1, D,A,P,S), 2 : (15, D,A,P,S), # PA0,1 47 | 3 : (16, D,A,P,S), 4 : (17, D,A,P,S), # PA2,3 48 | 5 : (20, D,A), 6 : (21, D,A,P), # PA4,5 49 | 7 : (22, D,A,P), 8 : (23, D,A,P), # PA6,7 50 | 9 : (49, D), 10: (50, D,P), # PA14,15 51 | # 52 | 11: (26, D,A,P), 12: (27, D,A,P), # PB0,1 53 | 13: (55, D,P), 14: (56, D,P), # PB3,4 54 | 15: (58, D,P), 16: (59, D,P), # PB6,7 55 | 17: (61, D,P), 18: (62, D,P), # PB8,9 56 | 19: (29, D,P), 20: (30, D,P), # PB10,11 57 | 21: (33, D), 22: (34, D,P), # PB12,13 58 | 23: (35, D,P), 24: (36, D,P), # PB14,15 59 | # 60 | 25: (8, D,A), 26: (9, D,A), # PC0,1 61 | 27: (10, D,A), 28: (11, D,A), # PC2,3 62 | 29: (37, D,P), 30: (38, D,P) # PC6,7 63 | } 64 | # for ports map contiguous ports on chip. Use 0 for unused pins (they will be masked) 65 | COMMS = {PORTS: ((1,15,16,17,20,21,22,23), # PA0-7 66 | (26,27,55,56,0,58,59), # PB0-7 67 | (61,62,29,30,33,34,35), # PB8-15 68 | (6,9,10,11,0,0,37,38) ), # PC0-7 69 | I2C: (61,62), # SCL, SDA (PB8,9) 70 | SPI: (34,35,36) # SCLK, MOSI, MISO 71 | } 72 | # helper for number of ports needed for DIGITAL_MESSAGE responses 73 | NUM_PORTS = len(PINS)//8 if len(PINS) % 8 == 0 else len(PINS+8)//8 74 | # max number of data bytes in non-Sysex messages 75 | MAX_DATA_BYTES 32 76 | 77 | #------------------------------------------------ 78 | ### Firmata protocol 79 | # The host may send a request capability message which will tell it which pins are exposed 80 | # and available and for what purpose. 81 | # The host may also request firmware and version info 82 | # V1 enables... 83 | # V2 adds ... 84 | # Then it will send read or write messages (in its protocol) for one pin or port at a time 85 | # At this time the code will create a pin instance and link it to that pin. 86 | # Also setting up buffers if required. 87 | # on subsequent calls a simple test confirms this pin is already allocated and 88 | # messages pass direct to each pins' class for action. 89 | # Pins/ports set as inputs have a simple interface to read and send data to server if it has changed. 90 | 91 | # - The extended analog message is used for all ANALOG, PWM and SERVO pins over 15 92 | 93 | # Possible commands 94 | # host can define: 95 | ## two data messages: 96 | # ANALOG_MESSAGE: 97 | # - uses analog pin 0-15 plus twobytes(14bits) value 98 | # - set the pin to that value 99 | # - only arrives for Analog out (PWM, SERVO) ? 100 | # DIGITAL_MESSAGE: 101 | # - uses port number (0-15)x8pins plus two bytes to give 1byte mask(bits 0-6,7) 102 | # - arrives when setting outputs 103 | # - send when sending digital pin data back (as a port) 104 | 105 | ## four control messages: 106 | # REPORT_ANALOG: 107 | # - uses an analog pin ref 0-15 plus 0=disable, 1=enable 108 | # - for each analog pin sets whether to report back or not 109 | # - not clear if this demands every sampleframe or only when changes or only on demand 110 | # REPORT_DIGITAL: 111 | # - uses a digital port ref (0-15) plus 0=disable, 1=enable 112 | # - ports are consecutive sets of 8 pins (0-127) 113 | # - all pins in port send result (pins set to analog are masked out and report 0) 114 | # SET_PIN_MODE: 115 | # - uses a digital pin (0-127) and I/O/A/P/S 116 | # REPORT_VERSION: 117 | # - asks to send back major and minor version to determine level of support 118 | # SYSTEM_RESET: 119 | # - asks client to do reset 120 | 121 | ## numerous sysex messages 122 | # ANALOG_MAPPING_QUERY 123 | # - ask for mapping of analog to pin numbers 124 | # - Analog messages are numbered 0 to 15, which traditionally refer to the Arduino pins labeled A0,A1,A2,... 125 | # - However, these pins are actually configured using "normal" pin numbers in the pin mode message, 126 | # - so some are used for non-analog functions. 127 | # - The analog mapping query provides information about which pins (as used with Firmata's pin mode message) correspond to the analog channels. 128 | # - send analog channel corresponding to pin 0, or 127 if pin 0 does not support analog 129 | # analog channel corresponding to pin 1, or 127 if pin 1 does not support analog,... 130 | # CAPABILITY_QUERY 131 | # - ask for supported modes and resolution of all pins 132 | # - requests a list of all modes supported by all pins, and the resolution used by each mode. 133 | # - send for each pin (start at 0) - mode plus resolution,..., end with 127, next pin... 134 | # - Each pin has 2 bytes for each supported mode, and a single 127 to mark the end of that pin's data. 135 | # PIN_STATE_QUERY 136 | # - ask for a pin's current mode and value 137 | # - allows host to read the current configuration of any pin. 138 | # - can also be used to verify pin mode settings are received properly. 139 | # - send pin number(0-127) plus current pin mode plus pins state (0-6) plus (7-13) plus... 140 | # - The pin "state" is any data written to the pin. 141 | # - For output modes (digital output, PWM, and Servo), this is the last value written to the pin. 142 | # - For input modes, typically the state is zero or the status of the pullup resistor. 143 | # EXTENDED_ANALOG 144 | # - analog write (PWM, Servo, etc) to any pin 145 | # - an alternative to the normal analog message, extended version allows addressing beyond pin 15, 146 | # - supports sending analog values with any number of bits. The number of data bits is inferred by the length of the message. 147 | # - receive pin + bits 0-6, bits7-13, ... 148 | # SERVO CONFIG 149 | # - set minPulse, maxPulse, (i.e. freq) 150 | # - can be changed at any time LSB + MSB = 14 bits (0-6), (7-13) 151 | # - receive pin number + minpulse LSB + MSB + maxpulse LSB + MSB 152 | # STRING_DATA a string message with 14-bits per char 153 | # SHIFT_DATA shiftOut config/data message (34 bits) 154 | # I2C_REQUEST 155 | # - I2C request messages from a host to an I/O board 156 | # - receive slave addr LSB + MSB and r/w,mode + data0 LSB + MSB + data1... 157 | # - where read/write and address mode bits are: 158 | # {7: always 0} + {6: reserved} + {5: address mode, 1 means 10-bit mode} + 159 | # {4-3: read/write, 00 => write, 01 => read once, 10 => read continuously, 11 => stop reading} + 160 | # {2-0: slave address MSB in 10-bit mode, not used in 7-bit mode} 161 | # - Then send back 162 | # - send slave addr LSB + MSB + register LSB + MSB + data 0 LSB + MSB + data1... 163 | # I2C_CONFIG 164 | # - Configure special I2C settings such as power pins and delay times 165 | # - receive Delay in ms LSB + MSB + extra info user defined 166 | # - needs special implementation possibly for each host 167 | # REPORT_FIRMWARE 168 | # - report name and version of the firmware 169 | # - send back major,minor version plus 7bit quantities of file name (firmata.py) 170 | # SAMPLING_INTERVAL 171 | # - sets how often analog data and i2c data is reported to the client. The default value is 19 milliseconds. 172 | # - receive LSB, MSB of sampling interval in ms 173 | # - implies there is a maximum sampling interval 65535 ms ? 174 | 175 | # 176 | MAX_DATA_BYTES = 32 # max number of data bytes in non-Sysex messages 177 | 178 | 179 | ### helper functions 180 | 181 | # used for flashing the pin to display the version number 182 | def strobeLED (count, onInterval, offInterval): 183 | """ Turn the LED on and off count times, 184 | - where each on and off interval is also set. 185 | Intervals in ms. 186 | """ 187 | led = pyb.pinMode(VERSION_BLINK_PIN, OUTPUT) # indicate pin is output 188 | for i in range(count): 189 | pyb.delay(offInterval); 190 | #!! how does pyb.LED work ? 191 | pyb.LED(VERSION_BLINK_PIN, True) # turn it on 192 | pyb.delay(onInterval) 193 | pyb.LED(VERSION_BLINK_PIN, False) # turn it off 194 | 195 | def strobe_version(): 196 | " flash the major, minor version number " 197 | strobeLED(FIRMATA_MAJOR_VERSION, 200, 400) 198 | pyb.delay(300) 199 | strobeLED(2,1,4) # separator - quick burst 200 | pyb.delay(300) 201 | strobeLED(FIRMATA_MINOR_VERSION, 200, 400) 202 | 203 | def compose_two_byte(value): 204 | """ split an 8 bit value into two seven bit values 205 | as per MIDI requirements. (high bit denotes a command) 206 | """ 207 | return (value & 0b01111111, # LSB 208 | (value >> 7) & 0b01111111) # MSB 209 | 210 | def validate_pins_p(): 211 | " check that vpins and physical pins are unique " 212 | pins = [a[0] for a in PINS.values()] 213 | pin_counts = [pins.count(i) for i in pins] 214 | vpins = PINS.keys() 215 | vpin_counts = [vpins.count(i) for i in vpins] 216 | # 217 | if sum(pin_counts) > len(pin_counts) or sum(vpin_counts) > len(vpin_counts): 218 | # duplicates 219 | return (False) 220 | else: return(True) 221 | 222 | # !!set of default callbacks if none defined 223 | # I,A - call read, O call write, PWM, SERVO - call analogwrite 224 | def default_input(firmata, pin, value): 225 | firmata.active_vpins[pin].report() 226 | 227 | ### 228 | class Firmata(object): 229 | """ This class contains the pin classes for each type and manages sampling rate etc """ 230 | ## Class constants 231 | #pin mode values 232 | # INPUT = 0x00 # digital pin in input mode 233 | # OUTPUT = 0x01 # digital pin in output mode 234 | # ANALOG = 0x02 # analog pin in input mode 235 | # PWM = 0x03 # digital pin in PWM output mode 236 | # SERVO = 0x04 # digital pin in Servo output mode 237 | # Version info - what we support 238 | FIRMATA_MAJOR_VERSION = 2 239 | FIRMATA_MINOR_VERSION = 0 240 | # Protocol enumerated values 241 | # message command bytes (128-255/0x80-0xFF) 242 | DIGITAL_MESSAGE = 0x90 # send data for a digital pin 243 | ANALOG_MESSAGE = 0xE0 # send data for an analog or PWM pin 244 | REPORT_ANALOG = 0xC0 # enable analog input by pin # 245 | REPORT_DIGITAL = 0xD0 # enable digital input by port pair 246 | SET_PIN_MODE = 0xF4 # set a pin to INPUT/OUTPUT/PWM/etc 247 | REPORT_VERSION = 0xF9 # report protocol version 248 | SYSTEM_RESET = 0xFF # reset from MIDI 249 | START_SYSEX = 0xF0 # start a MIDI Sysex message 250 | END_SYSEX = 0xF7 # end a MIDI Sysex message 251 | # extended command set using sysex (0-127/0x00-0x7F) 252 | # 0x00-0x0F reserved for custom commands 253 | #RESERVED_COMMAND = 0x00 # 2nd SysEx data byte is a chip-specific command (AVR, PIC, TI, etc). 254 | ANALOG_MAPPING_QUERY = 0x69 # ask for mapping of analog to pin numbers 255 | #ANALOG_MAPPING_RESPONSE = 0x6A # reply with mapping info 256 | CAPABILITY_QUERY = 0x6B # ask for supported modes and resolution of all pins 257 | #CAPABILITY_RESPONSE = 0x6C # reply with supported modes and resolution 258 | PIN_STATE_QUERY = 0x6D # ask for a pin's current mode and value 259 | PIN_STATE_RESPONSE = 0x6E # reply with a pin's current mode and value 260 | EXTENDED_ANALOG = 0x6F # analog write (PWM, Servo, etc) to any pin beyond 15 261 | SERVO_CONFIG = 0x70 # set max angle, minPulse, maxPulse, freq 262 | #STRING_DATA = 0x71 # a string message with 14-bits per char 263 | #SHIFT_DATA = 0x75 # shiftOut config/data message (34 bits) 264 | #I2C_REQUEST = 0x76 # I2C request messages from a host to an I/O board 265 | #I2C_REPLY = 0x77 # I2C reply messages from an I/O board to a host 266 | #I2C_CONFIG = 0x78 # Configure special I2C settings such as power pins and delay times 267 | REPORT_FIRMWARE = 0x79 # report name and version of the firmware 268 | #SAMPLING_INTERVAL = 0x7A # sampling interval 269 | #SYSEX_NON_REALTIME = 0x7E # MIDI Reserved for non-realtime messages 270 | #SYSEX_REALTIME = 0x7F # MIDI Reserved for realtime messages 271 | # 272 | flattened_pins = PINS.keys() # just the vpins in a list 273 | 274 | def __init__(self, serial_port, baudrate=57600, auto_start=True, delay=500): 275 | self.port = pyb.Serial(serial_port, baudrate, '8N1') # !!we're guessing 276 | self.analog_apins = {} # grows as analog pins defined. 277 | self.active_vpins = {} # hold all pin class instances once created 278 | # shadows not strictly required - could use .keys() but quicker at cost of array 279 | self.active_vpin_shadow = [] # holds list of active vpins 280 | self.active_apin_shadow = [] # holds list of active analog pins 281 | self.system_reset() 282 | self.system_reset_function = None 283 | # ivs for parsing state machine 284 | self.command = 0 285 | self.parsing_sysex = False # 286 | self.sysex_bytes_read = 0 # 287 | self.stored_input_data = [0]*MAX_DATA_BYTES # maximum non-sysex message length buffer 288 | self.multiByteChannel = 0 # channel data for multiByteCommands 289 | self.wait_for_data = 0 # this flag says the next serial input will be data 290 | self.execute_multibyte_command = 0 # execute this after getting multi-byte data 291 | # 292 | if auto_start: 293 | pyb.delay(delay) 294 | self.port.open() #!! guessing 295 | # Validate PINS setup 296 | if not validate_pins_p(): 297 | #!! raise exception 'pins not unique' 298 | pass 299 | 300 | def system_reset(self): 301 | """ resets the system state when receiving a 302 | SYSTEM_RESET message from the host 303 | """ 304 | # discard all pins 305 | self.active_vpins = {} 306 | self.active_vpin_shadow = [] 307 | # reset parsing state machine 308 | self.parsing_sysex = False 309 | self.sysex_bytes_read = 0 310 | self.multiByteChannel = 0 311 | self.wait_for_data = 0 312 | self.execute_multibyte_command = 0 313 | for i in range(len(self.stored_input_data)): 314 | self.stored_input_data[i] = 0 315 | # also execute users system reset callback function if defined 316 | if self.system_reset_function: 317 | self.system_reset_function() 318 | 319 | def set_system_reset_callback(self, function): 320 | " user can register a function to be called when system is reset " 321 | self.system_reset_function = function 322 | 323 | def send_message(self, bytes=[]): 324 | " sends stream of bytes back to host " 325 | for b in bytes: 326 | self.port.write(b) 327 | 328 | def send_sysex_message(self, bytes=[]): 329 | " sysex messages encode all 8 bit values as two 7bit values " 330 | self.port.write(START_SYSEX) 331 | for b in bytes: 332 | self.port.write(b) 333 | self.port.write(END_SYSEX) 334 | 335 | ##!! read ports - host cares about virtual ports. so can see/set pins en-masse. 336 | # - client cares about local ports so can set pins in single op using bit mask. 337 | # - cannot reconcile these unless we place physical pins in same order as virtual pins for a port. 338 | # what matters in protocol is virtual ports. 339 | # - report using port and set using port are functional interfaces 340 | 341 | 342 | ### child classes for pin modes 343 | class Firmata_pin(object): 344 | """ This class is base for 4 pin classes but init does job for all of them 345 | The base class also handles INPUT, OUTPUT, PWM directly. 346 | - SERVO has extra instance variables. 347 | - I2C handled ? !! 348 | """ 349 | def __init__(self, parent, vpin, mode, callback=None): 350 | self.parent = parent # to get Serial port 351 | self.vpin = vpin # the virtual pin 352 | if mode in PINS[vpin][1:]: 353 | self.last_sent = 0 # to compare current with last sent sample to see if changed 354 | self.pin = PINS[vpin][0] # the physical pin 355 | parent.active_pins[vpin] = self 356 | parent.active_vpin_shadow.append(vpin) 357 | # add analog pins 358 | if mode == ANALOG: 359 | # calc apin as next highest and add to analog pins 360 | apin = max(parent.active_apin_shadow) + 1 361 | self.analog_apins[apin] = self 362 | self.active_apin_shadow.append(apin) 363 | self.apin = apin 364 | self.mode = mode 365 | self.set_mode(mode) 366 | # action to perform 367 | if callback: 368 | self.callback = callback # will be called on action() 369 | # 370 | self.report_fun = foo # !! 371 | else: # raise 'mode not available' exception 372 | pass 373 | 374 | def set_callback_name(self, function): 375 | self.callback = function 376 | 377 | def id(self): 378 | return self.vpin 379 | 380 | def set_mode(self, mode): 381 | """ set mode and params to support it 382 | if existing mode changing then optimise 383 | """ 384 | if mode != self.mode: 385 | # called from outside constructor 386 | # remove self from list and make new pin object 387 | self._remove_pin(vpin) 388 | self._create_new_pin(vpin, mode, callback_name) 389 | else: 390 | # set I/O based on mode 391 | if mode in [INPUT, ANALOG]: 392 | pyb.set_pin_mode(self.pin, INPUT) # !! check pyb 393 | else: 394 | pyb.set_pin_mode(self.pin, OUTPUT) # !! check pyb 395 | 396 | def read(self, only_if_changed=False): 397 | """ return read value 398 | - if flag set then only return value if it has changed 399 | """ 400 | if self.mode == INPUT or self.mode == ANALOG: 401 | now = self.pin.read() 402 | if only_if_changed and now == self.last_sent: 403 | now = None # !! or False 404 | return now 405 | else: # its not readable (SERVO, PWM, OUTPUT) so return currently set value 406 | return self.last_sent 407 | 408 | # only have pin reports for REPORT_ANALOG 409 | # digital output pins report entire port of 8 pins () 410 | # for all pins, firmata reads back by using PIN_STATE_QUERY (a SYSEX command) 411 | def report(self, only_if_changed=False, sysex=False): 412 | " can only respond to a PIN_STATE_QUERY message " 413 | # The flags only_if_changed and sysex are unused 414 | value = self.read(only_if_changed) 415 | if value: 416 | msg = [f.PIN_STATE_RESPONSE, self.vpin, self.mode] 417 | msg.extend(compose_two_byte(value)) 418 | self.parent.send_sysex_message(msg) 419 | self.last_sent = value 420 | 421 | def action(self, *args): 422 | """ The action to perform for this pin. 423 | user sets action by adding callback 424 | """ 425 | if self.callback: 426 | if type(self.callback) == type(""): 427 | # call the callback string as a function: 428 | # - passing any args to it. 429 | # - return its return value 430 | return globals()[self.callback](*args) 431 | else: # its a function 432 | return(self.callback(*args)) 433 | 434 | class Firmata_analog_pin(Firmata_pin): 435 | def __init__(self, *args): 436 | super().__init__(*args) # super(Firmata_analog_pin, self).__init__(*args) 437 | #!! assign def callback if not supplied 438 | 439 | def report(self, only_if_changed=False, sysex=False): 440 | """ send host the analog value 441 | - if flag set then only return value if it has changed 442 | - use regular or SYSEX message format 443 | """ 444 | pin = self.apin 445 | value = self.read(only_if_changed) 446 | if value: 447 | if not(sysex) and pin < 16: 448 | # regular REPORT_ANALOG response 449 | msg = [f.ANALOG_MESSAGE | (pin & 0xF)] 450 | msg.extend(compose_two_byte(value)) 451 | self.parent.send_message(msg) 452 | elif sysex: 453 | # send sysex response 454 | msg = [f.PIN_STATE_RESPONSE, self.vpin, self.mode] 455 | msg.extend(compose_two_byte(value)) 456 | self.parent.send_sysex_message(msg) 457 | 458 | class Firmata_servo_pin(Firmata_pin): 459 | def __init__(self, *args): 460 | super().__init__(*args) # super(Firmata_servo_pin, self).__init__(*args) 461 | #!! assign def callback if not supplied 462 | 463 | def set_mode(self, mode): 464 | pyb.set_pin_mode(self.pin, OUTPUT) # !! check pyb 465 | # add min/max pulse ivs 466 | self.min_pulse = 0 # ms 467 | self.max_pulse = 2.5 # ms !! 468 | 469 | 470 | # class Firmata_PWM_pin(Firmata_pin): 471 | # def __init__(self, *args): 472 | # super().__init__(*args) # super(Firmata_PWM_pin, self).__init__(*args) 473 | # #!! assign def callback if not supplied 474 | 475 | #!! when error (e.g. mode unknown, ) then send sysex string message error 476 | 477 | 478 | ### 479 | def set_mode_pin(self, vpin, mode, callback=False): 480 | """ set the virtual pin to the mode defined. 481 | mode is one of the pin mode values. 482 | """ 483 | # check if pin legal 484 | # check if instance made already and correct mode 485 | # - change it if it is 486 | # else create it and add to active_pins 487 | create = False 488 | if vpin in self.flattened_pins: 489 | # pin is viable 490 | if vpin in self.active_vpin_shadow: 491 | # class already defined 492 | pin_object = self.active_pins[vpin] 493 | # do we need to change mode 494 | if pin_object.mode != mode and mode in PINS[vpin][1:]: 495 | # remove old instance and create new one 496 | self._remove_pin(vpin) 497 | create = True 498 | elif callback and callback != pin_object.callback: 499 | pin_object.set_callback(callback) 500 | # reset current value 501 | pin_object.current = 0 502 | else: # need new instance 503 | create = True 504 | # 505 | if create: 506 | self._create_new_pin(vpin, mode, callback_name) 507 | 508 | def _create_new_pin(self, vpin, mode, callback_name): 509 | " create specific class required " 510 | if mode == ANALOG: 511 | Firmata_analog_pin(self, vpin, mode, callback_name) 512 | # elif mode == PWM: 513 | # Firmata_PWM_pin(self, vpin, mode, callback_name) 514 | elif mode == SERVO: 515 | Firmata_servo_pin(self, vpin, mode, callback_name) 516 | else: 517 | Firmata_pin(self, vpin, mode, callback_name) 518 | 519 | def _remove_pin(self, vpin): 520 | " remove vpin from lists and apin if analog " 521 | if self.active_pins.count(vpin): 522 | # vpin in list so remove it 523 | pin_object = self.active_pins[vpin] 524 | if hasattr(pin_object, 'apin'): # or check classname ? 525 | self.analog_apins.remove(apin) 526 | self.active_apin_shadow.remove(apin) 527 | self.active_pins.remove(vpin) 528 | self.active_vpin_shadow.remove(vpin) 529 | 530 | def set_port_pins(self, port_number, mask): 531 | """ do set_pin_mode(vpin, INPUT) for pins in mask 532 | """ 533 | pstart = port_number * 8 534 | for i in range(0, 7): 535 | vpin = pstart + i # !! count down ? pstart + 8 - i 536 | if mask & (1 << i): 537 | # that pin is set 538 | # remove it if already defined as other than an input 539 | if self.active_pins.count(vpin): 540 | # vpin in list 541 | if self.active_pins[vpin].mode != INPUT: 542 | self._remove_pin(vpin) 543 | # add it as an input 544 | self.set_mode_pin(vpin, INPUT) # !! add callback ? 545 | else: # pin is clear so remove pin if defined as input 546 | if self.active_pins[vpin].mode == INPUT: 547 | self._remove_pin(vpin) 548 | 549 | ### 550 | def begin(self): 551 | """ 1. blink version 552 | 2. open serial 553 | 3. send version and firmware version back 554 | """ 555 | strobeLED() 556 | if self.port.status() != 'open': #!! guessing 557 | self.port.open() 558 | # report version 559 | self.report_version() 560 | # report firmware 561 | self.report_firmware_version() 562 | 563 | def available(self): 564 | return self.port.available() 565 | 566 | ### send functions 567 | def report_version(self): 568 | self.send_message([f.REPORT_VERSION, f.FIRMATA_MAJOR_VERSION, f.FIRMATA_MINOR_VERSION]) 569 | 570 | def report_firmware_version(self): 571 | msg = [f.REPORT_FIRMWARE, f.FIRMATA_MAJOR_VERSION, f.FIRMATA_MINOR_VERSION] 572 | filename = 'firmata.py' 573 | for c in filename: 574 | msg.append(c) 575 | self.send_sysex_message(msg) 576 | 577 | def send_digital_port(self, port_number): 578 | """ Firmata ports are all based on the virtual pin index and 8 bit ports. 579 | - there are 15 ports (pins 0-127) in 8 bit chunks 580 | So we use the vpin to relate to a port. 581 | """ 582 | # given a port number - assemble those pins into a byte for sending 583 | # if none have changed then do not send the byte 584 | pstart = port_number * 8 585 | byte = 0 586 | changed = False 587 | for i in range(pstart, pstart + 8): # count down =(pstart+7, pstart-1, -1) 588 | value = 0 589 | if i in self.active_vpins_shadow: 590 | pin = self.active_vpins[i] 591 | value = pin.read(True) # only get result if changed from last sent 592 | if value: 593 | changed = True 594 | # either have a value or not (no change) 595 | byte |= (1<> 7]) 601 | 602 | def send_string(self, string): 603 | """ strings are all sent as two byte sysex messages """ 604 | self.send_sysex_message(f.FIRMATA_STRING, string) 605 | 606 | ### callbacks 607 | def attach(self, command, callback_function): 608 | """ command is one of: 609 | ANALOG_MESSAGE, DIGITAL_MESSAGE, 610 | REPORT_ANALOG, REPORT_DIGITAL, 611 | SET_PIN_MODE, SYSTEM_RESET, 612 | FIRMATA_STRING, START_SYSEX 613 | The fallback is START_SYSEX 614 | """ 615 | self.callback_functions[command] = callback_function 616 | 617 | ### process incoming 618 | def process_sysex_message(self): 619 | """ sysex message has been received. 620 | process it here 621 | """ 622 | command = self.stored_input_data[0] # first byte in buffer is command 623 | if command == REPORT_FIRMWARE: 624 | self.report_firmware_version() 625 | elif command == STRING_DATA: 626 | if currentStringCallback: 627 | buffer = "" 628 | for i in range(1, (sysex_bytes_read - 1) / 2, 2): 629 | buffer += self.stored_input_data[i] + self.stored_input_data[i+1] << 7 630 | *currentStringCallback)(buffer) 631 | else: 632 | if currentSysexCallback: 633 | *currentSysexCallback(storedInputData[0], sysexBytesRead - 1, storedInputData + 1) 634 | 635 | 636 | def attachpin(self,): 637 | "" 638 | pass 639 | 640 | def process_input(self, ): 641 | """ Listen for incoming messages from host. 642 | this needs to be non blocking and may not receive a byte each time through. 643 | Gather info until entire command is collected - then dispatch: 644 | ANALOG_MESSAGE, DIGITAL_MESSAGE, REPORT_ANALOG, REPORT_DIGITAL, SET_PIN_MODE, REPORT_VERSION, SYSTEM_RESET 645 | Sysex messages: 646 | PIN_STATE_QUERY, REPORT_FIRMWARE, SAMPLING_INTERVAL, EXTENDED_ANALOG, 647 | ANALOG_MAPPING_QUERY, CAPABILITY_QUERY 648 | """ 649 | # state machine uses: command, parsing_sysex, sysex_bytes_read, stored_input_data, multiByteChannel, wait_for_data, execute_multibyte_command 650 | indata = self.port.read() # must be non-bocking 651 | if indata: 652 | # process it - else drop out 653 | if self.parsing_sysex: 654 | if indata == f.END_SYSEX: 655 | #stop sysex 656 | self.parsing_sysex = False 657 | #fire off handler function 658 | f.process_sysex_message() 659 | else: 660 | #normal data byte - add to buffer 661 | self.stored_input_data[self.sysex_bytes_read] = indata 662 | self.sysex_bytes_read += 1 663 | elif self.wait_for_data > 0 and indata < 128: 664 | self.wait_for_data -= 1 665 | self.stored_input_data[self.wait_for_data] = indata 666 | # do we have complete multibyte command ? 667 | if self.wait_for_data == 0 and self.execute_multibyte_command: # got the whole message 668 | if self.execute_multibyte_command == ANALOG_MESSAGE: 669 | # indicates an analog write message to SERVO or PWM port - two bytes - pin, value 670 | if currentAnalogCallback: 671 | (*currentAnalogCallback)(self.multiByteChannel, 672 | (self.stored_input_data[0] << 7) 673 | + self.stored_input_data[1]) 674 | elif self.execute_multibyte_command == DIGITAL_MESSAGE: 675 | # indicates to write to output pins defined in port - two bytes - port, pin mask 676 | if currentDigitalCallback: 677 | (*currentDigitalCallback)(self.multiByteChannel, 678 | (self.stored_input_data[0] << 7) 679 | + self.stored_input_data[1]) 680 | elif self.execute_multibyte_command == SET_PIN_MODE: 681 | # indicates to set the pin to that mode. two bytes - pin, mode 682 | self.set_mode_pin(self.stored_input_data[1], self.stored_input_data[0]) # !! should we add callback too? 683 | elif self.execute_multibyte_command == REPORT_ANALOG: 684 | # indicate that this analog pin is to report back - single byte - value 0-15 685 | if self.stored_input_data[0]: 686 | # tell pin to report 687 | self.set_mode_pin(self.multiByteChannel, ANALOG) # !! should we add callback too? 688 | else: # remove it if already registered 689 | self._remove_pin(self.multiByteChannel) 690 | elif self.execute_multibyte_command == REPORT_DIGITAL: 691 | # indicates this port of pins is to report back - single byte - pins in port mask 692 | self.set_port_pins(self.multiByteChannel, self.stored_input_data[0]) 693 | else: # unimplemented - don't know what to do with it - skip it :) 694 | self.execute_multibyte_command = 0 695 | else: 696 | #remove channel info from command byte if less than 0xF0 697 | # command could be local to this block !! test and remove self. if possible 698 | if indata < 0xF0: 699 | self.command = indata & 0xF0 700 | self.multiByteChannel = indata & 0x0F 701 | else: #commands in the 0xF* range don't use channel data 702 | self.command = indata 703 | # update progress or execute one byte commands 704 | if self.command == SET_PIN_MODE: 705 | self.wait_for_data = 2 # two data bytes needed 706 | self.execute_multibyte_command = self.command 707 | elif self.command == REPORT_DIGITAL: 708 | self.wait_for_data = 1 # one data byte needed 709 | self.execute_multibyte_command = self.command 710 | elif self.command == START_SYSEX: 711 | self.parsing_sysex = True 712 | self.sysex_bytes_read = 0 713 | elif self.command == SYSTEM_RESET: 714 | self.system_reset() 715 | elif self.command == REPORT_VERSION: 716 | self.report_version() 717 | 718 | 719 | 720 | 721 | ###----------------------------- 722 | ### Usage: 723 | 724 | ### Define main class 725 | # f = Firmata(port, baud) # port is a pyb.Serial instance, baud defaults to 57600 726 | # f = Firmata(port, baud, False, 3000) # do not autostart, set 3sec timeout 727 | 728 | ### Define new pins, modes and optionally callbacks 729 | ### - callbacks can be added later. They are existing functions 730 | # f.set_mode_pin(1, ANALOG) # Analog input pin (no callback) 731 | # f.set_mode_pin(1, ANALOG, read_analog) # Analog input pin (read_analog is a function) 732 | # f.set_mode_pin(2, INPUT, read_digital) # Digital input pin (read_digital is a function) 733 | # f.set_mode_pin(3, OUTPUT, write_output) # Digital output pin (write_output is a function) 734 | # f.set_mode_pin(12, PWM, drive_PWM) # PWM output pin (drive_PWM is a function) 735 | # f.set_mode_pin(4, SERVO, servo_device) # Servo output pin (servo_device is a function) 736 | ### Ports - virtual port of pins used as Input or Output 737 | # ports are only read and defined in COMMS variable 738 | # f.read_port(0) # returns 1 byte of packed input pins (masked for non-INPUT pins) 739 | 740 | ### Change mode of existing pin 741 | # f.active_vpins[4].set_mode(PWM) 742 | ### Change/Set callback for existing pin 743 | # f.active_vpins[2].set_callback_name(input_action) # where input_action is a function or string 744 | ### Redefine an existing pin to a new mode or callback 745 | # f.set_mode_pin(1, ANALOG, read_analog) # where read_analog is a function 746 | ### Read value from a pin (if I,A then get new value - else get value previously set) 747 | # f.active_vpins[1].read() # for pin 1 (ANALOG) 748 | # f.active_vpins[2].read() # for pin 2 (DIGITAL INPUT) 749 | ### Read but only if changed - returns None if unchanged 750 | # f.active_vpins[1].read(True) # for pin 1 (ANALOG) 751 | ### Report read values to Host 752 | # f.active_vpins[1].report() # for pin 1 (ANALOG) 753 | ### Report value to host only if changed 754 | # f.active_vpins[1].report(True) # for pin 1 (ANALOG) 755 | ### Report only changed values of all in-use pins to host 756 | # for p in f.active_vpins.values(): 757 | # p.report(True) 758 | ### Report only changed values of all DIGITAL pins 759 | # for pid in range(NUM_PORTS): 760 | # f.send_digital_port(pid) 761 | ## Report changed values of 1st 15 Analog pins 762 | # for apin in f.analog_apins(): 763 | # apin.report(True) 764 | ## Report changed values of all Analog pins (using sysex msg) 765 | # for apin in f.analog_apins(): 766 | # apin.report(True, True) 767 | ### or one at a time inside a loop 768 | # apin = 0 769 | # f.analog_apins[apin].report(True) 770 | # apin += 1 771 | # if apin > len(f.analog_apins): 772 | # apin = 0 773 | ### Perform callback function directly 774 | # f.active_vpins[4].action() # for pin 4 (SERVO) 775 | 776 | ## if an analog or digital out msg arrives before pin mode is set then a virtual pin mode command is performed first 777 | 778 | 779 | 780 | --------------------------------------------------------------------------------