Action when two buttons pressed within set time

The program below is working OK. Originally all my main code was in loop(). I revised it following recommendations I've seen here to confine as much as possible to functions. But I'm wondering if I should also have placed most variables in the functions? Or even in loop()? Or are they OK left in the globals section?

I'd welcome any other advice on improving my code. 'Arduino level' please, no advanced C/C++!

That includes a pointer to any program or library for achieving the above. I failed to find any that got close, after much prior searching. But the millis() tutorials by UKHeliBob and Robin2 helped a lot, many thanks.

// Tuesday 31 May 2022; simplified 'action' for testing
// After pressing button A, if button B is pressed within set time
// then take some action.
// Intended for music box, hoping to increase possible button actions.

const unsigned long period = 1500;  // max period allowed
const byte buttonAPin = A1; // First button
const byte buttonBPin = A2; // Second button
const byte blueLEDPin = 11; // Test: Lights briefly on correct logic.
bool preventingA = false; // Flag, true after A pressed, false after action.
bool preventingB = false; // Flag, true after B pressed, false after action.
// In both cases, false prevents timestamp captures, true allows them.

unsigned long currentMillis;
unsigned long AMillis;
unsigned long BMillis;

/////////////////////////////////////////

void setup()
{
  Serial.begin(115200);
  Serial.println("TwoButnFnForMB-2-TryFunctions");
  pinMode(buttonAPin, INPUT_PULLUP);
  pinMode(buttonBPin, INPUT_PULLUP);
  pinMode(blueLEDPin, OUTPUT);
  digitalWrite(blueLEDPin, LOW); // Set LED off; probably redundant
}

/////////////////////////////////////////

void loop()
{
  getTimeA();
  getTimeB();
  action();
}

/////////////////////////////////////////

void  getTimeA()
{
  // Get time button A pressed
  currentMillis = millis(); // Capture latest time
  // It's then the same for all variables

  // Read ButtonA to get its timestamp; actually several will
  // be read, while button is low, and the last will get used.
  if (digitalRead(buttonAPin) == LOW && (preventingA == false) )
    //   if (digitalRead(buttonAPin) == LOW && (preventingA == LOW) )
  {
    AMillis = currentMillis; // Capture time of press.
    preventingA = true; // To stop further capturing.
  }
}

/////////////////////////////////////////

void getTimeB()
{
  // Get time button B pressed; function getTimeB()
  if (digitalRead(buttonBPin) == LOW && (preventingB == false) )
  {
    BMillis = currentMillis;
    preventingB = true; // To stop further B capturing
    //    Serial.print("******* BMillis = ");
    //    Serial.println(BMillis);
    Serial.print("B-A = which is within the time allowed");
    Serial.println(BMillis - AMillis);
  }
}

/////////////////////////////////////////

void action()
{
  // Take action if B is pressed within time allowed
  if (preventingA == true && preventingB == true
      && (BMillis - AMillis <= period))
  {
    digitalWrite(blueLEDPin, HIGH);
    delay(2000); // Not critical enough to use millis()
    digitalWrite(blueLEDPin, LOW);
    preventingA = LOW; // Reset these for next usage
    preventingB = LOW;
  }
}

A couple of comments

I would make capturingA and capturingB boolean variable with values of true or false. That would make the meaning of the code more understandable

(BMillis - AMillis <= period)

BMillis will be greater than AMillis so is the test for less than the right thing to do ? It depends on what you want to do, of course

As to your variables, the easiest way is to use globals in such a small sketch. You could use local variables but then will need to return them from functions and pass them to other functions

Thanks. Yes, that's much better and I'll probably change as you suggest. And abandon the 'drawbridge' metaphor I've been using :slightly_smiling_face:

Edit: Although that might also need changing 'capturing' to something like 'blocking' or 'preventing'.

Pleased also to get your reassurance about globals.

Edit 2: Code changed to reflect your suggestion, and I think it reads much better.

consider

// check multiple buttons and toggle LEDs

enum { Off = HIGH, On = LOW };

byte pinsLed [] = { 10, 11, 12 };
byte pinsBut [] = { A1, A2, A3 };
#define N_BUT   sizeof(pinsBut)

byte butState [N_BUT];

unsigned long but0msec;
unsigned long but1msec;

#define Period  500

// -----------------------------------------------------------------------------
int
chkButtons ()
{
    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        byte but = digitalRead (pinsBut [n]);

        if (butState [n] != but)  {
            butState [n] = but;

            delay (10);     // debounce

            if (On == but)
                return n;
        }
    }
    return -1;
}

// -----------------------------------------------------------------------------
void
loop ()
{
    unsigned long msec = millis ();

    switch (chkButtons ())  {
    case 2:
        digitalWrite (pinsLed [2], ! digitalRead (pinsLed [2]));
        break;

    case 1:
        digitalWrite (pinsLed [1], ! digitalRead (pinsLed [1]));
        but1msec = ! msec ? 1 : msec;
        Serial.println (but1msec);
        break;

    case 0:
        digitalWrite (pinsLed [0], ! digitalRead (pinsLed [0]));
        but0msec = ! msec ? 1 : msec;
        Serial.println (but0msec);
        break;
    }

    if (( ((but1msec - but0msec) < Period) || ((but0msec - but1msec) < Period))
            && but0msec && but1msec)  {
        Serial.println ("loop: both buttonspressed");
        Serial.println (but0msec);
        Serial.println (but1msec);
        but1msec = but0msec = 0;
    }
    else if (but1msec && (msec - but1msec) > Period)  {
        but1msec = 0;
        Serial.println ("loop: but1msec reset");
    }
    else if (but0msec && (msec - but0msec) > Period)  {
        but0msec = 0;
        Serial.println ("loop: but0msec reset");
    }
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

    for (unsigned n = 0; n < sizeof(pinsBut); n++)  {
        pinMode (pinsBut [n], INPUT_PULLUP);
        butState [n] = digitalRead (pinsBut [n]);
    }

    for (unsigned n = 0; n < sizeof(pinsLed); n++)  {
        digitalWrite (pinsLed [n], Off);
        pinMode      (pinsLed [n], OUTPUT);
    }
}

Thanks, but that's going to need a lot of study! Even if it had explanatory comments. Or used my variable and constant names.

I'm guessing that as neither 'enum' nor '#define' appear in the extensive index of my 770 page 'Arduino Cookbook (3rd edition)' they are from C/C++ ?

from C, the 2nd ed of The C Programming Language is < 300 pages

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.