Detecting Multiple State Changes

Hello all,

I wanted to create a circuit for arduino uno using SIX pushbuttons and a common anode rgb LED, having each button represent a color, i.e. 1 for red, 2 for orange, 3 for yellow…

I understand that when using a single LED with a single button, i would declare my variables for the current state of the input pin, the old state of the input pin, and the state of the LED.

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables 
int val = 0;        //stores state of input pin
int old_val = 0;   //previous value of val     
int state = 0;    //0 is off while 1 is on 

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
}


void loop() {
  // read input pin:
  val = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if ((val == HIGH) && (old_val == LOW)) {
    // if the state has changed, increment the counter
    state = 1 - state;
    delay(10);
  }
  
  old_val = val;
  
  if (state == 1) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

Now if i want to do this for SIX buttons and six different colors (one at a time) , Im gonna need to use analogWrite, teach button state is read and stored and compared against its old state, etc. The thing i don’t understand is, do I need a val and old_val variable for each button? Im guessing i would use an array for the buttons, would i use an array for their states too?

Included is another code that takes a pot range and does something similar, I was considering editing and combining it with the other code.

// INPUT: Potentiometer should be connected to 5V and GND
int potPin = 3; // Potentiometer output connected to analog pin 3
int potVal = 0; // Variable to store the input from the potentiometer

// OUTPUT: Use digital pins 9-11, the Pulse-width Modulation (PWM) pins
// LED's cathodes should be connected to digital GND
int redPin = 9;   // Red LED,   connected to digital pin 9
int grnPin = 10;  // Green LED, connected to digital pin 10
int bluPin = 11;  // Blue LED,  connected to digital pin 11

// Program variables
int redVal = 0;   // Variables to store the values to send to the pins
int grnVal = 0;
int bluVal = 0;

void setup()
{
  pinMode(redPin, OUTPUT);   // sets the pins as output
  pinMode(grnPin, OUTPUT);   
  pinMode(bluPin, OUTPUT); 
}

// Main program
void loop()
{
  potVal = analogRead(potPin);   // read the potentiometer value at the input pin

  if (potVal < 341)  // Lowest third of the potentiometer's range (0-340)
  {                  
    potVal = (potVal * 3) / 4; // Normalize to 0-255

    redVal = 256 - potVal;  // Red from full to off
    grnVal = potVal;        // Green from off to full
    bluVal = 1;             // Blue off
  }
  else if (potVal < 682) // Middle third of potentiometer's range (341-681)
  {
    potVal = ( (potVal-341) * 3) / 4; // Normalize to 0-255

    redVal = 1;            // Red off
    grnVal = 256 - potVal; // Green from full to off
    bluVal = potVal;       // Blue from off to full
  }
  else  // Upper third of potentiometer"s range (682-1023)
  {
    potVal = ( (potVal-683) * 3) / 4; // Normalize to 0-255

    redVal = potVal;       // Red from off to full
    grnVal = 1;            // Green off
    bluVal = 256 - potVal; // Blue from full to off
  }
  analogWrite(redPin, redVal);   // Write values to LED pins
  analogWrite(grnPin, grnVal); 
  analogWrite(bluPin, bluVal);  
}

any help would be appreciated, Thanks!

I wrote a library to make that easier: Gammon Forum : Electronics : Microprocessors : Switches tutorial

Thanks Nick, it looks like I have a lot more studying to do. Unfortunately I'm not skilled enough to just take that library and run with it. I was hoping for perhaps the experience of another but since it's my first post, oh well. Seeing as I only got one reply it looks like I may need to restructure my question. Thanks again.

I just want to note that being that this is my first post on here, i do find it somewhat discouraging that only one person took the time to answer, despite the amount of users and knowledge on here. I figured that what I was trying to do is somewhat basic, perhaps too basic?

If you happen to read the post, and you don't understand or you think that I could improve it, please tell me how i could improve it rather than just reading it and moving on.
Better luck next time !

User0689:
any help would be appreciated, Thanks!

Here is an example for 5 buttons, that you can easily extend to any number of buttons just by writing the button pin numbers to the "buttonPins[]" array.

#define INPUTMODE INPUT_PULLUP    // INPUT or INPUT_PULLUP
#define BOUNCETIME 5              // bouncing time in milliseconds
byte buttonPins[]={2, 3, 4, 5, 6};// pin numbers of all buttons
#define NUMBUTTONS sizeof(buttonPins) // number of buttons (automatically calculated)
byte buttonState[NUMBUTTONS];  // array holds the actual HIGH/LOW states
byte buttonChange[NUMBUTTONS]; // array holds the state changes when button is pressed or released
enum{UNCHANGED,BUTTONUP,BUTTONDOWN};

void input(){
// read the input state and state changes of all buttons
  static unsigned long lastButtonTime; // time stamp for remembering the time when the button states were last read
  memset(buttonChange,0,sizeof(buttonChange)); // reset all old state changes
  if (millis()-lastButtonTime<BOUNCETIME) return;  // within bounce time: leave the function
  lastButtonTime=millis(); // remember the current time
  for (int i=0;i<NUMBUTTONS;i++) 
  {
    byte curState=digitalRead(buttonPins[i]);        // current button state
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // logic is inverted with INPUT_PULLUP
    if (curState!=buttonState[i])                    // state change detected
    {
      if (curState==HIGH) buttonChange[i]=BUTTONDOWN;
      else buttonChange[i]=BUTTONUP;
    }
    buttonState[i]=curState;  // save the current button state
  }
}


void output(){
// send a message to Serial if a button state (pressed/released) has changed
// button pressed: Send button pin number with minus sign
// button released: Send button pin number
  byte action;
  for (int i=0;i<NUMBUTTONS;i++)
  {
    switch (buttonChange[i])  
    {
      case BUTTONUP: Serial.println(buttonPins[i]);break;
      case BUTTONDOWN: Serial.println(-buttonPins[i]);break;
    }
  }
}


void setup() {
  Serial.begin(9600); // initialize Serial at 9600 baud
  // then initialize all buttons
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
}

void loop() {
  input();
  // processing(); // if you need any processing, write a function for it!  
  output();
}

Besides of that, the example shows how to use the IPO programming model using three functions for seperating

  • Input
  • Processing
  • Output

Doing that IPO programming logic, you can write any program for your Arduino as you like.

Thanks jurs, very kind. I searched and searched but never came across that particular sketch.

There is just one line that is foreign to me

enum{UNCHANGED,BUTTONUP,BUTTONDOWN};

What is "enum" and what does it do?

User0689:
Thanks jurs, very kind. I searched and searched but never came across that particular sketch.

There is just one line that is foreign to me

enum{UNCHANGED,BUTTONUP,BUTTONDOWN};

What is "enum" and what does it do?

An enumeration is just something that gives meaningful names for constant numbers.

If nothing else is specified, the counting begins as zero, so

enum{UNCHANGED,BUTTONUP,BUTTONDOWN};

is exactly the same as:

#define UNCHANGED 0
#define BUTTONUP 1
#define BUTTONDOWN 2

This sort of processing 'named constants' instead of 'constant numbers' is just helpful in writing 'speaking code' that tells you what's going on without writing comments.

To me it just looks more meaningful in the source code if I write:

switch (buttonChange[i])  
{
      case BUTTONUP: doThis();break;
      case BUTTONDOWN: doThat();break;
}

as if i would write

switch (buttonChange[i])  
{
      case 1: doThis();break;
      case 2: doThat();break;
}

which would do exactly the same, as BUTTONUP is just another name for 1 and BUTTONDOWN is another name for 2.

The thing i don’t understand is, do I need a val and old_val variable for each button? Im guessing i would use an array for the buttons, would i use an array for their states too?

Yes you need to know the old state for each button. An array would be a good idea.

Just to illustrate how it could be done without arrays, but with the switch manager library, here is testing for 3 switches:

#include <SwitchManager.h>

// pin assignments
const byte greenSwitchPin = 2;
const byte blueSwitchPin = 3;
const byte redSwitchPin = 4;


SwitchManager greenSwitch;
SwitchManager blueSwitch;
SwitchManager redSwitch;

// newState will be LOW or HIGH (the is the state the switch is now in)
// interval will be how many mS between the opposite state and this one
// whichPin will be which pin caused this change (so you can share the function amongst multiple switches)
 
void handleGreenSwitchPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  if (newState == LOW)
     {
     // do something
     }
  else
    {  // switch must be HIGH    
    // do something else
    }  
  }  // end of handleGreenSwitchPress

void handleBlueSwitchPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  if (newState == LOW)
     {
     // do something
     }
  else
    {  // switch must be HIGH    
    // do something else
    }  
  }  // end of handleBlueSwitchPress

void handleRedSwitchPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  if (newState == LOW)
     {
     // do something
     }
  else
    {  // switch must be HIGH    
    // do something else
    }  
  }  // end of handleRedSwitchPress

 
void setup ()
  {
  greenSwitch.begin (greenSwitchPin, handleGreenSwitchPress);
  blueSwitch.begin  (blueSwitchPin, handleBlueSwitchPress);
  redSwitch.begin   (redSwitchPin, handleRedSwitchPress);
  }
 
void loop ()
  {
  greenSwitch.check ();  // check for presses
  blueSwitch.check ();  // check for presses
  redSwitch.check ();  // check for presses
   
  // do other stuff here
  }

If you were doing similar things for each switch, then an array would probably be better.

Your question about arrays prompted me to revisit the library. To use an array it would be helpful for the callback function to know which switch was pressed (in case they all did a very similar thing). So I modified the library (and the post above) to get the pin number of the called switch, as part of the callback. Now using this we can simplify the whole thing to use an array of switches. For example:

#include <SwitchManager.h>

const byte SWITCH_COUNT = 6;
const byte EXAMPLE_LED = 13;

// pin assignments
const byte switches [SWITCH_COUNT] = { 2, 3, 4, 5, 6 };

