Firmata
Problem
Often times, working with embedded hardware, we want to have the low level bit-flipping access of embedded programming but keep the high level programming ability of languages like Python or Ruby. Let’s say we want to control the position of a stepper motor connected through a motor driver to an Arduino, but we don’t want a “closed” control system with just the Arduino–we want to control the position from a Python script on a computer (maybe connected to a web service, etc.).
Solution
I’ve found my best solution to be to communicate between the computer and the Arduino using the Firmata protocol, a serial communication protocol specification for the Arduino that has libraries for C, C++, Python, Ruby, and some other languages too. This lets us write some commands and routines, even passing data back and forth, facilitating communication between the embedded system and the user’s computer.
Step-by-Step
Arduino sketch
Let’s start with flashing a sketch to the Arduino that contains some custom commands. The Firmata library should exist in the most recent version of the Arduino IDE. You can open the “StandardFirmata” example sketch, and save it as a copy in your project folder so that you can edit it (example sketches themselves can’t be edited). This sketch alone provides a large variety of commands that you can send over serial, like flipping pins and sending PWM. However, for my needs I also wanted to extend these abilities. To start, add a custom command code that isn’t already in use. I found that the 0x30-0x3F range should be free. Let’s say you want to call commands “MOVE_STEPPER” and “REPORT_SENSOR”: Add this to the beginning of the sketch:
1 |
Later in the sketch you will find a function called void sysexCallback(byte command, byte argc, byte *argv)
. The switch case in this function accepts the command code for incoming “sysex” commands, which you can think of as custom commands. For the sketch to accept a command with the MOVE_STEPPER code, you need to add a case in the switch-case for that code, as follows:
1 | case MOVE_STEPPER: |
Note that this snippet requires that you have STEP_PIN and DIR_PIN set before hand, as well as having already sent the pinmode for both. I also have a READY command defined (can be 32-bit number) that is a placeholder (no data is sent) that the Arduino sends back to the computer to tell it that it is done with the operation. If you did want to send data back, if you for example made a command to retrieve data from an I2C or SPI sensor, you can use the following pattern into another case (case REPORT_SENSOR):
1 | Firmata.write(START_SYSEX); |
Python script
For simple projects we can get away without extending the Arduino class provided by the pyFirmata library, but in this case we want to add some functionality in the form of extra sysex commands. Let’s start with a customarduino.py
file to extend this class. Here is an example of what that file might look like in this case:
1 | from pyfirmata import Arduino, util |
Now we can instantiate this extended Arduino class in our main script to leverage our added commands. You need the following imports:
1 | from customarduino import CustomArduino |
Now you can create a CustomArduino object with the correct COM port:
1 | board = CustomArduino('/dev/tty.usbmodem1411') |
The library requires that you start the board “iterator” before making any communications:
1 | it = util.Iterator(board) |
You can still use pyFirmata’s existing features, like manually flipping pins:
1 | pin = board.get_pin('d:11:i') |
This is important since I’ve found you need to block your script until the serial init procedure is done. You can do this as follows, with any pin:
1 | # block the script until the Arduino has initialized and |
You can also use the commands that we just made:
1 | board.move_axis(500, 1, 500) |