Skip to content

Instantly share code, notes, and snippets.

@fpoto
Last active December 26, 2015 14:09
Show Gist options
  • Save fpoto/7163904 to your computer and use it in GitHub Desktop.
Save fpoto/7163904 to your computer and use it in GitHub Desktop.
Demonstration of the WiFiUdp.write() bug posted as issue #1642. Works when allTogether is set to true, fails when set to false.
#include <WiFi.h>
#include <WiFiUdp.h>
unsigned long inline ntpUnixTime ();
char ssid[] = "xxxxxxxx"; // your network SSID (name)
void setup()
{
Serial.begin(115200);
Serial.print("Attempting to connect to SSID: ");
Serial.print(ssid);
if (WiFi.begin(ssid) == WL_CONNECTED)
Serial.println(" connected");
else {
Serial.println(" failed");
while (true) ;
}
}
void loop()
{
unsigned long unixTime;
for (int requests = 0; requests < 5; ++requests) {
unixTime = ntpUnixTime(); // send NTP request and get response
if (! unixTime)
Serial.println("request failed");
else {
Serial.print("UTC time: ");
Serial.println(unixTime);
}
delay(2000); // wait for a while before next request
}
while (true) ; // stop here
}
/*
* © Francesco Potortì 2013 - GPLv3 - $Revision: 1.10 $
*
* Send an NTP packet and wait for the response, return the Unix time
*
* To lower the memory footprint, no buffers are allocated for sending
* and receiving the NTP packets. Four bytes of memory are allocated
* for transmision, the rest is random garbage collected from the data
* memory segment, and the received packet is read one byte at a time.
* Time returned is the Unix time, that is, seconds from 1970-01-01.
*/
unsigned long inline ntpUnixTime ()
{
// Statically allocate a WiFiUdp object and initialise it once.
// It would be better to allocate it dynamically and use close()
// on it when exiting the function, can't until bug #1637 is solved.
// For Ethernet, use EthernetUDP rather than WiFiUdp.
static WiFiUDP udp;
static int udpInited = udp.begin(1963); // open socket
const char timeServer[] = "pool.ntp.org"; // NTP server
// Only the first four bytes of an outgoing NTP packet need to be set
// appropriately, the rest can be whatever.
const long ntpFirstFourBytes = 0xEC0600E3; // NTP request header
byte buf[48];
// Fail if WiFiUdp.begin() could not init a socket
if (! udpInited)
return 0;
// Clear received data from possible stray received packets
udp.flush();
// Send an NTP request
udp.beginPacket(timeServer, 123); // 123 is the NTP port
*(long *)buf = ntpFirstFourBytes;
# define allTogether true
# if allTogether
udp.write(buf, 48);
#else
for (byte i = 0; i < 48; ++i)
udp.write(buf[i]);
#endif
if (udp.endPacket())
Serial.println("endPacket successful");
else {
Serial.println("endPacket failed");
while (true) ;
}
// Wait for response; check every pollIntv ms up to maxPoll times
const int pollIntv = 150; // poll every this many ms
const byte maxPoll = 15; // poll up to this many times
int pktLen; // received packet length
for (byte i=0; i<maxPoll; i++) {
if ((pktLen = udp.parsePacket()) == 48)
break;
delay(pollIntv);
}
if (pktLen != 48)
return 0; // no correct packet received
// Read and discard the first useless bytes
// We use the server receive time if we want to save 8 udp.read()
// else we use the server send time which is closest to the real time.
// Set useless to 32 for speed; set to 40 for accuracy.
const byte useless = 32;
for (byte i = 0; i < useless; ++i)
udp.read();
// Read the integer part of sending time
unsigned long time = udp.read(); // NTP time
for (byte i = 1; i < 4; i++)
time = time << 8 | udp.read();
// Round to the nearest second if we want accuracy
if (useless == 40) // we want accuracy
// The fractionary part is the next byte divided by 256: if it is
// greater than 500ms we round to the next second; we also account
// for an assumed network delay of 50ms, and (0.5-0.05)*256=115;
// additionally, we account for how much we delayed reading the packet
// since its arrival, which we assume on average to be pollIntv/2.
time += (udp.read() > 115 - pollIntv/8);
// Discard the rest of the packet
udp.flush();
return time - 2208988800ul; // convert NTP time to Unix time
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment