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);
}

 

Aisha & Marija – Musical Instrument Assignment

For this week’s assignment, we were struggling to come up with an idea so we scouted the internet for inspiration. We came across this video https://www.youtube.com/watch?v=J8XNTHETgxU and thought it was a perfect and cool idea! The concept is essentially playing notes by moving your hands at a certain distance away from the ultrasonic sensor. The notes we used were C,D,E,F,G,A,B. The materials we needed were one breadboard, 9 wires, one ultrasonic sensor, one switch, one 10ohms resistor, and one piezo buzzer.  We also placed down pieces of paper with the notes on it to help us navigate the keys.

This is an attempt at drawing the schematic:

This is what the breadboard looked like:

This is the final outcome where we played mary had a little lamb:

This is the code we are most proud of:

// plays note at a certain distance

if (distance < 0 || distance > 50  || buttonState == LOW) { //if not presed and not in front

   noTone(9); //dont play music

 }

 else if (( buttonState == HIGH)) {  //if pressed

   int sound = map(distance, 0, 50, 0, 6);  //map distance to the array of notes
   tone(9, notes[sound]);  //call a note depending on distance

 }

Future improvements:

  • Not sure if the schematic is correct
  • When we would reach A or B the sound would sometimes become fuzzy or cut out so it would be nice if we could fix that.
  • It would be better if we designed our own keyboard.

Distance musical instrument, Tarek Nabeh & Aadil Zakarya

Concept:

We wanted to create a musical instrument that works without touching anything (of course but the switch button). So we used the ultrasonic sensor to control the frequency of the sound outputted by the buzzer. While coding we found it very annoying for the buzzer to run all of the time, without any option to stop it. Hence, we found it critical to add a button that controls whether the ultrasonic sensor and the buzzer are running. We also wanted to make it fancy in a way that you don’t hold the button for it to work but just press on it to start and press again to stop it, and that’s what we did. The ultrasonic measures the distance by the duration of time of the ultrasonic wave takes to come back, and by that controls the frequency of the buzzer. We also created a feedback loop from the serial by which we can know the distance and the frequency outputted.

Code:

The pins were declared:

int buttonPin = 2;
int echo = 6;
int trig = 5;
int speaker = 9;
long duration;
long distance;
bool isPressed = false;

then the ultrasonic trig and echo were settled up to the pins and the serial was set up as well:

void setup() {
  // setup pin modes
  Serial.begin(9600);
  pinMode(speaker, OUTPUT);
  pinMode(trig, OUTPUT); 
  pinMode(echo, INPUT);
  // pinMode(buttonPin, INPUT);
  // pinMode(buttonPin, INPUT_PULLUP);
}

the void loop basically has everything to do with the switch. a global variable was declared so that we save the last state of the ultrasonic and the buzzer, and when the button is pressed it switches the state. so when if the sensor is not running it makes it start running and vice versa. Moreover, I noticed that a delay had to be added because the void loop runs numerous times while the button is being pressed so it basically turns the sensor on and off numerous times giving us a 50% possibility of tuning the sensors on when the button is released. Hence, the delay is crucial for the project to work.

void loop() {
  digitalWrite(trig, LOW);
  delayMicroseconds(2);
  int buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    Serial.println("button pressed");
    // delay(5000);
    if(isPressed == false){
      isPressed = true;
      delay(1000);
      
    }else if(isPressed == true){
      Serial.print("came pressed ");
      noTone(speaker);
      isPressed = false;
      Serial.println(isPressed);
      delay(1000);
    }    
    // blink(2);
  }
  // Serial.println(isPressed);
  if(isPressed == true){
    updateSensor();
  }
}

Finally, the function that makes the sensor work, as in Tarek’s previous project the sensor calculates the distance by measuring the time it takes for the waves to go out and come back to the sensor again. based on that it scales the distance from 0 to 100 cm to the frequencies from 0 to 455. then it makes the buzzer start the sound in that specific frequency.

void updateSensor(){
  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);
  duration = pulseIn(echo, HIGH);
  distance = duration * 0.034 / 2; 
  int frequency = ((int)(distance * 4.55) );
  frequency = (frequency > 455)? 455 : frequency;
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.print(" cm, ");
  Serial.print("frequency: ");
  Serial.println(frequency);
  tone(speaker,frequency);
  delay(200);
}

