IoT liquid Auto sampler- bringing it all together

Hello All,

Over the past few months I've been developing a portable autosampler meant to collect a certain amount of liquid Before moving on to the next state as describe in the following flow chart:

I've received much assistance from this forum in creating this prototype and implementing IoT functions which allow for remote monitoring of the sampler with key details such as volume collected, battery life, temperature, and also implementing SMS alerts which would allow for the operator to know when the containing is full, when the battery level is low, and when temperature is below freezing. I have successfully implemented all this functions into a single sketch on an Arduino Nano EVERY with mixed success. I would like to now ask the community for further assistance in fixing some minor bugs. here a few of them:

  • Temperature and battery percent on LCD blink for first few seconds, then LCD data is corrupted
  • When pump is started, battery percent drops below 20% and sends battery low SMS
  • When alert SMS messages fail to send, entire sketch is blocked because of delay
  • When GPRS fails to enable to post data to dweet, entire sketch is blocked because of delay

One of the issues I see are related to the SIM module and its use of delays for its vital functions. When the sketch calls for these functions, it blocks all other functions which is not beneficial. The sampler is supposed to run without depending on SIM module functions, which is not possible at the moment. I've made an attempt to replace all the delay functions in the loop with millis() functions, but other functions such as the enabling of GPRS to post data to dweet and freeboard, as well as the SMS alerts block critical functions of the sampler. For example, if the SIM module is stuck trying to post data to dweet and cant enable GPRS, it wont allow for the start of the sampling protocol.

This problem I think perhaps I can solve with a capacitor, but when I start the sampling protocol and the pump is activated, the battery charge drops below the "LOW" threshold, triggering the low battery SMS. Going off of what the datasheet for the pump states and what I see on my meter and power supply, the pump draws 0.44 amps. I'm attaching the full sketch and also a rudimentary schematic which includes the components I'm using:


these are the topics I've created related to this project:

// For SIM7000 cellular shield
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <SoftwareSerial.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// using Pololu DRV8876 (QFN) Single Brushed DC Motor Driver Carrier
LiquidCrystal_I2C lcd(0x3F, 20, 4);

#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;
// For SIM7000 shield
#define FONA_PWRKEY 6
#define FONA_RST 7
#define FONA_TX 10 // Microcontroller RX
#define FONA_RX 11 // Microcontroller TX

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic

#define LED 13

// Using SoftwareSerial
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);
SoftwareSerial *fonaSerial = &fonaSS;

Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 10000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

//const int FLOWSWITCH = 2; // Use pin 2 to wake up the Uno/Mega
const int  buttonPin = 3;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button




const char * phone_number = "+10000000006"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;


int flowPin = 2;    //This is the input pin on the Arduino


volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.
float tempC;
float tempF;
float tLimit = 45.0;
int percent;
float calibrationFactor = 80;                    //F=79*Q
volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;

unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop
//unsigned long currentMillis;                    // millis() of the current loop.
/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
/*********************************************************************************************************************/
/*************************************    PUMP STUFF      *************************************************************/
#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW

unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 5;
const byte primeBtnPin    = 8;
const byte purgeBtnPin    = 9;

const byte DRV8876_IN1_PIN    = A0;
const byte DRV8876_IN2_PIN    = A1;


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState     = 5;
byte state;


void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}
/************************************************************************************************************************/

void setup() {
  /************************************* PUMP STUFF ********************************************************************/
  lcd.begin();                           // screen size
  lcd.setCursor(0, 2);
  lcd.print("Batt [%]:  ");
  lcd.print(percent);
  lcd.setCursor(0, 3);
  lcd.print("Temp [F]:  ");
  lcd.print(tempF);
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  currentMillis = millis();
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, RISING);
  /*************************************************************************************************************************/





  Serial.begin(115200);
  //  while (!Serial) delay(1); // Wait for serial, for debug
  Serial.println(F("**SRS AUTOSAMPLER V1**"));

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();


  //pinMode(BUTTON, INPUT); // For the interrupt wake-up to work

#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif


  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  //  Serial.print("Parasite power is: ");
  //  if (sensors.isParasitePowerMode()) Serial.println("ON");
  //  else Serial.println("OFF");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

  Serial.println();
}

void loop()
{
  currentMillis = millis();                   // Get the current value of millis().

  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;


  delay(200);                         // Small delay to debounce the button.

  ///////////////////
  //flow rate loop//
  /////////////////
  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      //((1000.0 / (millis() - currentMillis)) *

      lcd.print("Rate: ");
      lcd.setCursor(5, 0);
      lcd.print(flowRate);
      lcd.setCursor(11, 0);
      lcd.print("L/Min");
      lcd.setCursor(0, 1);
      // print total volume
      lcd.print("Vol:  ");
      lcd.print(vol);
      lcd.print(" mL");

      pulseCount = 0;
    }
    else {
      lcd.clear();
      lcd.setCursor(5, 0);
      lcd.print("Rate: ");
      lcd.print ( pulseCount); // resets flow rate to 0
      lcd.print (" L/m");
      lcd.setCursor(0, 1);
      lcd.print("Vol:");
      lcd.print(vol);
      lcd.print(" mL");
      lcd.setCursor(0, 1);
      lcd.print("Vol:");
      lcd.print(vol);
      lcd.print(" mL");
      lcd.setCursor(0, 2);
      lcd.print("Batt [%]:  ");
      lcd.print(percent);
      lcd.setCursor(0, 3);
      lcd.print("Temp [F]:  ");
      lcd.print(tempF);

      totalMilliLitres = 0;
    }
  }

  switch (state)                        // Check which state we are in.
  {

    case OffState:
      while (digitalRead(primeBtnPin) == released)             // Prime button being pressed?
        PumpForward();                                         // Turn on the pump.



      while (digitalRead(purgeBtnPin) == released)             // Purge button being pressed?
        PumpReverse();                                // Reverse the pump
      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PumpState:
      if (totalMilliLitres > 350 || currentMillis - startMillis > 10000)         /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();
      lcd.setCursor(0, 0);
      lcd.print("PUMP ");

      break;

    case PausingState:
      if (currentMillis - startMillis > 14000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      lcd.setCursor(0, 0);
      lcd.print("REST ");
      break;

    case ReverseState:
      if (currentMillis - startMillis > 22000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();
      lcd.setCursor(0, 0);
      lcd.print("PURGE");
      break;

    case RestState:
      if (currentMillis - startMillis > 32000 )  // has it been 15 minutes AND the float switch is still open?
      {
        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again
      }
      else
        pumpOff();
      lcd.setCursor(0, 0);
      lcd.print("REST ");
      break;
  }

  dtostrf(vol, 1, 2, volBuff);
  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  //  delay(500);

  //  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  dtostrf(tempF, 1, 2, tempBuff);
  count = 0;      // Reset the counter so we start counting from 0 again

  if (currentMillis - counter2 > samplingRate2) {
    Serial.println("-----------------------------------------------------------");
    Serial.print("Battery voltage: "); Serial.print(battVoltage, 3); Serial.print("  "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
    Serial.print("Battery mV: "); Serial.println(battmV);
    Serial.print("Battery level [%]: "); Serial.println(percent);
    Serial.println(flowRate);         //Print the variable flowRate to Serial
    Serial.println(vol);
    Serial.print(sensors.getResolution(insideThermometer), DEC);
    Serial.print("Temp C: ");
    Serial.print(tempC);
    Serial.print(" Temp F: ");
    Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit

    counter2 = millis();
    currentMillis = millis();
  }
  // noInterrupts(); //Disable the interrupts on the Arduino
  // Only send SMS if the switch was closed

  buttonState = digitalRead(buttonPin);


  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }


  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (buttonState == HIGH && lastButtonState == LOW || vol > 3000 && vol - flowMilliLitres <= 3000) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
  }


  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
}


// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);

}

// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  lcd.setCursor (0, 0);
  //lcd.print("Purging");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
}









// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200); // Default SIM7000 shield baud rate

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600);
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[20];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}
void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}

// Turn off the MCU completely. Can only wake up from RESET button
// However, this can be altered to wake up via a pin change interrupt
//void MCU_powerDown() {
//  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
//  ADCSRA = 0; // Turn off ADC
//  power_all_disable ();  // Power off ADC, Timer 0 and 1, serial interface
//  sleep_enable();
//  sleep_cpu();
//}

Pumps and other inductive loads will take far more than average current during startup.
So there are a few possibilities:

  • Get a better power supply
  • Wait with sending low battery message until battery is low for 30 seconds or so
  • Put pump on separate power supply (I think it already is).
  • add a big capacitor to reduce the dip

It would be useful to make a schematic where + is on top, gnd is at bottom and information goes from left to right (sensors left, controller in middle, lcd and pump on right). You can make that in software, but pen and paper will also do.
Perhaps it is better to make more than one drawing as it will not easily fit on one page.

The power supply im using simulates the power supply the device will be powered by, which is a single 12 volt LiFePO4 5AH battery, so I cant change that yet. I did add a capacitor on the motor driver for the vin and gnd, but unfortunately I fried the Nano EVERY and don't have a replacement. I do have a couple ESP32's I'm trying to use instead. the sketch loads onto the ESP32 fine and the wiring has been changed to reflect the ESP32 pins, but for some reason the main loop of the sketch wont run. I read that its because the ESP32 uses millis() differently and the delays in the loop are handled by millis(). I can only get "hello", currentMillis, and "test" to print on the serial monitor but nothing else will. Whats strange is that when I load the blink without delay example onto the ESP32, it runs fine and prints out the millis

// For SIM7000 cellular shield
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <HardwareSerial.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// using Pololu DRV8876 (QFN) Single Brushed DC Motor Driver Carrier
LiquidCrystal_I2C lcd(0x3F, 20, 4);

