Multiple buttons for State Changes - Rgb led strips

I'm having a hard time trying to figure out what sort of statements I need to use in the coding for my project.

In a nutshell, I have 16 different rgb led strips. I will be having all of them change colors together in some sort of pattern.
I have a button hooked up accordingly for each different strip and when it's pressed I want it to turn that corresponding rgb strip a solid color until it's pressed again. When the button is pressed again I want that strip to return to the original pattern as all the others.

I'm not sure if I should be using if statements or switch or what? I'm new to programming so any help would be greatly appreciated.

Thank you :slight_smile:

What is your setup, what board are you using and what LED driver/shift register are you using? Do you have a sketch you are currently working on and can you provide us with pictures and/or a complete schematic.

Your problem sounds like it has an easy solution, but we don't know your exact hardware to be certain.

try to play around with this...

you can change state with a button push or by typing a character into the serial monitor.

think about what functions you will call to do different tricks with your LED strip...

int buttonPin = 3;
int ledPin = 13;
int state = 0;
int oldState;
unsigned long startTime;
unsigned long interval = 1000UL;
//
void setup() 
{
  Serial.begin(9600);
}
//
void loop () 
{
  if (Serial.available())
  {
    int dump = Serial.read();
    state++;
    if (state > 4) state = 0;
  }
  int buttonState = digitalRead(buttonPin);
  {
    if (buttonState == HIGH)
    {
      if (oldState == LOW)
      {
        state++;
        Serial.println(state);
        if (state > 4) state = 0;
      }
    }
  }
  oldState = buttonState;
  if (state == 0)
  {
    noFlash();
    startTime = millis();
  }
  else if (state == 1)
  {
    flashA();
  }
  else if (state == 2)
  {
    flashB();
  }
  else if (state == 3)
  {
    flashC();
  }
  else if (state == 4)
  {
    flashD();
  }
}
//
void noFlash()
{
  digitalWrite(ledPin, LOW);
}
//
void flashA() //slow flash
{
  if (millis() - startTime >= interval)
  {
    digitalWrite(ledPin, !digitalRead(ledPin));
    startTime = millis();
  }
}
//
void flashB() //med flash
{
  if (millis() - startTime >= interval/4UL)
  {
    digitalWrite(ledPin, !digitalRead(ledPin));
    startTime = millis();
  }
}
//
void flashC() //fast flash
{
  if (millis() - startTime >= interval/20UL)
  {
    digitalWrite(ledPin, !digitalRead(ledPin));
    startTime = millis();
  }
}

void flashD() //solid
{
  digitalWrite(ledPin, HIGH);
  startTime = millis();
}

Why not just have one function and pass in the value for the denominator?

HazardsMind:
What is your setup, what board are you using and what LED driver/shift register are you using? Do you have a sketch you are currently working on and can you provide us with pictures and/or a complete schematic.

Your problem sound like it has an easy solution, but we don't know your exact hardware to be certain.

I'm using the at mega 2560. I've got all the rgb LEDs hooked up to transistors and connected to the digital pins. I wanted a fade effect from color to color but I learned about PWM and found out I'm short quite a few pins haha. So I was just gonna do some changing of colors through HIGH and LOW states. The buttons are all connected up via he analog pins.

I've dealt with similar projects but not something with multiple inputs. I'm trying to keep it so that the light changing continues for all the rgb strips even if the state of one moves to a solid. Hopefully that makes sense. (Sorry about my lack of knowledge).

HazardsMind:
Why not just have one function and pass in the value for the denominator?

I wasn't sure if it was that simple. I'll mess around with that. Thank you.!

HazardsMind:
Why not just have one function and pass in the value for the denominator?

code was for demonstration purposes only... a visualization of how to approach the problem

You can cut down your pin usage by using shift registers and a 16-4 encoder for your buttons.
Look at these links.

Added:

Why not just have one function and pass in the value for the denominator?

This was meant for Bulldog

code was for demonstration purposes only... a visualization of how to approach the problem

Ok, that's fair enough.

I have code for 16 buttons in a 4x4 multiplex with debounce that I have been able to test by jumpering pins but I don't have enough buttons to completely see if every combination works (I tried a few and they work but 4x4 holes only allows so many wires at once) fast and smooth.
I also have 8 button pin-per-button code with debounce that could extend to 16 independent buttons.
It is jumper-tested too and I have a higher confidence in it, pin-wastey though it is.

