... | ... |
@@ -2,14 +2,14 @@ |
2 | 2 |
Background Radiation Monitor - Web Server |
3 | 3 |
|
4 | 4 |
A simple web server that makes available to clients over the Internet |
5 |
- readings from a MightyOhm Geiger counter. The MightyOhm is connected to |
|
6 |
- an Arduino Uno with attached Ethernet shield. This software module |
|
5 |
+ readings from a MightyOhm Geiger counter. The MightyOhm is connected |
|
6 |
+ to an Arduino Uno with attached Ethernet shield. This software module |
|
7 | 7 |
runs on the Arduino Uno an embedded HTTP server by which Internet |
8 | 8 |
applications can query the MightyOhm for Geiger counter readings. |
9 | 9 |
Also, this software runs a Network Time Protocol (NTP) client, that |
10 | 10 |
periodically synchronizes the local system clock to network time. |
11 |
- Included is a simple command line interface that may be used to change the |
|
12 |
- network interface IP address, NTP server address, or configure a |
|
11 |
+ Included is a simple command line interface that may be used to change |
|
12 |
+ the network interface IP address, NTP server address, or configure a |
|
13 | 13 |
verbose output mode. |
14 | 14 |
|
15 | 15 |
Copyright 2018 Jeff Owrey |
... | ... |
@@ -47,7 +47,7 @@ |
47 | 47 |
Revision History: |
48 | 48 |
* v10 released 25 Feb 2014 by J L Owrey |
49 | 49 |
* v11 released 24 Jun 2014 by J L Owrey |
50 |
- - optimization of processRxByte function to conserve SRAM |
|
50 |
+ - optimization of processByteFromMightyOhm function to conserve SRAM |
|
51 | 51 |
- removal of non-used function code |
52 | 52 |
- defaults to APIPA IP address in the event a DHCP address |
53 | 53 |
cannot be obtained |
... | ... |
@@ -69,6 +69,21 @@ |
69 | 69 |
* v16 released 16 Sep 2017 by J L Owrey |
70 | 70 |
- added capability of rebooting via network http request, |
71 | 71 |
i.e., "http://{device IP address}/reset" |
72 |
+ * v17 released 29 Oct 2019 by J L Owrey |
|
73 |
+ - modified NTP server address user setting to allow fully |
|
74 |
+ qualified domain names as well as IP addresses. Default |
|
75 |
+ NTP address set to "pool.ntp.org" per ntp.org request to use |
|
76 |
+ (in order to facilitate load balancing) the fully qualified |
|
77 |
+ domain name instead of individual server IP addresses. |
|
78 |
+ * v18 released 01 Nov 2019 by J L Owrey |
|
79 |
+ - fixed a bug in NTP time synchronization whereby the network time |
|
80 |
+ synchronization would only occur during boot up. Thereafter NTP |
|
81 |
+ time synchronization would fail to happen, resulting in a large |
|
82 |
+ amount of clock drift. |
|
83 |
+ * v19 released 10 Jul 2022 by J L Owrey |
|
84 |
+ - improved processing of serial data from the MightyOhm Geiger counter |
|
85 |
+ |
|
86 |
+12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
72 | 87 |
*/ |
73 | 88 |
|
74 | 89 |
/*** PREPROCESSOR DEFINES ***/ |
... | ... |
@@ -79,8 +94,8 @@ |
79 | 94 |
Define the header and version number displayed at startup |
80 | 95 |
and also by the 'view settings' command. |
81 | 96 |
*/ |
82 |
-#define STARTUP_HEADER "\n\rRadmon v1.6 (c) 2018\n" |
|
83 |
-#define RADMON_VERSION "v1.6" |
|
97 |
+#define STARTUP_HEADER "\n\rRadmon v1.9 (c) 2022" |
|
98 |
+#define RADMON_VERSION "v1.9" |
|
84 | 99 |
/* |
85 | 100 |
The following define sets the MAC address of the device. This |
86 | 101 |
address is a permanent attribute of the device's Ethernet interface, |
... | ... |
@@ -110,18 +125,23 @@ |
110 | 125 |
/* |
111 | 126 |
The following defines are for configuring a local NTP client |
112 | 127 |
for synchronizing the local system clock to network time. |
113 |
- Note that the default setting is the IP address of the following |
|
114 |
- time server: |
|
115 |
- time-c-b.nist.gov |
|
128 |
+ Note that the ntp server address should be sent to the local |
|
129 |
+ server pool of the country where the radmon will be used. See |
|
130 |
+ the web site 'ntp.org' for details. Users in the USA should set |
|
131 |
+ the ntp server to 'us.pool.ntp.org'. |
|
116 | 132 |
*/ |
117 |
-#define DEFAULT_NTP_SERVER_IP_ADDR "132.163.96.3" |
|
133 |
+#define DEFAULT_NTP_SERVER_ADDR "us.pool.ntp.org" |
|
118 | 134 |
#define NTP_PORT 8888 |
119 |
-#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message |
|
135 |
+#define NTP_PACKET_SIZE 48 // NTP time in the first 48 bytes of the message |
|
120 | 136 |
/* |
121 | 137 |
The following defines how often the system clock gets synchronized |
122 | 138 |
to network time. |
123 | 139 |
*/ |
124 |
-#define NET_SYNCH_INTERVAL 43200 //number in seconds |
|
140 |
+#define NTP_SYNCH_INTERVAL 43200 // number in seconds - 2 times a day |
|
141 |
+/* |
|
142 |
+ Number of retries if first time server request fails. |
|
143 |
+*/ |
|
144 |
+#define TIME_SERVER_REQUEST_RETRIES 3 |
|
125 | 145 |
/* |
126 | 146 |
The following defines the size of the buffer space required for the |
127 | 147 |
serial data string from the Mighty Ohm Geiger counter. The serial |
... | ... |
@@ -178,9 +198,9 @@ SoftwareSerial MightyOhmTxOut(5, 6); |
178 | 198 |
Create global variables to store the MightOhm data, next heartbeat |
179 | 199 |
time, and next synchronization time. |
180 | 200 |
*/ |
181 |
-char mightOhmData[MIGHTYOHM_DATA_STRING_LENGTH + 1]; |
|
182 |
-unsigned long lastSerialUpdateTime; |
|
183 |
-time_t nextClockSynchTime; |
|
201 |
+char mightyOhmData[MIGHTYOHM_DATA_STRING_LENGTH + 1]; |
|
202 |
+unsigned long nextSerialUpdateTime = 0; |
|
203 |
+unsigned long nextClockSynchTime = 0; |
|
184 | 204 |
/* |
185 | 205 |
Create global variables to store the verbose mode state (ON or OFF) |
186 | 206 |
and the IP address mode state (static or DHCP). |
... | ... |
@@ -189,10 +209,10 @@ boolean bVerbose; |
189 | 209 |
boolean bUseStaticIP; |
190 | 210 |
/* |
191 | 211 |
Create and initialize global arrays to hold the current IP address |
192 |
- and the NTP server IP address. |
|
212 |
+ and the NTP server address. |
|
193 | 213 |
*/ |
194 | 214 |
byte ipAddr[4]; |
195 |
-byte ntpIpAddr[4]; |
|
215 |
+char timeServer[32]; |
|
196 | 216 |
|
197 | 217 |
/*** SYSTEM STARTUP ***/ |
198 | 218 |
|
... | ... |
@@ -215,11 +235,13 @@ void setup() |
215 | 235 |
Start up the Ethernet interface using either a static or |
216 | 236 |
DHCP supplied address (depending on stored system configuration). |
217 | 237 |
*/ |
218 |
- if(bUseStaticIP) |
|
238 |
+ if (bUseStaticIP) |
|
219 | 239 |
{ |
220 | 240 |
Ethernet.begin(mac, ipAddr); |
221 |
- } else { |
|
222 |
- if ( Ethernet.begin(mac) == 0 ) |
|
241 |
+ } |
|
242 |
+ else |
|
243 |
+ { |
|
244 |
+ if (Ethernet.begin(mac) == 0) |
|
223 | 245 |
{ |
224 | 246 |
/* DHCP not responding so use APIPA address */ |
225 | 247 |
parseIpAddress(ipAddr, DEFAULT_APIPA_IP_ADDRESS); |
... | ... |
@@ -228,14 +250,11 @@ void setup() |
228 | 250 |
} |
229 | 251 |
} |
230 | 252 |
Serial.print(F("IP address: ")); Serial.println(Ethernet.localIP()); |
231 |
- /* |
|
232 |
- Start up NTP client service. |
|
233 |
- */ |
|
234 |
- Udp.begin(NTP_PORT); |
|
235 | 253 |
/* |
236 | 254 |
Synchronize the system clock to network time. |
237 | 255 |
*/ |
238 | 256 |
synchronizeSystemClock(); |
257 |
+ nextClockSynchTime = now() + NTP_SYNCH_INTERVAL; |
|
239 | 258 |
/* |
240 | 259 |
Start up the HTTP server. |
241 | 260 |
*/ |
... | ... |
@@ -245,33 +264,23 @@ void setup() |
245 | 264 |
Open serial communications to the MightyOhm device. |
246 | 265 |
*/ |
247 | 266 |
MightyOhmTxOut.begin(9600); |
248 |
- /* |
|
249 |
- Initialize initial time for sending out the hearbeat string. Normally |
|
250 |
- the system clock will be at approx 3200 msec at this point. So allow |
|
251 |
- some additional time for data to accumulate in MightyOhm data buffer. |
|
252 |
- */ |
|
253 |
- lastSerialUpdateTime = -1; |
|
254 | 267 |
/* |
255 | 268 |
Initialize MightyOhm data string to empty. |
256 | 269 |
*/ |
257 |
- mightOhmData[0] = 0; |
|
270 |
+ mightyOhmData[0] = 0; |
|
258 | 271 |
return; |
259 | 272 |
} |
260 | 273 |
|
261 | 274 |
/*** MAIN LOOP ***/ |
262 | 275 |
|
263 | 276 |
void loop() { |
264 |
- long currentTime; |
|
265 |
- |
|
266 |
- currentTime = millis(); |
|
267 |
- |
|
268 | 277 |
/* |
269 | 278 |
Check for user keyboard 'c' pressed. This character switches |
270 | 279 |
to command mode. |
271 | 280 |
*/ |
272 |
- if ( Serial.available() ) { |
|
281 |
+ if (Serial.available()) { |
|
273 | 282 |
// get incoming byte |
274 |
- if(Serial.read() == 'c') { |
|
283 |
+ if (Serial.read() == 'c') { |
|
275 | 284 |
commandMode(); |
276 | 285 |
} |
277 | 286 |
} |
... | ... |
@@ -280,8 +289,8 @@ void loop() { |
280 | 289 |
Poll serial input buffer from MightyOhm for new data and |
281 | 290 |
process received bytes to form a complete data string. |
282 | 291 |
*/ |
283 |
- while ( MightyOhmTxOut.available() ) { |
|
284 |
- processRxByte( MightyOhmTxOut.read() ); |
|
292 |
+ while (MightyOhmTxOut.available()) { |
|
293 |
+ processByteFromMightyOhm(MightyOhmTxOut.read()); |
|
285 | 294 |
} |
286 | 295 |
|
287 | 296 |
/* |
... | ... |
@@ -289,9 +298,12 @@ void loop() { |
289 | 298 |
serial port at regular intervals. |
290 | 299 |
*/ |
291 | 300 |
if (bVerbose) { |
292 |
- if (abs(millis() - lastSerialUpdateTime) > SERIAL_UPDATE_INTERVAL) { |
|
293 |
- lastSerialUpdateTime = millis(); |
|
294 |
- Serial.println( mightOhmData ); |
|
301 |
+ if (millis() > nextSerialUpdateTime) { |
|
302 |
+ Serial.println(mightyOhmData); |
|
303 |
+ /* |
|
304 |
+ Set the time for the next serial update to occur. |
|
305 |
+ */ |
|
306 |
+ nextSerialUpdateTime = millis() + SERIAL_UPDATE_INTERVAL; |
|
295 | 307 |
} |
296 | 308 |
} |
297 | 309 |
|
... | ... |
@@ -299,54 +311,29 @@ void loop() { |
299 | 311 |
Periodically synchronize local system clock to time |
300 | 312 |
provided by NTP time server. |
301 | 313 |
*/ |
302 |
- if ( now() > nextClockSynchTime ) { |
|
314 |
+ if (now() > nextClockSynchTime) { |
|
303 | 315 |
synchronizeSystemClock(); |
316 |
+ /* |
|
317 |
+ Set the time for the next network NTP |
|
318 |
+ time synchronization to occur. |
|
319 |
+ */ |
|
320 |
+ nextClockSynchTime = now() + NTP_SYNCH_INTERVAL; |
|
304 | 321 |
} |
305 | 322 |
|
306 | 323 |
/* |
307 | 324 |
Listen for and and process requests from HTTP clients. |
308 | 325 |
*/ |
309 |
- listenForEthernetClients(); |
|
326 |
+ listenForNetworkClients(); |
|
310 | 327 |
|
311 | 328 |
#ifdef DEBUG |
312 | 329 |
Serial.print("lp time: "); Serial.println(millis() - currentTime); |
313 | 330 |
#endif |
314 | 331 |
} |
315 | 332 |
|
316 |
-/* |
|
317 |
- Synchronize the local system clock to |
|
318 |
- network time provided by NTP time server. |
|
319 |
-*/ |
|
320 |
-void synchronizeSystemClock() |
|
321 |
-{ |
|
322 |
- byte count; |
|
323 |
- |
|
324 |
- Serial.println(F("Synchronizing with network time server...")); |
|
325 |
- |
|
326 |
- for(count = 0; count < 3; count++) // Attempt to synchronize 3 times |
|
327 |
- { |
|
328 |
- if(syncToNetworkTime() == 1) |
|
329 |
- { |
|
330 |
- // Synchronization successful |
|
331 |
- break; |
|
332 |
- } |
|
333 |
- delay(1000); |
|
334 |
- } /* end for */ |
|
335 |
- if(count == 3) { |
|
336 |
- Serial.println(F("synch failed")); |
|
337 |
- } |
|
338 |
- /* |
|
339 |
- Set the time for the next network NTP |
|
340 |
- time synchronization to occur. |
|
341 |
- */ |
|
342 |
- nextClockSynchTime = now() + NET_SYNCH_INTERVAL; |
|
343 |
- return; |
|
344 |
-} |
|
345 |
- |
|
346 | 333 |
/* |
347 | 334 |
Handle HTTP GET requests from an HTTP client. |
348 | 335 |
*/ |
349 |
-void listenForEthernetClients() |
|
336 |
+void listenForNetworkClients() |
|
350 | 337 |
{ |
351 | 338 |
// listen for incoming clients |
352 | 339 |
EthernetClient client = httpServer.available(); |
... | ... |
@@ -366,12 +353,12 @@ void listenForEthernetClients() |
366 | 353 |
firstLineFound = false; |
367 | 354 |
|
368 | 355 |
/* |
369 |
- * The beginning and end of an HTTP client request is always signaled |
|
370 |
- * by a blank line, that is, by two consecutive line feed and carriage |
|
371 |
- * return characters "\r\n\r\n". The following lines of code |
|
372 |
- * look for this condition, as well as the url extension (following |
|
373 |
- * "GET"). |
|
374 |
- */ |
|
356 |
+ The beginning and end of an HTTP client request is always signaled |
|
357 |
+ by a blank line, that is, by two consecutive line feed and carriage |
|
358 |
+ return characters "\r\n\r\n". The following lines of code |
|
359 |
+ look for this condition, as well as the url extension (following |
|
360 |
+ "GET"). |
|
361 |
+ */ |
|
375 | 362 |
|
376 | 363 |
while (client.connected()) { |
377 | 364 |
if (client.available()) { |
... | ... |
@@ -407,7 +394,9 @@ void listenForEthernetClients() |
407 | 394 |
i = 1; |
408 | 395 |
} |
409 | 396 |
|
410 |
- if (firstLineFound && (c == '\n' || i > REQUEST_STRING_BUFFER_LENGTH - 2)) { |
|
397 |
+ if (firstLineFound && (c == '\n' || i > |
|
398 |
+ REQUEST_STRING_BUFFER_LENGTH - 2)) |
|
399 |
+ { |
|
411 | 400 |
processedCommand = true; |
412 | 401 |
} |
413 | 402 |
} |
... | ... |
@@ -422,11 +411,14 @@ void listenForEthernetClients() |
422 | 411 |
transmitHttpHeader(client); |
423 | 412 |
|
424 | 413 |
char * pStr = strtok(sBuf, " "); |
425 |
- if (pStr != NULL) { |
|
426 |
- if (strcmp(pStr, "/rdata") == 0) |
|
414 |
+ if (pStr != NULL) |
|
415 |
+ { |
|
416 |
+ if (strcmp(pStr, "/rdata") == 0) { |
|
427 | 417 |
transmitRawData(client); |
428 |
- else if (strcmp(pStr, "/") == 0) |
|
418 |
+ } |
|
419 |
+ else if (strcmp(pStr, "/") == 0) { |
|
429 | 420 |
transmitWebPage(client); |
421 |
+ } |
|
430 | 422 |
else if(strcmp(pStr, "/reset") == 0) { |
431 | 423 |
client.print(F("ok")); |
432 | 424 |
delay(10); |
... | ... |
@@ -434,13 +426,18 @@ void listenForEthernetClients() |
434 | 426 |
client.stop(); |
435 | 427 |
software_Reset(); |
436 | 428 |
} |
437 |
- else |
|
429 |
+ else { |
|
438 | 430 |
transmitErrorPage(client); |
431 |
+ } |
|
439 | 432 |
} |
433 |
+ client.println(); |
|
440 | 434 |
|
441 |
- //Serial.println(mightOhmData); |
|
435 |
+ #ifdef DEBUG |
|
436 |
+ Serial.println(mightyOhmData); //debug |
|
437 |
+ #endif |
|
438 |
+ |
|
442 | 439 |
// give the web browser time to receive the data |
443 |
- delay(10); |
|
440 |
+ delay(20); |
|
444 | 441 |
// close the connection: |
445 | 442 |
client.stop(); |
446 | 443 |
} |
... | ... |
@@ -466,19 +463,19 @@ void transmitHttpHeader(EthernetClient client) { |
466 | 463 |
void transmitWebPage(EthernetClient client) { |
467 | 464 |
char strBuffer[MIGHTYOHM_DATA_STRING_LENGTH]; |
468 | 465 |
|
469 |
- strcpy(strBuffer, mightOhmData); |
|
466 |
+ strcpy(strBuffer, mightyOhmData); |
|
470 | 467 |
/* |
471 |
- * Send the actual HTML page the user will see in their web |
|
472 |
- * browser. |
|
468 |
+ Send the actual HTML page the user will see in their web |
|
469 |
+ browser. |
|
473 | 470 |
*/ |
474 |
- client.print(F("<!DOCTYPE HTML>" \ |
|
471 |
+ client.print(F("<!DOCTYPE HTML>" \ |
|
475 | 472 |
"<html><head><title>Radiation Monitor</title>" \ |
476 |
- "<style>pre {font: 16px arial, sans-serif;}" \ |
|
477 |
- "p {font: 16px arial, sans-serif;}" |
|
478 |
- "h2 {font: 24px arial, sans-serif;}</style>" \ |
|
479 |
- "</head><body><h2>Radiation Monitor</h2>" \ |
|
480 |
- "<p><a href=\"http://intravisions.com/radmon/\">" \ |
|
481 |
- "<i>intravisions.com/radmon</i></a></p>" \ |
|
473 |
+ "<style>pre {font: 16px arial, sans-serif;}" \ |
|
474 |
+ "p {font: 16px arial, sans-serif;}" \ |
|
475 |
+ "h2 {font: 24px arial, sans-serif;}</style>" \ |
|
476 |
+ "</head><body><h2>Radiation Monitor</h2>" \ |
|
477 |
+ "<p><a href=\"http://intravisions.com/\">" \ |
|
478 |
+ "<i>IntraVisions.com</i></a></p>" \ |
|
482 | 479 |
"<hr>")); |
483 | 480 |
/* Data Items */ |
484 | 481 |
client.print(F("<pre>UTC 	")); |
... | ... |
@@ -508,10 +505,10 @@ void transmitWebPage(EthernetClient client) { |
508 | 505 |
void transmitRawData(EthernetClient client) { |
509 | 506 |
char strBuffer[MIGHTYOHM_DATA_STRING_LENGTH]; |
510 | 507 |
|
511 |
- strcpy(strBuffer, mightOhmData); |
|
508 |
+ strcpy(strBuffer, mightyOhmData); |
|
512 | 509 |
/* |
513 |
- * Format and transmit a JSON compatible data string. |
|
514 |
- */ |
|
510 |
+ Format and transmit a JSON compatible data string. |
|
511 |
+ */ |
|
515 | 512 |
client.print(F("$,UTC=")); |
516 | 513 |
client.print(strtok(strBuffer, " ")); |
517 | 514 |
client.print(F(" ")); |
... | ... |
@@ -535,16 +532,13 @@ void transmitRawData(EthernetClient client) { |
535 | 532 |
} |
536 | 533 |
|
537 | 534 |
/* |
538 |
- * Send an error message web page back to the requesting |
|
539 |
- * client when the client provides an invalid url extension. |
|
540 |
- */ |
|
535 |
+ Send an error message web page back to the requesting |
|
536 |
+ client when the client provides an invalid url extension. |
|
537 |
+*/ |
|
541 | 538 |
void transmitErrorPage(EthernetClient client) { |
542 |
- client.print(F("<!DOCTYPE HTML>" \ |
|
539 |
+ client.print(F("<!DOCTYPE HTML>" \ |
|
543 | 540 |
"<html><head><title>Radiation Monitor</title></head>" \ |
544 |
- "<body><h2>Invalid Url</h2>" \ |
|
545 |
- "<p>You have requested a service at an unknown " \ |
|
546 |
- "url.</p><p>If you think you made this request in error, " \ |
|
547 |
- "please disconnect and try your request again.</p>" \ |
|
541 |
+ "<body><h2>404 Not Found</h2>" \ |
|
548 | 542 |
"</body></html>" |
549 | 543 |
)); |
550 | 544 |
} |
... | ... |
@@ -553,10 +547,11 @@ void transmitErrorPage(EthernetClient client) { |
553 | 547 |
Process bytes received from the MightyOhm Geiger counter, |
554 | 548 |
one at a time, to create a well formed string. |
555 | 549 |
*/ |
556 |
-void processRxByte( char RxByte ) |
|
550 |
+void processByteFromMightyOhm( char RxByte ) |
|
557 | 551 |
{ |
558 | 552 |
static char readBuffer[MIGHTYOHM_DATA_STRING_LENGTH]; |
559 | 553 |
static byte cIndex = 0; |
554 |
+ int headerPos = 0; |
|
560 | 555 |
|
561 | 556 |
/* |
562 | 557 |
Discard carriage return characters. |
... | ... |
@@ -573,18 +568,25 @@ void processRxByte( char RxByte ) |
573 | 568 |
else if (RxByte == '\n') |
574 | 569 |
{ |
575 | 570 |
/* |
576 |
- First copy the timestamp to the MightyOhm data buffer. The "CPS" |
|
577 |
- characters are not preserved in the temporary read buffer, so |
|
578 |
- restore them to the MightyOhm data buffer, as well. |
|
571 |
+ If a complete line of data has been received from the MightyOhm |
|
572 |
+ Geiger counter, then add a timestamp and copy the line to the |
|
573 |
+ MightyOhm data buffer. A line is complete if the line |
|
574 |
+ begins with a proper header and ends with a newline character. |
|
579 | 575 |
*/ |
580 |
- sprintf( mightOhmData, "%d:%02d:%02d %d/%d/%d, %s", \ |
|
581 |
- hour(), minute(), second(), month(), day(), year(), \ |
|
582 |
- MIGHTYOHM_DATA_STRING_HEADER ); |
|
583 |
- /* |
|
584 |
- Now copy the rest of the data in the temporary read buffer to the |
|
585 |
- MightyOhm data buffer. |
|
586 |
- */ |
|
587 |
- strcat(mightOhmData, readBuffer); |
|
576 |
+ headerPos = strstr(readBuffer, MIGHTYOHM_DATA_STRING_HEADER) - readBuffer; |
|
577 |
+ if( headerPos == 0 ) |
|
578 |
+ { |
|
579 |
+ /* |
|
580 |
+ Insert a timestamp at the beginning of the MightyOhm data buffer. |
|
581 |
+ */ |
|
582 |
+ sprintf( mightyOhmData, "%d:%02d:%02d %d/%d/%d, ", \ |
|
583 |
+ hour(), minute(), second(), month(), day(), year() ); |
|
584 |
+ /* |
|
585 |
+ Now copy the rest of the data in the temporary read buffer to the |
|
586 |
+ MightyOhm data buffer. |
|
587 |
+ */ |
|
588 |
+ strcat(mightyOhmData, readBuffer); |
|
589 |
+ } |
|
588 | 590 |
/* |
589 | 591 |
Flush the temporary read buffer. |
590 | 592 |
*/ |
... | ... |
@@ -592,18 +594,6 @@ void processRxByte( char RxByte ) |
592 | 594 |
readBuffer[0] = 0; |
593 | 595 |
return; |
594 | 596 |
} |
595 |
- /* |
|
596 |
- A new line of data will always have "CPS" as the first |
|
597 |
- three characters. Therefore, when these characters occur in |
|
598 |
- sequence, the read buffer should begin collecting characters. |
|
599 |
- This is a kluge to deal with an inherent problem in the Software |
|
600 |
- Serial library implementation that results in characters dropped |
|
601 |
- from the software serial stream buffer. |
|
602 |
- */ |
|
603 |
- if( strstr(readBuffer, MIGHTYOHM_DATA_STRING_HEADER) > 0 ) |
|
604 |
- { |
|
605 |
- cIndex = 0; |
|
606 |
- } |
|
607 | 597 |
/* |
608 | 598 |
Read characters into a temporary buffer until |
609 | 599 |
the line of data is complete or the buffer is full. |
... | ... |
@@ -617,6 +607,48 @@ void processRxByte( char RxByte ) |
617 | 607 |
return; |
618 | 608 |
} |
619 | 609 |
|
610 |
+/* |
|
611 |
+ Synchronize the local system clock to |
|
612 |
+ network time provided by NTP time server. |
|
613 |
+*/ |
|
614 |
+void synchronizeSystemClock() |
|
615 |
+{ |
|
616 |
+ byte count; |
|
617 |
+ |
|
618 |
+ Serial.print(F("Synchronizing with NTP server: ")); |
|
619 |
+ Serial.print(timeServer);Serial.println(F("...")); |
|
620 |
+ |
|
621 |
+ /* |
|
622 |
+ * NOTICE!!! NOTICE!!! NOTICE!!! |
|
623 |
+ * Due to a bug in the Ethernet library, it is necessary to reinitialize |
|
624 |
+ * the ethernet UDP library everytime after an after an EthernetClient |
|
625 |
+ * class object has been instantiated. Also, the Udp stop() function |
|
626 |
+ * must be called at the end of each session. |
|
627 |
+ */ |
|
628 |
+ Udp.begin(NTP_PORT); // see above comment |
|
629 |
+ |
|
630 |
+ count = 1; |
|
631 |
+ while (true) // Attempt to synchronize 3 times |
|
632 |
+ { |
|
633 |
+ if (syncToNetworkTime() == 1) { |
|
634 |
+ // Synchronization successful |
|
635 |
+ break; |
|
636 |
+ } |
|
637 |
+ if (count == TIME_SERVER_REQUEST_RETRIES) { |
|
638 |
+ Serial.print(F("synch failed: ")); |
|
639 |
+ break; |
|
640 |
+ } |
|
641 |
+ count++; |
|
642 |
+ delay(2000); |
|
643 |
+ } |
|
644 |
+ if (count > 1) { |
|
645 |
+ Serial.print(count);Serial.println(F(" retries")); |
|
646 |
+ } |
|
647 |
+ |
|
648 |
+ Udp.stop(); // see above comment |
|
649 |
+ return; |
|
650 |
+} |
|
651 |
+ |
|
620 | 652 |
/* |
621 | 653 |
Send a UDP request packet to an NTP time server and listen for a reply. |
622 | 654 |
When the reply arrives, parse the received UPD packet and compute unix |
... | ... |
@@ -625,19 +657,20 @@ void processRxByte( char RxByte ) |
625 | 657 |
int syncToNetworkTime() |
626 | 658 |
{ |
627 | 659 |
/* |
628 |
- Send a request to the NTP time server. |
|
660 |
+ Send a request to the NTP time server. Define a buffer to hold outgoing |
|
661 |
+ and incoming packets. |
|
629 | 662 |
*/ |
630 |
- byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold outgoing and incoming packets |
|
663 |
+ byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold packets |
|
631 | 664 |
/* |
632 | 665 |
Send an NTP packet to the time server and allow for network lag |
633 | 666 |
before checking if a reply is available. |
634 | 667 |
*/ |
635 |
- sendNTPpacket(packetBuffer); |
|
636 |
- delay(2000); // allow 2000 milli-seconds for network lag |
|
637 |
- |
|
668 |
+ sendNTPpacket(timeServer, packetBuffer); |
|
638 | 669 |
/* |
639 | 670 |
Wait for response from NTP time server. |
640 | 671 |
*/ |
672 |
+ delay(1000); // allow 1000 milli-seconds for network lag |
|
673 |
+ |
|
641 | 674 |
if ( Udp.parsePacket() ) |
642 | 675 |
{ |
643 | 676 |
/* |
... | ... |
@@ -645,8 +678,8 @@ int syncToNetworkTime() |
645 | 678 |
*/ |
646 | 679 |
Udp.read( packetBuffer, NTP_PACKET_SIZE ); |
647 | 680 |
/* |
648 |
- The timestamp starts at byte 40 of the received packet and is four bytes, |
|
649 |
- or two words, long. First, esxtract the two words. |
|
681 |
+ The timestamp starts at byte 40 of the received packet and is four |
|
682 |
+ bytes, or two words, long. First, esxtract the two words. |
|
650 | 683 |
*/ |
651 | 684 |
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); |
652 | 685 |
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); |
... | ... |
@@ -674,9 +707,9 @@ int syncToNetworkTime() |
674 | 707 |
} |
675 | 708 |
|
676 | 709 |
/* |
677 |
- Send an NTP request to the NTP time server. |
|
710 |
+ Send an NTP request to the NTP time server. |
|
678 | 711 |
*/ |
679 |
-void sendNTPpacket( byte* packetBuffer ) |
|
712 |
+void sendNTPpacket( char * serverAddress, byte * packetBuffer ) |
|
680 | 713 |
{ |
681 | 714 |
/* |
682 | 715 |
Set all bytes in the buffer to 0. |
... | ... |
@@ -699,8 +732,8 @@ void sendNTPpacket( byte* packetBuffer ) |
699 | 732 |
/* |
700 | 733 |
All NTP fields have been given values, so now |
701 | 734 |
send a packet requesting a timestamp. |
702 |
- */ |
|
703 |
- Udp.beginPacket( ntpIpAddr, 123 ); //NTP requests are to port 123 |
|
735 |
+ */ |
|
736 |
+ Udp.beginPacket( serverAddress, 123 ); // NTP requests are to port 123 |
|
704 | 737 |
Udp.write( packetBuffer, NTP_PACKET_SIZE ); |
705 | 738 |
Udp.endPacket(); |
706 | 739 |
return; |
... | ... |
@@ -717,31 +750,27 @@ void commandMode() |
717 | 750 |
{ |
718 | 751 |
char sCmdBuf[2]; |
719 | 752 |
|
720 |
- getCurrentIP(); //used for display of settings |
|
753 |
+ getCurrentIP(); // used for display of settings |
|
754 |
+ |
|
755 |
+ Serial.println(); |
|
756 |
+ displayMenu(); // display the menu |
|
721 | 757 |
|
722 | 758 |
while(true) |
723 | 759 |
{ |
724 |
- /* |
|
725 |
- Print the menu. |
|
726 |
- */ |
|
727 |
- Serial.print( F("\n" \ |
|
728 |
- "1 - view settings\r\n" \ |
|
729 |
- "2 - set IP address\r\n" \ |
|
730 |
- "3 - set NTP server\r\n" \ |
|
731 |
- "4 - toggle verbose\r\n" \ |
|
732 |
- "5 - exit without saving\r\n" \ |
|
733 |
- "6 - save & restart\r\n" \ |
|
734 |
- ">")); |
|
735 | 760 |
/* |
736 | 761 |
Get the command from the user. |
737 | 762 |
*/ |
763 |
+ Serial.print(F(">")); |
|
738 | 764 |
getSerialLine(sCmdBuf, 2); |
739 |
- Serial.print(F("\n\n\r")); |
|
765 |
+ Serial.print(F("\n\r")); |
|
740 | 766 |
/* |
741 | 767 |
Execute the command. |
742 | 768 |
*/ |
743 | 769 |
switch (sCmdBuf[0]) |
744 | 770 |
{ |
771 |
+ case '0': |
|
772 |
+ displayMenu(); |
|
773 |
+ break; |
|
745 | 774 |
case '1': |
746 | 775 |
displaySettings(); |
747 | 776 |
break; |
... | ... |
@@ -749,7 +778,7 @@ void commandMode() |
749 | 778 |
setIP(); |
750 | 779 |
break; |
751 | 780 |
case '3': |
752 |
- setNTPIP(); |
|
781 |
+ setNTPServer(); |
|
753 | 782 |
break; |
754 | 783 |
case '4': |
755 | 784 |
toggleVerbose(); |
... | ... |
@@ -765,6 +794,7 @@ void commandMode() |
765 | 794 |
server or to initialize the Ethernet interface |
766 | 795 |
with a static IP address. |
767 | 796 |
*/ |
797 |
+ delay(100); |
|
768 | 798 |
software_Reset(); |
769 | 799 |
return; |
770 | 800 |
default: |
... | ... |
@@ -774,6 +804,25 @@ void commandMode() |
774 | 804 |
return; |
775 | 805 |
} |
776 | 806 |
|
807 |
+/* |
|
808 |
+ Displays the menu. |
|
809 |
+*/ |
|
810 |
+void displayMenu() |
|
811 |
+{ |
|
812 |
+ /* |
|
813 |
+ Print the menu. |
|
814 |
+ */ |
|
815 |
+ Serial.print( F("Available commands (type a number):\r\n" \ |
|
816 |
+ " 0 - display this menu\r\n" \ |
|
817 |
+ " 1 - view settings\r\n" \ |
|
818 |
+ " 2 - set IP address\r\n" \ |
|
819 |
+ " 3 - set NTP server\r\n" \ |
|
820 |
+ " 4 - toggle verbose\r\n" \ |
|
821 |
+ " 5 - exit without saving\r\n" \ |
|
822 |
+ " 6 - save & restart\r\n" \ |
|
823 |
+ )); |
|
824 |
+} |
|
825 |
+ |
|
777 | 826 |
/* |
778 | 827 |
Displays the current system settings. Displays |
779 | 828 |
RadMon software version, local IP address, NTP server |
... | ... |
@@ -801,9 +850,8 @@ void displaySettings() |
801 | 850 |
Serial.println(sBuf); |
802 | 851 |
|
803 | 852 |
// Display NTP server IP address |
804 |
- sprintf(sBuf, "%d.%d.%d.%d", ntpIpAddr[0], ntpIpAddr[1], ntpIpAddr[2], ntpIpAddr[3]); |
|
805 | 853 |
Serial.print(F("NTP server: ")); |
806 |
- Serial.println(sBuf); |
|
854 |
+ Serial.println(timeServer); |
|
807 | 855 |
|
808 | 856 |
// Display verbose mode setting |
809 | 857 |
printVerboseMode(); |
... | ... |
@@ -842,21 +890,20 @@ void setIP() |
842 | 890 |
carriage return as the first character, then use the |
843 | 891 |
default IP address for the NTP server. |
844 | 892 |
*/ |
845 |
-void setNTPIP() |
|
893 |
+void setNTPServer() |
|
846 | 894 |
{ |
847 |
- char sBuf[16]; |
|
848 |
- |
|
849 |
- Serial.print(F("enter IP (<CR> for default): ")); |
|
850 |
- getSerialLine(sBuf, 16); |
|
895 |
+ char sBuf[32]; |
|
851 | 896 |
|
897 |
+ Serial.print(F("enter NTP server (<CR> for default): ")); |
|
898 |
+ getSerialLine(sBuf, 32); |
|
899 |
+ |
|
852 | 900 |
if (strlen(sBuf) == 0) |
853 | 901 |
{ |
854 |
- strcpy(sBuf, DEFAULT_NTP_SERVER_IP_ADDR); |
|
855 |
- parseIpAddress(ntpIpAddr, sBuf); |
|
902 |
+ strcpy(timeServer, DEFAULT_NTP_SERVER_ADDR); |
|
856 | 903 |
} |
857 | 904 |
else |
858 | 905 |
{ |
859 |
- parseIpAddress(ntpIpAddr, sBuf); |
|
906 |
+ strcpy(timeServer, sBuf); |
|
860 | 907 |
} |
861 | 908 |
Serial.println(); |
862 | 909 |
return; |
... | ... |
@@ -991,13 +1038,20 @@ char* getSerialLine(char* sBuffer, int bufferLength) |
991 | 1038 |
void writeSettingsToEEPROM() |
992 | 1039 |
{ |
993 | 1040 |
byte ix; |
1041 |
+ char c; |
|
994 | 1042 |
for (ix = 0; ix < 4; ix++) |
995 | 1043 |
{ |
996 | 1044 |
EEPROM.write(ix, ipAddr[ix]); |
997 |
- EEPROM.write(ix + 4, ntpIpAddr[ix]); |
|
998 | 1045 |
} |
999 |
- EEPROM.write(8, bVerbose); |
|
1000 |
- EEPROM.write(9, bUseStaticIP); |
|
1046 |
+ EEPROM.write(4, bVerbose); |
|
1047 |
+ EEPROM.write(5, bUseStaticIP); |
|
1048 |
+ ix = 0; |
|
1049 |
+ while(1) { |
|
1050 |
+ c = timeServer[ix]; |
|
1051 |
+ EEPROM.write(6 + ix, c); |
|
1052 |
+ if (c == 0 || ix > 31) break; |
|
1053 |
+ ix++; |
|
1054 |
+ } |
|
1001 | 1055 |
return; |
1002 | 1056 |
} |
1003 | 1057 |
|
... | ... |
@@ -1010,13 +1064,20 @@ void writeSettingsToEEPROM() |
1010 | 1064 |
void readSettingsFromEEPROM() |
1011 | 1065 |
{ |
1012 | 1066 |
byte ix; |
1067 |
+ char c; |
|
1013 | 1068 |
for (ix = 0; ix < 4; ix++) |
1014 | 1069 |
{ |
1015 | 1070 |
ipAddr[ix] = EEPROM.read(ix); |
1016 |
- ntpIpAddr[ix] = EEPROM.read(ix + 4); |
|
1017 | 1071 |
} |
1018 |
- bVerbose = EEPROM.read(8); |
|
1019 |
- bUseStaticIP = EEPROM.read(9); |
|
1072 |
+ bVerbose = EEPROM.read(4); |
|
1073 |
+ bUseStaticIP = EEPROM.read(5); |
|
1074 |
+ ix = 0; |
|
1075 |
+ while(1) { |
|
1076 |
+ c = EEPROM.read(6 + ix); |
|
1077 |
+ timeServer[ix] = c; |
|
1078 |
+ if (c == 0 || ix > 31) break; |
|
1079 |
+ ix++; |
|
1080 |
+ } |
|
1020 | 1081 |
return; |
1021 | 1082 |
} |
1022 | 1083 |
|
... | ... |
@@ -1046,4 +1107,3 @@ void software_Reset() |
1046 | 1107 |
asm volatile (" jmp 0"); |
1047 | 1108 |
return; |
1048 | 1109 |
} |
1049 |
- |