#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;
// For SIM7000 shield
#define FONA_PWRKEY 18
#define FONA_RST 5
#define FONA_TX 16 // ESP32 hardware serial RX2 (GPIO16)
#define FONA_RX 17 // ESP32 hardware serial TX2 (GPIO17)

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic

#define LED 13

// Using SoftwareSerial
HardwareSerial fonaSS(1);

Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 10000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

//const int FLOWSWITCH = 2; // Use pin 2 to wake up the Uno/Mega
const int  buttonPin = 33;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

unsigned long microsecs = esp_timer_get_time();



const char * phone_number = "+10000000006"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;


int flowPin = 25;    //This is the input pin on the Arduino


volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.
float tempC;
float tempF;
float tLimit = 45.0;
int percent;
float calibrationFactor = 80;                    //F=79*Q
volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;

unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop

/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
/*********************************************************************************************************************/
/*************************************    PUMP STUFF      *************************************************************/
#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW





unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 12;
const byte primeBtnPin    = 13;
const byte purgeBtnPin    = 14;

const byte DRV8876_IN1_PIN    = 26;
const byte DRV8876_IN2_PIN    = 27;


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState     = 5;
byte state;




void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}
/************************************************************************************************************************/

void setup() {
  /************************************* PUMP STUFF ********************************************************************/
  Serial.begin(115200);
  lcd.begin();                           // screen size
//  lcd.setCursor(0, 2);
//  lcd.print("Batt [%]:  ");
//  lcd.print(percent);
//  lcd.setCursor(0, 3);
//  lcd.print("Temp [F]:  ");
//  lcd.print(tempF);
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  currentMillis = millis();
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, RISING);
  /*************************************************************************************************************************/





  //  while (!Serial) delay(1); // Wait for serial, for debug
  Serial.println(F("**SRS AUTOSAMPLER V1**"));

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();


  //pinMode(BUTTON, INPUT); // For the interrupt wake-up to work

#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif


  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  //  Serial.print("Parasite power is: ");
  //  if (sensors.isParasitePowerMode()) Serial.println("ON");
  //  else Serial.println("OFF");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

  Serial.println();
}

void loop()
{
  Serial.println("hello");
  currentMillis = millis();                   // Get the current value of millis().
//  Time = millis();
  Serial.println(currentMillis);
  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;


  delay(200);                         // Small delay to debounce the button.
  Serial.println("test");
  ///////////////////
  //flow rate loop//
  /////////////////
  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      //((1000.0 / (millis() - currentMillis)) *

      lcd.print("Rate: ");
      lcd.setCursor(5, 0);
      lcd.print(flowRate);
      lcd.setCursor(11, 0);
      lcd.print("L/Min");
      lcd.setCursor(0, 1);
      // print total volume
      lcd.print("Vol:  ");
      lcd.print(vol);
      lcd.print(" mL");

      pulseCount = 0;
    }
    else {
      lcd.clear();
      lcd.setCursor(5, 0);
      lcd.print("Rate: ");
      lcd.print ( pulseCount); // resets flow rate to 0
      lcd.print (" L/m");
      lcd.setCursor(0, 1);
      lcd.print("Vol:");
      lcd.print(vol);
      lcd.print(" mL");
      lcd.setCursor(0, 1);
      lcd.print("Vol:");
      lcd.print(vol);
      lcd.print(" mL");
      lcd.setCursor(0, 2);
      lcd.print("Batt [%]:  ");
      lcd.print(percent);
      lcd.setCursor(0, 3);
      lcd.print("Temp [F]:  ");
      lcd.print(tempF);

      totalMilliLitres = 0;
    }
  }

  switch (state)                        // Check which state we are in.
  {

    case OffState:
      while (digitalRead(primeBtnPin) == released)             // Prime button being pressed?
        PumpForward();                                         // Turn on the pump.



      while (digitalRead(purgeBtnPin) == released)             // Purge button being pressed?
        PumpReverse();                                // Reverse the pump
      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PumpState:
      if (totalMilliLitres > 350 || currentMillis - startMillis > 10000)         /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();
      lcd.setCursor(0, 0);
      lcd.print("PUMP ");

      break;

    case PausingState:
      if (currentMillis - startMillis > 14000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      lcd.setCursor(0, 0);
      lcd.print("REST ");
      break;

    case ReverseState:
      if (currentMillis - startMillis > 22000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();
      lcd.setCursor(0, 0);
      lcd.print("PURGE");
      break;

    case RestState:
      if (currentMillis - startMillis > 32000 )  // has it been 15 minutes AND the float switch is still open?
      {
        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again
      }
      else
        pumpOff();
      lcd.setCursor(0, 0);
      lcd.print("REST ");
      break;
  }

  dtostrf(vol, 1, 2, volBuff);
  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  //  delay(500);

  //  Serial.print("Requesting temperatures...");
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  dtostrf(tempF, 1, 2, tempBuff);
  count = 0;      // Reset the counter so we start counting from 0 again

  if (currentMillis - counter2 > samplingRate2) {
    Serial.println("-----------------------------------------------------------");
    Serial.print("Battery voltage: "); Serial.print(battVoltage, 3); Serial.print("  "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
    Serial.print("Battery mV: "); Serial.println(battmV);
    Serial.print("Battery level [%]: "); Serial.println(percent);
    Serial.println(flowRate);         //Print the variable flowRate to Serial
    Serial.println(vol);
    Serial.print(sensors.getResolution(insideThermometer), DEC);
    Serial.print("Temp C: ");
    Serial.print(tempC);
    Serial.print(" Temp F: ");
    Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
    
    counter2 = millis();
    currentMillis = millis();
  }
  // noInterrupts(); //Disable the interrupts on the Arduino
  // Only send SMS if the switch was closed

  buttonState = digitalRead(buttonPin);


  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }


  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  if (buttonState == HIGH && lastButtonState == LOW || vol > 3000 && vol - flowMilliLitres <= 3000) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
  }


  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
  Serial.println("work");
    Serial.println(currentMillis);
  Serial.println(cloopTime);
}


// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);

}

// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  lcd.setCursor (0, 0);
  //lcd.print("Purging");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
}









// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200, SERIAL_8N1, FONA_TX, FONA_RX); // baud rate, protocol, ESP32 RX pin, ESP32 TX pin

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600, SERIAL_8N1, FONA_TX, FONA_RX); // Switch to 9600
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[20];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}
void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}

// Turn off the MCU completely. Can only wake up from RESET button
// However, this can be altered to wake up via a pin change interrupt
//void MCU_powerDown() {
//  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
//  ADCSRA = 0; // Turn off ADC
//  power_all_disable ();  // Power off ADC, Timer 0 and 1, serial interface
//  sleep_enable();
//  sleep_cpu();
//}

without looking into the details of your code I'm already sure that you did not (yet) really understand what non-blocking timing and non-blocking codeexecution means:

non-blocking code means:
there is only

one

big loop that is looping.
Do you understand? REALLY only ONE big loop

This one loop is void loop() itself.

Everything that shall happen repeatedly is done in this manner:

// your function 1
jump into function - check execution-condition - if yes execute ........../________ if no don't execute
jump out of function

// your function 2
jump into function - check execution-condition - if yes execute ........../________ if no don't execute
jump out of function

// your function 3
jump into function - check execution-condition - if yes execute ........../________ if no don't execute
jump out of function

as a raw pseudo-code

void loop() {
// your function 1
jump into function - check execution-condition - if true execute ........../________    if false don't execute 
jump out of function 

// your function 2
jump into function - check execution-condition - if true execute ........../________    if false don't execute 
jump out of function 


// your function 3
jump into function - check execution-condition - if true execute ........../________    if false don't execute 
jump out of function 

} // end of loop

As the code does quickly jump in / jump out of

all

functions
the repeated execution is secured

You might ask "but at some times some of the code shall not be executed?"
How do I make the code stop execution if execution shall not happen?

By conditional execution

This is controlled by checking the execution-condition

check execution-condition - if true execute ........../________ if false don't execute

It will take you again hours of work to re-structure your code to work this way.
For all your future developping my advice is you should this principle

Add one thing at a time. Then test the new added thing thoroughly.
Test for all and every circumstances.

You might think: "Oh man ! how long shall this endure? I want to finish my project!"
Sure.
Adding one thing at a time and then testing it thoroughly take

half the time

than it takes to cobble everything together and then start testing.
The reason is if you start testing at the end there are a lot of interactions between the different parts of the code that change again and again by modifying the code. You might "repair" bug "A" at place "Y" which causes bug "Z" instead of analysingthe bug "A" thoroughly to resolve the bug "A" in a way that impact on place "Y" and place "Z" of bug "A" does not occur

best regards Stefan

1 Like

thank you for this advice. I went through each sketch I used for the final sketch and adapted them for ESP32, they all worked and now the final sketch works! thanks for this advice. I'm still having trouble with the voltage drop though.

When I use the Pump and Purge buttons to manually operate the pump, there is no observable voltage drop. the voltage drop only happens when when its running the pumping protocol.

I added a 50V 100 uf capacitor to the motor controller VIN and gnd pins, but doesn't seem to do anything. I added an additional capacitor at VIN at R2 and GND at R1 with no change.

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 20, 4);
#include <Wire.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>

#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
/***********************************GPRS/SMS STUFF ****************************************/
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <HardwareSerial.h>
// For SIM7000 shield
#define FONA_PWRKEY 18
#define FONA_RST 5
#define FONA_TX 16 // ESP32 hardware serial RX2 (GPIO16)
#define FONA_RX 17 // ESP32 hardware serial TX2 (GPIO17)

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic
#define LED 2

HardwareSerial fonaSS(1);
Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 10000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

const int  buttonPin = 33;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


