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.

Connecting Arduino to p5.js: 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.

Possible Final Project Concept(s)

Concept

I came up with two potential concepts so far:

  1. A flappy bird-like game where a character goes up and down according to either:
  • Value of photoresistor (how dark the area around it is determines whether the character goes up or down)
  • Value of potentiometer (how the number you get from turning the dial determines whether the character goes up or down)
  • Volume of voice
  • Force Sensing Resistor

to avoid the protruding obstacles.

 

  1. A radio that changes FM and AM values according to what number you turn the potentiometer dial to (I will input song files that play according to their designated potentiometer value).
  • Each value will take you to a different era of music

Components

 Flappy Bird:

  • p5, Arduino UNO, and breadboard
  • Photoresistor OR potentiometer OR Force Sensing Resistor
  • Wires
  • LED?

Radio:

  • p5, Arduino UNO, and breadboard
  • Potentiometer(s) – possibly one for AM and one for FM?
  • Wires

 

The circuits for both are relatively simple, I will use the serial monitor values to input into p5.

p5 Design Elements

Flappy Bird:

  • Character
  • Protruding obstacles
  • Background
  • Play and Game Over screens?

Radio:

  • Vintage radio screen
  • Dials design (AM/ FM dials on the screen move according to which potentiometer you move?)
  • Serial monitor value displayed (with digital clock font) that change accordingly

Last week’s class exercise

Potentiometer and p5 Video

Daniel, Aayat, and I made it so that whenever you turn the potentiometer dial, the circle would either go up or down according to the serial value.

We did this by replacing the y coordinate of the ellipse to inData in p5 (which displays the serial value in numerical terms).

ref. to function draw () {}

p5 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);
   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: Concept

Inspiration: Etch a Sketch

Source: The New York Times Magazine

Etch a Sketch is a simple drawing tool. Users can rotate two knobs to alter x and y positions of a drawing dot, creating a continuous drawing. To erase the drawn image, users have to shake the board.

Brainstorming

Creating an Arduino version of Etch and Sketch does not seem to be too difficult, so I am thinking of a few things that make the Arduino version somewhat unique. However, there are a few guidelines I would like to maintain:

      1. Since it is a drawing tool, the control of the dot has to be precise.
      2. Related to (1), the final product should be intuitive to use.

So, the Arduino version of Etch and Sketch will likely have potentiometers to act as knobs and a button to erase the drawing. The below is the list of additional features that may or may not be implemented in my project:

      1. 3D. Introducing another drawing axis, people can create a 3D drawing. However, this may be a challenging task, and other features may not be implemented if I focus on creating this.
      2. Partial erasing. Similar to how texts are erased on smartphones, people will be able to undo their drawing, and erasing will become faster if keep erasing.
      3. Color/stroke thickness.
      4. Pause/Resume drawing. Similar to lifting a pen to draw things on another side, the drawing of the dot may pause and resume at any time. Maybe this can be implemented with the color feature using RGBA.

 

Hopefully, unlike my midterm project, the final product of this project will be not too complex.

Piano by Hessa&Zhaniya

For this week’s assignment, we wanted to design a mini piano using switch buttons. Our goal was to make each button have a different note so that when users play the piano they can recreate the sound from the “Happy Birthday” song. We also added a potentiometer to our circuit so that we could regulate the volume of the sound.

Image of notes:

To build the circuit we added switch buttons and resistors and connected the wires to the pins. We downloaded pitches from examples→toneKeyboard, each switch button had its own musical note from the song(C5, B4, A4, G4, D5). We struggled a bit with adding potentiometer and piezo buzzer to our circuit with piano buttons, however we managed to solve the problems and understand the process.

At the beginning we initialized the piano buttons, buzzer and potentiometer. Next, we went on to add the code for the buzzer and potentiometer. We wrote code to read the input on the analog pin and turn the piezo buzzer on and off. Also, we set a tone for each button and activated it. To improve this work, next time we can add more buttons and play new songs:)

Final code:
Link to GitHub

Final result:
IMG_6078

Analog input & output

Concept:

Analog input and output is a fundamental concept in electronics and microcontrollers, such as the Arduino. In this project, we will be using analog input and output to control a pair of LEDs, one red and one blue, which will mimic the flashing lights of a police car. This will be achieved by using a sensor, such as a motion sensor, to detect when a hand is waved in front of the LEDs. The reason for choosing this particular project is to gain a better understanding of analog input and output, as well as to create a fun and interactive project using the Arduino. By using a sensor to control the flashing of the LEDs, we can create a sense of realism and add an extra layer of interactivity to the project.

