All Posts

Assignment 11- Serial Communication

For the first-in-class exercise, we attached a potentiometer to the breadboard and connected Arduino to the P5js sketch. As we adjusted the serial input value of the potentiometer the ellipse moved up and down. We replaced the y coordinate of the ellipse with the incoming serial value to do so.

Here is the link to the video.

Code

P5js

// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;
// for incoming serial data
let outByte = 0;              // for outgoing data
 
function setup() {
  createCanvas(400, 300);          // make the canvas
  // check to see if serial is available:
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}
 
function draw() {
  
   background(0);
   fill(255);
   text("sensor value: " + inData, 30, 50);
  ellipse (200, inData, 50, 50)
  
}
// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 
// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 
// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 
// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log();
  
}
 
// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 
// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 
function closePort() {
  serial.close();
}

Arduino Code

void setup() {
  Serial.begin(9600); // initialize serial communications
}
 
void loop() {
  // read the input pin:
  int potentiometer = analogRead(A0);            
  // remap the pot value to fit in 1 byte:
  int mappedPot = map(potentiometer, 0, 1023, 0, 255); 
  // print it out the serial port:
  Serial.write(mappedPot); 
    // slight delay to stabilize the ADC:
  delay(1);                                                          
                                      
  
  // Delay so we only send 10 times per second and don't
  // flood the serial connection
  delay(100);
}

 

Final Project Proposal

By: Fuad & Phoebe

INSPIRATION

For our final project, we were interested in making another game, but instead of a more action-based game, one that was a subtle, calming, yet visually pleasing game. We discussed the mobile game I Love Hue, which is a puzzle type game that tasks the player with organizing a grid of tiles of different colors into a gradient. We also discussed the meaning of good interaction design and how to create a system whose parts were meaningful, intentional, and effective.  Something that was simple, with few “gadgets,” and minimalistic but visually pleasing animations were two things we wanted to incorporate. As such, we came up with a “color matching” game.

CONCEPT

Our color matching game consists of a series of “levels”, where each level displays a two squares side by, one square being the “target” color, and the other being the “test” square that the player must manipulate to match the target color. The player will be able to manipulate the color of the square by turning potentiometers on the Arduino board, each that correspond with the red, green, and blue values of the target color. The player will have a set amount of time to adjust the color of the test square and is scored based on how close the match is to the target color.

What Arduino will do 

As mentioned above, Arduino will provide the means for manipulating the color onscreen. Each potentiometer will be mapped to a value of 0 to 255, corresponding to RGB values. Other than perhaps clicking a key to start the game, the player shouldn’t need to use the keyboard or mouse.

What p5.js will do 

The sketch will provide almost all visuals of the game. However, we thought it would be interesting to include red, green, and blue LED’s on our board, whose brightness would change according to how close the user was to the values of the target color. Hence, the sketch would also communicate back to Arduino, adjusting the brightness of the LED’s.

Final Project Concept

Concept

For this project, I would like to make a drawing platform, in which as the user moves the pen connected to the device, the drawing appears on the p5js. To make the drawing more interesting and unique, I am going to add Perlin noise to the lines. Shown below is the rough diagram of the platform.

The device will consist of 2 arms (blue and orange), 2 joints (with potentiometers (green)), and a pen. Joint at the top is a fixed point so position of the arms will vary depending on the movement of the pen. Each joint is connected to a potentiometer in order to read the angle value. Since we will know the length of each arm and the angle between each arm, we will able to convert the values (polar) into a cartesian value that is easy to display on p5js.

For Perlin noise, I will simply add or substract random integers from the calculated points.

Interaction Between Arduino and P5JS

  1. Arduino reads potentiometer value
  2. Arduino sends sensor values to p5js
  3. P5js converts sensor values into cartesian coordinates
  4. P5js draws points on the canvas with some Perlin noise

I am very excited to work on this project, but I am worried about how smooth potentiometer will rotate, which may make moving a pen hard.

final project proposal

By: Fuad & Phoebe

INSPIRATION

For our final project, we were interested in making another game, but instead of a more action-based game, one that was a subtle, calming, yet visually pleasing game. We discussed the mobile game I Love Hue, which is a puzzle type game that tasks the player with organizing a grid of tiles of different colors into a gradient. We also discussed the meaning of good interaction design and how to create a system whose parts were meaningful, intentional, and effective.  Something that was simple, with few “gadgets,” and minimalistic but visually pleasing animations were two things we wanted to incorporate. As such, we came up with a “color matching” game.