Both have control sections not weaved into the button code, you can mess with one part without needing to worry about the rest.

Both control 1 led that could as easily be many. I do have multi-led code that the led section could be placed in the buttons codes.

All of these run on the same code framework, the parts written to fit together and be interchanged with minimal work.

ALL of them are free to the public examples.

I can post any of them here, have already done so but had to remove whole blocks of comments from the bigger ones to do so. My other option is to split sketches over 2 posts.

Question is, do you want any of that? It's not beginner code but none of the sketches is really big, the code without the comments fits in the 9500 character post limit with some to spare.

GoForSmoke:
I have code for 16 buttons in a 4x4 multiplex with debounce that I have been able to test by jumpering pins but I don't have enough buttons to completely see if every combination works (I tried a few and they work but 4x4 holes only allows so many wires at once) fast and smooth.
I also have 8 button pin-per-button code with debounce that could extend to 16 independent buttons.
It is jumper-tested too and I have a higher confidence in it, pin-wastey though it is.

Both have control sections not weaved into the button code, you can mess with one part without needing to worry about the rest.

Both control 1 led that could as easily be many. I do have multi-led code that the led section could be placed in the buttons codes.

All of these run on the same code framework, the parts written to fit together and be interchanged with minimal work.

ALL of them are free to the public examples.

I can post any of them here, have already done so but had to remove whole blocks of comments from the bigger ones to do so. My other option is to split sketches over 2 posts.

Question is, do you want any of that? It's not beginner code but none of the sketches is really big, the code without the comments fits in the 9500 character post limit with some to spare.

That would be awesome! Yes please. Even if it's not beginner code, I gotta start expanding my knowledge somehow. Even the response from early talking about feeding a value to a function with a denominator is new to me but I'm determined to learn whatever is necessary. I've got plenty of time to finish this so sitting down and learning is not an issue.

This is the 8 button sketch that can be made into 16 buttons by using 8 more pins.
It is easier to wire than the 4x4 multiplex. This one, you just connect the button to a pin and then to ground.
Tested on an UNO, you have plenty more buttons on the Mega but let me know if you want multiplexed buttons.

And yeah, I'm going to have to rip out some, maybe many comments to fit it in one post.

You may or may not need to tweak the debounce time if you have really dirty buttons.
Right now the button state must be stable for 20ms to be considered debounced.

// All you need to do this example is an Arduino and jumpers in pins 4 to 11.
// Push and let go the button on pin 11 once, led blinks. Again, led turns off.
// Push and let go the button on pin 4 once, led blink slows down.
// Push and let go the button on pin 5 once, led blink speeds up.
// Push or let go of any button and a status line will show it on Serial Monitor.
// Buttons are debounced and wired directly to ground or, the buttons can 
// be jumpers from the pins to grounded on USB connector housing. 
// This sketch is tested with jumpers.
// By GoForSmoke for free public use. Using Arduino 1.0.5-r2

const byte ledPin =  13;      // this is the onboard led pin

byte ledPinState = 0;
byte blinkLed = 1; // start with led blinking
word ledBlinkMs = 1000U; // blink interval
word ledBlinkNowMs = 0U; // low 16 bits of millis()
word ledBlinkStartMs = 0U; // 16 bit blink (on or off) time ms

//   button variables   button variables   button variables   button variables
byte currentButton = 0;
const byte BUTTONS = 8;
__attribute__ ((__packed__)) enum  buttonNames 
{
  fSlowDown = 0, fSpeedUp, fButton2, fButton3, fButton4, fButton5, fButton6, fBlinkToggle 
};

const byte buttonPin[ BUTTONS ] = 
{ 
  4, 5, 6, 7, 8, 9, 10, 11 
};

__attribute__ ((__packed__)) enum buttonBitName 
{ 
  bCurrentRead = 0, bLastRead, bCurrentState, bLastState, bCheckDebounce 
};
// buttonBit array, using 8 bytes to do what 40 bytes could instead
byte buttonBits[ BUTTONS ] = // current and last read in bit 0 and 1
{                           // current and last state in bit 2 and 3
  2, 2, 2, 2, 2, 2, 2, 2  // start with all lastRead states HIGH, all others LOW 
}; 

