Help with hanging sketch

After some time this sketch on my Mega will start to display strange characters on the LCD and sometimes the sketch will freeze up entirely. I know the Watchdog isnt a way to fix this permanently but I have ordered an Uno so I can use it as an ISP to burn the updated bootloader to the Megas (I ordered another one for fun :smiley: ). This system is to monitor and control an aquaponics system so it running without error is of paramount importance (health to fish and whatnot). I have reduced the sketch from 31% usage of Dynamic Memory all the way down to 12% using a modified form of HTTP GET to just send 1ā€™s and 0ā€™s and then do the logic on the server side. I need an extra set of eyes to go through here and see if they can see anything.

/*
Version 1.0.0 scraped for new design using recirc valve alone to control flow to growbed
Version 1.1.0 Started over with just code for pH probe. This code was directly from Atlas Scientific.
Version 1.1.1 Assigned float for pH.
version 1.2.0 Added logic for fill and drain functionality.
Version 1.2.1 Added "hasFlood" integer triggered by exceeding "floodThreshold".  This was needed because erronious readings from drainSensor caused this to be a one shot.. this ensures
              that we truly have a drain situation before recirculating.
Version 1.3.0 Added Water level probe function following lessons learned from the flood/drain situation.
Version 1.4.0 Added ability to correct pH during acid/baseOn and acid/baseOff window.  Adjust these windows to add more or less acid/base for correction.
Version 1.4.1 Added a temperature average function to smooth out temperature readings over a second.
Version 1.4.2 Added temperature compensation to pH circuit
Version 1.4.3 Removed response code from ph circuit (circuit was returning *OK everytime we sent it the new temperature. The arduino saw this as the current pH and base pump would run)
Version 1.4.4 Increased Drain threshold to be sure grow bed does drain ( 100 - 1000)
Version 1.5.0 Added SD capabilities.  Will add another version once I get it to initialize
Version 1.5.1 Completed SD capabilities logging an ID, timestamp (in millis), pH, and Temperature to LOG.CSV file
Version 1.6.0 Adding Web Server capabilities
Version 1.6.1 Added more values to be saved in csv file ID, Time In Cycle (mS), pH, Temperature, Acid Pump Status, Base Pump Status, Recirc Status, Drain Sensor, Fill Sensor, Fill Status
Version 2.0.0 Removed self filling capabilities.  This enables me to allow for faster pH corrections since the system naturally buffers itself after 5 minutes and it takes 3 minutes for
              dosing to become apparent in THIS system.  Changing total cycle to 5 minutes to allow dosing to mix within system and for pH to equalize.
Version 2.1.0 Changed main cycle time to 10 minutes since new growbed medium requires more flow time to fill.
Version 2.2.0 Added Real Time Clock capabilities (very buggy)
Version 2.2.1 Bug fixes for Real Time Clock capabilities.  Also added more expainations to most variables.  I was bored on an international flight to Hong Kong from Chicago
Version 3.0.0 Added online data storage capabilities through PHP and MySQL.  Website to follow.  This will allow for monitoring anywhere.
Version 3.0.1 Moved data logging to the loop and added a timer to upload data to MySQL only ever 5 seconds to reduce the amount of data sent to the server. Current settings will use approximately 600MB in a year.
               ***NOTE 8/3/2015 webpage for system is online and operating
Version 3.0.2 Using MYSQL current time and date variables to perform time and date stamps. This should take some load off of the Controller
Version 3.1.0 Noticed that after 12 hours or so the Data logging lags over the web.  Suspect memory capacity on Arduino is being used due to high amounts of data logging. Added an auto reset function to automatically
              reset the controller ever 21600000 milliseconds (6 hours)
Version 3.1.1 Changed reset value to 4 hours instead of 6
Version 3.2.0 Removed reset function. Decided to try only posting to php once instead of twice for each table.  Will modify the add.php on the webserver to query the database to both tables instead of a separate
              php file and multiple lines of code on Arduino.  This, hopefully, should eliminate the memory issues and conflicts.
Version 3.2.1 Removed the datastring to be written to the csv file.  Apparently, strings are really bad for RAM and the issue I am having is caused by running out of RAM. Also, Some of the small integers were changed to bytes
              for the same reason.
Version 3.6.0 Changed the method of sending data to MySQL from "GET" to "POST"
Version 3.6.1 Changed cycle back to 15 minutes due to new growbed being larger.  The system would barely begin to recirculate before the end of the cycle.
Version 4.0.0 Added LCD
Version 4.0.1 Worked on variables trying to make changes to these variables faster and requiring less memory
Version 4.0.2 Disabled SD card.  Depending on testing, this may be permanent.
Version 4.0.3 Added Arduino Watchdog function to detect if arduino has hung up.  It will then reset the system.
Version 4.1.0 Add alarms to be published to the web site
Version 4.1.1 Were not going to talk about this one.  I added a BUNCH of Serial.print commands trying to diagnose "the problem"
Version 4.1.2 Added Close connection messages to the webserver
Version 4.1.2 Removed the php client timeout function.
Version 4.2.0 Moved a bunch of the display logic to the webserver.  this enables me to just send a 1 or 0 to the server instead of a whole string of data. For Example, one alarm
              required a HTTP GET sting over 180 characters long, now this is done with only 6 :)
*/

// WHAT LIBRARIES ARE TO BE USED
#include <Math.h>
#include <Thermistor.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Time.h>
#include <EthernetUdp.h>
//#include <SD.h>
#include <MemoryFree.h>
#include <LiquidCrystalFast.h>

// LETS SETUP SOME VALUES
// ALARM VARIABLES
float phUpper = 8.00;
float phLower = 6.30;
float tempUpper = 86.00;
float tempLower = 70.00;
byte phHighAlarm = 0;
byte phLowAlarm = 0;
byte tempHighAlarm = 0;
byte tempLowAlarm = 0;
byte floodAlarm = 0;
// TIMING VARIABLES

const long timeZoneOffset = -18000L;                         // WHAT TIME ZONE SET TO Central Daylight Time
unsigned int ntpSyncTime = 7200;
unsigned long previousRate = 0;                              // ASSIGNS 0 TO PREVIOUS RATE ON INITIALIZATION
unsigned long previousMillis = 0;                            // ASSIGNS 0 TO PREVIOUS TIMING ON INTIALIZATION
unsigned long previouspH = 0;                                // ASSIGNS 0 TO PREVIOUS PH TIMING ON INITIALIZATION
unsigned long previousLogRate = 0;                           // ASSIGNS 0 TO PREVIOUS LOG RATE ON INITIALIZATION
const long acidOn = 436000;                                  // MILLISECONDS IN CYCLE TO ENABLE ACID DOSING (IF NEEDED)
const long acidOff = 440000;                                 // MILLISECONDS IN CYCLE TO DISABLE ACID DOSING (IF NEEDED)
const long acidOn2 = 882000;
const long acidOff2 = 890000;
const long baseOn = 870000;                                  // MILLISECONDS IN CYCLE TO ENABLE BASE DOSING (IF NEEDED)
const long baseOff = 890000;                                 // MILLISECONDS IN CYCLE TO DISABLE BASE DOSING (IF NEEDED)
const short rate = 1000;                                      // MILLISECONDS BETWEEN TEMPERATURE READINGS
const short logRate = 5000;                                   // MILLISECONDS BETWEEN WRITING NEW LINE TO CSV LOGFILE
//const long fillOn = 601000;                                // MILLISECONDS IN CYCLE TO ENABLE WATER FILLING (IF NEEDED)
//const long fillOff = 899999;                               // MILLISECONDS IN CYCLE TO DISABLE WATER FILLING (IF NEEDED)
const long recircOn = 601000;                                // MILLISECONDS IN CYCLE TO ENABLE WATER RECIRCULATION (IF NEEDED)
const long recircOff = 899999;                               // MILLISECONDS IN CYCLE TO DISABLE WATER RECIRCULATION (IF NEEDED)
const long cycle = 900000;                                   // MILLISECONDS TOTAL IN MAIN CYCLE
const long pHcycle = 900000;                                 // MILLISECONDS TOTAL IN PH CYCLE



