Trying to make a piano that teaches you how to play a song on it, But i cant figure out how to make the LEDS turn on how i'd like them to

Currently i have a 4 LED, 4 Piezzo and 4 button setup, the piano aspect works just fine but I'm not sure how to approach programming the LED part... I have 4 different sounds/notes saved as consts' and the actual order in which it should be played as a const but not sure where to go after that. (i'm using Tinker cad to plan it out by the way). Here's my code anyway:

// C++ code
//
  const float E4 = 324.63;
  const float D4 = 293.66;
  const float C4 = 261.63;
  const float B3 = 246.94;
const float songNotes[] = {
  E4, D4, C4, D4, E4, E4, E4, D4, D4, 
E4, D4, C4, D4, E4, E4, E4, D4, D4
  
};
void setup()
{
  pinMode(2, INPUT_PULLUP);
    pinMode(3, INPUT_PULLUP);
    pinMode(4, INPUT_PULLUP);
    pinMode(5, INPUT_PULLUP);
  	pinMode(6, OUTPUT);
  	pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
    pinMode(9, OUTPUT);
    pinMode(10, OUTPUT);
    pinMode(11, OUTPUT);
  	pinMode(12, OUTPUT);
    pinMode(13, OUTPUT);
    
}

void loop()
{
  
  
  
  int buttonState = 0;
buttonState = digitalRead(2);
  if (buttonState == LOW){
    tone(6, E4);
  }
  else {
  noTone(6);
  
    
   int buttonState = 0;
 buttonState = digitalRead(3);
   if (buttonState == LOW){
     tone(7, D4);
   }
    else{
      noTone(7);
    
      
    int buttonState = 0;
 buttonState = digitalRead(4);
      if (buttonState == LOW){
        tone(12, C4);
      }
      else{
        noTone(12);
      
        
    int buttonState = 0;
  buttonState = digitalRead(5);
        if (buttonState == LOW) {
          tone (13, B3);
        }
        else{
          noTone(13);
          

   
}
      }
    }
  }
}


any help would be appreciated :>

If you strike the key that lights up, you will be late by your own reaction speed.

I played piano long ago, IMO it would sound pretty lame.

You can consider this a demonstration for a framework.

You have a few things that are accociated to each other.

  1. button pin
  2. LED pin
  3. speaker pin
  4. note

Associated things fit best in a struct or class. Think e.g. of a phone book with a name, a phone number and possibly address and city.

Your struct could look like this

struct KEY
{
  const uint8_t pinButton;
  const uint8_t pinLed;
  const uint8_t pinSpeaker;
  const float tone;
  const uint32_t duration;
};

And you can use an array of structs to make a sequence

KEY noteSequence[] =
{
  {2, 8, 6, E4, 2000UL}, // button, LED, speaker, note, duration
  {3, 9, 7, D4,  500UL},
};

You can extend this with other KEYs. Later you can loop through this array.

To know how many elements there are in any type of array, you can use the below macro (place it at the top of your code)

#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

In setup() you can loop through the array to set pins to input or output

void setup()
{
  Serial.begin(115200);
  // loop through note sequence
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(noteSequence); cnt++)
  {
    // set button pins to input with pullup
    pinMode(noteSequence[cnt].pinButton, INPUT_PULLUP);
    // set LED pins as output
    pinMode(noteSequence[cnt].pinLed, OUTPUT);
    // and make sure it is off
    digitalWrite(noteSequence[cnt].pinLed, LOW);
  }
}

Note that this is not perfect because you initialise pins multiple times. But it is (for now) the easiest, else you need arrays for button pins and LED pins as well.

In loop() you need a few variables

void loop()
{
  // index in array of noteSequence
  static uint8_t noteToPlay;
  // flag to indicate if a nore is playing
  static bool isPlaying = false;
  // remember time when note started playing
  static uint32_t startTime;

  ...
  ...
}

static variables are like gloabal variables whose values will be remembered but they are only known inside (in this case) loop(). You can read up on C++ scope.

noteToPlay will be zero when you start

The first step is to indicate which key most be pressed by setting the LED.

  // indicate key to be pressed
  digitalWrite(noteSequence[noteToPlay].pinLed, HIGH);

