Go Down

Topic: Problem debouncing multiple buttons (Read 12958 times) previous topic - next topic

HazardsMind

Post your full code in code tags.
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

Yigiter007

Whole code was too big for one comments I split it among two comments
Quote

/*00000000      Libraries included    00000000*/

#include <SerialGraphicLCD.h>
#include <SoftwareSerial.h>

/*00000000      GLCD screen size  00000000*/

#define maxX 127//159
#define maxY 63 //127

/*00000000      variables for PINS    00000000*/

//Bup Bdown Bleft Bright Bcenter But1 But2 But3
const int buttonPin[8]={4,5,6,7,8,9,10,11};
const int ledPin13 = 13;       // the number of the LED pin

/*00000000    variables for debounce    00000000*/

//Bup Bdown Bleft Bright Bcenter But1 But2 But3
int buttonNumber[8]={0,1,2,3,4,5,6,7};
int buttonState[8];
int lastButtonState[8]={HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH};
long lastDebounceTime[8];
long debounceDelay[8]={10,10,10,10,10,10,10,10};
int reading[8];
int ledState=LOW;         // the current state of the output pin

/*00000000    delay replacement is timecheck variables    00000000*/

long previousMillis[100];        // will store last time LED was updated
unsigned long currentMillis[100];
int actionstate;
long interval;
int optionrowTextDisplayCounter=false;

/*00000000    buttonlook variables for buttons    00000000*/

int buttonlook[8]={0,0,0,0,0,0,0,0}; //variables to store if button is pressed for pin10

/*00000000    variables for menus    00000000*/

int menustack[6]={1,0,0,0,0,0}; //array acting as stack for menu porder when hopping from one menu to the other.
                               // set array size to number of menus desired
int currentmenuspot=1;
int prevmenuspot=1;
int menustackspot=0;
int menunumber;

int menupage=0; //0=Mainmenu 1=AirQ 2=TEMP 3=HEART 4=MORE
int optionrow[4][4]={{11,12,13,14},{21,22,23,24},{31,32,33,34},{41,42,43,44}}; //2D array as a map for menu options
int optionspotx=0;
int optionspoty=0;

int enterbuttons[5]={0,0,0,0,0}; //debounce puts a 1 to be later tested if button was pressed 1=pressed 0=not pressed
int enterbuttoncounter[5]={0,0,0,0,0};

/*00000000    LCD class declaration    00000000*/
LCD LCD;
/*00000000    SETUP    00000000*/

void setup()
{
  
  pinMode(buttonPin[0], INPUT);     //button on pin 4
  pinMode(buttonPin[1], INPUT);     //button on pin 5
  pinMode(buttonPin[2], INPUT);     //button on pin 6
  pinMode(buttonPin[3], INPUT);     //button on pin 7
  pinMode(buttonPin[4], INPUT);     //button on pin 8
  pinMode(buttonPin[5], INPUT);     //button on pin 9
  pinMode(buttonPin[6], INPUT);     //button on pin 10
  pinMode(buttonPin[7], INPUT);     //button on pin 11
 
  pinMode(ledPin13, OUTPUT);   // pin 13 on board LED
  
  for(int i=0; i>99; i++){previousMillis=0;}
  
  LCD.setBacklight(20);
  LCD.clearScreen();

  LCD.setHome();
  LCD.printStr(" Program starts in "); //displays "program is starting in 10/9/8 etc"
    
  for(int i = 5; i >= 0; i--) //splash screen
    {
      LCD.setX(113);
      LCD.setY(0);
      LCD.printNum(i);
      delay(500);
    } 
  LCD.clearScreen();
  delay(500);
}

/*00000000    LOOP    00000000*/

void loop()
{
  
selectmove(); //retrieves states of buttons to see if they were pressed
optionrowValueDisplay(); //display if buttons were pressed
optionrowTextDisplay();

}

/*00000000    displays when buttons are pressed     00000000*/

void optionrowValueDisplay()
{

actionstate=timecheck(200, 1);  
if(actionstate==1)
{
  LCD.setX(50);
  LCD.setY(00);
  LCD.printNum(optionspotx);
}

actionstate=timecheck(200, 3);  
if(actionstate==1)
{
  LCD.setX(50);
  LCD.setY(9);
  LCD.printNum(optionspoty);
}

actionstate=timecheck(200, 5);  
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(18);
  LCD.printNum(enterbuttoncounter[0]);
}

