Lab: Serial output from P5.js

Overview

When you use the P5.js‘ p5.serialport library, it communicates with a webSocket server in the P5.js IDE to give you access to serial devices attached to your computer. This lab shows you how to use P5 to control a microcontroller using asynchronous serial communication.

To get the most out of this tutorial, you should know what a microcontroller is and how to program them. You should also understand asynchronous serial communication between microcontrollers and personal computers. You should also understand the basics of P5.js, and should have tried the Serial Input to P5.js lab.

Things You’ll Need

For this lab, you’ll need the hardware below, and you’ll need to download the P5.js complete library. You’ll also need to install node.js and the P5.serialserver (instructions here). You can use the P5.js web editor or your favorite text editor for this (the Atom text editor works well).

An Arduino Uno. The USB connector is facing to the left, so that the digital pins are on the top of the image, and the analog pins are on the bottom.
An Arduino Uno.
LEDs. Shown here are four LEDs. The one on the right is an RGB LED. You can tell this because it has four legs, while the others have only two legs.
LEDs. Shown here are four LEDs. The one on the right is an RGB LED. You can tell this because it has four legs, while the others have only two legs.
Resistors. Shown here are 220-ohm resistors. You can tell this because they have two red and one brown band, followed by a gold band.
Resistors. Shown here are 220-ohm resistors. You can tell this because they have two red and one brown band, followed by a gold band.
An 8 ohm speaker with 2 wires solder to the speakers leads
An 8 ohm speaker (optional).This is a good alternate to the LED if you prefer audible output.

Prepare the breadboard

Conect power and ground on the breadboard to power and ground from the microcontroller. On the Arduino module, use the 5V and any of the ground connections:

An Arduino Uno on the left connected to a solderless breadboard, right. The Uno's 5V output hole is connected to the red column of holes on the far left side of the breadboard. The Uno's ground hole is connected to the blue column on the left of the board. The red and blue columns on the left of the breadboard are connected to the red and blue columns on the right side of the breadboard with red and black wires, respectively. These columns on the side of a breadboard are commonly called the buses. The red line is the voltage bus, and the black or blue line is the ground bus.
An Arduino Uno on the left connected to a solderless breadboard, right.

 

Made with Fritzing

Add an LED

Connect the LED and resistor to digital I/O pin 5 of the module. Alternately, you can replace the 220-ohm LED with a speaker. You’ll find code below that uses tones instead of LEDs where appropriate:

Schematic view of an Arduino connected to an LED. Digital pin 5 is connected to a 22-ohm resistor. The other side of the resistor is connected to the anode (long leg) of an LED. The cathode of the LED is connected to ground.
Schematic view of an Arduino connected to an LED.
Breadboard view of an Arduino connected to an LED. The +5 volts and ground pins of the Arduino are connected by red and black wires, respectively, to the left side rows of the breadboard. +5 volts is connected to the left outer side row (the voltage bus) and ground is connected to the left inner side row (the ground bus). The side rows on the left are connected to the side rows on the right using red and black wires, respectively, creating a voltage bus and a ground bus on both sides of the board. A blue wire connects Digital to a 22-ohm resistor that straddles the center divide of the breadboard in row 17. The other side of the resistor is connected to the anode (long leg) of an LED. The LED is mounted in rowsd 16 and 17 of the right side of the center section of the board. a black wire connects the cathode's row, row 16, to the ground bus on the right side of the board.
Breadboard view of an Arduino connected to an LED.

Program the Microcontroller

Program your Arduino to read the analog input as follows:

void setup() {
 Serial.begin(9600);           // initialize serial communications
}

void loop() {
 if (Serial.available() > 0) { // if there's serial data available
 int inByte = Serial.read();   // read it
 Serial.write(inByte);         // send it back out as raw binary data
 analogWrite(5, inByte);       // use it to set the LED brightness
 // if you're using a speaker instead of an LED, uncomment line below  and comment out the previous line:
 //  tone(5, inByte*10);     // play tone on pin 5
 }
}

The P5.js serialport library

To communicate with your microcontroller serially, you’re going to use the P5.js serialport library and the P5.serialserver (command line version) or p5.serialcontrol (GUI version). The P5.js serialport library can’t access your serial ports directly when a sketch is running in a browser because the browser doesn’t have direct access to the serial port. But it can communicate with a server program on your computer that can exchange data with the serialport.P5.serialserver (or p5.serialcontrol) is  the server that connects your sketch, running in a browser, with the serial ports on your computer as shown below:

Diagram of three rectangles connected by arrows. The rectangle on the right represents your p5.js sketch, running in a browser. Your sketch implements the p5.serialport library. Your sketch connects to p5 serial control, the middle rectangle, via a webSocket. The p5 serial control application runs on your laptop. It connects to a serial port on your computer and listens for webSocket connections from your p5.js sketch. It passes whatever comes in the serial port through to the webSocket and vice versa. The third rectangle is your Arduino, connected to p5 serial control via the serial port.
Diagram of the connection from the serial port to p5.js through p5.serialcontrol

 