Reflections:

While working on the project we noticed the potential that we have with just this Arduino and minor sensors like the ultrasonic and small gadgets like the buzzer. It also spiked our curiosity to find out what else can be done with such gadgets.

 

Week 10: Air Piano

This is a fun instrument that lets you play simple tunes by waving your hands in the air!!

The basic idea is to be able to generate different frequencies of sound based on the position of your hand, which is sensed using an ultra-sonic sensor.

For this to work, the sensor has to be an open space such that there are no obstacles in front of it for at least a meter. Place your palm or any other obstacle in front of the sensor to obtain varying frequencies.

Schematics using Fritzing:

Here’s the code that makes it all work:

#define echoPin 6 
#define trigPin 5 
long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement
int tones;  
int tim=200;// duration for which one sound pulse will last
int buzzerPin = 8;
int space =500;

void setup() {
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT
  pinMode(buzzerPin, OUTPUT);
  }
  
void loop() {
  
  //Finding distance of obstacle using ultrasonic sensor
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  duration = pulseIn(echoPin, HIGH); // Reads the echoPin, returns the sound wave travel time in microseconds
  distance = duration * 0.034 / 2; // Distance = time*speed/2
  
  //Emitting sound as per the distance
  
  tones =20+distance*5; //calculating the frequency that has to be emitted

   tone(buzzerPin, tones, tim); //emits sound at the specified frequency and for the specified duration
    
   delay (space); //delay between each beat
  
}

Improvements:

In the given code the sounds are emitted at a fixed time interval for a fixed duration. We can play around with this by changing the value of the variables ‘tim’ and ‘space’. Also, we can play around with the way the frequency is calculated from the distance.

Week 9: Digital Input-Output

Ramp Lights!

This week we learned how to control and manipulate digital and analog input/outputs on Arduino IDE. It was interesting how a few lines of code with basic instructions could control a physical output like an LED. For the assignment, I created a ramp light sequence (imagine lights turning on one by one on a ramp, tried to recreate that). The lights turn on sequentially with the rotation of a potentiometer. Here’s a video of how that turned out:

How it works:

The working of this is very simple: the potentiometer has values calibrated with its rotation, so I took those values as input and using if and else if conditions, I changed the state of the LEDs to HIGH or LOW.

The Schematics:

Here’s a diagram of the schematics made using Fritzing:

Arduino IDE code:

int potPin = A5;
int ledPin1 = 2;
int ledPin2 = 3;
int ledPin3 = 4;
int ledPin4 = 5;

void setup() {

  pinMode(potPin, INPUT);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  Serial.begin(9600);

}

void loop() {
  int potValue = analogRead(A5);
  Serial.println(potValue);

  if (potValue < 100) {
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
  }
  
  else if (potValue < 256) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);

  }

  

  else if (potValue < 512) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, LOW);
    digitalWrite(ledPin4, LOW);
  }

  else if (potValue < 768) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, HIGH);
    digitalWrite(ledPin4, LOW);
  }

  else if (potValue < 1024) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, HIGH);
    digitalWrite(ledPin4, HIGH);
  }


}

It was fun creating the ramp lights and seeing the lights turn on/off with the turn of my potentiometer!

 

 

Distance musical instrument, Tarek Nabih & Aadil Zakareya

Concept:

We wanted to create a musical instrument that works without touching anything (of course but the switch button). So we used the ultrasonic sensor to control the frequency of the sound outputted by the buzzer. While coding we found it very annoying for the buzzer to run all of the time, without any option to stop it. Hence, we found it critical to add a button that controls whether the ultrasonic sensor and the buzzer are running. We also wanted to make it fancy in a way that you don’t hold the button for it to work but just press on it to start and press again to stop it, and that’s what we did. The ultrasonic measures the distance by the duration of time of the ultrasonic wave takes to come back, and by that controls the frequency of the buzzer. We also created a feedback loop from the serial by which we can know the distance and the frequency outputted.

Code:

The pins were declared:

int buttonPin = 2;