//NETWORKING VARIABLES
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};         // MAC ADDRESS OF ETHERNET CARD
byte ip[] = {192, 168, 2, 7};                               // IP ADDRESS OF ETHERNET CARD

byte gateway[] = {192, 168, 2, 1};
byte subnet[] = {255, 255, 255, 0};


// IPAddress timeServer(132, 163, 4, 101);                      // time-a.timefreq.bldrdoc.gov
IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov UNSWITCH COMMENT TO ACTIVATE THIS SERVER
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov UNSWITCH COMMENT TO ACTIVATE THIS SERVER

unsigned int localPort = 8888;                               // local port to listen for UDP packets
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
EthernetUDP Udp;
unsigned long ntpLastUpdate = 0;
time_t prevDisplay = 0;                                      // when the digital clock was displayed
EthernetClient client;
char server[] = "218.m3allc.com";                            // IP Address (or name) of server to dump data to

// I/O VARIABLES
byte acidPump = 44;                                     // ACID PUMP OUTPUT ON PIN 44
byte basePump = 42;                                     // BASE PUMP OUTPUT ON PIN 44
byte recirc = 46;                                       // RECIRC VALVE OUTPUT ON PIN 48

Thermistor temp(A0);                                          // WATER TEMP THERMISTOR IS CONNECTED TO ANALOG PIN 0
Thermistor gtemp(A1);                                         // GROW BED THERMISTOR IS CONNECTED TO ANALOG PIN 1
byte SD_pin = 4;                                            // SLAVE SELECTOR FOR SD CARD IS PIN 10
byte EN_pin = 10;                                            // SLAVE SELECTOR FOR W5100

// MATH VARIABLES
byte hasFlood = 0;                                           // HAS THERE BEEN A GROWBED FLOOD WITHIN THE MAIN CYCLE
//byte drainSensorVal = 0;                                     // ACCUMULATION OF DRAIN VALUES WORKS WITH floodThreshold
//byte levelSensorVal = 0;
byte recircEnable = 0;                                       // ARE WE WITHIN THE RECIRCULATION WINDOW OF TIME
//long waterDetect = 0;                                        // DEFAULT VALUE FOR DRAIN DETECTION
//const long floodThreshold = 20000;                                  // HOW HIGH DOES drainSensorVal HAVE TO BE TO DECLARE FLOOD IN GROWBED
float temperature = 0;                                        // PLACE HOLDER FOR TEMPERATURE READINGS (IN *C)
int tempReadings = 0;                                       // ACCUMULATION OF NUMBER OF TEMPERATURE READINGS IN "RATE"
float tempTotal = 0;                                        // MATHEMATICAL ACCUMULATION OF TEMERATURE READINGS
float tempAverage = 0;                                      // DIVIDEND OF tempTotal/tempReadings
float gtemperature = 0;                                     // PLACE HOLDER FOR GROW BED TEMPERATURE READINGS (IN *C)
int gtempReadings = 0;                                      // ACCUMULATION OF NUMBER OF TEMPERATURE READINGS IN "RATE" FOR GROW BED
float gtempTotal = 0;                                       // MATHEMATICAL ACCUMULATION OF GROW BED TEMPERATURE READINGS
float gtempAverage = 0;                                     // DIVIDEND OF gtempTotal/gtempReadings
float rounded_up;



byte astatus = 0;                                        // STRING FOR RUN/OFF FOR ACID PUMP FOR MYSQL
byte bstatus = 0;                                        // STRING FOR RUN/OFF FOR BASE PUMP FOR MYSQL
byte rstatus = 0;                                        // STRING FOR RECIRC/FLOOD FOR RECIRC FOR MYSQL
byte dsensor = 0;                                        // STRING FOR WATER/DRY FOR DRAIN FOR MYSQL
byte fsensor = 0;                                        // STRING FOR WATER/DRY FOR FILL FOR MYSQL
byte fstatus = 0;                                        // STRING FOR FILL/OFF FOR DRAIN FOR MYSQL
byte plow = 0;
byte phigh = 0;
byte wlow = 0;
byte whigh = 0;
byte flood = 0;
int inChar;


float ph;                                                   // CREATION OF A PH FLOAT
float dissolved = 0.00;                                        // CRATEION OF A DISSOLVED OXYGEN FLOAT***this circuit does not exist...YET!****
byte acidEnable = 0;                                         // ARE WE WITHIN THE ACID DOSING WINDOW OF TIME
byte baseEnable = 0;                                         // ARE WE WITHIN THE BASE DOSING WINDOW OF TIME
//short waterDetectFail = 0;                                    // IF YOU HAD WATER DETECTION BUT IT DIDNT LAST LONG ENOUGH TO SATISFY floodThreshold THIS BECOMES 1
float acidThreshold = 7.10;                                 // KEEP PH BELOW THIS VALUE BUT...
float baseThreshold = 6.70;                                 // ABOVE THIS VALUE

//ATLAS SCIENTIFIC VALUES
char stamp_data_ph[30]; // char array for serial data from ph stamp
byte holding; // counter for serial chars
byte i; //for loop counter




const int numRows = 4;
const int numCols = 20;

// initialize the library with the numbers of the interface pins
LiquidCrystalFast lcd(33, 35, 31, 29, 27, 25, 23);