const char * phone_number = "+18000000006"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;

volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.

float tLimit = 45.0;
/**********************************************************************************************************************************/

Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 12;
const byte primeBtnPin    = 13;
const byte purgeBtnPin    = 14;

const byte DRV8876_IN1_PIN    = 26;
const byte DRV8876_IN2_PIN    = 27;

int flowPin = 25;    //This is the input pin on the Arduino
float calibrationFactor = 80;                    //F=79*Q
float tempC;
float tempF;
int percent;

/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
/*********************************************************************************************************************/


volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;
unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState     = 5;
byte state;

#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void setup() {
  Serial.begin(115200);
  lcd.begin();

  Serial.print("ESP32 PUMP");
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pinMode(flowPin, INPUT);
  digitalWrite(flowPin, HIGH);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, RISING);

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();
  /************************************************GRPS/SMS STUFF*************************************************/
#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif
  /************************************************GRPS/SMS STUFF end*********************************************/

  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

}

void loop() {
  currentMillis = millis();                   // Get the current value of millis().
  buttonState = digitalRead(buttonPin);
  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;


  delay(200);                         // Small delay to debounce the button.

  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  dtostrf(tempF, 1, 2, tempBuff);
  dtostrf(vol, 1, 2, volBuff);
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  lcd.setCursor(0, 3);
  lcd.print("B%");
  lcd.setCursor(3, 3);
  lcd.print(percent);
  lcd.setCursor(7, 3);
  lcd.print("TEMP:");
  lcd.setCursor(13, 3);
  lcd.print(tempF);
  if (percent < 100) {
    lcd.setCursor(5, 3);
    lcd.print(" ");
  }

  if (percent < 10) {
    lcd.setCursor(4, 3);
    lcd.print("  ");
  }

  Serial.println("-----------------------------------------------------------");
  Serial.print("Batt Volts: "); Serial.print(battVoltage, 3); Serial.print(" "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" "); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
  Serial.print("Battery mV: "); Serial.print(battmV); Serial.print(" "); Serial.print("Battery level [%]: "); Serial.println(percent);
  Serial.println(sensors.getResolution(insideThermometer), DEC);
  Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" "); Serial.print(" Temp F: "); Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
  Serial.println("-----------------------------------------------------------");
  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(totalMilliLitres); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(6, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(totalMilliLitres);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      pulseCount = 0;
    }
    else {
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(vol); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(6, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      flowRate = 0;

    }
  }

  switch (state)                        // Check which state we are in.
  {

    case OffState:
      while (digitalRead(primeBtnPin) == released)             // Prime button being pressed?
        PumpForward();                                         // Turn on the pump.




      while (digitalRead(purgeBtnPin) == released)             // Purge button being pressed?
        PumpReverse();                                // Reverse the pump

      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PumpState:
      if (currentMillis - startMillis > 10000)         /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();



      break;

    case PausingState:
      if (currentMillis - startMillis > 14000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      Serial.println("PAUSE!");
      lcd.setCursor(0, 0);
      lcd.print("PAUSED! ");

      break;

    case ReverseState:
      if (currentMillis - startMillis > 22000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();

      break;

    case RestState:
      if (currentMillis - startMillis > 32000 )  // has it been 15 minutes AND the float switch is still open?
      {
        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again

      }
      else
        pumpOff();
      Serial.println("REST!");
      lcd.setCursor(0, 0);
      lcd.print("RESTING!");
      break;
  }
  /***************************SMS ALERTS***************************************************/
  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
  }


  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
  /***************************SMS ALERTS END***************************************************/
  /**************************** DATA POST TO DWEET *********************************************/
  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }
}
// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  Serial.println("PURGING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
  lcd.setCursor(0, 0);
  lcd.print("PUMP OFF");
}

void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200, SERIAL_8N1, FONA_TX, FONA_RX); // baud rate, protocol, ESP32 RX pin, ESP32 TX pin

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600, SERIAL_8N1, FONA_TX, FONA_RX); // Switch to 9600
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[25];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}

Hi all,

I'm hoping someone can help me with a new issue I'm having. One of the functions of the pump is for it to be able to manually run or reverse the pump while two buttons for each respective function are help down (PRIME,PURGE). Unfortunately, since these functions are while statements, they block the code. I've tried changing them to IF statements but that makes the buttons completely inoperable. I wanted the PRIME button to not block the rest of the code, specifically the part of the code which tracks the volume, but I lost hope. Instead, I've added a new button which resets data related to flowrate and volume calculations to zero so that the sampling cycle can be ran after using the PRIME button and moved on. However, this led to me running into another issue.

There is a bool flag meant to control the sending of an SMS message indicating the collection jar is full (FULLSMS()). The circuit that activates full_sms() is also supposed to stop the sampling cycle and return to off_state once a threshold of 300mL is exceeded, or a float switch/reed switch goes high(float switch uses INPUT_PULLUP). Once it reaches the thresholds, the cycle does stop for a moment while the SMS is being sent, but once its sent it resets the button state and with it the button pin and I don't know how to alter one without altering the other.

case RestState:
      if (currentMillis - startMillis > 20000 && (digitalRead(buttonPin) == LOW))  // has it been 15 minutes AND the float switch is still open?
      {

        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again

      }
      else
        pumpOff();
      Serial.println("REST!");
      lcd.setCursor(0, 0);
      lcd.print("RESTING!");
      break;
  }
 /* If volume is over 300mL, set the buttonPin to high to stop the sampling cycle */
  if (vol > 300) {
    buttonPin == HIGH;
  }
    if (buttonPin == HIGH)
    {
    Serial.println("FLOW SWITCH: "); Serial.println("1");
    }
  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
    pumpOff();
    lcd.setCursor(0, 0);
    lcd.print("FULL          ");
  }

Please advise

LiquidCrystal_I2C lcd(0x3F, 20, 4);
#include <Wire.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>
#include <ezButton.h>
#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
/***********************************GPRS/SMS STUFF ****************************************/
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <HardwareSerial.h>
// For SIM7000 shield
#define FONA_PWRKEY 18
#define FONA_RST 5
#define FONA_TX 16 // ESP32 hardware serial RX2 (GPIO16)
#define FONA_RX 17 // ESP32 hardware serial TX2 (GPIO17)

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic
#define LED 2

HardwareSerial fonaSS(1);
Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 3000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

const int  buttonPin = 33;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


const char * phone_number = "+18326831676"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;

volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.

float tLimit = 45.0;
float setpoint = 40.0;
/**********************************************************************************************************************************/

Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 12;
const byte primeBtnPin    = 13;
const byte purgeBtnPin    = 14;
const byte resetBtnPin    = 32;
ezButton button1(12);  // create ezButton object that attach to pin 12;
ezButton button2(32);  // create ezButton object that attach to pin 12;
const byte DRV8876_IN1_PIN    = 26;
const byte DRV8876_IN2_PIN    = 27;

int flowPin = 25;    //This is the input pin on the Arduino
float calibrationFactor = 150;                    //calibration factor for sampling is 150, need to make if else statement for it
float tempC;
float tempF;
int percent;

/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
float vLimit = 30.0;
/*********************************************************************************************************************/


volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;
unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState     = 5;
byte state;

#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void setup() {
  Serial.begin(115200);
  lcd.begin();
  button1.setDebounceTime(50); // set debounce time to 50 milliseconds
  button2.setDebounceTime(50); // set debounce time to 50 milliseconds
  Serial.println("ESP32 PUMP");
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);
  pinMode(resetBtnPin , INPUT_PULLUP);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pinMode(flowPin, INPUT_PULLUP);
  digitalWrite(flowPin, LOW);

  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, FALLING);

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();
  /************************************************GRPS/SMS STUFF*************************************************/
#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif
  /************************************************GRPS/SMS STUFF end*********************************************/

  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

}

void loop() {
  button1.loop(); // MUST call the loop() function first
  button2.loop(); // MUST call the loop() function first
  int btn1State = button1.getState();
  int btn2State = button2.getState();


  currentMillis = millis();                   // Get the current value of millis().

  buttonState = digitalRead(buttonPin);

  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;





  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  dtostrf(tempF, 1, 2, tempBuff);
  dtostrf(vol, 1, 2, volBuff);
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  lcd.setCursor(0, 3);
  lcd.print("B%");
  lcd.setCursor(3, 3);
  lcd.print(percent);
  lcd.setCursor(7, 3);
  lcd.print("TEMP:");
  lcd.setCursor(13, 3);
  lcd.print(tempF);

 /********* removes the the zero on LCD after battery charge falls into double digits ******/

  if (percent < 100) {
    lcd.setCursor(5, 3);
    lcd.print(" ");
  }
  /****************************************************************************************/

/********* removes the the zero on LCD after battery charge falls into single digits ******/
  if (percent < 10) {
    lcd.setCursor(4, 3);
    lcd.print("  ");
  }
 /****************************************************************************************/

//  if ((digitalRead(buttonState) == LOW) || (vol > 300 )) {
//    //lcd.clear();
//    lcd.setCursor(0, 0);
//    lcd.print("FULL          ");
//    //delay(10000);
//    return;
//  } else {
//
//  }




 /******************data should only be read into serial monitor every 3 seconds ************************************/

  if (currentMillis - counter2 > samplingRate2) {
    Serial.println("-----------------------------------------------------------");
    Serial.print("Batt Volts: "); Serial.print(battVoltage, 3); Serial.print(" "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" "); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
    Serial.print("Battery mV: "); Serial.print(battmV); Serial.print(" "); Serial.print("Battery level [%]: "); Serial.println(percent);
    Serial.println(sensors.getResolution(insideThermometer), DEC);
    Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" "); Serial.print(" Temp F: "); Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
    Serial.print("FLOW ML: "); Serial.println(flowMilliLitres);

    Serial.println("-----------------------------------------------------------");

    counter2 = millis();
    currentMillis = millis();
  }
 /*****************************************************************************************************************/