Once you gain an understanding of serial communication, you can use any program that can connect with your computer’s serial ports to communicate with a microcontroller. Processing, Max/MSP, and OpenFrameworks are three other popular multimedia programming environments that can communicate via the serial ports.

Install the P5.serialcontrol App

Download the latest version of the P5.serialcontrol application and save it in your Applications folder. When you run it, it will check serial ports on your machine. Click “list ports” to refresh the list, and you should see the serial port of your Arduino board show up. You can now move onto using the P5.js editor making sure that the p5.serialport.js file is inside your project folder. You can skip the next section unless you want to run p5.serialserver from the command line instead from the p5.serialcontol app.

You’ll need to know the name of your serial port to get started. If you’re not sure how to get this, see the Serial Input to P5.js lab for how to get a list of ports.

The P5.js Sketch

The sketch you’re going to write will control the microcontroller’s LED from P5.js. Dragging the mouse up and down the canvas will dim or brighten the LED, and typing 0 through 9 will set the LED’s brightness in increments from off (0) through almost full brightness (9). There’s an alternate sketch that will make changing tones if you prefer that instead of a changing LED. The sketch will also receive serial input from the microcontroller just as in the Serial Input to P5.js lab, so that you can see that the microcontroller is getting the same values you’re sending.

Program P5.js For Serial Communication

As you saw in the Serial Input to P5.js lab,  you’ll need to copy the p5.seriaport.js lbrary into your sketch’s libraries folder. the setup of your sketch will initialize the P5.serialport library and define your callback functions for serial events. Program the global variables and setup() function as follows:

var serial;          // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1421'; // fill in your serial port name here
var inData;                            // for incoming serial data
var outByte = 0;                       // for outgoing data

function setup() {
 createCanvas(400, 300);          // make the canvas
 serial = new p5.SerialPort();    // make a new instance of the serialport library
 serial.on('data', serialEvent);  // callback for when new data arrives
 serial.on('error', serialError); // callback for errors
 serial.open(portName);           // open a serial port
}

You’re only using the ‘data’ and ‘error’ callbacks this time, but you can add the other serial callbacks if you want them.

Program the serialEvent() function and serialError() function similarly to those in the previous lab. They read incoming data (serialEvent()) and report any errors (serialError()), as follows:

function serialEvent() {
 // read a byte from the serial port:
 var inByte = serial.read();
 // store it in a global variable:
 inData = inByte;
}

function serialError(err) {
  println('Something went wrong with the serial port. ' + err);
}

Program the  draw() function to display the value of any incoming serial bytes. Here it is:

function draw() {
 // black background, white text:
 background(0);
 fill(255);
 // display the incoming serial data as a string:
 text("incoming value: " + inData, 30, 30);
}

To read the mouse and keyboard, you’ll need to write functions to respond to the ‘mouseDragged’ and ‘keyPressed’ events. ‘MouseDragged’ will happen whenever you click and drag the mouse on the canvas. When that happens, read the mouseY, and map its position on the canvas to a value from 0 to 255. Convert the result to a number using the int() function. Then send it out the serial port using the serial.write() function:

function mouseDragged() {
 // map the mouseY to a range from 0 to 255:
 outByte = int(map(mouseY, 0, height, 0, 255));
 // send it out the serial port:
 serial.write(outByte);
}

The serial.write() function is versatile. If you give it a variable or literal that’s a numeric data type, it will send it as its raw binary value. In the code above, note how you’re converting the output of the map() function to a number using the int() function.  If you give it a string, however, it will send out that ASCII string. So be aware of the difference, and make sure you know whether your serial receiving device wants raw binary or ASCII-encoded data.

Program the keyPressed() function similarly to the mouseDragged() function. You want it to read the key strokes, convert them to raw bytes, and send them out the serial port. But you only want to send them if they key hit was 0 through 9. The P5.js variable key returns a numeric value, so you can do math on it and convert it like so:

function keyPressed() {
 if (key >=0 && key >=9) { // if the user presses 0 through 9
 outByte = byte(key * 25); // map the key to a range from 0 to 225
 }
 serial.write(outByte); // send it out the serial port
}

That’s all you want your sketch to do, so try running it now. You should see that the initial incoming serial value is undefined, but when you drag the mouse up and down, or type 0 through 9, it will update when the Arduino program returns what it received. The LED will also change with these actions.

Sending ASCII-Encoded Serial Data

If you want to send ASCII-encoded serial data from P5.js, all you have to do is to serial.write() your string. Sending strings is the P5.serialport’s default behavior. On the Arduino side, you can read single characters one byte at a time simply as well. However, if you want to convert multi-byte number strings to numeric values, you’ll need a new function to read ASCII encoded numeric strings called parseInt().

Program the  Microcontroller Again

To start off with, load a sketch from the Arduino examples called PhysicalPixel. You can find it in the File Menu -> Examples -> Communication -> PhysicalPixel. Here’s what it looks like. Change the LED pin number to pin 5 as follows:

const int ledPin = 5; // the pin that the LED is attached to
int incomingByte;     // a variable to read incoming serial data into