void setup() {                                             // set up the hardware

  // OUTPUTS

  pinMode(acidPump, OUTPUT);
  pinMode(basePump, OUTPUT);
  pinMode(recirc, OUTPUT);
  //INPUTS


  Serial.begin(38400);                                                       //set baud rate for the hardware serial port_0 to 9600
  Serial3.begin(38400);                                                      //set baud rate for software serial port_3 to 9600
  lcd.begin(numCols, numRows);
  lcd.setCursor(0, 0);
  lcd.print(F("Initializing System"));

  Serial.println(F("Initializing Card"));
  lcd.setCursor(0, 1);
  lcd.print(F("MCM-APC-55 VER-4.2.0"));

  Serial.println(F("McMillin Aquaponics version 4.2.0"));
  //disable w5100 SPI while setting up SD
  pinMode(SD_pin, OUTPUT);
  pinMode(EN_pin, OUTPUT);

  // set up SD
  /*    if (!SD.begin(SD_pin)) {
        lcd.setCursor(0, 2);
        lcd.write("CARD INT FAILED");
        Serial.println("Card Initialization Failed");
      }
      else {
        lcd.setCursor(0, 2);
        lcd.write("CARD INT SUCCESS!");
        Serial.println("Card Initialization Successful!");
      }
    */

  // set up w5100


  //Ethernet.begin(mac, ip, subnet);
  int i = 0;
  int DHCP = 0;
  DHCP = Ethernet.begin(mac);
  while (DHCP == 0 && i < 30) {
    delay(1000);
    DHCP = Ethernet.begin(mac);
    i++;
  }
  if (!DHCP) {
    Serial.println(F("DHCP FAILED"));
    for (;;);
  }
  Serial.print(F("IP number assigned by DHCP is "));
  Serial.println(Ethernet.localIP());

  int trys = 0;
  while (!getTimeAndDate() && trys < 10) {
    trys++;
  }

  //////////////////////////////////////////////
  // takes a second for the w5100 to get ready
  delay(1000);
  lcd.clear();


  lcd.setCursor(6, 1);
  lcd.print(F("SETUP DONE"));

  Serial.println(F("Setup done"));
  Serial3.println(F("RESPONSE,0\r"));
  delay(100);
  Serial3.print(F("C,0")); // Set the pH board to single reading mode
  Serial3.print(F("\r")); // Send carriage return to end command
  delay(100);

  /*
  //Write Log.csv File Header

  digitalWrite(SD_pin, LOW);                                                     // Select SD card
  digitalWrite(EN_pin, HIGH);                                                    // Deselect Ethernet

  File logFile = SD.open("LOG.csv", FILE_WRITE);
  if (logFile)
  {
    logFile.println(", , ,"); //Just a leading blank line, incase there was previous data
    logFile.println("Date, Time, pH, Temperature, Acid Pump Status, Base Pump Status, Recirc Status, Drain Sensor, Fill Sensor, Fill Status");
    logFile.close();
  }
  else
  {
    Serial.println("Couldn't open log file");
    return;
  }
  digitalWrite(SD_pin, HIGH);                                                     // Deselect SD card
  digitalWrite(EN_pin, HIGH);                                                    // Deselect Ethernet
  */
  delay(1000);
  lcd.clear();
  time_t prevDisplay = 0; // when the digital clock was displayed


}

void loop() {                        //here we go....

  Ethernet.maintain();


  // Update the time via NTP server as often as the time you set at the top
  if (now() - ntpLastUpdate > ntpSyncTime) {
    int trys = 0;
    while (!getTimeAndDate() && trys < 10) {
      trys++;
    }
    if (trys < 10) {
      Serial.println(F("ntp server update success"));
    }
    else {
      Serial.println(F("ntp server update failed"));
    }
  }


  int floodReading = analogRead(A2);
  unsigned long currentRate = millis();
  unsigned long currentMillis = millis();
  unsigned long currentpH = millis();
  unsigned long currentLogRate = millis();
  //  drainSensorVal = digitalRead(drainSensor);
  /*
    if (timeStatus() != timeNotSet) {
      if (now() != prevDisplay) { //update the display only if time has changed
        prevDisplay = now();
      }
    }
  */





  ////////////////////////GROW BED DRAIN DETECTION AND RECIRCULATION////////////////////////////////////////
  if (floodReading < 250) {
    dsensor = 0;
  }
  else {
    dsensor = 1;
  }
  if (floodReading <= 25) {
    hasFlood = 1;
  }
  /*if (drainSensorVal == 0 && hasFlood < 1) {
    waterDetect++;
    waterDetectFail = 0;
  }
  else {
    waterDetectFail++;
  }
  if (waterDetectFail >= 10) {
    waterDetect = 0;
  }
  if (waterDetect > floodThreshold) {
    hasFlood = 1;
  }
  */
  if ((currentMillis - previousMillis) >= recircOn && (currentMillis - previousMillis) <= recircOff) {
    recircEnable = 1;
  }
  else {
    recircEnable = 0;
  }

  if (hasFlood == 1 || recircEnable == 1) {
    digitalWrite(recirc, HIGH);
    rstatus = 1;
    lcd.setCursor(0, 1);
    lcd.print(F("    RECIRCULATING   "));
  }
  else {
    digitalWrite(recirc, LOW);
    rstatus = 0;
    lcd.setCursor(0, 1);
    lcd.print(F("      FLOODING      "));
  }

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


  ///////////////////////////////WRITE TO SD CARD AND WEBSERVER/////////////////////////////////////////////
  if ((currentLogRate - previousLogRate) >= logRate) {
    //  writesd();
    sendDataToPHP();                                          // Call function to send data to webserver


    if (hasFlood == 0 && recircEnable == 1) {
      flood = 1;
    }
    else {
      flood = 0;
    }

    if (ph >= phUpper) {
      phigh = 1;
    }
    else {
      phigh = 0;
    }

    if (ph <= phLower) {
      plow = 1;
    }
    else {
      plow = 0;
    }
    if (rounded_up >= tempUpper) {
      whigh = 1;
    }
    else {
      whigh = 0;
    }
    if (rounded_up <= tempLower) {
      wlow = 1;
    }
    else {
      wlow = 0;
    }

    previousLogRate = currentLogRate;


  }
  //////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////PH CORRECTION/////////////////////////////////////////////////////
  if (((currentpH - previouspH) >= acidOn && (currentpH - previouspH) <= acidOff) || ((currentpH - previouspH) >= acidOn2 && (currentpH - previouspH) <= acidOff2)) {
    acidEnable = 1;
  }
  else {
    acidEnable = 0;
  }

  if ((currentpH - previouspH) >= baseOn && (currentpH - previouspH) <= baseOff) {
    baseEnable = 1;
  }
  else {
    baseEnable = 0;
  }

  if (baseEnable == 1 && ph < baseThreshold) {
    digitalWrite(basePump, HIGH);
    bstatus = 1;
  }

  else if (acidEnable == 1 && ph > acidThreshold) {
    digitalWrite(acidPump, HIGH);
    astatus = 1;
  }


  else {
    digitalWrite(acidPump, LOW);
    digitalWrite(basePump, LOW);
    bstatus = 0;
    astatus = 0;
  }
  //////////////////////////////////////////////////////////////////////////////////////////////////////////

  ///////////////////////GATHER TEMPERATURE READINGS FOR AVERAGES///////////////////////////////////////////
  temperature = temp.getTemp();
  tempTotal = tempTotal + temperature;
  tempReadings = tempReadings + 1;

  gtemperature = gtemp.getTemp();
  gtempTotal = gtempTotal + gtemperature;
  gtempReadings = gtempReadings + 1;
  //////////////////////////////////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////CALCULATE TEMERATURE AVERAGES///////////////////////////////////////////
  if ((currentRate - previousRate) > rate) {
    tempAverage = 0;
    tempAverage = tempTotal / tempReadings;
    rounded_up = ceilf(tempAverage * 100) / 100;

    float avgForpH = ((rounded_up - 32) / 1.8);
   
    gtempAverage = 0;
    gtempAverage = gtempTotal / gtempReadings;
    float grounded_up = ceilf(gtempAverage * 100) / 100;
   

  lcd.setCursor(0, 3);
  lcd.print(F("W:"));
  lcd.setCursor(2, 3);
  lcd.print(rounded_up);
  lcd.write((char)223);
  lcd.print(F("F"));
  lcd.setCursor(9, 3);
  lcd.print(F("  B:"));
  lcd.setCursor(13, 3);
  lcd.print(grounded_up);
  lcd.write((char)223);
  lcd.print(F("F"));
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////SEND TEMP TO PH CIRCUIT AND GET PH/////////////////////////////////////////////////

    Serial3.print(F("T,"));
    Serial3.print(avgForpH);
    Serial3.print(F("\r"));
    // delay(100);
    Serial3.print(F("R"));                                       // Send water temp for calibrated pH reading
    Serial3.print(F("\r"));                                      // Send carriage return to end command
    holding = 0;                                              // Reset buffer counter
    if (Serial3.available() > 1) {                            // If there is serial data in buffer
      holding = Serial3.available();                          // Set buffer counter to number of bytes
      for (i = 0; i < holding; i++) {                         // For loop to read out number of bytes in buffer
        stamp_data_ph[i] = Serial3.read();                    // Read serial data to char array
      }
    }
    else {
      Serial.println(F("pH ERR "));                           // Error if no data rx in serial buffer
    }
    ph = atof(stamp_data_ph);                                 // Write pH data to ph float for mathlcd.setCursor(0, 2);
   lcd.setCursor(0, 2);
  lcd.print(F("pH:"));
  lcd.setCursor(4, 2);
  lcd.print(ph);
  lcd.setCursor(8, 2);
  lcd.print(F("  DO:  "));
  lcd.setCursor(15, 2);
  lcd.print(dissolved);
    //////////////////////////////////////////////////////////////////////////////////////////////////////////
    writeLCD();
    ////////////////////////////////RESET VARIABLES FOR TEMP//////////////////////////////////////////////////


    previousRate = currentRate;
    tempTotal = 0;
    tempReadings = 0;
    gtempTotal = 0;
    gtempReadings = 0;
  }
  //////////////////////////////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////RESET VARIABLES FOR MAIN CYCLE////////////////////////////////////////////

  if (currentMillis - previousMillis >= cycle) {
    previousMillis = currentMillis;
    hasFlood = 0;
    //    waterDetect = 0;
  }
}

