Turn on Peristaltic pump based on PH reading

That's fine. A pencil and paper will be quicker. And you need to know the same things to do one with a pencil as you need to know to effectively use a CAD package, or Tinkercod, anyway, so…

It seems like your pH sesnor should have special provision of power to ensure that it doesn't do what it appears to be doing now.

a7

Is this of any use to you

Hi John,

After trying this a bit more unfortunately it doe snot sole the problem.

I tried running the GND from the Mega to the other side of the breadboard and just connect the two grounds from the probe to it, that didn't work.

I also plugged the two grounds form the probe straight into the mega, that also did not work.

I also just noticed that every time the LED comes on it is dimming the LCD display.

Also when I plug out the LED and the PH reading goes back to normal.

Am I perhaps chasing my tale, do you think this will be an issue when using the driver, the pump will be driven by a different power source.

I may also try connecting the Mega to an external power source rather than taking power from the mega itself.

I'm a tool, I think it was how I had the LED connected, all on the one rail.
I'm so sorry for wasting your time on that.

This may not be the correct place for this question but......

I now have the pump operating from the PH probe reading. Pump will turn on when above PH7 and turn off when below.

What I'd like to do now is have the pump dispense for say 20 seconds and then pause to allow for a new more accurate PH reading. And then dispense further or not as required.

I'm trying the millis instruction but again don't really know it. This is what I have tried with no success.

//////////////PH Probe - LCD - PH Pump////////////////////
//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//////////////L298n Dual H Bridge Driver///////////////////
int ena = A7;
int in1 = A6;
int in2 = A5;
//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 22.99;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;
/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 5000;
/////////////Set Up/////////////////
void setup() {
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  delay(5000);
  lcd.clear();
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  startMillis = millis();
  Serial.begin(9600);
  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;

  ////////////////////////Serial Print and LCD//////////////
  Serial.print("PH = ");
  Serial.println(phValue);//This is what i added from here
  delay(500);
lcd.begin(16, 2);
lcd.print("PH = ");
lcd.print(phValue);
/////////////////////////Turning on and Off pumps if PH greater or less than PH7.//////////////////////////////
if ((phValue >7 ) && (currentMillis - startMillis >= period))
  {
    digitalWrite(A7, HIGH);//If PH is greater than 7, A7 goes high, turns on LED
    digitalWrite(in1, HIGH);
    digitalWrite(in2, LOW);
    startMillis = millis();
  }
  else if (phValue < 7) {
    digitalWrite (A7, LOW);//If PH is less than 7, A7 goes low, turns off LED
    digitalWrite(in1, LOW);
    digitalWrite(in2, LOW);
}
}

Not sure if this has been mentioned but I assume the tank you are measuring PH in has agitation so when you add to balance your PH what you add is mixed well and fast. On large tanks I used an air bubbler. Also as mentioned you may want to use some hysteresis.

Ron

Not hard. But say how long the pause is. It sounds like a fixed mandatory no pumping time after the dispensing period has run.

So…

When the pH goes high, turn on the pump and start the timer for on-ness.

When the on timer expires, turn off the pump and start the timer for off-ness.

When the off timer expires, go back to letting the pH decide to turn on the pump.

A flag to say if the pump is ON, PAUSED or IDLE. A timer variable for timing off and the same can be using for timer on. Only when PAUSED will the pH be considered.

It's a tiny finite state machine, yeah that's the ticket. Google it

 arduino finite state machine

I'd just write it but I am looking through the smallest of windows at this from under the umbrella here. :wink:

HTH

a7

Thanks for that.
I had a quick google, seems a bit complicated, I'd like to try and figure it out, not sure how well that will work out. But you understand perfectly what I am trying. It will be a few days before I get back to it, working night shift and very busy.
I hope it's the sun and not the rain that has you under the umbrella.

Yes. There are 1000s of tutorials and such, for every learning style. Keep reading or viewing until you find a good match. There is an AHA! moment, once this is easy you will wonder why it was so hard.

I wrote this but have not tested it. But it should be close. I apologize for not taking the time to test it and present it in the context of a fully functioning skethc.

It's a little state machine that uses disState (dispenser status) to direct the actions of the dispenser respecting time and pH.

See if it makes any sense. The switch/case statement may be new to you - it's just a better syntax for what might otherwise need to be a hole chain of if/else if/else logic.

Review the syntax of the switch/case statement at your favorite learning source or see

switch/case statement

Very simply a selector of one of several blocks of code to execute.

// dispenser state machine parts

// 0. variables, global here for now, to support the machinery

// IDLE - the dispenser is not running
// RUN - the dispenser is on a timed run
// PAUSED - the dispenser is enjoying a time-out

enum {IDLE, RUN, PAUSED};

unsigned char disState = IDLE;
unsigned long theTimer;
unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;        // run for 20 seconds
const unsigned long pausePeriod = 15000;   // rest for 15 seconds

 // 1. at the top of the loop
 //...

  now = millis();