SwitchManager mySwitches [SWITCH_COUNT];

// newState will be LOW or HIGH (the is the state the switch is now in)
// interval will be how many mS between the opposite state and this one
// whichPin will be which pin caused this change (so you can share the function amongst multiple switches)
 
void handleSwitchPress (const byte newState, const unsigned long interval, const byte whichPin)
  {
  if (newState == LOW)
     {
     switch (whichPin)
       {
       case 2: digitalWrite (EXAMPLE_LED, HIGH);  
       } // end of switch
     }
  else
    {  // switch must be HIGH    
     switch (whichPin)
       {
       case 2: digitalWrite (EXAMPLE_LED, LOW);  
       } // end of switch
    }  
  }  // end of handleGreenSwitchPress
 
void setup ()
  {
  for (int i = 0; i < SWITCH_COUNT; i++)
    mySwitches [i].begin (switches [i], handleSwitchPress);
  pinMode (EXAMPLE_LED, OUTPUT);
  }
 
void loop ()
  {
  for (int i = 0; i < SWITCH_COUNT; i++)
    mySwitches [i].check ();
   
  // do other stuff here
  }

Now we are managing 6 switches, and the switch manager keeps track of the previous state, and how long it was between press/release.

My example code just makes the LED on pin 13 echo whether or not switch 2 is pressed, just for simplicity.

If my reference to "callback" functions is a bit obscure read Gammon Forum : Electronics : Microprocessors : Function pointers / function callbacks / function variables

Nick that is precisely the answer(s) that i was looking for! thanks... Each switch will be doing similar things, i.e. setting analogWrite to a certain level to display said color. However I did write a sketch a few days ago to attempt this (my first full self written sketch), but it was unsuccessful due to the fact that i used delay for debouncing, and by the time i got through six switches there was so much delay that the colors did light up but they flashed, which was not my intention. If i removed the delay, or shortened it, the LED would dim significantly. I was not saving the "state" of any of the switches, it was written like "If button is HIGH, then analogWrite said amounts to each pin of LED". I thought i had saved this code and i would be glad to post it but I didn't, so I decided to start fresh. So Im going to study the code you posted in depth and see what i can produce. Granted i could move on to something else but i am determined to master this.

One more thing, you said you modified the library, is there a new download? Or should i do it on my own..?

The link on my page ( http://gammon.com.au/Arduino/SwitchManager.zip ) should now be the new version.

(It is, I just did a test download).

jurs:
Here is an example for 5 buttons, that you can easily extend to any number of buttons just by writing the button pin numbers to the "buttonPins[]" array.

#define INPUTMODE INPUT_PULLUP    // INPUT or INPUT_PULLUP

#define BOUNCETIME 5              // bouncing time in milliseconds
byte buttonPins[]={2, 3, 4, 5, 6};// pin numbers of all buttons
#define NUMBUTTONS sizeof(buttonPins) // number of buttons (automatically calculated)
byte buttonState[NUMBUTTONS];  // array holds the actual HIGH/LOW states
byte buttonChange[NUMBUTTONS]; // array holds the state changes when button is pressed or released
enum{UNCHANGED,BUTTONUP,BUTTONDOWN};

void input(){
// read the input state and state changes of all buttons
  static unsigned long lastButtonTime; // time stamp for remembering the time when the button states were last read
  memset(buttonChange,0,sizeof(buttonChange)); // reset all old state changes
  if (millis()-lastButtonTime<BOUNCETIME) return;  // within bounce time: leave the function
  lastButtonTime=millis(); // remember the current time
  for (int i=0;i<NUMBUTTONS;i++)
  {
    byte curState=digitalRead(buttonPins[i]);        // current button state
    if (INPUTMODE==INPUT_PULLUP) curState=!curState; // logic is inverted with INPUT_PULLUP
    if (curState!=buttonState[i])                    // state change detected
    {
      if (curState==HIGH) buttonChange[i]=BUTTONDOWN;
      else buttonChange[i]=BUTTONUP;
    }
    buttonState[i]=curState;  // save the current button state
  }
}

void output(){
// send a message to Serial if a button state (pressed/released) has changed
// button pressed: Send button pin number with minus sign
// button released: Send button pin number
  byte action;
  for (int i=0;i<NUMBUTTONS;i++)
  {
    switch (buttonChange[i]) 
    {
      case BUTTONUP: Serial.println(buttonPins[i]);break;
      case BUTTONDOWN: Serial.println(-buttonPins[i]);break;
    }
  }
}

void setup() {
  Serial.begin(9600); // initialize Serial at 9600 baud
  // then initialize all buttons
  for (int i=0;i<NUMBUTTONS;i++) pinMode(buttonPins[i],INPUTMODE);
}

void loop() {
  input();
  // processing(); // if you need any processing, write a function for it! 
  output();
}




Besides of that, the example shows how to use the IPO programming model using three functions for seperating
- Input
- Processing
- Output

Doing that IPO programming logic, you can write any program for your Arduino as you like.

Thanks for the info. These old posts is very usefull.