Can't figure out where short button press is coming from

Hello All,

I found this script and I am trying to alter it for my needs. I am trying to make the led turn on after a long button press and the turn off after another long button press.

3000ms long press

I sort of have it working but for some reason that I can't figure out why, the script turns on the led at times with a short button press, and I don't see where the short press is coming from.

This is a state machine and it is confusing to me.

Please advise. There is a link in the script to the original script. I am showing my edited script.

Thank you

// ---------------------------------------------
// millis_and_finite_state_machine.ino https://wokwi.com/arduino/projects/314526434594914882
// // ---------------------------------------------
// millis_and_finite_state_machine.ino
// ---------------------------------------------
// License: The Unlicense, Public Domain.
// Author: Koepel
// 2019 january 23
// ---------------------------------------------
// 
// millis and a finite state machine
//
// This sketch uses a button and is going through a few different states.
// The serial monitor tells what to do.
//
// The "State Change Detection" example is used.
// The "Blink without Delay" example is used.
// Variables of the "bool" type are used to pass on information.
// Two "previousMillis" variables are used, but a single "previousMillis" 
// could be used, because they do not overlap.
//
// The finite state machine has a few ..._INIT states.
// They are used to make a setup for the new state.
//
// A push button is connected between pin 4 and GND.
//
// I want to change this sketch:
//   When the led is off and the button is pressed for a long time,
//   then I want to turn the led on and after some time turn it off.
// 


// An 'enum' is the best way to for a list of the states.
enum
{
STATE_IDLE_INIT,
STATE_IDLE,
STATE_ON_INIT,
STATE_ON_BEFORE_BUTTON,
STATE_ON_BUTTON_2_SECONDS,
STATE_OFF_INIT,
STATE_OFF,
STATE_BLINK_INIT,
STATE_BLINK,
} state;

const int buttonPin = 4;
int last_button_value = HIGH;   // pin 2 is HIGH when button is not pressed
unsigned long previousMillisButton;

const int ledPin = 6;
int led_value;

unsigned long previousMillisBlinking;
bool blinking;   // boolean variable, should the led blink or not.
bool police_lights_on = false;

void setup()
{
Serial.begin(115200);
Serial.println( "The sketch has started.");
pinMode( buttonPin, INPUT_PULLUP);
pinMode( ledPin, OUTPUT);
}


void loop()
{
unsigned long currentMillis = millis();


// ---------------------------------------------------
// BUTTON
// ---------------------------------------------------
// Detect if the button changes, and pass
// that information on, to the finite state machine.
// Only the event of the change is passed on, because
// this sketch did not need to know the state
// of the button, only a change.
// ---------------------------------------------------
bool buttonPressed = false;
bool buttonReleased = false;

int button_value = digitalRead( buttonPin);

if( button_value != last_button_value)
{
   if( button_value == LOW)  // low is button pressed
   {
      buttonPressed = true;
   }
   else
   {
      buttonReleased = true;
   }//if else
   
   last_button_value = button_value;//remember the last button value
}//if


// ---------------------------------------------------
// FINITE STATE MACHINE
// ---------------------------------------------------
// The final state machine uses information to
// make decisions.
// ---------------------------------------------------

switch( state)
{
  case STATE_IDLE_INIT:
    // Before going to the actual "idle" state, a message is printed.
    Serial.println( "Press the button to turn the led on.");
    state = STATE_IDLE;
    break;
  case STATE_IDLE:
    // This "idle" state is when there is nothing to do.
    // This state is executed until a button is pressed.
    // It "waits" until a button is pressed, but it does
    // not really wait, since it is run over and over again.
    if( buttonPressed)
    {
     //my add or edit
     state = STATE_ON_BUTTON_2_SECONDS;
     // state = STATE_ON_INIT; //orig
    }
    break;
  case STATE_ON_INIT:
    // The state when the led is "on" is split into three states.
    // This is the initial part to turn the led on and print a message.
       if (police_lights_on)
       {
       Serial.println("police_lights_on"); 
       Serial.println(police_lights_on); 
       digitalWrite( ledPin, HIGH);
        
       Serial.println( "Press button longer than two seconds to turn it off.");
       state = STATE_ON_BEFORE_BUTTON;
       }
       else
       {
        digitalWrite( ledPin, LOW);
        state = STATE_ON_BEFORE_BUTTON;
       }
    break;
  case STATE_ON_BEFORE_BUTTON:
    // This state "waits" until a button is pressed.
    // As soon as a button is pressed, the value of millis()
    // is stored, to be able to calculate how long the button is pressed.
    if( buttonPressed)
    {
      
       //previousMillisButton = currentMillis;//orig
       state = STATE_ON_BUTTON_2_SECONDS;//orig 
      
      
    }
    break;
  case STATE_ON_BUTTON_2_SECONDS:
    // When the button is released before 2 seconds are passed,
    // then return to the state to "wait" for the button.
    if( buttonReleased)
    {
      state = STATE_ON_BEFORE_BUTTON;//keep looping until the correct button press time has been satisfied
    }
    else
    {
      if( currentMillis - previousMillisButton >= 3000)
      {
        //my add or edit
        previousMillisButton = currentMillis;
        //state = STATE_OFF_INIT; //orig
        Serial.println( "Turn on Police light");
        if (!police_lights_on)
        {
           police_lights_on = true; 
           state = STATE_ON_INIT; 
           break;
        }
        else
        {
          police_lights_on = false;
          Serial.println( "Turn OFF Police light");
          state = STATE_OFF_INIT;
          break; 
        }
      }
    }
    break;
  case STATE_OFF_INIT:
    digitalWrite( ledPin, LOW);
    Serial.println( "Press button to blink the led.");
     
    state = STATE_OFF; //orig
    
         
    break;
  case STATE_OFF:
    if( buttonPressed)
    {
      //state = STATE_BLINK_INIT;//orig
      
      state = STATE_ON_BUTTON_2_SECONDS;
    }
    break;
  
  
  case STATE_BLINK_INIT:
    // Set up the blinking.
    // Updating the previousMillisBlinking is not really required.
    previousMillisBlinking = currentMillis;
    blinking = true;
    Serial.println( "Press button to stop blinking.");
    state = STATE_BLINK;
    break;
  case STATE_BLINK:
    // This state "waits" until a button is pressed.
    // At the moment the button is pressed, the led could be on or off.
    // Therefor the led is turned off, to be sure that it will be off.
    if( buttonPressed)
    {
      blinking = false;
      digitalWrite( ledPin, LOW);  // be sure to turn led off
      state = STATE_IDLE_INIT;
    }
    break;
}


// ---------------------------------------------------
// BLINK WITHOUT DELAY
// ---------------------------------------------------
// Blinking the led is outside the final state machine.
// The final state machine can turn the blinking on and off.
// ---------------------------------------------------

/*
if( blinking)
{
  if( currentMillis - previousMillisBlinking >= 300)
  {
    previousMillisBlinking = currentMillis;
    
    if( led_value == LOW)
    {
      led_value = HIGH;
    }
    else
    {
      led_value = LOW;
    }
    digitalWrite( ledPin, led_value);
  }
}
*/

//delay( 10);  // a delay as a simple way to debounce the button

}