/**************************** Flow meter stuff *******************************************************************/
  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(totalMilliLitres); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(5, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      pulseCount = 0;
    }
    else {
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(vol); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(6, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      flowRate = 0;

    }
  }
 /******************************************************************************************************************/

 /******************Sampling cycle starts below, within state change are the prime, purge, and reset buttons as WHILE************************************/

  switch (state)                        // Check which state we are in.
  {

    case OffState:
      while (digitalRead(primeBtnPin) == released)             // Prime button being pressed?
        PumpForward();                                         // Turn on the pump.





      while (digitalRead(purgeBtnPin) == released)             // Purge button being pressed?
        PumpReverse();                                // Reverse the pump

      while (digitalRead(resetBtnPin) == released)
        resetData();

      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PumpState:
      if (totalMilliLitres > vLimit || currentMillis - startMillis > 6000)        /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();



      break;

    case PausingState:
      if (currentMillis - startMillis > 9000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      Serial.println("PAUSE!");
      lcd.setCursor(0, 0);
      lcd.print("PAUSED! ");

      break;

    case ReverseState:
      if (currentMillis - startMillis > 16000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();

      break;

    case RestState:
      if (currentMillis - startMillis > 20000 && (digitalRead(buttonPin) == LOW))  // has it been 15 minutes AND the float switch is still open?
      {

        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again

      }
      else
        pumpOff();
      Serial.println("REST!");
      lcd.setCursor(0, 0);
      lcd.print("RESTING!");
      break;
  }
  /******************************************************************************************************************************************/

  /***************************SMS ALERTS***************************************************/
  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
    pumpOff();
    lcd.setCursor(0, 0);
    lcd.print("FULL          ");
  }
  /* If volume is over 300mL, set the buttonPin to high to stop the sampling cycle */
  if (vol > 300) {
    buttonPin == HIGH;
  }
    if (buttonPin == HIGH)
    {
    Serial.println("FLOW SWITCH: "); Serial.println("1");
    }
  /********************************************************************************/

  /***** the trigger for the COLD SMS ********************************************/
  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
   /********************************************************************************/

  /****************************Trigger for low battery SMS*********************************/

  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
  
  /***************************SMS ALERTS END***************************************************/
  /**************************** DATA POST TO DWEET *********************************************/
  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }
}

// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void resetData() {
  currentMillis = millis();
  startMillis = currentMillis;
  cloopTime = currentMillis;
  totalMilliLitres = 0;
  flowRate = 0;
  vol = 0;
  flowMilliLitres = 0;
  Serial.println("Data Reset");
  lcd.setCursor(0, 2);
  lcd.print(vol);
  lcd.setCursor(1,2);
  lcd.print("    ");
  pulseCount = 0;
}
// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  Serial.println("PURGING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
  lcd.setCursor(0, 0);
  lcd.print("PUMP OFF");
}

void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200, SERIAL_8N1, FONA_TX, FONA_RX); // baud rate, protocol, ESP32 RX pin, ESP32 TX pin

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600, SERIAL_8N1, FONA_TX, FONA_RX); // Switch to 9600
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[25];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}

the first and very importat advise is to understand that

all looping

must be done by one single loop which is void loop() ITSELF

all and if I write ALL then I mean ALL EACH AND EVERY other functions must work in a quickly jump in / jump out manner

using zero while-loops, using zero-for-loops
If I write zero I mean ZERO.

This means your code will be able to be responsive to everything all the time.
If a reaction is really done then depends on the coded if-conditions.

You are already using a state-machine but your while-loops inside the states dis-able the resposniveness.

the basic principle is instead of using while-loops like

    case OffState:
      while (digitalRead(primeBtnPin) == released)             // Prime button being pressed?
        PumpForward();                                         // Turn on the pump.

you have a second state-machine for pump forward / backward inside the function manualMode()

    case OffState:
      manualMode(); // on pressing button forward / reverse switch pump to forward / reverse if no button is pressed switch pump off
    if (condition_to_leave_OffState == true) { 
      state = PumpState; // or whatever state you want 
    }  

As I don't know the exact conditions that must be fullfilled to leave the OffState I named the condition "condition_to_leave_OffState"

best regards Stefan

I soldered a pair of ceramic 0.1uF on the negative and positive terminals of the DC motor to the DC motor body, instead of the ADS1115 and the DC motor sharing common power terminals, I isolated the power and ground wires going into the motor driver and placed a 25V 100uF capacitor between the power and ground wires, placed another 25V 100uF between the power and ground rails on the breadboard from which the ADS1115 gets A0, placed a 16V 100uF capacitor the voltage divider and A0 with the negative side of the capacitor going to ground, placed a 25V 100uF capacitor between power input and R1 with the negative side of the capacitor going to ground, and twisted the power and ground wires going from the motor driver to the DC motor, voltage drop is virtually gone!

Which is why even professional programmers sketch their requirements on paper in a diagram-view. Multiple ways to approach this logical thinking process:
State diagram - Wikipedia

Thinking before coding really is a time saving methodology.

and this means you program-logic is working now?

Not really no, I tried following your guidance but I definitely got something wrong.

I added two states:

 const byte PrimeState   = 6;
const byte PurgeState   = 7;

and created two bool variables

bool primeBtnPinHasBeenPressed = false; 
bool purgeBtnPinHasBeenPressed = false;

Then in void loop() I did

 if (digitalRead(primeBtnPin) == released) primeBtnPinHasBeenPressed = true;  
if (digitalRead(purgeBtnPin) == released) purgeBtnPinHasBeenPressed = true;)

and then in the state machine I did

  switch (state)                        // Check which state we are in.
  {

    case OffState:

      if(primeBtnPinHasBeenPressed = true) {
        state = PrimeState;
      }
      if(purgeBtnPinHasBeenPressed = true) {
        state = PurgeState;
      }

      while (digitalRead(resetBtnPin) == released)
        resetData();
      




      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PrimeState:
      if (digitalRead(primeBtnPin) == pressed)
        state = OffState;
        else
          manualMode();
          break;

    case PurgeState:
      if (digitalRead(purgeBtnPin) == pressed)
        state = OffState;
        else
          manualPurge();
          break;

and included two new functions

void manualMode() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void manualPurge() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}

But instead of adding both new states at the same time, first I added primestate, which worked great, although the button was a little sticky after releasing it. Then I included both primestate and purgestate, but this time only purgestate worked.

I tried a separate method where instead of making new states, I just created if statements for the two buttons and it was still buggy. When I pressed and held the Prime button, the motor would go forward until I released the button, working as advertised. But when I pressed and held the purgebutton, the motor would reverse, but it would run for a second then turn off for maybe a quarter second and turn on again for a second and turn off for a quarter second on and on until I released the button.

I'm including both versions of the script, the one with the state machine change at the top and the if statements at the bottom:

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 20, 4);
#include <Wire.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>
#include <ezButton.h>
#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
/***********************************GPRS/SMS STUFF ****************************************/
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <HardwareSerial.h>
// For SIM7000 shield
#define FONA_PWRKEY 18
#define FONA_RST 5
#define FONA_TX 16 // ESP32 hardware serial RX2 (GPIO16)
#define FONA_RX 17 // ESP32 hardware serial TX2 (GPIO17)

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic
#define LED 2

HardwareSerial fonaSS(1);
Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 3000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

const int  buttonPin = 33;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


const char * phone_number = "+10000000006"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;

volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.

float tLimit = 45.0;
float setpoint = 40.0;
/**********************************************************************************************************************************/

Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 12;
const byte primeBtnPin    = 13;
const byte purgeBtnPin    = 14;
const byte resetBtnPin    = 32;
ezButton button1(12);  // create ezButton object that attach to pin 12;
ezButton button2(32);  // create ezButton object that attach to pin 12;
const byte DRV8876_IN1_PIN    = 26;
const byte DRV8876_IN2_PIN    = 27;

int flowPin = 25;    //This is the input pin on the Arduino
float calibrationFactor = 150;                    //calibration factor for sampling is 150, need to make if else statement for it
float tempC;
float tempF;
int percent;

/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
float vLimit = 30.0;
/*********************************************************************************************************************/


volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;
unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState    = 5;
const byte PrimeState   = 6;
const byte PurgeState   = 7;
byte state;

#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW

 bool primeBtnPinHasBeenPressed = false;
  bool purgeBtnPinHasBeenPressed = false;

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void setup() {
  Serial.begin(115200);
  lcd.begin();
  button1.setDebounceTime(50); // set debounce time to 50 milliseconds
  button2.setDebounceTime(50); // set debounce time to 50 milliseconds
  Serial.println("ESP32 PUMP");
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);
  pinMode(resetBtnPin , INPUT_PULLUP);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pinMode(flowPin, INPUT_PULLUP);
  digitalWrite(flowPin, LOW);



  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, FALLING);

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();
  /************************************************GRPS/SMS STUFF*************************************************/
#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif
  /************************************************GRPS/SMS STUFF end*********************************************/

  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

}

void loop() {
  button1.loop(); // MUST call the loop() function first
  button2.loop(); // MUST call the loop() function first
  int btn1State = button1.getState();
  int btn2State = button2.getState();
  if (digitalRead(primeBtnPin) == released) primeBtnPinHasBeenPressed = true;
    if (digitalRead(purgeBtnPin) == released) purgeBtnPinHasBeenPressed = true;

  currentMillis = millis();                   // Get the current value of millis().

  buttonState = digitalRead(buttonPin);

  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;






  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  dtostrf(tempF, 1, 2, tempBuff);
  dtostrf(vol, 1, 2, volBuff);
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  lcd.setCursor(0, 3);
  lcd.print("B%");
  lcd.setCursor(3, 3);
  lcd.print(percent);
  lcd.setCursor(7, 3);
  lcd.print("TEMP:");
  lcd.setCursor(13, 3);
  lcd.print(tempF);
  if (percent < 100) {
    lcd.setCursor(5, 3);
    lcd.print(" ");
  }

  if (percent < 10) {
    lcd.setCursor(4, 3);
    lcd.print("  ");
  }
  //  if ((digitalRead(buttonState) == LOW) || (vol > 300 )) {
  //    //lcd.clear();
  //    lcd.setCursor(0, 0);
  //    lcd.print("FULL          ");
  //    //delay(10000);
  //    return;
  //  } else {
  //
  //  }





  if (currentMillis - counter2 > samplingRate2) {
    Serial.println("-----------------------------------------------------------");
    Serial.print("Batt Volts: "); Serial.print(battVoltage, 3); Serial.print(" "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" "); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
    Serial.print("Battery mV: "); Serial.print(battmV); Serial.print(" "); Serial.print("Battery level [%]: "); Serial.println(percent);
    Serial.println(sensors.getResolution(insideThermometer), DEC);
    Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" "); Serial.print(" Temp F: "); Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
    Serial.print("FLOW ML: "); Serial.println(flowMilliLitres);

    Serial.println("-----------------------------------------------------------");

    counter2 = millis();
    currentMillis = millis();
  }

  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(totalMilliLitres); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(5, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      pulseCount = 0;
    }
    else {
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(vol); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(6, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      flowRate = 0;

    }
  }


  switch (state)                        // Check which state we are in.
  {

    case OffState:

      if(primeBtnPinHasBeenPressed = true) {
        state = PrimeState;
      }
      if(purgeBtnPinHasBeenPressed = true) {
        state = PurgeState;
      }

      while (digitalRead(resetBtnPin) == released)
        resetData();
      




      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

    case PrimeState:
      if (digitalRead(primeBtnPin) == pressed)
        state = OffState;
        else
          manualMode();
          break;

    case PurgeState:
      if (digitalRead(purgeBtnPin) == pressed)
        state = OffState;
        else
          manualPurge();
          break;
      
    case PumpState:
      if (totalMilliLitres > vLimit || currentMillis - startMillis > 6000)        /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();



      break;

    case PausingState:
      if (currentMillis - startMillis > 9000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      Serial.println("PAUSE!");
      lcd.setCursor(0, 0);
      lcd.print("PAUSED! ");

      break;

    case ReverseState:
      if (currentMillis - startMillis > 16000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();

      break;

    case RestState:
      if (currentMillis - startMillis > 20000 && (digitalRead(buttonPin) == LOW))  // has it been 15 minutes AND the float switch is still open?
      {

        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again

      }
      else
        pumpOff();
      Serial.println("REST!");
      lcd.setCursor(0, 0);
      lcd.print("RESTING!");
      break;
  }
  /***************************SMS ALERTS***************************************************/
  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
    pumpOff();
    lcd.setCursor(0, 0);
    lcd.print("FULL          ");
  }
  if (vol > 300) {
    buttonPin == HIGH;
  }
  if (buttonPin == HIGH)
  {
    Serial.print("FLOW SWITCH: "); Serial.println("1");
  }

  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
  /***************************SMS ALERTS END***************************************************/
  /**************************** DATA POST TO DWEET *********************************************/
  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }
}
void manualMode() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void manualPurge() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}



// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void resetData() {
  currentMillis = millis();
  startMillis = currentMillis;
  cloopTime = currentMillis;
  totalMilliLitres = 0;
  flowRate = 0;
  vol = 0;
  flowMilliLitres = 0;
  Serial.println("Data Reset");
  lcd.setCursor(0, 2);
  lcd.print(vol);
  lcd.setCursor(1, 2);
  lcd.print("    ");
  pulseCount = 0;
}
// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  Serial.println("PURGING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
  lcd.setCursor(0, 0);
  lcd.print("PUMP OFF");
}

void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200, SERIAL_8N1, FONA_TX, FONA_RX); // baud rate, protocol, ESP32 RX pin, ESP32 TX pin

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600, SERIAL_8N1, FONA_TX, FONA_RX); // Switch to 9600
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[25];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 20, 4);
#include <Wire.h>
#include <OneWireNg.h>
#include <DallasTemperature.h>
#include <ezButton.h>
#include <Adafruit_ADS1X15.h>
#include <MultiMap.h>
#define ONE_WIRE_BUS 4
/***********************************GPRS/SMS STUFF ****************************************/
#include "Adafruit_FONA.h" // https://github.com/botletics/SIM7000-LTE-Shield/tree/master/Code
#include <HardwareSerial.h>
// For SIM7000 shield
#define FONA_PWRKEY 18
#define FONA_RST 5
#define FONA_TX 16 // ESP32 hardware serial RX2 (GPIO16)
#define FONA_RX 17 // ESP32 hardware serial TX2 (GPIO17)

#define SIMCOM_7000
#define PROTOCOL_HTTP_GET         // Generic
#define LED 2

HardwareSerial fonaSS(1);
Adafruit_FONA_LTE fona = Adafruit_FONA_LTE();

#define samplingRate 60000UL // The time in between posts, in millis
#define samplingRate2 3000UL // the time between sensor readouts
uint8_t readline(char *buff, uint8_t maxbuff, uint16_t timeout = 0);
char imei[16] = {0}; // Use this for device ID
char replybuffer[255]; // Large buffer for replies
uint8_t type;
unsigned long counter = 0;
unsigned long counter2 = 0;
bool bat = false;
bool opened = false;
bool cold = false;

char volBuff[12];
char tempBuff[12];
char batBuff[12];

char URL[300];  // Make sure this is long enough for your request URL
char body[200]; // Only need this is you're doing an HTTP POST request

const int  buttonPin = 33;
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


const char * phone_number = "+10000000006"; // Include country code, only numbe

const char * text_message1 = " mL FULL!"; // Change to suit your needs
const char * text_messageV = volBuff;

const char * text_message3 = " % BATTERY LOW!";
const char * text_messageB = batBuff;

const char * text_message2 = "*F FREEZING!"; // Change to suit your needs
const char * text_messageF = tempBuff;

volatile int count; //This integer needs to be set as volatile to ensure it updates correctly during the interrupt process.

float tLimit = 45.0;
float setpoint = 40.0;
/**********************************************************************************************************************************/

Adafruit_ADS1115 ads;
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;

unsigned long startMillis;                      // Keeps track of millis() when the pumping cycle starts.
unsigned long currentMillis;                    // millis() of the current loop.

// constants for Arduino IO-Pins with SELF-explaining names
const byte samplingBtnPin = 12;
const byte primeBtnPin    = 13;
const byte purgeBtnPin    = 14;
const byte resetBtnPin    = 32;
ezButton button1(12);  // create ezButton object that attach to pin 12;
ezButton button2(32);  // create ezButton object that attach to pin 12;
const byte DRV8876_IN1_PIN    = 26;
const byte DRV8876_IN2_PIN    = 27;

int flowPin = 25;    //This is the input pin on the Arduino
float calibrationFactor = 150;                    //calibration factor for sampling is 150, need to make if else statement for it
float tempC;
float tempF;
int percent;

/**************************************************** Battery stuf **************************************************/
int R1 = 27, R2 = 10; // 10k and 27k
float dividerRatio; // pre-calculate in setup
float calFactor = 1.01; // calibrate  if needed
int in[] = {
  10000, 12000, 12500, 12800, 12900, 13000, 13100, 13200, 13300, 13400, 13600, 14600, 14900
};                                                                                         // [13] IN holds the mV inputs
int out [] = {
  0, 9, 14, 17, 20, 30, 40, 70, 90, 99, 100, 110, 120
};                                                                                       // [13] OUT holds the percentage outputs
float bLimit = 20;
int Val;
float vLimit = 30.0;
/*********************************************************************************************************************/


volatile byte pulseCount;                       // counts the pulses of flow sensor
float flowRate;
unsigned int flowMilliLitres;                  //totaltotalMilliLitres used for the interrupts
unsigned long totalMilliLitres;
unsigned long oldTime;
unsigned long cloopTime;                      // to count pulses per second
unsigned long vol;                           //vol tracks total volume throughout loop


// constants for the states with SELF-explaining names
const byte OffState     = 0;
const byte PumpState    = 1;
const byte PausingState = 2;
const byte ReverseState = 3;
const byte RestState    = 4;
const byte FullState     = 5;
const byte PrimeState = 6;
byte state;

#define pressed HIGH
#define released LOW
#define closed HIGH
#define open LOW

// bool primeBtnPinHasBeenPressed = true;
//  bool purgeBtnPinHasBeenPressed = false;

void pulseCounter()
{
  // Increment the pulse counter
  pulseCount++;
}

