03 March 2026

Midi Instrument and Recorder

 


This Project uses 1 potentiometer and 2 buttons. The potentiometer is turned for the specific note you'd like to play. The first button is pressed when you'd like to play that note. The final button is pressed when you'd like to record a max of 4 notes in a row to be played at regular intervals. The recording button is a toggle, meaning it can be turned on, and you can move your hand to the other button to play, and then turned back off. The wires used were green, yellow, and orange for variables, red for power, and black for ground. The code was designed to utilize Millis for background playing. 











#include "MIDIUSB.h"


int inputX = A0; //potentiometer
int playNote = 13; //note player
int trackOne = 12; //record



int clampNoteMin = 40;
int clampNoteMax = 80;



int savedNote = 0; //to stop note


int currentNote = 0;


int recordOne[4] = {0,0,0,0};
//record toggle
int previousState;
int currentState;
bool status;
int lastLoopNote = 0; //to stop it next loop



//edge detetcion for the recording button
bool lastPlayState = 1;
//write index for going through the array
int writeIndex = 0;


//millis in bg
unsigned long previousMillis = 0;
int interval= 500;






void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}


void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}


void setup() {
 
  pinMode(inputX, INPUT_PULLUP);
  pinMode(playNote, INPUT_PULLUP);
  pinMode(trackOne, INPUT_PULLUP);
  previousState = digitalRead(trackOne); //toggle


  Serial.begin(115200);
}




void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
    MidiUSB.flush(); // cord stuff


}
  void turnOff(){
   
    for (int note = 0; note < 128; note++) {
      midiEventPacket_t off = {0x08, 0x80 | 0, note, 0};
      MidiUSB.sendMIDI(off);
      }
    MidiUSB.flush();
  }
 
//toggle from led toggle
void toggleRecord(){
   currentState = digitalRead(trackOne);
  if(currentState != previousState){


    if(currentState == 1)
    {
      status = !status;
    }
   
    delay(50);


  }
  previousState = currentState;



}



void loop() {
  unsigned long currentMillis = millis();
  toggleRecord();
   
  Serial.print("REC STATUS: ");
  Serial.println(digitalRead(trackOne));


  bool playState = digitalRead(playNote);


if (lastPlayState == 1 && playState == 0) {


  Serial.println("PLAY PRESSED");
  Serial.print("Saved note: ");
  Serial.println(savedNote);
  //save the note for turning off and play
  savedNote = map(analogRead(inputX),0,1023,clampNoteMin,clampNoteMax);
  noteOn(0, savedNote, 127);


//record played note to the array
  if (status) {
    recordOne[writeIndex] = savedNote;
   
    writeIndex = writeIndex + 1;


    if (writeIndex >= 4) {
      writeIndex = 0;
    }
  }
}


//  button release stop playing note
if (lastPlayState == 0 && playState == 1) {
  noteOff(0, savedNote, 0);
}


lastPlayState = playState;


 
 //saving part 1
 //read track 1
 
  //play saved song in the background
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    if (lastLoopNote != 0) {
      noteOff(0, lastLoopNote, 0);
    }
    int currentLoopNote = recordOne[currentNote];


    if (currentLoopNote != 0) {
      noteOn(0, currentLoopNote, 127);
      lastLoopNote = currentLoopNote;
    }
    else {
      lastLoopNote = 0;
    }
     
    currentNote = currentNote + 1;


    if (currentNote >= 4) {
      currentNote = 0;
    }
     
   
   
   
  }



  MidiUSB.flush();
}

02 March 2026

Analog Team 23

Analog Team 23


We are Analog Team 23 and we developed a memory-based sequence game using the Circuit Playground that combines visual cues with analog and digital inputs. The game begins by flashing one of three colors, each representing a specific action the player must perform. For example, yellow corresponds to turning the potentiometer knob, while blue requires pressing a button. With each successful round, the game adds another color to the sequence, increasing the difficulty and requiring the player to remember and repeat the entire pattern in order.



