In this article I’m going to talk about using communicating between an Arduino and Max/MSP using USB. Ultimately, in this series I’ll be working up to creating and documenting a reuseable serial protocol, and to start off I’m going to discuss some basic approaches..
If this is your first time communicating between an Arduino and Max/MSP, the easiest way to get started is using the Firmata Arduino firmware and the accompanying Max/MSP Firmata patch. (Firmware is just another name for the file you upload onto the Arduino.) Firmata is designed to allow for complete control of an Arduino from host software like Max/MSP. This is fine for prototyping and early experiments, but you’ll quickly find that there are limitations when working with Firmata. Using the computer to send command messages is much slower than generating commands on the Arduino itself, and makes it difficult to use shift registers, read sensors quickly, or really do anything beyond the simple reading and writing of analog and digital values.
On an Arduino there are two ways of sending data to the Serial port – Serial.print and Serial.write. Serial.print sends ASCII-encoded values, in which each character on your computer keyboard is assigned a numeric value. For example, Serial.print(100); will send three bytes — 49 48 48 (the ASCII values for ‘1’, ‘0’, and ‘0’). For sending sequential lists of numbers this is very inefficient, using 2-3x more bytes than are required. (Click here for more information on ASCII; click here for a conversion chart for ASCII values).
Instead, if you just want to send a list of numeric values (such as you would read from a sensor), Serial.write is more useful as it will send the raw value of each byte, from 0-255. The problem is that the serial data appears at the receiver side as a constant stream of numbers, with no formatting or indication of what the numbers mean. What we need is some way of grouping bytes together into packets.
Serial Communication in Max
To communicate using the serial port in Max/MSP you generally use the
serial object. This object provides access to all of the available serial ports on your computer. If you have an Arduino plugged in, it should show up as a serial port with some strange name like usbserial-absdf1234. Check the
serial help file to learn how to check the available ports – the accompanying patch uses a umenu to automatically show you the available ports.
Data received from the selected serial port comes out one byte at a time, in the order it is sent. So again, we need some way of grouping it into packets.
Basic approaches to Packeting
One possibility would be using a specific value to indicate the start of a new packet. For example, we could say every time we see the value ‘255’ we know the next byte is the start of a new packet:
raw data stream:
255 89 90 91 92 93 94 95 96 255 97 98 99 100 101 102 103 104 255 105
split into packets:
89 90 91 92 93 94 95 96
97 98 99 100 101 102 103 104
Here we can see that when we receive the value 255 we know that the next byte is the first byte in a packet.
The problem with this is that we lose the ability to use 255 as a valid data value. SLIP encoding, meaning “Serial Line Internet Protocol”, gives us a way of reclaiming that byte. Essentially, what SLIP encoding does is define both an end byte and an escape byte. When one of the data values we want to send is the same value as either our end or escape bytes, we escape that value by first writing the escape byte. Here is an example:
end byte =
escape byte =
raw data we want to send as a packet =
100 150 225 192 252 250 225 219
SLIP-encoded data stream =
100 150 225 219 192 252 250 225 219 219 192
Within the raw data stream of we have two values which are the same as either our escape or end byte. In our SLIP-encoded data stream we replaced those single bytes with pairs of bytes – the escape byte and then the data value we want to send. One way of looking at this is that when we see the escape byte we know that the next byte is a valid data value. Notice our SLIP-encoded data stream finishes with our end byte. When we see an end byte that isn’t preceded by an escape byte we know that our packet is complete.
SLIP-Encoding in Max
Let’s take a look at using SLIP encoding in Max. Starting at the top left, the top trigger object
t b 192 l does the following three things, in order from right to left:
- Send our data, in list form, out the right outlet, check the individual elements to see if we need to escape those values, and group all of the values in a zl.group object.
- Add an
Endmessage to the end of our packet.
- Send a
bangto the zl.group object to send the grouped bytes as a list.
SLIP-decoding in Max
To decode the data, we take the following steps:
- First we will check each byte to see if it the escape byte. Any byte following the escape byte goes straight to our zl.group object.
- Next we look for an unescaped end byte. If we find one, a
bangis sent to out
zl.groupobject to send out our packet as a list.
- Any other values coming in our grouped in the
My next post in this series will take a look at the Arduino patch to see how it handles SLIP encoding/decoding. And following that we will look at a few ways of making the process easier and more efficient. In the meantime, here are some files with a fully-functioning protocol implemented for both Max and Arduino: