This is my button tester example sketch. It is a button debouncer modified to report the bounces actually given by the button (they vary). Those are shown as times in us (microseconds) with symbols _ for pressed and ^ for released (single char prints faster than word).
The sketch also shows the handler output with time in ms (milliseconds), don't get them confused.
// This is button_watcher, a test for button bounce --- free and open code Jul 31, 2015 by GFS
// Revised Nov 7, 2014 including many comments on operation of the sketch.
// Revised Dec 28, 2014 and Jul 31 - Aug 2, 2015 by GFS
// This sketch can be used to check buttons for needed debounce time.
// If you get multiple UP and DOWN messages for 1 press or 1 release
// then increase debounce time which is in microseconds.
// The goal is to have 1 and only 1 DOWN message for any press and
// 1 and only 1 UP message for any release.
/*
As you press and release your button/switch/jumper,
You will see are some lines that start with ^ or _ and a number.
Lines that start with ^ are button release and microseconds down before the release.
Lines that start with _ are button press and microseconds before the press.
Those lines are only information to show bounces the routine filtered out.
They are not considered stable UP or DOWN yet.
If the debounce time is too short, you will see multiple messages with UP or DOWN
in them for one press or release. Increase debounce time. Different buttons will need
different debounce times.
You can always debounce extra. Remember debounce value here is in microseconds.
Lines that have the word UP or DOWN tell how long since last stable state.
*/
byte ledPin = 13;
byte buttonPin = 7; // my button is a jumper wire grounded on the metal USB port box.
unsigned long btNow, btStart, btElapsed, btDebounce = 5000UL; // bt = button time. 10ms debounce
byte pinStates; // always the current state is in bit 0 and the previous is in bit 1
// value --- meaning with pin mode INPUT_PULLUP.
// 0 --- no change, pressed now and pressed last check
// 1 --- changed, released now, pressed last check
// 2 --- changes, pressed now, released last check
// 3 --- no change, released now and released last check
byte buttonState; // this only changes when the pin states are stable for some time.
byte buttonPrev; // this only changes when the pin states are stable for some time.
unsigned long checkCounter; // how many checks since last stable change
void setup( void )
{
Serial.begin( 115200 );
Serial.println( "\n Button Bounce Check" );
pinMode( ledPin, OUTPUT ); // default os LOW
pinMode( buttonPin, INPUT_PULLUP ); // default is pullup HIGH
pinStates = 1; // sets initial pin state as not pressed
// buttonState logic will be 0=false for stable not pressed, 1=true for stable pressed
}
inline void buttonHandler( void ) // just so the cleanliness police don't freak out
{
pinStates &= 1; // bit mask, only 0 or 1 are possible values
pinStates *= 2; // current pin state becomes previous pin state as 0 or 2
pinStates += digitalRead( buttonPin ); // read the current pin state into bit 0
// now pinStates holds past and present states as 0 to 3
btNow = micros(); // time at loop start and read pin state to within 4 usecs
btElapsed = btNow - btStart;
switch ( pinStates )
{
case 0 : // 0 --- no change, pressed now and pressed last check
if ( buttonState == 1 ) // already past debounce, added July 31, 2015
{
btStart = btNow; // reset the state change window start IF debounce micros is up
}
else if ( btElapsed >= btDebounce )
{
buttonState = 1; // button is pressed now long enough to be considered stable
btStart = btNow; // reset the state change window start IF debounce micros is up
}
break;
case 1 : // 1 --- changed, released now, pressed last check
Serial.print( '^ ' ); // show the bounce
Serial.print( btElapsed ); // show micros between checks, the change detect window
Serial.println( "us" );
buttonState = 2; // button release detected
btStart = btNow; // reset the state change window start
break;
case 2 : // 2 --- changed, pressed now, released last check
Serial.print( '_ ' ); // show the bounce
Serial.print( btElapsed ); // show micros between checks, the change detect window
Serial.println( "us" );
buttonState = 3; // button press detected
btStart = btNow; // reset the state change window start
break;
case 3 : // 3 --- no change, released now and released last check
if ( buttonState == 0 ) // already past debounce, added July 31, 2015
{
btStart = btNow; // reset the state change window start IF debounce micros is up
}
else if ( btElapsed >= btDebounce )
{
buttonState = 0; // button is released now long enough to be considered stable
btStart = btNow; // reset the state change window start IF debounce micros is up
}
break;
}
}
void loop( void )
{
static unsigned long lastChange; // time between pin state change
buttonHandler( ); // this code only watches the button, job never finished
// this if() block only watches buttonState for change, job never finished
if ( buttonState != buttonPrev ) // button stable state change detected
{
buttonPrev = buttonState;
digitalWrite( ledPin, buttonState );
if ( buttonState ) // if not zero, it is true
{
Serial.print( F( "\nDOWN " ));
}
else
{
Serial.print( F( "UP " ));
}
Serial.print( millis() - lastChange );
Serial.println( F( " ms" ));
lastChange = millis();
}
}
And one other tip: Arduino RAM is small. Break the always use an int habit and you will save RAM (and have faster running code on these 8-bit machines), especially in arrays. If the range of values a variable may take is within 0 to 255 (pin number, for example) then use byte instead. A char variable is good for -128 to +127.