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 ). 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);
}