Switching two LED Patterns on Button Press

I am trying to make a Midi controller on an Arduino Uno R3 with 4 buttons, 3 of which which are lit by an LED Pattern that will be triggered/switched by button state.

I have the code for 3 current Buttons and each LED Pattern and Midi working in their own sketches.

I am looking for guidance in merging my 2 LED Patterns and having them triggered by a button while retaining the midi functionality of my project.

Thank You in Advance.

LED Pattern #1

#include <MIDI.h>
#define ATMEGA328 1
#ifdef ATMEGA328
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#endif
// BUTTONS

// BUTTON LIGHTS
int LED1 = 9;
int LED2 = 10;
int LED3 = 11;

//PHYSICAL BUTTONS
const int N_BUTTONS = 3;                                //*  total numbers of buttons
const int BUTTON_ARDUINO_PIN[N_BUTTONS] = { 2, 3, 4 };  //* pins of each button connected straight to the Arduino

int buttonCState[N_BUTTONS] = {};  // stores the button current value
int buttonPState[N_BUTTONS] = {};  // stores the button previous value

// DEBOUNCE
unsigned long lastDebounceTime[N_BUTTONS] = { 0 };  // the last time the output pin was toggled
unsigned long debounceDelay = 50;                   //* the debounce time; increase if the output flickers

// MIDI
byte midiCh = 1;  //* MIDI channel to be used
byte note = 36;   //* Lowest note to be used
byte cc = 1;      //* Lowest MIDI CC to be used

// SETUP
void setup() {

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  // Baud Rate
  // use if using with ATmega328 (uno, mega, nano...)
  // 31250 for MIDI class compliant | 115200 for Hairless MIDI
  Serial.begin(115200);  //*

#ifdef DEBUG
  Serial.println("Debug mode");
  Serial.println();
#endif

  // Buttons
  // Initialize buttons with pull up resistors
  for (int i = 0; i < N_BUTTONS; i++) {
    pinMode(BUTTON_ARDUINO_PIN[i], INPUT_PULLUP);
  }
}

// LOOP
void loop() {

  // Fade in
  for (int brightness = 0; brightness <= 255; brightness++) {
    analogWrite(LED1, brightness);
    analogWrite(LED2, brightness);
    analogWrite(LED3, brightness);
    delay(10);  // Adjust this delay to control the fading speed
  }
  // Stay at maximum brightness for 2 seconds
  delay(2000);

  // Fade out
  for (int brightness = 255; brightness >= 0; brightness--) {
    analogWrite(LED1, brightness);
    analogWrite(LED2, brightness);
    analogWrite(LED3, brightness);
    delay(10);  // Adjust this delay to control the fading speed
  }

  buttons();
}

/////////////////////////////////////////////
// BUTTONS
void buttons() {

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

    buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN[i]);  // read pins from arduino


    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

          // Sends the MIDI note ON accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 127, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button on");
#endif

        } else {

          // Sends the MIDI note OFF accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 0, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button off");
#endif
        }
        buttonPState[i] = buttonCState[i];
      }
    }
  }
}

LED Pattern #2

#include <MIDI.h>
#define ATMEGA328 1
#ifdef ATMEGA328
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#endif
// BUTTONS

// BUTTON LIGHTS
int LED1 = 9;
int LED2 = 10;
int LED3 = 11;

//PHYSICAL BUTTONS
const int N_BUTTONS = 3;                                //*  total numbers of buttons
const int BUTTON_ARDUINO_PIN[N_BUTTONS] = { 2, 3, 4 };  //* pins of each button connected straight to the Arduino

int buttonCState[N_BUTTONS] = {};  // stores the button current value
int buttonPState[N_BUTTONS] = {};  // stores the button previous value

// DEBOUNCE
unsigned long lastDebounceTime[N_BUTTONS] = { 0 };  // the last time the output pin was toggled
unsigned long debounceDelay = 50;                   //* the debounce time; increase if the output flickers

// MIDI
byte midiCh = 1;  //* MIDI channel to be used
byte note = 36;   //* Lowest note to be used
byte cc = 1;      //* Lowest MIDI CC to be used

// SETUP
void setup() {

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  // Baud Rate
  // use if using with ATmega328 (uno, mega, nano...)
  // 31250 for MIDI class compliant | 115200 for Hairless MIDI
  Serial.begin(115200);  //*

#ifdef DEBUG
  Serial.println("Debug mode");
  Serial.println();
#endif

  // Buttons
  // Initialize buttons with pull up resistors
  for (int i = 0; i < N_BUTTONS; i++) {
    pinMode(BUTTON_ARDUINO_PIN[i], INPUT_PULLUP);
  }
}

// LOOP
void loop() {

  digitalWrite(LED1, HIGH);
  digitalWrite(LED2, HIGH);
  digitalWrite(LED3, HIGH);

  delay(250);

  digitalWrite(LED1, LOW);
  digitalWrite(LED2, LOW);
  digitalWrite(LED3, LOW);

  delay(250);

  buttons();
}

