4-way button + multi-button boucing

Hi guys, I ám really new at this stuff (reading arduino for dummies and all… :confused: ).
I would like to control some lights (yeah, nothing new here!!!) but I´m stuck.

At first I learned how to turn LEDs on/off :slight_smile:
Getting more “complicated” I could use a relay to active eletrical lights.
Then looking around I found that I could use a single push-button to do more than on/off, by making it understand the “double click” and “hold”.
Since all that used just one button, I tried to make the same with 2 or 3 buttons and that´s where I am stuck.

Looking futher I could find a multi-button boucing script that would help me read all buttons that I want, but by using it, I will lose the “4-way” function!!!

In other words, I would like to light up a room with a push-button, but this button need to light on/off other rooms if I long press it (for example). And the other rooms could make the same (or not!) with their one push-buttons.

The 4-way button I find out, and which is working like a charm is:

/* 4-Way Button:  Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch

By Jeff Saltzman
Oct. 13, 2009

To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
1) Click:  rapid press and release
2) Double-Click:  two clicks in quick succession
3) Press and Hold:  holding the button down
4) Long Press and Hold:  holding the button for a long time 
*/

#define buttonPinA0 A0        // analog input pin to use as a digital input
#define ledPin13 13          // digital output pin for LED 1
#define ledPin8 8          // digital output pin for LED 2

// LED variables
boolean ledVal13 = false;    // state of LED 1
boolean ledVal8 = true;    // state of LED 2
//=================================================

void setup() {
   // Set button input pin
   pinMode(buttonPinA0, INPUT_PULLUP);
   
   pinMode(ledPin13, OUTPUT);
   digitalWrite(ledPin13, ledVal13);
   
   pinMode(ledPin8, OUTPUT);
   digitalWrite(ledPin8, ledVal8);
}

void loop() {
   // Get button event and act accordingly
   int b = checkButton();
   if (b == 1) clickEvent();
   if (b == 2) doubleClickEvent();
   if (b == 3) holdEvent();
   
/* MANTER APERTADO POR MUITO TEMPO
   if (b == 4) longHoldEvent();
*/   
}

//=================================================
// Events to trigger

void clickEvent() {
   ledVal13 = !ledVal13;
   digitalWrite(ledPin13, ledVal13);
}
void doubleClickEvent() {
   ledVal8 = !ledVal8;
   digitalWrite(ledPin8, ledVal8);
}
void holdEvent() {
   ledVal13 = !ledVal13;
   ledVal8 = !ledVal13;
   digitalWrite(ledPin13, ledVal13);
   digitalWrite(ledPin8, ledVal8);
}

/* MANTER APERTADO POR MUITO TEMPO
void longHoldEvent() {
   ledVal13 = !ledVal13;
   ledVal8 = ledVal13;
   digitalWrite(ledPin13, ledVal13);
   digitalWrite(ledPin8, ledVal8);
}
*/


//----------------------------------------------------------------------------

//=================================================
//  MULTI-CLICK:  One Button, Multiple Events

// Button timing variables
int debounce = 20;          // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250;            // max ms between clicks for a double click event
int holdTime = 1000;        // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000;    // ms long hold period: how long to wait for press+hold event

// Button variables
boolean buttonVal = HIGH;   // value read from button
boolean buttonLast = HIGH;  // buffered value of the button's previous state
boolean DCwaiting = false;  // whether we're waiting for a double click (down)
boolean DConUp = false;     // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true;    // whether it's OK to do a single click
long downTime = -1;         // time the button was pressed down
long upTime = -1;           // time the button was released
boolean ignoreUp = false;   // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false;        // when held, whether to wait for the up event
boolean holdEventPast = false;    // whether or not the hold event happened already
boolean longHoldEventPast = false;// whether or not the long hold event happened already

int checkButton() {    
   int event = 0;
   buttonVal = digitalRead(buttonPinA0);
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
   {
       downTime = millis();
       ignoreUp = false;
       waitForUp = false;
       singleOK = true;
       holdEventPast = false;
       longHoldEventPast = false;
       if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true)  DConUp = true;
       else  DConUp = false;
       DCwaiting = false;
   }
   // Button released
   else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
   {        
       if (not ignoreUp)
       {
           upTime = millis();
           if (DConUp == false) DCwaiting = true;
           else
           {
               event = 2;
               DConUp = false;
               DCwaiting = false;
               singleOK = false;
           }
       }
   }
   // Test for normal click event: DCgap expired
   if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
   {
       event = 1;
       DCwaiting = false;
   }
   // Test for hold
   if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
       // Trigger "normal" hold
       if (not holdEventPast)
       {
           event = 3;
           waitForUp = true;
           ignoreUp = true;
           DConUp = false;
           DCwaiting = false;
           //downTime = millis();
           holdEventPast = true;
       }

/* MANTER APERTADO POR MUITO TEMPO
       // Trigger "long" hold
       if ((millis() - downTime) >= longHoldTime)
       {
           if (not longHoldEventPast)
           {
               event = 4;
               longHoldEventPast = true;
           }
       }
*/
   }
   buttonLast = buttonVal;
   return event;
}

Need to “cut” the post in 2 ways…
Continuing :slight_smile:

And the multi-button bouncing I am working on, that works well too, is this one:

#define DEBOUNCE 10  // button debouncer, how many ms to debounce, 5+ ms is usually plenty
 
// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {4, 5, 6, 7, 8, 9}; 
 
// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)
 
// we will track if a button is just pressed, just released, or 'currently pressed' 
byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];
 
void setup() {
  byte i;
 
  // set up serial port
  Serial.begin(9600);
  Serial.print("Button checker with ");
  Serial.print(NUMBUTTONS, DEC);
  Serial.println(" buttons");
  // pin13 LED
  pinMode(13, OUTPUT);
 
  // Make input & enable pull-up resistors on switch pins
  for (i=0; i< NUMBUTTONS; i++) {
    pinMode(buttons[i], INPUT);
    digitalWrite(buttons[i], HIGH);
  }
}
 
void check_switches()
{
  static byte previousstate[NUMBUTTONS];
  static byte currentstate[NUMBUTTONS];
  static long lasttime;
  byte index;
  if (millis() < lasttime) {
     lasttime = millis(); // we wrapped around, lets just try again
  }
 
  if ((lasttime + DEBOUNCE) > millis()) {
    return; // not enough time has passed to debounce
  }
  // ok we have waited DEBOUNCE milliseconds, lets reset the timer
  lasttime = millis();
 
  for (index = 0; index < NUMBUTTONS; index++) {
    justpressed[index] = 0;       // when we start, we clear out the "just" indicators
    justreleased[index] = 0;
 
    currentstate[index] = digitalRead(buttons[index]);   // read the button
    if (currentstate[index] == previousstate[index]) {
      if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {
          // just pressed
          justpressed[index] = 1;
      }
      else if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {
          // just released
          justreleased[index] = 1;
      }
      pressed[index] = !currentstate[index];  // remember, digital HIGH means NOT pressed
    }
    //Serial.println(pressed[index], DEC);
    previousstate[index] = currentstate[index];   // keep a running tally of the buttons
  }
}
 
void loop() {
  check_switches();      // when we check the switches we'll get the current state
 
  for (byte i = 0; i < NUMBUTTONS; i++) {
    if (justpressed[i]) {
      Serial.print(i, DEC);
      Serial.println(" Just pressed"); 
      // remember, check_switches() will CLEAR the 'just pressed' flag
    }
    if (justreleased[i]) {
      Serial.print(i, DEC);
      Serial.println(" Just released");
      // remember, check_switches() will CLEAR the 'just pressed' flag
    }
    if (pressed[i]) {
      Serial.print(i, DEC);
      Serial.println(" pressed");
      // is the button pressed down at this moment
    }
  }
}

Working with then separately is not really a problem, but how to make then work together, so I could use the 4-way with a multi-button function? I tried all “crazy” stuff I could think but guess what? I can´t even compile it properly!

I found the anwser in another library!!!!

Library:

Google Code Archive - Long-term storage for Google Code Project Hosting.

credits: https://programmingelectronics.com

The main code:

/* ClickButton library demo

  Blinks a LED according to different clicks on one button.
  
  Short clicks:

    Single click - Toggle LED on/off
    Double click - Blink      (Toggles LED 2 times/second)
    Triple click - Fast blink (Toggles LED 5 times/second)
    
  Long clicks (hold button for one second or longer on last click):
    
    Single-click - Slow blink   (Toggles LED every second)
    Double-click - Sloow blink  (Toggles LED every other second)
    Triple-click - Slooow blink (Toggles LED every three seconds)


  The circuit:
  - LED attached from pin 10 to resistor (say 220-ish ohms), other side of resistor to GND (ground)
  - pushbutton attached from pin 4 to GND
  No pullup resistor needed, using the Arduino's (Atmega's) internal pullup resistor in this example.

  Based on the Arduino Debounce example.

  2010, 2013 raron
 
 GNU GPLv3 license
*/

#include "ClickButton.h"

// the LED
const int ledPin = 10;
int ledState = 0;

// the Button
const int buttonPin1 = 4;
ClickButton button1(buttonPin1, LOW, CLICKBTN_PULLUP);

// Arbitrary LED function 
int LEDfunction = 0;


void setup()
{
  pinMode(ledPin,OUTPUT);  

  // Setup button timers (all in milliseconds / ms)
  // (These are default if not set, but changeable for convenience)
  button1.debounceTime   = 20;   // Debounce timer in ms
  button1.multiclickTime = 250;  // Time limit for multi clicks
  button1.longClickTime  = 1000; // time until "held-down clicks" register
}


void loop()
{
  // Update button state
  button1.Update();

  // Save click codes in LEDfunction, as click codes are reset at next Update()
  if (button1.clicks != 0) LEDfunction = button1.clicks;
  

  // Simply toggle LED on single clicks
  // (Cant use LEDfunction like the others here,
  //  as it would toggle on and off all the time)
  if(button1.clicks == 1) ledState = !ledState;

  // blink faster if double clicked
  if(LEDfunction == 2) ledState = (millis()/500)%2;

  // blink even faster if triple clicked
  if(LEDfunction == 3) ledState = (millis()/200)%2;

  // slow blink (must hold down button. 1 second long blinks)
  if(LEDfunction == -1) ledState = (millis()/1000)%2;

  // slower blink (must hold down button. 2 second loong blinks)
  if(LEDfunction == -2) ledState = (millis()/2000)%2;

  // even slower blink (must hold down button. 3 second looong blinks)
  if(LEDfunction == -3) ledState = (millis()/3000)%2;


  // update the LED
  digitalWrite(ledPin,ledState);
}