This chapter discusses the following functions of the Firmata library:
begin()
sendAnalog()
sendDigitalPorts()
sendDigital()
sendString()
available()
processInput()
attach()
detach()
The hardware needed to use the example in this chapter includes:
Arduinos are used in a wide variety of projects, from the most simple to some extremely complex devices. In most cases, their exact use is known; you know beforehand that digital pin 3 will be used to light an LED, and that analog input 4 will read the value of a light sensor. For some projects, you may not know what is connected, but you will still need to set pins as input or output, depending on the situation. Imagine a laboratory setup, one where you can study how new components work before deciding to use them in your projects. You could write a quick sketch each time to see how a component works, but this isn't always the best solution and certainly not the easiest. One way to easily set up your laboratory is to use Firmata.
Firmata is a protocol that communicates between computers and microcontrollers to easily access the Arduino hardware from software on a host machine. It uses standard serial commands and as such can be used on several different Arduino models. Messages are sent serially to and from the host computer, indicating pin status or requesting a pin to change state.
To use the Firmata library, you must first import it. You can import the Firmata library from the Arduino IDE automatically, by going to the Sketch Import Library
Firmata menu entry. Alternatively, you can write the lines manually:
#include <Firmata.h>
#include <Boards.h>
The Firmata protocol has several revisions, and if two devices use different revisions, that can lead to errors. To prevent this, you can specify which protocol revision to use with setFirmwareVersion()
:
setFirmwareVersion(major, minor);
The major
and minor
parameters are byte
s, which specify the revision to use. For most Arduino applications, this is set to major version 0 and minor version 1.
To begin using the Firmata library, you must first call begin()
:
Firmata.begin();
Firmata.begin(speed);
This function opens a serial connection. By default, the speed is set to 57600 baud, but this can be changed by the optional speed parameter.
The status of pins is sent as messages to and from the software on the host machine. Messages can be addressed to digital and analog pins. To send the status of an analog pin, use sendAnalog()
:
Firmata.sendAnalog(byte pin, int value);
The pin parameter is the analog pin you are requesting information about. The value parameter is the value read from the pin. This function does not read the pin value directly; you must explicitly read the value first:
analogValue = analogRead(pin);
Firmata.sendAnalog(pin, analogValue);
Digital pins are sent differently. Because serial connections are slow, relative to the speed of a microprocessor, something had to be done to speed up the transfer. Digital pins are either on or off, 1 or 0. To send the maximum amount of information in the minimum packet size, multiple pins are sent in a single message.
Firmata.sendDigitalPorts(pin, firstPort, secondPort);
Up to eight pins can be sent in the pin parameter, sent as a byte. The pins must be sent in order; when starting at pin 6, it must be followed by pin 7, pin 8, and so on. To set the first pin, use the firstPort
parameter sent as a byte. To set the number of pins sent, use the secondPort
parameter. The pin data will be sent to the computer, specifying that the data received is the data of the pins from firstPort
to secondPort
.
This works well when sending a range of pin data but is not efficient if you want to send the status of a single pin or if the pins are not linear. You can also send the data of a single pin using sendDigitalPort()
:
Firmata.sendDigital(pin, value);
This function sends the status of the pin and sends the pin input as value
.
To send a string to the host computer, use sendString()
:
Firmata.SendString(string);
This sends the String string
to the host computer.
Receiving messages on an Arduino is the same as working with other types of serial information; first, you must wait until you have received data and then process that data. Data is received directly on the serial port. To see if data is waiting, use available()
:
result = Firmata.available();
This function does not take any parameters and returns true
if one or more bytes are waiting to be processed. To process data, use processInput():
Firmata.processInput();
Typically, you would use both functions together:
while(Firmata.available())
{
Firmata.processInput();
}
The Firmata library hides all the complicated parts of receiving data, including the data storage and processing. The library automatically decodes messages and enables you to perform actions on the data received using a system of callbacks.
Firmata works by using a system of callbacks, routines that are called when a specific action is performed, or in this case, when a specific message is received. Callbacks are highly customizable, and you can write a callback to perform almost any action you want simply by creating a function. Callbacks are put in place using an attach function; in the case of the Firmata library, it is called attach()
:
Firmata.attach(messagetype, function);
Table 16.1 lists the messagetype
parameter, which is one of the constants. The function
parameter is the callback function that you have written.
Table 16.1 Callback Constants
Constant | Use |
ANALOG_MESSAGE |
Analog value of a single pin |
DIGITAL_MESSAGE |
Digital value of a digital port |
REPORT_ANALOG |
Enables or disables the reporting of an analog pin |
REPORT_DIGITAL |
Enables or disables the reporting of a digital port |
SET_PIN_MODE |
Change the mode of the selected pin (input, output, and so on) |
FIRMATA_STRING |
Used for receiving text messages |
SYSEX_START |
Used for sending generic messages |
SYSTEM_RESET |
Used to reset firmware to default state |
A callback requires a certain number of parameters to be defined, which is extremely specific as to the datatypes to use. The system restart callback does not require any parameters:
void systemResetCallback(void);
To receive strings, the stringCallback
function requires one parameter:
void stringCallback(char *datastring);
SysEx
messages require more information and have three parameters:
void sysexCallback(byte pin, byte count, byte *array);
Finally, all other callbacks use a generic format:
void genericCallback(byte pin, int value);
Callbacks must have different names. If you use both digital and analog pins, you will have two functions: one for handling digital data and the other for analog input. For example, code will allow you to receive both digital and analog instructions:
void analogWriteCallback(byte pin, int value)
{
// Code goes here
}
void digitalWriteCallback(byte pin, int value)
{
// Code goes here
}
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
A note on handling digital data: Analog data is sent one pin at a time, but this is not the case with digital pins. As seen previously, digital pin data is sent in groups of 8. This is known as a port. Port 1 will send the data of pins 1 to 8, and port 2 will send the data of pins 9 to 16, and so on. It is up to you to control if the pins should be written. To write all pins from a specified port, use this code:
void digitalWriteCallback(byte port, int value)
{
byte i;
byte pinValue;
if (port < TOTAL_PORTS)
{
for(i=0; i<8; i++)
{
pinValue = (byte) value & (1 << i);
digitalWrite(i + (port*8), currentPinValue);
}
}
}
To set a pin input or output, the mode
parameter corresponds directly to the Arduino pinMode()
constants. However, the trick is to know what pin corresponds to what sort of input/output. To do this, you can use some predefined data for each board. The Boards.h
file details how many digital and analog pins a board has. For example, the Arduino Mega has the following line defined in the source code:
#define TOTAL_PINS 70 // 54 digital + 16 analog
To know if a pin is digital, use IS_PIN_DIGITAL()
and IS_PIN_ANALOG()
. To convert a pin to a digital or analog equivalent, use PIN_TO_DIGITAL()
and PIN_TO_ANALOG()
. You can use the following code to set the state of a digital pin:
void setPinModeCallback(byte pin, int mode)
{
if (IS_PIN_DIGITAL(pin))
{
pinMode(PIN_TO_DIGITAL(pin), mode);
}
}
To remove a callback, use detach()
:
Firmata.detach(callback);
The callback
parameter is one of the constants used to attach a callback (refer to Table 16.1).
One of the messages that the Firmata protocol can exchange is called SysEx. Short for System Excusive, SysEx was originally used in synthesizers using the MIDI protocol to include custom commands. When writing a protocol, it is almost impossible to imagine every scenario, and to make sure that the MIDI protocol could handle just about everything, SysEx was developed. The idea was to exchange information and change settings that could not be accessed by other means. In extreme cases, memory was transferred (partitions or instruments, for example). In the Firmata protocol, it allows users to exchange information such as I2C bus data and the servo motor configuration.
To receive SysEx data, you must first create a SysEx callback, as explained in the “Callbacks” section.
An example callback might look like this:
void sysexCallback(byte command, byte argc, byte *argv)
{
// Code goes here
}
The SysEx instruction identifier is sent as a byte, called command
. The Arduino Firmata library defines a series of constants to describe a received message; as listed in Table 16.2.
Table 16.2 SysEx Constants
Constant | Function |
RESERVED_COMMAND |
Reserved chip-specific instructions. |
ANALOG_MAPPING_QUERY |
Ask for analog to pin number mapping. |
ANALOG_MAPPING_RESPONSE |
Reply with mapping data. |
CAPABILITY_QUERY |
Ask for supported modes of all pins. |
CAPABILITY_RESPONSE |
Reply with capability data. |
PIN_STATE_QUERY |
Ask for a pin's current mode and value. |
PIN_STATE_RESPONSE |
Reply with pin mode and value. |
EXTENDED_ANALOG |
Analog write to any pin, including PWM and servo. |
SERVO_CONFIG |
Set servo parameters (angle, pulse, and such). |
STRING_DATA |
Send a string message. |
SHIFT_DATA |
34-bit shift out data. |
I2C_REQUEST |
Request I2C data. |
I2C_REPLY |
Respond with I2C data. |
I2C_CONFIG |
I2C parameters. |
REPORT_FIRMWARE |
Report version number of Firmata firmware. |
SAMPLING_INTERVAL |
Set sampling interval. |
SYSEX_NON_REALTIME |
MIDI reserved. |
SYSEX_REALTIME |
MIDI reserved. |
These constants are kept up to date at the Firmata website at http://firmata.org/wiki/V2.2ProtocolDetails
.
The beauty of Firmata is that it can adapt to so many situations. It is, of course, up to you to choose which pins will be used. If you want to expose only some pins, for example, to allow Firmata to control them, you can choose to enable just those relevant to your project. The sketch might receive Firmata instructions to update pins, but ultimately it is up to you, the developer, to decide if you should allow these instructions on all pins. Maybe you do not want a Firmata program to be able to modify certain pins. If a pressure sensor is connected to two pins, you do not want Firmata to change the pins to output and potentially damage the component.
The Arduino IDE has an excellent sketch that lets you begin working with Firmata: the StandardFirmata program. To access this program, go to Files Examples
Firmata
StandardFirmata, and upload the sketch to your board. However, uploading the sketch to your Arduino is only one-half the project; you also need a Firmata program on your computer. Several programs exist, and one is available on the Firmata website at
http://www.firmata.org/wiki/Main_Page#Firmata_Test_Program
.
Download the version for your system (Windows, Mac OS, and Linux binaries are available), and run the program. You need to know which serial port your Arduino is connected to. After this is done, you are presented with the Firmata screen, where the status of every pin is presented. This works by sending data to the Arduino as quickly as possible; the faster the data transfer, the more responsive the output will be. The Arduino also sends data to the computer, using a clever sampling rate technique, which is described next.
Using this system, you can instruct your Arduino to perform advanced features such as turning LEDs on and off without the need to write a sketch or reading input lines without knowing in advance what will be connected (if anything). However, this has its limitations. As explained previously, if you require a device to be present on specific pins, you might want to edit the Standard Firmata sketch to not poll or update those pins. It is up to you, the programmer, to know which pins you want to expose and to create or modify a sketch to make sure that only the pins that are usable can be accessed by Firmata.
The Standard Firmata sketch is complicated and is one of the larger sketches that you will see on an Arduino, but it is well structured and can be used as the basis for your own sketches. By looking at setup()
, you can see this:
Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION,
FIRMATA_MINOR_VERSION);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.attach(SYSTEM_RESET, systemResetCallback);
The first line sets the Firmata version, something that the Firmata application checks. It is defined using two constants: FIRMATA_MAJOR_REVISION
and FIRMATA_MINOR_REVISION
. These constants are set by the Arduino Firmata library. Next, a series of callbacks are defined; all seven possible callbacks are present in this sketch. This sketch can therefore react to every sort of Firmata message, or at least call a specific function when the message is received. It is then up to you to fill in the callbacks using the Standard Firmata sketch as an example.
In loop()
the sketch receives and processes messages from the computer:
while(Firmata.available())
Firmata.processInput();
One of the variables in the program is samplingInterval
. This defines the rate at which Firmata polls the pins. The sketch then has a clever technique to make sure that the wanted sampling rate is maintained. Following is the code that is used:
currentMillis = millis();
if (currentMillis - previousMillis > samplingInterval)
{
previousMillis += samplingInterval;
// Code goes here
}
The variables currentMillis
and previousMillis
are each defined as an unsigned
long
. Each time Arduino enters loop()
, the millis()
function will be called, returning the number of milliseconds that the sketch has been running for. This value is then placed inside the variable currentMillis
. Then, a comparison is made between currentMillis
minus previousMillis
and the samplingInterval
. If the value of currentMillis
minus previousMillis
is larger than samplingInterval
, previousMillis
is increased by the value contained in samplingInterval
, and the sketch is free to send all the pin data.
In this chapter, I have shown you the Firmata library and how it interacts with an Arduino. You have seen the different messages and the callbacks used to react to them. In the next chapter, you see how to use the Arduino GSM shield and connect to mobile data networks, transfer data to and from servers, and create your own wireless server. You also see how to place and receive telephone calls.