Help with time intervals using millis(), run one at a time

Hello,

Im using a DRV8871 to control a pump forward and reverse.

I would like to run the pump forward for 5 sec, once the pump forward is done, start pump reverse to purge the pump hose for 5 sec. Once both pump forward and reverse is complete I want the pump to wait in idle mode for 20 sec and restart this pump cycle after the 20 sec.

The time interval are going to change but this is just to see if it works. The result of this code is that the pump and purge runs at the same time. I would like for the pump, purge, and idle state to work one at a time. Pump first (5 sec), then purge (5 sec), then idle (20 sec). Start the pump cycle again. I added flag for this but not working at all. And I dont want to use delay because eventually I will be adding oled, time of flight, etape, etc.

Any help will be much appreciated.



#define PUMP_IN1 2 //motor control pin reverse direction
#define PURGE_IN2 3 //motor control pin forward direction

int PumpState = LOW;             // pump used to set the LED
int PurgeState = LOW;             // purge used to set the LED

bool pump =false;             // pump flag
bool purge =false;             // purge flag
bool idle = false;             // idle flag


const unsigned long pumpInterval = 5000;
const unsigned long purgeInterval = 5000;
const unsigned long waitInterval = 20000;
unsigned long previousWaitInterval= 0;
unsigned long previousPumpTime = 0;
unsigned long previousPurgeTime = 0;
unsigned long previousTime = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  pinMode(PUMP_IN1, OUTPUT);
  pinMode(PURGE_IN2, OUTPUT);
  
 // previousWaitInterval= millis();
  //previousPumpTime= millis();
  //previousPurgeTime= millis();
  
  delay(50);
  
}

void loop() {
  

  unsigned long currentTime = millis();
  
  if (currentTime - previousPumpTime >= pumpInterval) {
    Serial.println("pumping");
      PumpState = HIGH;
    digitalWrite(PUMP_IN1, PumpState);
    previousPumpTime = millis(); 
    // delay 4 milliseconds because of millis bug
    delay(4);
    pump = true;
    }

    if (currentTime - previousPurgeTime >= purgeInterval && pump == true){
  
    Serial.println("purging");
      PurgeState = HIGH;
    digitalWrite(PURGE_IN2, PurgeState);
    previousPurgeTime = millis();
    // delay 4 milliseconds because of millis bug
    delay(4);
    pump = false;
    purge = true;
    }
    
    if (currentTime - previousWaitInterval >= waitInterval && purge == true){
    Serial.println("waiting for next samples");
      PumpState = LOW;
      PurgeState = LOW;
    digitalWrite(PUMP_IN1, PumpState);
    digitalWrite(PURGE_IN2, PurgeState);
    previousWaitInterval = millis();
    // delay 4 milliseconds because of millis bug
    delay(4);
    
    }
     
    pump == false;             // pump flag
    purge == false;             // purge flag
    idle == false;             // idle flag

   }




These are equality checks not assignments.

1 Like

what about this approach.

#include "Particle.h"

#define PUMP_IN1 D2 //motor control pin reverse direction
#define PURGE_IN2 D3 //motor control pin forward direction

int PumpState = LOW;             // pump used to set the LED
int PurgeState = LOW;  

unsigned long interval = 0;
int counter = 0;

void setup() {

  SYSTEM_MODE(AUTOMATIC);
  //pumpControll(counter);
}

void loop() {
    
    if (millis() - interval > 5000) {
    counter ++;
    
    
    interval = millis();
    
  }
  //if(counter == 0){
     // pumpControll(counter);
  //}
  pumpControll(counter);
}

void pumpControll(int data){

if(data == 1){
   PumpState = HIGH;
   digitalWrite(PUMP_IN1, PumpState);
   Particle.publish("forward", String(interval), 60, PRIVATE); 
   delay(1000);
}
 if(data == 2){
 PurgeState = HIGH;
 digitalWrite(PURGE_IN2, PurgeState);
 Particle.publish("reverse", String(interval), 60, PRIVATE);
 delay(1000);
}

 if(data == 3){
    PumpState = LOW;
    PurgeState = LOW;
    digitalWrite(PUMP_IN1, PumpState);
    digitalWrite(PURGE_IN2, PurgeState);
    Particle.publish("stop", String(interval), 60, PRIVATE);
    delay(1000);
}
 if(data == 7){
    Particle.publish("start_over", String(interval), 60, PRIVATE);
    counter = 1;
    delay(1000);
}

}

Please note I didn’t test this. :slight_smile:

also

#define PUMP_IN1  2 //motor control pin reverse direction
#define PURGE_IN2  3 //motor control pin forward direction

should be:

#define PUMP_IN1  D2 //motor control pin reverse direction
#define PURGE_IN2  D3 //motor control pin forward direction

ok I edited my code just to test if is working and maybe is a FSM or whatever but look like is working :wink:

test

2 Likes

This sounds like a perfect application for a Finite State Machine (FSM). If you search here you’ll find quite a few good examples. You can use millis() to change state to forward, reverse, idle, etc.

3 Likes

@picsil Has the right idea, a FSM with the various pumping and resting phases and transitions between then is a very good way to handle this because you will keep the loop cadence high rather than holding it for long periods.