void setup() {
 Serial.begin(9600);             // initialize serial communication
 pinMode(ledPin, OUTPUT);        // initialize the LED pin as an output
}

void loop() {
 if (Serial.available() > 0) {   // see if there's incoming serial data
   incomingByte = Serial.read(); // read it
   if (incomingByte == 'H') {    // if it's a capital H (ASCII 72),
     digitalWrite(ledPin, HIGH); // turn on the LED
     // if you're using a speaker instead of an LED, uncomment line below  and comment out the previous line:
     //  tone(5, 440);           // play middle A on pin 5
   }
   if (incomingByte == 'L') {    // if it's an L (ASCII 76)
     digitalWrite(ledPin, LOW);  // turn off the LED
     // if you're using a speaker instead of an LED, uncomment line below  and comment out the previous line:
     // noTone(5);
   }
 }
}

When you run this, open the serial monitor and type H or L, and the LED will go on or off.  Try typing h or l instead. The LED won’t change, because H and h have different ASCII values, as do L and l. But you can see from this that you don’t need to memorize the ASCII chart to check for character values in your code. Put the character you want to read in single quotes, and the Arduino compiler will automatically convert the character to its ASCII value for you. It only works for single characters, though.

Program P5.js To Control the LED

To get P5.js to control this Arduino program serially, you only need to change the keyPressed() function to read H or L instead of 0 through 9. Here’s your new mousePressed() function:

function keyPressed() {
 if (key ==='H' || key ==='L') { // if the user presses H or L
 serial.write(key);              // send it out the serial port
 }
}

Because the key is already a single character, P5.js sends it out as is, and Arduino reads it as a single byte, looking for the ASCII value of H or L. Notice how the values returned to P5.js are 72 and 76, the ASCII values for H and L. For single characters like this, exchanging data is simple.

If you tried to change the LED with the mouse, you didn’t see anything happen unless your output value was 72 or 76. Why is that?

Processing ASCII-Encoded Strings With Arduino

It is also possible to read and interpret ASCII-encoded strings in Arduino. The String.parseInt() function reads an incoming string until it finds a non-numeric character, then converts the numeric string that it read into a long integer. This is a blocking function, meaning that String.parseInt() stops the program and does nothing until it sees a non-numeric character, or until a timeout passes. The timeout is normally one second (or 1000 milliseconds), but you can set it to a lower number of milliseconds using Serial.setTimeout(). Here’s a variation on the original Arduino sketch from above, using Serial.parseInt() this time:

void setup() {
 Serial.begin(9600);    // initialize serial communications
 Serial.setTimeout(10); // set the timeout for parseInt
}

void loop() {
 if (Serial.available() > 0) {   // if there's serial data available
 int inByte = Serial.parseInt(); // read it
   if (inByte > 0) {
      Serial.write(inByte);      // send it back out as raw binary data
      analogWrite(5, inByte);    // use it to set the LED brightness
      // if you're using a speaker instead of an LED, uncomment line below  and comment out the previous line:
     //  tone(5, inByte*10);     // play tone on pin 5
   }
 }
}

Upload this to your microcontroller, then open the Serial Monitor and send in some ASCII numeric strings. You’ll see the character that’s represented by the string’s value. For example, 65 will return A, 34 will return “, and so forth.

Notice that this version of the sketch has a conditional statement to check if the incoming byte is 0. This is because of a quirk of the parseInt() function. It returns 0 if the timeout is hit, or if the string is legitimately 0. This means you can’t really parse for a string like this: "0\n".

Program P5.js To Send a String With a Newline Character

Now that your microcontroller is expecting a string, program P5.js to send one. This means changing the mouseDragged() function. You still need to convert it to an integer using the int() function (you could also use round()), but then you need to convert it back to a String and add a delimiter. A quick way to do this is by adding the delimiter in the serial.write() command like so:

serial.write(outByte + '\n');

When the command encounters the two different elements, the number and the string (‘\n’), it will convert the number into a string in in order to concatenate the two. In addition, the newline on the end will is useful on the Arduino side. Since it’s a non-numeric character, the Serial.parseInt() function will see it and parse the string, not waiting for the timeout.

The full code for all the examples in this lab can be found in this gitHub repository.

Conclusion

When you’re sending data between two computers using asynchronous serial communication, you have to make sure that what the sender is sending is formatted the same as what the receiver is listening for. Think this out in advance before you code, then consider what functions you’ve got on both computers to convert data from strings to raw binary numbers and back. Test with fixed values at first, so you know you’re getting what you think you should. For example, sending an ASCII-encoded numeric string like this:

1023\n

Will always result in these six bytes:

49 48 50 51 10

Likewise, this text string:

Hello\n

will always be:

72 101 108 108 111 10

By sending a string you know both the ASCII and raw binary representations of, you can test your code easier, because what you’re sending won’t change. Once you know the sending and receiving works, then you can send variable strings.

The more you work with serial data, the more you’ll become familiar with the methods for handling it.

For more on serial flow control in P5.js, see the Two-Way Duplex Serial Communication Using P5.js Lab.

Originally written on October 5, 2015 by Tom Igoe
Last modified on August 21, 2018 by Tom Igoe