void setup() {
  Serial.begin(115200);
  lcd.begin();
  button1.setDebounceTime(50); // set debounce time to 50 milliseconds
  button2.setDebounceTime(50); // set debounce time to 50 milliseconds
  Serial.println("ESP32 PUMP");
  pinMode(primeBtnPin, INPUT_PULLUP);    // pinMode(5, INPUT);
  pinMode(purgeBtnPin, INPUT_PULLUP);    // pinMode(6, INPUT);
  pinMode(samplingBtnPin, INPUT_PULLUP); // pinMode(7, INPUT);
  pinMode(resetBtnPin , INPUT_PULLUP);

  pinMode(DRV8876_IN1_PIN, OUTPUT); // pinMode(A0, OUTPUT);
  pinMode(DRV8876_IN2_PIN, OUTPUT); // pinMode(A1, OUTPUT);

  pinMode(flowPin, INPUT_PULLUP);
  digitalWrite(flowPin, LOW);



  pulseCount        = 0;
  flowRate          = 0.0;
  flowMilliLitres   = 0.0;
  totalMilliLitres  = 0;
  vol               = 0;
  cloopTime = currentMillis;
  attachInterrupt(digitalPinToInterrupt(flowPin), pulseCounter, FALLING);

  dividerRatio = (R1 + R2) / R1;
  ads.setGain(GAIN_ONE);
  ads.begin();
  /************************************************GRPS/SMS STUFF*************************************************/
#ifdef LED
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);
#endif

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state

  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setFunctionality(1); // AT+CFUN=1
  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately

  // Perform first-time GPS/GPRS setup if the shield is going to remain on,
  // otherwise these won't be enabled in loop() and it won't work!
#ifndef turnOffShield

#if !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif
#endif
  /************************************************GRPS/SMS STUFF end*********************************************/

  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  if (!sensors.getAddress(insideThermometer, 0)) Serial.println("Unable to find address for Device 0");

  //if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();

  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(insideThermometer, 9);

  Serial.print("Device 0 Resolution: ");

}

void loop() {
  button1.loop(); // MUST call the loop() function first
  button2.loop(); // MUST call the loop() function first
  int btn1State = button1.getState();
  int btn2State = button2.getState();
//  if (digitalRead(primeBtnPin) == released) primeBtnPinHasBeenPressed = false;
  //  if (digitalRead(purgeBtnPin) == released) purgeBtnPinHasBeenPressed = true;

  currentMillis = millis();                   // Get the current value of millis().

  buttonState = digitalRead(buttonPin);

  if (digitalRead(samplingBtnPin) == released)
    if (state > OffState)                              // Are we already in a pumping cycle?
      state = OffState;
    else
      state = PumpState;




  //  delay(200);                         // Small delay to debounce the button.

  int adc0 = ads.readADC_SingleEnded(0);
  float battVoltage = ads.computeVolts(adc0) * dividerRatio * calFactor;
  float battVolt = battVoltage * 3.7;
  int battmV = (battVolt * 1000) + 30;
  Val = battmV;
  //  int percent;
  percent =  multiMap(Val, in, out, 13);
  //  delay(1000);
  dtostrf(percent, 1, 2, batBuff);
  dtostrf(tempF, 1, 2, tempBuff);
  dtostrf(vol, 1, 2, volBuff);
  sensors.requestTemperatures(); // Send the command to get temperatures
  //  Serial.println("DONE");
  printTemperature(insideThermometer); // Use a simple function to print out the data
  tempC = sensors.getTempCByIndex(0);
  tempF = (tempC * 1.8) + 32;
  lcd.setCursor(0, 3);
  lcd.print("B%");
  lcd.setCursor(3, 3);
  lcd.print(percent);
  lcd.setCursor(7, 3);
  lcd.print("TEMP:");
  lcd.setCursor(13, 3);
  lcd.print(tempF);
  if (percent < 100) {
    lcd.setCursor(5, 3);
    lcd.print(" ");
  }

  if (percent < 10) {
    lcd.setCursor(4, 3);
    lcd.print("  ");
  }
  //  if ((digitalRead(buttonState) == LOW) || (vol > 300 )) {
  //    //lcd.clear();
  //    lcd.setCursor(0, 0);
  //    lcd.print("FULL          ");
  //    //delay(10000);
  //    return;
  //  } else {
  //
  //  }





  if (currentMillis - counter2 > samplingRate2) {
    Serial.println("-----------------------------------------------------------");
    Serial.print("Batt Volts: "); Serial.print(battVoltage, 3); Serial.print(" "); Serial.print("AIN0: "); Serial.print(adc0 ); Serial.print(" "); Serial.print(" BatVolt: "); Serial.println(battVolt, 3);
    Serial.print("Battery mV: "); Serial.print(battmV); Serial.print(" "); Serial.print("Battery level [%]: "); Serial.println(percent);
    Serial.println(sensors.getResolution(insideThermometer), DEC);
    Serial.print("Temp C: "); Serial.print(tempC); Serial.print(" "); Serial.print(" Temp F: "); Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
    Serial.print("FLOW ML: "); Serial.println(flowMilliLitres);

    Serial.println("-----------------------------------------------------------");

    counter2 = millis();
    currentMillis = millis();
  }

  if (currentMillis >= (cloopTime + 1000))
  {
    cloopTime = currentMillis;
    if (pulseCount != 0) {
      flowRate =  (pulseCount / calibrationFactor);
      //oldTime = millis();
      flowMilliLitres = (flowRate / 60) * 1000;
      totalMilliLitres += flowMilliLitres;
      vol              += flowMilliLitres;
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(totalMilliLitres); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(5, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      pulseCount = 0;
    }
    else {
      Serial.print(flowRate); Serial.println(" :L/min");
      Serial.print(vol); Serial.println(" :mL");
      lcd.setCursor(0, 1);
      lcd.print(flowRate);
      lcd.setCursor(6, 1);
      lcd.print(":L/min");
      lcd.setCursor(0, 2);
      lcd.print(vol);
      lcd.setCursor(6, 2);
      lcd.print(": mL");
      flowRate = 0;

    }
  }


  switch (state)                        // Check which state we are in.
  {

    case OffState:

      //    manualMode();
      if (digitalRead(primeBtnPin) == pressed) {

        pumpOff();
      }
      else {
        manualMode();
        break;
      }
      if (digitalRead(purgeBtnPin) == pressed) {

        pumpOff();
      }
      else {
        manualPurge();
        break;
      }

      while (digitalRead(resetBtnPin) == released)
        resetData();
      




      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;


    case PumpState:
      if (totalMilliLitres > vLimit || currentMillis - startMillis > 6000)        /// did we collect 35mL? if not, go to next stage after 45000 millis
        state = PausingState;
      else

        PumpForward();



      break;

    case PausingState:
      if (currentMillis - startMillis > 9000)   // Have we been in this state too long?
        state = ReverseState;
      else
        pumpOff();
      Serial.println("PAUSE!");
      lcd.setCursor(0, 0);
      lcd.print("PAUSED! ");

      break;

    case ReverseState:
      if (currentMillis - startMillis > 16000) // Have we been in this state too long?
        state = RestState;//state++;
      else
        PumpReverse();

      break;

    case RestState:
      if (currentMillis - startMillis > 20000 && (digitalRead(buttonPin) == LOW))  // has it been 15 minutes AND the float switch is still open?
      {

        state = PumpState;
        startMillis = currentMillis;            // Reset the start time of the pumping sequence.
        totalMilliLitres = 0;                  //reset totalMilliLitres so it can start counting up from 0 again

      }
      else
        pumpOff();
      Serial.println("REST!");
      lcd.setCursor(0, 0);
      lcd.print("RESTING!");
      break;
  }
  /***************************SMS ALERTS***************************************************/
  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
    pumpOff();
    lcd.setCursor(0, 0);
    lcd.print("FULL          ");
  }
  if (vol > 300) {
    buttonPin == HIGH;
  }
  if (buttonPin == HIGH)
  {
    Serial.print("FLOW SWITCH: "); Serial.println("1");
  }

  if (tempF < tLimit && cold == false) {
    ColdSMS ();
    cold = true;
  }
  else if (tempF > tLimit + 2.0 && cold == true) {
    cold = false;
  }
  if (percent < bLimit && bat == false) {
    batSMS ();
    bat = true;
  }
  /***************************SMS ALERTS END***************************************************/
  /**************************** DATA POST TO DWEET *********************************************/
  if (millis() - counter > samplingRate) {
    PostData ();
    counter = millis ();
  }
}
void manualMode() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void manualPurge() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}



// senseful SUB-unit of code with a SELF-explaining name
void PumpForward() {
  digitalWrite(DRV8876_IN1_PIN, HIGH);  // comment is obsolete code epxlains ITSELF Turn on the pump (forward).
  digitalWrite(DRV8876_IN2_PIN, LOW);
  //  Serial.println("PUMPING!");
  lcd.setCursor(0, 0);
  lcd.print("PUMPING!");
}

void resetData() {
  currentMillis = millis();
  startMillis = currentMillis;
  cloopTime = currentMillis;
  totalMilliLitres = 0;
  flowRate = 0;
  vol = 0;
  flowMilliLitres = 0;
  Serial.println("Data Reset");
  lcd.setCursor(0, 2);
  lcd.print(vol);
  lcd.setCursor(1, 2);
  lcd.print("    ");
  pulseCount = 0;
}
// senseful SUB-unit of code with a SELF-explaining name
void PumpReverse() {
  digitalWrite(DRV8876_IN1_PIN, LOW);  // comment is obsolete code epxlains ITSELF Reverse the pump.
  digitalWrite(DRV8876_IN2_PIN, HIGH);
  Serial.println("PURGING!");
  lcd.setCursor(0, 0);
  lcd.print("PURGING!");
}


// senseful SUB-unit of code with a SELF-explaining name
void pumpOff()
{
  digitalWrite(DRV8876_IN1_PIN, LOW);
  digitalWrite(DRV8876_IN2_PIN, LOW);
  lcd.setCursor(0, 0);
  lcd.print("PUMP OFF");
}

void printTemperature(DeviceAddress deviceAddress)
{
  // method 1 - slower
  //Serial.print("Temp C: ");
  //Serial.print(sensors.getTempC(deviceAddress));
  //Serial.print(" Temp F: ");
  //Serial.print(sensors.getTempF(deviceAddress)); // Makes a second call to getTempC and then converts to Fahrenheit

  // method 2 - faster
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return;
  }

}

// function to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}