void writeLCD() {
  
  lcd.setCursor(0, 0);
  if (month() < 10) {
    lcd.print(F("0"));
    lcd.print(month());
  }
  else {
    lcd.print(month());
  }
  lcd.print(F("-"));
  if (day() < 10) {
    lcd.print(F("0"));
    lcd.print(day());
  }
  else {
    lcd.print(day());
  }
  lcd.print(F("-"));
  lcd.print(year());
  lcd.print(F("  "));
  lcd.setCursor(12, 0);
  if (hour() < 10) {
    lcd.print(F("0"));
    lcd.print(hour());
  }
  else {
    lcd.print(hour());
  }
  lcd.print(F(":"));
  if (minute() < 10) {
    lcd.print(F("0"));
    lcd.print(minute());
  }
  else {
    lcd.print(minute());
  }
  lcd.print(F(":"));
  if (second() < 10) {
    lcd.print(F("0"));
    lcd.print(second());
  }
  else {
    lcd.print(second());
  }
}
void sendDataToPHP() {
  if (client.connect(server, 80)) {
    client.print(F( "GET /add.php?"));
    client.print(F("d=CURDATE()"));
    client.print(F("&&"));
    client.print(F("t=CURTIME()"));
    client.print(F("&&"));
    client.print(F("p="));
    client.print( ph );
    client.print(F("&&"));
    client.print(F("do="));
    client.print( dissolved );
    client.print(F("&&"));
    client.print(F("w="));
    client.print( tempAverage );
    client.print(F("&&"));
    client.print(F("g="));
    client.print( gtempAverage );
    client.print(F("&&"));
    client.print(F("a="));
    client.print(astatus);
    client.print(F("&&"));
    client.print(F("b="));
    client.print(bstatus);
    client.print(F("&&"));
    client.print(F("r="));
    client.print(rstatus);
    client.print(F("&&"));
    client.print(F("ds="));
    client.print(dsensor);
    client.print(F("&&"));
    client.print(F("f="));
    client.print(fsensor);
    client.print(F("&&"));
    client.print(F("pl="));
    client.print(plow);
    client.print(F("ph="));
    client.print(phigh);
    client.print(F("wl="));
    client.print(wlow);
    client.print(F("wh="));
    client.print(whigh);
    client.print(F("fd="));
    client.print(flood);
    client.println(F( " HTTP/1.1"));
    client.println(F( "Host: 218.m3allc.com" ));
    client.println(F("Connection: close\r\n"));
    client.stop();
  }
  else
  {
    Serial.println(F("failed"));
  }
}


/*void writesd() {

  digitalWrite(SD_pin, LOW);                                                     // Select SD card
  digitalWrite(EN_pin, HIGH);                                                    // Deselect Ethernet

  File logFile = SD.open("LOG.csv", FILE_WRITE);
  while (logFile)
  {
    logFile.print(month());
    logFile.print("/");
    logFile.print(day());
    logFile.print("/");
    logFile.print(year());
    logFile.print(", ");
    logFile.print(hour());
    logFile.print(":");
    logFile.print(minute());
    logFile.print(":");
    logFile.print(second());
    logFile.print(",");
    logFile.print(ph);
    logFile.print(",");
    logFile.print(tempAverage);
    logFile.print(",");
    logFile.print(acidStatus);
    logFile.print(",");
    logFile.print(baseStatus);
    logFile.print(",");
    logFile.print(recircStatus);
    logFile.print(",");
    logFile.print(drainSensorVal);
    logFile.print(",");
    logFile.print(levelSensorVal);
    logFile.print(",");
  }
  digitalWrite(SD_pin, HIGH);                                                    // Deselect SD card
  digitalWrite(EN_pin, HIGH);                                                    // Deselect Ethernet
}
*/
// Do not alter this function, it is used by the system
int getTimeAndDate() {
  int flag = 0;
  Udp.begin(localPort);
  sendNTPpacket(timeServer);
  delay(1000);
  if (Udp.parsePacket()) {
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    unsigned long highWord, lowWord, epoch;
    highWord = word(packetBuffer[40], packetBuffer[41]);
    lowWord = word(packetBuffer[42], packetBuffer[43]);
    epoch = highWord << 16 | lowWord;
    epoch = epoch - 2208988800 + timeZoneOffset;
    flag = 1;
    setTime(epoch);
    ntpLastUpdate = now();
  }
  return flag;
}

