This is an routine to obtain the Unix time by accessing a web server. This is an alternative to using an NTP client that has the same performance as long as no sub-second accuracy is needed. The idea is that we access a web server and read the Date:
header that HTTP 1.1 compliant server will have to send.
This server uses very little memory and can easily be adapted to cooperative scheduling environments.
HTTP 1.1 mandates servers to send a Date: header in a standard format. This header must be sent even if the server receives an invalid request, as long as it is recognised as an HTTP 1.1 request. So we just send a line followed by two carriage return - newline sequences and read the answer, looking for a line beginning with Date:
. The request is built like this:
GET / HTTP/1.1
The resulting answer is parsed with code that is very small and efficient.
Putting a yield()
call after each WiFiUdp.method()
call will make this code friendly to cooperative scheduler environments.
For WiFi shield:
{ static WiFiClient client; unsigned long unixTime = webUnixTime(client); }
For Ethernet shield:
{ EthernetClient client; unsigned long unixTime = webUnixTime(client); }
/* * © Francesco Potortì 2013 - GPLv3 * * Send an HTTP packet and wait for the response, return the Unix time */ unsigned long webUnixTime (Client &client) { unsigned long time = 0; // Just choose any reasonably busy web server, the load is really low if (client.connect("g.cn", 80)) { // Make an HTTP 1.1 request which is missing a Host: header // compliant servers are required to answer with an error that includes // a Date: header. client.print(F("GET / HTTP/1.1 \r\n\r\n")); char buf[5]; // temporary buffer for characters client.setTimeout(5000); if (client.find((char *)"\r\nDate: ") // look for Date: header && client.readBytes(buf, 5) == 5) // discard { unsigned day = client.parseInt(); // day client.readBytes(buf, 1); // discard client.readBytes(buf, 3); // month int year = client.parseInt(); // year byte hour = client.parseInt(); // hour byte minute = client.parseInt(); // minute byte second = client.parseInt(); // second int daysInPrevMonths; switch (buf[0]) { case 'F': daysInPrevMonths = 31; break; // Feb case 'S': daysInPrevMonths = 243; break; // Sep case 'O': daysInPrevMonths = 273; break; // Oct case 'N': daysInPrevMonths = 304; break; // Nov case 'D': daysInPrevMonths = 334; break; // Dec default: if (buf[0] == 'J' && buf[1] == 'a') daysInPrevMonths = 0; // Jan else if (buf[0] == 'A' && buf[1] == 'p') daysInPrevMonths = 90; // Apr else switch (buf[2]) { case 'r': daysInPrevMonths = 59; break; // Mar case 'y': daysInPrevMonths = 120; break; // May case 'n': daysInPrevMonths = 151; break; // Jun case 'l': daysInPrevMonths = 181; break; // Jul default: // add a default label here to avoid compiler warning case 'g': daysInPrevMonths = 212; break; // Aug } } // This code will not work after February 2100 // because it does not account for 2100 not being a leap year and because // we use the day variable as accumulator, which would overflow in 2149 day += (year - 1970) * 365; // days from 1970 to the whole past year day += (year - 1969) >> 2; // plus one day per leap year day += daysInPrevMonths; // plus days for previous months this year if (daysInPrevMonths >= 59 // if we are past February && ((year & 3) == 0)) // and this is a leap year day += 1; // add one day // Remove today, add hours, minutes and seconds this month time = (((day-1ul) * 24 + hour) * 60 + minute) * 60 + second; } } delay(10); client.flush(); client.stop(); return time; }