Project Spec Sheet & Difficulty Rating
Time to Build: 45 minutes (Hardware) + 30 minutes (Software tuning)
Target Board Variant: Arduino Uno R3 (ATmega328P). See debugging notes on why the Uno R4 Minima is not recommended for this specific serial workflow.
Software Versions: Arduino IDE 2.x, Processing 4.3+
Core Concept: Asynchronous serial communication (UART over USB CDC) mapping physical polar coordinates to a 2D Cartesian rendering engine.
When exploring arduino and processing projects, the most common stumbling block isn't the wiring—it is the serial handshake. Processing 4 handles serial port enumeration differently than the Arduino IDE, and mismatched baud rates or unclosed serial monitors will immediately brick your visualizer. This guide walks through building a physical ultrasonic radar, providing bulletproof C++ and Java code, and diagnosing the exact serial errors that halt 90% of beginner builds.
Hardware BOM and Pin Mapping
We are using the classic Arduino Uno R3. While newer boards like the Uno R4 Minima or Nano ESP32 are excellent, they use native USB CDC serial. On Windows, Processing's Serial.list() often misindexes CDC ports or reads them as generic COM ports without the board name, causing ArrayIndexOutOfBoundsException errors. The Uno R3 uses an ATmega16U2 UART-to-USB bridge, which enumerates predictably across all operating systems.
| Component | Exact Variant / Spec | Arduino Pin | Notes |
|---|---|---|---|
| Microcontroller | Arduino Uno R3 (DIP ATmega328P) | N/A | Do not use R4 Minima for this specific Processing workflow. |
| Distance Sensor | HC-SR04 Ultrasonic (5V tolerant) | Trig: D9 Echo: D10 |
No voltage divider needed for Uno R3. If using a 3.3V board, use a 10k/20k divider on Echo. |
| Actuator | SG90 Micro Servo (9g, 180°) | Signal: D11 | Powered directly from 5V rail; acceptable for single SG90. Add external 5V PSU for heavier servos. |
| Power | USB 2.0/3.0 Cable (Data + Power) | USB Port | Ensure it is a data cable, not a charge-only cable. |
Arduino Firmware: Ultrasonic Distance Reading
This sketch sweeps the servo from 15° to 165°, triggers the HC-SR04 at each degree, and sends a comma-separated string (angle,distance\n) over serial at 9600 baud. We use a 30ms delay between steps to prevent the servo from jittering and to allow the ultrasonic ping to clear.
#include <Servo.h>
// Pin Definitions
const int trigPin = 9;
const int echoPin = 10;
const int servoPin = 11;
Servo myServo;
long duration;
int distance;
void setup() {
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
myServo.attach(servoPin);
}
void loop() {
// Sweep forward
for (int i = 15; i <= 165; i++) {
myServo.write(i);
delay(30); // Allow servo to reach position and ping to clear
distance = calculateDistance();
Serial.print(i);
Serial.print(",");
Serial.println(distance);
}
// Sweep backward
for (int i = 165; i > 15; i--) {
myServo.write(i);
delay(30);
distance = calculateDistance();
Serial.print(i);
Serial.print(",");
Serial.println(distance);
}
}
int calculateDistance() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Timeout set to 11700us (approx 2 meters max range)
duration = pulseIn(echoPin, HIGH, 11700);
if (duration == 0) {
return 200; // Return max dummy value if timeout occurs
}
return duration * 0.034 / 2;
}
Processing 4 Visualizer: The Radar Sweep
Upload the Arduino code, then close the Arduino IDE Serial Monitor. Open Processing 4 and paste the Java code below. This script initializes a 2D canvas, reads the serial buffer, parses the comma-separated values, and maps the polar coordinates to a Cartesian radar display.
import processing.serial.*;
Serial myPort;
String dataString = "";
int angle = 0;
int distance = 0;
int maxDist = 50; // Max distance in cm for scaling
void setup() {
size(800, 450); // Must be exactly half of 1600x900 or similar 16:9 ratio
background(0, 10, 20);
smooth();
// Error Handling: Try-Catch block for Serial Initialization
try {
// Print available ports to console for debugging
printArray(Serial.list());
// Index 0 is usually the Arduino Uno R3 on Mac/Linux, but check console on Windows
String portName = Serial.list()[0];
myPort = new Serial(this, portName, 9600);
myPort.bufferUntil('\n');
} catch (Exception e) {
println("SERIAL ERROR: Could not open port. Is the Serial Monitor closed? " + e.getMessage());
noLoop(); // Stop draw loop if serial fails
}
}
void draw() {
fill(0, 10, 20, 40); // Fading trail effect
noStroke();
rect(0, 0, width, height);
pushMatrix();
translate(width / 2, height - 20); // Move origin to bottom center
drawRadarGrid();
drawSweepLine();
drawObject();
popMatrix();
}
void serialEvent(Serial myPort) {
dataString = myPort.readStringUntil('\n');
if (dataString != null) {
dataString = trim(dataString);
String[] parsed = split(dataString, ',');
if (parsed.length == 2) {
try {
angle = int(parsed[0]);
distance = int(parsed[1]);
} catch (Exception e) {
// Ignore malformed packets during serial handshake
}
}
}
}
void drawRadarGrid() {
stroke(0, 255, 0, 50);
noFill();
// Draw arcs
for (int i = 1; i <= 4; i++) {
arc(0, 0, i * 150, i * 150, PI, TWO_PI);
}
// Draw radial lines
for (int a = 15; a <= 165; a += 30) {
float x = 300 * cos(radians(a));
float y = -300 * sin(radians(a));
line(0, 0, x, y);
}
}
void drawSweepLine() {
stroke(0, 255, 0);
strokeWeight(2);
float x = 300 * cos(radians(angle));
float y = -300 * sin(radians(angle));
line(0, 0, x, y);
}
void drawObject() {
if (distance < maxDist && distance > 2) {
float scale = map(distance, 0, maxDist, 0, 300);
float objX = scale * cos(radians(angle));
float objY = -scale * sin(radians(angle));
fill(255, 50, 50);
noStroke();
circle(objX, objY, 10);
}
}
Debugging Serial Handshakes: Exact Errors and Fixes
When bridging hardware and software, the serial port is a single-lane bridge. If one side is blocking, the whole system crashes. Here are the exact error strings you will encounter and how to fix them.
- Close the Arduino IDE Serial Monitor. The OS only allows one application to hold the COM port lock at a time.
- Verify the Port Index. Check the Processing console output for
Serial.list(). If your Arduino is on[1], changeSerial.list()[0]toSerial.list()[1]. - Check the Cable. Swap the USB cable. Charge-only cables lack the D+ and D- data lines, causing the board to power on but silently fail serial enumeration.
Error 1: Port Already in Use
Exact String: Serial port 'COM3' already in use. Try quitting any programs that may be using it.
Ranked Causes:
- Arduino IDE Serial Monitor is still open.
- A previous instance of the Processing sketch crashed and left the port locked in the background (check Task Manager/Activity Monitor).
- Another slicing software (like Cura or PrusaSlicer) is polling serial ports in the background.
Error 2: Array Index Out of Bounds
Exact String: ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
Ranked Causes:
- No serial devices are detected by the OS. Check Device Manager for unrecognized USB devices or missing CH340/CP210x drivers if using a clone board.
- The Arduino is connected, but Processing is reading a Bluetooth virtual COM port at index 0. Print the array and select the correct index.
Error 3: Null Pointer on Read
Exact String: NullPointerException: Cannot invoke "String.length()" because "s" is null
Ranked Causes:
- Processing is trying to parse data before the Arduino has sent the first complete line ending in
\n. Thetry-catchblock in the provided code handles this, but if you removed it, the sketch will crash on frame 1. - Baud rate mismatch. Ensure both sketches are strictly set to
9600.
Extending or Simplifying the Build
Once the baseline radar is functioning, you can adapt the hardware to fit your specific project constraints.
- Simplify (No Servo): Remove the SG90 servo and the
<Servo.h>library. Point the HC-SR04 in a fixed direction. In Processing, changedrawObject()to render a simple horizontal bar graph or a 1D proximity warning light instead of a 2D polar sweep. This is ideal for basic arduino and processing projects focused purely on data logging. - Extend (Add an MPU6050): Replace the fixed base with an IMU. Mount the HC-SR04 to a handheld rig. Read the MPU6050 yaw angle via I2C and send that as the
anglevariable instead of relying on a programmed servo sweep. This turns the project into a handheld spatial mapper. - Extend (Networked Data): Swap the Uno R3 for an ESP32. Instead of USB Serial, use WebSockets (
WebSocketsServerlibrary) to push the JSON payload to a Processing sketch running on a completely different computer on the same LAN.
Frequently Asked Questions
Can I use an Arduino Uno R4 or ESP32 for Arduino and Processing projects?
Yes, but with caveats. The Uno R4 Minima and ESP32 use native USB CDC (Communication Device Class) for serial over USB. On Windows, Processing's Serial.list() often struggles to differentiate CDC ports from standard RS-232 COM ports, sometimes requiring you to manually test indices [1] or [2]. Furthermore, the ESP32 operates at 3.3V logic; you must use a voltage divider (e.g., 10kΩ and 20kΩ resistors) on the HC-SR04 Echo pin to prevent frying the ESP32's GPIO with the sensor's 5V return signal.
Why is my Processing radar flickering or lagging behind the physical servo?
This is caused by blocking delays and serial buffer overflow. In the Arduino code, delay(30) is necessary for the physical servo to catch up, but if you reduce it to delay(5), the HC-SR04 will trigger before the previous acoustic ping has dissipated, causing false reads. In Processing, if the draw() loop runs at 60fps but serial data arrives at 30Hz, the visualizer will interpolate poorly. Ensure your serialEvent() strictly updates variables, and let the draw() loop handle rendering without blocking functions like delay().
How do I export my Processing sketch as a standalone desktop app?
Processing 4 includes a native export feature. Go to File → Export Application. Select your target OS (Windows, macOS, or Linux). Check "Include Java for macOS/Windows" to ensure the end-user doesn't need a JRE installed. Note that when exported, the hardcoded Serial.list()[0] index might change depending on the target machine's COM port allocation. For production apps, write a small setup routine in Processing that lists available ports and prompts the user to click the correct one before initializing the Serial object.
Where can I find more advanced rendering libraries for Processing?
For 3D mapping and advanced GUI elements, look into the Processing Foundation's official library repository. Specifically, the PeasyCam library is the industry standard for adding 3D orbital camera controls to Processing sketches, allowing you to map ultrasonic data to a 3D point cloud rather than a flat 2D plane.






