I need to run a pump to partially drain a tank, then when this step is complete i need to refill the tank based on pH value be adding either water or chemical until a float switch is made.
If i put the draining part of the code in the 'setup' and the topping up part in the 'loop' will this work or will both work at the same time?
how do run one piece for x amount of time and then kick in to another piece basically?
Any help would be greatly appreciated.
niall-c:
If i put the draining part of the code in the 'setup' and the topping up part in the 'loop' will this work or will both work at the same time?
Yes that will work.
You could just use a delay in setup.
Leo..
// sensible names for states; place this near the top of your code
#define WAITSTART 0
#define DRAIN 1
#define FILL 2
int currentState = WAITSTART;
void loop()
{
switch(currentState)
{
case WAITSTART:
waitStart();
break;
case DRAIN:
drain();
break;
case FILL:
fill();
break;
default:
break;
}
}
void waitStart()
{
if(digitalRead(yourButton) == LOW)
{
currentState = DRAIN;
}
}
void drain()
{
...
...
// once done draining, change the state
if(drain condition fulfilled)
{
currentState = FILL;
}
}
void fill()
{
...
...
// once done filling, change the state
if(digitalRead(yourFloatswitch) == LOW)
{
currentState = WAITSTART;
}
}
How it works:
The loop() starts with a state (WAITSTART) where it waits for a start condition; it stays in the WAITSTART state till a condition is fulfilled. In the example it waits for a button to be pressed (the button is supposed to be connected between pin and ground); your condition can be different (e.g. a pH level).
Note that loop() will execute the function associated with WAITSTART (waitStart()) thousands or millions of times till the button is pressed.
Once the button is pressed, the state changes to DRAIN and the drain() function is executed. Although you can use delay in this function, it's not the intention. Have a look at the BlinkWithoutDelay example that comes with the IDE how to implement non-blocking delays.
Assuming a non-blocking delay, loop() will again execute the function associated with this state (drain()) thousands or millions of times till the delay is done.
Once draining is completed, the state changes to FILL. Again the function associated with this state (fill()) is executed thousands or millions of times by loop() till a condition is fulfilled (in this case the float switch is activated; switch wired between pin and ground) after which the state changes to WAITSTART and the program waits for the start button to be pressed again.
Thanks very much sterretje. I have it draining first and then resupplying based on pH level. (i.e. adding 2 different chemical to reach pH set point).
I have another problem now if any one could help.
My code was continuously displaying the pH level on the serial monitor and was matching the actual pH level as it changed.
Now i have added code, that if the pH level is less then 7 set an output high, if pH greater then 7 set another output high. I have added this with a delay (in order to let the pH level settle).
Problem is the delay has now passed on the serial monitor display of the pH level, which means the pH display is not keeping up with the actual change >:( .
Ive attached my code, any help would be greatly appreciated.
Don't use delays As mentioned before, check the BlinkWithoutDelay example that comes with the IDE.
And the use of a statemachine makes life soooooo simple
// sensible names for our states
#define PH_NORM 0 // PH within limits
#define PH_LOW 1 // PH low
#define PH_HIGH 2 // PH high
// current state
int currentState = PH_NORM;
// current 'time'
unsigned long currentMillis = millis();
void loop() {
// start time of 'operation'
static unsigned long starttime = 0;
int sensorVal = digitalRead(FLOAT_SWITCH);
digitalWrite(PUMP_D_PIN, LOW);
if (input_stringcomplete) {
myserial.print(inputstring);
inputstring = "";
input_stringcomplete = false;
}
if (myserial.available() > 0) {
char inchar = (char)myserial.read();
sensorstring += inchar;
if (inchar == '\r') {
sensor_stringcomplete = true;
}
}
if (sensor_stringcomplete) {
Serial.println(sensorstring);
ph = sensorstring.toFloat();
if (ph >= 7.0) {
Serial.println("high");
currentsState = PH_HIGH;
}
if (ph <= 6.999) {
Serial.println("low");
currentsState = PH_LOW;
}
switch (currentState)
{
case PH_NORM:
// do nothing
break;
case PH_HIGH:
// if not started and tank not full
if (starttime == 0 && sensorVal == LOW)
{
// start pump
digitalWrite(PUMP_1_PIN, HIGH);
// set the start time for the delay
currentMillis = millis();
}
// if tank full or delay lapsed
if (sensorVal == HIGH || currentMillis - starttime >= 10000)
{
// switch pump off
digitalWrite(PUMP_1_PIN, LOW);
// reset start time for next time that delay is needed
starttime = 0;
// go back to PH_NORM state
currentState = PH_NORM;
}
break;
case PH_LOW:
break;
}
sensorstring = "";
sensor_stringcomplete = false;
}
}
Notes:
you have a delay after switching a pump off; I have no idea why.
for you to implement the PH_LOW
you set PUMP_D_PIN low in the loop but never set it back to high; not very useful
thanks for this sterretje. i will try add statemachine, quite new to coding so just figuring things out as i go.
I had the delay in to let the chemical reaction settle, but i notice know i dont need it - thanks.
I need the pH low and high states, in case of overshoot of the set point.
I'm trying to add chemical in 20mL amounts that's why im using delays. Il check out the blinkwithoutdelays code (i should of done that already ).
I'm nearly there, you see the thing is, when topping up with chem i need the pump to come on for 10 secs and off for 10secs then back on for 10 secs and off for ten secs and so to let the reading stabilize.
The code you supplied will keep the pump on for 10 sec or until the level is met and then turn it off without coming back on.
My code needs to only turn the pumps off when the level is made and no other condition.
The topping up needs to be done in 10 sec increments on and off
So i have this code half working, sequence of events are:
drain 100mL from tank.
stop draining
serial monitor constantly monitoring pH value.
one pump switched on for when pH is low (below 7)
different pump switched on for when pH is high (above 7)
either pump if on should switch off when measurement level is met.
when activated the pumps inject chemical in 10 sec intervals - on for 10 sec off for 10 sec (to allow stable reading).
The problem i am having now is that the 'low' pump is staying on when the value is high.
second problem pumps are not switching off when measurement level is met (float switch)
Please see code below.
thanks,
Niall
#include <SoftwareSerial.h>
#define rx 2
#define tx 3
#define PUMP_D_PIN 6
#define FLOAT_SWITCH 13
#define PH_LOW 8
#define PH_HIGH 9
#define PH_NORM 10
const int PUMP_1_PIN = 4;
int PUMP_1_STATE = LOW;
const int PUMP_2_PIN = 5;
int PUMP_2_STATE = LOW;
SoftwareSerial myserial(rx,tx);
int currentState = PH_NORM;
const long interval = 10000;
unsigned long previousMillis = 0;
String inputstring = "";
String sensorstring = "";
boolean input_stringcomplete = false;
boolean sensor_stringcomplete = false;
float ph;
void setup() {
Serial.begin(9600);
myserial.begin(9600);
inputstring.reserve(10);
sensorstring.reserve(30);
pinMode(PUMP_1_PIN, OUTPUT);
pinMode(PUMP_2_PIN, OUTPUT);
pinMode(PUMP_D_PIN, OUTPUT);
pinMode(FLOAT_SWITCH, INPUT_PULLUP);
digitalWrite(PUMP_D_PIN, HIGH);
delay(10000);
// put your setup code here, to run once:
}
void serialEvent() {
char inchar = (char)Serial.read();
inputstring += inchar;
if (inchar =='\r') {
input_stringcomplete = true;
}
}
void loop() {
int sensorVal = digitalRead(FLOAT_SWITCH);
digitalWrite(PUMP_D_PIN, LOW);
if (input_stringcomplete) {
myserial.print(inputstring);
inputstring = "";
input_stringcomplete = false;
}
if (myserial.available() > 0) {
char inchar = (char)myserial.read();
sensorstring +=inchar;
if (inchar == '\r'){
sensor_stringcomplete = true;
}
}
if (sensor_stringcomplete) {
Serial.println(sensorstring);
ph = sensorstring.toFloat();
if (ph >= 7.0) {
Serial.println("high");
currentState = PH_HIGH;
}
if (ph <= 6.999) {
Serial.println("low");
currentState = PH_LOW;
}
unsigned long currentMillis = millis();
switch (currentState) {
case PH_LOW:
if (currentMillis - previousMillis >= interval){
previousMillis = currentMillis;
if (PUMP_1_STATE == LOW)
PUMP_1_STATE = HIGH;
else
PUMP_1_STATE = LOW;
digitalWrite(PUMP_1_PIN, PUMP_1_STATE);
}
break;
case PH_HIGH:
if (currentMillis - previousMillis >= interval){
previousMillis = currentMillis;
if (PUMP_2_STATE == LOW)
PUMP_2_STATE = HIGH;
else
PUMP_2_STATE = LOW;
digitalWrite(PUMP_2_PIN, PUMP_2_STATE);
}
break;
case PH_NORM:
if (sensorVal == LOW) {
digitalWrite(PUMP_1_PIN, LOW);
digitalWrite(PUMP_2_PIN, LOW);
}
break;
}
sensorstring = "";
sensor_stringcomplete = false;
}
// put your main code here, to run repeatedly:
}
In the 'case' for the PH_NORM state, you can switch on/off whatever needs to be switched when the pH level is within limits.
Note on your use of #define
The #defines for the states can have any number. It looks like you think that they must be unique between the other #defines; that is not the case. Currently you're using 8, 9 and 10 which is OK but might indicate that there is also 1, 2, ....; I was initially a little confused
That suggestion worked a treat - doing what i want it to now.
The next thing now is improvements.
For the drain part this occurs firstly, i have this in the setup section - problem is when i switch on the serial monitor the whole things starts again, would like to improve this, i'll try your previous suggestion to use a case and state.
Next thing would be i would like to extend the time the pump is off for if not too complicated i.e. using the blinking LED as an example i would like the LED on for 10 seconds but off for 20 seconds. Tried a couple of things for this but no joy >:(
Split a state into two. In the one you switch the pump on for 10 seconds, in the other one you switch the pump off for 20 seconds.
switch (currentState)
{
case PH_LOW_PUMP_ON:
// check if tank is full
if (sensorVal == ??? )
{
// change state
currentState = PH_NORM;
// done
return;
}
// delay not started yet
if (previousMillis == 0)
{
// set start time of delay that pump must be on
previousMillis = currentMillis;
// switch pump on
digitalWrite(PUMP_1_PIN, ?? ? );
}
if (currentMillis - previousMillis >= intervalOn)
{
// reset previous millis for the next time that we need a delay
previousMillis = 0;
// change state
currentState = PH_LOW_PUMP_OFF;
}
break;
case PH_LOW_PUMP_OFF:
// check if tank is full
if (sensorVal == ??? )
{
// change state
currentState = PH_NORM;
// done
return;
}
// delay not started yet
if (previousMillis == 0)
{
// set start time of delay that pump must be on
previousMillis = currentMillis;
// switch pump on
digitalWrite(PUMP_1_PIN, ??? );
}
if (currentMillis - previousMillis >= intervalOff)
{
// reset previous millis for the next time that we need a delay
previousMillis = 0;
// change state
currentState = PH_LOW_PUMP_ON;
}
break;
}
Note that your switch/case is slowly but surely growing out of hand; it's advisable to start implementing functions.
void loop() {
switch (currentState)
{
case PH_LOW_PUMP_ON:
phLow_pumpOn();
break;
case PH_LOW_PUMP_OFF:
phLow_pumpOff();
break;
case PH_HIGH_PUMP_ON:
phHigh_pumpOn();
break;
case PH_HIGH_PUMP_OFF:
phHigh_pumpOff();
break;
case PH_NORM:
phNorm();
break;
}
}
/*
comment your function here
*/
void phLow_pumpOn()
{
// check if tank is full
if (sensorVal == ??? )
{
// change state
currentState = PH_NORM;
// done
return;
}
// delay not started yet
if (previousMillis == 0)
{
// set start time of delay that pump must be on
previousMillis = currentMillis;
// switch pump on
digitalWrite(PUMP_1_PIN, ??? );
}
if (currentMillis - previousMillis >= intervalOn)
{
// reset previous millis for the next time that we need a delay
previousMillis = 0;
// change state
currentState = PH_LOW_PUMP_OFF;
}
}
You need to write the other functions; you can see how clean the switch/case stays so you have a god overview what is happening in it.
The 'names' that I defined for the states were given based on my understanding of your code and more importantly my lack of knowledge of english language when it comes to chemistry; what is the opposite of acid? Something like 'alkaloid'? I suggest that you change the names of the states and functions. Also change the pump 'names' to e.g. WATERPUMP and CHEMICALPUMP.