int echo = 6;
int trig = 5;

int speaker = 9;

long duration;
long distance;

bool isPressed = false;

then the ultrasonic trig and echo were settled up to the pins and the serial was set up as well:

void setup() {
  // setup pin modes
  Serial.begin(9600);

  pinMode(speaker, OUTPUT);
  pinMode(trig, OUTPUT); 
  pinMode(echo, INPUT);
  // pinMode(buttonPin, INPUT);
  // pinMode(buttonPin, INPUT_PULLUP);

}

the void loop basically has everything to do with the switch. a global variable was declared so that we save the last state of the ultrasonic and the buzzer, and when the button is pressed it switches the state. so when if the sensor is not running it makes it start running and vice versa. Moreover, I noticed that a delay had to be added because the void loop runs numerous times while the button is being pressed so it basically turns the sensor on and off numerous times giving us a 50% possibility of tuning the sensors on when the button is released. Hence, the delay is crucial for the project to work.

oid loop() {
  digitalWrite(trig, LOW);
  delayMicroseconds(2);

  int buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    Serial.println("button pressed");
    // delay(5000);
    if(isPressed == false){
      isPressed = true;
      delay(1000);
      
    }else if(isPressed == true){
      Serial.print("came pressed ");
      noTone(speaker);
      isPressed = false;
      Serial.println(isPressed);
      delay(1000);
    }    
    // blink(2);
  }
  // Serial.println(isPressed);
  if(isPressed == true){
    updateSensor();
  }


}

Finally, the function that makes the sensor work, as in Tarek’s previous project the sensor calculates the distance by measuring the time it takes for the waves to go out and come back to the sensor again. based on that it scales the distance from 0 to 100 cm to the frequencies from 0 to 455. then it makes the buzzer start the sound in that specific frequency.

void updateSensor(){
  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);
  duration = pulseIn(echo, HIGH);
  distance = duration * 0.034 / 2; 

  int frequency = ((int)(distance * 4.55) );
  frequency = (frequency > 455)? 455 : frequency;

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.print(" cm, ");
  Serial.print("frequency: ");
  Serial.println(frequency);

  tone(speaker,frequency);

  delay(200);

}

 

Reflections:

While working on the project we noticed the potential that we have with just this Arduino and minor sensors like the ultrasonic and small gadgets like the buzzer. It also spiked our curiosity to find out what else can be done with such gadgets.

Rusty Trombone

The Muses of Ancient Greece are dead and gone, and I’ve discovered why.

The concept is simple: buttons for tones, distance sensor for modulation.

Schematic:

Code:

/* Name: Jan
   Project: Rusty Trombone

*/

// keeping track of button input pins
int buttonPins[] = {A1, A2, A4, A5};
int buttonRed = A1;
int buttonGreen = A2;
int buttonYellow = A4;
int buttonBlue = A5;

// distance sensor pins
int pinTrigger = 9;
int pinEcho = 8;

// buzzer pins
int buzzer = 12;

// keeping track of base tones {C, E, G, A}
float baseNotes[] = {262, 330, 392, 440};

//difference between semitones, for use with modulation
float semitone = 15;

float modulation = 0;
float inches;
int fret = 1;

void setup() {
  
  Serial.begin(9600);

  //initializing pins
  pinMode(buzzer, OUTPUT);
  
  pinMode(pinTrigger, OUTPUT);
  pinMode(pinEcho, INPUT);

  //setting the buttons to INPUT_PULLUP means i don't have to worry about pull down resistors clogging up my board
  pinMode(buttonRed, INPUT_PULLUP);
  pinMode(buttonGreen, INPUT_PULLUP);
  pinMode(buttonYellow, INPUT_PULLUP);
  pinMode(buttonBlue, INPUT_PULLUP);

}

