Serial Communication
Connect Processing to Arduino over serial to read sensor data, control hardware, and build a bi-directional potentiometer and LED control sketch.
Serial communication is the bridge between Processing’s visual world and the physical world of electronics. Through a USB cable, Processing can read sensor values from an Arduino, control LEDs, motors, and servos, and create installations that respond to the real environment. This tutorial covers the full serial workflow and builds a bi-directional sketch where a potentiometer controls a circle and mouse clicks blink an LED.
Installing the Serial Library
The Serial library ships with Processing — no installation needed. Just add the import:
import processing.serial.*;
Finding Your Port
Each operating system names serial ports differently:
- Windows:
COM3,COM4, … - macOS:
/dev/cu.usbmodem…,/dev/cu.usbserial… - Linux:
/dev/ttyACM0,/dev/ttyUSB0
List all available ports to the console so you can pick the right one:
import processing.serial.*;
void setup() {
String[] ports = Serial.list();
for (int i = 0; i < ports.length; i++) {
println(i + ": " + ports[i]);
}
}
Run the sketch, check the console, then use the matching index or name in your actual sketch.
Opening a Serial Port
import processing.serial.*;
Serial port;
void setup() {
size(600, 400);
// Replace 0 with the index from Serial.list() that matches your device
String portName = Serial.list()[0];
port = new Serial(this, portName, 9600);
// Tell Processing to buffer incoming bytes until it sees a newline
port.bufferUntil('\n');
}
The baud rate (9600) must match the value passed to Serial.begin(9600) in the Arduino sketch. Common rates: 9600, 57600, 115200.
Reading Incoming Data
Use the serialEvent() callback, which fires automatically whenever the buffer fills (i.e., when a newline arrives after calling bufferUntil('\n')):
int sensorValue = 0;
void serialEvent(Serial p) {
String raw = p.readStringUntil('\n');
if (raw != null) {
raw = raw.trim();
try {
sensorValue = int(raw);
} catch (Exception e) {
// discard malformed lines
}
}
}
trim() removes the trailing newline and any carriage-return characters (\r) that Arduino’s println() appends.
Sending Data to Arduino
Send a single byte or a string:
port.write('H'); // send the character H (ASCII 72)
port.write('L'); // send L
port.write("255\n"); // send a number as a string followed by newline
On the Arduino side, read with Serial.read() for single bytes, or Serial.readStringUntil('\n') for strings.
Arduino Code: Potentiometer + LED
Wire a potentiometer to Arduino pin A0 and an LED (with 220Ω resistor) to pin 13.
// Arduino sketch — upload this first
const int POT_PIN = A0;
const int LED_PIN = 13;
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// Send potentiometer reading (0–1023) as a line
int val = analogRead(POT_PIN);
Serial.println(val);
// Read incoming commands
if (Serial.available() > 0) {
char cmd = Serial.read();
if (cmd == 'H') digitalWrite(LED_PIN, HIGH);
if (cmd == 'L') digitalWrite(LED_PIN, LOW);
}
delay(20); // ~50 readings per second
}
Upload this to the Arduino before running the Processing sketch.
Full Example: Potentiometer Controls Circle; Click Blinks LED
import processing.serial.*;
Serial port;
int potValue = 0; // 0–1023 from Arduino
boolean ledState = false;
int blinkUntil = 0; // millis() timestamp to stop blinking
void setup() {
size(600, 600);
smooth();
textFont(createFont("SansSerif", 13));
String[] ports = Serial.list();
if (ports.length == 0) {
println("No serial ports found. Running in demo mode.");
return;
}
// Find the Arduino port — adjust the search string for your OS
String arduinoPort = null;
for (String p : ports) {
if (p.contains("usbmodem") || p.contains("usbserial") ||
p.contains("ACM") || p.contains("COM")) {
arduinoPort = p;
break;
}
}
if (arduinoPort == null) arduinoPort = ports[0];
println("Connecting to: " + arduinoPort);
port = new Serial(this, arduinoPort, 9600);
port.bufferUntil('\n');
}
void draw() {
background(20, 24, 36);
// Map potentiometer range (0–1023) to circle diameter (20–400)
float diameter = map(potValue, 0, 1023, 20, 400);
// LED indicator
boolean ledOn = millis() < blinkUntil && ledState;
color ledColour = ledOn ? color(255, 80, 60) : color(60, 60, 80);
// Draw circle controlled by pot
noStroke();
fill(80, 160, 240, 200);
ellipse(width / 2, height / 2, diameter, diameter);
// Inner highlight
fill(255, 80);
ellipse(width / 2 - diameter * 0.15, height / 2 - diameter * 0.15,
diameter * 0.3, diameter * 0.3);
// LED status indicator
fill(ledColour);
ellipse(width - 40, 40, 24, 24);
fill(255, 180);
textAlign(RIGHT, TOP);
text("LED", width - 52, 32);
// Instructions
fill(140, 160, 200);
textAlign(CENTER, BOTTOM);
textSize(12);
text("Pot value: " + potValue + " | Diameter: " + nf(diameter, 0, 1) + "px",
width / 2, height - 20);
text("Click anywhere to blink Arduino LED", width / 2, height - 5);
// Pot value bar
fill(50, 70, 110);
rect(20, height - 50, width - 40, 8, 4);
fill(80, 160, 240);
float barW = map(potValue, 0, 1023, 0, width - 40);
rect(20, height - 50, barW, 8, 4);
}
void serialEvent(Serial p) {
String raw = p.readStringUntil('\n');
if (raw != null) {
raw = raw.trim();
if (raw.length() > 0) {
try {
potValue = int(raw);
potValue = constrain(potValue, 0, 1023);
} catch (Exception e) { /* ignore */ }
}
}
}
void mousePressed() {
if (port == null) return;
// Toggle LED state
ledState = !ledState;
blinkUntil = millis() + 300; // LED on for 300 ms
if (ledState) {
port.write('H');
} else {
port.write('L');
}
}
What Happens at Runtime
- The Arduino reads the potentiometer 50 times per second and sends the value as an ASCII line (
"512\n", for example). serialEvent()in Processing fires on each newline, parses the integer, and stores it inpotValue.draw()mapspotValue(0–1023) to a circle diameter (20–400 px) and redraws the circle every frame.- When the user clicks in the Processing window, a
'H'or'L'byte is sent to the Arduino, which turns the LED on or off.
Tips and Troubleshooting
Port already in use. Only one application can own a serial port at a time. Make sure the Arduino IDE’s Serial Monitor is closed before running the Processing sketch.
Garbled data. If sensorValue jumps to wild numbers, the baud rate probably does not match. Verify both sides use the same rate.
No serialEvent() fires. Check that port.bufferUntil('\n') was called in setup() and that the Arduino sketch ends lines with println() (not print()).
Cross-platform port names. Rather than hard-coding a port name, always search Serial.list() for a matching substring, or let the user select a port via a Processing GUI (ControlP5 library provides a convenient dropdown).
Key Takeaways
Serial.list()enumerates all available ports; pick the right one by index or substring match.port.bufferUntil('\n')enables theserialEvent()callback pattern, which decouples reading from the draw loop.port.readStringUntil('\n')plustrim()gives clean numeric strings safe to pass toint().- Send bytes with
port.write('H')for single-character commands; useport.write("value\n")for numeric strings. - Always close the Arduino IDE’s Serial Monitor before running a Processing sketch that uses the same port.