Skip to content

Instantly share code, notes, and snippets.

@Nisden
Last active December 14, 2015 22:39
Show Gist options
  • Save Nisden/5159963 to your computer and use it in GitHub Desktop.
Save Nisden/5159963 to your computer and use it in GitHub Desktop.
Web Sever with NTP
#include "SPI.h"
#include "Ethernet.h"
#include "WebServer.h" // https://github.com/sirleech/Webduino
#include "Clock.h"; // http://www.blueleafsoftware.com/Resources/EmbeddedSand/Clock_library
byte ArduinoMAC[] = {0x90, 0xA2, 0xDA, 0x0D, 0xB7, 0xFA};
IPAddress ArduinoIP(10, 15, 11, 12);
Clock g_Clock;
WebServer webserver("", 80);
void indexCmd(WebServer &server, WebServer::ConnectionType type, char *, bool)
{
// Send Success
server.httpSuccess();
/* if we're handling a GET or POST, we can output our data here.
For a HEAD request, we just stop after outputting headers. */
if (type != WebServer::HEAD)
{
DateTime dt;
g_Clock.DecodeTo(dt);
// Time
P(timeMessage) = "<h1>The time is: </h1>";
server.printP(timeMessage);
server.print(dt.Hour);
server.print(":");
server.print(dt.Minute);
// Date
P(dateMessage) = "<h1>The date is: </h1>";
server.printP(dateMessage);
server.print(dt.Day);
server.print("-");
server.print(dt.Month);
server.print("-");
server.print(dt.Year);
}
}
void setup()
{
Serial.begin(9600);
// Initialize Ethernet.
Ethernet.begin(ArduinoMAC, ArduinoIP);
// Time
g_Clock.Setup();
g_Clock.SetTimezoneOffset(1, 0);
// Web Server commands
webserver.setDefaultCommand(&indexCmd);
webserver.addCommand("index.html", &indexCmd);
// Begin
webserver.begin();
}
void loop()
{
char buff[64];
int len = 64;
// Process web
webserver.processConnection(buff, &len);
// Process time
g_Clock.Maintain();
}
/* *********************************************************************
Simple class that provides the current time & date. At intervals,
the network time protocol (NTP) is used to obtain the time from
a time server. Between synchronization, the arduino's millisecond
counter is used to extrapolate the current time.
NTP query based on code from Arduino Ethernet library.
Conversion between Unix time & natural time represenation based on
code by Michael Margolis.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
Visit http://www.MegunoLink.com/Libraries for the latest version.
********************************************************************* */
#include "clock.h"
Clock::Clock() :
mc_nLocalUDPPort(8880),
mc_ulRefreshInterval(5UL * 60UL * 60UL * 1000UL), // normally every 5 hours
//mc_ulRefreshInterval(20 * 1000UL), // every 10 seconds for debuggin
mc_ulRetryInterval(5 * 1000UL),
mc_TimeServer(65,55,21,21) // time.nist.gov NTP server.
{
// Force refresh of NTP time in a second.
m_ulLastAcquired = millis() - (mc_ulRefreshInterval + 1000);
m_ulLastNTPTime = 0;
m_ulLastAttempt = 0;
// Assume zero offset for timezone.
m_nTimezoneOffset = 0;
}
void Clock::Setup()
{
}
void Clock::Maintain()
{
unsigned long ulCurrent;
// Update NTP time if we haven't acquired it for a while &&
// it has been a reasonable time since the last attempt.
ulCurrent = millis();
if (ulCurrent - m_ulLastAcquired > mc_ulRefreshInterval &&
(m_ulLastAttempt == 0 || ulCurrent - m_ulLastAttempt > mc_ulRetryInterval))
RefreshNTPTime();
}
unsigned long Clock::GetTimestamp()
{
unsigned long ulCurrentOffset;
if (m_ulLastNTPTime == 0)
return 0;
ulCurrentOffset = millis() - m_ulLastAcquired;
ulCurrentOffset = (ulCurrentOffset + 500) / 1000; // Round to nearest second.
ulCurrentOffset += m_nTimezoneOffset * 60L; // adjust for timezone [mins].
return ulCurrentOffset + m_ulLastNTPTime;
}
void Clock::WriteTime(HardwareSerial *pPort)
{
unsigned long ulTimestamp;
ulTimestamp = GetTimestamp();
pPort->print((ulTimestamp % 86400L)/3600); // Print hour
pPort->print(':');
if ( ((ulTimestamp % 3600) / 60) < 10 ) // print leading 0 for first 10 min of each hour
pPort->print('0');
pPort->print((ulTimestamp % 3600) / 60); // print the minute (3600 equals secs per minute)
pPort->print(':');
if ( (ulTimestamp % 60) < 10 ) // print leading 0 for first 10 sec of each min.
pPort->print('0');
pPort->print(ulTimestamp %60); // print the second
}
void Clock::WriteDateTime(HardwareSerial *pPort)
{
DateTime dt;
DecodeTo(dt);
pPort->print(dt.Year);
pPort->print('/');
if (dt.Month < 10)
pPort->print('0');
pPort->print(dt.Month);
pPort->print('/');
if (dt.Day < 10)
pPort->print('0');
pPort->print(dt.Day);
pPort->print(' ');
if (dt.Hour < 10)
pPort->print('0');
pPort->print(dt.Hour);
pPort->print(':');
if (dt.Minute < 10)
pPort->print('0');
pPort->print(dt.Minute);
pPort->print(':');
if (dt.Second < 10)
pPort->print('0');
pPort->print(dt.Second);
}
void Clock::DecodeTo(DateTime &dt)
{
unsigned long ulCurrent, ulDays;
byte abyDaysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31};
ulCurrent = GetTimestamp(); // Seconds since 1 Jan 1970
dt.Second = ulCurrent % 60;
ulCurrent /= 60; // now minutes since 1 Jan 1970
dt.Minute = ulCurrent % 60;
ulCurrent /= 60; // hours
dt.Hour = ulCurrent % 24;
ulCurrent /= 24; // now days.
// Determine year by counting days, taking care of leap years.
dt.Year = 1970;
ulDays = 0;
for(ulDays = 0, dt.Year = 1970; ulDays <= ulCurrent; ++dt.Year)
ulDays += IsLeapYear(dt.Year) ? 366 : 365;
--dt.Year; // we went one too far.
ulDays -= IsLeapYear(dt.Year) ? 366 : 365;
ulCurrent -= ulDays; // Now days this year, starting from 0.
// Make an adjustment for feb, if this is a leap year, then figure
// out which month we are in.
if (IsLeapYear(dt.Year))
abyDaysPerMonth[1] = 29;
for(dt.Month = 0, ulDays = 0; dt.Month < 12 && ulDays <= ulCurrent; ++dt.Month)
ulDays += abyDaysPerMonth[dt.Month];
dt.Day = ulCurrent - (ulDays - abyDaysPerMonth[dt.Month - 1]) + 1;
}
void Clock::SetTimezoneOffset(int nHours, int nMinutes)
{
m_nTimezoneOffset = nHours * 60 + nMinutes;
}
void Clock::RefreshNTPTime()
{
EthernetUDP UdpConnection;
int nByte;
unsigned long ul1900Seconds;
#ifdef DEBUG
Serial.println("Refreshing NTPTime");
#endif
UdpConnection.begin(mc_nLocalUDPPort);
SendNTPPacket(UdpConnection, mc_TimeServer);
delay(1000); // Wait for response
if (UdpConnection.parsePacket())
{
#ifdef DEBUG
Serial.println("NTPTime response received");
#endif
// Dump the first 40 bytes.
for(nByte = 0; nByte < 40; ++nByte)
UdpConnection.read();
// next four bytes are 64 bit integer time stamp of
// seconds since 1 Jan 1900.
ul1900Seconds = 0;
for(nByte = 0; nByte < 4; ++nByte)
ul1900Seconds = (ul1900Seconds<<8) | UdpConnection.read();
// Convert from NTP time basis to Unix time basis (seconds
// since 1 Jan 1970) & save. Record the machine time when
// this time was recorded to extrapolate the current time.
m_ulLastNTPTime = ul1900Seconds - 2208988800UL;
m_ulLastAcquired = millis();
m_ulLastAttempt = 0; // success.
} else
{
m_ulLastAttempt = millis(); // Will retry a little later.
#ifdef DEBUG
Serial.println("NTPTime no-response received");
#endif
}
UdpConnection.stop();
}
void Clock::SendNTPPacket(EthernetUDP &rUDP, const IPAddress &rAddress)
{
const int cnNTPPacketSize = 48;
static byte abyPacketBuffer[cnNTPPacketSize] PROGMEM =
{ 0b11100011, // LI, Version, Mode
0, // Stratum, or type of clock
6, // Polling Interval
0xEC, // Peer Clock Precision
0, 0, 0, 0, 0, 0, 0, 0, // 8 bytes of zero for Root Delay & Root Dispersion
49,
0x4E,
49,
52,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
};
byte byValue, iByte;
rUDP.beginPacket(rAddress, 123);
for (iByte = 0; iByte < cnNTPPacketSize; ++iByte)
{
byValue = pgm_read_byte_near(abyPacketBuffer+iByte);
rUDP.write(byValue);
}
rUDP.endPacket();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment