All Posts

Final Progress Almost there!!

My final is going Really well. I have finally been able to find the right balance of code that i needed thank to the help of Nouf and Jack. i had to add a stop command after every sound to make it not repeat itself

if (!serialActive) {
    text("Press Space Bar to select Serial Port", 20, 30);
  } else {
    text("Connected", 20, 50);
  }
  

   if (Contact2 ==1 && Contact3 ==0 && Contact4 ==0){
     if (Eaudio_played == 0)
       {
         Eaudio_played = 1
          Esound.play();  
       }
  }
  else if(Contact2 ==0 && Contact3 ==1 && Contact4 ==0){
    if (Laudio_played == 0)
    {
    Laudio_played =1;
    Lsound.play();
  }
  }
  else if(Contact2 ==0 && Contact3 ==0 && Contact4 ==1){
    if (Daudio_played == 0)
    {
    Daudio_played =1;
    Dsound.play();
  }
  }
  else{
    Eaudio_played = 0;
    Laudio_played = 0
    Daudio_played = 0
  Esound.stop();
  Lsound.stop();
  Dsound.stop();
  }
 }

 

All that i have left is building the actual  project which as the professor said was not as easy as i had expected. I started off with a base of cardboard like shown.

I then tested it out with the animals i build

I then spent the whole day on the lab working on my build( special thank to Ahmed the lab assistant) where i made a stage and a platform from my animals to go on. I was unfortunately too focused and forgot to take pictures. I had crocodile clips hanging from the stage but the professor told me to switch it to using nails and have copper tape to complete the circuit.

Work left    

I am yet to add the screws and add some design aspect after that i will hopefully be ready to present

Final project documentation:Jump Pinkie!

  • Concept

The concept of my game is based on the Chrome running dinosaur game, which I adapted for my midterm project. I wanted to learn to work with sprites, so I decided that the pony will be the main theme. For the final, I adapted it more, and when the player jumps physically, the pony will jump on the screen, too.

    • Implementation 
        • Description of interaction design

      The game starts when the player clicks on the mouse and they have to connect the port by pressing the space bar. After that, the player has to jump over the little dragon and earn points by collecting a pink magic box, they have 3 lives.  The score and remaining lives are displayed at the top of the screen.

For the physical part, I connected two force resistors to Arduino, by soldering really long wire to the sensor and sticking them onto the board.

    • Description of Arduino code
      void setup(void) {
        Serial.begin(9600);   
      }
       
      void loop(void) {
        int sensor1 = analogRead(A0);
        delay(5);
        int sensor2 = analogRead(A1);
        delay(5);
        Serial.print(sensor1); 
        Serial.print(','); 
        Serial.println(sensor2); 
       
        
      }

      Arduino code is pretty simple, I just get the sensor values and send them to p5 by also adding a coma.

    • Schematic and circuit
    • Description of p5.js code
            • Because I implemented a collision library, it calculates to what extent the pony collides with a Draco, which is why depending on where/when they collide, lives change non-linearly. For example, if it doesn’t jump at all it is -1 point, but if the pony touches the Draco slightly around half a point is deducted. This gives the players grace jump, as the collision area is not too precise and bigger. And the game ends when there are no lives left.
          //creates dracos
            if (millis() >= lastFrame + frameDelay) {
              dracos.push(new Draco());
              frameDelay = random(0.6, 1) * 2500;
              lastFrame = millis();
              score++;
              
            }
          
            //draw dracos and check for collision
            for(let d of dracos){
              d.move();
              d.display();
              if(pinkiePie.hits(d)){
                lives=(lives-1/17);
                lives1=round(lives);
             
                sound1.playMode('restart');
                sound1.play();
                
              }
              else{
                sound1.playMode('sustain');
              }
           
            }
            if(lives1<=0){
                gameState='lose';
                console.log("game over");
                //gameLost=true;
                loseSound.play();
              }

          I had trouble restarting the game and realized that my lives were not updated whenever I want to restart. That is why I created a restart function that resets all the variables.

      //to restart the game and scores
      const restartGame = () => {
        gameLost=false;
        score=0;
        lives=3;
        lives1=3;
        lastFrame=0;
        frameDelay=500;
        lastFrame1=0;
        frameDelay1=200;
        dracos = [];
        stars=[];
        pinkiePie= new PinkiePie(walk);
        new Draco();
        new Star();
        loop();
      }

       

    • Description of communication between Arduino and p5.js
function readSerial(data) {
  ////////////////////////////////////
  //READ FROM ARDUINO HERE
  ////////////////////////////////////
  //console.log("data"+data);
  if (data != null) {
    
    //for button
    //if(data==0)

    let fromArduino = split(trim(data), ",");
    
    // if the right length, then proceed
    if (fromArduino.length == 2) {
      
      sensor1= fromArduino[0];
      sensor2 = fromArduino[1];
      console.log("s1 "+sensor1);
      console.log("s2 "+sensor2);
      
      
      if((sensor1 <= 1) && (sensor2<=1)){
        pinkiePie.jump();
      }
    }
   
  }
  
}

Here, I get the data and split it on comma, and store the two values in an array, assign them to two sensor variables. When a player stands on the board, there is some amount of pressure, when they jump->almost no pressure, and when they land on the board there is a force again. That is why, I made an if statement when there is no force, Pinkie Pie will jump.

    • What are some aspects of the project that you’re particularly proud of?

Overall, I am really proud of how the project turned out and the experience players have when the game is dependent on their physical activity. I am also happy that I learned how to work with another type of sensor, which means that I can work with more sensors and hardware features in the future thanks to the knowledge I got from the class. I love how sound also adds interactive experience, as well as lives implementation.

    • What are some areas for future improvement?

There is a problem with the sensitivity of the game, as players should practice and get used to the pace of the game.  This could be improved by adding more sensors or maybe trying to implement other sensors, too. I also think the game could be improved by adding more levels or speeding up the pace when players play longer.

1962 – Final Intro to IM project

Project overview:

The three of us collectively decided to make something similar to an arcade machine as it was a very exciting part of our childhoods. Therefore, the project is a collection of three games that are designed to be played using an Arduino controller. The games include Flappy Bird, a popular mobile game in which the player controls a bird and navigates through obstacles; Racing Game, in which the player controls a race car and avoids colliding with the randomly generated cars as the race car overtakes them; and Space Invaders, a classic arcade game in which the player controls a spaceship and fights against invading aliens.

Arduino to P5js communication:

int button = 2;
int pot = A0;

int button1 = 4;
int button2 = 7;
int button3 = 8;

int lastPotValue;
int lastbutton;

long previousmill = 0;
long timebutton = 500;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  pinMode(pot, INPUT);
}

int getpot(){
  int potValue = analogRead(pot)/255  ;
  int temp;
  if(potValue == 2 || potValue == 1){
    temp = 1;
  }else if(potValue == 3 || potValue == 4){
    temp = 2;
  }else{
    temp = 0;
  }
  return temp;
}

void loop() {
  int potValue = getpot();
  int buttonState = !digitalRead(button);
  long currentmill = millis();

  int buttonstate1 = !digitalRead(button1);
  int buttonstate2 = !digitalRead(button2);
  int buttonstate3 = !digitalRead(button3);

  int game = 0;

  if(buttonstate1 == 1){
    game = 1;
  }
  
  if(buttonstate2 == 1){
    game = 2;
  }
  
  if(buttonstate3 == 1){
    game = 3;
  }
  
  Serial.println(String(buttonState) + "," + String(potValue) + "," + String(game));
  if(buttonState == 1 && currentmill - previousmill >= timebutton){
    previousmill = currentmill;
    lastbutton = buttonState;
  }
}

 

