I created a new function initButton to initialize the structure members that aren’t initialized in eventButton. That fixed it. I was hoping to avoid that step but obviously it’s necessary. Here’s the complete code. It’s a simple button event library. It’s in sketch form here for simplicity. This is all I need in terms of events for a current project, but I’d like to make the library version available to other users and maybe add a few more event types. Any feedback is welcome. Thanks.
// ActionButton Event Library
// 1/20/08
// Jeff Hoefs
#define DBL_CLICK_THRESH 100 // set double-press threshold to 100ms
#define DEBOUNCE 10 // set debounce time to 10ms
#define BUTTON_HOLD_TIME 1000 // set button hold trigger to 1000ms
#define PULL_UP 0 // using internal or external pull-up resistor
#define PULL_DOWN 1 // using external PULL_DOWN resistor
// make ActionButton a type def
typedef struct action_button {
boolean buttonCurrent;
boolean buttonLast;
boolean buttonPressedFlag;
boolean buttonDblClickFlag;
long buttonTimePressed;
long buttonTimeReleased;
int buttonHoldTime;
// Button Events
boolean buttonPressed;
boolean buttonHeld;
boolean buttonReleased;
boolean buttonDblClick;
} ActionButton;
void initButton(struct action_button *btn);
void buttonEvents(struct action_button *btn, int currentVal, boolean pressedVal);
void initButton(struct action_button *btn) {
btn->buttonLast = 1;
btn->buttonPressedFlag = 0;
btn->buttonDblClickFlag = 0;
btn->buttonTimePressed = 0;
btn->buttonTimeReleased = 0;
btn->buttonHoldTime = BUTTON_HOLD_TIME;
}
void buttonEvents(struct action_button *btn, int currentVal, boolean pressedVal) {
// clear button states
btn->buttonPressed = 0;
btn->buttonHeld = 0;
btn->buttonReleased = 0;
btn->buttonDblClick = 0;
// if button uses pull-down resistor, invert cur_val
if(pressedVal) currentVal = !currentVal;
// store current button state
btn->buttonCurrent = currentVal;
// if button is toggled
if(!btn->buttonCurrent && btn->buttonLast != btn->buttonCurrent) {
// debounce
if(millis() - btn->buttonTimePressed >= DEBOUNCE) {
// check for double click, account for millis roll over (9 hours)
if(millis() - btn->buttonTimeReleased > 0 && millis() - btn->buttonTimeReleased <= DBL_CLICK_THRESH) {
// don’t allow tripple click or any higher number of fast press events
if(!btn->buttonDblClickFlag) {
btn->buttonDblClick = 1;
btn->buttonDblClickFlag = 1;
}
}
// check for single press, always fired on initial press of double click, but never on 2nd press
// of double press event
else {
btn->buttonPressed = 1; // set state
btn->buttonDblClickFlag = 0;
}
btn->buttonLast = btn->buttonCurrent;
btn->buttonPressedFlag = 1;
btn->buttonTimePressed = millis();
}
}
// check for button released
else if(btn->buttonCurrent && btn->buttonLast != btn->buttonCurrent) {
// prevent on release from being triggered after a double click event
if(!btn->buttonDblClickFlag) {
btn->buttonReleased = 1; // set state
}
btn->buttonLast = btn->buttonCurrent;
btn->buttonPressedFlag = 0;
btn->buttonTimeReleased = millis();
}
else {
btn->buttonLast = btn->buttonCurrent;
if(btn->buttonPressedFlag) {
// button sustained press event
if(millis() - btn->buttonTimePressed >= btn->buttonHoldTime) {
btn->buttonHeld = 1; // set state
btn->buttonTimePressed = millis();
}
}
}
}
void setup() {
// test pull-down
pinMode(2, INPUT); // don’t enable internal pull-up for this pin
// test pull-up
pinMode(3, INPUT);
digitalWrite(3, HIGH); // enable internal pull-up resistor
beginSerial(19200);
}
void loop() {
// Create action buttons
ActionButton button1;
ActionButton button2;
// initialize buttons
initButton(&button1);
initButton(&button2);
// Set frequency for buttonHold events
// this means the buttonHold event will fire once every 1000ms
// while the button is held continuously
// default button hold time is 1000ms but you may override it
button1.buttonHoldTime = 2000;
while(true) {
// button, digital pin, specify if the button uses a pull-up or pull down resistor;
// note that the button must be passed as an address (&button_name)
buttonEvents(&button1, digitalRead(2), PULL_DOWN);
buttonEvents(&button2, digitalRead(3), PULL_UP);
// Test the button events
if(button1.buttonPressed) Serial.println(“Button 1 pressed”);
if(button1.buttonHeld) Serial.println(“Button 1 held”);
if(button1.buttonDblClick) Serial.println(“Button 1 double click”);
if(button1.buttonReleased) Serial.println(“Button 1 released”);
if(button2.buttonPressed) Serial.println(“Button 2 pressed”);
if(button2.buttonHeld) Serial.println(“Button 2 held”);
if(button2.buttonDblClick) Serial.println(“Button 2 double click”);
if(button2.buttonReleased) Serial.println(“Button 2 released”);
}
}