Trouble with state change using millis

Hi,
The sketch I'm trying make, monitors a pump, when the pump is on data is sent a database every minute, when the pump is off it sends data every 10 minutes.
Problem is when in the on state and then transitions to off state change is not detected until 10 minutes have pasted. State change from off to on is detected right away.

Using a NodeMCU board.

Any thoughts?

// Tme setup

// Time interval 1
unsigned long previousMillis = 0;
const long interval1 = 60000;  // one minute

// Time interval 2
//unsigned long previousMillis2 = 0;
const long interval2 = 600000;  // ten minutes

// Time interval var

unsigned long intervalx = 0;  // zero minutes

void setup() {
  
pinMode(pumpD1, INPUT); // enable internal pullup
digitalWrite (pumpD1, HIGH); 

  Serial.begin(115200);
//  
  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while(WiFi.status() != WL_CONNECTED) { 
    delay(500);
    Serial.print(".");
  
  Serial.println("");
  Serial.print("Connected to WiFi network with IP Address: ");
  Serial.println(WiFi.localIP());
  }
 
  dht.begin();
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {

Serial.println("Failed to read from DHT sensor!");
}
else
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" degrees Celsius Humidity: ");
Serial.print(h);

 Serial.println("Motor State on=1 off=0");
 Serial.println(digitalRead(pumpD1));

delay(200); 
}

void loop() {
  // put your main code here, to run repeatedly:
 unsigned long currentMillis = millis();
 if (digitalRead(pumpD1)==1) {
unsigned long intervalx = interval1;
if (currentMillis - previousMillis >= interval1) {
    
 Serial.print("intervvalx  ");
 Serial.println(intervalx);
 Serial.print("currentMillis  ");
 Serial.println(currentMillis);
 previousMillis = currentMillis;
 
 WebPost();
 }}
 if (digitalRead(pumpD1)==0) {
unsigned long intervalx = interval2;
if (currentMillis - previousMillis >= interval2) {
 Serial.print("intervvalx  ");
 Serial.println(intervalx);
 Serial.print("currentMillis  ");
 Serial.println(currentMillis);
 previousMillis = currentMillis;
 
 WebPost();
 }}
}

Read the how to use this forum-please read sticky to see how to properly post code and some advice on how to ask an effective question. Remove useless white space and format the code with the IDE autoformat tool (crtl-t or Tools, Auto Format) before posting code.

Thanks

If the pump is on long enough to make a post, you will have a recent time in prevMillis. When the pump goes off, it will be at least nine minutes before the ten minute interval is reached so the post regarding off is sent.

I think that you need to keep track of state changes for the pump. When it does change, post the new status and then go into your periodic update mode.

Hi 65td,

Problem is when in the on state and then transitions to off state change is not detected until 10 minutes have pasted.

does this mean you want a WebPost as soon as the Pump-state changes ON/OFF and OFF/ON regardless how much time has passed by?

example
10:00:00 ON WebPost
10:01:00 ON WebPost
10:02:00 ON WebPost at 10:02:05 pump switches OFF
10:02:05 OFF WebPost though only 5 seconds have passed by since last WebPost?

best regards Stefan

When the pump changes state you need to update the value of previousMillis so that the new state counts the time from when the state change happens.

Separately, you have this line of code

unsigned long intervalx = interval1;

but you never use intervalx

If you change this line

