How to active millis() only when I call it?

hello, I coding a simple toll system, I need that when the limit switch (S1) is on, the system wait for 5 seconds and after that it executes the function (turning on a motor). The thing is that millis() starts counting at the same that the programs starts, so my problem is that I if start the program and let's say I wait for 10 seconds and then I press S1, the motor will turn on immediately and It will not wait 5 seconds because the "time has already passed". I need to reset it some way but I don't know how can I do it.


const int S1 = 4;
const int S2 = 3;
const int STOP = 5;
const int MARCHA = 6;
const int FC1 = 7;
const int FC2 = 8;
const int MOTOR_SUBIR = 11;
const int MOTOR_BAJAR = 12;
const int SEMAFORO_ROJO = 13;
const int SEMAFORO_VERDE = 2;
const int INDICADOR_LUMINOSO = 10;
const int INDICADOR_SONORO = 9;

int estadoStop =  LOW;

const unsigned long interval = 5000;
long previousTime = 0;


void setup() {
  pinMode(S1, INPUT);
  pinMode(S2, INPUT);
  pinMode(STOP, INPUT_PULLUP);
  pinMode(MARCHA, INPUT_PULLUP);
  pinMode(FC1, INPUT);
  pinMode(FC2, INPUT);
  pinMode(MOTOR_SUBIR, OUTPUT);
  pinMode(MOTOR_BAJAR, OUTPUT);
  pinMode(SEMAFORO_ROJO, OUTPUT);
  pinMode(SEMAFORO_VERDE, OUTPUT);
  pinMode(INDICADOR_LUMINOSO, OUTPUT);
  pinMode(INDICADOR_SONORO, OUTPUT);
}

void loop() {
  // Semaforo en Rojo cuando la barrera no está abierta
  if(digitalRead(FC2) != HIGH) {
    digitalWrite(SEMAFORO_ROJO, HIGH);
    digitalWrite(SEMAFORO_VERDE, LOW);
  } else {
    digitalWrite(SEMAFORO_ROJO, LOW);
    digitalWrite(SEMAFORO_VERDE, HIGH);
  }

unsigned long currentTime = millis();

  // Si hay un auto presente, se encienden luz, bocina y temporizador
  if (digitalRead(S1) == HIGH && estadoStop == LOW) {
    activarSalidas();
    if (currentTime - previousTime >= interval) {
      subirBarrera();
      previousTime = currentTime;
    }
    
  }

  // Verifica el botón de STOP
  if (digitalRead(STOP) == HIGH && estadoStop == LOW) {
    bajarBarrera();
    detenerSalidas();
    estadoStop = HIGH;
  } else if (digitalRead(MARCHA) == HIGH && digitalRead(STOP) == LOW && estadoStop == HIGH || digitalRead(S2) == HIGH && digitalRead(STOP) == LOW && estadoStop == HIGH) {
    estadoStop = LOW;
  }

  if (digitalRead(STOP) == HIGH) {
    bajarBarrera();
  }

  // Sube la barrera si el botón MARCHA está presionado
  if (digitalRead(MARCHA) == HIGH && digitalRead(STOP) == LOW) {
    subirBarrera();
  }

  // Sensor de salida, Bajar la barrera al activarse
  if (digitalRead(S2) == HIGH) {
    bajarBarrera();
  }

  // Detener el motor al llegar el final de carrera
  if (digitalRead(FC1) == HIGH){
    digitalWrite(MOTOR_BAJAR, LOW);
  }
  if (digitalRead(FC2) == HIGH){
    digitalWrite(MOTOR_SUBIR, LOW);
  }
}

void activarSalidas() {
  digitalWrite(INDICADOR_LUMINOSO, HIGH);
  digitalWrite(INDICADOR_SONORO, HIGH);
}

void detenerSalidas() {
  digitalWrite(INDICADOR_LUMINOSO, LOW);
  digitalWrite(INDICADOR_SONORO, LOW);
}

void subirBarrera() {
  if (digitalRead(FC2) == LOW) {
    digitalWrite(MOTOR_BAJAR, LOW);
    digitalWrite(MOTOR_SUBIR, HIGH);
  } else if(digitalRead(FC2) == HIGH){
    digitalWrite(MOTOR_BAJAR, LOW);
    digitalWrite(MOTOR_SUBIR, LOW);
  }
}

void bajarBarrera() {
  if (digitalRead(FC1) == LOW) {
    digitalWrite(MOTOR_SUBIR, LOW);
    digitalWrite(MOTOR_BAJAR, HIGH);
  } else if(digitalRead(FC1) == HIGH){
    digitalWrite(MOTOR_BAJAR, LOW);
    digitalWrite(MOTOR_SUBIR, LOW);
  }
}

