Skip to content

Commit cedf93f

Browse files
committed
Switch from RTC to millis for intra-sync timekeeping
1 parent fb1419c commit cedf93f

File tree

1 file changed

+69
-96
lines changed

1 file changed

+69
-96
lines changed

arduino-ledclock.ino

Lines changed: 69 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of th
1818
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
1919
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
2020

21-
////////// RTC //////////
22-
#include <RTCZero.h>
23-
RTCZero rtc;
21+
////////// FAKE RTC using millis //////////
22+
unsigned long todMils = 0; //time of day in milliseconds
2423
#define OFFSET -21600 //we are this many seconds behind UTC
24+
#define ANTI_DRIFT 0 //msec to add per second - or seconds to add per day divided by 86.4
25+
//Doesn't have to be perfect – just enough for decent timekeeping display until the next ntp sync
2526

2627
////////// DISPLAY //////////
2728
#define NUM_MAX 4
@@ -37,16 +38,15 @@ LedControl lc=LedControl(DIN_PIN,CLK_PIN,CS_PIN,NUM_MAX);
3738
void setup() {
3839
Serial.begin(9600); while (!Serial) {;}
3940
startWiFi();
40-
rtc.begin();
4141
for(int i=0; i<NUM_MAX; i++) { lc.shutdown(i,false); lc.setIntensity(i,8); }
4242
//TODO create a new connection to server and close it every time? is that necessary for UDP? what about TCP?
4343
Serial.println("\nStarting connection to server...");
4444
Udp.begin(localPort);
45-
//startNTP(); //checkRTC will handle this - except when repowering
45+
startNTP();
4646
}
4747

4848
void loop() {
49-
checkRTC();
49+
checkRTC(false);
5050
checkNTP();
5151
}
5252