if (currentMillis - previousMillis >= interval1) {

to

if (currentMillis - previousMillis >= intervalx) {

then the same block of code can be used for both pump states which shortens the program and by removing duplicate code reduces the risk of errors. Something like this

void loop() {
        // put your main code here, to run repeatedly:
    unsigned long currentMillis = millis();
    byte pumpState = 0;
    static byte previousPumpState = pumpState
    pumpState = digitalRead(pumpD1);

    if (pumpState == 0  and previousPumpState == 1) {
        previousMillis = currentMillis;
        unsigned long intervalx = interval1;
    }
    else if (pumpState == 1 and previousPumpState == 0) {
        previousMillis = currentMillis;
        unsigned long intervalx = interval2;
    }
    if (currentMillis - previousMillis >= intervalx) {
        Serial.print("intervvalx  ");
        Serial.println(intervalx);
        Serial.print("currentMillis  ");
        Serial.println(currentMillis);
        previousMillis = currentMillis;
        
        WebPost();
    }
}

...R

I coded the functionality with a real statemachine.

It uses a variant of non-blocking timers based on millis(). This variant needs only

  • one timervariable and a call of the boolean-function. The function internally updates the timervariable

The statemachine has four states PumpIsOn, PumpChanged_On_Off, PumpIsOff, PumpChanged_Off_On

This enables to send an extra-Webpost immediately when the pumpstate changes from On->OFF and from Off->On
Not sure if this is what you want. I allowed myself to change some variable-names to be really selfexplaining

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

void WebPost() {
  Serial.println ("WebPost"); 
}


// definition of the states and the state-variable requiered for the statemachine
enum MyPumpStates { PumpIsOn, PumpChanged_On_Off, PumpIsOff, PumpChanged_Off_On } PumpStatus = PumpChanged_Off_On; 

 
const int pumpD1 = 2;

// Tme setup
unsigned long PumpON_Timer  = 0;
unsigned long PumpOff_Timer = 0;

const unsigned long MillisInOneMinute = 60000;  
const unsigned long MillisIn10Minutes = 600000;  // ten minutes

// Time interval var
unsigned long intervalx = 0;  // zero minutes

void setup() {

  pinMode(pumpD1, INPUT); // enable internal pullup with parameter INPUT_PULLUP
  // digitalWrite (pumpD1, HIGH); writing to an input makes no sense

  Serial.begin(115200);
  //
  /*
  WiFi.begin(ssid, password);
  Serial.println("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");

    Serial.println("");
    Serial.print("Connected to WiFi network with IP Address: ");
    Serial.println(WiFi.localIP());
  }

  dht.begin();
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
  }
  else {
    Serial.print("Temperature: ");
    Serial.print(t);
    Serial.print(" degrees Celsius Humidity: ");
    Serial.print(h);
  
    Serial.println("Motor State on=1 off=0");
    Serial.println(digitalRead(pumpD1));
    delay(200);
*/
    // preset PumpStatus in setup
    if (digitalRead(pumpD1) == 1) {
      PumpStatus = PumpIsOn;
      PumpON_Timer = millis();
    }
    else {
      PumpStatus = PumpIsOff;    
      PumpOff_Timer = millis();  
    }
    
}

void loop() {
  // put your main code here, to run repeatedly:
  unsigned long currentMillis = millis();
    
  switch (PumpStatus) 
    {
    case PumpIsOn:


      if (TimePeriodIsOver (PumpON_Timer, MillisInOneMinute) ) {
        Serial.print("intervval  ");
        Serial.println(MillisInOneMinute);
        Serial.print("currentMillis  ");
        Serial.println(currentMillis);
  
        WebPost();
      }
      if (digitalRead(pumpD1)  == 0 ) {    // remember this is only checked if PumpStatus is in state PumpIsOn
        PumpStatus = PumpChanged_On_Off; // so this really only detects a statechange On->Off
      }        
      break;

    case PumpChanged_On_Off:
      Serial.print("Pump was on now is off");
      WebPost();                 // do a WebPost as PumpStatus has changed
      PumpOff_Timer = millis();  // setup Timervariable for offtime to do next WebPost in 10 minutes from now
      PumpStatus = PumpIsOff;
      break;

    case PumpIsOff:
      if (TimePeriodIsOver (PumpOff_Timer, MillisIn10Minutes) ) {
        Serial.print("interval  ");
        Serial.println(MillisIn10Minutes);
        Serial.print("currentMillis  ");
        Serial.println(currentMillis);
  
        WebPost();
      }
      if (digitalRead(pumpD1) == 1) {   // remember this is only checked when PumpState is in state PumpIsOff
        PumpStatus = PumpChanged_Off_On; // so it really only detects a statechange Off->On
      }
      break;

    case PumpChanged_Off_On:
        Serial.print("Pump was off now is on");
        WebPost();               // do a WebPost as PumpStatus has changed 
        PumpON_Timer = millis(); // setup Timervariable for ontime to do next WebPost in 1 minute from now
        PumpStatus = PumpIsOn;     
        break;      
                    
  } // end of switch
  
}

best regards Stefan

Thank you for the input.

To clarify when the pump or switch goes to 1 (on) I want to post the tempature at one minute intervals to the database, when the pump turns off (low) I want to capture the stae change and reset the timing for posting to every 10 minutes.

Robin2 - I had a typo that you found it was suppose to be what you had corrected.

I will try both offered suggestions.

Once again, thank you for your input.
I used thecode that StefanL38 posted, it operated the way as I had wanted.