this should happen only when button on, not every single loop cycle

function is not optimal, it can be rewritten so:

void bajarBarrera() {
    digitalWrite(MOTOR_SUBIR, LOW);
    digitalWrite(MOTOR_BAJAR, digitalRead(FC1) == LOW);
}

check other functions, they may be similar short

Thanks, how can i rewrite it to accomplish the function that i want?, i tried to put it inside the conditional but it just works the same.

delete entire text, write new sketch, that after button press begin to wait 5 seconds then print a message. if you success make next step. and again, and again until all you expectations are implemented.

Hello, I have already used the Blink Without Delay code, but it doesn't use an input as in my case and it is just not the same case, I need that whenever S1 is pressed a timer of 5 seconds starts, but this can be at any time and also it can be multiple times. Most of tutorials that i have seen don't use any input at all. I have tried using while() too but it occurs the same problem that with delay(). How can I accomplish the function that i want? thanks

That's a great idea, I'm trying that but I can't get it yet, can you help me with it please?

Maybe look at the code for the pin 7 button in:

If you put this...

... after this...

currentTime should be initialized (zeroed), immediately after S1 is pressed.

But, you should make this...

be this...

pinMode(S1, INPUT_PULLUP);

... so you know S1 is HIGH until it is pressed.

This is how millis() works.
 

In this sketch I make a "zero time" by storing millis() immediately after a button press (S1 for you). The button can be pressed at any time (1s, 4s, 6s, 10s...) and will only "execute the function" (print the seconds - turn the motor for you) in multiples of 5 seconds. See if you can create a "zero time" immediately after S1 is on... and only turn on a motor at 5 seconds after "zero time"

unsigned long zeroTime, splitTime, newTime, oldTime = 0, fivek = 4999; // needed 1ms for Serial printing
bool startFlag = 0;
int buttonPin = 2;

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  if (!digitalRead(buttonPin)) { // the button is press (LOW)
    zeroTime = millis(); // set zeroTime to the time of button press
    delay(150);
    oldTime = zeroTime; // use oldTime as a variable
    startFlag = 1; // this indicates the button was pressed
    Serial.print("Button pressed. \nSplit time of 0 seconds at ");
    Serial.println(zeroTime);
  }

  if (startFlag) { // if the button was pressed
    newTime = millis(); // start a new 5 seconds count up
    if (newTime - oldTime > fivek) { // if the 5 seconds interval has passed
      splitTime = (newTime - zeroTime)/1000; // time to do things
      Serial.print("Split time of ");
      Serial.print(splitTime); // show the split time, or turn on a motor
      Serial.print(" seconds at ");
      Serial.println(newTime);
      oldTime = newTime; // start a new split time
    }
  }
}
1 Like

Thanks for the help guys, but i don't get it about how can I solve the problem yet, I kinda get that millis() is a clock that starts at the same time that the program and i understand that a previousTime reset the variable associated to millis() to repeat it again instantly, but that is not what i want, I'm not able to figure out yet how can I use the function millis() to create a timer of 5 seconds when I press the button and start in 0 again ONLY when I press that Input again.
In other words, i want to create like a "Timer On Delay" that only begins to count when I press the button (S1), and after counting 5 seconds, it will make the motor to start rotating.

millis() starts when the Arduino gets power.
You need to store a "zero time" ( zeroTime = millis(); ) immediately after S1 is pressed... and count five seconds from zeroTime.

See post 12... try the link. Press the button at any time, as many times as you want. It counts in multiples of five seconds from any button press.

1 Like

Hi @terraz,

I am not sure if you will really achieve what you want with the code as it is now. Your application is the ideal reason to use a "state machine" instead of coding everything in one loop().

For your specific request a solution might look like this:

unsigned long activationTime ;
boolean waitActive = false;

