[Badr & Ziyad] Week 10: Music Instrument

For this week’s assignment, Badr and I tried to build a simple musical instrument using a servo to emulate a simple drum and a buzzer to play a melody.

Drum: 

We defined three different drum modes (beats) that can be looped over using a pushbutton. Every time the button is clicked, the drum moves to the next mode. Originally, we planned to use two servos (drums), but it turned out noisier than we thought, so we decided to use only one. To move the servos, we have used servo.write().

Millis():

To avoid using delay() which will affect the interaction, we have used Millis to set breaks. For that, we had to create an array to store the duration of the breaks, variables to store the elapsed time, and if conditions. 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
unsigned long curr = millis(); // store the elapsed time
bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes
int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks)
int curr_prd=0; // keep track of the current break
unsigned long curr = millis(); // store the elapsed time bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks) int curr_prd=0; // keep track of the current break
unsigned long curr = millis(); // store the elapsed time
bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes
int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks)
int curr_prd=0; // keep track of the current break

Beats (Modes): 

To alternate between the modes, we have used if/else conditions, boolean variables, digitalRead() to read the state of the push button and a prevButton variable to store the previous state.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// switch between the modes
// default mode
if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){
level2=1;
period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200;
}
// mode 3
else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){
level3=0;
period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300;
}
// mode 2
else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){
level3=1; level2=0;
period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60;
}
// switch between the modes // default mode if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){ level2=1; period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200; } // mode 3 else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){ level3=0; period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300; } // mode 2 else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){ level3=1; level2=0; period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60; }
// switch between the modes
  // default mode
  if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){
    level2=1;
    period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200;
  }
  // mode 3
  else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){
    level3=0;
    period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300;
  }
  // mode 2
  else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){
    level3=1; level2=0;
    period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60;
  }