1 Like

Thank you for your help! Very much appreciated.

Quick question. How would you make different time intervals, lets say you want the wait (stop) time 20 seconds?

There are multiple ways.
Common to all would be that at the state transition you’d set the desired interval and the starting timestamp in a global or static local variable and then check the elapsed time (difference between current time and transitioning timestamp) against the interval.
This can be done either per state individually or - if you are certain that yon won’t need to transition prematureley - at the top of your FSM.

2 Likes

How would someone typically handle adding additional sensors in the future to a FSM that was written to control the Pump ?
Is it as simple as adding a generalized function for each FSM State in the future (read the depth sensor, etc) ?

1 Like

When each FSM adheres to the paradigm to not trap the flow at any instance you can have multiple FSMs running concurrently.

3 Likes

Which relay board are you using ?
The reason I’m asking is I’ve used a few that, with a library, allows you to easily pass the run-time to the controller when you request the relay turn on…similar to this: TurnOnRelay01(600); and never look back.

This one is 10000 times over complicated but i’m fan of Goldberg :slight_smile: and works :smile:

#include "Particle.h"

#define PUMP_IN1 D2 //motor control pin reverse direction
#define PURGE_IN2 D3 //motor control pin forward direction

int PumpState = LOW;             // pump used to set the LED
int PurgeState = LOW;  

unsigned long interval = 0;
int counter = -1;
int fvrTb = 0;
int revTb = 0;
int stopTb = 0;
int st_overTb = 0;
int special1 = 3;
int special2 = 7;
int special3 = 20;
bool helper1 = false;


void setup() {

  SYSTEM_MODE(AUTOMATIC);
  Particle.function("intervals", intervals);
  revTb =  fvrTb + special1;
  stopTb =  revTb + special2;
  st_overTb =  stopTb+ special3;
  //pumpControll(counter);
}


int intervals(String message){
        int colonPos = message.indexOf(":") ;
    if (colonPos < 0) return -1 ;                 // syntax error not found
    String command = message.substring(0,colonPos) ;
    String argument = message.substring(colonPos+1) ;
    
    if (command.equals("fwd")){
        int overwrite = argument.toInt();
        special1 = overwrite;
        return overwrite;
    }
    
    else if (command.equals("rev")){
        int overwrite = argument.toInt();
       special2 = overwrite;
        return overwrite;

    }
    
    else if (command.equals("stop")){
        PumpState = LOW;
        PurgeState = LOW;
        digitalWrite(PUMP_IN1, PumpState);
        digitalWrite(PURGE_IN2, PurgeState);
        counter = -1;
        helper1 = false;
    }
    else if (command.equals("start")){
        counter = 0;
        helper1 = true;
    }
    
     else if (command.equals("stover")){
        int overwrite = argument.toInt();
        special3 = overwrite;
        //st_overTb = stopTb;
        return overwrite;
    }

   
    else if (command.equalsIgnoreCase("reset")){
        System.reset();
        return 0;
    } else {
        return -2 ;                         // command not found
    }
}




void loop() {
    
    
    if (millis() - interval > 1000) {
     if(helper1){
       counter ++;
     }
    
    
    interval = millis();
    
  }
  revTb =  fvrTb + special1;
  stopTb =  revTb + special2;
  st_overTb =  stopTb+ special3;
  
    pumpControll(counter);
  
}

void pumpControll(int data){
 


if(data == fvrTb ){
   PumpState = HIGH;
   digitalWrite(PUMP_IN1, PumpState);
   Particle.publish("forward", String(interval), 60, PRIVATE); 
   //delay(1000);
}
 if(data == revTb){
 PurgeState = HIGH;
 digitalWrite(PURGE_IN2, PurgeState);
 Particle.publish("reverse", String(interval), 60, PRIVATE);
// delay(1000);
}

 if(data == stopTb){
    PumpState = LOW;
    PurgeState = LOW;
    digitalWrite(PUMP_IN1, PumpState);
    digitalWrite(PURGE_IN2, PurgeState);
    Particle.publish("stop", String(interval), 60, PRIVATE);
    //delay(1000);
}
 if(data == st_overTb){
    //Particle.publish("start_over", String(interval), 60, PRIVATE);
    counter = 0;
   // data = 0;
   // delay(1000);
}

}

commands as follows:
stop: permanent stop
start: start cycle
rev: reverse time delay
fwd: forward time delay
stover: how long to wait to start over
for example
fwd:15 or rev:7 or stover:32 etc.
don’t forget baout “:” between comand and time reuest and at the end of start: and stop:

All you have left is to figure out how to change the cycle patterns, how to repeat individual pattern, and I’m not sure, but I think you should call stop between fwd. and rev. It will be safer and better for the driver and the pump :wink:

1 Like

Thank you again. I appreciate you writing out an example. It’s definitely more complex. Good point on adding a stop between fwd and rev. It will take me some time working with this code to understand it fully. Im still learning how to code and your examples is teaching me alot. Thanks

Right now im using a Adafruit DRV8871 but im might use a DC Motor + Stepper FeatherWing in the future.