// Power on/off the module
void powerOn(bool state) {
  if (state) {
    Serial.println(F("Turning on SIM7000..."));
    digitalWrite(FONA_PWRKEY, LOW);
    delay(100); // Turn on module
    digitalWrite(FONA_PWRKEY, HIGH);
    delay(4500); // Give enough time for the module to boot up before communicating with it
  }
  else {
    Serial.println(F("Turning off SIM7000..."));
    fona.powerDown(); // Turn off module
  }
}

void moduleSetup() {
  // SIM7000 takes about 3s to turn on and SIM7500 takes about 15s
  // Press Arduino reset button if the module is still turning on and the board doesn't find it.
  // When the module is on it should communicate right after pressing reset

  // Software serial:
  fonaSS.begin(115200, SERIAL_8N1, FONA_TX, FONA_RX); // baud rate, protocol, ESP32 RX pin, ESP32 TX pin

  Serial.println(F("Configuring to 9600 baud"));
  fonaSS.println("AT+IPR=9600"); // Set baud rate
  delay(100); // Short pause to let the command run
  fonaSS.begin(9600, SERIAL_8N1, FONA_TX, FONA_RX); // Switch to 9600
  if (! fona.begin(fonaSS)) {
    Serial.println(F("Couldn't find FONA"));
    while (1); // Don't proceed if it couldn't find the device
  }

  // Hardware serial:
  /*
    fonaSerial->begin(115200); // Default SIM7000 baud rate

    if (! fona.begin(*fonaSerial)) {
    DEBUG_PRINTLN(F("Couldn't find SIM7000"));
    }
  */

  // The commented block of code below is an alternative that will find the module at 115200
  // Then switch it to 9600 without having to wait for the module to turn on and manually
  // press the reset button in order to establish communication. However, once the baud is set
  // this method will be much slower.
  /*
    fonaSerial->begin(115200); // Default LTE shield baud rate
    fona.begin(*fonaSerial); // Don't use if statement because an OK reply could be sent incorrectly at 115200 baud

    Serial.println(F("Configuring to 9600 baud"));
    fona.setBaudrate(9600); // Set to 9600 baud
    fonaSerial->begin(9600);
    if (!fona.begin(*fonaSerial)) {
    Serial.println(F("Couldn't find modem"));
    while(1); // Don't proceed if it couldn't find the device
    }
  */

  type = fona.type();
  Serial.println(F("FONA is OK"));
  Serial.print(F("Found "));
  switch (type) {
    case SIM800L:
      Serial.println(F("SIM800L")); break;
    case SIM800H:
      Serial.println(F("SIM800H")); break;
    case SIM808_V1:
      Serial.println(F("SIM808 (v1)")); break;
    case SIM808_V2:
      Serial.println(F("SIM808 (v2)")); break;
    case SIM5320A:
      Serial.println(F("SIM5320A (American)")); break;
    case SIM5320E:
      Serial.println(F("SIM5320E (European)")); break;
    case SIM7000:
      Serial.println(F("SIM7000")); break;
    case SIM7070:
      Serial.println(F("SIM7070")); break;
    case SIM7500:
      Serial.println(F("SIM7500")); break;
    case SIM7600:
      Serial.println(F("SIM7600")); break;
    default:
      Serial.println(F("???")); break;
  }

  // Print module IMEI number.
  uint8_t imeiLen = fona.getIMEI(imei);
  if (imeiLen > 0) {
    Serial.print("Module IMEI: "); Serial.println(imei);
  }
}

bool netStatus() {
  int n = fona.getNetworkStatus();

  Serial.print(F("Network status ")); Serial.print(n); Serial.print(F(": "));
  if (n == 0) Serial.println(F("Not registered"));
  if (n == 1) Serial.println(F("Registered (home)"));
  if (n == 2) Serial.println(F("Not registered (searching)"));
  if (n == 3) Serial.println(F("Denied"));
  if (n == 4) Serial.println(F("Unknown"));
  if (n == 5) Serial.println(F("Registered roaming"));

  if (!(n == 1 || n == 5)) return false;
  else return true;

}



void FullSMS () {
  char message3[20];

  strcpy(message3, volBuff);
  strcat(message3, text_message1);

  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);
  pinMode(buttonPin, INPUT);
  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message3)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}


