gpioarduino-esp32

GPIO init issue with ESP32


I would like to take different actions with a long vs. a short button press and I'm facing the problem that my input always reads a short button press on init. I have a function that looks like:

enum input button_get(void) {
  #define BTN_LONGPRESS 500
  static int presscycles = 0; // count the number of iterations the button has been pushed for
  static int released = 0;    // has the button been released?
  static int pushed = 0;      // keep track if the button has ever been pushed
  
  if (digitalRead(BUTTON)) {
    pushed = 1;
    released = 0;
    presscycles++;
  } else {
    if (pushed == 1)
      released = 1; //only set released to 1 if the button was pushed at least once
  }
  if ( released == 1 &&
       presscycles >= BTN_LONGPRESS ) {
    //long press
    released = 0;
    presscycles = 0;
    Serial.print("IN_REFILL\n");
    delay(3000);
    return IN_REFILL;
    
  } else if ( released == 1 &&
              presscycles > 0 && 
              presscycles < BTN_LONGPRESS ) {
    //short press
    released = 0;
    presscycles = 0;
    Serial.print("IN_CLEAN\n");

    delay(3000);
    return IN_CLEAN;
  } else {
    return IN_NONE;
}

and for some reason, the pushed (and hence also the released flag) are always set to1 after a reset, why is this and how can I prevent that from happening?

I'm using the ESPRESSIF FREENOVE ESP32 WROVER


Solution

  • From your code, I suspect you are calling button_get from the main loop:

    void setup() {
       // setup code
    }
    
    void loop() {
      // code
      enum input = button_get();
      // do_something(input);
    }
    

    Your timing math in button_get assumes that button_get (and thus the main loop) is called at a 1ms period (1000 loops / second).

    As written, the main loop function will run as fast as it can. Much much faster than the assumed 1000 loops / second https://forum.arduino.cc/t/cycles-per-second-in-the-loop/61382/3. This causes button_get and your timing math to not be accurate to the real time (i.e. your press_cycles variable increments much faster than you are expecting).

    You can slow down the main loop so that your timing math is based on a 1ms period (1000 loops / second) by putting a 1ms delay in the main loop (or you could also put it in button_get, but it may be better to leave it in the main loop for organization reasons).

    void setup() {
       // setup code
    }
    
    void loop() {
      delay(1); // added delay for timing math
      // code
      enum input = button_get();
      // do_something(input);
    }
    

    This should fix the timing issue you are seeing!

    While this solution is convenient, it has the downside of slowing the code for the timing math, and is not necessarily accurate.

    If you would like the rest of your program to run as fast as possible, you can remove the 1 ms delay, and do your timing math with the millis() function.

    You can instead calculate elapsed time by using something like:

    // button pressed
    int startTime = millis();
    // button released
    int endTime = millis();
    
    int deltaTime = endTime - startTime;
    
    if (deltaTime >= BTN_LONGPRESS) {
      // timing code
    

    This is also likely more accurate timing wise, because delaying 1ms in the main is really like a 1ms + buffer delay because of the time it takes to execute your code and run the chip etc.