We implemented this using the web serial. Here is how it briefly works:
  1.       The user connects an Arduino board to their computer using a USB cable.
  2.       The user writes and uploads a sketch (see above for the code) to the Arduino board that defines the behavior of the board and the data that it will send to the computer.
  3.     The user opens a P5.js sketch in their web browser and includes the p5.webserial.js library in their code.
  4.       The user adds event listeners to their P5.js sketch that will be called when the user connects or disconnects from the Arduino board, when the Arduino board is ready to be connected to, when there is an error communicating with the Arduino board, or when data is received from the Arduino board.
  5.       The user calls the getPorts() method of the p5.WebSerial object to check for any available Arduino boards. If an Arduino board is available, the portavailable event listener is called, which can be used to open a connection to the Arduino board.
  6.       Once the connection to the Arduino board is established, the user can send data to the Arduino board using the send() method of the p5.WebSerial object. The user can also receive data from the Arduino board using the data event listener, which is called whenever data is received from the Arduino board.
  7.       The user can use the received data from the Arduino board to control the behavior and appearance of their P5.js sketch. The user can also send data from the P5.js sketch to the Arduino board to control the behavior of the Arduino board.
  8.       When the user is finished using the Arduino board, they can close the connection to the board using the close() method of the p5.WebSerial object.

Flappy Bird:

Code:

function restart(){
  menu = 0;
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
  SCORE = 0;
  SCROLL_SPEED = 4;
  lives = 5;
}

function getRndInteger(min, max) {
  // https://www.w3schools.com/js/js_random.asp
  return Math.floor(Math.random() * (max - min)) + min;
}


function StartGame(){
  background(bg);
  fill("#7cfc00");
  rect(0, HEIGHT - GROUND_HEIGHT, WIDTH, HEIGHT);

  bird.draw();
  bird.update();
  bird.checkDeath(pipes);
  
  pipes.update();
  pipes.drawPipes();

  fill(255);
  textSize(60);
  textAlign(CENTER);
  text(SCORE, WIDTH / 9, HEIGHT / 7);
  textSize(30);
  text("lives: "+lives,WIDTH - WIDTH / 4, HEIGHT / 9);
  textAlign(CORNER);
}


class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}

The getRndInteger() function is a helper function that returns a random integer between two given values. This function is used to randomly generate the heights of the pipes in the game. The Bird and Pipes classes define the objects that appear in the game. The Bird class has a draw() method that is used to draw the bird on the screen, an update() method that is used to update the bird’s position and velocity, a flap() method that causes the bird to jump upwards, and a checkDeath() method that checks if the bird has collided with any of the pipes and ends the game if necessary. The Pipes class has an update() method that updates the positions of the pipes and a drawPipes() method that draws the pipes on the screen. Overall, the code defines a simple game in which the player controls a bird and must avoid colliding with pipes by jumping over them. The game keeps track of the player’s score and ends if the bird hits a pipe.

Racing Game:

The generateCars() function is used to randomly generate cars that appear on the screen and the displayCars() function is used to draw the cars on the screen. The displayScore() function is used to display the player’s current score on the screen. The potentiometer returns three readings: 0,1, and 2 based on the positioning. Based on the number being returned by the potentiometer – we handle the car movement.

Space Invaders: 

 

 

 

// both games =================================== function score(){ textSize(32); fill(250); text("Score: "+currentScore,20,50); } function increaseD(){ if(currentScore === 1 + prevScore){ difficulty += 0.5; prevScore = currentScore; // console.log(difficulty); } return random(1,5)+difficulty; } 

//space invadors ======================================
function startPage(){
  textSize(27);
  fill(250);
  text("Space invador",27,250);
  textSize(15);
  text("press enter to start",52,290);
}

function removeRocks(){
  rocks.splice(0,rocks.length);
  rocksctr = 0;
}



function displaybullets(){
    for(let i = 0; i < bullets.length; i++){
      bullets[i].display();
      
      if(bullets[i].y < 0){
        bullets.splice(i,1);
        numBullets--;
      }
      
    }
  // console.log(numBullets);
}

function generaterocks(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          rocks[rocksctr] = new boulders();
          rocks[rocksctr].display();
          // console.log(rocksctr);
          rocksctr++;
        }
     }
  }
}

function displayrocks(){
  for(let i = 0; i < rocks.length; i++){
    rocks[i].display();
    // console.log(">",rocks.length);
    
    let temp = false;
    for(let j = 0; j < bullets.length; j++){
      if(bullets[j].didcollide(rocks[i])){
        temp = true;
        bullets.splice(i,1);
        numBullets--;
      }
    }
    
    if(mainship.didcollide(rocks[i])){
      rocks.splice(i,1);
      rocksctr--;
      gamestatus = "end";
      bomb.play();
      losing1.play();
      
    }else if(rocks[i].y > height || temp){
      rocks.splice(i,1);
      rocksctr--;
    }
  }
}
 var timechecker = 0.5;

function makebullet(x,y){

  // console.log(x);
  bullets[numBullets] = new bulletClass(x,y);
  m0 = millis(); //time when laser created 
  if(timechecker>0.3){
  bullets[numBullets].display();}
  numBullets++;
  m = millis();
  timechecker = m - m0;
} //tried to disable continous shooting maybe look into it later 



// end space invadors ================================

// start racing car game functions=====================
function startPage2(){
  textSize(27);
  fill(250);
  text("Car racing",63,255);
  textSize(15);
  text("press enter to start",52,290);
}


function generateCars(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0 && carrs.length < 4){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          carrs[carsctr] = new cars();
          carrs[carsctr].display();
          // console.log(carsctr);
          carsctr++;
        }
     }
  }
}

function displayCars(){
  for(let i = 0; i < carrs.length; i++){
    carrs[i].display();
    // console.log(">",carrs.length);
    
    let temp = false;
    
    if(maincar.didcollide(carrs[i])){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore = 0;
      // bomb.play();
      gamestatus = "end";
      losing2.play();
      // gamestatus = "end";
      // bomb.play();
    }else if(carrs[i].y > height || temp){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore++;
      cargoing.play();
    }
  }
}


function checklanes(x,other){
  
  if(x === 1){
     if(lanes2[other.temp] === 1){
       other.temp = int(random(0,4));
       other.x = lanes[other.temp];
       checklanes(1,other);
     }else{
       lanes2[other.temp] = 1;
     }
  }else if(x === 0){
    lanes2[other.temp] = 0;
  }
}

function removeCars(){
  carrs.splice(0,carrs.length);
  carsctr = 0;
}


Initialization:

we initialized a lot of variables that would be used by the serial and the three games. the pre load function was also used to prepare the necessary pictures and sounds as well as fonts. the we set up what was necessary in the set up function.

let whichgame = 0;
let whichgameprev = 0;
// space invadors
let mainship;
let bullets = [];
let numBullets = 0;
let arrRocks;
let rocks = []
let lasersound;
let rocksctr = 0;

let difficulty = 0; // both games
let currentScore = 0;  //both games
let prevScore = 0;  //both games
let gamestatus = "start"; // both games
let rate = 0; // both games
let widthh = 400;

//racing game
let arrCars;
let carrs = [];
let carsctr = 0;
let lanes = [8,88,168,248,328];
let lanes2 = [0,0,0,0,0];


