Final Project – Rhythm Kukreja (Pulse/Heart Beat Monitor with LEDs)

Description

Create a physically interactive system of your choice that relies on a multimedia computer for some sort of processing or data analysis. The Final should use BOTH Processing AND Arduino. Your focus should be on careful and timely sensing of the relevant actions of the person or people that you’re designing this for, and on clear, prompt, and effective responses. Any interactive system is going to involve systems of listening, thinking, and speaking from both parties. Whether it involves one cycle or many, the exchange should be engaging. You may work alone or in pairs.

Description of the game

So, I used a pulse sensor to make a heartbeat monitor which can be used for different purposes. Maybe in the health care industry or sports industry.

I used two LEDs that turn on and off according to the pulse rate. I added a scaling component in the code to adjust the size of the rate. I also checked the timings between heartbeat by subtracting the last-current. I also made the heart pump by increasing the stroke weight. You can also use “S” or “s” to save the heartbeat in the folder where the code. it saved and later you can compare the heartbeat.

I did the user testing as well after that. The test person came back from a run, thus his heart was beating faster.

He gave some feedback. Thank you.

 

Certain challenges:-

  1. It took me some time to figure out how the sensor worked because I have never worked with a pulse sensor before.
  2. it was hard to display the pulse wave first, then I found some resources to make me understand the concept.
  3. it was hard to add further things to the project to make it more useful.

The code is following:-

Arduino

//  Variables
int pulsePin = 0;                 // Pulse Sensor purple wire connected to analog pin 0
int blinkPin = 13;                // pin to blink led at each beat
int fadePin = 5;                  // pin to do fancy classy fading blink at each beat
int fadeRate = 0;                 // used to fade LED on with PWM on fadePin

// Volatile Variables, used in the interrupt service routine!
volatile int BPM;                   // int that holds raw Analog in 0. updated every 2mS
volatile int Signal;                // holds the incoming raw data
volatile int TBH = 600;             // int that holds the time interval between beats! Must be seeded! 
volatile boolean Pulse = false;     // "True" when User's live heartbeat is detected. "False" when not a "live beat". 
volatile boolean QS = false;        // becomes true when Arduoino finds a beat.

// Regards Serial OutPut  -- Set This Up to your needs
static boolean serialVisual = false;   // Set to 'false' by Default.  Re-set to 'true' to see Arduino Serial Monitor ASCII Visual Pulse 

void setup(){
  pinMode(blinkPin,OUTPUT);         // pin that will blink to your heartbeat!
  pinMode(fadePin,OUTPUT);          // pin that will fade to your heartbeat!
  Serial.begin(115200);             // we agree to talk fast!
  interruptSetup();                 // sets up to read Pulse Sensor signal every 2mS 
   // IF YOU ARE POWERING The Pulse Sensor AT VOLTAGE LESS THAN THE BOARD VOLTAGE, 
   // UN-COMMENT THE NEXT LINE AND APPLY THAT VOLTAGE TO THE A-REF PIN
//   analogReference(EXTERNAL);   
}

//  Where the Magic Happens
void loop(){
  
    serialOutput() ;       
    
  if (QS == true){     // A Heartbeat Was Found
                       // BPM and TBH have been Determined
                       // Quantified Self "QS" true when arduino finds a heartbeat
        fadeRate = 255;         // Makes the LED Fade Effect Happen
                                // Set 'fadeRate' Variable to 255 to fade LED with pulse
        serialOutputWhenBeatHappens();   // A Beat Happened, Output that to serial.     
        QS = false;                      // reset the Quantified Self flag for next time    
  }
     
  ledFadeToBeat();                      // Makes the LED Fade Effect Happen 
  delay(20);                             //  take a break
}

void ledFadeToBeat(){
    fadeRate -= 15;                         //  set LED fade value
    fadeRate = constrain(fadeRate,0,255);   //  keep LED fade value from going into negative numbers!
    analogWrite(fadePin,fadeRate);          //  fade LED
  }


void serialOutput(){   // Decide How To Output Serial. 
 if (serialVisual == true){  
     arduinoSerialMonitorVisual('-', Signal);   // goes to function that makes Serial Monitor Visualizer
 } else{
      sendDataToSerial('S', Signal);     // goes to sendDataToSerial function
 }        
}


//  Decides How To OutPut BPM and TBH Data
void serialOutputWhenBeatHappens(){    
 if (serialVisual == true){            //  Code to Make the Serial Monitor Visualizer Work
    Serial.print("*** Heart-Beat Happened *** ");  //ASCII Art Madness
    Serial.print("BPM: ");
    Serial.print(BPM);
    Serial.print("  ");
 } else{
        sendDataToSerial('B',BPM);   // send heart rate with a 'B' prefix
        sendDataToSerial('Q',TBH);   // send time between beats with a 'Q' prefix
 }   
}



//  Sends Data to Pulse Sensor Processing App, Native Mac App, or Third-party Serial Readers. 
void sendDataToSerial(char symbol, int data ){
    Serial.print(symbol);

    Serial.println(data);                
  }


//  Code to Make the Serial Monitor Visualizer Work
void arduinoSerialMonitorVisual(char symbol, int data ){    
  const int sensorMin = 0;      // sensor minimum, discovered through experiment
const int sensorMax = 1024;    // sensor maximum, discovered through experiment

  int sensorReading = data;
  // map the sensor range to a range of 12 options:
  int range = map(sensorReading, sensorMin, sensorMax, 0, 11);

  // do something different depending on the 
  // range value:
  switch (range) {
  case 0:     
    Serial.println("");     /////ASCII Art Madness
    break;
  case 1:   
    Serial.println("---");
    break;
  case 2:    
    Serial.println("------");
    break;
  case 3:    
    Serial.println("---------");
    break;
  case 4:   
    Serial.println("------------");
    break;
  case 5:   
    Serial.println("--------------|-");
    break;
  case 6:   
    Serial.println("--------------|---");
    break;
  case 7:   
    Serial.println("--------------|-------");
    break;
  case 8:  
    Serial.println("--------------|----------");
    break;
  case 9:    
    Serial.println("--------------|----------------");
    break;
  case 10:   
    Serial.println("--------------|-------------------");
    break;
  case 11:   
    Serial.println("--------------|-----------------------");
    break;
  
  } 
}


volatile int rate[10];                    // array to hold last ten TBH values
volatile unsigned long sampleCounter = 0;          // used to determine pulse timing
volatile unsigned long lastBeatTime = 0;           // used to find TBH
volatile int P =512;                      // used to find peak in pulse wave, seeded
volatile int T = 512;                     // used to find trough in pulse wave, seeded
volatile int thresh = 525;                // used to find instant moment of heart beat, seeded
volatile int amp = 100;                   // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true;        // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false;      // used to seed rate array so we startup with reasonable BPM


void interruptSetup(){     
  // Initializes Timer2 to throw an interrupt every 2mS.
  TCCR2A = 0x02;     // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
  TCCR2B = 0x06;     // DON'T FORCE COMPARE, 256 PRESCALER 
  OCR2A = 0X7C;      // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
  TIMSK2 = 0x02;     // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
  sei();             // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED      
} 


// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE. 
// Timer 2 makes sure that we take a reading every 2 miliseconds
ISR(TIMER2_COMPA_vect){                         // triggered when Timer2 counts to 124
  cli();                                      // disable interrupts while we do this
  Signal = analogRead(pulsePin);              // read the Pulse Sensor 
  sampleCounter += 2;                         // keep track of the time in mS with this variable
  int N = sampleCounter - lastBeatTime;       // monitor the time since the last beat to avoid noise

    //  find the peak and trough of the pulse wave
  if(Signal < thresh && N > (TBH/5)*3){       // avoid dichrotic noise by waiting 3/5 of last TBH
    if (Signal < T){                        // T is the trough
      T = Signal;                         // keep track of lowest point in pulse wave 
    }
  }

  if(Signal > thresh && Signal > P){          // thresh condition helps avoid noise
    P = Signal;                             // P is the peak
  }                                        // keep track of highest point in pulse wave

  //  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
  // signal surges up in value every time there is a pulse
  if (N > 250){                                   // avoid high frequency noise
    if ( (Signal > thresh) && (Pulse == false) && (N > (TBH/5)*3) ){        
      Pulse = true;                               // set the Pulse flag when we think there is a pulse
      digitalWrite(blinkPin,HIGH);                // turn on pin 13 LED
      TBH = sampleCounter - lastBeatTime;         // measure time between beats in mS
      lastBeatTime = sampleCounter;               // keep track of time for next pulse

      if(secondBeat){                        // if this is the second beat, if secondBeat == TRUE
        secondBeat = false;                  // clear secondBeat flag
        for(int i=0; i<=9; i++){             // seed the running total to get a realisitic BPM at startup
          rate[i] = TBH;                      
        }
      }

      if(firstBeat){                         // if it's the first time we found a beat, if firstBeat == TRUE
        firstBeat = false;                   // clear firstBeat flag
        secondBeat = true;                   // set the second beat flag
        sei();                               // enable interrupts again
        return;                              // TBH value is unreliable so discard it
      }   


      // keep a running total of the last 10 TBH values
      word runningTotal = 0;                  // clear the runningTotal variable    

      for(int i=0; i<=8; i++){                // shift data in the rate array
        rate[i] = rate[i+1];                  // and drop the oldest TBH value 
        runningTotal += rate[i];              // add up the 9 oldest TBH values
      }

      rate[9] = TBH;                          // add the latest TBH to the rate array
      runningTotal += rate[9];                // add the latest TBH to runningTotal
      runningTotal /= 10;                     // average the last 10 TBH values 
      BPM = 60000/runningTotal;               // how many beats can fit into a minute? that's BPM!
      QS = true;                              // set Quantified Self flag 
      // QS FLAG IS NOT CLEARED INSIDE THIS ISR
    }                       
  }

  if (Signal < thresh && Pulse == true){   // when the values are going down, the beat is over
    digitalWrite(blinkPin,LOW);            // turn off pin 13 LED
    Pulse = false;                         // reset the Pulse flag so we can do it again
    amp = P - T;                           // get amplitude of the pulse wave
    thresh = amp/2 + T;                    // set thresh at 50% of the amplitude
    P = thresh;                            // reset these for next time
    T = thresh;
  }

  if (N > 2500){                           // if 2.5 seconds go by without a beat
    thresh = 512;                          // set thresh default
    P = 512;                               // set P default
    T = 512;                               // set T default
    lastBeatTime = sampleCounter;          // bring the lastBeatTime up to date        
    firstBeat = true;                      // set these to avoid noise
    secondBeat = false;                    // when we get the heartbeat back
  }

  sei();                                   // enable interrupts when youre done!
}// end isr

Processing:-

import processing.sound.*;
SoundFile file;
import processing.serial.*;
PFont font;
PFont portsFont;
Scrollbar scaleBar;

Serial port;

int Sensor;      // holds pusle sensor data from the arduino
int TBH;         // HOLDS TIME BETWEN HEARTBEATS FROM ARDUINO
int BPM;         // HOLDS HEART RATE VALUE FROM ARDUINO
int[] RawY;      // HOLDS HEARTBEAT WAVEFORM DATA BEFORE SCALING
int[] ScaledY;   // USED TO POSITION SCALED HEARTBEAT WAVEFORM
int[] rate;      // USED TO POSITION BPM DATA WAVEFORM
float zoom;      // USED WHEN SCALING PULSE WAVEFORM TO PULSE WINDOW
float offset;    // USED WHEN SCALING PULSE WAVEFORM TO PULSE WINDOW
color eggshell = color(171,219,227);
int heart = 0;   // This variable times the heart image 'pulse' on screen
//  THESE VARIABLES DETERMINE THE SIZE OF THE DATA WINDOWS
int PulseWindowWidth = 490;
int PulseWindowHeight = 512;
int BPMWindowWidth = 180;
int BPMWindowHeight = 340;
boolean beat = false;    // set when a heart beat is detected, then cleared when the BPM graph is advanced

// SERIAL PORT STUFF TO HELP YOU FIND THE CORRECT SERIAL PORT
String serialPort;
String[] serialPorts = new String[Serial.list().length];
boolean serialPortFound = false;
Radio[] button = new Radio[Serial.list().length];


void setup() {
  size(700, 600);  // Stage size
  file = new SoundFile(this, "heart.mp3");
  frameRate(100);
  font = loadFont("Arial-BoldMT-24.vlw");
  textFont(font);
  textAlign(CENTER);
  rectMode(CENTER);
  ellipseMode(CENTER);
// Scrollbar constructor inputs: x,y,width,height,minVal,maxVal
  scaleBar = new Scrollbar (400, 575, 180, 12, 0.5, 1.0);  // set parameters for the scale bar
  RawY = new int[PulseWindowWidth];          // initialize raw pulse waveform array
  ScaledY = new int[PulseWindowWidth];       // initialize scaled pulse waveform array
  rate = new int [BPMWindowWidth];           // initialize BPM waveform array
  zoom = 0.75;                               // initialize scale of heartbeat window

// set the visualizer lines to 0
 for (int i=0; i<rate.length; i++){
    rate[i] = 555;      // Place BPM graph line at bottom of BPM Window
   }
 for (int i=0; i<RawY.length; i++){
    RawY[i] = height/2; // initialize the pulse window data line to V/2
 }

 background(0);
 noStroke();
 // DRAW OUT THE PULSE WINDOW AND BPM WINDOW RECTANGLES
 drawDataWindows();
 drawHeart();

// GO FIND THE ARDUINO
  fill(eggshell);
  text("Select Your Serial Port",245,30);
  listAvailablePorts();

}