If the player correctly completes the sequence, the board flashes green to indicate success and advances to the next round. If a mistake is made, such as turning the potentiometer too far, which may register as multiple inputs the board flashes red and the game resets. This highlights how sensitive analog components can be and how precise input control affects gameplay.





This project demonstrates how analog sensors and programmed feedback can be combined to create an engaging, progressively challenging interactive experience.





Code:

#include <Adafruit_CircuitPlayground.h>


//Defining playground analog inputs

const byte potPin = A2;

const byte buttonPin = A3;


// Game loop variables

int sequence[50];      // stores the pattern

int roundLength = 0;   // how many steps this round


void setup() {

  CircuitPlayground.begin();

  pinMode(buttonPin, INPUT_PULLUP);


  //Random seed so the pattern changes every time the playground boots up

  randomSeed(analogRead(A1));


  roundLength = 1;

  sequence[0] = random(0, 3);

}


void loop() {


  delay(400);


  // Showing the sequence first


  for (int i = 0; i < roundLength; i++) {


    // Light up pixels for the current step

    if (sequence[i] == 0) {

      // BLUE = button

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 0, 0, 255);

    }

    else if (sequence[i] == 1) {

      // YELLOW = pot

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 255, 255, 0);

    }

    else {

      // WHITE = tilt

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 255, 255, 255);

    }


    delay(500);

    CircuitPlayground.clearPixels();

    delay(250);

  }


  // Listening for player inputs


  // Setting baselines

  int potStart = analogRead(potPin);

  bool tiltNeedsCenter = false; // boolean to make sure that the board returns to center before tilt input can be used again (prevents multiple fires)


  for (int i = 0; i < roundLength; i++) {


    int expected = sequence[i];

    int playerAction = -1;


    // Wait until the player does ONE action

    while (playerAction == -1) {


      // Check for button press

      if (digitalRead(buttonPin) == LOW) {

        playerAction = 0;

        delay(200);

      }


      // Check for potentiometer input

      int potNow = analogRead(potPin);


      if (abs(potNow - potStart) > 80) {

        playerAction = 1;

        potStart = potNow;  // update baseline

        delay(200);

      }


      // Check for tilt

      float x = CircuitPlayground.motionX();

      float tiltAmount = abs(x);    // Ignores tilt direction


      if (tiltAmount > 8) {

        tiltAmount = 8;

      }


      // Mapping tilts to make the player tilt the playground farther in order to get a tilt input

      int tiltLevel = map((int)(tiltAmount * 100), 0, 800, 0, 100);

      float tiltCurve = (tiltLevel / 100);

      tiltCurve = tiltCurve * tiltCurve;

      int finalTiltValue = tiltCurve * 100;


      if (tiltAmount < 1) {       // Checks if playground is not tilted

        tiltNeedsCenter = false;

      }


      if (!tiltNeedsCenter && finalTiltValue > 40) {

        playerAction = 2;

        tiltNeedsCenter = true;     // must re-center before next tilt

        delay(200);

      }

    }


    // Giving player feedback for actions


    // Light up pixels to show what the player just did

    if (playerAction == 0) {

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 0, 0, 255);

    }

    else if (playerAction == 1) {

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 255, 255, 0);

    }

    else if (playerAction == 2) {

      for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 255, 255, 255);

    }


    // How long the feedback shows before getting cleared

    delay(300);

    CircuitPlayground.clearPixels();


    // Compare player input to expected

    if (playerAction != expected) {

      // FAIL = flash red and restart

      for (int t = 0; t < 3; t++) {

        for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 255, 0, 0);

        delay(200);

        CircuitPlayground.clearPixels();

        delay(200);

      }


      // restart the game

      roundLength = 1;

      sequence[0] = random(0, 3);

      return; // exit loop() early and start over

    }

  }


  // Handles player success


  for (int t = 0; t < 3; t++) {

    for (int p = 0; p < 10; p++) CircuitPlayground.setPixelColor(p, 0, 255, 0);

    delay(200);

    CircuitPlayground.clearPixels();

    delay(200);

  }


  // Add one more step to the pattern for the next round

  if (roundLength < 50) {

    sequence[roundLength] = random(0, 3);

    roundLength++;

  }


  delay(300);

}