void loop() {
  // Semaforo en Rojo cuando la barrera no está abierta
  if(digitalRead(FC2) != HIGH) {
    digitalWrite(SEMAFORO_ROJO, HIGH);
    digitalWrite(SEMAFORO_VERDE, LOW);
  } else {
    digitalWrite(SEMAFORO_ROJO, LOW);
    digitalWrite(SEMAFORO_VERDE, HIGH);
  }


  // Si hay un auto presente, se encienden luz, bocina y temporizador
  if (digitalRead(S1) == HIGH && estadoStop == LOW) {
    activarSalidas();
    if (!waitActive) {
      waitActive = true;
      activationTime = millis();
    }
    if (waitActive && millis()- activationTime >= interval) {
      subirBarrera();
      waitActive = false;
    }
   
  }

It is not the full sketch but just the part you have to change. Explanations:

  • Declare activationTime and (new now) waitActive outside loop()
  • If the condition for S1 and estadoStop is met and waitActive is false
    • activationTime is set to the current millis() time and waitActive is set to true
    • After the interval time has elapsed and while waitActive is true subirBarrera() is called and waitActive is set to false for future use.
    • While waitActive is true, activationTime will not be changed.

That may solve your timing problem, but be aware that this only works while S1 == HIGH and estadoStop == LOW!!!!!

Have a look at state machines ...

1 Like

Thanks, It worked!

You are welcome ... I only coded for you what @xfpd and others posted already, so my contribution was limited ... :wink:

But I still recommend to have a look at state machines as you might get in trouble with all those different conditions otherwise.

1 Like

I tried to model your application in Wokwi:

and wrote a state machine solution for it:

/*
  Forum: https://forum.arduino.cc/t/how-to-active-millis-only-when-i-call-it/1195428
  Wokwi: https://wokwi.com/projects/382889761036905473
*/

constexpr byte AUTOPRESENTE = 4;
constexpr byte SENSORSALIDA  = 3;
constexpr byte STOP = 5;
constexpr byte MARCHA = 6;
constexpr byte BARRIERCLOSED = 7;
constexpr byte BARRRIEROPEN  = 8;
constexpr byte MOTOR_SUBIR = 11;  // Up
constexpr byte MOTOR_BAJAR = 12;  // Down
constexpr byte SEMAFORO_ROJO = 13;
constexpr byte SEMAFORO_VERDE = 2;
constexpr byte INDICADOR_LUMINOSO = 10;
constexpr byte INDICADOR_SONORO = 9;
constexpr unsigned long WAITTIME = 5000;
constexpr int WAITTONE = 440;
constexpr int OPENTONE = 340;


enum tollStates {BARRIERDOWN, BARRIERUP, MOVINGUP, MOVINGDOWN, UNDEFINED};
tollStates tollState = BARRIERDOWN;

struct buttonType {
  byte pin;
  byte state;
  byte prevState;
  byte openState;
  unsigned long lastChange = 0;
  void init(byte aPin, byte startState) {
    openState = startState;
    pin = aPin;
    state = startState;
    prevState = startState;
    if (startState) {
      pinMode(pin, INPUT_PULLUP);
    } else {
      pinMode(pin, INPUT);
    }
  }
  boolean pressed() {
    byte actState = digitalRead(pin);
    if (actState != prevState) {
      lastChange = millis();
      prevState = actState;
    }
    if (state != actState && millis() - lastChange > 20) {
      state = actState;
         return (state != openState);
      }
      return false;
    }
    boolean getState(){
      state = digitalRead(pin);
      delay(10);  // poor man's debouncing
      return state;
    }
};

buttonType marcha, stop, sensorSalida, autoPresente, barrierClosedSwitch, barrierOpenSwitch;

void setup() {
  Serial.begin(115200);
  autoPresente.init(AUTOPRESENTE, LOW);
  sensorSalida.init(SENSORSALIDA,LOW);
  stop.init(STOP,HIGH);
  marcha.init(MARCHA, HIGH);
  barrierClosedSwitch.init(BARRIERCLOSED, LOW);
  barrierOpenSwitch.init(BARRRIEROPEN,LOW);
  pinMode(MOTOR_SUBIR, OUTPUT);
  pinMode(MOTOR_BAJAR, OUTPUT);
  pinMode(SEMAFORO_ROJO, OUTPUT);
  pinMode(SEMAFORO_VERDE, OUTPUT);
  pinMode(INDICADOR_LUMINOSO, OUTPUT);
  pinMode(INDICADOR_SONORO, OUTPUT);
  getInitialBarrierState();
}


unsigned long activationTime;
boolean waitActive = false;
boolean blink      = false;

void loop() {
  checkButtons();
  stateMachine();
  handleFailure();
}

void stateMachine() {
  switch (tollState) {
    case BARRIERDOWN:
      if (!barrierIsDown()) {
        tollState = BARRIERUP;
        semaforosGreen();
        Serial.println("Change to UP");
        break;
      }
      if (autoPresent() && !waitActive) {
        activationTime = millis();
        waitActive = true;
        activarSalidas(WAITTONE);
        Serial.println("Auto present");
      }
      if (waitActive && millis() - activationTime >= WAITTIME) {
        waitActive = false;
        tollState = MOVINGUP;
        Serial.println("Start moving up");
      }
      break;
    case BARRIERUP:
      if (!barrierIsUp()) {
        tollState = BARRIERDOWN;
        semaforosRed();
        detenerSalidas();
        Serial.println("Change to Down");
        break;
      }
      if (autoHasLeft()) {
        Serial.println("Auto has left, start moving down");
        tollState = MOVINGDOWN;
      }
      break;
    case MOVINGUP:
      if (!barrierIsUp()) {
        subirBarrera();
      } else {
        Serial.println("Barrier is up");
        stopBarrera();
        semaforosGreen();
        tollState = BARRIERUP;
      }
      break;
    case MOVINGDOWN:
      if (!barrierIsDown()) {
        bajarBarrera();
      } else {
        Serial.println("Barrier is down");
        stopBarrera();
        detenerSalidas();
        tollState = BARRIERDOWN;
      }
      break;
    case UNDEFINED:
      blink = true;
      break;
  }
}

void checkButtons() {
  if (stop.pressed()) {
    Serial.println("STOP pressed!");
    tollState = MOVINGDOWN;
  }
  if (marcha.pressed()) {
    Serial.println("MARCHA pressed!");
    activarSalidas(WAITTONE);
    tollState = MOVINGUP;
  }
}



void getInitialBarrierState() {
  semaforosRed();
  if (barrierIsDown()) {
    tollState = BARRIERDOWN;
    Serial.println("Barrier is down");
  }
  if (barrierIsUp()) {
    semaforosGreen();
    tollState = BARRIERUP;
    Serial.println("Barrier is up");
  }
  if (barrierIsUp() == barrierIsDown()) {
    Serial.println("Undefined State!");
    tollState = UNDEFINED;
  }
}

boolean barrierIsDown() {
  return barrierClosedSwitch.getState();
}

boolean barrierIsUp() {
  return barrierOpenSwitch.getState();
}

boolean autoHasLeft() {
  return sensorSalida.pressed();
}

boolean autoPresent() {
  return autoPresente.pressed();
}


void activarSalidas(int aTone) {  // Signals on
  digitalWrite(INDICADOR_LUMINOSO, HIGH);
  digitalWrite(INDICADOR_SONORO, HIGH);
  // Just for Wokwi
  tone(INDICADOR_SONORO,aTone);
}

void detenerSalidas() {  // Signals off
  digitalWrite(INDICADOR_LUMINOSO, LOW);
  digitalWrite(INDICADOR_SONORO, LOW);
  // Just for Wokwi
  noTone(INDICADOR_SONORO);
}

void stopBarrera() { // Stop Barrier
  digitalWrite(MOTOR_BAJAR, LOW);
  digitalWrite(MOTOR_SUBIR, LOW);
}

void subirBarrera() { // Open Barrier
  semaforosRed();  // Always RED when moving!
  digitalWrite(MOTOR_BAJAR, LOW);
  digitalWrite(MOTOR_SUBIR, HIGH);
}

void bajarBarrera() {  // Close Barrier
  semaforosRed();  // Always RED when moving!
  digitalWrite(MOTOR_SUBIR, LOW);
  digitalWrite(MOTOR_BAJAR, HIGH);
}

void semaforosRed() {
  if (!blink) {
    digitalWrite(SEMAFORO_ROJO, HIGH);
    digitalWrite(SEMAFORO_VERDE, LOW);
  }
}

void semaforosGreen() {
  if (!blink) {
    activarSalidas(OPENTONE); // Everytime with Green the signals will be switched on as well
    digitalWrite(SEMAFORO_ROJO, LOW);
    digitalWrite(SEMAFORO_VERDE, HIGH);
  }
}


void handleFailure() {
  static unsigned long lastBlink = 0;
  static byte state = HIGH;
  if (!blink) {
    return;
  }
  if (millis() - lastBlink > 500) {
    lastBlink = millis();
    state = !state;
    digitalWrite(SEMAFORO_ROJO, state);
    digitalWrite(SEMAFORO_VERDE, LOW);
  }
}




Feel free to check it out on Wokwi, here is the link: Barrier Control II

The main control elements are:
image

  • Start the simulation

  • Make sure that the barrier switch is on cerrada (closed)

    • Semaforo will be Green off / Red on
  • Press Auto present(e)

    • Signals (led and sound) will start
    • After 5s the Subir led is switched on
  • Move Barrera switch to open (abierta)

    • Subir led will switch off,
    • Signals stay on,
    • Semaforo will switch Green on / Red off
  • Press Sensor salida

    • Bajar will switch on
    • Signals will stay on
    • Semaforo will switch Green off / Red on
  • Move Barrera switch to closed (cerrada)

    • Bajar led switches off,
    • Signals switch off
    • Semaforo stay Green off / Red on
  • If you press Stop while the barrier is closed, it will be recognized but have no effect

  • If you press Stop while the barrier is open, it will start the MOVINGDOWN state

  • If you press Marcha while the barrier is open, it will be recognized but have no effect

  • If you press Marcha while the barrier is closed, it will start the MOVINGUP state

  • While waiting for the 5s the tone will be different from the tone while "Green" is shown

In the simulation it is possible to immediately switch between open and closed (which will of course take time in reality). All buttons/switches use debouncing (or at least a poor way).

I also included the detection of an undefined start state (barrier neither up nor down).

Maybe it does not cover all issues but hopefully provides you some assistance ...

Have fun!

1 Like

Here is a demo-Code that does

  • wait for a button-press
    • if button-press is detected start a first timer that works as a switch-on-delay
  • switch on after switch-on-delay-time has passed by
  • start a on-timer and keep switched on for the ON-TIME
    • if ON-TIME has passed by switch OFF and start a ignore-button-timer
  • if ignore-button-time has passed by change to "- wait for a button-press"
const byte triggerInputPin  = 2;
const byte triggerIsActive = LOW; // LOW o HIGH

const byte PinToSwitch     = 3;
const byte OutPutOn  = HIGH;
// the attention-mark ! is the not-operator which inverts the value
// !true = false  !false = true     !HIGH = LOW    !LOW = HIGH
const byte OutPutOff = !OutPutOn; 

// set InitialWaitTime to 0 if you don't want a delay between triggering and switching output
unsigned long InitialWaitTime = 2000; // set to zero if you don't want an initial wait
unsigned long ActiveTime = 3000;
unsigned long LockedTime = 5000; // set to zero if you don't want a lock-time

unsigned long MonoFlopTimer; // variable used for non-blocking timing

// constants of the state-machine
const byte sm_Idling      = 0;
const byte sm_InitialWait = 1;
const byte sm_Activated   = 2;
const byte sm_Locked      = 3;

byte MonoFlopState = sm_Idling; // state-variable of the state-machine


void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  PrintFileNameDateTime();
  pinMode(triggerInputPin,INPUT_PULLUP); // INPUT_PULLUP
  digitalWrite(PinToSwitch,OutPutOff);
  pinMode(PinToSwitch,OUTPUT);
}


