This tutorial covers the the topic of using serial communication within Max. Any number of external devices use a serial protocol (e.g. RS-232, Bluetooth) to communicate with a computer, and serial streams can even be used for low-bandwidth communication between computers (think dial-up Internet!). Devices such as professional-level video mixers, DVD players, and theatrical lighting systems use serial interfaces to receive commands, and microcontroller systems used by the physical computing community rely on serial communication to transmit sensor data back to a computer.
For this tutorial we'll be using the example of serial communication with a very popular electronics prototyping platform called the
Arduino. It consists of a small microcontroller mounted on a prototyping board that is programmed by a C-like language. It can communicate back to a host computer using a serial line over USB. Even if you don't have an Arduino, the principles shown in this tutorial should give you enough insight to communicate with any serial device you need.
Before we look at the Max patcher, below is the code that we've used for programming our Arduino in this tutorial. In this example, the Arduino doesn't read any sensor data or do anything particularly fancy: it just waits for any input from the computer, then begins to stream a serial feed of numbers back. The code for the Arduino is here:
// Arduino Serial Tester
// rld, cycling'74, 3.2008
long randomvalue = 0; // random value
long countervalue = 0; // counter value
int serialvalue; // value for serial input
int started = 0; // flag for whether we've received serial yet
void setup()
{
TABSTOP Serial.begin(9600); // open the arduino serial port
}
void loop()
{
TABSTOP if(Serial.available()) // check to see if there's serial data in the buffer
TABSTOP {
TABSTOPTABSTOP serialvalue = Serial.read(); // read a byte of serial data
TABSTOPTABSTOP started = 1; // set the started flag to on
TABSTOP }
TABSTOP if(started) { // loop once serial data has been received
TABSTOPTABSTOP randomvalue = random(1000); // pick a new random number
TABSTOPTABSTOP Serial.print(countervalue); // print the counter
TABSTOPTABSTOP Serial.print(" "); // print a space
TABSTOPTABSTOP Serial.print(randomvalue); // print the random value
TABSTOPTABSTOP Serial.print(" "); // print a space
TABSTOPTABSTOP Serial.print(serialvalue); // echo the received serial value
TABSTOPTABSTOP Serial.println(); // print a line-feed
TABSTOPTABSTOP countervalue = (countervalue+1)%1000; // increment the counter
TABSTOPTABSTOP delay(100); // pause
TABSTOP }
}
Even if the specifics of the Arduino programming language are not familiar, you can look a the comments for each line and see what's happening. The microcontroller initializes itself and opens up a serial port running at 9600 baud - this is the speed at which it will communicate with our Max patcher. It then enters a loop in which it checks to see if we've sent it any data and, once we do, starts streaming numbers back to the computer. It generates a number that counts from 0 to 1000 over and over again, a random number (in the same range), and an echo of the last value we've sent into the chip. These three values are printed (as if on a terminal) over the serial line.
Take a look at the tutorial. It consists of a
serial object at the top with some supporting logic at the output that eventually drives an
lcd graph of the data coming from the Arduino. The Max
serial object is used for communication with serial devices that don't have special drivers that put them in another category (e.g. MIDI or HID). These generic devices communicate with Max over a serial
port, which is defined as the first argument to the object. In Max these ports are
lettered (
a,
b, etc.). Click on the
print message box at the top of the patcher: the available serial ports on your computer will appear in the Max window, along with their letter mappings. The Arduino should appear there, along with any other serial devices you may have. If necessary, you can change the letter argument to the
serial object by unlocking the patcher and retyping the object.
In addition to the serial port, the
serial object takes a number of additional arguments to set up the communications
protocol. The second argument of the object sets the
baud rate, and needs to match that of the Arduino we're using. Since we've told the Arduino to transmit data at
9600 baud, we put the same argument to our
serial object. Additional arguments to the object allow us to specify the number of
data bits,
stop bits, and the
parity (even or odd) of the device we're talking to. Since the Arduino is a fairly typical device, the default settings will work.
The
serial object can both
transmit and
receive serial data. It transmits data by taking a
byte (an integer in the range of
0-
255) in its inlet and sending it over the selected serial port. These bytes can represent command values or can be translated into ASCII (the same numbers output from the
key object). In order to receive data, the
serial object must be
polled (just like
mousestate) - when a
bang is received by the object, it empties its internal buffer of any waiting serial messages and sends them sequentially (as bytes) out its outlet.
Assuming that our Arduino is loaded with our program (above) and connected to the computer, we should be able to talk to it provided the
serial object is set to the correct port. Turn on the
metro object with the
toggle box. As you can see from the
number box attached to the object, nothing will happen. This is because, in our Arduino code, we
wait for the computer to communicate with the microcontroller first.
Type the number
10 into the
number box attached to the
serial object and hit return. Assuming the
serial object transmitted the byte correctly, you should see numbers begin to appear out of the object. Turn on the
toggle labeled
1 to print the "raw" serial data in the Max window.
You'll notice that the numbers appearing in the Max window from the "raw" serial output don't seem to make much sense. This is because the Arduino is transmitting its values as ASCII, as if they were typed on a computer. Thus, the number 15 is represented as ASCII value 49 ("1") followed by ASCII value 53 ("5"). Spaces between our three values are represented by the ASCII for a space (32), and each set of three numbers is terminated by a carriage return and a line feed (ASCII 13 followed by 10). To use these values in Max, we need to first group them together and then convert them from ASCII into a human-readable string.
Uncheck the
toggle labeled
1 and check the
toggle labeled
2. This prints out the output of the
zl group object in the patcher. The
zl object is taking sequences of these ASCII values and grouping them into a
list; the
select object above the
zl forces the list to be output (and cleared) whenever a
13 (carriage return) is received. In addition, it eliminates line-feed (ASCII
10) characters from the stream. Since the right outlet of
select outputs anything not selected by the object, the rest of the values pour into the
zl. As a result, the Max window will show a list of ASCII values that should represent three numbers separated by spaces (ASCII
32).
Uncheck the
2 toggle and turn on the
3 toggle. This shows the output from the
itoa object, which takes the grouped list and converts the integers into their relevant ASCII characters. As a result, the Max window should show something that finally makes sense given our Arduino code: we should see a value counting from
0 to
1000 followed by a random value followed by
10 (the byte we
sent to the microcontroller earlier).
The
itoa object creates a Max
symbol, which is readable by us for the purposes of debugging, but not quite what we need for our patcher. While we recognize that the message is a list of three integers, objects like
unpack will not. The
fromsymbol object
parses a symbol, separating different types of data based on white-space; as a result, our symbol becomes converted into a list of three integers, which the
unpack object can split apart. Note that the
number boxes out of the
unpack correctly show our values. Change the
number box at the top again, and you will see that the third value out of the
serial object will update accordingly.
Take a look at the drawing part of the patcher and see how it works. Like many of our
lcd-based examples, it simply visualizes the
serial object's output, in this case making a graph based on the counter (x), random value (y), and echoed byte (color). These values could easily be applied to control whatever you like.
The Max
serial object provides the capability to communicate to and from standard serial devices within Max. It transmits and receives data as single bytes; objects such as
zl,
itoa, and
fromsymbol can be used to make sense of it all.
See Also
Name |
Description |
serial |
Send and receive characters from serial ports and cards
|
itoa |
Convert integers to ASCII characters
|
fromsymbol |
Transform a symbol into individual numbers/messages
|