//flappy bird
var menu = 0; 
var SCROLL_SPEED = 4;
var SCORE = 0;
let oof; 
let bruh;
let music; 
var bird ;
var pipes;
var lives = 5;
const GRAVITY = 8.81;
const JUMP_HEIGHT = 6.0;
const GROUND_HEIGHT = 20; 
const WIDTH = 600;
const HEIGHT = 550;

//-------------arduino----------------
// let x=0;
// var c; 
let values = [];
// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial(); 

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

function preload() {
  main = loadImage('mainpicture.png');
  
  //space invadar
  img = loadImage('Hs4QN2.gif');
  startScreen = loadImage('startscreen.gif');
  ship = loadImage('Untitled-1.png');
  bullet = loadImage('bullet.png');
  soundFormats('mp3', 'ogg');
  lasersound = loadSound('lasersound.mp3');
  bomb = loadSound('explo.mp3');
  rock1 = loadImage('rock1.png');
  rock2 = loadImage('rock2.png');
  rock3 = loadImage('rock3.png');
  gameoverpng = loadImage('gameover.png');
  mainFont = loadFont('PressStart2P-vaV7.ttf');
  alternateFont = loadFont('metal lord.otf');
  losing2 = loadSound('losing2.wav');
  arcade = loadSound('arcade.mp3');  
  sp = loadSound('space.wav');
  
  //racing car game;
  imgg = loadImage('maincar.png');
  car1 = loadImage('car1.png');
  car2 = loadImage('car2.png');
  car3 = loadImage('car3.png');
  backgroundd = loadImage('background.png');
  backgrounddd = loadImage('final.gif');
  cargoing = loadSound('mixkit-fast-car-drive-by-1538.wav');
  startscreen = loadImage('startscreen.png');
  done = loadImage('gameovercar.png');
  losing1 = loadSound('losing1.wav');
  carpassing = loadSound('carpassing.wav');
  extraedge = loadImage('extraedge.png');
  
  //flappy bird
  music = loadSound("bgmusic.mp3");
  bg = loadImage('bg11.png');
  home = loadImage('homescreem.png');
  b = loadImage('bird.png');
  jump = loadSound('flap-1.mp3');
  oof = loadSound('oof.mp3');
  
}


function setup() {
  
  createCanvas(600, 550);
  arcade.play();
  
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  
  //space invadors
  mainship = new spaceship();
  arrRocks = [rock1,rock2,rock3] ;
  textFont(mainFont);
  
  //racing
  maincar = new main_car();
  arrCars = [car1,car2,car3] ;
  
  //flappy bird
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
}

The draw function: this is where all the functions get called:

The draw() function starts by clearing the background of the canvas with background(0), then it checks the value of whichgame and renders the appropriate game. The code uses several other functions, such as controls(), score(), and startPage(), to handle game mechanics and display game elements.

In the first if statement, the code checks if whichgame is equal to 0, and if so, it displays two images: main and extraedge and this is like the default screen. In the second if statement, the code checks if whichgame is equal to 1 and, if so, it displays the game for whichgame 1 which is space invadors. This game has several possible states (running, end, start) and the code uses if statements to handle each state. The third and fourth if statements do the same thing for games 2 and 3, car race and flappy bird respectively.

function draw() {
  //console.clear();
  var y=int(values[0]); //button value 1 or 0 
  var x = int(values[1]); //potentiometer reading 0,1,2
  var prewhichgame= int(values[2]);
  if(prewhichgame != whichgame && prewhichgame != 0 &&!isNaN(prewhichgame)){
    whichgame = prewhichgame;
  }
  values.splice(0);
  background(0);
  if(whichgame != whichgameprev){
    gamestatus = "start";
    whichgameprev = whichgame;
    currentScore = 0;
    difficulty = 0;
    prevScore = 0;
    restart();
  }

  if(whichgame == 0){ 
    
    
    image(main,0,0);
    image(extraedge,400,0);

    
  }if(whichgame == 1){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(img, 0, 0);
      controls(x,y);
      mainship.display();
      displaybullets();
      generaterocks();
      displayrocks();
      score();
    }else if(gamestatus == "end"){
      image(gameoverpng, 0, 0);
      removeRocks();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
      controls2(x,y);
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage();
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 2){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(backgrounddd,0,0);
      maincar.display();
      controls(x,y);
      generateCars();
      displayCars();
      score();
    }else if(gamestatus == "end"){
      image(done, 0, 0);
      controls2(x,y);
      
      removeCars();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage2();
      // background(startscreen);    
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 3){
    arcade.pause();
    if(menu==0){
      controls2(x,y);
      
      background(home);   
      textSize(25);
      textFont()
    }else if(menu==1){
      StartGame();
      controls(x,y);
    }
  }
  
}

the classes: 

The boulders class represents the falling boulders, and the bulletClass class represents bullets that the player can shoot to destroy the boulders. The spaceship class represents the player’s spaceship, and the cars class represents the cars that the player must avoid. The main_car class is a subclass of spaceship, and it appears to have the same functionality as the spaceship class.

The boulders class has a display() function that is used to draw the boulder on the screen, a move() function that is used to update the boulder’s position, and a width() function that is used to determine the width of the boulder. The bulletClass class has a display() function that is used to draw the bullet on the screen, a move() function that is used to update the bullet’s position, and a didcollide(other) function that is used to check if the bullet has collided with another object.

The spaceship and main_car classes have a display() function that is used to draw the spaceship or car on the screen, a move() function that is used to update the spaceship or car’s position, and a didcollide(other) function that is used to check if the spaceship or car has collided with another object. The cars class has the same functions as the boulders class, but it is used to represent cars rather than boulders.

// space invador classes start
class boulders{
  constructor(){
    this.x = random(0,widthh-50);
    this.y = -20;
    this.rocktype =  int(random(0, 3));
    this.rateFall = increaseD();
  }
  display(){
    image(arrRocks[this.rocktype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    if(this.rocktype == 0){
      return 71;
    }else if(this.rocktype == 1){
      return 48;
    }else if(this.rocktype == 2){
      return 91;
    }
  }
 
  
}



class bulletClass{
  constructor(x,y){
    this.x = x;
    this.y = y;
    lasersound.play();
    this.collision = false;
  }
  display(){
    image(bullet,this.x,this.y);
    this.move();
  }
  move(){
    this.y -= 7;
  }
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        currentScore++;
        return true;
      }      
      
    }
  }
  
}