const byte buttonReadBits = 3; // bit mask for the read bitss
const byte buttonStateBits = 12; // bit mask for the state bits

// button debounce timing variables
const word debounceMs = 20; // debounced when reads for debounceMs ms are the same
word debounceMillisLowByte[ BUTTONS ]; 
word msNow = 0; 


byte blinkProcessState = 0;  // the button gets pushed and released twice


void  bar()
{
  Serial.println( F( "============================================================" ));    
}

void  printMillis()
{
  Serial.print( F( "  time " ));
  Serial.println( millis() );
}

void setup() 
{
  Serial.begin( 115200 );
  Serial.print( "\n  BWD with 8 buttons\n" );

  pinMode( ledPin, OUTPUT );  // default is LOW 

  for ( currentButton = 0; currentButton < BUTTONS; currentButton++ )
  {
    pinMode( buttonPin[ currentButton ], INPUT_PULLUP ); // my button connects to ground, not 5V
  }

  // start button processing with button 0 then each loop(), process the next
  currentButton = BUTTONS;  

}

void loop() // make sure that loop() runs fast and stays in the "now".
{
  static byte  tmp;

  // BUTTON CODE BLOCK, it handles debouncing. 
  // the task is to set the button state when a Stable Button State is reached.

  if ( ++currentButton >= BUTTONS )
  {
    currentButton = 0;
  }
  // reads and states are stored as bits in buttonBits[ BUTTONS ]
  // bCurrentRead = 0, bLastRead, bCurrentState, bLastState, bCheckDebounce

  // msNow gets the low byte of millis() to time the very short debounce time
  msNow = (byte) ( millis() & 0xFFFF ); // don't worry, unsigned rollover makes it work 

  // read the pin which may be changing fast and often as the jumper contacts the housing
  tmp = digitalRead( buttonPin[ currentButton ] );
  bitWrite( buttonBits[ currentButton ], bCurrentRead, tmp ); // momentary state

  if ((( buttonBits[ currentButton ] & buttonReadBits ) == 1 ) ||
    (( buttonBits[ currentButton ] & buttonReadBits ) == 2 )) // if currentRead != lastRead
  {
    debounceMillisLowByte[ currentButton ] = msNow;
    bitWrite( buttonBits[ currentButton ], bCheckDebounce, 1 ); // button changed
  }
  else if ( bitRead( buttonBits[ currentButton ], bCheckDebounce )) // if checkDebounce == true
  { 
    if ( msNow - debounceMillisLowByte[ currentButton ] >= debounceMs ) // stable button state achieved
    {
      bitWrite( buttonBits[ currentButton ], bCurrentState, !bitRead( buttonBits[ currentButton ], bCurrentRead )); // mission accomplished, button is stable
      // note that buttonState is opposite buttonRead
      bitWrite( buttonBits[ currentButton ], bCheckDebounce, 0 ); // stop debounce checking until pin change
    }
  }
  bitWrite( buttonBits[ currentButton ], bLastRead, bitRead( buttonBits[ currentButton ], bCurrentRead ));
  //
  // End of the BUTTON CODE BLOCK


  // BUTTON CHANGED CODE BLOCK 

  // which button is current?
  // fSlowDown = 0, fSpeedUp, fButton2, fButton3, fButton4, fButton5, fButton6, fBlinkToggle 

  if ((( buttonBits[ currentButton ] & buttonStateBits ) == 4 ) ||
    (( buttonBits[ currentButton ] & buttonStateBits ) == 8 )) // if currentState != lastState
  {
    bitWrite( buttonBits[ currentButton ], bLastState, bitRead( buttonBits[ currentButton ], bCurrentState ));

    switch ( currentButton )
    {
    case fSlowDown :
      if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
      {
        if ( ledBlinkMs < 4000U )
        {
          ledBlinkMs *= 2U;
        }
        bar();    
        Serial.print( F( "slow down -- blink millis " ));
        Serial.print( ledBlinkMs );
        printMillis();
        bar();   
      } 
      break;

    case fSpeedUp : 
      if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
      {
        if ( ledBlinkMs > 20U )
        {
          ledBlinkMs /= 2U;
        }
        bar();    
        Serial.print( F( "speed up -- blink millis " ));
        Serial.print( ledBlinkMs );
        printMillis();
        bar();   
      } 
      break;

    case fBlinkToggle : 
      // debug type prints -- also testing some loading, does it alter the blink much?
      bar();    
      Serial.print( F( "processState " ));
      Serial.print( blinkProcessState );
      Serial.print( F( "  blink toggle   current state " ));
      Serial.print( bitRead( buttonBits[ currentButton ], bCurrentState ), HEX );
      printMillis();
      bar();    

      switch ( blinkProcessState )
      {
      case 0: // 0 = 1st press has not happened, led is OFF, looking for press
        if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 1 ) // button is pressed 
        {
          blinkProcessState = 1;
          blinkLed = 0;
          ledPinState = 0;
          ledBlinkStartMs = (word) ( millis() & 0xFFFF )- ledBlinkMs; 
        }
        break; // note that until the 1st press, this case runs over and over

      case 1: // 1 = 1st press is down, led is ON, looking for release
        if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 0 ) // button is released 
        {
          blinkProcessState = 2;
        }
        break; // note that until the 1st release, this case runs over and over

      case 2: // 2 = 1st push relesased, led stays ON, looking for 2nd press
        if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 1 ) // button is pressed 
        {
          blinkProcessState = 3;
          blinkLed = 1;
        }
        break; // note that until the 2nd press, this case runs over and over

      case 3: // 3 = 2nd press is down, led is OFF, looking for release
        if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 0 ) // button is released 
        {
          blinkProcessState = 0; // back to the start
        }
        break; // note that until the 2nd release, this case runs over and over
      }
      break;

    default : // switch currentButton
      bar();    
      Serial.print( F( "switch " ));
      Serial.print( currentButton );
      if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
      {
        Serial.print( F( " pressed" ));
      }
      else
      {
        Serial.print( F( " released" ));
      }
      printMillis();
      bar();   
      break;
    }
  }
  // End of the LED CONTROL CODE


  // LED BLINK CODE BLOCK
  if ( blinkLed )
  { 
    ledBlinkNowMs = (word) ( millis() & 0xFFFF );

    if ( ledBlinkNowMs - ledBlinkStartMs >= ledBlinkMs )
    { 
      ledPinState = !ledPinState;
      digitalWrite(ledPin, ledPinState );
      ledBlinkStartMs = ledBlinkNowMs;
    }
  }
  else
  {
    ledPinState = LOW;
    digitalWrite(ledPin, LOW );
  }
  // End of the LED BLINK CODE
}

