@@ -18,10 +18,11 @@ const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of th
18
18
byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
19
19
WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
20
20
21
- // //////// RTC //////////
22
- #include < RTCZero.h>
23
- RTCZero rtc;
21
+ // //////// FAKE RTC using millis //////////
22
+ unsigned long todMils = 0 ; // time of day in milliseconds
24
23
#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
25
26
26
27
// //////// DISPLAY //////////
27
28
#define NUM_MAX 4
@@ -37,16 +38,15 @@ LedControl lc=LedControl(DIN_PIN,CLK_PIN,CS_PIN,NUM_MAX);
37
38
void setup () {
38
39
Serial.begin (9600 ); while (!Serial) {;}
39
40
startWiFi ();
40
- rtc.begin ();
41
41
for (int i=0 ; i<NUM_MAX; i++) { lc.shutdown (i,false ); lc.setIntensity (i,8 ); }
42
42
// TODO create a new connection to server and close it every time? is that necessary for UDP? what about TCP?
43
43
Serial.println (" \n Starting connection to server..." );
44
44
Udp.begin (localPort);
45
- // startNTP(); //checkRTC will handle this - except when repowering
45
+ startNTP ();
46
46
}
47
47
48
48
void loop () {
49
- checkRTC ();
49
+ checkRTC (false );
50
50
checkNTP ();
51
51
}
52
52
@@ -101,11 +101,12 @@ void printWiFiStatus() {
101
101
102
102
// //////// NTP //////////
103
103
bool checkingNTP = false ;
104
+ unsigned long checkNTPStart = 0 ;
104
105
105
106
void startNTP (){ // Called at intervals to check for ntp time
106
107
if (!checkingNTP) {
107
- Serial.println (F (" NTP call started" ));
108
108
checkingNTP = true ;
109
+ checkNTPStart = millis ();
109
110
sendNTPpacket (timeServer); // send an NTP packet to a time server
110
111
}
111
112
} // end fn startNTP
@@ -142,8 +143,6 @@ unsigned long sendNTPpacket(IPAddress& address) {
142
143
143
144
void checkNTP (){ // Called on every cycle to see if there is an ntp response to handle
144
145
if (checkingNTP && Udp.parsePacket ()) {
145
- Serial.println (F (" NTP packet received" ));
146
- checkingNTP = false ;
147
146
// We've received a packet, read the data from it
148
147
Udp.read (packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
149
148
@@ -155,77 +154,69 @@ void checkNTP(){ //Called on every cycle to see if there is an ntp response to h
155
154
// combine the four bytes (two words) into a long integer
156
155
// this is NTP time (seconds since Jan 1 1900):
157
156
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). " ) );
169
168
Serial.print (F (" RTC set to " )); printRTCTime (); Serial.println ();
169
+ checkingNTP = false ;
170
+ checkRTC (true );
170
171
}
171
172
} // end fn checkNTP
172
173
173
174
174
175
// //////// RTC and NTP //////////
175
176
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" ));
183
199
startNTP ();
184
200
}
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
187
203
} // end if new second
188
204
} // end fn checkRTC
189
205
190
206
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);
205
214
}
206
215
207
216
208
217
// //////// LED DISPLAY //////////
209
218
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
229
220
230
221
byte smallnum[30 ]={
231
222
B11111100, B10000100, B11111100, // 0
@@ -252,40 +243,22 @@ byte bignum[50]={
252
243
B00001110, B10011111, B11010001, B01111111, B00111110 // 9
253
244
};
254
245
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
262
254
// 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
269
262
}
270
263
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