// Do not alter this function, it is used by the system
unsigned long sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

// Clock display of the time and date (Basic)
void clockDisplay() {
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

// Utility function for clock display: prints preceding colon and leading 0
void printDigits(int digits) {
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

adamlwvdc36:
After some time this sketch on my Mega will start to display strange characters on the LCD and sometimes the sketch will freeze up entirely. ā€¦

Pictures? Or movie of when this happens? Can this occur in any stage of the processing, or when some conditions are met? This might shed some light on which parts of the code this is going wrong. Being able to look at the code is one thing, but it is also important to see how or when the problem manifests itself.

I was going to suggest some code cleanup before trying to debug. You have tons of global variables and some pretty long procedures. Refactor that into smaller modules and maybe the problem will jump out at you. Itā€™s good that youā€™re using version control, but your code is likely stomping on one of those globals and you just donā€™t know it yet.

Ok, so I will type this AGAIN since when I went to post it again the forum told me the page was not foundā€¦

I have noticed that when the LCD goes haywire it is usually when one of the pumps, or the recirc valve opensā€¦ basically when writing a digital output High.

http://218.m3allc.com/images/lcdnormal.jpg

This is my LCD when all is normal.

As soon as the recirc valve opened (this time, just now) the screen did this:

http://218.m3allc.com/images/lcdfail1.jpg

http://218.m3allc.com/images/lcdfail2.jpg

The first one still shows some recognizable data but then it goes totally nuts and that is what the second one is of. The strange thing is that right now, the sketch is still writing data to the webserver, correctly.

Its like a piece of the transmission to the LCD either gets lost or doesnt get written and that writing a pin high causes the program to skip certain things or freeze. Am I nuts?

Now, lyndon, I understand what you mean by tons of global variables. But, if I create a function to say handle the recirculation logic, variables and their values are needed in the function where I client print to the PHP. It was my understanding that a function cannot return a value like thatā€¦ or does that rule not apply to the global variables? If, in fact, I can declare a global variable and then have one function change it, and then use it in another function then this opens up a nice situation for me. I COULD declare a bunch of these as local variables and only have those that are needed by all of them as global

This is the part where I decide if I need to correct pH in the system:

if (((currentpH - previouspH) >= acidOn && (currentpH - previouspH) <= acidOff) || ((currentpH - previouspH) >= acidOn2 && (currentpH - previouspH) <= acidOff2)) {
    acidEnable = 1;
  }
  else {
    acidEnable = 0;
  }

  if ((currentpH - previouspH) >= baseOn && (currentpH - previouspH) <= baseOff) {
    baseEnable = 1;
  }
  else {
    baseEnable = 0;
  }

  if (baseEnable == 1 && ph < baseThreshold) {
    digitalWrite(basePump, HIGH);
    bstatus = 1;
  }

  else if (acidEnable == 1 && ph > acidThreshold) {
    digitalWrite(acidPump, HIGH);
    astatus = 1;
  }


  else {
    digitalWrite(acidPump, LOW);
    digitalWrite(basePump, LOW);
    bstatus = 0;
    astatus = 0;
  }

This is the part where I reset my timing and other variables:

////////////////////////////////RESET VARIABLES FOR MAIN CYCLE////////////////////////////////////////////

  if (currentMillis - previousMillis >= cycle) {
    previousMillis = currentMillis;
    hasFlood = 0;
    //    waterDetect = 0;
  }
}

Notice anything? Yeah, I dont declare that ā€œpreviouspH = currentpHā€ therefore resetting the ph cycleā€¦ Not going to say this is THE problem, but it is definitely a problem. I dont know why, but this part still works, like it is supposed to (maybe the IDE is a mind reader?). I went back into earlier versions of my sketch and found where it was removedā€¦ it was actually after I started having this issue in the first placeā€¦ BUT, the other things I have done could fix the original problem (F macros, removing Strings, writing the HTTP GET in a shorter format <sending ā€œr=0 or 1ā€ instead of ā€œrstatus=recirc or floodā€ therefore reducing the print to the server> and other such things. Ive decided to just jump in to writing several functions and declaring local variables within them, and leaving the ones needed in other functions as globals. I will update when Ive reached nerd level 10 and have uploaded yet ANOTHER version LOL.

It happens when a pump or valve is activated. Hmm, that can cause large current drains on the powersupply or cause elevated voltages in the ground return path. Maybe it is electrical in nature, disturbing the signals to the lcd

Ok, the new code is MUCH smaller than what I started with. Lets explore on the electrical thing. I am running my relay bank, and every single circuit on a separate 5v power supply with grounds tied together. The arduino and the power to pull in the relays is powered by a 12v power supply, its negative is also tied to the others. My outputs run directly to the relay bank however I do have pull down resistors paralleled off of them to ground (10k). I have 2 10k NTC thermistors setup as voltage divider with my A0 and A1 coming from the middle of said voltage divider. ph probe and water detection circuit (npn transistor and some resistors) are also run from the external 5v. The arduino should have no load on it other than what it takes to power the opto isolators on the relay bank and whatever serial data and parallel data to the ph probe and lcd, respectively. Can somebody please look at my ethernet settings pleaseā€¦ I just had another hang up and for laughs disconnected the network cable, this caused the sketch to run. In my client print commands, is there something there that is not right? are the ethernet configurations not correct? something I can get rid of?

Im uploading the new streamlined code to assist.

/*
Version 1.0.0 scraped for new design using recirc valve alone to control flow to growbed
Version 1.1.0 Started over with just code for pH probe. This code was directly from Atlas Scientific.
Version 1.1.1 Assigned float for pH.
version 1.2.0 Added logic for fill and drain functionality.
Version 1.2.1 Added "hasFlood" integer triggered by exceeding "floodThreshold".  This was needed because erronious readings from drainSensor caused this to be a one shot.. this ensures
              that we truly have a drain situation before recirculating.
Version 1.3.0 Added Water level probe function following lessons learned from the flood/drain situation.
Version 1.4.0 Added ability to correct pH during acid/baseOn and acid/baseOff window.  Adjust these windows to add more or less acid/base for correction.
Version 1.4.1 Added a temperature average function to smooth out temperature readings over a second.
Version 1.4.2 Added temperature compensation to pH circuit
Version 1.4.3 Removed response code from ph circuit (circuit was returning *OK everytime we sent it the new temperature. The arduino saw this as the current pH and base pump would run)
Version 1.4.4 Increased Drain threshold to be sure grow bed does drain ( 100 - 1000)
Version 1.5.0 Added SD capabilities.  Will add another version once I get it to initialize
Version 1.5.1 Completed SD capabilities logging an ID, timestamp (in millis), pH, and Temperature to LOG.CSV file
Version 1.6.0 Adding Web Server capabilities
Version 1.6.1 Added more values to be saved in csv file ID, Time In Cycle (mS), pH, Temperature, Acid Pump Status, Base Pump Status, Recirc Status, Drain Sensor, Fill Sensor, Fill Status
Version 2.0.0 Removed self filling capabilities.  This enables me to allow for faster pH corrections since the system naturally buffers itself after 5 minutes and it takes 3 minutes for
              dosing to become apparent in THIS system.  Changing total cycle to 5 minutes to allow dosing to mix within system and for pH to equalize.
Version 2.1.0 Changed main cycle time to 10 minutes since new growbed medium requires more flow time to fill.
Version 2.2.0 Added Real Time Clock capabilities (very buggy)
Version 2.2.1 Bug fixes for Real Time Clock capabilities.  Also added more expainations to most variables.  I was bored on an international flight to Hong Kong from Chicago
Version 3.0.0 Added online data storage capabilities through PHP and MySQL.  Website to follow.  This will allow for monitoring anywhere.
Version 3.0.1 Moved data logging to the loop and added a timer to upload data to MySQL only ever 5 seconds to reduce the amount of data sent to the server. Current settings will use approximately 600MB in a year.
               ***NOTE 8/3/2015 webpage for system is online and operating
Version 3.0.2 Using MYSQL current time and date variables to perform time and date stamps. This should take some load off of the Controller
Version 3.1.0 Noticed that after 12 hours or so the Data logging lags over the web.  Suspect memory capacity on Arduino is being used due to high amounts of data logging. Added an auto reset function to automatically
              reset the controller ever 21600000 milliseconds (6 hours)
Version 3.1.1 Changed reset value to 4 hours instead of 6
Version 3.2.0 Removed reset function. Decided to try only posting to php once instead of twice for each table.  Will modify the add.php on the webserver to query the database to both tables instead of a separate
              php file and multiple lines of code on Arduino.  This, hopefully, should eliminate the memory issues and conflicts.
Version 3.2.1 Removed the datastring to be written to the csv file.  Apparently, strings are really bad for RAM and the issue I am having is caused by running out of RAM. Also, Some of the small integers were changed to bytes
              for the same reason.
Version 3.6.0 Changed the method of sending data to MySQL from "GET" to "POST"
Version 3.6.1 Changed cycle back to 15 minutes due to new growbed being larger.  The system would barely begin to recirculate before the end of the cycle.
Version 4.0.0 Added LCD
Version 4.0.1 Worked on variables trying to make changes to these variables faster and requiring less memory
Version 4.0.2 Disabled SD card.  Depending on testing, this may be permanent.
Version 4.0.3 Added Arduino Watchdog function to detect if arduino has hung up.  It will then reset the system.
Version 4.1.0 Add alarms to be published to the web site
Version 4.1.1 Were not going to talk about this one.  I added a BUNCH of Serial.print commands trying to diagnose "the problem"
Version 4.1.2 Added Close connection messages to the webserver
Version 4.1.2 Removed the php client timeout function.
Version 4.2.0 Moved a bunch of the display logic to the webserver.  this enables me to just send a 1 or 0 to the server instead of a whole string of data. For Example, one alarm
              required a HTTP GET sting over 180 characters long, now this is done with only 6 :)
Version 4.4.0 Wrote Several functions to reduce the number of global variables
*/

// WHAT LIBRARIES ARE TO BE USED
#include <SPI.h>
#include <Ethernet.h>
#include <Time.h>
#include <EthernetUdp.h>
#include <LiquidCrystalFast.h>

// LETS SETUP SOME VALUES

// MUST REMAIN GLOBAL!!
byte astatus = 0;                                        // STRING FOR RUN/OFF FOR ACID PUMP FOR MYSQL
byte bstatus = 0;                                        // STRING FOR RUN/OFF FOR BASE PUMP FOR MYSQL
byte rstatus = 0;                                        // STRING FOR RECIRC/FLOOD FOR RECIRC FOR MYSQL
byte dsensor = 0;                                        // STRING FOR WATER/DRY FOR DRAIN FOR MYSQL
byte fsensor = 0;                                        // STRING FOR WATER/DRY FOR FILL FOR MYSQL
byte fstatus = 0;                                        // STRING FOR FILL/OFF FOR DRAIN FOR MYSQL
byte plow = 0;
byte phigh = 0;
byte wlow = 0;
byte whigh = 0;
byte flood = 0;
float rounded_up;
float roundedforph;
float grounded_up;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
byte hasFlood = 0;                                           // HAS THERE BEEN A GROWBED FLOOD WITHIN THE MAIN CYCLE
byte recircEnable = 0;                                       // ARE WE WITHIN THE RECIRCULATION WINDOW OF TIME
unsigned long ntpLastUpdate = 0;
unsigned long previousMillis = 0;                            // ASSIGNS 0 TO PREVIOUS TIMING ON INTIALIZATION
unsigned long currentMillis;
float ph;                                                   // CREATION OF A PH FLOAT
float dissolved = 0.00;                                        // CRATEION OF A DISSOLVED OXYGEN FLOAT***this circuit does not exist...YET!****

//NETWORKING VARIABLES
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};         // MAC ADDRESS OF ETHERNET CARD
byte ip[] = {192, 168, 2, 7};                               // IP ADDRESS OF ETHERNET CARD
byte gateway[] = {192, 168, 2, 1};
byte subnet[] = {255, 255, 255, 0};
EthernetUDP Udp;
EthernetClient client;
IPAddress timeServer(132, 163, 4, 101);                      // time-a.timefreq.bldrdoc.gov
// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov UNSWITCH COMMENT TO ACTIVATE THIS SERVER
// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov UNSWITCH COMMENT TO ACTIVATE THIS SERVER

//LCD VALUES
const int numRows = 4;
const int numCols = 20;
LiquidCrystalFast lcd(33, 35, 31, 29, 27, 25, 23);

void setup() {                                             // set up the hardware
  Serial.begin(38400);                                                       //set baud rate for the hardware serial port_0 to 9600
  Serial3.begin(38400);                                                      //set baud rate for software serial port_3 to 9600
  lcd.begin(numCols, numRows);
  lcd.setCursor(0, 0);
  lcd.print(F("Initializing System"));
  Serial.println(F("Initializing Card"));
  lcd.setCursor(0, 1);
  lcd.print(F("MCM-APC-55 VER-4.4.0"));
  Serial.println(F("McMillin Aquaponics version 4.4.0"));
  int i = 0;
  int DHCP = 0;
  DHCP = Ethernet.begin(mac);
  while (DHCP == 0 && i < 30) {
    delay(1000);
    DHCP = Ethernet.begin(mac);
    i++;
  }
  if (!DHCP) {
    Serial.println(F("DHCP FAILED"));
    for (;;);
  }
  Serial.print(F("IP number assigned by DHCP is "));
  Serial.println(Ethernet.localIP());

  int trys = 0;
  while (!getTimeAndDate() && trys < 10) {
    trys++;
  }

  delay(1000);
  lcd.clear();
  lcd.setCursor(6, 1);
  lcd.print(F("SETUP DONE"));
  Serial.println(F("Setup done"));
  Serial3.println(F("RESPONSE,0\r"));
  delay(100);
  Serial3.print(F("C,0")); // Set the pH board to single reading mode
  Serial3.print(F("\r")); // Send carriage return to end command
  delay(1000);
  lcd.clear();
}

void loop() {                        //here we go....
  currentMillis = millis();
  Ethernet.maintain();
  unsigned int ntpSyncTime = 7200;

  if (now() - ntpLastUpdate > ntpSyncTime) {
    int trys = 0;
    while (!getTimeAndDate() && trys < 10) {
      trys++;
    }
    if (trys < 10) {
      Serial.println(F("ntp server update success"));
    }
    else {
      Serial.println(F("ntp server update failed"));
    }
  }
  maincycle();

  temps();

  phCorrect();
  recircFlood();
  declareAlarms();
  logit();
  updatentptime();
}


void maincycle() {
  const long cycle = 900000;                                   // MILLISECONDS TOTAL IN MAIN CYCLE

  if (currentMillis - previousMillis >= cycle) {
    previousMillis = currentMillis;
    hasFlood = 0;
    lcd.clear();
  }
}

void phanddo() {
  char stamp_data_ph[30]; // char array for serial data from ph stamp
  byte holding; // counter for serial chars
  byte i; //for loop counter

  Serial3.print(F("T,"));
  Serial3.print(roundedforph);
  Serial3.print(F("\r"));
  // delay(100);
  Serial3.print(F("R"));                                       // Send water temp for calibrated pH reading
  Serial3.print(F("\r"));                                      // Send carriage return to end command
  holding = 0;                                              // Reset buffer counter
  if (Serial3.available() > 1) {                            // If there is serial data in buffer
    holding = Serial3.available();                          // Set buffer counter to number of bytes
    for (i = 0; i < holding; i++) {                         // For loop to read out number of bytes in buffer
      stamp_data_ph[i] = Serial3.read();                    // Read serial data to char array
    }
  }
  else {
    Serial.println(F("pH ERR "));                           // Error if no data rx in serial buffer
  }
  ph = atof(stamp_data_ph);                                 // Write pH data to ph float for mathlcd.setCursor(0, 2);
  lcd.setCursor(0, 2);
  lcd.print(F("pH:"));
  lcd.setCursor(4, 2);
  lcd.print(ph);
  lcd.setCursor(8, 2);
  lcd.print(F("  DO:  "));
  lcd.setCursor(15, 2);
  lcd.print(dissolved);
}

void logit() {
  unsigned long previousLogRate = 0;                           // ASSIGNS 0 TO PREVIOUS LOG RATE ON INITIALIZATION
  const short logRate = 5000;                                   // MILLISECONDS BETWEEN WRITING NEW LINE TO CSV LOGFILE
  unsigned long currentLogRate;

  currentLogRate = millis();
  if ((currentLogRate - previousLogRate) >= logRate) {
    //  writesd();
    sendDataToPHP();                                          // Call function to send data to webserver
    previousLogRate = currentLogRate;
  }
}

void temps() {
  unsigned long currentRate;
  unsigned long previousRate = 0;                              // ASSIGNS 0 TO PREVIOUS RATE ON INITIALIZATION
  const short rate = 1000;                                      // MILLISECONDS BETWEEN TEMPERATURE READINGS
  long Resistance;
  long gResistance;
  double wavgADC;
  double gavgADC;
  double wtemp;
  double gtemp;
  double wtempA;
  double gtempA;
  int wRawADC = analogRead(A0);
  int gRawADC = analogRead(A1);
  int tempReadings = 0;                                       // ACCUMULATION OF NUMBER OF TEMPERATURE READINGS IN "RATE"
  int tempTotal = 0;
  int gtempTotal = 0;
  int gtempReadings = 0;

  currentRate = millis();

  tempTotal = tempTotal + wRawADC;
  tempReadings = tempReadings + 1;

  gtempTotal = gtempTotal + gRawADC;
  gtempReadings = gtempReadings + 1;


  if ((currentRate - previousRate) > rate) {
    wavgADC = 0;
    wavgADC = tempTotal / tempReadings;
    Resistance = ((10240000 / wavgADC) - 10000);
    wtemp = log(Resistance);
    wtemp = 1 / (0.001129148 + (0.000234125 * wtemp) + (0.0000000876741 * wtemp * wtemp * wtemp));
    wtemp = wtemp - 273.15;  // Convert Kelvin to Celsius

    wtempA = (wtemp * 1.8) + 32.0; // Convert to Fahrenheit
    roundedforph = ceilf(wtemp * 100) / 100;
    rounded_up = ceilf(wtempA * 100) / 100;

    gavgADC = 0;
    gavgADC = gtempTotal / gtempReadings;
    gResistance = ((10240000 / gavgADC) - 10000);
    gtemp = log(gResistance);
    gtemp = 1 / (0.001129148 + (0.000234125 * gtemp) + (0.0000000876741 * gtemp * gtemp * gtemp));
    gtemp = gtemp - 273.15;  // Convert Kelvin to Celsius

    gtemp = (gtemp * 1.8) + 32.0; // Convert to Fahrenheit
    grounded_up = ceilf(gtemp * 100) / 100;

    lcd.setCursor(0, 3);
    lcd.print(F("W:"));
    lcd.setCursor(2, 3);
    lcd.print(rounded_up);
    lcd.write((char)223);
    lcd.print(F("F"));
    lcd.setCursor(9, 3);
    lcd.print(F("  B:"));
    lcd.setCursor(13, 3);
    lcd.print(grounded_up);
    lcd.write((char)223);
    lcd.print(F("F"));
    phanddo();
    writeLCD();
    previousRate = currentRate;
    tempTotal = 0;
    tempReadings = 0;
    gtempTotal = 0;
    gtempReadings = 0;
  }
}

void declareAlarms() {
  float phUpper = 8.00;
  float phLower = 6.30;
  float tempUpper = 86.00;
  float tempLower = 70.00;

  if (hasFlood == 0 && recircEnable == 1) {
    flood = 1;
  }
  else {
    flood = 0;
  }
  if (ph >= phUpper) {
    phigh = 1;
  }
  else {
    phigh = 0;
  }
  if (ph <= phLower) {
    plow = 1;
  }
  else {
    plow = 0;
  }
  if (rounded_up >= tempUpper) {
    whigh = 1;
  }
  else {
    whigh = 0;
  }
  if (rounded_up <= tempLower) {
    wlow = 1;
  }
  else {
    wlow = 0;
  }
}

void recircFlood() {
  byte recirc = 46;                                       // RECIRC VALVE OUTPUT ON PIN 48
  pinMode(recirc, OUTPUT);
  int floodReading = analogRead(A2);
  const long recircOn = 601000;                                // MILLISECONDS IN CYCLE TO ENABLE WATER RECIRCULATION (IF NEEDED)
  const long recircOff = 899999;                               // MILLISECONDS IN CYCLE TO DISABLE WATER RECIRCULATION (IF NEEDED)

  if (floodReading < 250) {
    dsensor = 0;
  }
  else {
    dsensor = 1;
  }
  if (floodReading <= 25) {
    hasFlood = 1;
  }

  if ((currentMillis - previousMillis) >= recircOn && (currentMillis - previousMillis) <= recircOff) {
    recircEnable = 1;
  }
  else {
    recircEnable = 0;
  }

  if (hasFlood == 1 || recircEnable == 1) {
    digitalWrite(recirc, HIGH);
    rstatus = 1;
    lcd.setCursor(0, 1);
    lcd.print(F("    RECIRCULATING   "));
  }
  else {
    digitalWrite(recirc, LOW);
    rstatus = 0;
    lcd.setCursor(0, 1);
    lcd.print(F("      FLOODING      "));
  }
}

void phCorrect() {
  pinMode(44, OUTPUT);
  pinMode(42, OUTPUT);
  const long acidOn = 882000;                                  // MILLISECONDS IN CYCLE TO ENABLE ACID DOSING (IF NEEDED)
  const long acidOff = 890000;                                 // MILLISECONDS IN CYCLE TO DISABLE ACID DOSING (IF NEEDED)
  const long baseOn = 870000;                                  // MILLISECONDS IN CYCLE TO ENABLE BASE DOSING (IF NEEDED)
  const long baseOff = 890000;                                 // MILLISECONDS IN CYCLE TO DISABLE BASE DOSING (IF NEEDED)
  byte acidEnable = 0;                                         // ARE WE WITHIN THE ACID DOSING WINDOW OF TIME
  byte baseEnable = 0;                                         // ARE WE WITHIN THE BASE DOSING WINDOW OF TIME
  float acidThreshold = 7.10;                                 // KEEP PH BELOW THIS VALUE BUT...
  float baseThreshold = 6.70;                                 // ABOVE THIS VALUE


  if ((currentMillis - previousMillis) >= acidOn && (currentMillis - previousMillis) <= acidOff) {
    acidEnable = 1;
  }
  else {
    acidEnable = 0;
  }

  if ((currentMillis - previousMillis) >= baseOn && (currentMillis - previousMillis) <= baseOff) {
    baseEnable = 1;
  }
  else {
    baseEnable = 0;
  }

  if (baseEnable == 1 && ph < baseThreshold) {
    digitalWrite(42, HIGH);
    bstatus = 1;
  }

  else if (acidEnable == 1 && ph > acidThreshold) {
    digitalWrite(44, HIGH);
    astatus = 1;
  }


  else {
    digitalWrite(44, LOW);
    digitalWrite(42, LOW);
    bstatus = 0;
    astatus = 0;
  }
}

void writeLCD() {

  lcd.setCursor(0, 0);
  if (month() < 10) {
    lcd.print(F("0"));
    lcd.print(month());
  }
  else {
    lcd.print(month());
  }
  lcd.print(F("-"));
  if (day() < 10) {
    lcd.print(F("0"));
    lcd.print(day());
  }
  else {
    lcd.print(day());
  }
  lcd.print(F("-"));
  lcd.print(year());
  lcd.print(F("  "));
  lcd.setCursor(12, 0);
  if (hour() < 10) {
    lcd.print(F("0"));
    lcd.print(hour());
  }
  else {
    lcd.print(hour());
  }
  lcd.print(F(":"));
  if (minute() < 10) {
    lcd.print(F("0"));
    lcd.print(minute());
  }
  else {
    lcd.print(minute());
  }
  lcd.print(F(":"));
  if (second() < 10) {
    lcd.print(F("0"));
    lcd.print(second());
  }
  else {
    lcd.print(second());
  }
}
void sendDataToPHP() {
  char server[] = "my host";                            // IP Address (or name) of server to dump data to

  if (client.connect(server, 80)) {
    client.print(F( "GET /add.php?"));
    client.print(F("d=CURDATE()"));
    client.print(F("&&"));
    client.print(F("t=CURTIME()"));
    client.print(F("&&"));
    client.print(F("p="));
    client.print( ph );
    client.print(F("&&"));
    client.print(F("do="));
    client.print( dissolved );
    client.print(F("&&"));
    client.print(F("w="));
    client.print( rounded_up );
    client.print(F("&&"));
    client.print(F("g="));
    client.print( grounded_up );
    client.print(F("&&"));
    client.print(F("a="));
    client.print(astatus);
    client.print(F("&&"));
    client.print(F("b="));
    client.print(bstatus);
    client.print(F("&&"));
    client.print(F("r="));
    client.print(rstatus);
    client.print(F("&&"));
    client.print(F("ds="));
    client.print(dsensor);
    client.print(F("&&"));
    client.print(F("f="));
    client.print(fsensor);
    client.print(F("&&"));
    client.print(F("pl="));
    client.print(plow);
    client.print(F("ph="));
    client.print(phigh);
    client.print(F("wl="));
    client.print(wlow);
    client.print(F("wh="));
    client.print(whigh);
    client.print(F("fd="));
    client.print(flood);
    client.println(F( " HTTP/1.1"));
    client.println(F( "Host: my host" ));
    client.println(F("Connection: close\r\n"));
    client.stop();
  }
  else
  {
    Serial.println(F("failed"));
  }
}

int getTimeAndDate() {
  const long timeZoneOffset = -18000L;                         // WHAT TIME ZONE SET TO Central Daylight Time
  int flag = 0;
  unsigned int localPort = 8888;                               // local port to listen for UDP packets

  Udp.begin(localPort);
  sendNTPpacket(timeServer);
  delay(1000);
  if (Udp.parsePacket()) {
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
    unsigned long highWord, lowWord, epoch;
    highWord = word(packetBuffer[40], packetBuffer[41]);
    lowWord = word(packetBuffer[42], packetBuffer[43]);
    epoch = highWord << 16 | lowWord;
    epoch = epoch - 2208988800 + timeZoneOffset;
    flag = 1;
    setTime(epoch);
    ntpLastUpdate = now();
  }
  return flag;
}

unsigned long sendNTPpacket(IPAddress & address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

Since relays, pumps and valves (solenoids) are inductive devices they need fly-back diodes to redirect the current back to the beginning of the coil once the switch is opening. Or this is going to create hefty voltage spikes on the switching element. Whatever itā€™s type is. You havenā€™t mentioned that in your description yet. So show more details about that.

https://www.youtube.com/watch?v=LXGtE3X2k7Y

https://www.youtube.com/watch?v=c6I7Ycbv8B8

p.s. To eliminate the possibility of the Ethernet code being the culprit, why no just make a new program that only writes stuff to the lcd, and turns on the pumps and valves or what not. If the lcd display corruption occurs then you know it cannot be caused by ethernet/networking related stuff. The code is quite long and complicated to worm through. Reduce the code to the essentials and see if the bug is still there. :wink:

The outputs are all run through an opto isolated relay board.

How do I run 2 programs on one arduino?

adamlwvdc36:
The outputs are all run through an opto isolated relay board.

Ok, one can assume this is engineered properly with the necessary protective measures. But assuming something is sometimes the root of a problem. Any details about which one this is?

How do I run 2 programs on one arduino?

You donā€™t need to. Just write a new test program and upload it instead.

https://www.amazon.com/gp/aw/d/B00QBYND ā€¦ ref=plSrch

That is the exact relay board.