actionstate=timecheck(200, 7);  
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(27);
  LCD.printNum(enterbuttoncounter[1]);
}

actionstate=timecheck(200, 12);  
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(36);
  LCD.printNum(enterbuttoncounter[2]);
}

actionstate=timecheck(200, 13);  
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(45);
  LCD.printNum(enterbuttoncounter[3]);
}


}


Yigiter007

Quote

void optionrowTextDisplay()
{
  
if(optionrowTextDisplayCounter==true)
  {
      actionstate=timecheck(125, 0);  
      if(actionstate==1)
        {
          LCD.setHome();
          LCD.printStr("x spot ");
        }
      actionstate=timecheck(125, 2);  
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(9);
          LCD.printStr("y spot ");
        }
      actionstate=timecheck(125, 4);  
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(18);
          LCD.printStr("center but ");
        }
      actionstate=timecheck(125, 6);  
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(27);
          LCD.printStr("button 1 ");
        }
     optionrowTextDisplayCounter=true;
  }
}

void selectmove()
{
  
for(int i=0; i<8; i++)
{
cleardebounce(); //clears debounces variables
debounce(buttonNumber); //debounces input pins
if(buttonlook==1) //loops through all the pins
{

  switch(i)
          {
              case 0:        //action for when Bup button is pressed
                if(optionspoty!=3)
                  {
                    optionspoty=optionspoty+1;
                  }
                break;

              case 1:       //action for when Bdown button is pressed
                if(optionspoty!=0)
                  {
                    optionspoty=optionspoty-1;   
                  } 
                break;
               
              case 2:        //action for when Bleft button is pressed
                if(optionspotx!=0)
                  {
                    optionspotx=optionspotx-1;
                  } 
                break;
                
              case 3:        //action for when Bright button is pressed
                if(optionspotx!=3)
                  {
                    optionspotx=optionspotx+1;
                  } 
                break;
                
              case 4:        //action for when Bcenter button is pressed
                    enterbuttons[0]=1;
                    ++enterbuttoncounter[0];
                break;
                
              case 5:        //action for when But1 button is pressed
                    enterbuttons[1]=1;
                    ++enterbuttoncounter[1];
                break;
                
              case 6:        //action for when But2 button is pressed
                    enterbuttons[2]=1;
                    ++enterbuttoncounter[2];
                break;
                
              case 7:        //action for when But3 button is pressed
                    enterbuttons[3]=1;
                    ++enterbuttoncounter[3];
                break;
               
            }

}


}

}

/*00000000   debounce functions    00000000*/


void cleardebounce() //clears variables so that they may be retested. Must be called before each debounce.
{
  for(int i=0; i<7; i++)
    {
      buttonlook=0;
    }
    
  for(int i=0; i<4; i++)
    {
      enterbuttons=0;
    }
}

/*00000000   debounce   00000000*/

int debounce(int buttonNumber) //recieves a pin and debounces that pin
{
  reading[buttonNumber] = digitalRead(buttonPin[buttonNumber]);
  if (reading[buttonNumber] != lastButtonState[buttonNumber])
  {lastDebounceTime[buttonNumber] = millis();}
  
  if ((millis() - lastDebounceTime[buttonNumber]) > debounceDelay[buttonNumber])
  {
    if (reading[buttonNumber] != buttonState[buttonNumber])
    {
      buttonState[buttonNumber] = reading[buttonNumber];

      if (buttonState[buttonNumber] == LOW)
      {
        ledState = !ledState;
        buttonlook[buttonNumber]=1;        //changes variable to 1 to be later tested if true
      }
    }
  }
  
  // set the LED:
  digitalWrite(ledPin13, ledState);// lights the LED so you know button was pressed

  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState[buttonNumber] = reading[buttonNumber];
}

/*00000000   timecheck   00000000*/

int timecheck(long interval, int i)
{
  currentMillis = millis();
 
  if(currentMillis - previousMillis > interval)
    {
      previousMillis = currentMillis;   // save the last time function was performed
      actionstate = 1;
    }
  else
    {actionstate = 0;}
  
return actionstate;
}


Yigiter007

For whatever reason when I post the code in the comments in forum code format it took out the brackets next to my variables. in
Code: [Select]
int timecheck(long interval, int i) each variable is a spot in an array, so
Code: [Select]
currentMillis is actually
Code: [Select]
currentMillis[i] where the function sends i a place in the array for that particular call so that the values of timecheck are stores and different from other times the function is called.

68tjs

Have a look on boards schematics (Arduino, Texas, NXP, ST etc....) all use hardware debouncing , not software debouncing.
Put a capacitor in parallel with switch button (100nF)  it will works simply.

Jiggy-Ninja

That's because you used Quote tags instead of Code tags.

I use a struct for my buttons, with an appropriate debounce function like this:
In a separate button.h file (because Arduino IDE is stupid about function prototypes):
Code: [Select]
#ifndef DEBOUNCE_H
#define DEBOUNCE_H

typedef struct
{
  unsigned char pin;
  unsigned char level;
  unsigned char edge;
  unsigned char prev_level;

  unsigned long t_prev_ms;
  unsigned long debounce_delay;
} button_t;

void debounce_button( button_t* );
void init_button( button_t*, unsigned char, unsigned long, unsigned char=1 );

#endif

In button.c
Code: [Select]
#include "Arduino.h"
#include "button.h"

void debounce_button( button_t *b )
{
  unsigned long t_now_ms = millis();
  byte curr_level = digitalRead( b->pin );

  // Edge can only be active for one cycle.
  // Clear it at the beginning.
  b->edge = 0;
 
  // If the button level has changed, reset the debounce timestamp.
  if( curr_level != b->prev_level )
    b->t_prev_ms = t_now_ms;

  // If the debounce timer has not been reset for the duration of the debounce interval,
  // The current state is valid.
  if( t_now_ms - b->t_prev_ms > b->debounce_delay )
  {
    // If the new level is different from the old,
    //  set the edge flag.
    if( b->level != curr_level )
      b->edge = 1;
    b->level = curr_level;
  }
  b->prev_level = curr_level;
}

void init_button( button_t *b, unsigned char pin, unsigned long debounce, unsigned char level )
{
  b->pin = pin;
  b->level = level;
  b->edge = 0;
  b->prev_level = level;
  b->t_prev_ms = 0;
  b->debounce_delay = debounce;
}

I made a library out of my code for my own use, but you can adapt it to how you need. Instead of using a struct like I did, you can use parallel arrays to achieve the same thing.

On thing I strongly recommend against, do not perform actions in your debounce function. Your debounce function should strictly determine the state of the switch.

Here's a sample sketch I used for 1 button:
Code: [Select]
#include "button.h"

button_t button;
byte LED_level = LOW;

void setup() {
  // put your setup code here, to run once:
  init_button( &button, 12, 50 );
 
  pinMode( button.pin, INPUT_PULLUP );
  pinMode( 13, OUTPUT );
}

void loop() {
  // put your main code here, to run repeatedly:
  debounce_button( &button );
 
  if( (button.level==LOW) && button.edge )
  {
    LED_level = (LED_level==HIGH) ? LOW : HIGH;
  }
 
  digitalWrite( 13, LED_level );
}
Hackaday: https://hackaday.io/MarkRD
Advanced C++ Techniques: https://forum.arduino.cc/index.php?topic=493075.0

graynomad

Quote
For whatever reason when I post the code in the comments in forum code format it took out the brackets next to my variables.

CODE tags, not a quote. [ i ] is a BB code for italics.

Go back and change your posts.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Yigiter007

On a scale of one to ten my programming skills are a 2 maybe 3. I don't know how to do structs, i haven't seen them before or quite understand them. Can I take your code as is and plug it into mine and just change the names of the calls? I don't mind learning about them i just need a little guidance.

graynomad

So you are ignoring our (multiple) requests for code tags. Good luck with the project.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Grumpy_Mike

Quote
On a scale of one to ten my programming skills are a 2 maybe 3.

And on a scale of one to ten you social skills are a -5.

If you will not do what is asked of you, for the very good reason of the forum software not mangling your code then why should any one help you?

Yigiter007

Code: [Select]
/*00000000      Libraries included    00000000*/

#include <SerialGraphicLCD.h>
#include <SoftwareSerial.h>

/*00000000      GLCD screen size  00000000*/

#define maxX 127//159
#define maxY 63 //127

/*00000000      variables for PINS    00000000*/

//Bup Bdown Bleft Bright Bcenter But1 But2 But3
const int buttonPin[8]={4,5,6,7,8,9,10,11};
const int ledPin13 = 13;       // the number of the LED pin

/*00000000    variables for debounce    00000000*/