/////////////////////////////////////////////
// BUTTONS
void buttons() {

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

    buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN[i]);  // read pins from arduino


    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

          // Sends the MIDI note ON accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 127, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button on");
#endif

        } else {

          // Sends the MIDI note OFF accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 0, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button off");
#endif
        }
        buttonPState[i] = buttonCState[i];
      }
    }
  }
}

If you want your buttons to do something outside the buttons() function, then have the buttons() function return something (for example: int buttons()) and send back which button was pressed... then using that returned value, show a LED pattern.

2 Likes

which button should be a switch for LEDs behavior?

But I can see only three buttons coded?

I will be adding a 4th button on pin 5 with the purpose of making it toggle the led lighting patterns between codes 1&2. Sorry for not stating that clearly.

I will be adding a 4th button on pin 5 to switch the behavior.

I will give that a shot, thanks for the help and guidance.

#include <MIDI.h>
#define ATMEGA328 1
#ifdef ATMEGA328
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#endif
// BUTTONS

// BUTTON LIGHTS
int LED1 = 9;
int LED2 = 10;
int LED3 = 11;

//PHYSICAL BUTTONS
const int N_BUTTONS = 3;                                //*  total numbers of buttons
const int BUTTON_ARDUINO_PIN[N_BUTTONS] = { 2, 3, 4 };  //* pins of each button connected straight to the Arduino

int buttonCState[N_BUTTONS] = {};  // stores the button current value
int buttonPState[N_BUTTONS] = {};  // stores the button previous value

// DEBOUNCE
unsigned long lastDebounceTime[N_BUTTONS] = { 0 };  // the last time the output pin was toggled
unsigned long debounceDelay = 50;                   //* the debounce time; increase if the output flickers

// MIDI
byte midiCh = 1;  //* MIDI channel to be used
byte note = 36;   //* Lowest note to be used
byte cc = 1;      //* Lowest MIDI CC to be used

// SETUP
void setup() {

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  // Baud Rate
  // use if using with ATmega328 (uno, mega, nano...)
  // 31250 for MIDI class compliant | 115200 for Hairless MIDI
  Serial.begin(115200);  //*

#ifdef DEBUG
  Serial.println("Debug mode");
  Serial.println();
#endif

  // Buttons
  // Initialize buttons with pull up resistors
  for (int i = 0; i < N_BUTTONS; i++) {
    pinMode(BUTTON_ARDUINO_PIN[i], INPUT_PULLUP);
  }
}

// LOOP
void loop() {
  if (digitalRead(5)) {
    // Fade in
    for (int brightness = 0; brightness <= 255; brightness++) {
      analogWrite(LED1, brightness);
      analogWrite(LED2, brightness);
      analogWrite(LED3, brightness);
      delay(10);  // Adjust this delay to control the fading speed
    }
    // Stay at maximum brightness for 2 seconds
    delay(2000);

    // Fade out
    for (int brightness = 255; brightness >= 0; brightness--) {
      analogWrite(LED1, brightness);
      analogWrite(LED2, brightness);
      analogWrite(LED3, brightness);
      delay(10);  // Adjust this delay to control the fading speed
    }
  }
  else {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);

    delay(250);

    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, LOW);

    delay(250);
  }
  buttons();
}

/////////////////////////////////////////////
// BUTTONS
void buttons() {

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

    buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN[i]);  // read pins from arduino


    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

          // Sends the MIDI note ON accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 127, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button on");
#endif

        } else {

          // Sends the MIDI note OFF accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 0, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button off");
#endif
        }
        buttonPState[i] = buttonCState[i];
      }
    }
  }
}
1 Like

Kolaha, Thank You for the coding help. I changed the code on my end to start the LED fading loop on start up and trigger the state change of the LED's with your guidance.

The button press seems to only work to change states if the fading is almost to the LED's lowest brightness point.

is there something in my current code that is preventing the change upon pressing as opposed to releasing the button?

I would like the change to occur at any point in the LED Fade up and down.

Thanks for your help.

#include <MIDI.h>
#define ATMEGA328 1
#ifdef ATMEGA328
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#endif
// BUTTONS

// BUTTON LIGHTS
int LED1 = 9;
int LED2 = 10;
int LED3 = 11;

//PHYSICAL BUTTONS
const int N_BUTTONS = 4;                                //*  total numbers of buttons
const int BUTTON_ARDUINO_PIN[N_BUTTONS] = { 2, 3, 4, 5 };  //* pins of each button connected straight to the Arduino

int buttonCState[N_BUTTONS] = {};  // stores the button current value
int buttonPState[N_BUTTONS] = {};  // stores the button previous value

// DEBOUNCE
unsigned long lastDebounceTime[N_BUTTONS] = { 0 };  // the last time the output pin was toggled
unsigned long debounceDelay = 50;                   //* the debounce time; increase if the output flickers

bool ledsFlashing = true;