void MonoFlopStateMachine() {

  switch (MonoFlopState) {

    case sm_Idling:
      // check if trigger-input reads triggering signal
      if (digitalRead(triggerInputPin) == triggerIsActive) {
        MonoFlopTimer = millis();       // make snapshot of time
        MonoFlopState = sm_InitialWait; // set next state of state-machine
        Serial.println( F("trigger-signal detected start inital wait") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_InitialWait:
      // wait until initial waittime has passed by
      if ( TimePeriodIsOver(MonoFlopTimer,InitialWaitTime) ) {
        // if waittime HAS passed by
        MonoFlopTimer = millis();           // make new snapshot of time
        digitalWrite(PinToSwitch,OutPutOn); // switch IO-Pin to activated state
        MonoFlopState = sm_Activated;       // set next state of state-machine
        Serial.println( F("InitialWaitTime is over switching output to ACTIVE") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_Activated:
      // check if time the IO-pin shall be active is over
      if ( TimePeriodIsOver(MonoFlopTimer,ActiveTime) ){
        // if activetime of IO-pin IS over
        MonoFlopTimer = millis();             // make new snapshot of time
        digitalWrite(PinToSwitch,OutPutOff);  // switch IO-pin to DE-activated state
        MonoFlopState = sm_Locked;            // set next state of state-machine
        Serial.println( F("Activetime is over switching output to IN-active") );
        Serial.println( F("and starting locktime") );
      }
      break; // IMMIDIATELY jump down to END OF SWITCH

    case sm_Locked:
      // check if time the logic shall be locked against too early re-triggering is over
      if ( TimePeriodIsOver(MonoFlopTimer,LockedTime) ){
        // if locked time IS over
        Serial.println( F("locktime is over change state to idling") );
        MonoFlopState = sm_Idling; // set state-machine to idle-mode
      }
      break; // IMMIDIATELY jump down to END OF SWITCH
  } // END OF SWITCH
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 250);

  // run down code of state-machine over and over again
  // all the logic for reading in sensor-signal and switching output ON/OFF 
  // is inside the function
  MonoFlopStateMachine(); 
}

best regards Stefan

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