Exercise: Max Arduino Companion

Objective

Communicate data between Max/MSP/Jitter and an Arduino using the USB serial bus.

Max is a visual programming system for event-driven musical, audio, and video processing; for a general overview please see Max/MSP Visual Programming. The Arduino and Max solve different problems well. The Arduino has direct access to its hardware interface and can create and respond to electrical signals with very low latency. Max can be highly useful for constructing ad hoc user interfaces, music composition, real-time audio synthesis (MSP), and video (Jitter) processing. Max itself runs on Windows or macOS, but can be interfaced to physical hardware over USB using Arduinos or other microcontrollers. This example explores linking Max and an Arduino using a simple extensible text message protocol over the USB serial bus.

The serial communication does introduce significant latency between the Arduino and Max so this approach is strongest for systems in which Max is used for tasks such as the GUI, visual display, sequencing, and overall event control. It would not be suitable for high-bandwidth tasks such as cycling individual digital outputs to create a stepper motor waveform. However, the extensibility of the protocol means that you can add those low-level hardware behaviors to the Arduino sketch and invoke them via messages.

Arduino Interfacing

The exercise is titled as ‘companion’ because Max and the Arduino can be seen as having highly complementary peer roles; depending on the application, one might view the Arduino as providing an interface service to Max, or Max as providing audio output to an Arduino-based device. The best system design will carefully partition the responsibilities according to the problem at hand.

This usage pattern divides system control into an Arduino sketch and Max patch, communicating using serial data over a USB port. The Arduino sketch typically performs hardware I/O and perhaps special-purpose low-level control of a device. If the application is undemanding, it may be possible to use a generic firmware like Firmata to relay input and output values from Max. For most machines, the hardware control requires more customized I/O to read specific sensors or a local control loop. In this case, the firmware needs to be highly customized, and it is usually easier to start from a simpler example.

The following example uses the OneInOneOutASCII Arduino Sketch as the firmware. This sketch is designed to be easily extended and features a readable text-based communications protocol which is human-readable and straightforward to decode from a Max patch. The Firmata protocol uses a compact binary data format, but as a result is harder to debug.

The Max patch is self-contained in a single file, arduino-companion.maxpat. In a more elaborated system, it is recommended to separate the Arduino support into a set of re-usable abstractions.

Preparation

Setting up the exercise requires a couple of preparatory steps:

  1. Compiling and loading the OneInOneOutASCII.ino Arduino sketch onto an Arduino Uno.

  2. Determining the exact name of the serial port offered by the Arduino over USB.

  3. Running the arduino-companion.maxpat patcher in Max on a laptop.

  4. Verifying or setting the serial port name in the interface.

The OneInOneOutASCII sketch is provided in the course distribution in the OneInOneOutASCII folder. The source code of the sketch provides details of the messaging scheme.

The arduino-companion Max patcher is in the course distribution in the arduino-companion folder.

Steps and observations

  1. Upload the OneInOneOutASCII sketch onto an Arduino Uno.

  2. Open the Serial Monitor, set the baud rate to 115200 and the line ending to Newline. You should see data messages start to stream by.

  3. Enter poll 1000 in the input box and press enter. The message rate should slow.

  4. Enter led 0 in the input box and press enter. The LED marked L should go off.

  5. Enter led 1 in the input box and press enter. The LED marked L should go on again.

  6. Note the exact serial port name from the Tools/Port menu in the Arduino IDE.

  7. Exit the Arduino IDE.

  8. The easiest way to run the Max patcher is to double-click on the arduino-companion.maxpat file. Or Max can be launched and the file opened using File/Open.

  9. The first step in operating the patch is opening the Arduino port by selecting the correct serial port from the drop-down menu near the top. If the desired port is not in the list, please press ‘Rescan serial ports’ and retry.

  10. If the connection succeeds, the lower portion should begin showing various changing status values, including the clock value.

  11. The middle area contains a few controls for affecting the hardware output. You should be able to toggle the built-in LED. On each press you should also see the RX LED flash briefly as the control message is received on the Arduino.

Examining the Patcher

The patcher itself contains comments which document the various features. It initially opens in a presentation mode in which only the main controls are visible. At the top is a selector for choosing the serial port device representing your Arduino. Below that are a few controls which send control messages to the Arduino. At the bottom are a few fields of data decoded from the status messages coming from the Arduino.

If you toggle Presentation Mode, the details of the patch become visible. At the center is the [serial] object which both sends and receives data to the Arduino.

Above is a set of objects which convert normal Max message lists into string data to send out the serial port. A text command such as svo 4 30 which sets the servo command on output 4 to 30 degrees is initially represented as a normal Max list [svo 4 30], converted to a string of character data using [atoi], and transmitted.

Below the [serial] object are objects to decode the lines of text received from the Arduino. The character stream is divided into lines with [sel], [zl group], and [itoa], then parsed into a normal Max list with [fromsymbol]. Below that is typical Max list processing: a [route] separates the different message types which are decoded using various forms of [unpack].

Extending the System

The first step in extending the system is writing custom I/O code in the Arduino sketch, defining new message formats if necessary. The input and output sections of this patcher can then be extended for the new messages. It is recommended to keep the messages fixed-format with a known number of fields as that will greatly simplify the data processing.

Arduino and Max Code

  1. Documentation: OneInOneOutASCII Arduino Sketch

  2. Sketch Folder: OneInOneOutASCII

  3. Max patcher: arduino-companion.maxpat

  4. Max folder: arduino-companion