Great. Thank you everyone!! I should be able to head in the right direction.

Here is blink multiple leds with uneven, independent blink times. Everything fits, including comments.
This uses pin per led to flash 5 leds but you can add more by changing LEDS and the ledPin[] array.

If you want serial performance data, set SERPRINT to 1.
The sketch will run for runBeforeStop + 1 blinks and then stop so you can scroll and view data easily.
Looking at the data should help to understand the sketch and all timing to be checked for closeness.

//  Blink Multiple leds With Uneven Delays, Serial reporting optional

/*
The hardest part of this was getting the debug/report prints to not screw the timing
by more than 1 milli. I even left in some commented-out lines just for comparison.
Serial is not free, it blocks execution!
*/

// make SERPRINT 1 to enable serial prints
#define  SERPRINT  0

// #if SERPRINT ---- if I leave this in, the sketch won't compile
byte runBeforeStop = 199; // to limit serial print lines set this to < 200
//#endif

const byte    LEDS = 5;
const byte    ledPin[ LEDS ] = 
{ 
  3, 5, 7, 9, 11 // choose what you want but don't use 0 or 1
};

byte          ledState = 0; // this will handle 8 led states as bits
//            to track more than 8 leds, use larger unsigned integers than byte
byte          led = 0; // this example processes 1 led per pass through loop()

unsigned int  millisNow; // fill with the lower 16 bits of millis()
unsigned int  startBlink[ LEDS ]; // these all default to zero 
unsigned int  waitBlink[ LEDS ][ 2 ]; // one for OFF time, one for ON time
//               using the low 16 bits of millis allows 65.535 second blink 
//               however if you need longer, change these to unsigned long
//          you can initialize this array as const or use PROGMEM to store it
//          I chose to use a formula. With a different formula I could get rid
//          of the whole array and use one or more variables to run the formula.

// This is code. Make your own way all you *can*! More you do, better you get.
// And note, this mess just begs to be turned into a Class Objects example.

