... | ... |
@@ -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 |
... | ... |
@@ -75,6 +75,13 @@ |
75 | 75 |
NTP address set to "pool.ntp.org" per ntp.org request to use |
76 | 76 |
(in order to facilitate load balancing) the fully qualified |
77 | 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 |
+ |
|
84 |
+12345678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
78 | 85 |
*/ |
79 | 86 |
|
80 | 87 |
/*** PREPROCESSOR DEFINES ***/ |
... | ... |
@@ -85,8 +92,8 @@ |
85 | 92 |
Define the header and version number displayed at startup |
86 | 93 |
and also by the 'view settings' command. |
87 | 94 |
*/ |
88 |
-#define STARTUP_HEADER "\n\rRadmon v1.7 (c) 2019\n" |
|
89 |
-#define RADMON_VERSION "v1.7" |
|
95 |
+#define STARTUP_HEADER "\n\rRadmon v1.8 (c) 2019" |
|
96 |
+#define RADMON_VERSION "v1.8" |
|
90 | 97 |
/* |
91 | 98 |
The following define sets the MAC address of the device. This |
92 | 99 |
address is a permanent attribute of the device's Ethernet interface, |
... | ... |
@@ -95,7 +102,7 @@ |
95 | 102 |
specific instance of the Ethernet shield. This MAC address should |
96 | 103 |
be shown on a label affixed to the device housing. |
97 | 104 |
*/ |
98 |
-#define ETHERNET_MAC_ADDRESS 0xNN, 0xNN, 0xNN, 0xNN, 0xNN, 0xNN |
|
105 |
+#define ETHERNET_MAC_ADDRESS 0x90, 0xA2, 0xDA, 0x0D, 0x84, 0xF6 |
|
99 | 106 |
/* |
100 | 107 |
The following defines an APIPA default address in the event that |
101 | 108 |
DHCP mode is ON and a DHCP address cannot be obtained. |
... | ... |
@@ -106,7 +113,7 @@ |
106 | 113 |
out over the device's USB port. This heartbeat consists of a serial |
107 | 114 |
data string containing the current radiation reading and GM time. |
108 | 115 |
*/ |
109 |
-#define SERIAL_UPDATE_INTERVAL 5000 //milli-seconds |
|
116 |
+#define SERIAL_UPDATE_INTERVAL 1000 //milli-seconds |
|
110 | 117 |
/* |
111 | 118 |
The following define sets the port number the HTTP service will use to |
112 | 119 |
listen for requests from Internet clients. Normally HTTP requests use |
... | ... |
@@ -116,18 +123,19 @@ |
116 | 123 |
/* |
117 | 124 |
The following defines are for configuring a local NTP client |
118 | 125 |
for synchronizing the local system clock to network time. |
119 |
- Note that the default setting is the IP address of the following |
|
120 |
- time server: |
|
121 |
- time-c-b.nist.gov |
|
126 |
+ Note that the ntp server address should be sent to the local |
|
127 |
+ server pool of the country where the radmon will be used. See |
|
128 |
+ the web site 'ntp.org' for details. Users in the USA should set |
|
129 |
+ the ntp server to 'us.pool.ntp.org'. |
|
122 | 130 |
*/ |
123 | 131 |
#define DEFAULT_NTP_SERVER_ADDR "pool.ntp.org" |
124 | 132 |
#define NTP_PORT 8888 |
125 |
-#define NTP_PACKET_SIZE 48 // NTP time stamp is in the first 48 bytes of the message |
|
133 |
+#define NTP_PACKET_SIZE 48 // NTP time in the first 48 bytes of the message |
|
126 | 134 |
/* |
127 | 135 |
The following defines how often the system clock gets synchronized |
128 | 136 |
to network time. |
129 | 137 |
*/ |
130 |
-#define NET_SYNCH_INTERVAL 21600 //number in seconds - 4 times a day |
|
138 |
+#define NTP_SYNCH_INTERVAL 43200 // number in seconds - 2 times a day |
|
131 | 139 |
/* |
132 | 140 |
Number of retries if first time server request fails. |
133 | 141 |
*/ |
... | ... |
@@ -190,7 +198,7 @@ SoftwareSerial MightyOhmTxOut(5, 6); |
190 | 198 |
*/ |
191 | 199 |
char mightOhmData[MIGHTYOHM_DATA_STRING_LENGTH + 1]; |
192 | 200 |
unsigned long nextSerialUpdateTime = 0; |
193 |
-time_t nextClockSynchTime = -1; |
|
201 |
+unsigned long nextClockSynchTime = 0; |
|
194 | 202 |
/* |
195 | 203 |
Create global variables to store the verbose mode state (ON or OFF) |
196 | 204 |
and the IP address mode state (static or DHCP). |
... | ... |
@@ -240,14 +248,11 @@ void setup() |
240 | 248 |
} |
241 | 249 |
} |
242 | 250 |
Serial.print(F("IP address: ")); Serial.println(Ethernet.localIP()); |
243 |
- /* |
|
244 |
- Start up NTP client service. |
|
245 |
- */ |
|
246 |
- Udp.begin(NTP_PORT); |
|
247 | 251 |
/* |
248 | 252 |
Synchronize the system clock to network time. |
249 | 253 |
*/ |
250 | 254 |
synchronizeSystemClock(); |
255 |
+ nextClockSynchTime = now() + NTP_SYNCH_INTERVAL; |
|
251 | 256 |
/* |
252 | 257 |
Start up the HTTP server. |
253 | 258 |
*/ |
... | ... |
@@ -310,7 +315,7 @@ void loop() { |
310 | 315 |
Set the time for the next network NTP |
311 | 316 |
time synchronization to occur. |
312 | 317 |
*/ |
313 |
- nextClockSynchTime = now() + NET_SYNCH_INTERVAL; |
|
318 |
+ nextClockSynchTime = now() + NTP_SYNCH_INTERVAL; |
|
314 | 319 |
} |
315 | 320 |
|
316 | 321 |
/* |
... | ... |
@@ -323,36 +328,6 @@ void loop() { |
323 | 328 |
#endif |
324 | 329 |
} |
325 | 330 |
|
326 |
-/* |
|
327 |
- Synchronize the local system clock to |
|
328 |
- network time provided by NTP time server. |
|
329 |
-*/ |
|
330 |
-void synchronizeSystemClock() |
|
331 |
-{ |
|
332 |
- byte count; |
|
333 |
- |
|
334 |
- Serial.println(F("Synchronizing with network time server...")); |
|
335 |
- |
|
336 |
- count = 0; |
|
337 |
- while (1) // Attempt to synchronize 3 times |
|
338 |
- { |
|
339 |
- if (syncToNetworkTime() == 1) { |
|
340 |
- // Synchronization successful |
|
341 |
- break; |
|
342 |
- } |
|
343 |
- if (count == TIME_SERVER_REQUEST_RETRIES) { |
|
344 |
- Serial.print(F("synch failed: ")); |
|
345 |
- break; |
|
346 |
- } |
|
347 |
- count++; |
|
348 |
- delay(2000); |
|
349 |
- } |
|
350 |
- if (count > 0) { |
|
351 |
- Serial.print(count);Serial.println(F(" retries")); |
|
352 |
- } |
|
353 |
- return; |
|
354 |
-} |
|
355 |
- |
|
356 | 331 |
/* |
357 | 332 |
Handle HTTP GET requests from an HTTP client. |
358 | 333 |
*/ |
... | ... |
@@ -376,12 +351,12 @@ void listenForEthernetClients() |
376 | 351 |
firstLineFound = false; |
377 | 352 |
|
378 | 353 |
/* |
379 |
- * The beginning and end of an HTTP client request is always signaled |
|
380 |
- * by a blank line, that is, by two consecutive line feed and carriage |
|
381 |
- * return characters "\r\n\r\n". The following lines of code |
|
382 |
- * look for this condition, as well as the url extension (following |
|
383 |
- * "GET"). |
|
384 |
- */ |
|
354 |
+ The beginning and end of an HTTP client request is always signaled |
|
355 |
+ by a blank line, that is, by two consecutive line feed and carriage |
|
356 |
+ return characters "\r\n\r\n". The following lines of code |
|
357 |
+ look for this condition, as well as the url extension (following |
|
358 |
+ "GET"). |
|
359 |
+ */ |
|
385 | 360 |
|
386 | 361 |
while (client.connected()) { |
387 | 362 |
if (client.available()) { |
... | ... |
@@ -417,7 +392,9 @@ void listenForEthernetClients() |
417 | 392 |
i = 1; |
418 | 393 |
} |
419 | 394 |
|
420 |
- if (firstLineFound && (c == '\n' || i > REQUEST_STRING_BUFFER_LENGTH - 2)) { |
|
395 |
+ if (firstLineFound && (c == '\n' || i > |
|
396 |
+ REQUEST_STRING_BUFFER_LENGTH - 2)) |
|
397 |
+ { |
|
421 | 398 |
processedCommand = true; |
422 | 399 |
} |
423 | 400 |
} |
... | ... |
@@ -447,10 +424,13 @@ void listenForEthernetClients() |
447 | 424 |
else |
448 | 425 |
transmitErrorPage(client); |
449 | 426 |
} |
450 |
- |
|
451 |
- //Serial.println(mightOhmData); |
|
427 |
+ client.println(); |
|
428 |
+ #ifdef DEBUG |
|
429 |
+ Serial.println(mightOhmData); //debug |
|
430 |
+ #endif |
|
431 |
+ |
|
452 | 432 |
// give the web browser time to receive the data |
453 |
- delay(10); |
|
433 |
+ delay(20); |
|
454 | 434 |
// close the connection: |
455 | 435 |
client.stop(); |
456 | 436 |
} |
... | ... |
@@ -478,17 +458,17 @@ void transmitWebPage(EthernetClient client) { |
478 | 458 |
|
479 | 459 |
strcpy(strBuffer, mightOhmData); |
480 | 460 |
/* |
481 |
- * Send the actual HTML page the user will see in their web |
|
482 |
- * browser. |
|
461 |
+ Send the actual HTML page the user will see in their web |
|
462 |
+ browser. |
|
483 | 463 |
*/ |
484 |
- client.print(F("<!DOCTYPE HTML>" \ |
|
464 |
+ client.print(F("<!DOCTYPE HTML>" \ |
|
485 | 465 |
"<html><head><title>Radiation Monitor</title>" \ |
486 |
- "<style>pre {font: 16px arial, sans-serif;}" \ |
|
487 |
- "p {font: 16px arial, sans-serif;}" |
|
488 |
- "h2 {font: 24px arial, sans-serif;}</style>" \ |
|
489 |
- "</head><body><h2>Radiation Monitor</h2>" \ |
|
490 |
- "<p><a href=\"http://intravisions.com/\">" \ |
|
491 |
- "<i>IntraVisions.com</i></a></p>" \ |
|
466 |
+ "<style>pre {font: 16px arial, sans-serif;}" \ |
|
467 |
+ "p {font: 16px arial, sans-serif;}" \ |
|
468 |
+ "h2 {font: 24px arial, sans-serif;}</style>" \ |
|
469 |
+ "</head><body><h2>Radiation Monitor</h2>" \ |
|
470 |
+ "<p><a href=\"http://intravisions.com/\">" \ |
|
471 |
+ "<i>IntraVisions.com</i></a></p>" \ |
|
492 | 472 |
"<hr>")); |
493 | 473 |
/* Data Items */ |
494 | 474 |
client.print(F("<pre>UTC 	")); |
... | ... |
@@ -520,8 +500,8 @@ void transmitRawData(EthernetClient client) { |
520 | 500 |
|
521 | 501 |
strcpy(strBuffer, mightOhmData); |
522 | 502 |
/* |
523 |
- * Format and transmit a JSON compatible data string. |
|
524 |
- */ |
|
503 |
+ Format and transmit a JSON compatible data string. |
|
504 |
+ */ |
|
525 | 505 |
client.print(F("$,UTC=")); |
526 | 506 |
client.print(strtok(strBuffer, " ")); |
527 | 507 |
client.print(F(" ")); |
... | ... |
@@ -545,13 +525,13 @@ void transmitRawData(EthernetClient client) { |
545 | 525 |
} |
546 | 526 |
|
547 | 527 |
/* |
548 |
- * Send an error message web page back to the requesting |
|
549 |
- * client when the client provides an invalid url extension. |
|
550 |
- */ |
|
528 |
+ Send an error message web page back to the requesting |
|
529 |
+ client when the client provides an invalid url extension. |
|
530 |
+*/ |
|
551 | 531 |
void transmitErrorPage(EthernetClient client) { |
552 |
- client.print(F("<!DOCTYPE HTML>" \ |
|
532 |
+ client.print(F("<!DOCTYPE HTML>" \ |
|
553 | 533 |
"<html><head><title>Radiation Monitor</title></head>" \ |
554 |
- "<body><h2>404 Not Found</h2>" \ |
|
534 |
+ "<body><h2>404 Not Found</h2>" \ |
|
555 | 535 |
"</body></html>" |
556 | 536 |
)); |
557 | 537 |
} |
... | ... |
@@ -584,7 +564,7 @@ void processRxByte( char RxByte ) |
584 | 564 |
characters are not preserved in the temporary read buffer, so |
585 | 565 |
restore them to the MightyOhm data buffer, as well. |
586 | 566 |
*/ |
587 |
- sprintf( mightOhmData, "%d:%02d:%02d %d/%d/%d, %s", \ |
|
567 |
+ sprintf( mightOhmData, "%d:%02d:%02d %d/%d/%d, %s", \ |
|
588 | 568 |
hour(), minute(), second(), month(), day(), year(), \ |
589 | 569 |
MIGHTYOHM_DATA_STRING_HEADER ); |
590 | 570 |
/* |
... | ... |
@@ -624,6 +604,48 @@ void processRxByte( char RxByte ) |
624 | 604 |
return; |
625 | 605 |
} |
626 | 606 |
|
607 |
+/* |
|
608 |
+ Synchronize the local system clock to |
|
609 |
+ network time provided by NTP time server. |
|
610 |
+*/ |
|
611 |
+void synchronizeSystemClock() |
|
612 |
+{ |
|
613 |
+ byte count; |
|
614 |
+ |
|
615 |
+ Serial.print(F("Synchronizing with NTP server: ")); |
|
616 |
+ Serial.print(timeServer);Serial.println(F("...")); |
|
617 |
+ |
|
618 |
+ /* |
|
619 |
+ * NOTICE!!! NOTICE!!! NOTICE!!! |
|
620 |
+ * Due to a bug in the Ethernet library, it is necessary to reinitialize |
|
621 |
+ * the ethernet UDP library everytime after an after an EthernetClient |
|
622 |
+ * class object has been instantiated. Also, the Udp stop() function |
|
623 |
+ * must be called at the end of each session. |
|
624 |
+ */ |
|
625 |
+ Udp.begin(NTP_PORT); // see above comment |
|
626 |
+ |
|
627 |
+ count = 1; |
|
628 |
+ while (true) // Attempt to synchronize 3 times |
|
629 |
+ { |
|
630 |
+ if (syncToNetworkTime() == 1) { |
|
631 |
+ // Synchronization successful |
|
632 |
+ break; |
|
633 |
+ } |
|
634 |
+ if (count == TIME_SERVER_REQUEST_RETRIES) { |
|
635 |
+ Serial.print(F("synch failed: ")); |
|
636 |
+ break; |
|
637 |
+ } |
|
638 |
+ count++; |
|
639 |
+ delay(2000); |
|
640 |
+ } |
|
641 |
+ if (count > 1) { |
|
642 |
+ Serial.print(count);Serial.println(F(" retries")); |
|
643 |
+ } |
|
644 |
+ |
|
645 |
+ Udp.stop(); // see above comment |
|
646 |
+ return; |
|
647 |
+} |
|
648 |
+ |
|
627 | 649 |
/* |
628 | 650 |
Send a UDP request packet to an NTP time server and listen for a reply. |
629 | 651 |
When the reply arrives, parse the received UPD packet and compute unix |
... | ... |
@@ -632,9 +654,10 @@ void processRxByte( char RxByte ) |
632 | 654 |
int syncToNetworkTime() |
633 | 655 |
{ |
634 | 656 |
/* |
635 |
- Send a request to the NTP time server. |
|
657 |
+ Send a request to the NTP time server. Define a buffer to hold outgoing |
|
658 |
+ and incoming packets. |
|
636 | 659 |
*/ |
637 |
- byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold outgoing and incoming packets |
|
660 |
+ byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold packets |
|
638 | 661 |
/* |
639 | 662 |
Send an NTP packet to the time server and allow for network lag |
640 | 663 |
before checking if a reply is available. |
... | ... |
@@ -652,8 +675,8 @@ int syncToNetworkTime() |
652 | 675 |
*/ |
653 | 676 |
Udp.read( packetBuffer, NTP_PACKET_SIZE ); |
654 | 677 |
/* |
655 |
- The timestamp starts at byte 40 of the received packet and is four bytes, |
|
656 |
- or two words, long. First, esxtract the two words. |
|
678 |
+ The timestamp starts at byte 40 of the received packet and is four |
|
679 |
+ bytes, or two words, long. First, esxtract the two words. |
|
657 | 680 |
*/ |
658 | 681 |
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); |
659 | 682 |
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); |
... | ... |
@@ -681,7 +704,7 @@ int syncToNetworkTime() |
681 | 704 |
} |
682 | 705 |
|
683 | 706 |
/* |
684 |
- Send an NTP request to the NTP time server. |
|
707 |
+ Send an NTP request to the NTP time server. |
|
685 | 708 |
*/ |
686 | 709 |
void sendNTPpacket( char * serverAddress, byte * packetBuffer ) |
687 | 710 |
{ |
... | ... |
@@ -706,8 +729,8 @@ void sendNTPpacket( char * serverAddress, byte * packetBuffer ) |
706 | 729 |
/* |
707 | 730 |
All NTP fields have been given values, so now |
708 | 731 |
send a packet requesting a timestamp. |
709 |
- */ |
|
710 |
- Udp.beginPacket( serverAddress, 123 ); //NTP requests are to port 123 |
|
732 |
+ */ |
|
733 |
+ Udp.beginPacket( serverAddress, 123 ); // NTP requests are to port 123 |
|
711 | 734 |
Udp.write( packetBuffer, NTP_PACKET_SIZE ); |
712 | 735 |
Udp.endPacket(); |
713 | 736 |
return; |
... | ... |
@@ -724,31 +747,27 @@ void commandMode() |
724 | 747 |
{ |
725 | 748 |
char sCmdBuf[2]; |
726 | 749 |
|
727 |
- getCurrentIP(); //used for display of settings |
|
750 |
+ getCurrentIP(); // used for display of settings |
|
751 |
+ |
|
752 |
+ Serial.println(); |
|
753 |
+ displayMenu(); // display the menu |
|
728 | 754 |
|
729 | 755 |
while(true) |
730 | 756 |
{ |
731 |
- /* |
|
732 |
- Print the menu. |
|
733 |
- */ |
|
734 |
- Serial.print( F("\n" \ |
|
735 |
- "1 - view settings\r\n" \ |
|
736 |
- "2 - set IP address\r\n" \ |
|
737 |
- "3 - set NTP server\r\n" \ |
|
738 |
- "4 - toggle verbose\r\n" \ |
|
739 |
- "5 - exit without saving\r\n" \ |
|
740 |
- "6 - save & restart\r\n" \ |
|
741 |
- ">")); |
|
742 | 757 |
/* |
743 | 758 |
Get the command from the user. |
744 | 759 |
*/ |
760 |
+ Serial.print(F(">")); |
|
745 | 761 |
getSerialLine(sCmdBuf, 2); |
746 |
- Serial.print(F("\n\n\r")); |
|
762 |
+ Serial.print(F("\n\r")); |
|
747 | 763 |
/* |
748 | 764 |
Execute the command. |
749 | 765 |
*/ |
750 | 766 |
switch (sCmdBuf[0]) |
751 | 767 |
{ |
768 |
+ case '0': |
|
769 |
+ displayMenu(); |
|
770 |
+ break; |
|
752 | 771 |
case '1': |
753 | 772 |
displaySettings(); |
754 | 773 |
break; |
... | ... |
@@ -782,6 +801,25 @@ void commandMode() |
782 | 801 |
return; |
783 | 802 |
} |
784 | 803 |
|
804 |
+/* |
|
805 |
+ Displays the menu. |
|
806 |
+*/ |
|
807 |
+void displayMenu() |
|
808 |
+{ |
|
809 |
+ /* |
|
810 |
+ Print the menu. |
|
811 |
+ */ |
|
812 |
+ Serial.print( F("Available commands (type a number):\r\n" \ |
|
813 |
+ " 0 - display this menu\r\n" \ |
|
814 |
+ " 1 - view settings\r\n" \ |
|
815 |
+ " 2 - set IP address\r\n" \ |
|
816 |
+ " 3 - set NTP server\r\n" \ |
|
817 |
+ " 4 - toggle verbose\r\n" \ |
|
818 |
+ " 5 - exit without saving\r\n" \ |
|
819 |
+ " 6 - save & restart\r\n" \ |
|
820 |
+ )); |
|
821 |
+} |
|
822 |
+ |
|
785 | 823 |
/* |
786 | 824 |
Displays the current system settings. Displays |
787 | 825 |
RadMon software version, local IP address, NTP server |
... | ... |
@@ -853,9 +891,9 @@ void setNTPServer() |
853 | 891 |
{ |
854 | 892 |
char sBuf[32]; |
855 | 893 |
|
856 |
- Serial.print(F("enter IP (<CR> for default): ")); |
|
894 |
+ Serial.print(F("enter NTP server (<CR> for default): ")); |
|
857 | 895 |
getSerialLine(sBuf, 32); |
858 |
- |
|
896 |
+ |
|
859 | 897 |
if (strlen(sBuf) == 0) |
860 | 898 |
{ |
861 | 899 |
strcpy(timeServer, DEFAULT_NTP_SERVER_ADDR); |