void draw() {
if(serialPortFound){
  // ONLY RUN THE VISUALIZER AFTER THE PORT IS CONNECTED
  background(0);
  noStroke();
  drawDataWindows();
  drawPulseWaveform();
  drawBPMwaveform();
  drawHeart();
// PRINT THE DATA AND VARIABLE VALUES
  fill(eggshell);                                       // get ready to print text
  text("Check your Heart Beat and Pulse",245,30);     // tell them what you are
  text("TBH " + TBH + "mS",600,585);                    // print the time between heartbeats in mS
  text(BPM + "BPM",600,200);                           // print the Beats Per Minute
  text("Scale the Pulse Rate " + nf(zoom,1,2), 150, 585); // show the current scale of Pulse Window

//  DO THE SCROLLBAR THINGS
  scaleBar.update (mouseX, mouseY);
  scaleBar.display();

} else { // SCAN BUTTONS TO FIND THE SERIAL PORT

  for(int i=0; i<button.length; i++){
    button[i].overRadio(mouseX,mouseY);
    button[i].displayRadio();
  }

}

}  //end of draw loop


void drawDataWindows(){
    // DRAW OUT THE PULSE WINDOW AND BPM WINDOW RECTANGLES
    fill(eggshell);  // color for the window background
    rect(255,height/2,PulseWindowWidth,PulseWindowHeight);
    rect(600,385,BPMWindowWidth,BPMWindowHeight);
}

void drawPulseWaveform(){
  // DRAW THE PULSE WAVEFORM
  // prepare pulse data points
  RawY[RawY.length-1] = (1023 - Sensor) - 212;   // place the new raw datapoint at the end of the array
  zoom = scaleBar.getPos();                      // get current waveform scale value
  offset = map(zoom,0.5,1,150,0);                // calculate the offset needed at this scale
  for (int i = 0; i < RawY.length-1; i++) {      // move the pulse waveform by
    RawY[i] = RawY[i+1];                         // shifting all raw datapoints one pixel left
    float dummy = RawY[i] * zoom + offset;       // adjust the raw data to the selected scale
    ScaledY[i] = constrain(int(dummy),44,556);   // transfer the raw data array to the scaled array
  }
  stroke(250,0,0);                               // red is a good color for the pulse waveform
  noFill();
  beginShape();                                  // using beginShape() renders fast
  for (int x = 1; x < ScaledY.length-1; x++) {
    vertex(x+10, ScaledY[x]);                    //draw a line connecting the data points
  }
  endShape();
}

void drawBPMwaveform(){
// DRAW THE BPM WAVE FORM
// first, shift the BPM waveform over to fit then next data point only when a beat is found
 if (beat == true){   // move the heart rate line over one pixel every time the heart beats
   file.play();
   beat = false;      // clear beat flag (beat flag waset in serialEvent tab)
   for (int i=0; i<rate.length-1; i++){
     rate[i] = rate[i+1];                  // shift the bpm Y coordinates over one pixel to the left
   }
// then limit and scale the BPM value
   BPM = min(BPM,200);                     // limit the highest BPM value to 200
   float dummy = map(BPM,0,200,555,215);   // map it to the heart rate window Y
   rate[rate.length-1] = int(dummy);       // set the rightmost pixel to the new data point value
 }
 // GRAPH THE HEART RATE WAVEFORM
 stroke(250,0,0);                          // color of heart rate graph
 strokeWeight(2);                          // thicker line is easier to read
 noFill();
 beginShape();
 for (int i=0; i < rate.length-1; i++){    // variable 'i' will take the place of pixel x position
   vertex(i+510, rate[i]);                 // display history of heart rate datapoints
 }
 endShape();
}

void drawHeart(){
  // DRAW THE HEART AND MAYBE MAKE IT BEAT
    fill(250,0,0);
    stroke(250,0,0);
    // the 'heart' variable is set in serialEvent when arduino sees a beat happen
    heart--;                    // heart is used to time how long the heart graphic swells when your heart beats
    heart = max(heart,0);       // don't let the heart variable go into negative numbers
    if (heart > 0){             // if a beat happened recently,
      strokeWeight(8);          // make the heart big
    }
    smooth();   // draw the heart with two bezier curves
    bezier(width-100,50, width-20,-20, width,140, width-100,150);
    bezier(width-100,50, width-190,-20, width-200,140, width-100,150);
    strokeWeight(1);          // reset the strokeWeight for next time
}

void listAvailablePorts(){
  serialPorts = Serial.list();
  fill(0);
  textFont(font,16);
  textAlign(LEFT);
  // set a counter to list the ports backwards
  int yPos = 0;
  for(int i=serialPorts.length-1; i>=0; i--){
    button[i] = new Radio(35, 95+(yPos*20),12,color(180),color(80),color(255),i,button);
    text(serialPorts[i],50, 100+(yPos*20));
    yPos++;
  }
  textFont(font);
  textAlign(CENTER);
}
void mousePressed(){
  scaleBar.press(mouseX, mouseY);
  if(!serialPortFound){
    for(int i=0; i<button.length; i++){
      if(button[i].pressRadio(mouseX,mouseY)){
        try{
          port = new Serial(this, Serial.list()[i], 115200);  // make sure Arduino is talking serial at this baud rate
          delay(1000);
          println(port.read());
          port.clear();            // flush buffer
          port.bufferUntil('\n');  // set buffer full flag on receipt of carriage return
          serialPortFound = true;
        }
        catch(Exception e){
          println("Couldn't open port " + Serial.list()[i]);
        }
      }
    }
  }
}

void mouseReleased(){
  scaleBar.release();
}

void keyPressed(){

 switch(key){
   case 's':    // pressing 's' or 'S' will take a jpg of the processing window
   case 'S':
     saveFrame("heartLight-####.jpg");    // take a shot of that!
     break;

   default:
     break;
 }
}
class Radio {
  int _x,_y;
  int size, dotSize;
  color baseColor, overColor, pressedColor;
  boolean over, pressed;
  int me;
  Radio[] radios;
  
  Radio(int xp, int yp, int s, color b, color o, color p, int m, Radio[] r) {
    _x = xp;
    _y = yp;
    size = s;
    dotSize = size - size/3;
    baseColor = b;
    overColor = o;
    pressedColor = p;
    radios = r;
    me = m;
  }
  
  boolean pressRadio(float mx, float my){
    if (dist(_x, _y, mx, my) < size/2){
      pressed = true;
      for(int i=0; i<radios.length; i++){
        if(i != me){ radios[i].pressed = false; }
      }
      return true;
    } else {
      return false;
    }
  }
  
  boolean overRadio(float mx, float my){
    if (dist(_x, _y, mx, my) < size/2){
      over = true;
      for(int i=0; i<radios.length; i++){
        if(i != me){ radios[i].over = false; }
      }
      return true;
    } else {
      return false;
    }
  }
  
  void displayRadio(){
    noStroke();
    fill(baseColor);
    ellipse(_x,_y,size,size);
    if(over){
      fill(overColor);
      ellipse(_x,_y,dotSize,dotSize);
    }
    if(pressed){
      fill(pressedColor);
      ellipse(_x,_y,dotSize,dotSize);
    }
  }
}
    
    
    
/*
    from the book "Processing" by Reas and Fry
*/

class Scrollbar{
 int x,y;               // the x and y coordinates
 float sw, sh;          // width and height of scrollbar
 float pos;             // position of thumb
 float posMin, posMax;  // max and min values of thumb
 boolean rollover;      // true when the mouse is over
 boolean locked;        // true when it's the active scrollbar
 float minVal, maxVal;  // min and max values for the thumb

 Scrollbar (int xp, int yp, int w, int h, float miv, float mav){ // values passed from the constructor
  x = xp;
  y = yp;
  sw = w;
  sh = h;
  minVal = miv;
  maxVal = mav;
  pos = x - sh/2;
  posMin = x-sw/2;
  posMax = x + sw/2;  // - sh;
 }

 // updates the 'over' boolean and position of thumb
 void update(int mx, int my) {
   if (over(mx, my) == true){
     rollover = true;            // when the mouse is over the scrollbar, rollover is true
   } else {
     rollover = false;
   }
   if (locked == true){
    pos = constrain (mx, posMin, posMax);
   }
 }

 // locks the thumb so the mouse can move off and still update
 void press(int mx, int my){
   if (rollover == true){
    locked = true;            // when rollover is true, pressing the mouse button will lock the scrollbar on
   }else{
    locked = false;
   }
 }

 // resets the scrollbar to neutral
 void release(){
  locked = false;
 }

 // returns true if the cursor is over the scrollbar
 boolean over(int mx, int my){
  if ((mx > x-sw/2) && (mx < x+sw/2) && (my > y-sh/2) && (my < y+sh/2)){
   return true;
  }else{
   return false;
  }
 }

 // draws the scrollbar on the screen
 void display (){

  noStroke();
  fill(255);
  rect(x, y, sw, sh);      // create the scrollbar
  fill (250,0,0);
  if ((rollover == true) || (locked == true)){
   stroke(250,0,0);
   strokeWeight(8);           // make the scale dot bigger if you're on it
  }
  ellipse(pos, y, sh, sh);     // create the scaling dot
  strokeWeight(1);            // reset strokeWeight
 }

 // returns the current value of the thumb
 float getPos() {
  float scalar = sw / sw;  // (sw - sh/2);
  float ratio = (pos-(x-sw/2)) * scalar;
  float p = minVal + (ratio/sw * (maxVal - minVal));
  return p;
 }
 }


void serialEvent(Serial port){
try{
   String inData = port.readStringUntil('\n');
   inData = trim(inData);                 // cut off white space (carriage return)

   if (inData.charAt(0) == 'S'){          // leading 'S' for sensor data
     inData = inData.substring(1);        // cut off the leading 'S'
     Sensor = int(inData);                // convert the string to usable int
   }
   if (inData.charAt(0) == 'B'){          // leading 'B' for BPM data
     inData = inData.substring(1);        // cut off the leading 'B'
     BPM = int(inData);                   // convert the string to usable int
     beat = true;                         // set beat flag to advance heart rate graph
     heart = 20;                          // begin heart image 'swell' timer
   }
 if (inData.charAt(0) == 'Q'){            // leading 'Q' means IBI data
     inData = inData.substring(1);        // cut off the leading 'Q'
     TBH = int(inData);                   // convert the string to usable int
   }
} catch(Exception e) {
  // println(e.toString());
}

}

 

Final Project: Smash Your Memory [User Testing]

IDEA:

I would have the pads laid on the floor and use longer wires to create a bigger space.

user testing:

THINGS TO WORK ON 

  1. User testing feedback
    1. shorten the melody line
    2. allow players to practice and familiarize the notes
  2. Display ranking
  3. Aesthetics and physical decorations
    1. wooden box to contain the breadboard and Arduino Uno
    2. square pieces to package the pads
//Processing codes

/*
 1. fix the melody
 2. display ranking
 3. aesthetics and physical decoration
 */

/*
 name: Chi-Ting Tsai
 assignment: final project
 date: 12/07/2021
 "Smash Your Memory"
 */

import processing.sound.*;
SoundFile demoMelody;
SoundFile recC;
SoundFile recD;
SoundFile recE;
SoundFile recG;
SoundFile recA;

int size = 200;
color frame = color(237, 168, 17);

int stage = 0;
int noteC = 1;
int noteD = 2;
int noteE = 3;
int noteG = 5;
int noteA = 6;
int[] correctMelody = {noteD, noteC, noteE, noteD, noteG, noteA, noteG, noteE, noteD, noteC};


void setup() {
  size (1024, 768);
  printArray(Serial.list());
  String portname=Serial.list()[3];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  demoMelody = new SoundFile (this, "/Users/chi-tingtsai/Downloads/10.wav");
  recC = new SoundFile (this, "/Users/chi-tingtsai/Downloads/C.wav");
  recD = new SoundFile (this, "/Users/chi-tingtsai/Downloads/D.wav");
  recE = new SoundFile (this, "/Users/chi-tingtsai/Downloads/E.wav");
  recG = new SoundFile (this, "/Users/chi-tingtsai/Downloads/G.wav");
  recA = new SoundFile (this, "/Users/chi-tingtsai/Downloads/A.wav");
}

void draw() {
  if (stage == 0) {
    ranking();
  } else if (stage == 1) {
    rules();
  } else if (stage == 2) {
    game();
  } else if (stage == 3) {
    lose();
  } else if (stage ==4) {
    win();
  }
}

//display ranking
void ranking() {
  background(color(99, 144, 130));
  PFont F = createFont("PatuaOne-Regular", 1);
  fill(255);
  textAlign(CENTER);

  //Game title
  textFont(F, 50);
  text("Smash Your Memory", width/2, height/2-50);
  //explain how to start
  textFont(F, 30);
  text("Smash Covid to Begin Game", width/2, height*3/4);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);
}