void setup()
{
#if SERPRINT
  Serial.flush();
  Serial.begin( 115200 );
  Serial.println( "\nMulti-led blink test\n" ); 
#endif

  for ( led = 0; led < LEDS; led++ )
  {
#if SERPRINT
    Serial.print( " Led " ); 
    Serial.print( led, DEC ); 
    Serial.print( " >> " ); 
#endif

    // setting the blink delays by formula here 
    // YES for this the array is not needed, the code below could use the formula
    // however this example can also use a pre-compiled array
    // and if User I/O or other dynamic change method is added, the array may serve that
    // But Please, change it all to suit your ideas as they occur!

    waitBlink[ led ][ 0 ] = 250U * ((unsigned int) led + 1U ); // OFF millis 
    waitBlink[ led ][ 1 ] = 10U * ((unsigned int) led + 1U ); // ON millis

#if SERPRINT
    Serial.print( " OFF millis = " ); 
    Serial.print( waitBlink[ led ][ 0 ] ); 
    Serial.print( " ON millis = " ); 
    Serial.println( waitBlink[ led ][ 1 ] ); 
#endif

    pinMode( ledPin[ led ], OUTPUT ); // default output is LOW
    digitalWrite( ledPin[ led ], bitRead( ledState, led ));
  }  
#if SERPRINT
  Serial.println( ); 
  
  Serial.println( "Led <led> t <millisNow> x <pin state> n <next change>" );
#endif

  led = 0;
}

void loop()
{

  millisNow = (unsigned int)( millis() & 0xFFFF ); // lower 16 bits roll over just fine

  if ( millisNow - startBlink[ led ] >= waitBlink[ led ][ bitRead( ledState, led ) ] )  
  {
#if SERPRINT
    Serial.print( led, DEC ); 
    Serial.print( " t " ); 
    Serial.print( millisNow ); 
/*
    Serial.print( " states " ); 
    Serial.print( ledState, BIN );
*/
#endif

    startBlink[ led ] += waitBlink[ led ][ bitRead( ledState, led ) ]; // update start time
    // Thank You Robin2, this really does take care of late updates 

    bitWrite( ledState, led, ( bitRead( ledState, led ) == 0 )); // change led state bit
    digitalWrite( ledPin[ led ], bitRead( ledState, led ) ); // change the led pin  

#if SERPRINT
/*  put these in and see what it does occasionally to timing. then add more serial 
    Serial.print( " change " ); 
    Serial.print( ledState, BIN );
    Serial.print( " start " ); 
    Serial.print( startBlink[ led ] );
    Serial.print( " wait " ); 
    Serial.print( waitBlink[ led ][ bitRead( ledState, led ) ] );
*/
    Serial.print( " x " ); 
    Serial.print( bitRead( ledState, led ) );
    Serial.print( " n " ); 
    Serial.print( startBlink[ led ] + waitBlink[ led ][ bitRead( ledState, led ) ] );
    Serial.println();
    
    if ( runBeforeStop < 200 )
    {
      if ( !runBeforeStop-- )
      {
        while( 1 );
      }
    }
#endif

  }

  led++;
  if ( led >= LEDS )
  {
    led = 0;
  }
}

Thanks a lot!' I really appreciate it :slight_smile:

I can't type all this fast enough to get ahead of you. I go to post and "a new reply has been posted".
Fear not, this is the last installment until I see questions.

Fitting the sketches together, please ask questions about any part you are not SURE how it works.
If you find yourself adding even 1 delay() then you are not sure of how it all works!

Inside of loop() are like multiple sketches that run by themselves (but can influence others) in turn.
Each must have a turn as quickly as you want the whole to run smooth.
Currently the slowest thing in the sketches is Serial.print() which may hold up events by whole milliseconds.

No delay()! Even delay( 1 ) . In 1 millisecond ALL the buttons and then some may get checked.

Please note that I don't use interrupts in these as there is no need. Buttons and leds do not need interrupt speed.
Adding code that uses interrupts (libraries like 1-wire use them) should be easier without concerns over conflict.

Note the similarities in structure. The parts are made to be 'standard fit'.

Some time in future I will crank out library classes but I need to review C++ classes, haven't used them since 1999.

Thanks a lot! This is great. I've got a lot to learn but I'm excited to figure it out. I'll let u know if I have any issues. Thanks again for all your help .