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
}
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.
@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.
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.
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) ?
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 and works
#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
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