// MIDI
byte midiCh = 1;  //* MIDI channel to be used
byte note = 36;   //* Lowest note to be used
byte cc = 1;      //* Lowest MIDI CC to be used

// SETUP
void setup() {

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  // Baud Rate
  // use if using with ATmega328 (uno, mega, nano...)
  // 31250 for MIDI class compliant | 115200 for Hairless MIDI
  Serial.begin(115200);  //*

#ifdef DEBUG
  Serial.println("Debug mode");
  Serial.println();
#endif

  // Buttons
  // Initialize buttons with pull up resistors
  for (int i = 0; i < N_BUTTONS; i++) {
    pinMode(BUTTON_ARDUINO_PIN[i], INPUT_PULLUP);
  }
}

// LOOP

void loop() {
  if (digitalRead(5) == LOW) {
    // Toggle the LED flashing state when the button is pressed
    ledsFlashing = !ledsFlashing;

    // Wait for the button to be released to prevent rapid toggling
    while (digitalRead(5) == LOW) {
      // Do nothing, wait for button release
    }
  }

  if (ledsFlashing) {
    // LED state change when the button is NOT pressed
    for (int brightness = 0; brightness <= 255; brightness++) {
      analogWrite(LED1, brightness);
      analogWrite(LED2, brightness);
      analogWrite(LED3, brightness);
      delay(10);  // Adjust this delay to control the fading speed
    }
    // Stay at maximum brightness for 2 seconds
    delay(2000);

    // Fade out
    for (int brightness = 255; brightness >= 0; brightness--) {
      analogWrite(LED1, brightness);
      analogWrite(LED2, brightness);
      analogWrite(LED3, brightness);
      delay(10);  // Adjust this delay to control the fading speed
    }
  } else {
    // LED state when the button is pressed (flashing paused)
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    digitalWrite(LED3, HIGH);
    delay(250); // Stay at maximum brightness for 250 milliseconds
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
    digitalWrite(LED3, LOW);
    delay(250); // Stay off for 250 milliseconds
  }

  buttons();
}
/////////////////////////////////////////////
// BUTTONS
void buttons() {

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

    buttonCState[i] = digitalRead(BUTTON_ARDUINO_PIN[i]);  // read pins from arduino


    if ((millis() - lastDebounceTime[i]) > debounceDelay) {

      if (buttonPState[i] != buttonCState[i]) {
        lastDebounceTime[i] = millis();

        if (buttonCState[i] == LOW) {

          // Sends the MIDI note ON accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 127, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button on");
#endif

        } else {

          // Sends the MIDI note OFF accordingly to the chosen board
#ifdef ATMEGA328
          // use if using with ATmega328 (uno, mega, nano...)
          MIDI.sendNoteOn(note + i, 0, midiCh);  // note, velocity, channel

#elif DEBUG
          Serial.print(i);
          Serial.println(": button off");
#endif
        }
        buttonPState[i] = buttonCState[i];
      }
    }
  }
}

Even though this delay() is only 10 milliseconds, it is called 255 times in a code-blocking loop, and for that 2.5 seconds, no processing (or button reading code) will occur. This 2+ seconds where "nothing happens" occurs three times in every loop.

The sketch need something to sense any button press, even during the delay()s. I regularly use blocking code (for-loops, while-loops, delay()-loops) but need to have a button sensed, so I use a simple interrupt that is sensed at any time, even during blocking code. And when that blocking code has finished, the button pressed is acted-upon. Here is how you can use it...

Put your button on an available interrupt pin (Arduino Uno/Nano = pin 2/INT0 and pin 3/INT1)...

int buttonPin = 2; // interrupt pin INT0

Add a flag to note that a button was pressed... it MUST be set to volatile for the interrupt

volatile bool button; // type volatile for interrupt handler

You still need to wire and program the pin to use the button...

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
.
.

Then prepare the interrupt routine to be called...

.
  attachInterrupt(digitalPinToInterrupt(buttonPin), buttonISR, FALLING); // INT#, ISR, TRIGGER
.

The button-press interrupt routine only sets a flag that a button was pressed...

int buttonISR() {
  noInterrupts(); // disable interrupts while in the ISR
  button = 1; // set the flag indicating the button was pressed
  interrupts(); // enable interrupts when leaving the ISR
}

And finally in loop() give a little delay() for debounce of the button coming out of the ISR, and if the button flag is set, move to the next function...

void loop() {
  int cases = 8;
  if (button) { // button flag is returned from ISR
    delay(150); // debounce
    button = 0; // reset button flag in ISR
    count++; // increment selected pattern
    if (count > cases) { // if counter reached last case...
      count = 1; // ... reset counter to first case
    }
  }

  switch (count) {
    case 0: zero(); break;
    case 1: trace(); break;
    case 2: evenodd(); break;
    case 3: zap(); break;
    case 4: zztzzt(); break;
    case 5: gap(); break;
    case 6: twinkle(); break;
    case 7: fade(); break;
    case 8: sections(); break;
  }
}
1 Like

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