Reflections and improvements:

As someone who enjoys tinkering with gadgets and technology, working on an analog input and output project was a lot of fun for me. I was excited to see how the switch would work and how the output would be displayed on the screen. Unfortunately, the video of my project got corrupted and I was unable to show the switch in action (when pressed while the motion sensor was used the LED would flash).

Despite this setback, I was still happy with how the project turned out. The analog input and output functioned exactly as I had expected, and I was able to learn a lot about how these types of systems work. I enjoyed the process of building and testing the project, and it was satisfying to see it all come together in the end.

Looking back, there are a few things I would do differently if I had the chance to work on this project again. For one, I would make sure to back up the video of the project before showing it to others. This way, if something goes wrong with the video, I would still have a copy of it to refer to.

Assignment 7: Music Machine by Nouf and Vivianna

Our idea for the musical instrument was to use sounds that the servo motor created rather than incorporating the piezo buzzer. So, we decided to try and recreate a gumball machine and the sound it makes when the gumballs fall out of the machine.

The Physical Design Process

We built the structure using a box, water bottle and cardboard. We then prepared the servo motors and prepared a cardboard piece to act as a ball stopper to control the amount of balls that fall through the hole.

Box and water bottle for the structure
Servo motor ball stopper

 

 

 

 

 

We attached the servo motor and started testing out the servo motor with balls made from different materials to see what sound they made. We also decided to create two ramps for the balls to slide on to make more sound. As for the materials, we used pipe-cleaners, paper, and marbles. We found that the marbles made the best sound and rolled nicely down the ramp. The paper balls made a decent sound, whereas the pipe-cleaners were almost silent when they fell.

 

We attached the servo motor and started testing out the servo motor with balls made from different materials to see what sound they made. We also decided to create two ramps for the balls to slide on to make more sound. As for the materials, we used pipe-cleaners, paper, and marbles. We found that the marbles made the best sound and rolled nicely down the ramp. The paper balls made a decent sound, whereas the pipe-cleaners were almost silent when they fell.

Here is the final version of the physical components:

And here is the schematic of our circuit:

Some things that didn’t work out as expected…

Our initial plan was to have the balls fall down one by one, but that didn’t work as expected. The balls either fell together when the ball stopper opened, or they would get stuck even when the stopper opened. When they do fall down, most of the time they wouldn’t roll down the ramps as intended. Usually, the balls would fall to the side, fall straight down, or when it does fall on the slide it wouldn’t roll all the way down and it would get stuck on the ramp. In this case, the marbles worked best since they were smooth spheres, but the marbles also had the problem with falling to the side. Additionally, at first we wanted the stopper to open and close continuously after one button press, but currently it just does the cycle once and you have to keep pressing the button for the continuous opening and closing.

The Coding Process

For implementing the sensors, we decided to use a push button and potentiometer. We used the button to control the servo motor, which either blocks or unblocks the hole where the balls fall out of. When it is open the balls fall out, and when it is closed the balls are stopped. To do this we used 2 if statements that checked if the button is clicked once, in which case the lever is opened, and if the button is clicked again then the lever is closed again.

 if(clicks%2 == 0)
 {
   pos = 0;
 }
 else{   
   if (pos <= 70)
   {
     pos += servoSpeed;
   }
}
 
myServo.write(pos);
delay(100);

Another variation of code was to have one press do both the opening and closing of the ball stopper. This was the code we used:

if (buttonState == 1){
   for (pos = 0; pos <= 160; pos += servoSpeed) {
      myServo.write(pos);             
      delay(15);                       
   }
   for (pos = 70; pos >= 0; pos -= servoSpeed) {
      myServo.write(pos);              
      delay(100);               
   }
  } else if (buttonState == 0) {
    myServo.write(70);
    delay(15);
  }

We experimented with the delay, and the number for the position of the servo motor. If we made the delay longer, the servo motor would take longer to reach the position. Moreover, since the potentiometer controls the speed of the servo motor, when the potentiometer is at the maximum number, the servo motor moves faster, but since the delay is longer it doesn’t seem as fast. When the delays were all set to 15 and the potentiometer was at the maximum, the servo motor didn’t sweep the full amount (i.e. it didn’t complete the number of degrees in the for loop). While this was experimental, we ended up liking the sound it created and the movements of it.

Finally, here are a few different videos of our music machine!

This one is the first code, where one press opens and the other press closes it:

 

This one all the delay amounts are the same. We didn’t experiment with the potentiometer for this version, but the sound was nice:

 

This one has one long delay:

 

And this final one we tested with just marbles and shortened the long delay, which was in the previous video: