I have uploaded a video to YouTube of my system. You can check it out if you want. [youtube]https://www.youtube.com/watch?v=RRdf3rdoOTY[/youtube]
I know my code is long (although not as long as yours) but if you could go through and see if there are any places I could save on either memory or process time and still have it function I would greatly appreciate it. I have the SD card disabled at this time since more detailed information is available on the server.
Code:
/*
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.
*/
// 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
// TIMING VARIABLES
const int timeZone = -5; // WHAT TIME ZONE SET TO Central Daylight Time
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 = 301000; // 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
EthernetUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
EthernetClient client;
char server[] = "myserver"; // 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 = 48; // RECIRC VALVE OUTPUT ON PIN 48
byte drainSensor = 49; // DRAIN SENSOR INPUT ON PIN 49
byte levelSensor = 47; // LEVEL SENSOR INPUT ON PIN 47
byte fillValve = 46; // FILL VALVE OUTPUT ON
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 acidStatus = 0; // IS ACID PUMP RUNNING
byte baseStatus = 0; // IS BASE PUMP RUNNING
byte recircStatus = 0; // IS WATER RECIRCULATING
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 = 10000; // HOW HIGH DOES drainSensorVal HAVE TO BE TO DECLARE FLOOD IN GROWBED
int 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
char phchar[6];
char phphp[6] = "00.00";
char tempchar[6];
char gtempchar[6];
char dochar[6] = "0.00";
char astatus[4] = ""; // STRING FOR RUN/OFF FOR ACID PUMP FOR MYSQL
char bstatus[4] = ""; // STRING FOR RUN/OFF FOR BASE PUMP FOR MYSQL
char rstatus[7] = ""; // STRING FOR RECIRC/FLOOD FOR RECIRC FOR MYSQL
char dsensor[6] = ""; // STRING FOR WATER/DRY FOR DRAIN FOR MYSQL
char fsensor[6] = "Water"; // STRING FOR WATER/DRY FOR FILL FOR MYSQL
char fstatus[5] = "Off"; // STRING FOR FILL/OFF FOR DRAIN FOR MYSQL
char rstatuslcd[21] = "";
char timelcd[9] = "";
char datelcd[11] = "";
char params[200] = "";
int inChar;
float ph; // CREATION OF A PH FLOAT
float dissolved = 0; // 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
time_t prevDisplay = 0; // when the digital clock was displayed
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
pinMode(drainSensor, INPUT);
pinMode(levelSensor, INPUT);
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.write("Initializing Card");
Serial.println("Initializing Card");
lcd.setCursor(0, 1);
lcd.write("MCM-APC-55 VER-4.0.0");
Serial.println("McMillin Aquaponics version 4.0.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);
if (Ethernet.begin(mac) == 0) {
// no point in carrying on, so do nothing forevermore:
while (1) {
Serial.println("Failed to configure Ethernet using DHCP");
delay(10000);
}
}
Serial.print("IP number assigned by DHCP is ");
Serial.println(Ethernet.localIP());
Udp.begin(localPort);
// Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
//////////////////////////////////////////////
// takes a second for the w5100 to get ready
delay(1000);
lcd.clear();
lcd.setCursor(6, 1);
lcd.write("SETUP DONE");
Serial.println("Setup done");
Serial3.println("RESPONSE,0\r");
delay(100);
Serial3.print("C,0"); // Set the pH board to single reading mode
Serial3.print("\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....
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();
}
}
*/
sprintf(datelcd, "%02d-%02d-%4d", month(), day(), year());
sprintf(timelcd, "%02d:%02d:%02d", hour(), minute(), second());
////////////////////////GROW BED DRAIN DETECTION AND RECIRCULATION////////////////////////////////////////
if (drainSensorVal == 0) {
sprintf(dsensor, "Water");
}
else {
sprintf(dsensor, "Dry");
}
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);
recircStatus = 1;
sprintf(rstatus, "Recirc");
sprintf(rstatuslcd, " RECIRCULATING ");
//Serial.println("Recirculating");
}
else {
digitalWrite(recirc, LOW);
recircStatus = 0;
sprintf(rstatus, "Flood");
sprintf(rstatuslcd, " FLOODING ");
//Serial.println("Filling Grow Bed");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////WRITE TO SD CARD AND WEBSERVER/////////////////////////////////////////////
if ((currentLogRate - previousLogRate) >= logRate) {
// writesd();
sendDataToPHP(); // Call function to send data to webserver
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);
baseStatus = 1;
sprintf(bstatus, "Run");
}
else if (acidEnable == 1 && ph > acidThreshold) {
digitalWrite(acidPump, HIGH);
acidStatus = 1;
sprintf(astatus, "Run");
}
else {
digitalWrite(acidPump, LOW);
digitalWrite(basePump, LOW);
baseStatus = 0;
acidStatus = 0;
sprintf(astatus, "Off");
sprintf(bstatus, "Off");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////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;
float 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;
dtostrf(rounded_up, 5, 2, tempchar);
dtostrf(grounded_up, 5, 2, gtempchar);
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////SEND TEMP TO PH CIRCUIT AND GET PH/////////////////////////////////////////////////
Serial3.print("T,");
Serial3.print(avgForpH);
Serial3.print("\r");
// delay(100);
Serial3.print("R"); // Send water temp for calibrated pH reading
Serial3.print("\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 math
dtostrf(ph, 4, 2, phphp);
dtostrf(ph, 5, 2, phchar); // Write pH data to phchar for LCD display
//////////////////////////////////////////////////////////////////////////////////////////////////////////
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);
lcd.write(datelcd);
lcd.write(" ");
lcd.setCursor(12, 0);
lcd.write(timelcd);
lcd.setCursor(0, 1);
lcd.write(rstatuslcd);
lcd.setCursor(0, 2);
lcd.write("pH:");
lcd.setCursor(3, 2);
lcd.write(phchar);
lcd.setCursor(8, 2);
lcd.write(" DO:");
lcd.setCursor(15, 2);
lcd.write(dochar);
lcd.setCursor(0, 3);
lcd.write("W:");
lcd.setCursor(2, 3);
lcd.write(tempchar);
lcd.write((char)223);
lcd.write("F");
lcd.setCursor(9, 3);
lcd.write(" B:");
lcd.setCursor(13, 3);
lcd.write(gtempchar);
lcd.write((char)223);
lcd.write("F");
}
void sendDataToPHP() {
if (client.connect(server, 80)) {
client.print( "GET /addrecent.php?");
client.print("date=CURDATE()");
client.print("&&");
client.print("time=CURTIME()");
client.print("&&");
client.print("ph=");
client.print( ph );
client.print("&&");
client.print("dissolved=");
client.print( dissolved );
client.print("&&");
client.print("wtemp=");
client.print( tempAverage );
client.print("&&");
client.print("gtemp=");
client.print( gtempAverage );
client.print("&&");
client.print("astatus=");
client.print( astatus );
client.print("&&");
client.print("bstatus=");
client.print( bstatus );
client.print("&&");
client.print("rstatus=");
client.print( rstatus );
client.print("&&");
client.print("dsensor=");
client.print( dsensor );
client.print("&&");
client.print("fsensor=");
client.print( fsensor );
client.print("&&");
client.print("fstatus=");
client.print( fstatus );
client.println( " HTTP/1.1");
client.println( "Host: myserver" );
client.println( "Content-Type: application/x-www-form-urlencoded" );
client.println();
client.println();
}
else
{
Serial.println(F("failed"));
}
int connectLoop = 0;
while (client.connected())
{
while(client.available())
{
inChar = client.read();
Serial.write(inChar);
connectLoop = 0;
}
client.stop();
connectLoop++;
if (connectLoop > 10000)
{
Serial.println();
Serial.println(F("Timeout"));
client.stop();
}
}
}
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
}
void printDigits(int digits) {
// utility for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 3600000) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress & address)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}