Press button (pin 7 to GND) and led13 blinks until button release.
Note: code updated to show total bounce+debounce time in micros and a loop() speed counter.
// buttonsketch 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Feb 24/18 by GFS
/*
Button Debounce Example, free by GoForSmoke
This code shows use of struct, union, bit fields and 16 bit timers.
It can work with many buttons, uses structs to hold button data.
Button data structs can be arrayed even for matrix arrangements.
-- for this example connect a button between pin 7 and GND
--- or stick a jumper in pin 7 and while holding the board steady
--- tap the free end onto the grounded USB port box to press.
Yes 16 bit timers, and they time micros where millis skips digits.
My debounce time is 2000 micros after the last change detected.
*/
#include <avr/io.h>
#include "Arduino.h"
// these defines are used to read and write button status bits
// button status tells the last 2 button reads and whether or not it is bouncing (read again later)
// all in one small number, 0 to 7. Your sketch may only care if the value is 2 = press event.
#define CURRENT_1 1
#define PREVIOUS_2 2
#define HISTORY_3 3
#define BOUNCE_4 4
#define pre_BOUNCE_8 8
struct statusBits // this is a custom variable with named bit fields
{
byte current : 1; // 1 bit can only be 0 or 1
byte previous : 1;
byte bounce : 1;
byte pre_bounce : 1;
byte : 4; // these 4 bits are unnamed, could be used if named to count 0 to 15
};
union buttonRead // the same data can be read different ways as different names
{
struct statusBits bits;
byte value;
};
buttonRead buttonState; // used to read button status as a single state value
struct buttonData // 6 bytes
{
byte pin; // these static variables are private to this function
statusBits state; // current, previous, bounce, pre_bounce
unsigned int start; // 16 bit time
};
buttonData demoButton = { 7, CURRENT_1, 0 };
const unsigned int debounceMicros = 2000; // button response takes at least 1 milli after the last bounce
statusBits buttonTask( buttonData *button )
{
// -------------- start of button handler: debounces as needed, sets button->state
button->state.previous = 0;
button->state.previous = button->state.current;
button->state.current = digitalRead( button->pin );
if (( button->state.current ) != ( button->state.previous )) // if different,
// state change detected
{
if ( button->state.bounce == 0 )
{
button->state.bounce = 1;
button->state.pre_bounce = button->state.previous;
}
button->start = micros(); // restarts the bounce clock
}
else if ( button->state.bounce > 0 ) // then wait to clear debounce bit
{
unsigned int now = micros(); // temp stack variable
if ( now - button->start >= debounceMicros ) // then stable button state achieved
{
button->state.previous = button->state.pre_bounce;
button->state.bounce = button->state.pre_bounce = 0;
}
}
// -------------- end of button handler: debounces as needed, sets button->state
return button->state;
}
byte ledState, ledPin = 13;
unsigned int startBlink, waitBlink;
void blinkLed()
{
if ( waitBlink > 0 )
{
unsigned int now = millis(); // temp variable now = low 16 bits of millis
if ( now - startBlink >= waitBlink )
{
ledState = !ledState;
digitalWrite( ledPin, ledState );
startBlink += waitBlink;
}
}
else if ( ledState > 0 )
{
ledState = 0;
digitalWrite( ledPin, LOW );
}
}
void setup()
{
Serial.begin( 115200 );
Serial.println( F( "\n\n\n Button Debounce Example, free by GoForSmoke\n" ));
Serial.println( F( "This code shows use of struct, union, bit fields and 16 bit timers." ));
Serial.println( F( "It can work with many buttons, uses structs to hold button data." ));
Serial.println( F( "Button data structs can be arrayed even for matrix arrangements." ));
Serial.println( F( "\n-- for this example connect a button between pin 7 and GND" ));
Serial.println( F( "--- or stick a jumper in pin 7 and while holding the board steady" ));
Serial.println( F( "--- tap the free end onto the grounded USB port box to press." ));
pinMode( demoButton.pin, INPUT_PULLUP );
pinMode( ledPin, OUTPUT );
};
void loop()
{
blinkLed();
buttonState.bits = buttonTask( &demoButton ); // read bit fields into union
// this is why the buttonRead union was made, to read the bits as one state value
switch ( buttonState.value )
{
case CURRENT_1 :
Serial.print( F( "up " ));
Serial.println( millis());
Serial.println();
waitBlink = 0;
break;
case PREVIOUS_2 :
Serial.print( F( "down " ));
Serial.println( millis());
waitBlink = 200;
break;
}
// end of do something with button
// put any new tasks here or up top or between the handler and do something tasks
}
I think that the bit-field names do make it easier to read what the button handler function does.
The big leap may be in going from the four status bits to a single state value to switch to case code for.
Yes, I did this all using bit logic commands before but this way might be within reach of more forum members.
Handling the button with a function means no library to mess with, some members don't like libraries, and it makes the code more accessible. I made the button data as a struct so that multiple buttons can easily be added. The best way is to make a button struct array and process only one button per pass through loop() so as not to block any other tasks in the code now or likely added in the future, just don't block and the sketch can have a future!
I'd put in a loop() counter but it would break up the down-up pairing on Serial Monitor.
First, please test even if only with a jumper in pin 7 touched to the USB port box to ground it, hold the board still though!
Serial Monitor should be set up for 115200 baud and open. up and down times are in millis, see how fast you can tap the button... I got down to 31ms between up and down and I'm an old coot.
I get solid results with 2000 micros of pin reads since the last change as debounce time --- no 2ms wide bounces.
What's your mileage please?
This example has a blink task as well as a button task. As now it blinks only when the button is held down.
I will add a user command task if there's interest. How does 40 commands with average 5 letters, stored in EEPROM sound?