I don't see any debounce in the code. I just use one of the many button libraries, some of which process a long vs short press. (I think, button2 is one that I have used).

1 Like

previousMillisButton needs to be set in
STATE_INIT and STATE_ON_BEFORE_BUTTON when the button is pushed, and before it transitions to STATE_ON_BUTTON_2_SECONDS. Otherwise STATE_ON_BUTTON_2_SECONDS would be working with a stale or uninitialised previousMillisButton, and could trigger immediately.

Edit to add: Also STATE_OFF.

1 Like

Well, when @Koepel wrote it there was

  delay( 10);  // a delay as a simple way to debounce the button

but some time later it was // commented out.

If we consider that the rest of the loop takes no time, the delay effectively makes the loop run at 100 Hz, which as noted is a simple way to get past bounce artifacts.

a7

Hi DaveX,
Your fix worked, thanks!

I did NOT see a "STATE_INIT" but what I did below seems to be working.

"previousMillisButton needs to be set in
STATE_INIT and STATE_ON_BEFORE_BUTTON when the button is pushed, and before it transitions to STATE_ON_BUTTON_2_SECONDS.
Otherwise, STATE_ON_BUTTON_2_SECONDS would be working with a stale, or uninitialized, previousMillisButton, and could trigger immediately.
Edit to add: Also STATE_OFF."

//02_13_2022 as suggested by DaveX "I did NOT see a "STATE_INIT" "

fix for stopping "short button press firing off the led police lights", I added previousMillisButton = currentMillis; in these 5 states
case STATE_IDLE_INIT:
case STATE_IDLE:
case STATE_ON_INIT:
case STATE_ON_BEFORE_BUTTON:
case STATE_OFF:

Thanks, DaveX, I will have to try to wrap my head around why it worked.

Now DaveX I have another question.
Let's say I want another long button press, for say 5 seconds to run another function, and I want the second long button press 5-second function to run first, and not fire off the first 3-second function.

Can this be easily done in this state machine? I am thinking of just using bool flags, but is there an easier way? Thanks for any help. I really appreciate your first suggestion that solved my first problem.

It is just a silly sketch to demonstrate what can be done with a Final State Machine and how it would look. It is to give an impression, to get the idea.
There is even a over-the-top-extra-silly example for a long press (a very long press acts as a escape which is detected while the button is still being pressed): Too_Much_For_One_Button.ino
Try all the examples in Wokwi simulation with the green buttons: Fun_with_millis

Please use a library. There are also basic examples. Search for: arduino short press long press

Thanks, Do you have a sketch with a library?

Try diagramming out the current states, and how the 5-second press would fit in, and what it does to the STATE_ON_BUTTON_2_SECONDS state.

Thanks

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