void rules() {
  background(color(99, 144, 130));
  PFont F = createFont("PatuaOne-Regular", 1);
  fill(255);
  textAlign(CENTER);

  //explain how to play
  textFont(F, 50);
  text("RULES", width/2, height/4);
  textFont(F, 30);
  text("Listen to the pentatonic melody played after this scene.\n It's only going to play once!\n Smash the corresponding pads to recreate the melody!\nC: COVID\nD: Deadlines\nE: course Enrollment\nG: GPA\nA: Anxiety", width/2, height/2-120);
  textFont(F, 30);
  text("Smash course Enrollment to listen to the melody and begin!", width/2, height-110);

  //frame
  noStroke();
  rectMode(CENTER);
  fill(frame);
  rect(width/2, 10, width, 20);
  rect(width/2, height-10, width, 20);
  rect(10, height/2, 20, height);
  rect(width-10, height/2, 20, height);
}

import processing.serial.*;
Serial myPort;

int aSignal = 0;
int prevASignal = 3;
int val = 0;

void game() {
  //background
   background(color(99, 144, 130));
  fill(255);
  PFont F = createFont("PatuaOne-Regular", 1);
  textAlign(CENTER);

  textFont(F, 50);
  text("Listen Carefully and Recreate!", width/2, height/2-50);
}

void serialEvent(Serial myPort) {
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!= null) {
    aSignal = parseInt(s);
    //println("signal from arduino: " + s);
    //scene transition
    if (stage == 0 && aSignal == noteC) {
      recC.play();
      stage = 1;
    }
    //if (stage == 1 && aSignal == noteD) {
    //  demoMelody.play();
    //}
    if (stage == 1 && aSignal == noteE) {
      stage = 2;
      demoMelody.play();
    }
    if ((stage == 3 || stage == 4) && aSignal == noteG) {
      recG.play();
      stage = 0;
    }

    //note playing and verification
    if (stage == 2 && prevASignal!= aSignal) {
      println("number stored in the array: " + aSignal);
      if (aSignal == noteC) {
        recC.play();
      } else if (aSignal == noteD) {
        recD.play();
      } else if (aSignal == noteE) {
        recE.play();
      } else if (aSignal == noteG) {
        recG.play();
      } else if (aSignal == noteA) {
        recA.play();
      }
      //verify note in array
      if (correctMelody[val] == aSignal) {
        //change circle color
        val++;
        prevASignal = aSignal;
        if (val == 10) {
          stage = 4;
        }
      } else {
        //change circle color
        delay(5000);
        stage = 3;
      }
    }
  }
  myPort.write('\n');
}

void lose() {
  background(color(99, 144, 130));
  PFont F = createFont("PatuaOne-Regular", 1);
  noStroke();
  fill(255);
  
  textFont(F, 80);
  text("Not the right note!", width/2, height/2);
  textFont(F, 50);
  text("Awwww... Smash GPA to see your ranking and restart.", width/2, height/2+50);
}

void win() {
  background(color(99, 144, 130));
  PFont F = createFont("PatuaOne-Regular", 1);
  noStroke();
  fill(255);

  textFont(F, 80);
  text("Congratulations! You have done it!", width/2, height/2);
  textFont(F, 50);
  text("Have you found your peace by smashing those that give off negative energy?", width/2, height/2-50);
  textFont(F, 30);
  text("Smash GPA to see your ranking", width/2, height/2+50);
}
//Arduino codes

//variable declaration
const int C = 1;
const int D = 2;
const int E = 3;
const int G = 5;
const int A = 6;
const int in_C = 2;
const int in_D = 3;
const int in_E = 4;
const int in_G = 5;
const int in_A = 6;
const int speaker_Pin = 8;
const int led_C = 9;
const int led_D = 10;
const int led_E = 11;
const int led_G = 12;
const int led_A = 13;

int switch_C, switch_D, switch_E, switch_G, switch_A = 0;

int smash = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(in_C, INPUT);
  pinMode(in_D, INPUT);
  pinMode(in_E, INPUT);
  pinMode(in_G, INPUT);
  pinMode(in_A, INPUT);
  pinMode(speaker_Pin, OUTPUT);
  pinMode(led_C, OUTPUT);
  pinMode(led_D, OUTPUT);
  pinMode(led_E, OUTPUT);
  pinMode(led_G, OUTPUT);
  pinMode(led_A, OUTPUT);

  Serial.begin(9600);
  Serial.println("0");
}

void loop() {
  // put your main code here, to run repeatedly:
  switch_C = digitalRead(in_C);
  switch_D = digitalRead(in_D);
  switch_E = digitalRead(in_E);
  switch_G = digitalRead(in_G);
  switch_A = digitalRead(in_A);

  while (Serial.available()) {
    if (Serial.read() == '\n') {
      //Note C
      if (switch_C == 1) {                //released, if the two aluminum foils touch
        digitalWrite(led_C, HIGH);        //turns on LED
        smash = C;
      }
      else if (switch_C && switch_D && switch_E && switch_G && switch_A == 0)
      { //released, if the two aluminum foils part
        digitalWrite (led_C, LOW);        //turns off LED
        smash = 0;
      };

      //Note D
      if (switch_D == 1) {                //released, if the two aluminum foils touch
        digitalWrite(led_D, HIGH);        //turns on LED
        smash = D;
      }
      else if (switch_C && switch_D && switch_E && switch_G && switch_A == 0)
      { //released, if the two aluminum foils part
        digitalWrite (led_D, LOW);        //turns off LED
        smash = 0;
      };

      //Note E
      if (switch_E == 1) {                //released, if the two aluminum foils touch
        digitalWrite(led_E, HIGH);        //turns on LED
        smash = E;
      }
      else if (switch_C && switch_D && switch_E && switch_G && switch_A == 0)
      { //released, if the two aluminum foils part
        digitalWrite (led_E, LOW);        //turns off LED
        smash = 0;
      };

      //Note G
      if (switch_G == 1) {                //released, if the two aluminum foils touch
        digitalWrite(led_G, HIGH);        //turns on LED
        smash = G;
      }
      else if (switch_C && switch_D && switch_E && switch_G && switch_A == 0)
      { //released, if the two aluminum foils part
        digitalWrite (led_G, LOW);        //turns off LED
        smash = 0;
      };

      //Note A
      if (switch_A == 1) {                //released, if the two aluminum foils touch
        digitalWrite(led_A, HIGH);        //turns on LED
        smash = A;
      }
      else if (switch_C && switch_D && switch_E && switch_G && switch_A == 0)
      { //released, if the two aluminum foils part
        digitalWrite (led_A, LOW);        //turns off LED
        smash = 0;
      }
      Serial.println(smash);
    }
  }
  //  Serial.print("smash: ");
  //  Serial.println(smash);
  //  Serial.print("C: ");
  //  Serial.println(switch_C);
  //  Serial.print("D: ");
  //  Serial.println(smash);
  //  Serial.print("E: ");
  //  Serial.println(smash);
  //  Serial.print("G: ");
  //  Serial.println(smash);
  //  Serial.print("A: ");
  //  Serial.println(smash);
}

Final project progress: Mini-Tesla car

Description:

For my project, I am building a mini-Tesla car that will feature both autopilot mode and manual mode. When the autopilot mode is enabled, the car will be able to move around and avoid all obstacles. Whereas for the manual mode, the user will be able to control the car through processing using the arrows/buttons.

Progress:
Autopilot mode:

Instead of the L298N motor driver shield, I am using the Adafruit motor shield (due to its unavailability) to control the wheels and add movement to the car. To use it correctly, I had to download a new library called “Adafruit Motor Shield V2 Library”, and a couple of other sub-libraries that come with it. 

#include <Adafruit_MotorShield.h>

Adafruit_MotorShield AFMS(0x61);

Adafruit_DCMotor *rb = AFMS.getMotor(3);
Adafruit_DCMotor *lb = AFMS.getMotor(2);
Adafruit_DCMotor *rf = AFMS.getMotor(1);
Adafruit_DCMotor *lf = AFMS.getMotor(4);
Calculate the distance:

For that, I am using pulseIn() which starts the timing when the pin goes to HIGH then stops it when it goes to LOW. It works on pulses higher than 10 microseconds so I had to use a delay there.

// calculate the distance ahead
digitalWrite(4, HIGH);
delayMicroseconds(10);
digitalWrite(4, LOW);
dist = pulseIn(5, HIGH, 10000) * 340 / 10000 / 2;
Movement:

If all directions are blocked, do a U-turn:

To do that, I am alternating BACKWARD and FORWARD between the 4 wheels.

Right back wheel: Backward

Left back wheel: Forward

Right front wheel: Backward

Left front wheel: Forward

I have also added speed for the turn, then decreased it back at the end.

The delay is quite high 700 microseconds, to leave them time to make the whole U-turn.

if (rdist<= min_dist && ldist<= min_dist) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(700);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If both directions are free, choose right:

The delay is half the one used for a U-turn.