class spaceship{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(ship,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//start racing car classes: ================================

class cars{
  constructor(){
    this.temp = int(random(0,5));
    this.x = lanes[this.temp];
    this.y = -20;
    this.cartype =  int(random(0, 3));
    this.rateFall = increaseD();
    //checklanes(1,this); commenting this cuz you've used recursion its causing stack overflow theres no base case here  
  }
  
  display(){
    image(arrCars[this.cartype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    return 70;
  }
 
  
}





class main_car{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(imgg,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (90 + other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//end racing car classes=========================



//flappy bird classes==================================

class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}
// end flappy bird classes =============================

 

Game Controls

Flappy Bird: Use the button on the arduino or the UP key to jump.

Racing Game: Use the potentiometer or left and right keys to control the car’s steering.

Space Invaders: Use the potentiometers or left and right keys to control the spaceship’s movement and button or UP key to fire lasers.

user testing:

 

Conclusion

This project demonstrates how to create and play games using p5.js and Arduino. The project includes three games that can be controlled using potentiometers and push buttons, and can be easily extended to include additional games and custom controller designs. We’re particularly proud of the aesthetics of the games – we were able to recreate what we initally had in mind. Furthermore, we had a lot of bugs which wouldn’t let the games run smoothly. We figured out how to implement the games smoothly by making changes in our algorithms and by handling specific types of errors which were mostly given by the arduino board. However, there is no proper restart function for flappy bird – if you lose, the canvas simply starts from scratch.

Work contributed: Car racing game, Space invaders, while helping in integrating the games together, and using my graphic design skills in making 1962 more user friendly. Also helped making the circuit necessary to send data.

 

Final Project – 1962

Project overview:

The three of us collectively decided to make something similar to an arcade machine as it was a very exciting part of our childhoods. Therefore, the project is a collection of three games that are designed to be played using an Arduino controller. The games include Flappy Bird, a popular mobile game in which the player controls a bird and navigates through obstacles; Racing Game, in which the player controls a race car and avoids colliding with the randomly generated cars as the race car overtakes them; and Space Invaders, a classic arcade game in which the player controls a spaceship and fights against invading aliens.

Arduino to P5js communication:

int button = 2;
int pot = A0;

int button1 = 4;
int button2 = 7;
int button3 = 8;

int lastPotValue;
int lastbutton;

long previousmill = 0;
long timebutton = 500;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  pinMode(pot, INPUT);
}

int getpot(){
  int potValue = analogRead(pot)/255  ;
  int temp;
  if(potValue == 2 || potValue == 1){
    temp = 1;
  }else if(potValue == 3 || potValue == 4){
    temp = 2;
  }else{
    temp = 0;
  }
  return temp;
}

void loop() {
  int potValue = getpot();
  int buttonState = !digitalRead(button);
  long currentmill = millis();

  int buttonstate1 = !digitalRead(button1);
  int buttonstate2 = !digitalRead(button2);
  int buttonstate3 = !digitalRead(button3);

  int game = 0;

  if(buttonstate1 == 1){
    game = 1;
  }
  
  if(buttonstate2 == 1){
    game = 2;
  }
  
  if(buttonstate3 == 1){
    game = 3;
  }
  
  Serial.println(String(buttonState) + "," + String(potValue) + "," + String(game));
  if(buttonState == 1 && currentmill - previousmill >= timebutton){
    previousmill = currentmill;
    lastbutton = buttonState;
  }
}

 

We implemented this using the web serial. Here is how it briefly works:
  1.       The user connects an Arduino board to their computer using a USB cable.
  2.       The user writes and uploads a sketch (see above for the code) to the Arduino board that defines the behavior of the board and the data that it will send to the computer.
  3.     The user opens a P5.js sketch in their web browser and includes the p5.webserial.js library in their code.
  4.       The user adds event listeners to their P5.js sketch that will be called when the user connects or disconnects from the Arduino board, when the Arduino board is ready to be connected to, when there is an error communicating with the Arduino board, or when data is received from the Arduino board.
  5.       The user calls the getPorts() method of the p5.WebSerial object to check for any available Arduino boards. If an Arduino board is available, the portavailable event listener is called, which can be used to open a connection to the Arduino board.
  6.       Once the connection to the Arduino board is established, the user can send data to the Arduino board using the send() method of the p5.WebSerial object. The user can also receive data from the Arduino board using the data event listener, which is called whenever data is received from the Arduino board.
  7.       The user can use the received data from the Arduino board to control the behavior and appearance of their P5.js sketch. The user can also send data from the P5.js sketch to the Arduino board to control the behavior of the Arduino board.
  8.       When the user is finished using the Arduino board, they can close the connection to the board using the close() method of the p5.WebSerial object.

Flappy Bird:

Code:

function restart(){
  menu = 0;
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
  SCORE = 0;
  SCROLL_SPEED = 4;
  lives = 5;
}

function getRndInteger(min, max) {
  // https://www.w3schools.com/js/js_random.asp
  return Math.floor(Math.random() * (max - min)) + min;
}


function StartGame(){
  background(bg);
  fill("#7cfc00");
  rect(0, HEIGHT - GROUND_HEIGHT, WIDTH, HEIGHT);

  bird.draw();
  bird.update();
  bird.checkDeath(pipes);
  
  pipes.update();
  pipes.drawPipes();

  fill(255);
  textSize(60);
  textAlign(CENTER);
  text(SCORE, WIDTH / 9, HEIGHT / 7);
  textSize(30);
  text("lives: "+lives,WIDTH - WIDTH / 4, HEIGHT / 9);
  textAlign(CORNER);
}


class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}

The getRndInteger() function is a helper function that returns a random integer between two given values. This function is used to randomly generate the heights of the pipes in the game. The Bird and Pipes classes define the objects that appear in the game. The Bird class has a draw() method that is used to draw the bird on the screen, an update() method that is used to update the bird’s position and velocity, a flap() method that causes the bird to jump upwards, and a checkDeath() method that checks if the bird has collided with any of the pipes and ends the game if necessary. The Pipes class has an update() method that updates the positions of the pipes and a drawPipes() method that draws the pipes on the screen. Overall, the code defines a simple game in which the player controls a bird and must avoid colliding with pipes by jumping over them. The game keeps track of the player’s score and ends if the bird hits a pipe.

Racing Game:

The generateCars() function is used to randomly generate cars that appear on the screen and the displayCars() function is used to draw the cars on the screen. The displayScore() function is used to display the player’s current score on the screen. The potentiometer returns three readings: 0,1, and 2 based on the positioning. Based on the number being returned by the potentiometer – we handle the car movement.

Space Invaders: 

 

 

 

// both games =================================== function score(){ textSize(32); fill(250); text("Score: "+currentScore,20,50); } function increaseD(){ if(currentScore === 1 + prevScore){ difficulty += 0.5; prevScore = currentScore; // console.log(difficulty); } return random(1,5)+difficulty; } 

//space invadors ======================================
function startPage(){
  textSize(27);
  fill(250);
  text("Space invador",27,250);
  textSize(15);
  text("press enter to start",52,290);
}

function removeRocks(){
  rocks.splice(0,rocks.length);
  rocksctr = 0;
}



function displaybullets(){
    for(let i = 0; i < bullets.length; i++){
      bullets[i].display();
      
      if(bullets[i].y < 0){
        bullets.splice(i,1);
        numBullets--;
      }
      
    }
  // console.log(numBullets);
}

function generaterocks(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          rocks[rocksctr] = new boulders();
          rocks[rocksctr].display();
          // console.log(rocksctr);
          rocksctr++;
        }
     }
  }
}

function displayrocks(){
  for(let i = 0; i < rocks.length; i++){
    rocks[i].display();
    // console.log(">",rocks.length);
    
    let temp = false;
    for(let j = 0; j < bullets.length; j++){
      if(bullets[j].didcollide(rocks[i])){
        temp = true;
        bullets.splice(i,1);
        numBullets--;
      }
    }
    
    if(mainship.didcollide(rocks[i])){
      rocks.splice(i,1);
      rocksctr--;
      gamestatus = "end";
      bomb.play();
      losing1.play();
      
    }else if(rocks[i].y > height || temp){
      rocks.splice(i,1);
      rocksctr--;
    }
  }
}
 var timechecker = 0.5;

function makebullet(x,y){

  // console.log(x);
  bullets[numBullets] = new bulletClass(x,y);
  m0 = millis(); //time when laser created 
  if(timechecker>0.3){
  bullets[numBullets].display();}
  numBullets++;
  m = millis();
  timechecker = m - m0;
} //tried to disable continous shooting maybe look into it later 



// end space invadors ================================

// start racing car game functions=====================
function startPage2(){
  textSize(27);
  fill(250);
  text("Car racing",63,255);
  textSize(15);
  text("press enter to start",52,290);
}


function generateCars(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0 && carrs.length < 4){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          carrs[carsctr] = new cars();
          carrs[carsctr].display();
          // console.log(carsctr);
          carsctr++;
        }
     }
  }
}