//Bup Bdown Bleft Bright Bcenter But1 But2 But3
int buttonNumber[8]={0,1,2,3,4,5,6,7};
int buttonState[8];
int lastButtonState[8]={HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH,HIGH};
long lastDebounceTime[8];
long debounceDelay[8]={10,10,10,10,10,10,10,10};
int reading[8];
int ledState=LOW;         // the current state of the output pin

/*00000000    delay replacement is timecheck variables    00000000*/

long previousMillis[100];        // will store last time LED was updated
unsigned long currentMillis[100];
int actionstate;
long interval;
int optionrowTextDisplayCounter=false;

/*00000000    buttonlook variables for buttons    00000000*/

int buttonlook[8]={0,0,0,0,0,0,0,0}; //variables to store if button is pressed for pin10

/*00000000    variables for menus    00000000*/

int menustack[6]={1,0,0,0,0,0}; //array acting as stack for menu porder when hopping from one menu to the other.
                               // set array size to number of menus desired
int currentmenuspot=1;
int prevmenuspot=1;
int menustackspot=0;
int menunumber;

int menupage=0; //0=Mainmenu 1=AirQ 2=TEMP 3=HEART 4=MORE
int optionrow[4][4]={{11,12,13,14},{21,22,23,24},{31,32,33,34},{41,42,43,44}}; //2D array as a map for menu options
int optionspotx=0;
int optionspoty=0;

int enterbuttons[5]={0,0,0,0,0}; //debounce puts a 1 to be later tested if button was pressed 1=pressed 0=not pressed
int enterbuttoncounter[5]={0,0,0,0,0};

/*00000000    LCD class declaration    00000000*/
LCD LCD;
/*00000000    SETUP    00000000*/

void setup()
{
 
  pinMode(buttonPin[0], INPUT);     //button on pin 4
  pinMode(buttonPin[1], INPUT);     //button on pin 5
  pinMode(buttonPin[2], INPUT);     //button on pin 6
  pinMode(buttonPin[3], INPUT);     //button on pin 7
  pinMode(buttonPin[4], INPUT);     //button on pin 8
  pinMode(buttonPin[5], INPUT);     //button on pin 9
  pinMode(buttonPin[6], INPUT);     //button on pin 10
  pinMode(buttonPin[7], INPUT);     //button on pin 11

  pinMode(ledPin13, OUTPUT);   // pin 13 on board LED
 
  for(int i=0; i>99; i++){previousMillis[i]=0;}
 
  LCD.setBacklight(20);
  LCD.clearScreen();

  LCD.setHome();
  LCD.printStr(" Program starts in "); //displays "program is starting in 10/9/8 etc"
   
  for(int i = 5; i >= 0; i--) //splash screen
    {
      LCD.setX(113);
      LCD.setY(0);
      LCD.printNum(i);
      delay(500);
    }
  LCD.clearScreen();
  delay(500);
}

/*00000000    LOOP    00000000*/

void loop()
{
 
selectmove(); //retrieves states of buttons to see if they were pressed
optionrowValueDisplay(); //display if buttons were pressed
optionrowTextDisplay();

}

/*00000000    displays when buttons are pressed     00000000*/

void optionrowValueDisplay()
{

actionstate=timecheck(200, 1); 
if(actionstate==1)
{
  LCD.setX(50);
  LCD.setY(00);
  LCD.printNum(optionspotx);
}

actionstate=timecheck(200, 3); 
if(actionstate==1)
{
  LCD.setX(50);
  LCD.setY(9);
  LCD.printNum(optionspoty);
}

actionstate=timecheck(200, 5); 
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(18);
  LCD.printNum(enterbuttoncounter[0]);
}

actionstate=timecheck(200, 7); 
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(27);
  LCD.printNum(enterbuttoncounter[1]);
}

actionstate=timecheck(200, 12); 
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(36);
  LCD.printNum(enterbuttoncounter[2]);
}

actionstate=timecheck(200, 13); 
if(actionstate==1)
{
  LCD.setX(70);
  LCD.setY(45);
  LCD.printNum(enterbuttoncounter[3]);
}


}

void optionrowTextDisplay()
{
 
if(optionrowTextDisplayCounter==true)
  {
      actionstate=timecheck(125, 0); 
      if(actionstate==1)
        {
          LCD.setHome();
          LCD.printStr("x spot ");
        }
      actionstate=timecheck(125, 2); 
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(9);
          LCD.printStr("y spot ");
        }
      actionstate=timecheck(125, 4); 
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(18);
          LCD.printStr("center but ");
        }
      actionstate=timecheck(125, 6); 
      if(actionstate==1)
        {
          LCD.setX(0);
          LCD.setY(27);
          LCD.printStr("button 1 ");
        }
     optionrowTextDisplayCounter=true;
  }
}