CONCEPT 

Our color matching game consists of a series of “levels”, where each level displays a two squares side by, one square being the “target” color, and the other being the “test” square that the player must manipulate to match the target color. The player will be able to manipulate the color of the square by turning potentiometers on the Arduino board, each that correspond with the red, green, and blue values of the target color. The player will have a set amount of time to adjust the color of the test square and is scored based on how close the match is to the target color.

 

What Arduino will do 

As mentioned above, Arduino will provide the means for manipulating the color onscreen. Each potentiometer will be mapped to a value of 0 to 255, corresponding to rgb values. Other than perhaps clicking a key to start the game, the player shouldn’t need to use the keyboard or mouse.

What p5.js will do 

The sketch will provide almost all visuals of the game. However, we thought it would be interesting to include red, green, and blue LED’s on our board, whose brgithness would change according to how close the user was to the values of the target color. Hence, the sketch would also coommunicate back to Arduino, adjusting the brightness of the LED’s.

Week 11: Serial Communication Exercises

Exercise 1

Extending the potentiometer example demoed in class, we created a smiley ball that bounces horizontally across the screen whose velocity was dependent on the value of the potentiometer. Inside our Arduino code, we write the value of the potentiometer as serial output to the p5.js sketch: 

void loop() {
// put your main code here, to run repeatedly:
 int potentiomenter = analogRead(A0); 
 int mappedPot = map(potentiomenter, 0, 1023, 0, 255); 
 Serial.write(mappedPot); 
delay(100);

The corresponding breadboard is shown below: 

[image]

Inside our p5.js sketch, we created our smiley ball by making a simple Circle class with an x and y position and a move() method. The move method() doesn’t change the ball’s y position, as it remains constant along the horizontal axis height/2, but it does update the x position, which is calculated using the variable inData – the value of the potentiometer read from Arduino. A snipped of the code is shown below: 

class Circle {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
move() {
if (this.x<=50) {x = 1;}
else if (this.x>=550) {x =-1;}


let y;


if (inData==undefined) {
y=0;
} else {
y = inData;
}


this.x+=(y/20)*x;
}

Finally, a demo of the final product is shown below: 

Exercise 2

We thought that the first exercise was rather bland in terms of visuals, so for this exercise we wanted our p5.js sketch to be more interesting. We made the connection of a sun rising and falling to the brightness of an LED, hence our sketch shows a sun against a mountain landscape that can “rise” and “fall” by pushing the up and down arrow keys. As the sun “rises”, the sky changes to a bright magenta color, and when the sun “falls”, the sky deepens to a dark blue color. We achieved this by having the sun’s y position (also built from the Circle class of Exercise 1) determined the rgb values of the background, as demonstrated in the following code: 

function draw() {
background(300-c.y, 100-c.y, 100+c.y);
c.display();
c.move();


var rightBrightness = map(c.y, height, 0, 0, 255);

outData = rightBrightness; // setup the serial output
serial.write(outData); // write to serial for Arduino to pickup

This seamless gradient effect then corresponds to the brightness of an LED on our Arduino board. 

[image]

The sun’s y position is stored as outData, which is then sent to Arduino. Inside our Arduino code, we map the incoming value to a scale of 0 to 255 and use that value to analog write the LED brightness. A snippet of this code is shown below: 

void loop() {
if (Serial.available() > 0) { // see if there's incoming serial data
incomingByte = Serial.read(); // read it
led2brightness = map(incomingByte, 0, 255, 0, 255); // map the input value to the brightness for second LED
} else { }
analogWrite(ledPin, led2brightness); // write the brightness to the led pin 2


}

Finally, a demo of the finished product is shown below:

Exercise 3 

For the final exercise, we had to use bidirectional serial communication, which basically involved controlling a p5js sketch with an Arduino and tweaking the p5js sketch to affect the arduino. On the arduino, we used a distance sensor to affect the change of wind in p5js to make the ball move. Depending on the ball’s velocity, we programmed the p5js sketch so that an LED will turn on each time the ball bounces.

if (pos.y > height-mass/2) {
    velocity.y *= -0.9;  // A little dampening when hitting the bottom
    pos.y = height-mass/2;
    bounces+=1; 
    outData = "H"; 
    serial.write(outData); 
    console.log(outData); 
  }

The corresponding breadboard is shown below: 

[image]

Every time the ball strikes the platform, which occurs with the increment of the bounce counter, the program inside the p5js sketch sends “H” signals to the arduino. The Arduino then receives the H signals and activates the LED. The duration and distance variables in our Arduino code return the sound wave travel time in microseconds and the distance is the duration divided by two, respectively. The distance is next transmitted to the p5js sketch, which receives it as “indata” in p5js. A portion of the code is displayed below: 

void loop() {
  if (Serial.available() > 0) { // see if there's incoming serial data
    incomingByte = Serial.read(); // read it
    //Serial.println(incomingByte); 
    if (incomingByte == 'H') {    // if it's a capital H (ASCII 72),
      digitalWrite(LED, HIGH); // turn on the LED
      delay(100); 
      digitalWrite(LED, LOW); 
    }
  }
  //Clears the trigPin condition
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  // Sets the trigPin HIGH (ACTIVE) for 10 microseconds
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration = pulseIn(echoPin, HIGH);
  // Calculating the distance
  distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  // Displays the distance on the Serial Monitor
  Serial.write(distance); 
  //delay(1); 
}

Finally, a demo of the final product is shown below: 

(The images and videos of this post were lost with one of our colleagues phone that got damaged in water and never recovered.)

Pauline, Fuad, and Phoebe

 

Week 11: In-class Exercises

EXERCISE 1

Extending the potentiometer example demoed in class, we created a smiley ball that bounces horizontally across the screen whose velocity was dependent on the value of the potentiometer. Inside our Arduino code, we write the value of the potentiometer as serial output to the p5.js sketch:

void loop() {
// put your main code here, to run repeatedly:
int potentiomenter = analogRead(A0);
int mappedPot = map(potentiomenter, 0, 1023, 0, 255);
Serial.write(mappedPot);
delay(100);

The corresponding breadboard is shown below:

[image]

Inside our p5.js sketch, we created our smiley ball by making a simple Circle class with an x and y position and a move() method. The move method() doesn’t change the ball’s y position, as it remains constant along the horizontal axis height/2, but it does update the x position, which is calculated using the variable inData – the value of the potentiometer read from Arduino. A snipped of the code is shown below:

class Circle {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
move() {
if (this.x<=50) {x = 1;}
else if (this.x>=550) {x =-1;}


let y;


if (inData==undefined) {
y=0;
} else {
y = inData;
}


this.x+=(y/20)*x;
}

Finally, a demo of the final product is shown below:

 

EXCERCISE 2

We thought that the first exercise was rather bland in terms of visuals, so for this exercise we wanted our p5.js sketch to be more interesting. We made the connection of a sun rising and falling to the brightness of an LED, hence our sketch shows a sun against a mountain landscape that can “rise” and “fall” by pushing the up and down arrow keys. As the sun “rises”, the sky changes to a bright magenta color, and when the sun “falls”, the sky deepens to a dark blue color. We achieved this by having the sun’s y position (also built from the Circle class of Exercise 1) determine the rgb values of the background, as demonstrated in the following code:

function draw() {
background(300-c.y, 100-c.y, 100+c.y);
c.display();
c.move();


var rightBrightness = map(c.y, height, 0, 0, 255);

 

outData = rightBrightness; // setup the serial output
serial.write(outData); // write to serial for Arduino to pickup

This seamless gradient effect then corresponds to the brightness of an LED on our Arduino board.

[image]

The sun’s y position is stored as outData, which is then sent to Arduino. Inside our Arduino code, we map the incoming value to a scale of 0 to 255 and use that value to analog write the LED brightness. A snippet of this code is shown below:

void loop() {
if (Serial.available() > 0) { // see if there's incoming serial data
incomingByte = Serial.read(); // read it
led2brightness = map(incomingByte, 0, 255, 0, 255); // map the input value to the brightness for second LED
} else { }
analogWrite(ledPin, led2brightness); // write the brightness to the led pin 2


}

Finally, a demo of the finished product is shown below:

 

EXERCISE 3

For the final exercise we were required to incorporate bidirectional serial communication, basically using the arduino to control a p5js sketch, and using changes in the p5js sketch to produce changes to the arduino. On the arduino, we used a distance sensor to affect the change of wind in p5js to make the ball move. On the p5js sketch we programmed it in such a way that everytime the ball bounces an LED lights up depending on the velocity of the ball.

Final Project – Preliminary Idea

Concept

For the final project, I am still in the process of finalizing an option, but three of my initial ideas are the following: 

  1. Working on my midterm project
  2. Building a physical car that can be controlled using Arduino 
  3. A runner game where the character/ object needs to avoid obstacles 

Each of the ideas consists of two primary sections: p5.js and Arduino.

p5.js Component

For the first project, I will work to further improve the ‘chasing’ mechanism of the game. For the final project, once again, the system should be able to allow user to control the character using a keyboard/ mouse (initially). If I decide to go with this idea, I am planning on making infinite levels or lengths so that the user can interact with the game as long as they desire. 

Later, the idea of serial communication will be incorporated in order to allow a physical device to play the game. 

For the second project, I will have to work from scratch to develop a system that keeps track of user input from the keyboard or mouse, and then control the motion of the actual device. If I decide to build this device, I assume the p5.js task will be less compared to the physical computing work. Either way, I intend to build a car that interacts with the p5.js and lets the user control its motion. 

 

Arduino/ Physical Computing

For the first or the final project, the purpose of physical computing will be to facilitate the user to play the game without using their keyboard or mouse – in other words, it will use electrical components and sensors to record user input either in the form of digital or analog data. This data will alter some attributes of the game components as a result the game can be played using an Arduino component. 

The second option heavily relies on physical computing. The first job is to build a functional car. Then, it should be able to communicate with p5.js for control. Additionally, there could be a second physical device that could control the car instead of using the keyboard. 

The sensors and components that I may use are potentiometers, ultrasonic sound sensors, LEDs, photosensitive resistors (LDRs) as well as servo motors, wheels and DC batteries (for the second option). 

I will go with one idea, but I am concerned about the availability of additional sensors the second option may require. The coding part should not consume a lot of time, but the physical computing part may take a while. Either way, I look forward to developing a system that communicates with p5.js and Arduino to control an object or a character in the game environment. 

Serial Communication exercise

Control ellipse using potentiometer values:

This code is controlled by potentiometer values from Arduino, and these values are set to be X coordinate, which allows the ellipse to move whenever the potentiometer is changing its direction. The only drawback was the slow response of the program and the reading of the sensors.

Code:

// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;              // for outgoing data
 
function setup() {
  createCanvas(400, 300);          // make the canvas
  // check to see if serial is available:
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}
 
function draw() {
  
   background(0);
   fill(255);
   //show the values
   text("sensor value: " + inData, 30, 50);
  
   //draw ellipse using those values
   ellipse(inData, height/2, 50,70);
 
}

// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 
// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 
// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 
// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log(inData);
}
 
// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 
// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 
function closePort() {
  serial.close();
}

 

Final Project Concept

Concept

For my final project, I would like to continue working on my jumping pony game concept by adding more features using Arduino.

The basic and simple idea and integration:

Use Arduino buttons to control the pony’s action->interaction from Arduino to p5js

When the pony jumps successfully turn the LED light on. Also, when the game ends, send an audio losing sound through the piezo buzzer->signal from p5js to Arduino.

The next step and more complex integration will be adding sensors to control the pony game. I am thinking of using a sound detection sensor, so when the user jumps, Arduino will detect the sound, and pony will jump together with the user.

The only challenge I am afraid of is the sensibility of the sensors, so it has to be really precise and there should not any delay in sending the sound signal to the game. As the game is fast-pacing, any delay will make the user experience dissatisfying.

If everything is gonna be successful, I believe this game can resemble the Xbox console concept and can be a fun cardio exercise 😆

Seven Xbox 360 Kinect Playing Tips and Tricks from the Number One #1 Kinect  Player in the World | BoZack's Blog

Week 11: Serial Communication Exercises

Exercise 1

Make something that uses only one sensor on Arduino and makes the ellipse in p5 move on the horizontal axis, in the middle of the screen, and nothing on Arduino is controlled by p5.js.

For this exercise, a tiny circle’s x-coordinates are controlled by the reading from a potentiometer. The Arduino sends data to p5.js and the data received is used to control the movement of the circle on the canvas. 

The schematics of the circuit are as below. 

Two separate files behind this exercise are:

p5.js Code:

// Global variables added
let h = 1000;          // Canvas height
let w = 1000;          // Canvas width
let ellipse_height = 50;
let ellipse_width = 50;


// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;              // for outgoing data
 

function setup() 
{
  createCanvas(w, h);          // make the canvas
  
  // check to see if serial is available:
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}
 


function draw() 
{

  background(50);
  fill(255);

  textSize(25);
  text("Raw sensor value: " + inData, 30, 50);
  
  
  // ------------------ Ellipse Values ----------------
  let max_pot_reading = 255;
  let map_error_corr = ellipse_width/2;
  
  // X-coordinate is scaled based on the readings from potentiometer
  let scaled_x_pos = map(inData, 0, max_pot_reading, map_error_corr, (w-map_error_corr));
  
  
  // Draw ellipse using the values determined above
  draw_ellipse(scaled_x_pos, (h/2 - map_error_corr), ellipse_height, ellipse_width);
  
  
  // Text Display on the right side of the canvas
  let rounded_x_pos = round(scaled_x_pos, 2);          // Data rounded upto 2 decimal places
  textSize(25);
  fill("white");
  text("Scaled X-position: " + rounded_x_pos, (w/2 + 150), 50);
  
}



// Function to draw an ellipse
// Aruguments; (x, y) coordinates && width and height
function draw_ellipse(x, y, w_, h_)
{
  fill("red");
  ellipse(x, y, w_, h_);
}




// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 
// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 
// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 
// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log(inData);
}
 
// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 
// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 
function closePort() {
  serial.close();
}

 

Arduino Code:

int potPin = A0;

void setup() 
{
  Serial.begin(9600); // initialize serial communications
}
 
void loop() 
{
  // read the input pin:
  int pot_read = analogRead(potPin); 

  // remap the pot value to fit in 1 byte:
  int mappedPot = map(pot_read, 0, 1023, 0, 255); 

  // print it out the serial port:
  Serial.write(mappedPot);    
                           
  // slight delay to stabilize the ADC:
  delay(1);                                          
  
  // Delay to send 10 times per second 
  delay(100);
}

Use this link to access the p5.js editor.

 

Exercise 2

Make something that controls the LED brightness from p5.js.

In this exercise, keyboard arrows can be used to control the movement of a ball on the screen — X-coordinates of the ball. As the ball is moved across the screen, its gradient changes (darker red means less bright LED, whereas brighter/ true red means brighter LED). In this exercise, when the ball is at the leftmost part of the canvas, the LED attached emits less light and vice versa. 

Here, the ball is located in the middle of the screen and is moderately red. As it is moved to different sides using the arrows, its color changes accordingly — in return, the LED’s brightness changes as well.

The schematics of the circuit are as below. 

Two separate files behind this exercise are:

p5.js Code:

// Global variables added
let h = 1000;          // Canvas height
let w = 1000;          // Canvas width
let ellipse_h = 50;
let ellipse_w = 50;
let ellipse_x = w/2;
let ellipse_y = h/2;
let ellipse_vx = 0;
let error_corr = (ellipse_w/2);
let ellipse_vel = 3;



// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;              // for outgoing data



 
function setup() 
{
  
  createCanvas(w, h);          // make the canvas
  
  
  
  // check to see if serial is available:
  if (!navigator.serial) 
  {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  
  // check for any ports that are available:
  serial.getPorts();
  
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  
  // open whatever port is available:
  serial.on("portavailable", openPort);
  
  // handle serial errors:
  serial.on("requesterror", portError);
  
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  
  
  
  
}
 
function draw() {

  background(50);
  displayText();

  let color_gradient = map(ellipse_x, 0, (w - error_corr), 0, 255);
  let color_ = color(color_gradient, 0, 0);
  draw_ellipse(ellipse_x, ellipse_y, ellipse_w, ellipse_h, color_);
  pos_check();
  pos_update();
  
}


function displayText()
{
  textSize(25);
  fill("white");
  text("Move the ellipse using the RIGHT and LEFT arrows of the keyboard: ", error_corr * 5, h/2 - 400);
}



// Function to draw an ellipse
// Aruguments; (x, y) coordinates && width and height
function draw_ellipse(x, y, w_, h_, color)
{
  fill(color);
  ellipse(x, y, w_, h_);
}


function pos_check()
{
  if ((ellipse_x - error_corr) <= 0)
    ellipse_x = error_corr;
  else if (ellipse_x >= (w - error_corr))
    ellipse_x = w - error_corr;
}

function pos_update()
{
    ellipse_x += ellipse_vx;
}



function keyPressed() 
{
  // If RIGHT_ARROW is pressed, the character needs to move right, so set horizontal velocity using global var
  if (keyCode === RIGHT_ARROW)                   
  {
    ellipse_vx = ellipse_vel;
    
  }
  
  // If LEFT_ARROW is pressed, the character needs to move left, so set horizontal velocity using global var
  if (keyCode === LEFT_ARROW) 
  {
    ellipse_vx = -ellipse_vel;
  }
  
  let serial_val_ = (map(ellipse_x, 0, (w - error_corr), 0, 255));
  serial.write(serial_val_);
  // print("Serial " + serial_val_);
  

  
}


// Function that helps to move the ellipse one step at a time
function keyReleased() 
{
  // After RIGHT or LEFT arrow is released, set horizontal velocity to 0
  if (keyCode === RIGHT_ARROW || keyCode === LEFT_ARROW) 
  {
    ellipse_vx = 0;
  }

}






// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 


// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 

// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 


// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}



// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log(inData);
  
  // serial.write(ellipse_x);
}



// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 


// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 


function closePort() {
  serial.close();
}

 

Arduino Code:

int ledPin = 5;
int serialRead = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}
 
void loop() 
{
  while (Serial.available() > 0) 
  {
    serialRead = Serial.read();
    analogWrite(ledPin, serialRead);
  }
}

Use this link to access the p5.js editor.

 

Exercise 3

Make it so every time the ball bounces one led lights up and then turns off, and you can control the wind from one analog sensor. 

For this exercise, a potentiometer has been used to read analog data from the Arduino board. The data collected is mapped to a scale of user-set-value (currently set to 3). So, based on this value, wind speed is determined. Additionally, whenever the ball bounces, a LED flickers. 

A few other improvizations have been implemented in addition to the wind-gravity-file given:

  1. A background image has been uploaded to replicate a visual of a ball moving across a canvas. 
  2. The user can determine if the ball remains within the canvas or not. It is facilitated by a global variable at the beginning of the program. 
  3. The user can see the statistics related to the ball on the screen. 

 

The schematics of the circuit are as below. 

Two separate files behind this exercise are:

p5.js Code:

// USER CONTROl - The user can decide if the ball can stay within the canvas or can go beyond it
// 'y' = The ball can travel beyond right/ left border
// 'n' = The ball should stay within the border
let beyondBorder = "n";
let max_wind_vel = 3;                // Maximum Wind Speed
let ledBrightness = 255;


// Misc Global Variables
let canvas_w = 800;
let canvas_h = 800;

// Global Variables for gravity wind
let velocity;
let gravity;
let position_;
let acceleration;
let wind;
let drag = 0.99;
let mass = 50;
let error_corr = 5;                  // Variable that fixes ball dimension
let scaled_wind;                     // Variable used to track wind speed and direction

// Global Variables for Serial Communication
// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 

// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let temp_inData;
let outByte = 0;              // for outgoing data

let statusOnOff = 0;          // LED Blinking Effect
let bg_image;                 // Image variable for background image


// Loading the background image
function preload()
{
  bg_img = loadImage("image/bg_dash.png");
}



function setup() 
{
  // Initial program setup
  imageMode(CORNER);
  createCanvas(canvas_w, canvas_h);
  noFill();
  position_ = createVector(width/2, 0);
  velocity = createVector(0,0);
  acceleration = createVector(0,0);
  gravity = createVector(0, 0.5*mass);
  wind = createVector(0,0);
  
  
  // check to see if serial is available:
  if (!navigator.serial) 
  {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  
  // check for any ports that are available:
  serial.getPorts();
  
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  
  // open whatever port is available:
  serial.on("portavailable", openPort);
  
  // handle serial errors:
  serial.on("requesterror", portError);
  
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}



function draw() 
{
  // background(255);
  image(bg_img, 0, 0, canvas_w, canvas_h);
  display_text();
  
  // Conditional that disables/ enables the functionality to keep the ball within the canvas
  if (beyondBorder == "n" || beyondBorder == "N" || beyondBorder == "NO" || beyondBorder == "no" || beyondBorder == "No")
    pos_check();
  
  // Initial Setup
  applyForce(wind);
  applyForce(gravity);
  velocity.add(acceleration);
  velocity.mult(drag);
  position_.add(velocity);
  acceleration.mult(0);
  fill(247, 180, 5);
  ellipse(position_.x,position_.y,mass,mass);
  
  if (position_.y > height - mass/2) 
  {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position_.y = height - mass/2;
    
  }
  
  
  // LED SETUP
  if (position_.y > height - mass/2 - mass) 
  {
    if (velocity.y > 1) 
    {
      serial.write(ledBrightness);
    }
  }
  
  
  // Wind Setup
  let max_pot_reading = 255;
  scaled_wind = map(inData, 0, max_pot_reading, -max_wind_vel, max_wind_vel);
  
  print("X Pos: ", position_.x);
  // print("Pot Reading: ", inData);
  // print("Scaled: ", scaled_data);
  
  wind.x = scaled_wind;
  // wind.y = scaled_data;
  
}




// Replicates the ball's movement
function applyForce(force)
{
  // Newton's 2nd law: F = M * A
  // or A = F / M
  let f = p5.Vector.div(force, mass);
  acceleration.add(f);
}



// Keyboard Control
function keyPressed()
{
  if (keyCode==LEFT_ARROW){
    wind.x=-1;
  }
  if (keyCode==RIGHT_ARROW){
    wind.x=1;
  }
  if (key==' '){
    mass=random(15,80);
    position_.y=-mass;
    velocity.mult(0);
  }
}



// Function that checks if the ball is within the canvas or not
// It restricts the ball from exiting the canvas
// Called in draw() function
function pos_check()
{
  // error_corr helps to keep ball within the frame. 
  // Without it, some portion of the ball disappears into the walls.
  // Here, 2.5 is a special value that avoids glitchy effect when wind direction is changed
  if ((position_.x - mass/2) < 2.5)
    position_.x = mass/2 + error_corr;
  
  else if (position_.x >= (canvas_w - mass/2) - 2.5)
    position_.x = canvas_w - mass/2 - error_corr;
}



// Function that displays information on the canvas
function display_text()
{
  textSize(17);
  fill("white");
  
  let x_ = round(position_.x, 2) - mass/2 + 1;
  let y_ = canvas_h - (round(position_.y, 2) - mass/2 + mass);
  
  text("X-position = " + round(x_), (canvas_w - 200), 50);
  text("Y-position = " + round(y_), (canvas_w - 200), 70);
  text("Wind-Speed = " + round(scaled_wind, 3), (canvas_w - 200), 90);
  
}




// if there's no port selected, 
// make a port select button appear:
function makePortButton() 
{
  // create and position_ a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 


// make the port selector window appear:
function choosePort() 
{
  if (portButton) portButton.show();
  serial.requestPort();
}
 

// open the selected port, and make the port 
// button invisible:
function openPort() 
{
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 


// pop up an alert if there's a port error:
function portError(err) 
{
  alert("Serial port error: " + err);
}



// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() 
{
  inData = Number(serial.read());
  console.log(inData);
  
  serial.write(statusOnOff);          // Blinking Effect
}



// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() 
{
  console.log("port connected");
  serial.getPorts();
}
 


// if a port is disconnected:
function portDisconnect() 
{
  serial.close();
  console.log("port disconnected");
}
 


function closePort() 
{
  serial.close();

 

Arduino Code:

// Global variables
int ledPin = 5;
int potPin = A0;
int serialRead = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);          // Set the LED pin
}
 
void loop() 
{
  // Reading input from the potentiometer
  while (Serial.available() > 0) 
  {
    serialRead = Serial.read();
    analogWrite(ledPin, serialRead);
  }

  // read the input pin:
  int pot_read = analogRead(potPin); 

  // remap the pot value to fit in 1 byte:
  int mappedPot = map(pot_read, 0, 1023, 0, 255); 

  // print it out the serial port:
  Serial.write(mappedPot);    

  // slight delay to stabilize the ADC:
  delay(1);                                          
  
  // Delay to send 10 times per second 
  delay(100);
}

Use this link to access the p5.js editor.

A video demonstration of this exercise can be found here:

 

View the entire project on GitHub.