function displayCars(){
  for(let i = 0; i < carrs.length; i++){
    carrs[i].display();
    // console.log(">",carrs.length);
    
    let temp = false;
    
    if(maincar.didcollide(carrs[i])){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore = 0;
      // bomb.play();
      gamestatus = "end";
      losing2.play();
      // gamestatus = "end";
      // bomb.play();
    }else if(carrs[i].y > height || temp){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore++;
      cargoing.play();
    }
  }
}


function checklanes(x,other){
  
  if(x === 1){
     if(lanes2[other.temp] === 1){
       other.temp = int(random(0,4));
       other.x = lanes[other.temp];
       checklanes(1,other);
     }else{
       lanes2[other.temp] = 1;
     }
  }else if(x === 0){
    lanes2[other.temp] = 0;
  }
}

function removeCars(){
  carrs.splice(0,carrs.length);
  carsctr = 0;
}


Initialization:

we initialized a lot of variables that would be used by the serial and the three games. the pre load function was also used to prepare the necessary pictures and sounds as well as fonts. the we set up what was necessary in the set up function.

let whichgame = 0;
let whichgameprev = 0;
// space invadors
let mainship;
let bullets = [];
let numBullets = 0;
let arrRocks;
let rocks = []
let lasersound;
let rocksctr = 0;

let difficulty = 0; // both games
let currentScore = 0;  //both games
let prevScore = 0;  //both games
let gamestatus = "start"; // both games
let rate = 0; // both games
let widthh = 400;

//racing game
let arrCars;
let carrs = [];
let carsctr = 0;
let lanes = [8,88,168,248,328];
let lanes2 = [0,0,0,0,0];


//flappy bird
var menu = 0; 
var SCROLL_SPEED = 4;
var SCORE = 0;
let oof; 
let bruh;
let music; 
var bird ;
var pipes;
var lives = 5;
const GRAVITY = 8.81;
const JUMP_HEIGHT = 6.0;
const GROUND_HEIGHT = 20; 
const WIDTH = 600;
const HEIGHT = 550;

//-------------arduino----------------
// let x=0;
// var c; 
let values = [];
// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial(); 

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

function preload() {
  main = loadImage('mainpicture.png');
  
  //space invadar
  img = loadImage('Hs4QN2.gif');
  startScreen = loadImage('startscreen.gif');
  ship = loadImage('Untitled-1.png');
  bullet = loadImage('bullet.png');
  soundFormats('mp3', 'ogg');
  lasersound = loadSound('lasersound.mp3');
  bomb = loadSound('explo.mp3');
  rock1 = loadImage('rock1.png');
  rock2 = loadImage('rock2.png');
  rock3 = loadImage('rock3.png');
  gameoverpng = loadImage('gameover.png');
  mainFont = loadFont('PressStart2P-vaV7.ttf');
  alternateFont = loadFont('metal lord.otf');
  losing2 = loadSound('losing2.wav');
  arcade = loadSound('arcade.mp3');  
  sp = loadSound('space.wav');
  
  //racing car game;
  imgg = loadImage('maincar.png');
  car1 = loadImage('car1.png');
  car2 = loadImage('car2.png');
  car3 = loadImage('car3.png');
  backgroundd = loadImage('background.png');
  backgrounddd = loadImage('final.gif');
  cargoing = loadSound('mixkit-fast-car-drive-by-1538.wav');
  startscreen = loadImage('startscreen.png');
  done = loadImage('gameovercar.png');
  losing1 = loadSound('losing1.wav');
  carpassing = loadSound('carpassing.wav');
  extraedge = loadImage('extraedge.png');
  
  //flappy bird
  music = loadSound("bgmusic.mp3");
  bg = loadImage('bg11.png');
  home = loadImage('homescreem.png');
  b = loadImage('bird.png');
  jump = loadSound('flap-1.mp3');
  oof = loadSound('oof.mp3');
  
}


function setup() {
  
  createCanvas(600, 550);
  arcade.play();
  
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  
  //space invadors
  mainship = new spaceship();
  arrRocks = [rock1,rock2,rock3] ;
  textFont(mainFont);
  
  //racing
  maincar = new main_car();
  arrCars = [car1,car2,car3] ;
  
  //flappy bird
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
}

The draw function: this is where all the functions get called:

The draw() function starts by clearing the background of the canvas with background(0), then it checks the value of whichgame and renders the appropriate game. The code uses several other functions, such as controls(), score(), and startPage(), to handle game mechanics and display game elements.

In the first if statement, the code checks if whichgame is equal to 0, and if so, it displays two images: main and extraedge and this is like the default screen. In the second if statement, the code checks if whichgame is equal to 1 and, if so, it displays the game for whichgame 1 which is space invadors. This game has several possible states (running, end, start) and the code uses if statements to handle each state. The third and fourth if statements do the same thing for games 2 and 3, car race and flappy bird respectively.

function draw() {
  //console.clear();
  var y=int(values[0]); //button value 1 or 0 
  var x = int(values[1]); //potentiometer reading 0,1,2
  var prewhichgame= int(values[2]);
  if(prewhichgame != whichgame && prewhichgame != 0 &&!isNaN(prewhichgame)){
    whichgame = prewhichgame;
  }
  values.splice(0);
  background(0);
  if(whichgame != whichgameprev){
    gamestatus = "start";
    whichgameprev = whichgame;
    currentScore = 0;
    difficulty = 0;
    prevScore = 0;
    restart();
  }

  if(whichgame == 0){ 
    
    
    image(main,0,0);
    image(extraedge,400,0);

    
  }if(whichgame == 1){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(img, 0, 0);
      controls(x,y);
      mainship.display();
      displaybullets();
      generaterocks();
      displayrocks();
      score();
    }else if(gamestatus == "end"){
      image(gameoverpng, 0, 0);
      removeRocks();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
      controls2(x,y);
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage();
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 2){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(backgrounddd,0,0);
      maincar.display();
      controls(x,y);
      generateCars();
      displayCars();
      score();
    }else if(gamestatus == "end"){
      image(done, 0, 0);
      controls2(x,y);
      
      removeCars();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage2();
      // background(startscreen);    
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 3){
    arcade.pause();
    if(menu==0){
      controls2(x,y);
      
      background(home);   
      textSize(25);
      textFont()
    }else if(menu==1){
      StartGame();
      controls(x,y);
    }
  }
  
}

the classes: 

The boulders class represents the falling boulders, and the bulletClass class represents bullets that the player can shoot to destroy the boulders. The spaceship class represents the player’s spaceship, and the cars class represents the cars that the player must avoid. The main_car class is a subclass of spaceship, and it appears to have the same functionality as the spaceship class.

The boulders class has a display() function that is used to draw the boulder on the screen, a move() function that is used to update the boulder’s position, and a width() function that is used to determine the width of the boulder. The bulletClass class has a display() function that is used to draw the bullet on the screen, a move() function that is used to update the bullet’s position, and a didcollide(other) function that is used to check if the bullet has collided with another object.

