For this assignment, I worked with Youssab to connect p5.js and Arduino. We completed three exercises to practice sending data between the physical and digital worlds.
Part 1: One Sensor to p5.js
In this first exercise, we used a potentiometer on the Arduino. This controls the horizontal position of a circle on the computer screen. The Arduino reads the sensor value and sends it to p5.js. Then, p5.js moves the ball left or right based on that number.
Schematic
Arduino Code
void setup() {
Serial.begin(9600);
}
void loop() {
// Read analog value and send it as a line of text
int sensorValue = analogRead(A0);
Serial.println(sensorValue);
delay(20);
}
p5.js Code
let port;
let connectBtn;
let ballX = 0;
let sensorVal = 0;
function setup() {
createCanvas(600, 400);
background(50);
port = createSerial();
// Open the port automatically if used before
let usedPorts = usedSerialPorts();
if (usedPorts.length > 0) {
port.open(usedPorts[0], 9600);
}
connectBtn = createButton('Connect to Arduino');
connectBtn.position(10, 10);
connectBtn.mousePressed(connectBtnClick);
}
function draw() {
// Check if port is open
if (port.available() > 0) {
let data = port.readUntil("\n");
if (data.length > 0) {
// Update value
sensorVal = Number(data.trim());
}
}
background(256);
// Map sensor val to canvas width
ballX = map(sensorVal, 0, 1023, 25, width - 25);
// Draw ball
fill(0, 255, 100);
noStroke();
ellipse(ballX, height / 2, 50, 50);
}
function connectBtnClick() {
if (!port.opened()) {
port.open('Arduino', 9600);
} else {
port.close();
}
}
Part 2: p5.js to LED Brightness
For the second part, we reversed the flow of data. We send information from p5.js to the Arduino. The ball on the screen represents a light bulb. If you drag the ball higher, the LED gets brighter. If you drag it lower, the LED dims.
Schematic
Arduino Code
void setup() {
Serial.begin(9600);
pinMode(9, OUTPUT); //pmw pin
}
void loop() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
int brightness = input.toInt(); // convert str to int
brightness = constrain(brightness, 0, 255); // just in case data is weird
analogWrite(9, brightness);
}
}
p5.js Code
let port;
let connectBtn;
// Ball variables
let ballX = 300;
let ballY = 200;
let ballSize = 50;
let isDragging = false;
// Data variables
let brightness = 0;
let lastSent = -1;
function setup() {
createCanvas(600, 400);
port = createSerial();
let usedPorts = usedSerialPorts();
if (usedPorts.length > 0) {
port.open(usedPorts[0], 9600);
}
connectBtn = createButton('Connect to Arduino');
connectBtn.position(10, 10);
connectBtn.mousePressed(connectBtnClick);
}
function draw() {
background(50);
// ball logic
if (isDragging) {
ballX = mouseX;
ballY = mouseY;
// Keep ball inside canvas
ballY = constrain(ballY, 0, height);
ballX = constrain(ballX, 0, width);
}
// map brightness to y pos
brightness = floor(map(ballY, 0, height, 255, 0));
// send data
if (port.opened() && brightness !== lastSent) {
port.write(String(brightness) + "\n");
lastSent = brightness;
}
//draw ball
noStroke();
fill(brightness, brightness, 0);
ellipse(ballX, ballY, ballSize);
stroke(255);
line(ballX, 0, ballX, ballY);
}
// --- MOUSE INTERACTION FUNCTIONS ---
function mousePressed() {
// check if mouse is inside the ball
let d = dist(mouseX, mouseY, ballX, ballY);
if (d < ballSize / 2) {
isDragging = true;
}
}
function mouseReleased() {
// stop dragging when mouse is let go
isDragging = false;
}
function connectBtnClick() {
if (!port.opened()) {
port.open('Arduino', 9600);
} else {
port.close();
}
}
Part 3: Gravity Wind and Bi-directional Communication
This final exercise combined everything. We used the gravity wind example code. We modified it to do two things.
First, we use a potentiometer to control the wind force. Second, when the ball bounces on the floor, the LED lights up.
This required some problem solving. I had to ignore very small bounces. Without that check, the LED would blink constantly when the ball rolled on the floor. I also added a visual indicator arrow to show the direction of the wind.
Schematic
Arduino Code
void setup() {
Serial.begin(9600);
pinMode(9, OUTPUT); // LED on Pin 9
}
void loop() {
// read & send pot value
int potValue = analogRead(A0);
Serial.println(potValue);
if (Serial.available() > 0) {
char inChar = Serial.read();
// check for blink command
if (inChar == 'B') {
digitalWrite(9, HIGH);
delay(50);
digitalWrite(9, LOW);
}
}
delay(15);
}
p5.js Code
let port;
let connectBtn;
let velocity;
let gravity;
let position;
let acceleration;
let wind;
let drag = 0.99;
let mass = 50;
let sensorVal = 512;
function setup() {
createCanvas(640, 360);
noFill();
// Physics Setup
position = createVector(width/2, 0);
velocity = createVector(0,0);
acceleration = createVector(0,0);
gravity = createVector(0, 0.5*mass);
wind = createVector(0,0);
// Serial Setup
port = createSerial();
let usedPorts = usedSerialPorts();
if (usedPorts.length > 0) {
port.open(usedPorts[0], 9600);
}
connectBtn = createButton('Connect to Arduino');
connectBtn.position(10, 10);
connectBtn.mousePressed(connectBtnClick);
}
function draw() {
background(255);
// read for wind
if (port.available() > 0) {
let data = port.readUntil("\n");
if (data.length > 0) {
sensorVal = Number(data.trim());
}
}
// map wind
let windX = map(sensorVal, 0, 1023, -0.8, 0.8);
wind.set(windX, 0);
// apply physics
applyForce(wind);
applyForce(gravity);
velocity.add(acceleration);
velocity.mult(drag);
position.add(velocity);
acceleration.mult(0);
// draw
fill(0);
ellipse(position.x, position.y, mass, mass);
drawWindIndicator(windX);
// detect bounce
if (position.y > height - mass/2) {
velocity.y *= -0.9;
position.y = height - mass/2;
// send blink command
if (abs(velocity.y) > 1 && port.opened()) {
port.write('B');
}
}
// collision detection
if (position.x > width - mass/2) {
position.x = width - mass/2;
velocity.x *= -0.9;
} else if (position.x < mass/2) {
position.x = mass/2;
velocity.x *= -0.9;
}
}
function applyForce(force){
let f = p5.Vector.div(force, mass);
acceleration.add(f);
}
function connectBtnClick() {
if (!port.opened()) {
port.open('Arduino', 9600);
} else {
port.close();
}
}
// helper to visualize the wind
function drawWindIndicator(w) {
push();
translate(width/2, 50);
fill(150);
noStroke();
text("Wind Force", -30, -20);
stroke(0);
strokeWeight(3);
line(0, 0, w * 100, 0);
fill(255, 0, 0);
noStroke();
if (w > 0.05) triangle(w*100, 0, w*100-10, -5, w*100-10, 5); // Right Arrow
if (w < -0.05) triangle(w*100, 0, w*100+10, -5, w*100+10, 5); // Left Arrow
pop();
}
function keyPressed(){
// reset ball
if (key==' '){
mass=random(15,80);
position.y=-mass;
position.x = width/2;
velocity.mult(0);
}
}