Next you check if a note is already playing

  // check if a tone is not already playing
  if (isPlaying == false)
  {

If not, you check if the key for that note is pressed.

    // check if the associated key is pressed
    if (digitalRead(noteSequence[noteToPlay].pinButton) == LOW)
    {

And if it is you will play the note, remember the start time and set the flag that the note is already playing.

    // check if the associated key is pressed
    if (digitalRead(noteSequence[noteToPlay].pinButton) == LOW)
    {
      // play the tone
      tone(noteSequence[noteToPlay].pinSpeaker, noteSequence[noteToPlay].tone);
      // remember the start time
      startTime = millis();
      // indicate that a note is playing
      isPlaying = true;
    }
  }

In the next step you check if a note is playing and if the duration has lapsed

  // if a tone is playing and the duration has lapsed
  if (isPlaying == true && millis() - startTime >= noteSequence[noteToPlay].duration)
  {

If so, you switch the LED of and check if the key was released

    // current LED off
    digitalWrite(noteSequence[noteToPlay].pinLed, LOW);
    // wait for key to be released
    if (digitalRead(noteSequence[noteToPlay].pinButton) == HIGH)
    {

And if so, you switch the tone off, indicate that a note is no longer playing and go to the next step in the sequence.

      // tone off
      noTone(noteSequence[noteToPlay].pinSpeaker);
      // indicate no longer playing
      isPlaying = false;
      // next note in sequence
      noteToPlay++;
      // limit to number of notes
      if (noteToPlay == NUMELEMENTS(noteSequence))
      {
        noteToPlay = 0;
      }
    }
  }

Full code

/*
buttons 2, 3, 4 ,5
speakers 6, 7, 12, 13
LEDs 8, 9, 10, 11
*/

#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

const float E4 = 324.63;
const float D4 = 293.66;
const float C4 = 261.63;
const float B3 = 246.94;

struct KEY
{
  const uint8_t pinButton;
  const uint8_t pinLed;
  const uint8_t pinSpeaker;
  const float tone;
  const uint32_t duration;
};

// clang-format off
KEY noteSequence[] =
{
  {2, 8, 6, E4, 2000UL}, // button, LED, speaker, note, duration
  {3, 9, 7, D4,  500UL},
};
// clang-format on

void setup()
{
  Serial.begin(115200);
  // loop through note sequence
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(noteSequence); cnt++)
  {
    // set button pins to input with pullup
    pinMode(noteSequence[cnt].pinButton, INPUT_PULLUP);
    // set LED pins as output
    pinMode(noteSequence[cnt].pinLed, OUTPUT);
    // and make sure it is off
    digitalWrite(noteSequence[cnt].pinLed, LOW);
  }
}

void loop()
{
  // index in array of noteSequence
  static uint8_t noteToPlay;
  // flag to indicate if a nore is playing
  static bool isPlaying = false;
  // remember time when note started playing
  static uint32_t startTime;

  // indicate key to be pressed
  digitalWrite(noteSequence[noteToPlay].pinLed, HIGH);
  // check if a tone is not already playing
  if (isPlaying == false)
  {
    // check if the associated key is pressed
    if (digitalRead(noteSequence[noteToPlay].pinButton) == LOW)
    {
      // play the tone
      tone(noteSequence[noteToPlay].pinSpeaker, noteSequence[noteToPlay].tone);
      // remember the start time
      startTime = millis();
      // indicate that a note is playing
      isPlaying = true;
    }
  }

  // if a tone is playing and the duration has lapsed
  if (isPlaying == true && millis() - startTime >= noteSequence[noteToPlay].duration)
  {
    // current LED off
    digitalWrite(noteSequence[noteToPlay].pinLed, LOW);
    // wait for key to be released
    if (digitalRead(noteSequence[noteToPlay].pinButton) == HIGH)
    {
      // tone off
      noTone(noteSequence[noteToPlay].pinSpeaker);
      // indicate no longer playing
      isPlaying = false;
      // next note in sequence
      noteToPlay++;
      // limit to number of notes
      if (noteToPlay == NUMELEMENTS(noteSequence))
      {
        noteToPlay = 0;
      }
    }
  }
}

Notes:

  • Compiles (Arduino Nano) but is not tested.
  • This is meant as a framework that might not fully suite your needs.

If you have questions, ask.

1 Like

tone() can only handle one pin at a time, so if this is what you want to use, then you don't need 4 piezos , one will be enough

if you want to be able to play multiple tones at once (press multiple keys) then a bit more hardware is needed. You could look at Julian Ilett's penny organ's videos for ideas

worth watching the process from identifying frequencies to adding polyphonic sound etc

(and more)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.