// if both directions are free, choose right
    else if (rdist>=220 && ldist>=220){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If the right way is free, choose right:

// if right way is free, choose right
    else if (rdist>=ldist){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }

If the left way is free, choose left:

// if left way is free, choose left
    else if (rdist>=ldist){
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(FORWARD);
      lb->run(BACKWARD);
      rf->run(FORWARD);
      lf->run(BACKWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
Manual mode:

When enabled, the user can control the car through Processing using either the arrows or the screen buttons. When an action is specified, Processing sends the data to Arduino.

else {
    // forward
    if (direc == 0) {
      rb->run(FORWARD);
      lb->run(FORWARD);
      rf->run(FORWARD);
      lf->run(FORWARD);
    }
    // right
    if (direc == 1) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(BACKWARD);
      lb->run(FORWARD);
      rf->run(BACKWARD);
      lf->run(FORWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
    // backward
    if (direc == 2) {
      rb->run(BACKWARD);
      lb->run(BACKWARD);
      rf->run(BACKWARD);
      lf->run(BACKWARD);
    }
    // left
    if (direc == 3) {
      rb->setSpeed(80);
      lb->setSpeed(80);
      rf->setSpeed(80);
      lf->setSpeed(80);
      rb->run(FORWARD);
      lb->run(BACKWARD);
      rf->run(FORWARD);
      lf->run(BACKWARD);
      delay(400);
      rb->setSpeed(40);
      lb->setSpeed(40);
      rf->setSpeed(40);
      lf->setSpeed(40);
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
    // stop
    if (direc == 4) {
      rb->run(RELEASE);
      lb->run(RELEASE);
      rf->run(RELEASE);
      lf->run(RELEASE);
    }
}
Processing:
import processing.serial.*;
Serial myPort;  
PImage img;

void setup() { 
  size(960,720);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  img = loadImage("bg.png");
}

void draw() {
  background(#e9eaea);
  image(img, width*1/2-135, height/2-125,270,250);
  fill(#555555);
  textSize(30);
  text("Use the arrows to control the car.", width*1/2-200, height/2+ 145);
  text("Click on S to switch to stop the car.", width*1/2-210, height/2+ 195);
  text("Click on A to switch to autopilot mode.", width*1/2-225, height/2+ 245);
  if(keyPressed) {                                                                                            //if a key is pressed, do the following
    if (keyCode == UP){     
      myPort.write(0+","+0+"\n"); 
    }
    else if (keyCode == DOWN){     
      myPort.write(0+","+2+"\n");  
    }
    else if (keyCode == RIGHT){     
      myPort.write(0+","+1+"\n");  
    }
    else if (keyCode == LEFT){     
      myPort.write(0+","+3+"\n");  
    }
    else if (key == 's'){     
      myPort.write(0+","+4+"\n");
    }
    else if (key == 'a'){     
      myPort.write(1+","+0+"\n");
    }
    else {
      myPort.write(4+","+9+"\n");
    }
  }
  else {
    myPort.write(4+","+9+"\n");
  }
}

 

View post on imgur.com

View post on imgur.com

Hardest parts:

One of the challenges I have faced was using the motor shield, as some of the parts were soldered (between the two right pads), which affected the address of the shield. So I had to either fix it through code or remove the soldering. One more issue was related to the VIN jumper because for some time there was no power in the shield, it might be because the 5v pad was also soldered.

User testing:

View post on imgur.com

View post on imgur.com

Comments from user:
  • Add more speed (Answer: I think it would be a bad idea because it will lead to the car hitting obstacles)
  • Add sound ( Answer: I will look into adding a buzzer if it is not going to affect the circuit because the motors require too much voltage)
  • Add lights that automatically turn on when it’s dark ( Answer: I will implement that using LDRs)

Final User Test

 

FEEDBACK

According to my friend’s feedback, I think what I can improve is to make clearer instructions on the coin pusher. Since when I was making the physical setting, the coin pusher was pretty tricky.

I used the distance sensor to check if a coin has approached. But if the coin falls into the gap, the distance sensor would output an outlier, which could then lead to error. So I wrote “SWIPE a coin to START” before the game starts, hoping that people could just swipe — put the coin into the space and hold it for a second.

At first, my friend just tossed the coin into it, and the distance sensor didn’t capture that before the coin fell through it. Since she majors in IMA, she figured it out herself, but not everyone can understand how distance sensor works! So I decided to write specific instructions on the box about how to swipe a coin.

The other parts of the game was straightforward and understandable. As soon as my friend saw the claw/hand, she realized that it is a claw machine, so naturally she knew the way to play around it.

The last part was to show that there is possibility that you are lucky enough to grab a toy with an award. I set the probability to be quite low, so it took a long time before she could get the award. I had to change the code so that she could cheat on it.

 

PROGRESS:

int joyX = A0;
int joyY = A1;
int button = 4;
int button_state;
const int trigPin = 7;
const int echoPin = 6;
int duration = 0;
int dist = 0;
int prize=0;

int red = 10;
int yellow = 9;
int blue = 8;

int posX;
int posY;



void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("0,0,0,0");
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(button, OUTPUT);
  pinMode(trigPin , OUTPUT);
  pinMode(echoPin , INPUT);
  pinMode(red, OUTPUT);
  pinMode(yellow , OUTPUT);
  pinMode(blue , OUTPUT);
  pinMode(2,INPUT);
  digitalWrite(2,HIGH);
}

void loop() {
  // put your main code here, to run repeatedly:
//  int posX = analogRead(joyX); 
//  int posY = analogRead(joyY); 
//  Serial.println(posY);
//  Serial.println(posX);
  
  digitalWrite(red,prize);
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(2);
  digitalWrite(trigPin, LOW); 
   
  duration = pulseIn(echoPin, HIGH);
  dist = duration/58.2;
//  Serial.println(dist);

  while (Serial.available()) {
    prize = Serial.parseInt();
    if (Serial.read() == '\n'){ 
       button_state = digitalRead(button);
       // move 0
       posX = analogRead(joyX); 
       posY = analogRead(joyY); 
       
       if (posX >= 550) {
          Serial.print(1);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");
       
       // move 1
       if (posX <= 360) {
          Serial.print(0);   
       } else {
          Serial.print(0);
       }
       Serial.print(",");   
           
       // grab 2
       if (button_state == 0) {
          Serial.print(0);
       } else {
          Serial.print(1);
       }
       Serial.print(",");
       
       // coin pusher 3
       if ( dist < 6 )
          {
            Serial.println(1);
          } else {
            Serial.println(0);       
          }
        digitalWrite(red,prize);
        digitalWrite(yellow,prize);
        digitalWrite(blue,prize);
      }

     } 

}

 

import fisica.*;
import processing.serial.*;
import processing.sound.*;
Serial myPort;
PImage claw;
PImage[][] claws;
PImage bird;
PImage[][] birds;
PImage bg;

boolean start = false;
boolean grab = false;
boolean get = false;
boolean drop = false;
boolean ret = false;
boolean prize = false;
boolean coin = false;
boolean right = false;
boolean left = false;

float speed;
float posX=20, posY=10;
FCircle[] bodies;

ArrayList<FBody> touching = new ArrayList<FBody>();
FCircle tip;
FBody prev_touch;

FWorld world;
int toy_num = 30;

SoundFile grab_sound;
SoundFile drop_sound;
SoundFile prize_sound;
SoundFile start_sound;
 
void setup() {
  size(750, 600);
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  
  //
  bg = loadImage("bg.jpeg");
  bg.resize(750,600);
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  
  grab_sound = new SoundFile(this, "pick_up.wav");
  drop_sound = new SoundFile(this, "drop.wav");
  prize_sound = new SoundFile(this, "prize.wav");
  start_sound = new SoundFile(this, "coin.wav");
}

void draw() {
  //println(mouseX,mouseY);
  
  background(bg);
  world.step();
  world.draw();
  
  // box
  fill(255, 200);
  noStroke();
  rect(603, 250, 200, 280);
  strokeWeight(3);
  stroke(0);
  line(603, 250, 605, 530);
  
  if (coin) {
    start_sound.play();
    coin = false;
  }
  
  if (start){
    initialize_claw();
  } else {
    fill(#A09B10);
    PFont f = createFont("Dialog-48", 40);
    textFont(f);
    text("SWIPE a coin to START", 145, 100);
    fill(#671235);
    text("SWIPE a coin to START", 140, 100);
  }
}

void initialize_claw() {
  if (posX >= width - 80) {
    posX = width-80;
  }
  
  if (posX <= 20) {
    posX = 20;
  }
  
  
  if (!grab) {
    if (ret) {
      if (posX > 20) {
        image(claws[0][0], posX, 10);
        posX -=3;
        if (prize) {
          fill(#901758);
          text("CONGRATULATIONS!!!", 150, 100);
        }
      } else {
        ret = false;
        world.remove(prev_touch);
        prize = false;
      }
    } else {
      image(claws[0][0], posX, 10);
      if (right) {
        posX += speed;
      }
      
      if (left) {
        posX -= speed;
      }
    }
  } else {
    if (posX >= 550 && !get) {
      grab = false;
      get = false;
      ret = true;
    } else {
    
    claw_down();
    if (posY<0) {
      posY+=3;
    }
    if (!drop) {
      FBody toy = world.getBody(posX+60, posY+80);
      if (get && posY < 20) {
        image(claws[6][0], posX, posY);
        if (posX <= 598) {
          posX += 3;
          toy.setPosition(posX+60, posY+80);
        } else {
          drop = true;
          drop_sound.play();
          toy.setStatic(false);
          grab = false;
          get = false;
          drop = false;
          ret = true;
        // prize
        int ram = (int)random(100);
        if (ram <= 1) {
          prize = true;
          prize_sound.play();
        }
        
         }
        }
      }
    }
  }
    
}

void claw_down() {
  FBody touch;
  tip.setPosition(posX+40, posY+90);
  if (posY < 500 && posY > 0) {
    touching = tip.getTouching();
    if (get) {
      image(claws[6][0], posX, posY);
      posY -= 3;
      if (touching.size()>0) {
        touch = touching.get(0);
        prev_touch = touch;
 
        touch.setStatic(true);
        touch.setPosition(posX+60, posY+80);
      }
    } else{
      image(claws[7][0], posX, posY);
      if (touching.size()>0) {
        get = true;
        grab_sound.play();
      } else {
        posY += 3;
        if (posY >= 500) {
          grab = false;
          get = false;
          ret = true;
        }
      }
    }
  }
}



void generate_claw() {
  claw = loadImage("claw.png");
  claws = new PImage[8][5];
  int w = claw.width / 5; 
  int h = claw.height / 8; 

  for (int y=0; y < 8; y++) {
    for (int x=0; x< 5; x++) {
      claws[y][x] = claw.get(x*w, y*h, w, h);
      claws[y][x].resize(100,120);
    }
  }   
}

void generate_birds() { 
  bodies = new FCircle[30];
  
  bird = loadImage("bird.png");
  birds = new PImage[2][3];
  int w1 = bird.width / 3;
  int h1 = bird.height / 2;
  
  for (int y=0; y < 2; y++) {
    for (int x=0; x< 3; x++) {
      birds[y][x] = bird.get(x*w1, y*h1, w1, h1);
      birds[y][x].resize(85, 85);
    }
  }
  
  for (int i=0; i<toy_num; i++) {
    int radius = 50;
    FCircle b = new FCircle(radius);
    b.setPosition(random(100, 500), height/2);
    b.setStroke(0);
    b.setStrokeWeight(2);
    b.setDensity(radius / 10);
    int random_x = int(random(0,2));
    int random_y = int(random(0,3));
    b.attachImage(birds[random_x][random_y]);
    world.add(b);
    bodies[i] = b;
  }
  
  FBox edge = new FBox(10, 300);
  edge.setPosition(600, 400);
  edge.setStatic(true);
  world.add(edge);
}


void mousePressed() {
  // if button pressed, reset
  world.clear();
  grab = false;
  ret = false;
  get = false;
  drop = false;
  start = false;
  
  bg = loadImage("bg.jpeg");
  bg.resize(750,600);
  generate_claw();
  Fisica.init(this);
  world = new FWorld();
  world.setEdges(0, 0, 750, 530, 255);
  generate_birds();
  
  tip = new FCircle(35);
  tip.setFill(0);
  tip.setStatic(true);
  tip.setDrawable(false);
  world.add(tip);
  
  initialize_claw();
}

void keyPressed() {
  // joystick
  
  //if (keyCode == ENTER) {
  //  speed = 0;
  //  grab = true;
  //} 
  //else {
  //  speed = 8;  
  //}
  speed = 8;
  
  if (keyCode == RIGHT) {
    posX += speed;
  } 
  if (keyCode == LEFT) {
    posX -= speed;
  } 

  
}



void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    println(s);
    int values[]=int(split(s,','));
    if (values.length>=1){
      if (values[3] == 1) {
        coin = true;
        start = true;
      }
      
      if (values[0] == 1 && start) {
        left = false;
        right = true;
      } else {
        right = false;      
      }
      
      if (values[1] == 1 && start) {
        right = false;
        left = true;
      } else {
        left = false;      
      }      
      
      if (values[2] == 1 && start) {
        grab = true;
      }
      
    }
  }
  myPort.write(int(prize)+"\n");
  
}

 

Final Project- Game: Space Survivor 🚀💫 [STATUS-90%]


Description of Project: 

Create a physically interactive system of your choice that relies on a multimedia computer for some sort of processing or data analysis. The Final should use BOTH Processing AND Arduino.Your focus should be on careful and timely sensing of the relevant actions of the person or people that you’re designing this for, and on clear, prompt, and effective responses. Any interactive system is going to involve systems of listening, thinking, and speaking from both parties. Whether it involves one cycle or many, the exchange should be engaging. You may work alone or in pairs.

Description of the game:

It is a space shooter game where there are spaceship has to protect itself from asteroids by either passing it by from a distance or by shooting it down. Each asteroid shot down is one point and to let  the asteroid  pass by is a negative point. As soon as your space ship crashes into an asteroid, the game is over and your final point are displayed on the screen.

Instead of a usual control as by a keyboard, this game is controlled by the movements of your hand. Using an ultrasonic sensor, the game using the distance between the the sensor and your hand, moves the spaceship along. A photo resistor is also installed in the glove, whose readings I have mapped from 0 to 10 and divided it in half, to help the with the shooting of the bullets by the player. Two led lights (red and blue) indicate if the shooting is taking place or no. Here’s a picture of the setup:

Here you can notice that the photo resistor is placed on the index  finger to give the player a better control to the shooting. all they have to do is close their finger or the entire hand, whatever is comfortable to them.

USER TESTING: 

Video: 

Feedback: 

  • ” You should add an instructions panel at the start of the game for the glove usage” –  I think that is a good idea and I will imply it next.
  • ” You should add sound” – This too I will imply next.
  • “The glove is not the most flexible with different hand sizes, maybe you can use disposable gloves and attach the sensor each time. It will also be easy to wear.” – I am not sure about the disposable gloves because it will create too much unnecessary waste, but I do agree with different hand sizes. I will try getting Professor’s take on this.

Next steps:

I will implement the above given suggestions and also work on the serial communication part of the game. I believe this game can be more smooth than what it is right now.

CODE:

processing:

import processing.serial.*;
Serial myPort;

int yPos=0;

ArrayList<Star> stars = new ArrayList<Star>();
int frequency = 4; //frequency of star

Ship playerShip;

ArrayList<Asteroid> asteroids = new ArrayList<Asteroid>();
int asteroidFrequency = 60;

ArrayList<Bullet> bullets = new ArrayList<Bullet>();

int points;

EndScene end;

void setup() {
  printArray(Serial.list());
  String portname=Serial.list()[4];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  fullScreen();
  playerShip = new Ship();
  points = 0;
}

void draw() {


  if (end != null) {
    end.drawEndScene();
  } else {
    background(0);
    drawStar();

    drawAsteroid();
    fill(255, 0, 0);
    stroke(255);
    playerShip.drawShip(myPort);
    drawBullet();
    

    stroke(255);
    fill(255);
    textSize(30);
    text("Points: " + points, 50, 50);

    checkCollision();
  }
}

void drawBullet() {
  serialBullet(myPort);
  for (int i = 0; i<bullets.size(); i++) {
    bullets.get(i).drawBullet();
  }
}

void checkCollision() {
  for (int i = 0; i < asteroids.size(); i++) {
    Asteroid a = asteroids.get(i);
    if (a.checkCollision(playerShip) == true) {
      end = new EndScene();
    }
    for (int b = 0; b < bullets.size(); b++) {
      Bullet bullet = bullets.get(b);
      if (a.checkCollision(bullet) == true) {
        points++;

        asteroids.remove(a);
        bullets.remove(b);
      }
    }
  }
}


void drawAsteroid() {
  if (frameCount % asteroidFrequency == 0) {
    asteroids.add(new Asteroid(random(100, 250)));
  }
  for (int i = 0; i<asteroids.size(); i++) {
    Asteroid currentAsteroid = asteroids.get(i);
    currentAsteroid.drawAsteroid();
    if (currentAsteroid.y > height + currentAsteroid.size) {
      asteroids.remove(currentAsteroid);
      i--;
      points--;
    }
  }
}

void drawStar() {
  strokeWeight(1);
  stroke(255);
  if (frameCount % frequency == 0) {
    Star myStar = new Star();
    stars.add(myStar);
  }
  for (int i = 0; i<stars.size(); i++) {
    Star currentStar = stars.get(i);
    currentStar.drawStar();
  }
}

void serialBullet(Serial myPort) {
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null) {
    println(s);
    int values[]=int(split(s, ','));
    if (values.length==2 && values[1] <= 5) {
      Bullet b = new Bullet(playerShip);
      bullets.add(b);
    }
  }
}

    void mousePressed() {
      if (end != null && end.mouseOverButton() == true) {
        resetGame();
      }
    }

    void resetGame() {
      stars.clear();
      bullets.clear();
      asteroids.clear();
      playerShip = new Ship();
      end = null;
      points = 0;
    }

class Asteroid {
  float size;
  float x;
  float y;
  int num;

  int speed_asteroid = 5; //speed of asteroid

  Asteroid(float size) {
    this.size = size;
    this.x = random(width);
    this.y = 0;
  }

  void drawAsteroid() {
    fill(150);
    stroke(150);
    ellipse(x, y, size, size);
    y+=speed_asteroid;
  }

  boolean checkCollision( Object crash) {
    if (crash instanceof Ship) {
      Ship playerShip = (Ship) crash;
      float tan_shoot =  10*tan(60);
      float distance_ship= dist(x, y, playerShip.xPos, playerShip.y-tan_shoot);
      if (distance_ship< size/2 +tan_shoot){ //tan_shoot+ 10) {
        //background(255, 0, 0);
        fill(255, 0, 0, 100);
        rect(0, 0, width, height);
        fill(255);
        
        return true;
      }
    } else if (crash instanceof Bullet) {
      Bullet bullet = (Bullet) crash;
      float distance_bullet = dist(x, y, bullet.x, bullet.y); 
      //println(distance_bullet);
      if (distance_bullet <= size + bullet.size/2 ) {
        fill(0, 255, 0, 100);
        rect(0, 0, width, height);
        fill(255);
        
        return true;
      }
    }
    return false;
    
  }
}

class Bullet {
  float x;
  float y;
  float velocity_y;
  float size; 

  Bullet(Ship playerShip) {
    this.x = playerShip.xPos;
    this.y = playerShip.y - 15;
    this.velocity_y = -10;
    this.size = 10;
  }

  void drawBullet() {
        fill(255);
        ellipse(x+100, y, size, size);
        y +=velocity_y;
      }
}

class EndScene {
  int button_x;
  int button_y; 
  int button_width; 
  int button_height;


  EndScene() {
    this.button_width = 300;
    this.button_height = 100;
    this.button_x = width/2 - this.button_width/2;
    this.button_y = height/2 - this.button_height/2;
  }

  void drawEndScene() {
    //Background
    fill(#FAE714);
    rect(0, 0, width, height);
    rect(button_x, button_y, button_width, button_height);

    //Game over text
    stroke(0);
    fill(0);
    textSize(150);
    textAlign(CENTER);
    text("GAME OVER!", width/2, height/4);

    //Restart Button
    fill(0);
    stroke(200);
    rect(button_x, button_y, button_width, button_height);
    fill(200);
    textSize(60);
    textAlign(CENTER); 
    text("RETRY?", button_x+150, button_y+70);
    
    //Player Score 
    stroke(0);
    fill(0);
    textSize(80);
    textAlign(CENTER); 
    text("FINAL SCORE: " + points, width/2, height - height/4);
  }

  boolean mouseOverButton() {
    return (mouseX > button_x 
      && mouseX < button_x + button_width
      && mouseY > button_y
      && mouseY < button_y + button_height);
  }
}

class Ship {
  PImage ship;
  float x;
  float y;
  float velocity_x;
  boolean leftPressed, rightPressed;
  int xPos=0;

  int speed = 6; //speed of ship

  Ship() {
    this.x = width/2;
    this.y = height - height/4;
    this.velocity_x = 0;
    ship = loadImage("spaceplane.png");
  }

  void drawShip(Serial myPort) {
    frameRate(60);
    String s=myPort.readStringUntil('\n');
    s=trim(s);
    if (s!=null) {
      println(s);
      int values[]=int(split(s, ','));
      if (values.length==2) {
        xPos=(int)map(values[0], 0, 30, 0, width);
        image(ship, xPos, y);
        ship.resize(200, 100);
      }
    }
  }
}

class Star {
  float x;
  float y;
  int velocity_star;
  
  Star() { 
    this.x = random(width);
    this.y = 0;
    this.velocity_star = 10; //velocity of falling star
  }
  
  void drawStar() {
    y+=velocity_star;
    fill(#FCEB24);
    circle(x,y,5);
  }
}

Arduino:

int left = 0; 
int right = 0; 

const int echoPin = 2;  // attach pin D2 Arduino to pin Echo of HC-SR04
const int trigPin = 3; //attach pin D3 Arduino to pin Trig of HC-SR04
const int led_red = 9;
const int led_blue = 8;
const int photo = A0;


// defines variables
long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement

void setup() {
  Serial.begin(9600); 
  Serial.println("0,0");
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode (led_red, OUTPUT);
  pinMode(led_blue, OUTPUT);
  pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT
   
  
}
void loop() {
  while (Serial.available()){
    right = Serial.parseInt();
    left = Serial.parseInt(); 
  }

  digitalWrite(trigPin, LOW);
  digitalWrite(trigPin, HIGH);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH);

  // Calculating the distance
  distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (going and coming)
  int constrained_distance = constrain(distance, 0, 30);
  
  int light = analogRead(photo);
  int mapped_light = map(light, 0, 1023, 0, 10);
  int constrained_light = constrain(light, 900, 1000);

  if (mapped_light <= 5) {
    digitalWrite(led_red, HIGH);
    digitalWrite(led_blue, LOW);
  } else {
    digitalWrite(led_red, LOW);
    digitalWrite(led_blue, HIGH);
  }
  Serial.print(constrained_distance); 
  Serial.print(","); 
  Serial.println(mapped_light); 
  delayMicroseconds(100);
}

 

Final project Progress: Fatema Nassar, Ingy El Sheikh

Idea:

Basically, our idea was to imitate a sketchbook.The user gets to draw with their hands. There are 5 different generative art pieces, hence five different ‘brushes’ that the user can draw with.The user can choose which brush to use, by pushing  the button corresponding to each brush connect to. arduino.We also decided to incorporate an element of allowing the user to choose the color they want to draw with using a color gradient on processing, that we connected to Arduino through a slide potentiometer. The user can reset the drawing, showing them a blank sketch so they. start the drawing again. We also, wanted to incorporate allowing the user to save the drawing they made to make it more personal to each individual, but we haven’t done that yet.

Process:

This is the order we chose to complete the project in:

  1. First of all, we finalized the idea since we were not working together initially. We decided on everything we needed and the process we would complete the project in.
  2. The brushes were complete (the 5 generative art pieces that play as the brushes)
  3.  started working on the fiducial QR code (which was a pain !) in order to allow the user to use their hands to draw on the sketch, we decided we will have a glove with the QR stuck to it and the user will wear the glove to draw, luckily we were able to get it to work. Simultaneously we were working on the arduino side of things, completing first the color gradient. The color gradient on processing is actually an image and the get(); function was used to get the color at the position of the rectangle that was also mapped and connected to arduino through the slide potentiometer.
  4. Completed the the creation of the box using the laser cutter ! as well as finishing connecting the arcade buttons on arduino and creating code to connect it to processing, so that each button corresponds to a different brush.
  5. We then finished assembling the box and creating the reset button (to return the sketch back to being blank again). We decided to create a box, so that the arduino doesn’t show as it is not very visually pleasing, and in order to ensure that nothing moves around as we are using a lot of wires that may move around.
  6. We conducted user testing. 

Difficulties:

First of all, the QR code was very difficult to get to work, but after so many days of trying to figure it out we were able to get it work. On the arduino side of things, there were so many connection on the arduino, and so much code that sometimes, they were glitches and loose wires which was very frustrating.  But after several tests and removing the wires and re attaching them , we were able to get everything working well.

User Testing:

User #1:

User #2:

Feedback& next steps:

The feedback for both users was. generally the same:

  1. The camera is flipped, hence when the user moves their hand to the left the drawing draws to the right. We are aware of that and it is our next step.
  2. The QR code sometimes does not get picked up, so the drawing stops, this is because the user is holding the QR code as just a paper now,  so. when they hold it wrong on bend it backwards the camera doesn’t pick it up. In order to fix that we will stick it on a glove that the user will wear, so the qr is always the right side up, and straight, hence it will be fixed to the camera will always pick it up.
  3. Lastly, the users wanted us to incorporate adding the names of the brushes to the box so they know which brush corresponds to which button. However, we are a bit skeptical about doing so because we feel like we would be giving to much instruction on what the buttons do. We will take more opinions on this matter.

processing code:

import processing.video.*;
import jp.nyatla.nyar4psg.*;
import processing.serial.*;
import java.util.*;

int n=1, mode=1;
PVector mouseloc = new PVector(0, 0);
PVector p;

Capture cam;
MultiMarker nya;

Brush myBrush = new Brush(1, 2, mouseloc);


// serial stuff
Serial myPort;
int xPos=0;
int yPos;
boolean onOff=false;
boolean onOff2=false;
PImage myImage;
color pix;

int [] val = {0, 0, 0, 0, 0, 0, 0, 0};

void setup() {
  size(1280, 720, P3D);
  colorMode(RGB, 100);
  //fullScreen(P3D);

  printArray(Serial.list());
  String portname=Serial.list()[0];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  myImage  = loadImage("color.jpeg");

  printArray(Capture.list());
  cam=new Capture(this, 1280, 720);
  nya=new MultiMarker(this, width, height, "data/camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
  nya.addARMarker("data/patt.hiro", 40);//id=0
  cam.start();

  background(0);
  loadPixels();
}

int c=0;

void draw() {

  c++;
  if (cam.available() !=true) {
    return;
  }

  pushMatrix();
  cam.read();
  scale(-1, 1);
  //image(cam, -cam.width, 0);
  translate(width, 0);
  nya.detect(cam);
  nya.drawBackground(cam);
  popMatrix();




  if ((!nya.isExist(0))) {
    return;
  }
  p=nya.screen2ObjectCoordSystem(0, width/2, height/2);
  updatePixels();

  if (mode==1) {
    drawScreen();
  }
  // nya.endTransform();

  if (mode==2) {
    // save("pic"+String.valueOf(n++)+".tif");
    // delay(10);
    // takeScreenShot();
    // delay(10);
  }
}


void drawScreen() {
  //brush color picker
  imageMode(CENTER);
  fill(0);
  rectMode(CENTER);
  noStroke();
  rect(myImage.width/2, myImage.height/2, myImage.width, myImage.height*1.5);
  image(myImage, myImage.width/2, myImage.height/2, myImage.width, myImage.height);
  color pix = get(xPos, myImage.height/2);
  fill(pix);
  stroke(0);
  rect(xPos, myImage.height/2, 10, myImage.height*1.5);

  buttonPressed();
  myBrush.setCol(pix);

  mouseloc.x = p.x;
  mouseloc.y = p.x;
  myBrush.draww(mouseloc);
  loadPixels();
}

void buttonPressed() {
  if (val[0] == 1 ) {
    myBrush.setbrush(1);
  } else if (val[1] == 1 ) {
    myBrush.setbrush(2);
  } else if (val[2]==1 ) {
    myBrush.setbrush(3);
  } else if (val[3]==1 ) {
    myBrush.setbrush(4);
  } else if (val[4]==1 ) {
    myBrush.setbrush(5);
  }
  // reset
  else if (val[5]==1 ) {
    println("bla");
    background(0);
    loadPixels();
  }
}

void serialEvent(Serial myPort) {
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null) {
    println(s);
    int values[]=int(split(s, ','));
    if (values.length==9) {
      xPos=(int)map(values[0], 89, 912, 0, myImage.width);

      //yPos=(int)map(values[1],0,1023,0, height);
      //yPos=height/2-230;
      //myBrush.setbrush(2);

      val[0] = (int)values[1];  // read it and store it in val[]
      val[1] = (int)values[2];
      val[2] = (int)values[3];
      val[3] = (int)values[4];
      val[4] = (int)values[5];
      val[5] = (int)values[6];//reset
      val[6] = (int)values[7];//draw
      val[7] = (int)values[8];//saveScreen
    }
    myPort.write(int(onOff)+","+int(onOff2)+"\n");
  }
}
class Brush {

  int brushN;

  float stepSize, linelength, diam;
  float angle;
  float x, y, tempX, tempY;
  color col;
  //
  int counter;
  PFont font;
  float fontSize;
  String letters;
  float  fontSizeMin;
  //

  //linemoduelw
  PShape lineModule;

  float [] numX={ -100, -70, 0, 70, 100, 70, 0, -70};
  float [] numY={ 0, 70, 100, 70, 0, -70, -100, -70};

  Brush(int defaultt, float step_size, PVector mouse) {
    // 1 blur
    // 2 random cylinder
    // 3 dynamic shapes
    // 4 text lines
    // 5 rotating lines
    brushN = defaultt;
    stepSize = step_size;
    angle = 270;
    x = mouseX;
    y = mouse.y;
    col = color(255);
    linelength = 100;
    diam = 2;
    fontSizeMin=200;

    fontSize=50;
    letters = "It isn't true that my mattress is made of cotton candy. It was the first time he had ever seen someone cook dinner on an elephant. Homesickness became contagious in the young campers' cabin. Every manager should be able to recite at least ten nursery rhymes backward.";
  }
  void setbrush(int brushn) {
    brushN=brushn;
  }
  void setSize(float size) {
    stepSize=size;
  }
  void setCol(int colorr) {
    col=colorr;
  }
  void setLineLength(float len) {
    linelength=len;
  }
  void setDiam(float diamm) {
    diam= diamm;
  }

  void setAngle(float ang) {
    angle=ang;
  }

  void draww(PVector mouse) {

    switch (brushN) {
    case 2:
      setLineLength(100);
      setSize(5);
      setDiam(2);
      blur(mouse);
      break;

    case 3:
      setLineLength(100);
      setSize(3);
      randomprisms(mouse);
      break;

    case 4:

      dynamicShapes(mouse);
      break;

    case 5:
      textlines(mouse);
      break;

    default :
      setLineLength(100);
      strokeWeight(1);
      rotatingLines(mouse);
      break;
    }
  }

  void helper_randomPrisms() {
    float angle = TWO_PI/8;
    float rad = 100 * random(0.5, 1);
    for (int i=0; i<8; i++) {
      numX[i]=cos(angle * i) * rad;
      numY[i]=sin(angle * i) * rad;
    }
  }

  void blur(PVector mouse) {
    float [] blurX ={ -stepSize, -stepSize, 0, stepSize, stepSize, stepSize, 0, -stepSize};
    float [] blurY ={         0, stepSize, stepSize, stepSize, 0, -stepSize, -stepSize, -stepSize};
    float d = dist(x, y, mouse.x, mouse.y);

    if (d > stepSize) {
      // gets the angle between the mouse and the location of the brush so that next point is drawn in the right intended location
      angle = (float)Math.atan2(mouse.y-y, mouse.x-x);

      tempX = x;
      tempY = y;

      nya.beginTransform(0);
      //
      //translate(x, y);
      rotate(angle);
      noStroke();
      fill(col, 40);

      //
      for (int i=0; i<500; i++) {
        int dir =  int(random(0, 7));
        rotate(random(5));
        println(blurX[dir]);
        tempX=blurX[dir]*random(1, 10);
        tempY=blurY[dir]*random(1, 10);
        circle(tempX, tempY, diam);
      }
      //
      nya.endTransform();

      x = mouse.x;
      y = mouse.y;
    }
  }

  void randomprisms(PVector mouse) {
    float d = dist(x, y, mouse.x, mouse.y);

    if (d>stepSize) {
      // gets the angle between the mouse and the location of the brush so that next point is drawn in the right intended location
      angle = (float)Math.atan2(mouse.y-y, mouse.x-x);
      for ( int i=0; i<8; i++) {
        numX[i]+=random(0-stepSize/2, stepSize/2);
        numY[i]+=random(0-stepSize/2, stepSize/2);
      }

      nya.beginTransform(0);
      //
      //translate(x, y);
      rotate(angle+random(-0.1, 0.1));
      stroke(col);
      strokeWeight(stepSize/4);
      rectMode(CENTER);
      noFill();
      rect(0, 0, linelength*random(0.95, 1)*d/100, linelength*random(0.95, 1)*d/100);

      nya.endTransform();

      x=x+(float)Math.cos((angle))*stepSize;
      y=y+(float)Math.sin((angle))*stepSize;
    }


    helper_randomPrisms();
  }

  void dynamicShapes(PVector mouse) {
    float d = dist(x, y, mouse.x, mouse.y);
    println("x: ",x,"y", y," mouse.x: ", mouse.x,"mouse.y: " ,mouse.y);
    if (d>stepSize) {
      //
      lineModule = createShape();
      stroke(col);
      strokeWeight(1);
      noFill();
      lineModule.beginShape();

      lineModule.vertex(100, 0); // first point

      lineModule.vertex(0, 100);// /
      lineModule.vertex(-100, 0);// \
      lineModule.vertex(0, -100);// /
      lineModule.vertex(100, 0);// \

      lineModule.endShape();
      // gets the angle between the mouse and the location of the brush so that next point is drawn in the right intended location
      angle = (float)Math.atan2(mouse.y-y, mouse.x-x);


      nya.beginTransform(0);
      //
      //translate(mouse.x, mouse.y);
      rotate((angle-PI));
      shape(lineModule, 0, 0, d, noise(1+frameCount*0.0001)*10);
      //
      nya.endTransform();


      x=x+(float)Math.cos((angle))*stepSize;
      y=y+(float)Math.sin((angle))*stepSize;
    }
  }

  void textlines(PVector mouse) {
    float d = dist(x, y, mouse.x, mouse.y);
    d= constrain(d, 60, 7000);
    if (d>stepSize) {
      // gets the angle between the mouse and the location of the brush so that next point is drawn in the right intended location
      angle = (float)Math.atan2(mouse.y-y, mouse.x-x);
      fontSize= fontSizeMin*60/d;
      font = createFont("Calibri", fontSize);

      textFont(font, fontSize);
      char newLetter = letters.charAt(counter);
      stepSize = textWidth(newLetter);
      fill(col);
      //stroke(col);


      nya.beginTransform(0);
      //
      //translate(x, y);
      
      rotate(angle);
      text(newLetter, 0, 0);
      //
      nya.endTransform();

      counter++;

      if (counter>letters.length()-1) counter=0;

      x = x + (float)Math.cos(angle)*stepSize;
      y = y + (float)Math.sin(angle)*stepSize;
    }
  }

  void rotatingLines(PVector mouse) {

    nya.beginTransform(0);
    strokeWeight(1);
    stroke(col);
    
    rotate(radians(angle));
    line(0, 0, linelength, 0);
    //
    nya.endTransform();

    angle +=5;

    linelength= int(noise(50+0.01*frameCount)*300);
  }
}

Arduino code:

int left = 0;
int right = 0;
int buttonOne = 9;
int buttonTwo = 10;
int buttonThree = 11;
int buttonFour = 12;
int buttonFive = 13;
int buttonSix = 3;//reset
int buttonSeven = 5;//Draw
int buttonEight = 4;//saveScreen


int val1 = 0;
int val2 = 0;
int val3 = 0;
int val4 = 0;
int val5 = 0;
int val6 = 0;
int val7 = 0;
int val8 = 0;

void setup() {
  pinMode(buttonOne, INPUT);
  pinMode(buttonTwo, INPUT);
  pinMode(buttonThree, INPUT);
  pinMode(buttonFour, INPUT);
  //   pinMode(buttonFive, INPUT);
  Serial.begin(9600);
  Serial.println("0,0");

}
void loop() {
  while (Serial.available()) {
    right = Serial.parseInt();
    left = Serial.parseInt();
    if (Serial.read() == '\n') {

      int sensor = analogRead(A0);
      delay(1);
      val1 = digitalRead(buttonOne);
      delay(1);

      val2 = digitalRead(buttonTwo);
      delay(1);

      val3 = digitalRead(buttonThree);
      delay(1);
      //
      val4 = digitalRead(buttonFour);
      delay(1);

      val5 = digitalRead(buttonFive);
      delay(1);

      val6 = digitalRead(buttonSix);
      delay(1);

      val7 = digitalRead(buttonSeven);
      delay(1);
      
      val8 = digitalRead(buttonEight);
      delay(1);
  


      Serial.print(sensor);
      Serial.print(',');
      Serial.print(val1);
      Serial.print(',');
      Serial.print(val2);
      Serial.print(',');
      Serial.print(val3);
      Serial.print(',');
      Serial.print(val4);
      Serial.print(',');
      Serial.print(val5);
      Serial.print(',');
      Serial.print(val6);//
      Serial.print(',');
      Serial.print(val7);//
      Serial.print(',');
      Serial.println(val8);
    }
  }



}

 

Final Project Progress : Shamma & Theyab

WEATHER PATIO 


 


ARDUINO & PROCESSING

Done By : Shamma & Theyab

 


PROJECT SUMMARY:

The project is a smart outdoor cover that we called the weather patio . It has the following different features. First, the system will detect if it’s raining, and if it is then it will close the rollable roof. Second, the system measures the temperature and if the temperature is very high, it will turn on a fan. Third, it detects if it’s night time and turns on the lights when it’s dark. Fourth, it displays all the values on a screen. Last, but not least, it allows for manual control from processing on a screen, so the roof , the fan and the light can be turned on manually using the screen controls.

SYSTEM I/O:

Input:

- Rain Sensor

- Temperature Sensor

- Light Sensor

Output:

-Servo or DC Motor

-Fan DC

-LED Strip with relay

 




DESCRIPTION OF EACH INPUT/OUTPUT ON ARDUINO & WHAT IT WILL SEND/RECEIVE FROM PROCESSING  :

 

Input Arduino:

        1. Rain sensor to detect if it’s raining
        2. Temperature sensor to detect the ambient temperature.
        3. Light sensor to detect the light in the area.

 

Output Arduino:

        • Motor (servo or DC) to close and open the roof top
        • Fan to cool down the temperature
        • LED strip or bulb to add light during the night

           

Basically, all the inputs will send the data to the processing in order to be visualized on the processing screen. That allows monitoring of the temperature, rain existence and light in the area. Thus, all the outputs of the Arduino will receive control from the processing , as the system we are building will be controlled manually using the screen of processing or automatically using the Arduino code.

DESCRIPTION OF PROCESSING & WHAT IT WILL SEND/RECEIVE TO ARDUINO:

Processing is used for two main reasons:

-   Monitor the values given by the temperature sensor and other sensors.

-   Allow for manual control of the outputs. As we will add buttons on the screen to be able to turn on/off the outputs when we need regardless of the value.

The manual control is needed as sometimes even it is not raining we might want to close the roof top. Or even turn on / off the fan regardless if it’s too hot, even turn on and off the light regardless of the time of the day.




PROCEDURE:

RAIN FALL SENSOR:
The rain sensor detects water that comes short circuiting the tape of the printed circuits.

The sensor acts as a variable resistance that will change status : the resistance increases when the sensor is wet and the resistance is lower when the sensor is dry.

Connection:

Arduino –> Comparator

5V –> VCC

GND –> GND

DO –> D4

AO –> A0

Blueled ->pin 3

A blue LED is connected to show the presence of rain. If it’s raining, the blue LED will turn on, if it’s not raining the blue LED is off.

Code of Rain Sensor :

//RAINFALL SENSOR CODE
const int capteur_D = 4;
const int capteur_A = A0;
int blueled=2;
int val_analogique;
void setup()
{
  pinMode(capteur_D, INPUT);
  pinMode(capteur_A, INPUT);
  pinMode(blueled, OUTPUT);
  Serial.begin(9600);
}
void loop()
{
if(digitalRead(capteur_D) == LOW) 
  {
    Serial.println("Digital value : wet"); 
    delay(10); 
    digitalWrite(blueled,HIGH);
  }
else
  {
    Serial.println("Digital value : dry");
    delay(10); 
    digitalWrite(blueled,LOW);
  }
val_analogique=analogRead(capteur_A); 
 Serial.print("Analog value : ");
 Serial.println(val_analogique); 
 Serial.println("");
  delay(1000);
}
LIGHT SENSOR:
LDR light dependent resistor to have an automatic light turn on when it’s dark.


Connection

LDR: one side to 5V, other pin to analog pin A1 and to a 4.7K resistor connected to GND.

Output: LED on pin 3

Code with LDR:

const int capteur_D = 4;
const int capteur_A = A0;
int blueled=2;//for rain
int val_analogique;
int LDRsensorPin=A1;
int LDRsensorValue = 0;
int greenled = 3;//green light for ldr
void setup()
{
  pinMode(capteur_D, INPUT);
  pinMode(capteur_A, INPUT);
  pinMode(blueled, OUTPUT);
  pinMode(greenled, OUTPUT);
  Serial.begin(9600);
}

void loop()
{
if(digitalRead(capteur_D) == LOW) 
  {
    Serial.println("Digital value : wet"); 
    delay(10); 
    digitalWrite(blueled,HIGH);
  }
else
  {
    Serial.println("Digital value : dry");
    delay(10); 
    digitalWrite(blueled,LOW);
  }
val_analogique=analogRead(capteur_A); 
 Serial.print("Analog value : ");
 Serial.println(val_analogique); 
 Serial.println("");
 LDRsensorValue = analogRead(LDRsensorPin);
 if(LDRsensorValue < 600)
 {
   Serial.println("LED light on");
   digitalWrite(greenled,HIGH);Serial.println(LDRsensorValue);
   //delay(1000);
 }
 else {digitalWrite(greenled,LOW);Serial.println(LDRsensorValue);}
  delay(1000);
TEMPERATURE SENSOR & FAN:

We need a temperature sensor DS18B20 with a 4.7kohm resistor for input, as output we have a dc fan.

           If the temperature is high the fan will turn on.

           If the temperature is low the fan is off.

           Digital pin used is pin 5 for the temperature sensor

           Red led is to show if the temp above the limit.

           Redled is on pin 6

Code :

//code for temperature sensor


#include <OneWire.h>


#include <DallasTemperature.h>


#define ONE_WIRE_BUS temppin //pin in 4


OneWire oneWire(ONE_WIRE_BUS);


DallasTemperature sensors(&oneWire);


void setup()


{


  pinMode(redled, OUTPUT);


 


  Serial.begin(9600);


  //code for tempqrature


  sensors.begin();


}


void loop()


{


/********************************************************************/


//temperature sensor


sensors.requestTemperatures(); // Send the command to get temperature readings


Serial.println("Temperature is: ");


 celsius = sensors.getTempCByIndex(0);


Serial.print(celsius);


if(celsius>baselinetemp)digitalWrite(redled,HIGH);


else digitalWrite(redled,LOW);


}

STEPPER MOTOR :
The stepper motor used is a 5 V motor with a Stepper Motor with Driver (28BYJ-48 5V DC).

A stepper motor, much like a DC motor has a rotating permanent magnet propelled by stationary electrical magnets, however the motion is divided into a number of steps around the rotation of the rotating magnet. It does so by having several teeth on the rotating magnet that line up with specific locations around the stationary charged magnets. When voltage is supplied to a specific magnet or specific sequence of magnets the motor will rotate, or step to that position and hold.

Stepper motors can spin like a regular DC motor, however they can also stop on a position like a servo motor.

Code is using the library Stepper.h :

#include <Stepper.h>
// Define number of steps per rotation:
const int stepsPerRevolution = 2048;
// Wiring:
// Pin 8 to IN1 on the ULN2003 driver
// Pin 9 to IN2 on the ULN2003 driver
// Pin 10 to IN3 on the ULN2003 driver
// Pin 11 to IN4 on the ULN2003 driver
// Create stepper object called 'myStepper', note the pin order:
Stepper myStepper = Stepper(stepsPerRevolution, 8, 10, 9, 11);
void setup() {
// Set the speed to 5 rpm:
myStepper.setSpeed(5);
// Begin Serial communication at a baud rate of 9600:
Serial.begin(9600);
}
void loop() {
// Step one revolution in one direction:
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
delay(500);
// Step one revolution in the other direction:
Serial.println("counterclockwise");
myStepper.step(-stepsPerRevolution);
delay(500);
}

Code Explanation:

The Stepper.h Arduino library is included in the first step of the project. On the website, you may learn more about this library.
// Include the Arduino Stepper.h library:
#include <Stepper.h>
Then we estimated how many steps it takes the motor to complete one rotation. We'll use the motor in full-step mode in this example. This translates to 2048 steps to complete a 360-degree rotation (see motor specifications above).
// Define number of steps per rotation:
const int stepsPerRevolution = 2048;
The next step is to build a new instance of the Stepper class, which represents a specific stepper motor that is attached to the Arduino. The function Stepper(steps, pin1, pin2, pin3, pin4) is used for this, where steps is the number of steps per revolution and pin1 through pin4 are the motor's pins. Set the pins in the following order to acquire the right step sequence: 8, 10,9,11.
// Create stepper object called 'myStepper', note the pin order:
Stepper myStepper = Stepper(stepsPerRevolution, 8, 10, 9, 11);
We labeled the stepper motor 'myStepper' in this example, but you could call it 'z motor' or 'liftmotor' instead. For example ,
Stepper liftmotor = Stepper(stepsPerRevolution, 8, 10, 9, 11);
Multiple stepper motor objects with distinct names and pins can be created. This makes it simple to control two or more stepper motors at once. 

With the function, you may specify the speed in rpm in the setup by having
setSpeed(rpm).
 At 5 V, a 28byj-48 stepper motor's maximum speed is around 10-15 rpm.
void setup() {
// Set the speed to 5 rpm:
myStepper.setSpeed(5);
// Begin Serial communication at a baud rate of 9600:
Serial.begin(9600);
}
We just call the step(steps) function in the loop part of code, which rotates the motor a certain number of steps at a speed defined by the function below
setSpeed(rpm)
Passing a negative value to this function causes the motor to spin in the opposite direction.
void loop() {
// Step one revolution in one direction:
Serial.println("clockwise");
myStepper.step(stepsPerRevolution);
delay(500);
// Step one revolution in the other direction:
Serial.println("counterclockwise");
myStepper.step(-stepsPerRevolution);
delay(500);
}

Source Used : 

28byj-48-stepper-motor-arduino-tutorial




ASSEMBLY:

After having each sensor as input and component as outputs, we put everything together to assemble the project. For the processing code we based our code on the professors code :

https://github.com/aaronsherwood/introduction_interactive_media/blob/master/arduinoExamples/serialExamples/04_sendMultipleToProcessing/04_sendMultipleToProcessing.ino

In order to send multiple values from the Arduino to processing, we kept the handshake and we read the values until having the new line. The processing is to display values on the screen of the temperature, the rain status, and the light status. We created rectangles to present each input. The background color of the rectangles changes in accordance to the value. If the rain is present, the rectangle of rain changes to blue color and displays the sentence “it’s raining”. If the temperature is high or low we can see it as a graph with history of the previous 30 values. The temperature is displayed in Celsius and in Farhranayt. The LDR shows the light value so the rectangle changes color to yellow when it is dark which indicates that the light is turned on. In other words, processing was our way to visualize the data coming from the Arduino.

CONNECTION:

The stepper Motor is connected to the cover that should close when it rains. In order to have the motor control the cover we assumed the cover has a roller controller like the one found in normal curtains. So, we designed the controller that could be fitted into the stepper motor and to the cable of the roller curtain. Once the stepper motor turns clockwise, it will close the cover and if it turns anti clockwise it will open the cover
The reference to have the piece for the roller curtain is on this tutorial found below ...

https://circuitdigest.com/microcontroller-projects/arduino-based-roller-blinds-project-to-automate-and-control-window-curtains-using-nodemcu-and-stepper-motor

IMAGES OF OUR PROCEDURE :

The 3D printer that we used to print the pieces we made 


The journey of making our brainstorming session come into reality by putting the hardware of Arduino to processing connection.

The doll sized house we are in the process of making to allow you a full experience


PROGRESS VIDEO :

USER TESTING VIDEO :

FEEDBACK FROM THE TESTER :

At first without explaining our project to him , he said he was not able to figure it out, like he cant make sense of what's being presented to him. However, after I gave the general instructions of what each shall do, and the whole project's concept, it made sense. He recommends us to create a brochure or a manual as a guidelines. Another thing he suggested is maybe connect our light sensor to an actual light bulb so it creates a more realistic experience or instead remove the other LEDs so only the light one has the impression of a light bulb as the others are technical. Last, but not least, his final comment was that cause our concept is based on an outdoor experience, it will be nice if after finishing our project we test it out in the daylight.

NEXT STEPS OF IMPROVING :

We would definitely try to either remove the other less or connect to a light bulb if it works with us, and other than that add the extra features that shall be there. For instance, we are still designing our doll size house and we still haven't done the temperature connection to the fan. Our main goal is to finish up with the actual features we shall have , and if we were able to finish all that, then we may consider either removing less or adding a light bulb as mentioned above.



PROCESSING FULL CODE:

//import Serial communication library
import processing.serial.*;
//init variables
Serial myPort;
int tempC;
int tempF;
int yDist;
int rainstatus;
int[] tempHistory = new int[100];
int ldrvalue;
void setup()
{
//set the size of the window
 size(800,600);
 printArray(Serial.list());
 String portname=Serial.list()[7];
 println(portname);
 
//init serial communication port
 myPort = new Serial(this, portname, 9600);
 myPort.clear();
 myPort.bufferUntil('\n');
//fill tempHistory with default temps
 for(int index = 0; index<100; index++)
 tempHistory[index] = 0;
}

void draw()
{
 //get the temp from the serial port
 //while (myPort.available() > 0) 
 {

//refresh the background to clear old data
 background(123);
 //draw the temp rectangle
 colorMode(RGB, 160);  //use color mode sized for fading
 stroke (0);
 rect (49,120,22,162);
 //fade red and blue within the rectangle
 for (int colorIndex = 0; colorIndex <= 160; colorIndex++) 
 {
 stroke(160 - colorIndex, 0, colorIndex);
 line(50, colorIndex + 120, 70, colorIndex + 120);
 }
//draw graph
 stroke(0);
 fill(255,255,255);
 //rect(90,180,100,100);
 rect(90,120,220,150);
 for (int index = 0; index<100; index++)
 { 
 if(index == 99)
 tempHistory[index] = tempC;
 else
 tempHistory[index] = tempHistory[index + 1];
 point(90 + index, 280 - tempHistory[index]); 
 }
//write reference values
 fill(0,0,0);
textAlign(RIGHT);
text("Temperature Value",180,80);
 text("210 F", 45, 130); 
 text("32 F", 45, 287);
//draw triangle pointer
 yDist = int(260 - (160 * (tempC * 0.01)));
 stroke(0);
 triangle(75, yDist + 20, 85, yDist + 15, 85, yDist + 25);
//write the temp in C and F
 fill(0,0,0);
textAlign(LEFT);
 text(tempC + " C", 115, 137);
 tempF = ((tempC*9)/5) + 32;
 text(str(int(tempF)) + " F", 115, 165);
 
 text("Light Value",520,80);
 if(ldrvalue<400){
 fill(0,255,0);
 rect(475,120,220,150);
 fill(0,0,0);
 text("Light Value : "+ldrvalue + " .", 480,137);
 text("It's dark , light is turned ON. ", 480, 157);
 }
 else
 {fill(255,255,255);
 rect(475,120,220,150);
 fill(0,0,0);
 text("Light Value : "+ldrvalue + " .", 480, 137);
 text("It's day , light is turned OFF. ", 480, 157);}
 
 
  text("Rain Status",320,340);
 if(rainstatus==1){
   fill(0,0,255);
 rect(275,350,220,150);
 fill(0,0,0);
 text("It's Raining! The roof is closed." , 280,370);
 }
 else {fill(255,255,255);
 rect(275,350,220,150);
 fill(0,0,0);
 text("No rain ", 280,370);}
 
 }
} 

void serialEvent(Serial myPort){

  String s=myPort.readStringUntil('\n');

  s=trim(s);

  if (s!=null){

    int values[]=int(split(s,','));

    if (values.length==3){

     // tempC=(int)map(values[0],0,1023,0, width);
tempC=values[0];
ldrvalue=values[1];
      rainstatus=values[2];

    }
      println(tempC,ldrvalue,rainstatus);

  myPort.write('0');

}

  }

ARDUINO FULL CODE :

const int capteur_D = 4;

const int capteur_A = A0;

int blueled=2;//for rain

int val_analogique;

int LDRsensorPin=A1;

int LDRsensorValue = 0;

int greenled = 3;//green light for ldr

int redled=6;

int temppin=5;

int baselinetemp=25;

int ldrvaluelimit=400;

int celsius=0;

bool closed=false;
int raining=0;
//code for stepper motor

#include <Stepper.h>

const int stepsPerRevolution = 2048;

Stepper myStepper(stepsPerRevolution, 8, 10,9, 11);

 

 

//code for temperature sensor

#include <OneWire.h>

#include <DallasTemperature.h>

#define ONE_WIRE_BUS temppin //pin in 4

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature sensors(&oneWire);

 

void setup()

{

  pinMode(capteur_D, INPUT);

  pinMode(capteur_A, INPUT);

  pinMode(blueled, OUTPUT);

  pinMode(greenled, OUTPUT);

  pinMode(redled, OUTPUT);

 

myStepper.setSpeed(10);

 

  Serial.begin(9600);

  //code for tempqrature

  sensors.begin();

//handshake

  Serial.println("0,0");

}

 

void loop()

{

  /********************************************************************/

  //rain sensor

if(digitalRead(capteur_D) == LOW)

  {

   // Serial.println("Digital value : wet");
   raining=1;
    delay(10);

    digitalWrite(blueled,HIGH);

    if(!closed){//Serial.println("close: turn counterclockwise"); 

    myStepper.step(stepsPerRevolution);

    delay(1000);

    closed=true;  }

  }

else

  {raining=0;
    //Serial.println("Digital value : dry");

    delay(10);

    digitalWrite(blueled,LOW);

   if(closed){

    //Serial.println("open: turn clockwise"); 

    myStepper.step(- stepsPerRevolution);

    delay(1000);

    closed=false;}

  }

val_analogique=analogRead(capteur_A);
 //Serial.println("Analog value for rain sensor : ");

//Serial.print(val_analogique);

// Serial.println("");

delay(500);

/********************************************************************/

//ldr sendor


 LDRsensorValue = analogRead(LDRsensorPin);

if(LDRsensorValue < ldrvaluelimit)

{

  /// Serial.println("LED light on");

   digitalWrite(greenled,HIGH);//Serial.print(LDRsensorValue);

   //delay(1000);

}

else {digitalWrite(greenled,LOW);//Serial.println("LED light off,LDR value:");Serial.print(LDRsensorValue);
}

 

/********************************************************************/

//temperature sensor

sensors.requestTemperatures(); // Send the command to get temperature readings

//Serial.println("Temperature is: ");

 celsius = sensors.getTempCByIndex(0);

if(celsius>baselinetemp)digitalWrite(redled,HIGH);

else digitalWrite(redled,LOW);

 

 

  delay(1000);

//processing code

Serial.print(celsius);
Serial.print(',');
Serial.print(LDRsensorValue);
Serial.print(',');
Serial.println(raining);
/*/
if(Serial.available()>0){
char state = Serial.read ( );    // Reading the data received and saving in the state variable

if(state == '1')             // If received data is '1', then turn on LED

{ 

digitalWrite (greenled, HIGH); 

}  

if (state == '0') {     // If received data is '0', then turn off led

digitalWrite (greenled, LOW);

} 

} 

delay(50);

 */   

  }




 REFERENCES:

https://create.arduino.cc/projecthub/MisterBotBreak/how-to-use-a-rain-sensor-bcecd9

https://create.arduino.cc/projecthub/SURYATEJA/automatic-street-light-controller-27159f

https://randomnerdtutorials.com/guide-for-ds18b20-temperature-sensor-with-arduino/

https://www.aranacorp.com/en/control-a-stepper-motor-with-arduino/

https://osoyoo.com/2017/07/10/arduino-lesson-stepper-motor/

https://www.makerguides.com/28byj-48-stepper-motor-arduino-tutorial/

https://github.com/aaronsherwood/introduction_interactive_media/blob/master/arduinoExamples/serialExamples/04_sendMultipleToProcessing/04_sendMultipleToProcessing.ino

https://circuitdigest.com/microcontroller-projects/arduino-based-roller-blinds-project-to-automate-and-control-window-curtains-using-nodemcu-and-stepper-motor

Extra-credit assignment: Flappy Bird

Description:

For this assignment, I tried to recreate an Arduino version of flappy bird using pushbuttons as primary controllers.  The main idea is that the avatar (bird), which continuously moves to the right, has to move through the equally sized gaps (between the pairs of obstacles aka pipes) that are placed at random heights. The bird is affected by gravity and is always pulled down to the bottom, however, the push button allows it to make a sort of jump in the air and therefore move a bit to the top. After every successful passing through the gap, the score is incremented by one. When the avatar hits the obstacle, the game ends, the score is displayed and the user can restart the game by clicking on the second push button.

Process:
Obstacles:

To generate obstacles I have used a class that includes three parameters (float xcoord for the x-coordinate, float first and second for the y-coordinates of the obstacles) and a couple of functions. Generate() generates random values for the y-coordinate of the first obstacle, then adds 140 (for the gap) and gets the y-coordinate of the second obstacle. Create() draws the rectangles on the display window, and whenever the obstacle goes out of the screen, it gets generated again on the other side with different y-coordinates.

class Obstacle {

  float xcoord; // x coordinate
  float first = random(150,450); // top
  float second = first+140; // bottom
  
  void generate(){
    first = random(150,450);
    second = first+140;
  }
 
  void create(){
    fill(#4caf50);
    noStroke();
    rect(xcoord, 0, 80, first);
    rect(xcoord, second, 80, height);
    fill(#9bf09b);
    rect(xcoord+5, 0, 80-10, first-5);
    rect(xcoord+5, second+5, 80-10, height);
   
    if(xcoord < -80) { // if out of screen
      xcoord = width+230;
      generate();
      score++;
    }
  }
}
Movement:

Instead of making the bird move inside the display window, I used an infinite side-scrolling in which the character is static whereas the background moves from the right to the left. 

To add movement to the obstacles, I am decreasing their x-coordinate by a constant amount (speed) inside the draw() loop.

Obstacle obstacle1= new Obstacle(); // generate first obstacle
Obstacle obstacle2= new Obstacle(); // generate second obstacle
Obstacle obstacle3= new Obstacle(); // generate third obstacle
Obstacle obstacle4= new Obstacle(); // generate fourth obstacle

  obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of the first obstacle
  obstacle2.xcoord=width+((width/3)/2-40)+(width/3);  // x coordinate of the second obstacle
  obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2;  // x coordinate of the third obstacle
  obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3;  

// create all obstacles
    obstacle1.create();
    obstacle2.create();
    obstacle3.create();
    obstacle4.create();
    // move obstacles
    obstacle1.xcoord -= speed;
    obstacle2.xcoord -= speed;
    obstacle3.xcoord -= speed;
    obstacle4.xcoord -= speed;
Collisions:

To flag when the avatar touches one of the obstacles, I first used an ellipse to limit the area of the avatar, then checked if it overlapped with the obstacle. Meaning that the x and y coordinates of the avatar would be inside the obstacle area.

// check if avatar touches the obstacles
  if((avatar_ycoord-25<=obstacle1.first && obstacle1.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle1.second&& obstacle1.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle2.first && obstacle2.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle2.second && obstacle2.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle3.first && obstacle3.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle3.second && obstacle3.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle4.first && obstacle4.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle4.second && obstacle4.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  // check if avatar goes out of display window
  if(avatar_ycoord>height || avatar_ycoord<0){
    stop=true;
  }
Gravity:

The gravity is set to 3 and pulls the avatar down constantly. But when the user clicks on the button, the avatar makes a quick jump and its y-coordinate is affected.

Arduino:

On the Arduino ide, I am reading the state of the buttons, and sending them to processing using Serial.println().

int inPin = 3;  // green pushbutton
int inPin2 = 4; // yellow pushbutton
int val = 0;
int val2 = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("0,0");
}
void loop() {
  while (Serial.available()) {
    if (Serial.read() == '\n') {
       val = digitalRead(inPin); // green push button state
       val2 = digitalRead(inPin2); // yellow push button state
       // send the state of the push button to processing
       if (val == HIGH && val2== HIGH) {         
          Serial.println("1,1");
       }
       else if (val == HIGH && val2== LOW) {         
          Serial.println("1,0");
       }
       else if (val == LOW && val2== LOW) {         
          Serial.println("0,0");
       }
       else if (val == LOW && val2== HIGH) {    
          Serial.println("0,1");
       }
    }
  }
}
End game: 

When a collision occurs, or the avatar goes out of the screen, the game ends.

Restart game: 

To restart the game, the user has to click on the second push button.

View post on imgur.com

Code:
Processing:
import processing.serial.*;
Serial myPort;
PImage img; // avatar img

int speed = 3; // speed of game
int avatar_xcoord = 70; // avatar's x coordinate
int avatar_ycoord = 0;  // avatar's y coordinate
int gravity = 3; // gravity effect
boolean stop = false; // boolean var to end game
int score; // score
boolean clicked= false; // boolean var to restard game

Obstacle obstacle1= new Obstacle(); // generate first obstacle
Obstacle obstacle2= new Obstacle(); // generate second obstacle
Obstacle obstacle3= new Obstacle(); // generate third obstacle
Obstacle obstacle4= new Obstacle(); // generate fourth obstacle

void setup(){
  size(900,600);
  obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of the first obstacle
  obstacle2.xcoord=width+((width/3)/2-40)+(width/3);  // x coordinate of the second obstacle
  obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2;  // x coordinate of the third obstacle
  obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3;  // x coordinate of the fourth obstacle
  // Get response from Arduino
  printArray(Serial.list());
  String portname=Serial.list()[1];
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  // load image for avatar
  img = loadImage("avatar.png");
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    int values[]=int(split(s,','));
    // jump effect for the avatar
    if (stop==false && values[0]==1) {avatar_ycoord -= gravity*0.6;}
    // restart game
    else if (stop==true && values[1]==1) {clicked=true; print("hi");}
  }
  myPort.write("\n");
}

void draw(){
  // check if avatar touches the obstacles
  if((avatar_ycoord-25<=obstacle1.first && obstacle1.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle1.second&& obstacle1.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle2.first && obstacle2.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle2.second && obstacle2.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle3.first && obstacle3.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle3.second && obstacle3.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  if((avatar_ycoord-25<=obstacle4.first && obstacle4.xcoord<=avatar_xcoord+25) || (avatar_ycoord+25>=obstacle4.second && obstacle4.xcoord<=avatar_xcoord+25)){
    stop=true;
  }
  // check if avatar goes out of display window
  if(avatar_ycoord>height || avatar_ycoord<0){
    stop=true;
  }
  
  background(#cdeefd);
  // if game ends
  if (stop==true){
    background(#cdeefd);
    textSize(25);
    stroke(#9bf09b);
    noFill();
    strokeWeight(8);
    rect(width/2-200, height/2-150, 400, 300);
    strokeWeight(4);
    // display score
    text("Game Over!", width/2-60, height/2-70);
    text("Score: " + score, width/2-40, height/2-70 + 40);
    text("Click on yellow", width/2-75, height/2-70 + 80);
    text("button to replay!", width/2-85, height/2-70 + 120);
    // restart game and reset all parameters
    if (clicked){
      stop=false;  // reset boolean var
      clicked=false; // reset boolean var
      score=0; // reset score
      obstacle1.xcoord=width+(width/3)/2-40; // x coordinate of he first obstacle
      obstacle2.xcoord=width+((width/3)/2-40)+(width/3); // x coordinate of he second obstacle
      obstacle3.xcoord=width+((width/3)/2-40)+(width/3)*2; // x coordinate of he third obstacle
      obstacle4.xcoord=width+((width/3)/2-40)+(width/3)*3; // x coordinate of he fourth obstacle
      avatar_xcoord= 70; // x coordinate of avatar
      avatar_ycoord= 0; // y coordinate of avatar
    }
  }
  // if game starts
  if (stop==false){
    // create all obstacles
    obstacle1.create();
    obstacle2.create();
    obstacle3.create();
    obstacle4.create();
    // move obstacles
    obstacle1.xcoord -= speed;
    obstacle2.xcoord -= speed;
    obstacle3.xcoord -= speed;
    obstacle4.xcoord -= speed;
    // display the avatar
    image(img, avatar_xcoord-30, avatar_ycoord-35, 80, 70);
    avatar_ycoord += gravity;
    fill(0);
    textSize(12);
    // display the score
    text("Score: " + score, 820, 20);
  }
}

class Obstacle {

  float xcoord; // x coordinate
  float first = random(150,450); // top
  float second = first+140; // bottom
  
  void generate(){
    first = random(150,450);
    second = first+140;
  }
 
  void create(){
    fill(#4caf50);
    noStroke();
    rect(xcoord, 0, 80, first);
    rect(xcoord, second, 80, height);
    fill(#9bf09b);
    rect(xcoord+5, 0, 80-10, first-5);
    rect(xcoord+5, second+5, 80-10, height);
   
    if(xcoord < -80) { // if out of screen
      xcoord = width+230;
      generate();
      score++;
    }
  }
}
Arduino:
int inPin = 3;  // green pushbutton
int inPin2 = 4; // yellow pushbutton
int val = 0;
int val2 = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("0,0");
}
void loop() {
  while (Serial.available()) {
    if (Serial.read() == '\n') {
       val = digitalRead(inPin); // green push button state
       val2 = digitalRead(inPin2); // yellow push button state
       // send the state of the push button to processing
       if (val == HIGH && val2== HIGH) {         
          Serial.println("1,1");
       }
       else if (val == HIGH && val2== LOW) {         
          Serial.println("1,0");
       }
       else if (val == LOW && val2== LOW) {         
          Serial.println("0,0");
       }
       else if (val == LOW && val2== HIGH) {    
          Serial.println("0,1");
       }
    }
  }
}

 

Final HeartBeat Monitor/ Music (BEEPS)/ LED project

Description

Finalize your idea,  determine whether it’s going to be a group/solo arrangement,  identify the materials/space you need, and identify what you think will be hardest to complete. Post your progress on the project so far.

Idea

For the final project, I would be creating a heartbeat monitor using a pulse sensor and working with LEDs and a buzzer to generate sounds and lights according to the pulse sensor.

Materials

Pulse Sensor

Jumper Wires

Resistors

Transistors

LEDs

Arduino Board

and other resources while working on the project

Hardest Part

I think the hardest part would be controlling the way heartbeat would be displayed on the processing screen, since I haven’t done that before.

Progress

I have been waiting for the pulse sensor until today, but in the meantime, I was finding alternatives to the buzzer to create sounds. Maybe use the mac speaker and processing to do that work.

Final Project: How fast you can dance

inspiration 

From the dancing games in the arcade, where you step on pads to create sound or match the falling arrows.

Team: solo

game

The game entails 5 pads, each representing one note on the pentatonic scale. In processing, there will be five circles corresponding to the five pads. The game would give you a sequence of notes by lighting up the circles in processing and, through Arduino, the LEDs surrounding the pads. The player would have to recreate the sequence by stepping on the pads. The LEDs and the circles would light up as the player jump between the pads.

FUNCTION

Have players remember the sequence and recreate it. The sequence gets longer as the game progresses.

mATERIALS

  1. LED strips
  2. sponges (non-conductive materials)
  3. force detectors
  4. Arduino
  5. lots of wires
  6. space: a 2m x 2m space to place the pads

challenges

Codes: The hardest part would be recording what the player is doing and checking if it is the same as the displayed demo sequence.

Physical: Making the pads durable enough for people to step on them and still remain functional.

PROGRESS

I’ve been doing research about verifying and checking the notes.  I have sone codes for doing more but will need something more to meet the needs of the game.