// 2. later in the loop check and adjust the dispenser
//...

  switch (disState) {
  case IDLE :  // not running. should it?
    if (pHValue > 7) {

      digitalWrite(A7, HIGH); // If pH is greater than 7, A7 goes high, turns on LED
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);

      theTimer = now;
      disState = RUN;         // now wait out the run period
    }
    else {
      digitalWrite (A7, LOW); // If pH is less than or equal to 7, A7 goes low, turns off LED
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
    }

    break;

  case RUN :  // running. for long enough?
    if (now - theTimer >= runPeriod) {
      digitalWrite (A7, LOW);
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);

      theTimer = now;
      disState = PAUSED;      // now wait out the pause period
    }

    break;

  case PAUSED :  // paused. for long enough?
    if (now - theTimer >= pausePeriod)
      disState = IDLE;    // back to pH control

    break;
  }

HTH

a7

Wow, you wrote that and replied very quick. Thank You very much, I really appreciate your help.

I didn't think I'd get to it for another day or two, but had to try it. You are a genius with this stuff.
It worked as you wrote it.

However I have one or two problems, when I add it to what I have already, the delays I have for printing to the LCD are buggering the whole thing up.

Visually the delays give me what I am looking for, it shows PH - EC and Temperature, it flicks between EC and Temperature showing EC for 8 seconds and Temperature for 4 but it affects the timing of the pump coming on and off. I understand this is because of the delay being so long (12 seconds).

Now I could take out the delay and temperature reading, and it would show PH and EC values and be constantly polling them but I'm sure that would not be the right way of doing it.

Secondly I am adding a second pump to pump.
One is for pumping PH DOWN if greater than PH7 the other for pumping PH UP if less than PH5.

I didn't expect to just be able to copy and paste over for a second pump, I just tried it anyway, not many errors showed when compiling this, just around the switch, state expected unqualified-id before switch.

I've tried to code some of it myself for both issues, but not getting remotely close, I won't get to it again until probably Sunday. But have you any advice on what to look at next.

///////////////PH Probe - EC Probe - LCD - PH Pump////////////////////

//////////////EC Probe///////////////////
#include "DFRobot_EC.h"
#include <EEPROM.h>
#define EC_PIN A1
float voltage,ecValue,temperature = 25;
DFRobot_EC ec;

//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//////////////L298n Dual H Bridge Driver///////////////////
/////////////Pump Up//////////////
int ena = A7;
int in1 = A6;
int in2 = A5;

/////////////Pump Down//////////////(Not Added Yet)
int enb = 8;
int in3 = 9;
int in4 = 10;

//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 22.99;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;

/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
enum {IDLE, RUN, PAUSED};

unsigned char disState = IDLE;
unsigned long theTimer;
unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;        // run for 20 seconds
const unsigned long pausePeriod = 15000;   // rest for 15 seconds