void loop() {
  fret = 0;
  bool buttonPressed = 0;
  inches = getDistance();
  for (int i = 0; i < 20; i+=2) {
    fret++;
    if (inches < i) {
      modulation = semitone * fret;
      break;
    }
  }

  if (digitalRead(buttonRed) == LOW) {      //if the first key is pressed
    tone(buzzer, 262 + modulation); 
    Serial.println(inches);                 
  }
  else if (digitalRead(buttonGreen) == LOW) { //if the second key is pressed
    tone(buzzer, 330 + modulation);                   
  }
  else if (digitalRead(buttonYellow) == LOW) { //if the third key is pressed
    tone(buzzer, 392 + modulation);                    
  }
  else if (digitalRead(buttonBlue) == LOW) {
    tone(buzzer, 440 + modulation);
  }
  
  else {
    noTone(buzzer);                        //if no key is pressed turn the buzzer off
  }
}


float getDistance()
{
  float echoTime;                   //variable to store the time it takes for a ping to bounce off an object
  float calculatedDistance;         //variable to store the distance calculated from the echo time

  //send out an ultrasonic pulse that's 10ms long
  digitalWrite(pinTrigger, HIGH);
  delayMicroseconds(1);
  digitalWrite(pinTrigger, LOW);

  echoTime = pulseIn(pinEcho, HIGH);      //use the pulsein command to see how long it takes for the
                                          //pulse to bounce back to the sensor

  calculatedDistance = echoTime / 148.0;  //calculate the distance of the object that reflected the pulse (half the bounce time multiplied by the speed of sound)


  return calculatedDistance;              //send back the distance that was calculated
}

Evidence of My Crimes

Reflections: Code as is requires a lot of fine-tuning for the buzzer not to sound like it’s gently caressing a combine harvester. More specifically, changing the changes in tone from continuous to discrete kept the buzzing down. Tweaking the distance intervals at which the tone goes up or down a semitone keeps it from oscillating too much, as might changing how often the tone sounds. The equation for calculating distance from the sensor also has a lot of room for tuning, but I have not the hubris to claim to be a master of time nor space. Fun project though, had a blast.

Unusual Instrument – Daniel & Sara

Concept

For this week’s assignment, [Daniel] and I constructed a musical instrument that measures the distance between the ultrasonic sensor and anything placed in front of it (i.e., your hand). Depending on how far you place your hand/ an object from the sensor, the piezo buzzer will play one of 8 different notes we inputted into the code.

To do this, we used a force sensing resistor that you had to press on whenever you wanted the ultrasonic sensor to detect your hand, and a piezo buzzer that would play the sound as output.

Arduino: How to Make an Instrument

Schematic and Images

Here is the schematic for the circuit:

Code (or code highlight?)

int trig = 10; // digital 10
int echo = 11; // digital 11
long duration;
long distance;
int force;
 
 
void setup() {
 // runs once
 pinMode(echo, INPUT); // digital 11 
 pinMode(trig, OUTPUT); // digital 10
 Serial.begin(9600); // open serial monitor to track
}
 
void loop() {
 // runs repeatedly
 digitalWrite(trig, LOW);
 delayMicroseconds(2);
 digitalWrite(trig, HIGH);
 delayMicroseconds(10);
 digitalWrite(trig, LOW);
 duration = pulseIn(echo, HIGH);
 distance = (duration / 2 * 0.0344);
 
 int notes[7] = {233, 261, 293, 311, 349, 392, 440};
 //              Bb    C   D     Eb    F   G   A    
 
 force = analogRead(A0); // analog 0
 
 if (distance < 0 || distance > 50 || force < 100){
   noTone(12);
 
 } else if (force > 100){
   int sound = map (distance, 0, 50, 0, 7);
   tone(12, notes[sound]);
  
 }
}

The notes were based on the key of Bb, as we intended to make Bohemian Rhapsody first, the notes and values are:

Bb: 233

C: 261

D: 293

Eb: 311

F: 349

G: 392

A: 440

Video:

Here I’m playing Twinkle Twinkle Little Star using the instrument.

Reflection and improvements:

  • The ultrasonic sensor was very sensitive to movement very close to it, but the farthest note (A), was very hard to detect just because it was so far away from the sensor. This made the sound laggy when it came out of the piezo buzzer, which made the song sound very untuned and the note inconsistent.
  • It may also be because of the quality of the sensor, but the range seemed too limited and not so accurate. If we tried to make it a shorter distance between frequencies, then it would have a lot of sliding, and if we wanted to keep the same distance apart we would have needed to move it further and cause a much more unstable sound