void selectmove()
{
 
for(int i=0; i<8; i++)
{
cleardebounce(); //clears debounces variables
debounce(buttonNumber[i]); //debounces input pins
if(buttonlook[i]==1) //loops through all the pins
{

  switch(i)
          {
              case 0:        //action for when Bup button is pressed
                if(optionspoty!=3)
                  {
                    optionspoty=optionspoty+1;
                  }
                break;

              case 1:       //action for when Bdown button is pressed
                if(optionspoty!=0)
                  {
                    optionspoty=optionspoty-1;   
                  }
                break;
               
              case 2:        //action for when Bleft button is pressed
                if(optionspotx!=0)
                  {
                    optionspotx=optionspotx-1;
                  }
                break;
               
              case 3:        //action for when Bright button is pressed
                if(optionspotx!=3)
                  {
                    optionspotx=optionspotx+1;
                  }
                break;
               
              case 4:        //action for when Bcenter button is pressed
                    enterbuttons[0]=1;
                    ++enterbuttoncounter[0];
                break;
               
              case 5:        //action for when But1 button is pressed
                    enterbuttons[1]=1;
                    ++enterbuttoncounter[1];
                break;
               
              case 6:        //action for when But2 button is pressed
                    enterbuttons[2]=1;
                    ++enterbuttoncounter[2];
                break;
               
              case 7:        //action for when But3 button is pressed
                    enterbuttons[3]=1;
                    ++enterbuttoncounter[3];
                break;
               
            }

}


}

}

/*00000000   debounce functions    00000000*/


void cleardebounce() //clears variables so that they may be retested. Must be called before each debounce.
{
  for(int i=0; i<7; i++)
    {
      buttonlook[i]=0;
    }
   
  for(int i=0; i<4; i++)
    {
      enterbuttons[i]=0;
    }
}

/*00000000   debounce   00000000*/

int debounce(int buttonNumber) //recieves a pin and debounces that pin
{
  reading[buttonNumber] = digitalRead(buttonPin[buttonNumber]);
  if (reading[buttonNumber] != lastButtonState[buttonNumber])
  {lastDebounceTime[buttonNumber] = millis();}
 
  if ((millis() - lastDebounceTime[buttonNumber]) > debounceDelay[buttonNumber])
  {
    if (reading[buttonNumber] != buttonState[buttonNumber])
    {
      buttonState[buttonNumber] = reading[buttonNumber];

      if (buttonState[buttonNumber] == LOW)
      {
        ledState = !ledState;
        buttonlook[buttonNumber]=1;        //changes variable to 1 to be later tested if true
      }
    }
  }
 
  // set the LED:
  digitalWrite(ledPin13, ledState);// lights the LED so you know button was pressed

  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState[buttonNumber] = reading[buttonNumber];
}

/*00000000   timecheck   00000000*/

int timecheck(long interval, int i)
{
  currentMillis[i] = millis();

  if(currentMillis[i] - previousMillis[i] > interval)
    {
      previousMillis[i] = currentMillis[i];   // save the last time function was performed
      actionstate = 1;
    }
  else
    {actionstate = 0;}
 
return actionstate;
}

Yigiter007

Sorry I thought I had fixed the previous posted code. Here is the same code posted in code format. Sorry about the delayed response. (also attached the file if anyone wants it)

Yigiter007

Did anyone look at my code? Or did everyone leave this topic. Sorry for the delayed code format fix.

graynomad

I'm looking at it but it's hard to see what's happening with all the other code. You should really have a test program that does nothing but debounce the buttons and get that module working, then add it to the LCD stuff.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Yigiter007

The debounce function works, but I think either my pseudo time delay I created or something with the way LCD.printStr is  displaying text doesn't work. I can see the LED pin 13 light up when i test the debounce function and it works with no delay.

The  LCD.printNum function works fine and displays the numbers correctly and I used printNum and printStr the same just put them in different functions since the text only needs to be put once and stays there until it is cleared or over written.

Could it be since that I'm running through the whole code without delays that the LCD.printStr isn't getting enough time to display its text before it gets called again further down the code? Maybe something to do with the buffer.

I can chop my code up into modular pieces and test different functions if you want me to.

Go Up