The spaceship and main_car classes have a display() function that is used to draw the spaceship or car on the screen, a move() function that is used to update the spaceship or car’s position, and a didcollide(other) function that is used to check if the spaceship or car has collided with another object. The cars class has the same functions as the boulders class, but it is used to represent cars rather than boulders.

// space invador classes start
class boulders{
  constructor(){
    this.x = random(0,widthh-50);
    this.y = -20;
    this.rocktype =  int(random(0, 3));
    this.rateFall = increaseD();
  }
  display(){
    image(arrRocks[this.rocktype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    if(this.rocktype == 0){
      return 71;
    }else if(this.rocktype == 1){
      return 48;
    }else if(this.rocktype == 2){
      return 91;
    }
  }
 
  
}



class bulletClass{
  constructor(x,y){
    this.x = x;
    this.y = y;
    lasersound.play();
    this.collision = false;
  }
  display(){
    image(bullet,this.x,this.y);
    this.move();
  }
  move(){
    this.y -= 7;
  }
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        currentScore++;
        return true;
      }      
      
    }
  }
  
}


class spaceship{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(ship,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//start racing car classes: ================================

class cars{
  constructor(){
    this.temp = int(random(0,5));
    this.x = lanes[this.temp];
    this.y = -20;
    this.cartype =  int(random(0, 3));
    this.rateFall = increaseD();
    //checklanes(1,this); commenting this cuz you've used recursion its causing stack overflow theres no base case here  
  }
  
  display(){
    image(arrCars[this.cartype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    return 70;
  }
 
  
}





class main_car{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(imgg,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (90 + other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//end racing car classes=========================



//flappy bird classes==================================

class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}
// end flappy bird classes =============================

 

Game Controls

Flappy Bird: Use the button on the arduino or the UP key to jump.

Racing Game: Use the potentiometer or left and right keys to control the car’s steering.

Space Invaders: Use the potentiometers or left and right keys to control the spaceship’s movement and button or UP key to fire lasers.

Conclusion

This project demonstrates how to create and play games using p5.js and Arduino. The project includes three games that can be controlled using potentiometers and push buttons, and can be easily extended to include additional games and custom controller designs. We’re particularly proud of the aesthetics of the games – we were able to recreate what we initally had in mind. Furthermore, we had a lot of bugs which wouldn’t let the games run smoothly. We figured out how to implement the games smoothly by making changes in our algorithms and by handling specific types of errors which were mostly given by the arduino board. However, there is no proper restart function for flappy bird – if you lose, the canvas simply starts from scratch.

My contribution to the project was coding the handling the serial communication and the design of the physical user interface. I did some of the error handling and debugging towards the end as well.

 

Final project:user testing

Progress

As I was testing the game, I understood that one sensor is not enough as data gets into the game randomly depending on where is the user. That is why I decided to connect two sensors as well as get a board for jumping. I had a lot of stress getting data from two sensors as it was getting lost somewhere or as far as I understood, there was just an extra line.😭

Schematic

Circuit

User testing video

Results:

It seems like shoes can be a little obstacle on how legs are pressed on the board, as it might be difficult to center the pressure of the legs with shoes. If the legs are pressed correctly every time a person jumps, the game is pretty responsive.

Final Project

Concept

For my final project, I made a radio that changes FM channels according to what value range you turn the potentiometer dial to. I inputted song files that play according to their designated serial monitor value and an array of different songs for each channel.

This includes:

  • 51-240 (Hip Hop), which is designated the variable RED on p5
  • 241-429 (History), variable YELLOW on p5
  • 430-619 (Classic Rock), variable GREEN on p5
  • 620-814 (Classical), variable TEAL on p5
  • 815-1023 (Khaleeji), variable BLUE on p5

For 0-50 Radio is switched off (variable OFF on p5)

Implementation and Interaction

So, whenever you turn the dial to 267, for example, an audio file from the History channel will start to play, and the yellow LED will simultaneously light up to indicate the change in channels.

The interactive element comes from you turning the dial to change the channel, and being able to control the output according to what value you stop on.

Arduino Code

int potPin = A5; // potentiometer
int bluePin = 2;
int tealPin = 3;
int greenPin = 4;
int yellowPin = 5;
int redPin = 6;
int currentColor = 0; // current color
int OFF = 0;
int RED = 6;
int YELLOW = 5;
int GREEN = 4;
int TEAL = 3;
int BLUE = 2;

void setup() {
  pinMode (potPin, INPUT);
  pinMode (bluePin, OUTPUT);
  pinMode (tealPin, OUTPUT);
  pinMode (greenPin, OUTPUT);
  pinMode (yellowPin, OUTPUT);
  pinMode (redPin, OUTPUT);
  Serial.begin (9600); // serial monitor count
}

void loop() {

  // x == y (x is equal to y)
  // x != y (x is not equal to y)
  // x <  y (x is less than y)
  // x >  y (x is greater than y)
  // x <= y (x is less than or equal to y)
  // x >= y (x is greater than or equal to y)

  delay(100);
  int potMeasure = analogRead (A5);
  int mappedPot = map(potMeasure, 0, 1023, 0, 255);
  Serial.println(currentColor);

  if ((potMeasure > 0) && (potMeasure <= 50)) {
    currentColor = 0; 
    digitalWrite (redPin, LOW);
    digitalWrite (yellowPin, LOW);
    digitalWrite (greenPin, LOW);
    digitalWrite (tealPin, LOW);
    digitalWrite (bluePin, LOW);
    int OFF = 0;
  }
  else if ((potMeasure > 50) && (potMeasure <= 240)) {
    currentColor = 6;
    digitalWrite (redPin, HIGH);
    digitalWrite (yellowPin, LOW);
    digitalWrite (greenPin, LOW);
    digitalWrite (tealPin, LOW);
    digitalWrite (bluePin, LOW);
    int RED = 1;
  }
  else if ((potMeasure > 240) && (potMeasure < 430)) {
    currentColor = 5;
    digitalWrite (yellowPin, HIGH);
    digitalWrite (redPin, LOW);
    digitalWrite (greenPin, LOW);
    digitalWrite (tealPin, LOW);
    digitalWrite (bluePin, LOW);
    int YELLOW = 2;
  }
  else if ((potMeasure >= 430) && (potMeasure < 620)) {
    currentColor = 4;
    digitalWrite (greenPin, HIGH);
    digitalWrite (redPin, LOW);
    digitalWrite (yellowPin, LOW);
    digitalWrite (tealPin, LOW);
    digitalWrite (bluePin, LOW);
    int GREEN = 3;
  }
  else if ((potMeasure >= 620) && (potMeasure < 815)) {
    currentColor = 3;
    digitalWrite (tealPin, HIGH);
    digitalWrite (redPin, LOW);
    digitalWrite (yellowPin, LOW);
    digitalWrite (greenPin, LOW);
    digitalWrite (bluePin, LOW);
    int TEAL = 4;
  }

  else if ((potMeasure >= 815) && (potMeasure <= 1023)) {
    currentColor = 2;
    digitalWrite (bluePin, HIGH);
    digitalWrite (redPin, LOW);
    digitalWrite (yellowPin, LOW);
    digitalWrite (greenPin, LOW);
    digitalWrite (tealPin, LOW);
    int BLUE = 5;
  }
}

For my Arduino code, I essentially made it so that whenever the integer potMeasure (potentiometer value) is between a range of values, it would trigger the designated pin to light up.

For example, this segment of code here shows that whenever the potMeasure value is between or equivalent to 817 and 1023, all other pins but the bluePin are on LOW (to indicate them being switched off), and the bluePin is on HIGH (to indicate it being switched on). Each colored pin stands for each colored LED on the board.

else if ((potMeasure >= 817) && (potMeasure <= 1023)) { 
currentColor = 2; 
digitalWrite (bluePin, HIGH); 
digitalWrite (redPin, LOW); 
digitalWrite (yellowPin, LOW); 
digitalWrite (greenPin, LOW); 
digitalWrite (tealPin, LOW); 
int BLUE = 5; 
}

The currentColor integer encompasses each of the potMeasure ranges for each LED to light up, hence why if the serial monitor is between or equivalent to 815 and 1023, the value 2 would pop up as it is the designated value for the blue LED.

int potMeasure = analogRead (A5); 
int mappedPot = map(potMeasure, 0, 1023, 0, 255); 
Serial.println(currentColor);

p5 Code

For p5, I first uploaded all the audio files for each channel to function preload and designated each channel an array of audio files that p5 can randomly choose from.

soundFormats("mp3");
 // BLUE (KHALEEJI)
blueSong1 = loadSound ("MEHAD.mp3");
blueSong2 = loadSound ("UMY KM AHWAHA.mp3")
blueSounds = [blueSong1, blueSong2] // etc

I then designated a new variable called oldData in function draw, which holds the previous value of inData (serial monitor value/ potMeasure on Arduino).  This was done so that whenever the user changes the channel, p5 would stop the song from playing so as to not interrupt the next song.

// oldData holds the *previous* value of inData

// if user changes station, p5 checks that oldData has changed into inData
// then prints the change
if (inData != oldData) {
  // The station changed
   console.log("changed from " + oldData + " to " + inData);
  
  // stops all songs if inData (new station value) is not equal to oldData (old station value)
  redSong1.stop();
  redSong2.stop();
  yellowSong1.stop();
  yellowSong2.stop();
  greenSong1.stop();
  tealSong1.stop();
  blueSong1.stop();
  blueSong2.stop();

I then created an if statement for each inData value (BLUE in this context is the Khaleeji channel. Within each statement, there would be a new variable initializing the random selection of songs from the designated array, which it would then play.

if (inData == BLUE){
  khaleejiChannel = random(blueSounds)
  khaleejiChannel.play()
}

I then added more if statements further below within function draw, which consisted of the font and text to be displayed on the screen every time a channel is changed.

else if (inData == BLUE) { // KHALEEJI
    
    // CHANNEL NAME TEXT
    noStroke();
    fill (128,172,57); // text color
    textFont(radioFont, 45); // (font name, font size)
    text("KHALEEJI", 178, 148); // (text, x, y)
    glow(color(128,172,57), 19); // (color, intensity) calls glow function
    
  }

The glow variable refers to the glow function further below, which makes the text look like it’s lit up.

Audiovisual Communication between Arduino and p5

Arduino and p5 input:

  • Potentiometer values

Arduino output (visual):

  • LEDs lighting up

p5 output (audio):

  • Songs being played

p5 Screen Design

Arduino Board

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Video Demonstration

Click here to watch video demonstration

Highlights

  • I’m proud of the fact that I was able to make an array for each channel without the program lagging.
  • Figuring out the C++ coding for Arduino was challenging, but now I understand it enough to be able to modify it to include any last-minute changes.
  • The p5 screen design (which I made on procreate).
  • The glow function, which I was able to apply using this video tutorial:

Click here

Improvement

  • Some other visual demonstration that indicates what channel you’re on. I would have liked for the background to possibly show the sound waves of the song playing and in the color of the designated channel.
  • I also wanted to do something more for the physical presentation of the Arduino board, though I was able to customize it just a little using some card paper.

Final project progress update

Process

I finally connected Arduino to my game and before setting up the jumping board, I tried checking whether the force resistor works and if so, how it works.  I searched on the internet how to connect the force resistor sensor to Arduino and get the data.

Schematic

I followed this schematic to hook up the resistor:

wiring fsr to arduino

Arduino code:

int fsrPin = 0;     // the FSR and 10K pulldown are connected to a0
int fsrReading;     // the analog reading from the FSR resistor divider
 
void setup(void) {
  Serial.begin(9600);   
}
 
void loop(void) {
  fsrReading = analogRead(fsrPin);  
 
  Serial.print("Analog reading = ");
  Serial.print(fsrReading); 
}

Next steps:

  • Work on the jumpability of the game
  • Set up the jumping area->work on the sensitivity of the sensors
  • Fix the bugs with lives

Final Project Documentation

Concept

Choosing to take Intro to IM as my first coding experience was akin to taking a giant leap for me. Yet I am surprised how bit by bit this class cleared so many concepts and provided me with the hands-on experience I needed in coding platforms. For my final project, as promised and discussed in previous posts, I tried to create a painting/drawing platform by connecting P5js to Arduino. Interactive Media in itself is a form of creative expression and I wanted this project to be a platform for a digital form of artistic expression. This attempt at coding as etch-a-sketch is an ode to that childish playfulness that often gets subdued as we grow up.

Arduino setup-

Revised console-setup-

Implementation

The project mainly includes two potentiometers and a switch. The values sent from the first potentiometer map the vertical movement of the ellipse that is drawn based on its R-value (RGB color). It moves the ellipse up and down. Similarly, the movement of the second potentiometer maps the horizontal value of the ellipse and makes it go left and right. The way in which the first potentiometer controls the r-value, the second potentiometer also adjusts the alpha value of the ellipse that is drawn. when both the potentiometers are moved together, the user is able to draw curved lines as well. The third main component is the switch which when pressed clears the canvas for a new sketch to be drawn.

The p5.js component is crucial to the functioning of the program since it handles everything from sending data to the Arduino to displaying the design that is drawn in the most effective way. Three functions are primarily added in the p5js code. They include draw, readSerial, and frame. The draw function is where the ellipse is drawn and moved along the canvas. The readSerial function establishes the serial connection between the Arduino port and the p5js sketch when the space bar is pressed by the user within the canvas based on bilateral handshaking. After pressing the space bar, the port selection dialog box pop-ups from which the suitable port is selected. A global variable called the “buttonState” is defined at the beginning of the code that clears the canvas every time the digital switch is pressed or in other words when its state changes from 0 to 1.

Finally, the frame function deals with the aesthetics of the project. It draws the authentic etch-a-sketch style background to make the screen look more inviting. I have also looped a whimsical song that plays in the background every time the serial connection is established and as long as the program is running.

The Arduino side mainly executes the commands that are sent by the p5js sketch. The analog sensors (potentiometers) are read and then their values are printed similarly through the addition of a local variable within the loop function, the digital values being sent from the switch are read. The pin mode has been added in the setup as the input. The other digital and analog values are added and read in the loop function.

Arduino Code

// Inputs:
// - A0 - first potentiomemter 
// - A1 - second potentiomemter
// - 2  - switch 


void setup() {
  // Serial communication is started to send the data
  Serial.begin(9600);

   pinMode(2, INPUT); 


  // bidirectional communication starts
  while (Serial.available() <= 0) {
    Serial.println("0,0"); // send a starting message
  }
}

void loop() {
  // waits to receive data from p5js first and then starts executing
  while (Serial.available()) {

    int left = Serial.parseInt();
    int right = Serial.parseInt();
    if (Serial.read() == '\n') {
      int sensor = analogRead(A0);
      delay(5);
      int sensor2 = analogRead(A1);
      delay(5);
      int button = digitalRead(2);
      delay (5);

      Serial.print(sensor);
      Serial.print(',');
      Serial.print(sensor2);
      Serial.print(',');
      Serial.println(button);
    }
  }
}

P5js sketch and Code

Components I am Proud of

Overall, I am quite pleased with my humble outcome as it performs its bilateral communication functions quite smoothly. I am particularly proud of how the addition of mapping the alpha values of the ellipses that are being drawn on the screen gives a special “sketchy” effect to the project. Moreover, as we toggle the speed of the potentiometers the density of the stroke changes which is quite similar to the manner in which strokes that are drawn by a sketch pen change with varying pressure.

A few Sketches and user interaction-

Blind Contouring a portrait-


Plotting the map of India-

User interaction-

 

Future Improvements

I would have loved to add another switch in the project that allowed the user to change the color of the stroke from black to another color. But through my best possible attempts in the given time frame, I was able to add a switch that changes the color of the stroke when it is being continuously pressed. This beat its practical purpose though since the user would have to use both of his hands to toggle the potentiometers and at the same time keep pressing the switch.

Final Progress

I have my circuit ready with 2 buttons: one for start/restart & another for “collect”, and a potentiometer to inflate the balloon. I wanted to use a pressure sensor for blowing but the readings weren’t too sensitive to human blowing and it wasn’t too accurate. So, I decided to keep it simple and make it like an arcade game with hand interaction only. I edited my code to read the potentiometer values directly from Arduino.

while (Serial.available()) {
    int left = Serial.parseInt();
    int right = Serial.parseInt();
    if (Serial.read() == '\n') {
      digitalWrite(2, left);
      digitalWrite(5, right);
      int sensor = analogRead(A0); //potentiometer 
      delay(1);
      int sensor2 = digitalRead(7); //start button

The challenging part was having a steady serial connection from Arduino. I realized that it would go faster if there are equal number of data coming in and out of Arduino (even though I am not sending anything back to Arduino). I consider my project 80% done and hope to have more progress until the showcase.

Final Project DJ Falcon – Q M and Daniel

So, here it is! After fixing hundreds of errors and spending countless man hours, we finally have our project– DJ Falcon!

DJ Falcon:

Using Arduino and p5js, we created DJ Falcon, an audio mixer set that modifies tracks and makes everyone jam to the tunes. The mixer also has a visualizer whose waves sync with the audio being played.

The user is welcomed with this screen:

You can then choose a song from the list to mix:

Then you rotate the knobs on the mixer to shake things up!

The Process:

There are two parts to this code – the p5js part and the Arduino part.

Components Used:

Ph a000066 iso (1) ztbmubhmho Arduino UNO board

12002 04 Breadboard

09939 01 Rotary Potentiometers (2)

19c7994 40 Push down Switch (1)

Mfr 25frf52 10k sml Resistors

11026 02 Jumper Wires

Apps and Online Services:

Ide web  Arduino IDE Making p5.js Accessible. by Luis Morales-Navarro and Mathura… | by Processing Foundation | Processing Foundation | Medium p5js

The Process:

We started the project by drawing out everything first in a notebook. Here’s the very first (very rough) sketch of our idea:

I decided to work on the interface for the mixer and took care of the code and fixing the issues that came up, while QM was going to put together the circuit and the hardware. After setting up the basic circuit on the breadboard and building a very basic interface with buttons on p5js, we had to connect the both.

We then incorporated the p5js sound library in our p5 code. We used the built in reverb, delay, frequency functions and tested those out together by uploading an mp3 file on p5js. It was all working fine. We had to then figure out how to take values from the potentiometer, map them, and use them as arguments for the sound functions.

But before that, we had to write code for reading the values from the pin. QM got to writing the code for the Arduino IDE while Daniel got to figuring out how to take those inputs in p5js. This is where we hit our first roadblock. We just could not figure out the correct p5js library for serial communication and also couldn’t figure out how to separate the values we got from the potentiometer and store them in variables. With a little help from Professor Ang, we finally figured out the right library and used a list to store our values from where we used split() and trim() function to separate the values from each input. This is the bit of code that helped us do it:

function serialEvent() {
if (serial.available() > 0) {
serialData = serial.readLine();
if (serialData) {
// false if serialData == null
console.log(serialData);
serialData = trim(serialData); // remove trailing whitespace
serialValues = split(serialData, ","); // split the string to array
console.log("serialValues ", serialValues);
rev_value = Number(serialValues[0]);
console.log("Rev: " + rev_value);
rate_value = Number(serialValues[1]);
console.log("Rate: " + rate_value);
play_value = Number(serialValues[2]);
console.log("Play/pause: " + play_value);
standby_value = Number(serialValues[3]);
console.log("Standby: " + standby_value);
rev_mapped = map(rev_value, 0, 1023, 0, 100);
rate_mapped = map(rate_value, 0, 1023, 0, 200);
console.log("rev mapped: " + rev_mapped);
console.log("rate mapped: " + rate_mapped);
}
}
}

It’s also worth mentioning that some of the code for the serial connection were reusable code snippets written by Professor Sherwood and Ang from class examples.

This is how our board looked:

Once we connected the Arduino board and had the serial connection going, it was now time to map the values to fit into the arguments. I had a pretty solid grip on the ranges for the different music functions like the dryness and wetness of reverb, due to being a music major, and had a good understanding of how our random values would translate to what we hear (QM also ended up learning a fair bit about music at this point from Daniel).

But even then, it wasn’t smooth sailing. Although we had an understanding of the values, p5 was playing its sly tricks with us with the inner working (or should I say, inner loopings!) of the draw() function causing a haphazard in the music where multiple tracks were playing by overlapping each other and it sounded more like a cacophony than a vibey night at a club.

One evening, after working for more than 5 hours continuously trying to solve these, we decided to call it a night and come back the next day.

Maybe sleeping on the bugs helped, because the next day, after the both of us worked simultaneously on our own laptops trying to fix the bugs, QM had a slight hint of a breakthrough when one of the buttons successfully paused the music without looping. Still it wouldn’t make it play again.

But then…(drumrolls) a moment of genius from Daniel and some tweaking of the code and suddenly everything worked! Words fail to describe how victorious we felt at that moment:

Now it was time to put everything together. QM, with a noob-level experience in soldering decided to solder the board together and Daniel would give a finishing touch to the interface, incorporating a dope background, cool fonts and a cleaner look. Here’s some of that scrappy soldering work:

And here’s Daniel’s work on the interface:

We were both happy with the font and the look of the mixer and the DJ Falcon mixer was ready.

The Schematic:

Code: Arduino IDE

const int switchPin1 = 2;
const int switchPin2 = 3;
void setup() {
Serial.begin(9600);
pinMode(switchPin1, INPUT); // initialize serial communications
pinMode(switchPin2, INPUT);
}

void loop() {
// read the input pin:
int pot = analogRead(A0);
int mappedPot = map(pot, 0, 1023, 0, 255);
Serial.print(pot);
Serial.print(",");
int potRev = analogRead(A1);
int mappedtest = map(potRev, 0, 1023, 0, 255);
Serial.print(potRev);
Serial.print(",");
int button1 = digitalRead(switchPin1);
int button2 = digitalRead(switchPin2);
Serial.print(button1);
Serial.print(",");
Serial.println(button2);
delay(1);
delay(100);
}

Code for p5js:

Improvements:

We had more ambitious ideas in mind like using the delay() feature and a standby button but those were causing bugs due to the draw() function of p5js, but we’re sure these can be implemented in the future.