Analog Team 22

By: Jesse Smith and Jonathan Tsigas


Our game is themed on solving a combination lock that combines rotating and aligning the Circuit Playground with illuminated LEDs. Each round, a distinct LED will glow, and the player has to move the Circuit Playground to get the "Key" LED to align with the "Lock" LED. Once the player believes the LEDs are aligned, they press the button to set that lock in. That sequence will occur three times, making the player solve three separate lock mechanisms to unlock the complete lock. Once all three locks are solved, the player must shake the Circuit Playground to try to open the lock. If they successfully unlock all three locks, then all the lights will glow green, and they will move onto the next level. If they did not do it correctly, all of the lights will glow red, and they will have to restart the level. 

The switch on the CPE turns the game on and off. 

Demonstration





#include <Adafruit_CircuitPlayground.h> #include <Adafruit_Circuit_Playground.h> const int led = 13; const int slideSwitch = 7; int neoState = 0; int i = 0; int mappedValue = 0; int previousValue = 0; bool showedCombination = false; unsigned long previousTime = 0; int lock1 = 0; int lock2 = 0; int lock3 = 0; const int ButtonPin = A3; bool lockFailed = false; int currentLock = 0; int Locks[3]; int shakeThresh = 25; //const int buttonA = 4; void setup() { // put your setup code here, to run once: pinMode(led, OUTPUT); //pinMode(buttonA, INPUT_PULLDOWN); //pinMode(CPLAY_RIGHTBUTTON, INPUT_PULLUP); Serial.begin(9600); CircuitPlayground.begin(); pinMode(ButtonPin, INPUT); randomSeed(micros()); //pinMode(buttonA, INPUT_PULLUP); delay(2000); } void loop() { // put your main code here, to run repeatedly: //Serial.println("Hi"); // Serial.println(analogRead(A2)); //Serial.println(digitalRead(ButtonPin)); if (showedCombination == false) { showCombination(); } previousValue = mappedValue; i = analogRead(A2); mappedValue = map(i, 0, 1024, 0, 10); // Serial.println(mappedValue); if (previousValue != mappedValue) { CircuitPlayground.setPixelColor(previousValue, 0, 0 , 0); } CircuitPlayground.setPixelColor(mappedValue, 30, 10, 100); if (digitalRead(ButtonPin)) { //Serial.println("Num picked"); if (mappedValue != Locks[currentLock]) { Serial.println("wrong"); lockFailed = true; } else if (mappedValue == Locks[currentLock]) { Serial.println("correct"); } currentLock++; delay(500); } float shake = abs(CircuitPlayground.motionX()) + abs(CircuitPlayground.motionY()) + abs(CircuitPlayground.motionZ()); Serial.println(shake); if (shake > shakeThresh && currentLock == sizeof(Locks) / sizeof(Locks[0])) { if (lockFailed) { for (int b = 0; b <= 9; b++) { CircuitPlayground.setPixelColor(b, 255, 0, 0); } } else if (!lockFailed) { for (int b = 0; b <= 9; b++) { CircuitPlayground.setPixelColor(b, 0, 255, 0); } } } } void showCombination() { for (int y = 0; y < sizeof(Locks) / sizeof(Locks[0]); y++) { Locks[y] = random(10); Serial.println(Locks[y]); CircuitPlayground.setPixelColor(Locks[y], 30, 10, 100); delay(500); CircuitPlayground.setPixelColor(Locks[y], 0, 0, 0); delay(500); showedCombination = true; } }