@@ -101,11 +101,12 @@ void printWiFiStatus() {
101101

102102
////////// NTP //////////
103103
bool checkingNTP = false;
104+
unsigned long checkNTPStart = 0;
104105

105106
void startNTP(){ //Called at intervals to check for ntp time
106107
if(!checkingNTP) {
107-
Serial.println(F("NTP call started"));
108108
checkingNTP = true;
109+
checkNTPStart = millis();
109110
sendNTPpacket(timeServer); // send an NTP packet to a time server
110111
}
111112
} //end fn startNTP
@@ -142,8 +143,6 @@ unsigned long sendNTPpacket(IPAddress& address) {
142143

143144
void checkNTP(){ //Called on every cycle to see if there is an ntp response to handle
144145
if(checkingNTP && Udp.parsePacket()) {
145-
Serial.println(F("NTP packet received"));
146-
checkingNTP = false;
147146
// We've received a packet, read the data from it
148147
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
149148

@@ -155,77 +154,69 @@ void checkNTP(){ //Called on every cycle to see if there is an ntp response to h
155154
// combine the four bytes (two words) into a long integer
156155
// this is NTP time (seconds since Jan 1 1900):
157156
unsigned long secsSince1900 = highWord << 16 | lowWord;
158-
//Serial.print("Seconds since Jan 1 1900 = ");
159-
//Serial.println(secsSince1900);
160-
161-
// now convert NTP time into everyday time:
162-
//Serial.print("Unix time = ");
163-
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
164-
const unsigned long seventyYears = 2208988800UL;
165-
// subtract seventy years:
166-
unsigned long epoch = secsSince1900 - seventyYears;
167-
168-
rtc.setEpoch(epoch+OFFSET);
157+
//TODO I suppose secsSince1900 has been adjusted for leap seconds?
158+
secsSince1900 += OFFSET;
159+
160+
unsigned int requestTime = millis()-checkNTPStart; //this handles rollovers OK b/c 2-(max-2) = 4
161+
162+
unsigned long newtodMils = (secsSince1900%86400)*1000+(requestTime/2); //TODO plus mils? //add half the request time
163+
unsigned int drift = todMils-newtodMils;
164+
todMils = newtodMils;
165+
166+
Serial.print(F("NTP request took ")); Serial.print(requestTime,DEC); Serial.println(F("ms."));
167+
Serial.print(F("RTC is off by ")); Serial.print(drift); Serial.println(F(" (sec res and request time notwithstanding)."));
169168
Serial.print(F("RTC set to ")); printRTCTime(); Serial.println();
169+
checkingNTP = false;
170+
checkRTC(true);
170171
}
171172
} //end fn checkNTP
172173

173174

174175
////////// RTC and NTP //////////
175176
byte rtcSecLast = 61;
176-
void checkRTC(){
177-
//we can't do a snapshot but should be ok because only triggered after a seconds value change
178-
if(rtcSecLast != rtc.getSeconds()){ //A new second! Do stuff!
179-
rtcSecLast = rtc.getSeconds();
180-
//Trigger time update at top of each minute
181-
if(rtc.getSeconds()==0 && rtc.getMinutes()%5==0){
182-
Serial.print(F("According to RTC, it's ")); printRTCTime(); Serial.println(F(" - time for an NTP check"));
177+
unsigned long millisLast = 0;
178+
bool justAppliedAntiDrift = 0;
179+
void checkRTC(bool justSet){
180+
if(!justSet){ //if we haven't just set the fake "rtc", let's increment it
181+
unsigned long millisSnap = millis();
182+
todMils += millisSnap-millisLast;
183+
millisLast=millisSnap;
184+
}
185+
int rtcMin = ((todMils/1000)/60)%60;
186+
int rtcSec = (todMils/1000)%60;
187+
if(rtcSecLast != rtcSec || justSet){ //A new second!
188+
if(!justSet && !justAppliedAntiDrift){ //Apply anti-drift each time we naturally reach a new second
189+
//If it's negative, we'll set todMils back and set a flag, so we don't do it again at the upcoming "real" new second.
190+
//We shouldn't need to check if we'll be setting it back past zero, as we won't have done the midnight rollover yet (below)
191+
//and at the first second past midnight, todMils will almost certainly be larger than ANTI_DRIFT.
192+
todMils += ANTI_DRIFT;
193+
if(ANTI_DRIFT<0) justAppliedAntiDrift = 1;
194+
} else justAppliedAntiDrift = 0;
195+
//Now we can assume todMils is accurate
196+
if(todMils>86400000) todMils-=86400000; //is this a new day? set it back 24 hours
197+
if(!justSet && rtcSec==0 && rtcMin==0){ //Trigger NTP update at top of the hour
198+
Serial.println(F("Time for an NTP check"));
183199
startNTP();
184200
}
185-
//Display time on LEDs
186-
displayHMS(rtc.getHours(),rtc.getMinutes(),rtc.getSeconds());
201+
displayTime(); //Display time on LEDs
202+
rtcSecLast = rtcSec; //register that we have done stuff
187203
} //end if new second
188204
} //end fn checkRTC
189205

190206
void printRTCTime(){
191-
// Serial.print(rtc.getYear());
192-
// Serial.print("/");
193-
// Serial.print(rtc.getMonth());
194-
// Serial.print("/");
195-
// Serial.print(rtc.getDay());
196-
// Serial.print(" ");
197-
if(rtc.getHours()<10) Serial.print(F("0"));
198-
Serial.print(rtc.getHours()); //gmt/offset malarkey
199-
Serial.print(F(":"));
200-
if(rtc.getMinutes()<10) Serial.print(F("0"));
201-
Serial.print(rtc.getMinutes());
202-
Serial.print(F(":"));
203-
if(rtc.getSeconds()<10) Serial.print(F("0"));
204-
Serial.print(rtc.getSeconds());
207+
unsigned long todSecs = todMils/1000;
208+
int rtcHrs = (todMils/1000)/3600; //rtc.getHours() - 43195/3600 = 11hrs
209+
int rtcMin = ((todMils/1000)/60)%60; //rtc.getMinutes() - (43195/60)=719min, 719%60=59min
210+
int rtcSec = (todMils/1000)%60; //rtc.getSeconds() - 43195%60 = 55sec
211+
if(rtcHrs<10) Serial.print(F("0")); Serial.print(rtcHrs); Serial.print(F(":"));
212+
if(rtcMin<10) Serial.print(F("0")); Serial.print(rtcMin); Serial.print(F(":"));
213+
if(rtcSec<10) Serial.print(F("0")); Serial.print(rtcSec);
205214
}
206215

207216

208217
////////// LED DISPLAY //////////
209218

210-
// //Binary counter display test
211-
// byte lastAddr = NUM_MAX-1;
212-
// byte lastCol = 7;
213-
// byte lastCount = 0; //max 255
214-
// void testDisplay(){
215-
// while(true){
216-
// lastAddr++; if(lastAddr>=NUM_MAX){
217-
// lastAddr=0; lastCount++; if(lastCount>=255){
218-
// lastCount=0;
219-
// }
220-
// }
221-
// lastCol++; if(lastCol>=8) lastCol=0;
222-
// Serial.print(F("Setting ")); Serial.print(lastAddr,DEC);
223-
// Serial.print(F("/")); Serial.print(lastCol,DEC);
224-
// Serial.print(F(" to ")); Serial.println(lastCount,DEC);
225-
// lc.setColumn((NUM_MAX-1)-lastAddr,lastCol,lastCount);
226-
// delay(250);
227-
// }
228-
// }
219+
//See commit fb1419c for binary counter display test
229220

230221
byte smallnum[30]={
231222
B11111100, B10000100, B11111100, // 0
@@ -252,40 +243,22 @@ byte bignum[50]={
252243
B00001110, B10011111, B11010001, B01111111, B00111110 // 9
253244
};
254245

255-
void displayHMS(int h, int m, int s){
256-
if(h>24 || h<0) h=0;
257-
if(m>60 || m<0) m=0;
258-
if(s>60 || s<0) s=0;
259-
int ci = (NUM_MAX*8)-1; //column index
260-
//display = (NUM_MAX-1)-(ci/8)
261-
//dispcol = ci%8
246+
void displayTime(){
247+
unsigned long todSecs = todMils/1000; if(todSecs>=86400) todSecs=0;
248+
int rtcHrs = (todMils/1000)/3600; //rtc.getHours() - 43195/3600 = 11hr
249+
int rtcMin = ((todMils/1000)/60)%60; //rtc.getMinutes() - (43195/60)=719min, 719%60=59min
250+
int rtcSec = (todMils/1000)%60; //rtc.getSeconds() - 43195%60 = 55sec
251+
int ci = (NUM_MAX*8)-1; //total column index - we will start at the last one and move backward
252+
//display index = (NUM_MAX-1)-(ci/8)
253+
//display column index = ci%8
262254
//big numbers are 5 pixels wide; small ones are 3 pixels wide
263-
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(h/10)*5+i]); ci--; } ci--; //h tens + 1 col gap
264-
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(h%10)*5+i]); ci--; } ci--; ci--; //h ones + 2 col gap
265-
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(m/10)*5+i]); ci--; } ci--; //m tens + 1 col gap
266-
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(m%10)*5+i]); ci--; } ci--; //m ones + 1 col gap
267-
for(int i=0; i<3; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, smallnum[(s/10)*3+i]); ci--; } ci--; //s tens + 1 col gap
268-
for(int i=0; i<3; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, smallnum[(s%10)*3+i]); ci--; } //s ones
255+
if(rtcHrs<10) ci-=5; else //leading blank instead of zero
256+
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(rtcHrs/10)*5+i]); ci--; } ci--; //h tens + 1col gap
257+
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(rtcHrs%10)*5+i]); ci--; } ci--; ci--; //h ones + 2col gap
258+
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(rtcMin/10)*5+i]); ci--; } ci--; //m tens + 1col gap
259+
for(int i=0; i<5; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, bignum[(rtcMin%10)*5+i]); ci--; } ci--; //m ones + 1col gap
260+
for(int i=0; i<3; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, smallnum[(rtcSec/10)*3+i]); ci--; } ci--; //s tens + 1col gap
261+
for(int i=0; i<3; i++){ lc.setColumn((NUM_MAX-1)-(ci/8),ci%8, smallnum[(rtcSec%10)*3+i]); ci--; } //s ones
269262
}
270263

271-
272-
// //Serial input control if we need it for live config
273-
// void setup(){
274-
// Serial.begin(9600); while(!Serial){ ; }
275-
// Serial.println("Hello world!");
276-
// }
277-
// int incomingByte = 0;
278-
// void loop(){
279-
// if(Serial.available()>0){
280-
// incomingByte = Serial.read();
281-
// switch(incomingByte){
282-
// case 96: clear
283-
// Serial.println
284-
// break;
285-
// default: break;
286-
// }
287-
// Serial.print("I received: ");
288-
// Serial.println(incomingByte, DEC);
289-
// }
290-
// }
291-
//
264+
//See commit fb1419c for serial input control if needed for runtime config

0 commit comments

Comments
 (0)