Executing a countdown timer with millis() when if conditions are met

Hello everyone, I have been stuck on this problem for months, went completely out of my way and learned some Circuitpython and kind of got it right but not all the way, so I am back at it with Arduino. I can accomplish my goal using delay() but not using millis().
Ultimate goal is to keep pump running for some time after boiler shuts off

I have two relays and two buttons, pressing the start button turns on both relays, boiler and pump, pressing the stop button will shut off one relay ( boiler) but keep the pump relay on for some time, using delay() prevents me from starting the boiler while the delay is executing.

I would appreciate any kind of help.


const int start = 6, boiler = 4, pump = 3, stop = 8;
const long time= 300;


void setup(void) {
  pinMode(start, INPUT_PULLUP);
  pinMode(stop, INPUT_PULLUP);
  pinMode(boiler, OUTPUT);
  pinMode(pump, OUTPUT);
  delay(500);
  Serial.begin(9600);
  delay(200);
  digitalWrite(pump, LOW);
  digitalWrite(boiler, LOW);
}

void heating() {

  digitalWrite(pump, HIGH);
  digitalWrite(boiler, HIGH);
}


void loop() {


  if (digitalRead(start) == LOW && (digitalRead(boiler) == LOW)) {
    heating();


  }

  else if (digitalRead(stop) == LOW && digitalRead(boiler) == HIGH) {

    digitalWrite(boiler, LOW);
    delay(time);
    digitalWrite(pump, LOW);
  }
}

Please post the millis using code.

seems a perfect use case for a state machine (and use a button library to make your life easy)


(the top transition is for an emergency stop if needed)


details

here is what it looks like (now that you are searching for yourself I can post that :slight_smile: )

Boiler+Pump - Wokwi ESP32, STM32, Arduino Simulator

#include <Toggle.h>
const int RELAY_OFF = LOW;
const int RELAY_ON = HIGH;

const byte pumpRelayPin = 9;
const byte boilerRelayPin = 8;
const byte startButtonPin = 3;
const byte stopButtonPin = 2;


const unsigned long waitTime = 5000ul; // 5s in ms
unsigned long startTime;

Toggle startButton, stopButton;

enum {OFF, BOTH, WAIT_A_BIT} state = OFF;

void setup() {
  pinMode(pumpRelayPin, OUTPUT); digitalWrite(pumpRelayPin, RELAY_OFF);      // pump off
  pinMode(boilerRelayPin, OUTPUT); digitalWrite(boilerRelayPin, RELAY_OFF);  // boiler off

  startButton.begin(startButtonPin);
  stopButton.begin(stopButtonPin);
  Serial.begin(115200);

}

void loop() {
  switch (state) {
    case OFF:
      startButton.poll();
      if (startButton.onPress()) {
        digitalWrite(pumpRelayPin, RELAY_ON);    // pump ON
        digitalWrite(boilerRelayPin, RELAY_ON);  // boiler ON
        state = BOTH;
      }
      break;

    case BOTH:
      stopButton.poll();

      if (stopButton.onPress()) {
        digitalWrite(boilerRelayPin, RELAY_OFF); // boiler OFF
        startTime = millis();
        state = WAIT_A_BIT;
      }
      break;

    case WAIT_A_BIT:
      stopButton.poll();
      if (stopButton.onPress() || (millis() - startTime >= waitTime)) {
        digitalWrite(pumpRelayPin, RELAY_OFF); // pump OFF
        state = OFF;
      }
      break;
  }
}

if you want to start the boiler again while the pump is still active, you need to also add a transition from the WAIT_A_BIT state by testing if the startButton is pressed

1 Like

I am looking into it right now, thanks for pointing me to it

if you want to be able to start the boiler again while the pump is still active, you need to also add a transition from the WAIT_A_BIT state by testing if the startButton is pressed going to the BOTH state

This state machine stuff is what I have been looking for, I have a somewhat functional sketch, I just cant make the timer work.

const uint16_t timeInterval = 5000;



const uint8_t pilotLight = 13;  
const uint8_t boiler = 4;  
const uint8_t pump = 3;     
const uint8_t start = 6;         
const uint8_t stop = 8;         
unsigned long previousMillis = 0;
unsigned long currentMillis;
enum states { 
  HEATING,
  COOLDOWN,
  STAND_BY
};

states state;


void setup() {
  pinMode(pilotLight, OUTPUT);   
  pinMode(boiler, OUTPUT);  
  pinMode(pump, OUTPUT);    
  pinMode(start, INPUT);          
  pinMode(stop, INPUT);          

  digitalWrite(pilotLight, LOW);   
  digitalWrite(boiler, LOW); 
  digitalWrite(pump, LOW);     

  state = STAND_BY;
}

void loop() {



  switch (state) {

    case HEATING:

      digitalWrite(pilotLight, LOW);  
      digitalWrite(boiler, HIGH);
      digitalWrite(pump, HIGH);


      if (digitalRead(stop) == HIGH) {  // If stop is pressed,
        state = COOLDOWN;
        currentMillis = millis();
        //   turn on pump
      }

      break;

    case COOL_DOWN:


        

      digitalWrite(pilotLight, HIGH);  //   turn off pilotLight,
      digitalWrite(boiler, HIGH);
      digitalWrite(pump, LOW);
      
      
      if (currentMillis - previousMillis > timeInterval) {
        state = STAND_BY;
        previousMillis = currentMillis;
        
      }



      break;

    case STAND_BY:

      digitalWrite(pilotLight, HIGH);   // Set pilotLight high initially
      digitalWrite(boiler, LOW);  // Set boiler low initially
      digitalWrite(pump, LOW);

      if (digitalRead(start) == HIGH) {
        state = HEATING;
      }
      
      break;
  }
  Serial.println(currentMillis);
}

good progress

I added to post#3 (while you were working...) a solution using the Toggle library to handle the buttons. This code implements the state machine as it looks in the drawing. (click on the details triangle to see that)

that will give you some food for thoughts and you can add the missing transition to go back to the BOTH state if you press the start button whilst being in the WAIT_A_BIT state

I've seen that, that is awesome, I have other projects that I will approach this way, thank you!

it's a good way to think about the code in an asynchronous way and the code is easy to read too

have fun !

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