Processing Intermediate Article 9

Serial Communication

Connect Processing to Arduino over serial to read sensor data, control hardware, and build a bi-directional potentiometer and LED control sketch.

⏱ 17 min serial arduino hardware sensors iot

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.

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

  1. The Arduino reads the potentiometer 50 times per second and sends the value as an ASCII line ("512\n", for example).
  2. serialEvent() in Processing fires on each newline, parses the integer, and stores it in potValue.
  3. draw() maps potValue (0–1023) to a circle diameter (20–400 px) and redraws the circle every frame.
  4. 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 the serialEvent() callback pattern, which decouples reading from the draw loop.
  • port.readStringUntil('\n') plus trim() gives clean numeric strings safe to pass to int().
  • Send bytes with port.write('H') for single-character commands; use port.write("value\n") for numeric strings.
  • Always close the Arduino IDE’s Serial Monitor before running a Processing sketch that uses the same port.