Moving the Servo:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// set the tempo for level 3
if (curr-prev>=period[curr_prd] && level2==0 && level3==1){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// set the tempo for level 2
if (curr-prev>=period[curr_prd] && level2==1 && level3==0){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// set the tempo for the default level
if (curr-prev>=period[curr_prd] && level2==0 && level3==0){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// Move the servo
if (played[0]){servo_r.write(90);}
if (played[1]){servo_r.write(120);}
if (played[2]){servo_r.write(100);}
if (played[3]){servo_r.write(120);}
// set the tempo for level 3 if (curr-prev>=period[curr_prd] && level2==0 && level3==1){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // set the tempo for level 2 if (curr-prev>=period[curr_prd] && level2==1 && level3==0){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // set the tempo for the default level if (curr-prev>=period[curr_prd] && level2==0 && level3==0){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // Move the servo if (played[0]){servo_r.write(90);} if (played[1]){servo_r.write(120);} if (played[2]){servo_r.write(100);} if (played[3]){servo_r.write(120);}
// set the tempo for level 3
  if (curr-prev>=period[curr_prd] && level2==0 && level3==1){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // set the tempo for level 2
  if (curr-prev>=period[curr_prd] && level2==1 && level3==0){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // set the tempo for the default level
  if (curr-prev>=period[curr_prd] && level2==0 && level3==0){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // Move the servo
  if (played[0]){servo_r.write(90);}
  if (played[1]){servo_r.write(120);}
  if (played[2]){servo_r.write(100);}
  if (played[3]){servo_r.write(120);}

Buzzer: 

We chose to play the tetris theme on the buzzer, and used the ultrasonic sensor to change the speed at which the melody is being played. The larger the values read by the sensor are, the slower the music will play. 

Melody:

To play the melody, we stored the convenient notes in an array (we got them online), then proceeded to calculate how long the note is using sizeof() and the tempo which is 160.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
int tempo = 160; // Tempo of the song // Melody notes
int melody[] = { NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_A5, NOTE_GS5 };
// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2;
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;
// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2;
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;
int tempo = 160; // Tempo of the song // Melody notes int melody[] = { NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_A5, NOTE_GS5 }; // Calculate the duration of each note int notes= sizeof(melody) / sizeof(melody[0]) / 2; int note = (60000 * 4) / tempo; int divider = 0, duration = 0; // Calculate the duration of each note int notes= sizeof(melody) / sizeof(melody[0]) / 2; int note = (60000 * 4) / tempo; int divider = 0, duration = 0;
int tempo = 160; // Tempo of the song // Melody notes 
int melody[] = { NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_A5, NOTE_GS5 };

// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2; 
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;

// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2; 
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;

Ultrasonic Sensor:

Depending on the distance measured by the sensor, the speed of the song is changing. For that, we used map() to modify it within a certain range. We also had to convert microseconds to cm.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// get the distance using the ultrasonic sensor
long duration, cm;
digitalWrite(9, LOW);
delayMicroseconds(2);
digitalWrite(9, HIGH);
delayMicroseconds(10);
digitalWrite(9, LOW);
duration = pulseIn(8, HIGH);
// convert microseconds to cm
cm = duration/29/2;
if (cm>20) {
cm = 20;
}
// map the distance form 80 to 150 to fit
cm = map(cm,0,20,80,150);
// get the distance using the ultrasonic sensor long duration, cm; digitalWrite(9, LOW); delayMicroseconds(2); digitalWrite(9, HIGH); delayMicroseconds(10); digitalWrite(9, LOW); duration = pulseIn(8, HIGH); // convert microseconds to cm cm = duration/29/2; if (cm>20) { cm = 20; } // map the distance form 80 to 150 to fit cm = map(cm,0,20,80,150);
// get the distance using the ultrasonic sensor
  long duration, cm;
  digitalWrite(9, LOW);
  delayMicroseconds(2);
  digitalWrite(9, HIGH);
  delayMicroseconds(10);
  digitalWrite(9, LOW);
  duration = pulseIn(8, HIGH);
  // convert microseconds to cm
  cm = duration/29/2;
  if (cm>20) {
   cm = 20;
  }
  // map the distance form 80 to 150 to fit 
  cm = map(cm,0,20,80,150);

Challenges: 

The biggest challenge we faced was getting the drum to work without using delay(). Using delay() would have affected the interaction with the button and sensor, so we used millis() instead. When we tried to implement the millis() solution, the drum’s playing started to become irregular and sometimes stopped working after some time. To fix this, we used arrays to keep track of the angles and timings of each angle change of the servo. The code started working properly after we introduced this fix and restructured the logic we used in the loop.

We also faced some issues with taping of our servo and stick coming loose occasionally while working on the project.

Code: 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include <Servo.h>
#include "notes.h"
Servo servo_r;
unsigned long prev=0; // variable to store the time
bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes
int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks)
int curr_prd=0; // keep track of the current break
bool level2=0; // Mode 2
bool level3=0; // Mode 3
bool prevbutton=0; // store the previous state of the pushbutton
int prevnote = 0; // store the prev note
int currnote = 0; // store the current note
int tempo = 160; // Tempo of the song
// Melody notes
int melody[] = {
NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4,
NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5,
NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4,
NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5,
NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5,
NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5,
NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5,
NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4,
NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4,
NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4,
NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5,
NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5,
NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5,
NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5,
NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4,
REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5,
NOTE_A5, NOTE_GS5
};
// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2;
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;
void setup() {
Serial.begin(9600);
servo_r.attach(6); // Set the servo
pinMode(3, INPUT); // Set the button pinmode
pinMode(9, OUTPUT); // set the echo pin for the sensor
pinMode(8, INPUT); // set the pin for the sensor
}
void loop() {
// switch between the modes
// default mode
if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){
level2=1;
period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200;
}
// mode 3
else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){
level3=0;
period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300;
}
// mode 2
else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){
level3=1; level2=0;
period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60;
}
unsigned long curr = millis(); // store the elapsed time
// set the tempo for level 3
if (curr-prev>=period[curr_prd] && level2==0 && level3==1){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// set the tempo for level 2
if (curr-prev>=period[curr_prd] && level2==1 && level3==0){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// set the tempo for the default level
if (curr-prev>=period[curr_prd] && level2==0 && level3==0){
prev=curr;
// change the array parameters
if (played[0]==1){played[0]=0; played[1]=1;}
else if (played[1]==1){played[1]=0; played[2]=1;}
else if (played[2]==1){played[2]=0; played[3]=1;}
else if (played[3]==1){played[3]=0; played[0]=1;}
curr_prd=(curr_prd+1)%4;
}
// Move the servo
if (played[0]){servo_r.write(90);}
if (played[1]){servo_r.write(120);}
if (played[2]){servo_r.write(100);}
if (played[3]){servo_r.write(120);}
// read the state of the button
prevbutton = digitalRead(3);
// get the distance using the ultrasonic senor
long duration, cm;
digitalWrite(9, LOW);
delayMicroseconds(2);
digitalWrite(9, HIGH);
delayMicroseconds(10);
digitalWrite(9, LOW);
duration = pulseIn(8, HIGH);
// convert microseconds to cm
cm = duration/29/2;
if (cm>20) {
cm = 20;
}
// map the distance form 80 to 150 to fit
cm = map(cm,0,20,80,150);
if (curr-prevnote >= cm) {
prevnote = curr;
// play the note
tone(11, melody[currnote], duration*0.9);
// loop over the array
currnote = (currnote + 1) % 100;
}
}
#include <Servo.h> #include "notes.h" Servo servo_r; unsigned long prev=0; // variable to store the time bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks) int curr_prd=0; // keep track of the current break bool level2=0; // Mode 2 bool level3=0; // Mode 3 bool prevbutton=0; // store the previous state of the pushbutton int prevnote = 0; // store the prev note int currnote = 0; // store the current note int tempo = 160; // Tempo of the song // Melody notes int melody[] = { NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4, REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5, NOTE_A5, NOTE_GS5 }; // Calculate the duration of each note int notes= sizeof(melody) / sizeof(melody[0]) / 2; int note = (60000 * 4) / tempo; int divider = 0, duration = 0; void setup() { Serial.begin(9600); servo_r.attach(6); // Set the servo pinMode(3, INPUT); // Set the button pinmode pinMode(9, OUTPUT); // set the echo pin for the sensor pinMode(8, INPUT); // set the pin for the sensor } void loop() { // switch between the modes // default mode if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){ level2=1; period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200; } // mode 3 else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){ level3=0; period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300; } // mode 2 else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){ level3=1; level2=0; period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60; } unsigned long curr = millis(); // store the elapsed time // set the tempo for level 3 if (curr-prev>=period[curr_prd] && level2==0 && level3==1){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // set the tempo for level 2 if (curr-prev>=period[curr_prd] && level2==1 && level3==0){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // set the tempo for the default level if (curr-prev>=period[curr_prd] && level2==0 && level3==0){ prev=curr; // change the array parameters if (played[0]==1){played[0]=0; played[1]=1;} else if (played[1]==1){played[1]=0; played[2]=1;} else if (played[2]==1){played[2]=0; played[3]=1;} else if (played[3]==1){played[3]=0; played[0]=1;} curr_prd=(curr_prd+1)%4; } // Move the servo if (played[0]){servo_r.write(90);} if (played[1]){servo_r.write(120);} if (played[2]){servo_r.write(100);} if (played[3]){servo_r.write(120);} // read the state of the button prevbutton = digitalRead(3); // get the distance using the ultrasonic senor long duration, cm; digitalWrite(9, LOW); delayMicroseconds(2); digitalWrite(9, HIGH); delayMicroseconds(10); digitalWrite(9, LOW); duration = pulseIn(8, HIGH); // convert microseconds to cm cm = duration/29/2; if (cm>20) { cm = 20; } // map the distance form 80 to 150 to fit cm = map(cm,0,20,80,150); if (curr-prevnote >= cm) { prevnote = curr; // play the note tone(11, melody[currnote], duration*0.9); // loop over the array currnote = (currnote + 1) % 100; } }
#include <Servo.h>
#include "notes.h"

Servo servo_r;
unsigned long prev=0; // variable to store the time
bool played[] = {1, 0, 0, 0}; // boolean variables to control the modes
int period[]= {200, 150, 150, 200}; // Tempo of the servo (breaks)
int curr_prd=0; // keep track of the current break
bool level2=0; // Mode 2
bool level3=0; // Mode 3
bool prevbutton=0; // store the previous state of the pushbutton
int prevnote = 0; // store the prev note
int currnote = 0; // store the current note
int tempo = 160; // Tempo of the song
// Melody notes
int melody[] = {
  NOTE_E5, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4,
  NOTE_A4, NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5,
  NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4,
  NOTE_A4, NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5,
  NOTE_A5, NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5,
  NOTE_D5, NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5,
  NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, 
  NOTE_B4, NOTE_C5, NOTE_D5, NOTE_C5, NOTE_B4, NOTE_A4,
  NOTE_A4, NOTE_C5, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_B4,
  NOTE_C5, NOTE_D5, NOTE_E5, NOTE_C5, NOTE_A4, NOTE_A4,
  NOTE_A4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_F5, NOTE_A5,
  NOTE_G5, NOTE_F5, NOTE_E5, NOTE_C5, NOTE_E5, NOTE_D5,
  NOTE_C5, NOTE_B4, NOTE_B4, NOTE_C5, NOTE_D5, NOTE_E5,
  NOTE_C5, NOTE_A4, NOTE_A4, REST, NOTE_E5, NOTE_C5,
  NOTE_D5, NOTE_B4, NOTE_C5, NOTE_A4, NOTE_GS4, NOTE_B4,
  REST, NOTE_E5, NOTE_C5, NOTE_D5, NOTE_B4, NOTE_C5, NOTE_E5,
  NOTE_A5, NOTE_GS5
};
// Calculate the duration of each note
int notes= sizeof(melody) / sizeof(melody[0]) / 2; 
int note = (60000 * 4) / tempo;
int divider = 0, duration = 0;


void setup() {
  Serial.begin(9600);
  servo_r.attach(6); // Set the servo
  pinMode(3, INPUT); // Set the button pinmode
  pinMode(9, OUTPUT); // set the echo pin for the sensor
  pinMode(8, INPUT); // set the pin for the sensor
}

void loop() {
  // switch between the modes
  // default mode
  if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==0){
    level2=1;
    period[0] = 200; period[1] = 150; period[2] = 150; period[3] = 200;
  }
  // mode 3
  else if (!prevbutton && digitalRead(3)==HIGH && level2==0 && level3==1){
    level3=0;
    period[0] = 300; period[1] = 300; period[2] = 300; period[3] = 300;
  }
  // mode 2
  else if (!prevbutton && digitalRead(3)==HIGH && level2==1 && level3==0){
    level3=1; level2=0;
    period[0] = 60; period[1] = 60; period[2] = 60; period[3] = 60;
  }

  unsigned long curr = millis(); // store the elapsed time

  // set the tempo for level 3
  if (curr-prev>=period[curr_prd] && level2==0 && level3==1){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // set the tempo for level 2
  if (curr-prev>=period[curr_prd] && level2==1 && level3==0){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // set the tempo for the default level
  if (curr-prev>=period[curr_prd] && level2==0 && level3==0){
    prev=curr;
    // change the array parameters
    if (played[0]==1){played[0]=0; played[1]=1;}
    else if (played[1]==1){played[1]=0; played[2]=1;}
    else if (played[2]==1){played[2]=0; played[3]=1;}
    else if (played[3]==1){played[3]=0; played[0]=1;}
    curr_prd=(curr_prd+1)%4;
  }
  // Move the servo
  if (played[0]){servo_r.write(90);}
  if (played[1]){servo_r.write(120);}
  if (played[2]){servo_r.write(100);}
  if (played[3]){servo_r.write(120);}

  // read the state of the button
  prevbutton = digitalRead(3);

  // get the distance using the ultrasonic senor
  long duration, cm;
  digitalWrite(9, LOW);
  delayMicroseconds(2);
  digitalWrite(9, HIGH);
  delayMicroseconds(10);
  digitalWrite(9, LOW);
  duration = pulseIn(8, HIGH);
  // convert microseconds to cm
  cm = duration/29/2;
  if (cm>20) {
   cm = 20;
  }
  // map the distance form 80 to 150 to fit 
  cm = map(cm,0,20,80,150);

  if (curr-prevnote >= cm) {
    prevnote = curr;
    // play the note
    tone(11, melody[currnote], duration*0.9);
    // loop over the array
    currnote = (currnote + 1) % 100;
  }
}

Leave a Reply