/////////////Set Up/////////////////
void setup() {
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("EC Calibrating");
  delay(5000);
  lcd.clear();

  ////////////////////Pump Up//////////////////
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

  ////////////////////Pump Down////////////////
  pinMode(8, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  now = millis();
  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;

  ////////////////////////EC Probe Sketch that is avaialbe online/////////////////////////
  {
    static unsigned long timepoint = millis();
    if(millis()-timepoint>1000U)  //time interval: 1s
    {
      timepoint = millis();
      voltage = analogRead(EC_PIN)/1024.0*5000;   // read the voltage
      //temperature = readTemperature();          // read your temperature sensor to execute temperature compensation
      ecValue =  ec.readEC(voltage,temperature);  // convert voltage to EC with temperature compensation
      Serial.print("temperature:");
      Serial.print(temperature,1);
      Serial.print("^C  EC:");
      Serial.print(ecValue,2);
      Serial.println("ms/cm");
    }
    ec.calibration(voltage,temperature);          // calibration process by Serail CMD
}
  ////////////////////////Serial Print and LCD//////////////
  Serial.print("PH = ");
  Serial.println(phValue);
delay(4000);
lcd.begin(16, 2);
lcd.print("PH = ");
lcd.print(phValue);
lcd.setCursor(0, 1);
lcd.print("EC = ");
lcd.print(ecValue);
lcd.print("ms/cm");
delay(8000);
lcd.setCursor(0, 1);
lcd.print("Temperature=");
lcd.print(temperature,1);


/////////////////////////Turning on and Off pumps if PH greater than PH7.//////////////////////////////
// IDLE - the dispenser is not running
// RUN - the dispenser is on a timed run
// PAUSED - the dispenser is enjoying a time-out

switch (disState) {
  case IDLE :  // not running. should it?
    if (phValue > 7) {

      digitalWrite(A7, HIGH); // If pH is greater than 7, A7 goes high, turns on LED
      digitalWrite(in1, HIGH);
      digitalWrite(in2, LOW);

      theTimer = now;
      disState = RUN;         // now wait out the run period
    }
    else {
      digitalWrite (A7, LOW); // If pH is less than or equal to 7, A7 goes low, turns off LED
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);
    }

    break;

  case RUN :  // running. for long enough?
    if (now - theTimer >= runPeriod) {
      digitalWrite (A7, LOW);
      digitalWrite(in1, LOW);
      digitalWrite(in2, LOW);

      theTimer = now;
      disState = PAUSED;      // now wait out the pause period
    }

    break;

  case PAUSED :  // paused. for long enough?
    if (now - theTimer >= pausePeriod)
      disState = IDLE;    // back to pH control

    break;
  }
}
/////////////////////////Turning on and Off pumps if PH less than PH6.//////////////////////////////
switch (disState) {
  case IDLE :  // not running. should it?
    if (phValue > 6) {

      digitalWrite(8, HIGH); // If pH is greater than , 8 goes high, turns on LED
      digitalWrite(in3, HIGH);
      digitalWrite(in4, LOW);

      theTimer = now;
      disState = RUN;         // now wait out the run period
    }
    else {
      digitalWrite(8, LOW); // If pH is less than or equal to 8, 8 goes low, turns off LED
      digitalWrite(in3, LOW);
      digitalWrite(in4, LOW);
    }

    break;

  case RUN :  // running. for long enough?
    if (now - theTimer >= runPeriod) {
      digitalWrite(8, LOW);
      digitalWrite(in3, LOW);
      digitalWrite(in4, LOW);

      theTimer = now;
      disState = PAUSED;      // now wait out the pause period
    }

    break;

  case PAUSED :  // paused. for long enough?
    if (now - theTimer >= pausePeriod)
      disState = IDLE;    // back to pH control

    break;
  }
}```

First, if this run, pause, measure cycle is your solution to the power/sensor issue I urge you to solve it differently. There should be no need to stop and wait to get a reading.

But if that's the way you are going to go, there are several approaches.

You tried to duplicate the state machine for another pump. A half-decent stab, aside from a syntax error that places your new code in the sketch improperly. Use the IDE Autoformat tool, it might make syntax errors easier to see.

But you'd need to go a bit further and give each dispenser its own state variable… just read through the code and you will notice that your second switch/case gets all confused with state information from the first. So simply done, just duplicate all the variables, like disState1, disState2 and so forth.

Once you are cut/paste/editing code like this, we will point out that array variables would be appropriate - if you had 7 dispensers, better to have one code that can do it all than 7 nearly identical copies running around. Which is a nightmare to create and hard to fix or modify.

But for two, OK. For now. array variables goes on your list of things to look into.

Here however I wonder if you anticipate having both dispense running. I suspect not. So I would recommend beefing up the existing switch/case.

My ride to the beach is imminent. For now, look at this pseudocode and see if you can fix up my sketch for a second dispenser:

     IDLE - decide to turn on one or the other dispenser, if so start the timer and -> RUN

    RUN - is it time to stop the dispenser that is running? If so, start the timer and -> PAYSE

    PAUSE - if it is time, -> IDLE

In the RUN code, you don't even need to know which dispenser is even running - just turn them both off!

Give that a try while I work on my bottom turns and cutbacks. She who must not be kept waiting has texted, we're off to the beach.

a7

I'm not sure what the power/sensor issue you are referring too is. The code for turning the pump on and off for 20 and 15 seconds worked.

The issue was the delay I had inserted in the print LCD section, when I leave that in the timings for the on/off pump were then incorrect. I'm guessing I have to change the delay in the LCD section to millis.

I found the syntax error to be an extra bracket, I have tried to duplicate the variables but with no joy, at one stage the LED was either always on or always off

I have yet to try and understand array variables.

And generally I'm having a really bad day at it, so much so that I couldn't even get the original code to work.

This is the latest iteration.
The LED comes on and off for PH7 but I haven't tested it for less than PH6 yet, I need a lower PH solution or adjust the calibration factor.

It compiled with out issues.

I also removed the delay completely for the LCD print, I did that as a temporary measure to see if pumps(LED) are working.

///////////////PH Probe - EC Probe - LCD - PH Pump////////////////////

//////////////EC Probe///////////////////
#include "DFRobot_EC.h"
#include <EEPROM.h>
#define EC_PIN A1
float voltage, ecValue, temperature = 25;
DFRobot_EC ec;

//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//////////////L298n Dual H Bridge Driver///////////////////
/////////////Pump Up//////////////
int ena = A7;
int in1 = A6;
int in2 = A5;

/////////////Pump Down//////////////(Not Added Yet)
int enb = 8;
int in3 = 9;
int in4 = 10;

//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 22.99;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;

/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
enum {IDLE, IDLE1, RUN, RUN1, PAUSED, PAUSED1};

unsigned char disState = IDLE;
unsigned char disState1 = IDLE1;
unsigned long theTimer;
unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;    // run for 20 seconds
const unsigned long pausePeriod = 15000;  // rest for 15 seconds

/////////////Set Up/////////////////
void setup() {
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("EC Calibrating");
  delay(2000);

  ////////////////////Pump Up//////////////////
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

  ////////////////////Pump Down////////////////
  pinMode(8, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  now = millis();
  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;

  ////////////////////////EC Probe Sketch that is avaialbe online/////////////////////////
  {
    static unsigned long timepoint = millis();
    if (millis() - timepoint > 1000U)  //time interval: 1s
    {
      timepoint = millis();
      voltage = analogRead(EC_PIN) / 1024.0 * 5000;  // read the voltage
      //temperature = readTemperature();          // read your temperature sensor to execute temperature compensation
      ecValue = ec.readEC(voltage, temperature);  // convert voltage to EC with temperature compensation
      Serial.print("temperature:");
      Serial.print(temperature, 1);
      Serial.print("^C  EC:");
      Serial.print(ecValue, 2);
      Serial.println("ms/cm");
    }
    ec.calibration(voltage, temperature);  // calibration process by Serail CMD
  }
  ////////////////////////Serial Print and LCD//////////////
  Serial.print("PH = ");
  Serial.println(phValue);

  lcd.begin(16, 2);
  lcd.print("PH = ");
  lcd.print(phValue);
  lcd.setCursor(0, 1);
  lcd.print("EC = ");
  lcd.print(ecValue);
  lcd.print("ms/cm");

  lcd.setCursor(0, 1);
  lcd.print("Temperature=");
  lcd.print(temperature, 1);

  /////////////////////////Turning on and Off pumps if PH greater than PH7.//////////////////////////////
  // IDLE - the dispenser is not running
  // RUN - the dispenser is on a timed run
  // PAUSED - the dispenser is enjoying a time-out

  switch (disState) {
    case IDLE:  // not running. should it?
      if (phValue > 7) {

        digitalWrite(A7, HIGH);  // If pH is less than 7, A7 goes high, turns on LED
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = RUN;  // now wait out the run period

      } else {
        digitalWrite(A7, LOW);  // If pH is greater than or equal to 7, A7 goes low, turns off LED
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);
      }

      break;

    case RUN:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(A7, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = PAUSED;  // now wait out the pause period
      }

      break;

    case PAUSED:  // paused. for long enough?
      if (now - theTimer >= pausePeriod)
        disState = IDLE;  // back to pH control

      break;
  }
/////////////////////////Turning on and Off pumps if PH less than PH6.///////////////////////////////
switch (disState1) {
    case IDLE1:  // not running. should it?
      if (phValue > 6) {

        digitalWrite(8, HIGH);  // If pH is less than , 8 goes high, turns on LED
        digitalWrite(in3, HIGH);
        digitalWrite(in4, LOW);

        theTimer = now;
        disState1 = RUN;  // now wait out the run period

      } else {
        digitalWrite(8, LOW);  // If pH is greater than or equal to 6, 8 goes low, turns off LED
        digitalWrite(in3, LOW);
        digitalWrite(in4, LOW);
      }

      break;

    case RUN1:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(8, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState1 = PAUSED1;  // now wait out the pause period
      }

      break;

    case PAUSED1:  // paused. for long enough?
      if (now - theTimer >= pausePeriod)
        disState = IDLE;  // back to pH control

      break;
  }
}

OK, I thought maybe remembering another thread altogether that your readings were not good unless the pump has been off for some time.

So I thought that the idea of running, pausing then measuring was designed to accommodate that.

At a glance the code you just posted needs a bit of work. In exactly the way cut/paste/edit can go wrong: failure to completely follow through on the editing.

These would be very hard to find. Do not ask me how I know. In your second machine switch/case, viz:

        disState1 = RUN;  // now wait out the run NO! RUN1

//... 
 
        disState = IDLE;  // back to pH control NO! IDLE1


There may be other errors, this leapt off the page.

As for testing… no need to wait for some pH 6, just change the numbers so as to exercise the second switch/case. Who cares the wrong pump runs? TBH at this point I wouldn't even be dispensing, I'd be watching an LED proxy, and my pH would come from a potentiometer proxy for the sesnor. Like the simulation I think was part of this thread. Separating the software from the hardware development in this way makes life way easier. Divide and conquer.

If your code does not work, sprinkle Serial.print()ing about it liberally to see the values of key variables and confirm they inform the flow of your code properly.

I have never gotten something to work and been sure it is doing what I want exactly how I want it to without some confirmation of that beyond merely "it's working". Serial.print(), status LEDs, DMM, logic analyser and oscilloscope. Usually printing is enough…

HTH

a7

So I've had some success.
I can get both pumps going, I ran both codes separately and then joined them together. They will both run when greater or less than required values.

However for the second pump when it comes back within range (by plugging back in the PH probe. Probe in = 7.5, Probe out = -4.5) the LED will just stay on. I should say this only happens if the LED is already in a high state. If i plug the probe back in, the LED will revert to it's cycle of on/off.

///////////////PH Probe - EC Probe - LCD - PH Pump////////////////////

//////////////EC Probe///////////////////
#include "DFRobot_EC.h"
#include <EEPROM.h>
#define EC_PIN A1
float voltage, ecValue, temperature = 25;
DFRobot_EC ec;

//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//////////////L298n Dual H Bridge Driver///////////////////
/////////////Pump Up//////////////
int ena = A7;
int in1 = A6;
int in2 = A5;

/////////////Pump Down//////////////(Not Added Yet)
int enb = 8;
int in3 = 9;
int in4 = 10;

//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 23.5;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;

/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
enum {IDLE, IDLE1, RUN, RUN1, PAUSED, PAUSED1};

unsigned char disState = IDLE;
unsigned char disState1 = IDLE1;
unsigned long theTimer;
unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;    // run for 20 seconds
const unsigned long pausePeriod = 15000;  // rest for 15 seconds

/////////////Set Up/////////////////
void setup() {
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("EC Calibrating");
  delay(2000);

  ////////////////////Pump Up//////////////////
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

  ////////////////////Pump Down////////////////
  pinMode(8, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  now = millis();
  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;

  ////////////////////////EC Probe Sketch that is avaialbe online/////////////////////////
  {
    static unsigned long timepoint = millis();
    if (millis() - timepoint > 1000U)  //time interval: 1s
    {
      timepoint = millis();
      voltage = analogRead(EC_PIN) / 1024.0 * 5000;  // read the voltage
      //temperature = readTemperature();          // read your temperature sensor to execute temperature compensation
      ecValue = ec.readEC(voltage, temperature);  // convert voltage to EC with temperature compensation
      Serial.print("temperature:");
      Serial.print(temperature, 1);
      Serial.print("^C  EC:");
      Serial.print(ecValue, 2);
      Serial.println("ms/cm");
    }
    ec.calibration(voltage, temperature);  // calibration process by Serail CMD
  }
  ////////////////////////Serial Print and LCD//////////////
  Serial.print("PH = ");
  Serial.println(phValue);

  lcd.begin(16, 2);
  lcd.print("PH = ");
  lcd.print(phValue);
  lcd.setCursor(0, 1);
  lcd.print("EC = ");
  lcd.print(ecValue);
  lcd.print("ms/cm");

  lcd.setCursor(0, 1);
  lcd.print("Temperature=");
  lcd.print(temperature, 1);

  /////////////////////////Turning on and Off pumps if PH greater than PH7.//////////////////////////////
  // IDLE - the dispenser is not running
  // RUN - the dispenser is on a timed run
  // PAUSED - the dispenser is enjoying a time-out

  switch (disState) {
    case IDLE:  // not running. should it?
      if (phValue > 7) {

        digitalWrite(A7, HIGH);  // If pH is less than 7, A7 goes high, turns on LED
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = RUN;  // now wait out the run period

      } else {
        digitalWrite(A7, LOW);  // If pH is greater than or equal to 7, A7 goes low, turns off LED
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);
      }

      break;

    case RUN:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(A7, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = PAUSED;  // now wait out the pause period
      }

      break;

    case PAUSED:  // paused. for long enough?
      if (now - theTimer >= pausePeriod)
        disState = IDLE;  // back to pH control

      break;
  }
/////////////////////////Turning on and Off pumps if PH less than PH6.///////////////////////////////
switch (disState1) {
    case IDLE1:  // not running. should it?
      if (phValue < 6) {

        digitalWrite(8, HIGH);  // If pH is less than , 8 goes high, turns on LED
        digitalWrite(in3, HIGH);
        digitalWrite(in4, LOW);

        theTimer = now;
        disState1 = RUN1;  // now wait out the run period

      } else {
        digitalWrite(8, LOW);  // If pH is greater than or equal to 6, 8 goes low, turns off LED
        digitalWrite(in3, LOW);
        digitalWrite(in4, LOW);
      }

      break;

    case RUN1:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(8, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState1 = PAUSED1;  // now wait out the pause period
      }

      break;

    case PAUSED1:  // paused. for long enough?
      if (now - theTimer >= pausePeriod)
        disState1 = IDLE1;  // back to pH control

      break;
  }
}

OK, you have another cut/paste/edit fail.

Imma be kind and point it out. :wink:

But you'll have to start doing like we all must from time to time - go blind trying to find an error in the code we are working with...

    case RUN1:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(8, LOW);
        digitalWrite(in1, LOW);  // Bzzzt!
        digitalWrite(in2, LOW);  // Bzzzt!

        theTimer = now;
        disState1 = PAUSED1;  // now wait out the pause period
      }

That may not be the last one...

BTW I think using just theTimer is OK. Full treatment would have theTimer1, following your choice for naming up the second copy of the switch/case. It would only matter if somehow both dispensers were running. Dunno if that is possible. Rather than wonder, I'd just introduce another unsigned long variable. And maybe an alarm if somehow both pumps are running.

Did you consider the idea in my #31 above?

Also see again the wokwi linked in #9. I've added a bit to it. It doesn't what you are doing, but it shows the simulator using proxies and also has hysteresis for both conditions too hot (base) and too cold (acid).

HTH

a7

I'm not sure what I am looking at with the hint your provided. I thought the code was ok, as I ran them separately and they did what they were supposed too. Both pieces match each other except for the pins used and PH value required.

What I have done is add in a new timer1 variable and that seems to have sorted the issue out for me. I did have one in originally but it didn't seem to work, I have been looking at and trying different bits all day. This is a crash course in programming here.

For now it is doing what i want, dispensing when above or below specified value.
I want to go back and try and sort out my LCD display now without adding in delay, so that i can read PH - EC and Temperature.

I originally had PH on top row, and then EC for 8 seconds and Temp for 4 seconds, but that was using a delay which messed up the on/off on the pumps.

Just an aside to that, I have a ESP32 Node MCU, how difficult or is it possible to just pole the values so that they can be read remotely, I have seen the Blynk app being mentioned, it's a like to have but if it is going to take me to long then I won't.

I also did have a look at the post #31 but again I wasn't sure where to start, was I to start from scratch or join to the two codes together.

What I have me not be pretty but it's functional.

///////////////PH Probe - EC Probe - LCD - PH Pump////////////////////

//////////////EC Probe///////////////////
#include "DFRobot_EC.h"
#include <EEPROM.h>
#define EC_PIN A1
float voltage, ecValue, temperature = 25;
DFRobot_EC ec;

//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//////////////L298n Dual H Bridge Driver///////////////////
/////////////Pump Up//////////////
int ena = A7;
int in1 = A6;
int in2 = A5;

/////////////Pump Down//////////////(Not Added Yet)
int enb = 8;
int in3 = 9;
int in4 = 10;

//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 22;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;

/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
enum {IDLE, IDLE1, RUN, RUN1, PAUSED, PAUSED1};

unsigned char disState = IDLE;
unsigned char disState1 = IDLE1;
unsigned long theTimer;
unsigned long theTimer1;
unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;    // run for 20 seconds
const unsigned long pausePeriod = 15000;  // rest for 15 seconds

/////////////Set Up/////////////////
void setup() {
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("EC Calibrating");
  delay(2000);

  ////////////////////Pump Up//////////////////
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

  ////////////////////Pump Down////////////////
  pinMode(8, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  now = millis();
  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;

  ////////////////////////EC Probe Sketch that is avaialbe online/////////////////////////
  {
    static unsigned long timepoint = millis();
    if (millis() - timepoint > 1000U)  //time interval: 1s
    {
      timepoint = millis();
      voltage = analogRead(EC_PIN) / 1024.0 * 5000;  // read the voltage
      //temperature = readTemperature();          // read your temperature sensor to execute temperature compensation
      ecValue = ec.readEC(voltage, temperature);  // convert voltage to EC with temperature compensation
      Serial.print("temperature:");
      Serial.print(temperature, 1);
      Serial.print("^C  EC:");
      Serial.print(ecValue, 2);
      Serial.println("ms/cm");
    }
    ec.calibration(voltage, temperature);  // calibration process by Serail CMD
  }
  ////////////////////////Serial Print and LCD//////////////
  Serial.print("PH = ");
  Serial.println(phValue);

  lcd.begin(16, 2);
  lcd.print("PH = ");
  lcd.print(phValue);
  lcd.setCursor(0, 1);
  lcd.print("EC = ");
  lcd.print(ecValue);
  lcd.print("ms/cm");

  lcd.setCursor(0, 1);
  lcd.print("Temperature=");
  lcd.print(temperature, 1);

  /////////////////////////Turning on and Off pumps if PH greater than PH7.//////////////////////////////
  // IDLE - the dispenser is not running
  // RUN - the dispenser is on a timed run
  // PAUSED - the dispenser is enjoying a time-out

  switch (disState) {
    case IDLE:  // not running. should it?
      if (phValue > 7) {

        digitalWrite(A7, HIGH);  // If pH is less than 7, A7 goes high, turns on LED
        digitalWrite(in1, HIGH);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = RUN;  // now wait out the run period

      } else {
        digitalWrite(A7, LOW);  // If pH is greater than or equal to 7, A7 goes low, turns off LED
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);
      }

      break;

    case RUN:  // running. for long enough?
      if (now - theTimer >= runPeriod) {
        digitalWrite(A7, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer = now;
        disState = PAUSED;  // now wait out the pause period
      }

      break;

    case PAUSED:  // paused. for long enough?
      if (now - theTimer >= pausePeriod)
        disState = IDLE;  // back to pH control

      break;
  }
/////////////////////////Turning on and Off pumps if PH less than PH6.///////////////////////////////
switch (disState1) {
    case IDLE1:  // not running. should it?
      if (phValue < 6) {

        digitalWrite(8, HIGH);  // If pH is less than , 8 goes high, turns on LED
        digitalWrite(in3, HIGH);
        digitalWrite(in4, LOW);

        theTimer1 = now;
        disState1 = RUN1;  // now wait out the run period

      } else {
        digitalWrite(8, LOW);  // If pH is greater than or equal to 6, 8 goes low, turns off LED
        digitalWrite(in3, LOW);
        digitalWrite(in4, LOW);
      }

      break;

    case RUN1:  // running. for long enough?
      if (now - theTimer1 >= runPeriod) {
        digitalWrite(8, LOW);
        digitalWrite(in1, LOW);
        digitalWrite(in2, LOW);

        theTimer1 = now;
        disState1 = PAUSED1;  // now wait out the pause period
      }

      break;

    case PAUSED1:  // paused. for long enough?
      if (now - theTimer1 >= pausePeriod)
        disState1 = IDLE1;  // back to pH control

      break;
  }
}

The below sketch is a pump start with the analog condition for your reference. I have defined the pump state, including the pump pin, setpoint, and delay time parameter. In the sketch, only the alarm status from the analog input without a timer on/off pump.
>> Wokwi Simulator <<

#define analogPin A0

enum AlarmType : uint8_t {AIL, AIH}; // AIL: Analog Input with Low alarm, AIH: Analog Input with High alarm

typedef struct {
  uint8_t pumpPin;          // Pump output pin
  uint8_t alarmPin;         // Alarm output pin
  float setpoint;           // Alarm setpoint
  AlarmType type;           // Alarm type
  unsigned long delayTime;  // Delay time
} PumpState;

PumpState pumps[] = {
  {pumpPin: 3, alarmPin: 4, setpoint: 80.0F, type: AIH, delayTime: 2000UL},
  {pumpPin: 2, alarmPin: 5, setpoint: 20.0F, type: AIL, delayTime: 4000UL}
};
const uint8_t numberOfPumps = sizeof(pumps) / sizeof(PumpState);  // Number of pumps

float analogValue;

void setup() {
  for (uint8_t index = 0; index < numberOfPumps; index++) {
    pinMode(pumps[index].pumpPin, OUTPUT);        // Pump Pin Mode
    pinMode(pumps[index].alarmPin, OUTPUT);       // Alarm Pin Mode
  }
}

void loop() {
  int rawValue = analogRead(analogPin);           // RAW input value
  analogValue  = rawValue * 100.0F / 1023.0F;     // Analog scaling
  for (uint8_t index = 0; index < numberOfPumps; index++) {
    bool alarm = AnalogAlarm(analogValue, pumps[index].setpoint, pumps[index].type); // Alarm condition
    digitalWrite(pumps[index].alarmPin, alarm);   // Alarm Output

    bool pump = alarm;                            // <--- TODO: Pump Timer On/Off
    digitalWrite(pumps[index].pumpPin, pump);     // Pump Output
  }
}

bool AnalogAlarm(float value, float setpoint, AlarmType type) {
  switch (type) {
    case AIH:                     // Analog Input with High alarm
      return value >= setpoint;   // High alarm condition
      break;
    default:                      // Analog Input with Low alarm
      return value <= setpoint;   // Low alarm condition
      break;
  }
}

I think i have the LCD display sorted now. I will double check it tomorrow. It's way past bed time here.

///////////////PH Probe - EC Probe - LCD - PH Pump////////////////////

//////////////EC Probe///////////////////
#include "DFRobot_EC.h"
#include <EEPROM.h>
#define EC_PIN A1
float voltage, ecValue, temperature = 25;
DFRobot_EC ec;

//////////////LCD//////////////////////////////////////////
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//////////////L298n Dual H Bridge Driver///////////////////
/////////////Pump Up//////////////
int ena = A7;
int in1 = A6;
int in2 = A5;

/////////////Pump Down//////////////(Not Added Yet)
int enb = 8;
int in3 = 9;
int in4 = 10;

//////////////PH Probe - Calibrated as per instructions with offset value/////////////////////
float calibration = 22.5;  //change this value to calibrate
const int analogInPin = A0;
int sensorValue = 0;
unsigned long int avgValue;
float b;
int buf[10], temp;

/////////////Timing - PH Pump - Pause for PH check after dispense/////////////////////////////
enum { IDLE,
       IDLE1,
       RUN,
       RUN1,
       PAUSED,
       PAUSED1 };

unsigned char disState = IDLE;
unsigned char disState1 = IDLE1;

unsigned long theTimer;
unsigned long theTimer1;

unsigned long now;  // time for everybody all the time

const unsigned long runPeriod = 20000;    // run for 20 seconds
const unsigned long pausePeriod = 15000;  // rest for 15 seconds

/////////////LCD Display (millis)//////////////
unsigned long previousMillis = 0;
const long interval = 10000;
bool isFirstPage = true;

/////////////Set Up/////////////////
void setup() {
  Serial.begin(115200);
  lcd.begin(16, 2);
  lcd.print("PH Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("EC Calibrating");
  delay(2000);

  ////////////////////Pump Up//////////////////
  pinMode(A7, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);

  ////////////////////Pump Down////////////////
  pinMode(8, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);

  ////////////////////////PH Probe Sketch that is avaialbe online///////////////////////////
}
void loop() {
  unsigned long currentMillis = millis();
  now = millis();

  for (int i = 0; i < 10; i++) {
    buf[i] = analogRead(analogInPin);
    delay(30);
  }
  for (int i = 0; i < 9; i++) {
    for (int j = i + 1; j < 10; j++) {
      if (buf[i] > buf[j]) {
        temp = buf[i];
        buf[i] = buf[j];
        buf[j] = temp;
      }
    }
  }
  avgValue = 0;
  for (int i = 2; i < 8; i++)
    avgValue += buf[i];
  float pHVol = (float)avgValue * 5.0 / 1024 / 6;
  float phValue = -5.70 * pHVol + calibration;
  Serial.print("PH = ");
  Serial.println(phValue);

  ////////////////////////EC Probe Sketch that is avaialbe online/////////////////////////
  {
    static unsigned long timepoint = millis();
    if (millis() - timepoint > 1000U)  //time interval: 1s
    {
      timepoint = millis();
      voltage = analogRead(EC_PIN) / 1024.0 * 5000;  // read the voltage
      //temperature = readTemperature();          // read your temperature sensor to execute temperature compensation
      ecValue = ec.readEC(voltage, temperature);  // convert voltage to EC with temperature compensation
      Serial.print("temperature:");
      Serial.print(temperature, 1);
      Serial.print("^C  EC:");
      Serial.print(ecValue, 2);
      Serial.println("ms/cm");
    }
    ec.calibration(voltage, temperature);  // calibration process by Serail CMD
  }
  ////////////////////////Serial Print and LCD//////////////

  lcd.begin(16, 2);
  lcd.print("PH = ");
  lcd.print(phValue);
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    isFirstPage = !isFirstPage;
  }
  if(isFirstPage){
   lcd.setCursor(0, 1);
  lcd.print("EC = ");
  lcd.print(ecValue);
  lcd.print("ms/cm");
  }
  else {
  lcd.setCursor(0, 1);
    lcd.print("Temperature=");
    lcd.print(temperature, 1);
  }

    /////////////////////////Turning on and Off pumps if PH greater than PH7.//////////////////////////////
    // IDLE - the dispenser is not running
    // RUN - the dispenser is on a timed run
    // PAUSED - the dispenser is enjoying a time-out

    switch (disState) {
      case IDLE:  // not running. should it?
        if (phValue > 7) {

          digitalWrite(A7, HIGH);  // If pH is less than 7, A7 goes high, turns on LED
          digitalWrite(in1, HIGH);
          digitalWrite(in2, LOW);

          theTimer = now;
          disState = RUN;  // now wait out the run period

        } else {
          digitalWrite(A7, LOW);  // If pH is greater than or equal to 7, A7 goes low, turns off LED
          digitalWrite(in1, LOW);
          digitalWrite(in2, LOW);
        }

        break;

      case RUN:  // running. for long enough?
        if (now - theTimer >= runPeriod) {
          digitalWrite(A7, LOW);
          digitalWrite(in1, LOW);
          digitalWrite(in2, LOW);

          theTimer = now;
          disState = PAUSED;  // now wait out the pause period
        }

        break;

      case PAUSED:  // paused. for long enough?
        if (now - theTimer >= pausePeriod)
          disState = IDLE;  // back to pH control

        break;
    }
    /////////////////////////Turning on and Off pumps if PH less than PH6.///////////////////////////////
    switch (disState1) {
      case IDLE1:  // not running. should it?
        if (phValue < 6) {

          digitalWrite(8, HIGH);  // If pH is less than , 8 goes high, turns on LED
          digitalWrite(in3, HIGH);
          digitalWrite(in4, LOW);

          theTimer1 = now;
          disState1 = RUN1;  // now wait out the run period

        } else {
          digitalWrite(8, LOW);  // If pH is greater than or equal to 6, 8 goes low, turns off LED
          digitalWrite(in3, LOW);
          digitalWrite(in4, LOW);
        }

        break;

      case RUN1:  // running. for long enough?
        if (now - theTimer1 >= runPeriod) {
          digitalWrite(8, LOW);
          digitalWrite(in1, LOW);
          digitalWrite(in2, LOW);

          theTimer1 = now;
          disState1 = PAUSED1;  // now wait out the pause period
        }

        break;

      case PAUSED1:  // paused. for long enough?
        if (now - theTimer1 >= pausePeriod)
          disState1 = IDLE1;  // back to pH control

        break;
    }
  }

Thanks for reaching out, i will look at it more tomorrow, it's nearly 02.30 here and i have 3 kids to get up to very soon.