Published

COI - Beat Keeper

A beat training machine, which the user can program with a custom beat with two different sensory inputs.

BeginnerFull instructions provided518
COI - Beat Keeper

Things used in this project

Hardware components

Static Mat
×1
Computer
×1
USB-A to Mini-USB Cable
USB-A to Mini-USB Cable
×2
Jumper wires (generic)
Jumper wires (generic)
×4
Breadboard (generic)
Breadboard (generic)
×1
LED (generic)
LED (generic)
Yellow, Green, Red
×3
Pin Cables
×4
Resistor 1k ohm
Resistor 1k ohm
×3
Seeed Studio RGB Backlight LCD
×1
Seeed Studio Rotary Angle Sensor
×1
Seeed Studio Sound Sensor
×1
Seeed Studio Touch Sensor
×1

Story

Read more

Code

Beat_Keeper.ino

Arduino
#include <Wire.h>
#include <rgb_lcd.h>
#include <math.h>
rgb_lcd lcd;

#define S_SENS A3
#define T_SENS 8
#define ROT_SENS A0

int yellowPinNum = 3;
int greenPinNum = 5;
int redPinNum = 6;

int thresholdSound = 400; //Out of 1024

//minTime and maxTime can be customized.
int minTime = 250;//Corresponds to 240 bpm
int maxTime = 1000;//Corresponds to 60 bpm
int beatTime = maxTime;

long long lastMillis = 0;

double score = 1.0; //This is a value kept between 0 and 1 which is affected by your most recent performance.
double memoryPersistance = .8; //This value is customizable and should be between 0 and 1;

char touchCode = 'T';
char soundCode = 'S';
char bothCode = 'B';
char silentCode = '-';
String beat = "S-T-T-T-"; //This is customizable to produce a new beat. Beats can be of arbitrary length.
int unitsPerBeat = 2; //Customizable, is the number of characters in beat per "beat"
int beatIndex = 0;

boolean touchedLastTime = false;
boolean soundLastTime = false;
boolean scoreIntegrated = false;

void setup() {
  // Make Sure to Initialize the Pins for Output:
  pinMode(yellowPinNum, OUTPUT);
  pinMode(greenPinNum, OUTPUT);
  pinMode(redPinNum, OUTPUT);

  //Set up LCD screen
  lcd.begin(16,2);
  lcd.setRGB(255,255,255);

  // Set the pinMode of the inputs
  pinMode(S_SENS, INPUT);
  pinMode(T_SENS, INPUT);
  pinMode(ROT_SENS, INPUT);
  
  //Safety:
  unitsPerBeat = max(unitsPerBeat, 1);
}

void loop() {
  //Check for a beat rollover
  boolean rollOver = false;
  if(millis()-lastMillis > beatTime){
    lastMillis = lastMillis + beatTime;
    rollOver = true;
    beatIndex = (beatIndex + 1)%beat.length();
  }
  if(millis() < lastMillis){
    lastMillis = millis();
    rollOver = true;
    beatIndex = (beatIndex + 1)%beat.length();
  }

  //Read speed preference and display on LCD.
  if(rollOver){
    beatTime = minTime+(int)(analogRead(ROT_SENS)/1024.0 * (maxTime-minTime));
    double bpm = 60.0*1000.0/beatTime;
    lcd.setCursor(0,0);
    lcd.print("                ");//Clear Screen
    lcd.setCursor(0,1);
    lcd.print("                ");
    lcd.setCursor(0,0);
    lcd.print("BPM: ");
    lcd.print(bpm);
    beatTime = beatTime/unitsPerBeat;
  }

  //Check to see if in first quarter. If so, turn on appropriate LED's, and check for touches.
  if(millis()-lastMillis < beatTime/3){
    scoreIntegrated = false; //Means soundLastTime, touchedLastTime have potentially new values;

    if(beatIndex == 0){
      analogWrite(yellowPinNum, 0); //Full brightness on first beat.
    } 
    else if (beatIndex%unitsPerBeat==0){
      analogWrite(yellowPinNum, 200); //Lower Brightness.
    }//End if is first beat
    if(beat[beatIndex]==touchCode || beat[beatIndex] == bothCode){
      analogWrite(greenPinNum, 0);
    }
    if(beat[beatIndex]==soundCode || beat[beatIndex]==bothCode){
      analogWrite(redPinNum, 0);
    }

    if(digitalRead(T_SENS)==1){
      touchedLastTime=true;
    }
    if(analogRead(S_SENS)>thresholdSound){
      soundLastTime=true;
    }
  } 
  else if(millis()-lastMillis > (beatTime * 2)/3){
    scoreIntegrated = false; //Means soundLastTime, touchedLastTime have potentially new values;

    if(digitalRead(T_SENS)==1){
      touchedLastTime=true;
    }
    if(analogRead(S_SENS)>thresholdSound){
      soundLastTime=true;
    }
  } 
  else{
    analogWrite(yellowPinNum, 255);//Inverted for some reason.
    analogWrite(greenPinNum, 255);
    analogWrite(redPinNum, 255);

    if(scoreIntegrated == false){
      double correctness = 0;
      if(beat[beatIndex]==bothCode && touchedLastTime && soundLastTime){
        correctness=1;
      } 
      else if(beat[beatIndex]==touchCode && touchedLastTime && !soundLastTime){
        correctness=1;
      } 
      else if(beat[beatIndex]==soundCode && !touchedLastTime && soundLastTime){
        correctness=1;
      } 
      else if(beat[beatIndex]==silentCode && !touchedLastTime && !soundLastTime){
        correctness=score;
      }
      
      score = score * memoryPersistance + correctness*(1-memoryPersistance);

      lcd.setRGB((int)(255*(1-score)),(int)(255 * score),0);

      lcd.setCursor(0,1);
      lcd.print((int)(score*100));
      lcd.print("%");

      scoreIntegrated = true;
      touchedLastTime=false;
      soundLastTime=false;
    }
  }
}

Credits

Comments