void ColdSMS () {
  char message[20];

  strcpy(message, tempBuff);
  strcat(message, text_message2);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void batSMS () {
  char message2[25];

  strcpy(message2, batBuff);
  strcat(message2, text_message3);
  pinMode(FONA_RST, OUTPUT);
  digitalWrite(FONA_RST, HIGH); // Default state
  pinMode(FONA_PWRKEY, OUTPUT);

  powerOn(true); // Power on the module
  moduleSetup(); // Establish first-time serial comm and print IMEI

  fona.setNetworkSettings(F("hologram")); // For Hologram SIM card, change appropriately
  while (!netStatus()) {
    Serial.println(F("Failed to connect to cell network, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Connected to cell network!"));

  // Send a text to your phone!
  if (!fona.sendSMS(phone_number, message2)) {
    Serial.println(F("Failed to send text!"));
  }
  else {
    Serial.println(F("Sent text alert!"));
  }
}

void PostData ()
{
  // If the shield was already on, no need to re-enable
#if defined(turnOffShield) && !defined(SIMCOM_3G) && !defined(SIMCOM_7500) && !defined(SIMCOM_7600)
  // Disable GPRS just to make sure it was actually off so that we can turn it on
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn on GPRS
  while (!fona.enableGPRS(true)) {
    Serial.println(F("Failed to enable GPRS, retrying..."));
    delay(2000); // Retry every 2s
  }
  Serial.println(F("Enabled GPRS!"));
#endif

  // Post something like temperature and battery level to the web API
  // Construct URL and post the data to the web API

  // Format the floating point numbers



  // Also construct a combined, comma-separated location array
  // (many platforms require this for dashboards, like Adafruit IO):
  //sprintf(locBuff, "%s,%s,%s,%s", speedBuff, latBuff, longBuff, altBuff); // This could look like "10,33.123456,-85.123456,120.5"

  // Construct the appropriate URL's and body, depending on request type
  // In this example we use the IMEI as device ID

#ifdef PROTOCOL_HTTP_GET
  // GET request

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  // You can adjust the contents of the request if you don't need certain things like speed, altitude, etc.
  sprintf(URL, "GET /dweet/for/%s?temp=%s&bat=%s&vol=%s HTTP/1.1\r\nHost: dweet.io\r\n\r\n",
          imei, tempBuff, batBuff, volBuff);

  // Try a total of three times if the post was unsuccessful (try additional 2 times)
  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s?temp=%s&bat=%s&vol=%s", imei, tempBuff, batBuff, volBuff);

  while (counter < 3 && !fona.postData("GET", URL)) {
    Serial.println(F("Failed to post data, retrying..."));
    counter++; // Increment counter
    delay(1000);
  }
#endif

#elif defined(PROTOCOL_HTTP_POST)
  // You can also do a POST request instead

  counter = 0; // This counts the number of failed attempts tries

#if defined(SIMCOM_3G) || defined(SIMCOM_7500) || defined(SIMCOM_7600)
  sprintf(body, "{\"lat\":%s,\"long\":%s}\r\n", latBuff, longBuff); // Terminate with CR+NL
  sprintf(URL, "POST /dweet/for/%s HTTP/1.1\r\nHost: dweet.io\r\nContent-Length: %i\r\n\r\n", imei, strlen(body));

  while (counter < 3 && !fona.postData("www.dweet.io", 443, "HTTPS", URL, body)) { // Server, port, connection type, URL
    Serial.println(F("Failed to complete HTTP/HTTPS request..."));
    counter++; // Increment counter
    delay(1000);
  }
#else
  sprintf(URL, "http://dweet.io/dweet/for/%s", imei);
  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff);

  // Let's try a POST request to thingsboard.io
  // Please note this can me memory-intensive for the Arduino Uno
  // and may not work. You might have to split it up into a couple requests
  // and send part of the data in one request, and the rest in the other, etc.
  // Perhaps an easier solution is to swap out the Uno with an Arduino Mega.
  /*
    const char * token = "qFeFpQIC9C69GDFLWdAv"; // From thingsboard.io device
    sprintf(URL, "http://demo.thingsboard.io/api/v1/%s/telemetry", token);
    sprintf(body, "{\"lat\":%s,\"long\":%s,\"speed\":%s,\"head\":%s,\"alt\":%s,\"temp\":%s,\"batt\":%s}", latBuff, longBuff,
          speedBuff, headBuff, altBuff, tempBuff, battBuff);
    //  sprintf(body, "{\"lat\":%s,\"long\":%s}", latBuff, longBuff); // If all you want is lat/long
  */

  while (counter < 3 && !fona.postData("POST", URL, body)) {
    Serial.println(F("Failed to complete HTTP POST..."));
    counter++;
    delay(1000);
  }
#endif


#endif

  //Only run the code below if you want to turn off the shield after posting data
#ifdef turnOffShield
  // Disable GPRS
  // Note that you might not want to check if this was successful, but just run it
  // since the next command is to turn off the module anyway
  if (!fona.enableGPRS(false)) Serial.println(F("Failed to disable GPRS!"));

  // Turn off GPS
  if (!fona.enableGPS(false)) Serial.println(F("Failed to turn off GPS!"));

  // Power off the module. Note that you could instead put it in minimum functionality mode
  // instead of completely turning it off. Experiment different ways depending on your application!
  // You should see the "PWR" LED turn off after this command
  //  if (!fona.powerDown()) Serial.println(F("Failed to power down FONA!")); // No retries
  counter = 0;
  while (counter < 3 && !fona.powerDown()) { // Try shutting down
    Serial.println(F("Failed to power down FONA!"));
    counter++; // Increment counter
    delay(1000);
  }
#endif

  // Alternative to the AT command method above:
  // If your FONA has a PWRKEY pin connected to your MCU, you can pulse PWRKEY
  // LOW for a little bit, then pull it back HIGH, like this:
  //  digitalWrite(PWRKEY, LOW);
  //  delay(600); // Minimum of 64ms to turn on and 500ms to turn off for FONA 3G. Check spec sheet for other types
  //  delay(1300); // Minimum of 1.2s for SIM7000
  //  digitalWrite(PWRKEY, HIGH);

  // Shut down the MCU to save power
#ifndef samplingRate
  Serial.println(F("Shutting down..."));
  delay(5); // This is just to read the response of the last AT command before shutting down
  MCU_powerDown(); // You could also write your own function to make it sleep for a certain duration instead
#else
  //  // The following lines are for if you want to periodically post data (like GPS tracker)
  //  Serial.print(F("Waiting for ")); Serial.print(samplingRate); Serial.println(F(" seconds\r\n"));
  //  delay(samplingRate * 1000UL); // Delay

  // Only run the initialization again if the module was powered off
  // since it resets back to 115200 baud instead of 4800.
#ifdef turnOffShield
  fona.powerOn(FONA_PWRKEY); // Powers on the module if it was off previously
  moduleSetup();
#endif

#endif
}

Quick update:

So I was thinking, "you know if something works in one script, but the opposite works in another script, why not combine the two working things?"

So I did that, and instead of having two extra states I just have one for purge and did this:

  switch (state)                        // Check which state we are in.
  {

    case OffState:

      //    manualMode();
      if (digitalRead(primeBtnPin) == pressed) {

        pumpOff();
      }
      else {
        manualMode();
        break;
      }
      
      if(purgeBtnPinHasBeenPressed = true) {
        state = PurgeState;
      }

      while (digitalRead(resetBtnPin) == released)
        resetData();
      




      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

      case PurgeState:
      if (digitalRead(purgeBtnPin) == pressed)
        state = OffState;
        else
          manualPurge();
          break;

Now those two buttons don't block the rest of the program which is great! Only small issue that the manualPurge stays on for like two seconds after I release the pureBtnPin. Next thing I need to fix is the pump returning to pumpstate after threshold is reached

Another quick update:

I'm trying to resolve the issue with the pump returning to pump state after reaching the threshold of 300mL and created the "ThresholdLimit" bool and set it to false. I created an int called Threshold with a value of 300 and wrote

          if(vol > Threshold) {
        ThresholdLimit = true; 
      }

and to see what the status of ThresholdLimit I wrote

    if (ThresholdLimit = true) {
      Serial.print("THRESHOLD REACHED"); Serial.println(Threshold);
    }

this was for the full state case

   case FullState:
    if (ThresholdLimit = false ||  (digitalRead(buttonPin) == LOW))
    
      state = PumpState;
    
     else
       Serial.println("FULL!");
     state = OffState;
          break;

but before I even do anything, the serial monitor returns ThresholdLimit as true

whats going on here?

I guess this is a typo
a single equal-sign "=" is assigning

a double-equal-sign "==" is comparing

if(primeBtnPinHasBeenPressed = true) {
should be
if(primeBtnPinHasBeenPressed == true) { // == double-equal-sign to COMPARE

same here you are assigning instead of comparing

if (ThresholdLimit == false // double-equal-sign for COMPARING

Ok so then is this part right? since I'm assigning the bool to be false while threshold is higher than volume?

          if(Threshold > vol) {
        ThresholdLimit = false; 
      }

I made the change to compare the bool to false in FullState, but it still wont return to PumpState

   case FullState:
    if (ThresholdLimit == false ||  (digitalRead(buttonPin) == LOW))
    
      state = PumpState;
    
     else
       Serial.println("FULL!");
     state = OffState;
          break;
  }

I used this bit of code to see the status of the bool and it retuns thresholdlimit as false since it prints out "THRESHOLD NOT REACHED", so I think the serial monitor is reading ThresholdLimit as false but my code is still wrong. Do I need to include 'ThresholdLimit = true" under the else statement in FullState?

    if (ThresholdLimit == true) {
      Serial.print("THRESHOLD REACHED"); Serial.println(Threshold);
    } else
    {
      Serial.println("THRESHOLD NOT REACHED");
    }

I also manually ran the pump until vol was greater than 300 mL, which is the threshold limit, but the serial monitor still shows "threshold not reached"

Quick update

I change the threshold limit to

          if(Threshold < vol && ThresholdLimit == false)  {
        ThresholdLimit = true; 
      }

And now when I manually pump over 300mL and is reflected on vol, the flag changes and serial monitor displays "THRESHOLD REACHED", however, FullState still wont return to PumpState and goes to OffState

   case FullState:
    if (ThresholdLimit == false ||  (digitalRead(buttonPin) == LOW))
    
      state = PumpState;
    
     else
       Serial.println("FULL!");
     state = OffState;
          break;
  }

the buttonPin is on pin D33 and is pinMode(buttonPin, INPUT_PULLUP); so button pin should be LOW, but I also have button pin set as buttonState for the FullSMS() function buttonState = digitalRead(buttonPin); would this have something to do with the issue?

  if (buttonState == HIGH && lastButtonState == LOW || vol > 300 && vol - flowMilliLitres <= 300) {
    opened = false;
    lastButtonState = buttonState;
    FullSMS ();
    pumpOff();
    lcd.setCursor(0, 0);
    lcd.print("FULL          ");
  }

quick edit: Also, the voltage drop issue is back. I had like 7 capacitors and the voltage drop was non existent, but now its doing the same thing where when the motor runs battery percent drops to around 30-40% which is still not as bad as it used to be. I added a diode between the vin and gnd pins of the DRV 8876 motor driver carrier but it doesnt do anything

Another update,

so what I found was that when I start the sampling cycle and I collect over 300mL, ThresholdLimit is still false. However, once I push the SmplngBtnPin to turn off the sampling cycle, and return to OffState, thats when thresholdLimit turns true.. which makes sense because the if statement for ThresholdLimit is in Offstate, which the state machine does not jump into. Does this mean I need to take:

          if(Threshold < vol && ThresholdLimit == false)  {
        ThresholdLimit = true; 
      }

out of OffState and make it global? If so, how do I make it global?

    case OffState:

      //    manualMode();
      if (digitalRead(primeBtnPin) == pressed) {

        pumpOff();
      }
      else {
        manualMode();
        break;
      }
      
      if(purgeBtnPinHasBeenPressed = true) {
        state = PurgeState;
      }
      

      
      while (digitalRead(resetBtnPin) == released)
        resetData();
      

          if(Threshold < vol && ThresholdLimit == false)  {
        ThresholdLimit = true; 
      }


      startMillis = currentMillis;      // Keep resetting the start time of the pumping sequence.
      pumpOff();
      break;

Quick update:

I moved

    if(Threshold < vol && ThresholdLimit == false)  
         {
          ThresholdLimit = true; 
      }

into void setup and once I hit 300mL, the ESP32 kept sending FullSMS once after the other. I moved it from void setup into FullState, but now it does the same thing again where it wont return to pump state


   case FullState:
    if(Threshold < vol && ThresholdLimit == false)  
         {
          ThresholdLimit = true; 
      }
      if (ThresholdLimit == false ||  (digitalRead(buttonPin) == LOW)) 
    
      state = PumpState;
    
     else
       Serial.println(state);
     state = OffState;
          break;
  }

one more quick update:

I change FullState to this:

   case FullState:
    if(Threshold < vol && ThresholdLimit == false)  
     ThresholdLimit = true; 
      if (ThresholdLimit == true ||  (digitalRead(buttonPin) == HIGH)) 
       state = OffState;
    
     else
       Serial.println(state);
     state = PumpState;
          break;
  }

and now the serial monitor displays ThresholdLimit change from false to true by displaying
22:18:13.208 -> THRESHOLD REACHED300

but it still wont return to OffState, it just keep going to PumpState. What am I doing wrong?

I haven't read all your inbetween messages, but what jumped into my eyes is that you do not have a "break;" statement at each possible way to leave a "case".

example

//    manualMode();
      if (digitalRead(primeBtnPin) == pressed) {

        pumpOff();
       // here should be a break;-statement too
      }
      else {
        manualMode();
        break;
      }
      
      if(purgeBtnPinHasBeenPressed = true) {
        state = PurgeState;
      }

And as a second thing
your code still has while-loops which are blocking

you should organise your whole code to be non-blocking
you can control everything by a state-machine

Making a variable global is simply done by declaring it outside all
functions above all functions.

If you did not yet know what global scope is:
how many hours did you spend trying this / trying that?
I guess many hours. You should invest a few hours into learning ,ore basics

and investing a few hours to re-organise your code to work consequently with statemachines where each and every detail works in a quickly jump_in / jump_out-manner

I started writing a tutorial about that but it is not yet finished

best regards Stefan

for analysing your code what is going on
here are three macros that you can insert at the top of your code.
It must be at the top , because these are macros which are different from C++-code.

The dbgc-macro prints to the serial monitor only once if the variable specified as the second parameter has changed since last time printing it.

it can be used with boolean and integer-variables but not with chars or strings.

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *

https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
// print only once when value has changed
#define dbgc(myFixedText, variableName) \
  do { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  } while (false);
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *

you did post some parts of your code. Posting parts of your code is almost useless, because how should I gain overview about your whole code if I can't see your complete code?

If I have your complete code I can add the serial debug-output for analysing but ....

I hope you see it makes sense to always post your complete code.
Posting the code as a code-section limits the height in your posting because as a code-section the code is a scrollable window.
And posting the actual code makes sure that we are talking about the exact same code.

best regards Stefan