Browse code

minor restructering

gandolf authored on 07/05/2022 18:40:34
Showing 3 changed files
1 1
deleted file mode 100644
... ...
@@ -1,1106 +0,0 @@
1
-/*
2
- Background Radiation Monitor - Web Server
3
- 
4
- A simple web server that makes available to clients over the Internet
5
- readings from a MightyOhm Geiger counter. The MightyOhm is connected
6
- to an Arduino Uno with attached Ethernet shield.  This software module
7
- runs on the Arduino Uno an embedded HTTP server by which Internet
8
- applications can query the MightyOhm for Geiger counter readings.
9
- Also, this software runs a Network Time Protocol (NTP) client, that
10
- periodically synchronizes the local system clock to network time.
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
- verbose output mode.
14
- 
15
- Copyright 2018 Jeff Owrey
16
-    This program is free software: you can redistribute it and/or modify
17
-    it under the terms of the GNU General Public License as published by
18
-    the Free Software Foundation, either version 3 of the License, or
19
-    (at your option) any later version.
20
-
21
-    This program is distributed in the hope that it will be useful,
22
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
23
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
-    GNU General Public License for more details.
25
-
26
-    You should have received a copy of the GNU General Public License
27
-    along with this program.  If not, see http://www.gnu.org/license.
28
- 
29
- Circuit:
30
- * Main components: Arduino Uno, Ethernet shield, Mighty Ohm Geiger counter
31
- * Ethernet shield attached to pins 10, 11, 12, 13
32
- * In order to allow the MightyOhm to operate on the Uno's 5 volt power
33
-   supply, and thus make the MightyOhm's serial output compatible with the
34
-   Uno, the following has to be done (see MightyOhm schematic):
35
-     1. Change R6 to 1K Ohm.
36
-     2. Change R11 to 330 Ohm.
37
-     3. Connect +5v from the Uno to MightyOhm J6 pin 1.
38
-     4. Connect GND from the Uno to MightyOhm J6 pin 3.
39
-     5. Connect D5 from the Uno to MightyOhm J7 pin 5.
40
-   
41
- Misc Notes:
42
-   As of this release the Uno's SRAM gets entirely maxed out by
43
-   this program.  Any modifications to this program that requires
44
-   additional memory seriously entails the risk that the modifications
45
-   will cause the program to become un-stable.
46
-   
47
- Revision History:  
48
-   * v10 released 25 Feb 2014 by J L Owrey
49
-   * v11 released 24 Jun 2014 by J L Owrey
50
-       - optimization of processRxByte function to conserve SRAM
51
-       - removal of non-used function code
52
-       - defaults to APIPA IP address in the event a DHCP address
53
-         cannot be obtained
54
-   * v12 released 20 Dec 2014 by J L Owrey
55
-       - removed Timestamp global variable to make more dynamic
56
-         memory available for local variables
57
-       - optimized clock network synch algorithm
58
-       - optimized serial update algorithm
59
-   * v13 released 22 Jul 2015 by J L Owrey
60
-       - add use of "F" function to store constant strings in
61
-         program flash memory in order to save SRAM space
62
-   * v14 released 19 Aug 2015 by J L Owrey
63
-       - add ability to respond to web a client request with either
64
-         a JSON compatible string or a standard HTML document
65
-   * v15 released 20 Feb 2016 by J L Owrey
66
-       - improved http request handling
67
-       - simplified raw data request format
68
-       - simplified serial data output
69
-   * v16 released 16 Sep 2017 by J L Owrey
70
-       - added capability of rebooting via network http request,
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
-
84
-12345678901234567890123456789012345678901234567890123456789012345678901234567890         
85
-*/
86
-
87
-/***  PREPROCESSOR DEFINES  ***/
88
-
89
-//#define DEBUG
90
-
91
-/*
92
- Define the header and version number displayed at startup
93
- and also by the 'view settings' command.
94
-*/
95
-#define STARTUP_HEADER "\n\rRadmon v1.8 (c) 2019"
96
-#define RADMON_VERSION "v1.8"
97
-/*
98
- The following define sets the MAC address of the device.  This
99
- address is a permanent attribute of the device's Ethernet interface,
100
- and never, ever, should be changed.  This address was provided
101
- by the Arduino Ethernet shield manufacturer for use with this
102
- specific instance of the Ethernet shield.  This MAC address should
103
- be shown on a label affixed to the device housing.
104
-*/
105
-#define ETHERNET_MAC_ADDRESS 0x90, 0xA2, 0xDA, 0x0D, 0x84, 0xF6
106
-/*
107
- The following defines an APIPA default address in the event that
108
- DHCP mode is ON and a DHCP address cannot be obtained.
109
-*/
110
-#define DEFAULT_APIPA_IP_ADDRESS "169.254.100.10"
111
-/*
112
- The following define sets the period of a 'heartbeat' string sent
113
- out over the device's USB port.  This heartbeat consists of a serial
114
- data string containing the current radiation reading and GM time.
115
-*/
116
-#define SERIAL_UPDATE_INTERVAL 1000  //milli-seconds
117
-/*
118
- The following define sets the port number the HTTP service will use to
119
- listen for requests from Internet clients.  Normally HTTP requests use
120
- port 80.
121
-*/
122
-#define HTTP_SERVER_PORT 80
123
-/*
124
- The following defines are for configuring a local NTP client
125
- for synchronizing the local system clock to network time.
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'.
130
-*/
131
-#define DEFAULT_NTP_SERVER_ADDR "pool.ntp.org"
132
-#define NTP_PORT 8888
133
-#define NTP_PACKET_SIZE 48 // NTP time in the first 48 bytes of the message
134
-/*
135
- The following defines how often the system clock gets synchronized
136
- to network time.
137
-*/
138
-#define NTP_SYNCH_INTERVAL 43200 // number in seconds - 2 times a day
139
-/*
140
- Number of retries if first time server request fails.
141
-*/
142
-#define TIME_SERVER_REQUEST_RETRIES 3
143
-/*
144
- The following defines the size of the buffer space required for the
145
- serial data string from the Mighty Ohm Geiger counter.  The serial
146
- data string is defined as the text from newline character to newline
147
- character.
148
-*/
149
-#define MIGHTYOHM_DATA_STRING_LENGTH 65
150
-/*
151
- The beginning of the MightyOhm data string always begins with the
152
- same three characters.  These three characters determine the 
153
- beginning of a new line of data from the MightyOhm.
154
-*/
155
-#define MIGHTYOHM_DATA_STRING_HEADER "CPS"
156
-/*
157
- Set the depth of the string buffer that receives the http
158
- request header from the client.  Must be large enough to
159
- capture 'GET /rdata '.
160
-*/
161
-#define REQUEST_STRING_BUFFER_LENGTH 24
162
-
163
-/***  LIBRARY MODULES USED  ***/
164
-
165
-#include <Time.h>
166
-#include <SPI.h>         
167
-#include <Ethernet.h>
168
-#include <EthernetUdp.h>
169
-#include <SoftwareSerial.h>
170
-#include <EEPROM.h>;
171
-
172
-/***  GLOBAL DECLARATIONS ***/
173
-
174
-/*
175
- Create and initialize a mac address object for the Ethernet interface. 
176
-*/
177
-byte mac[] = { ETHERNET_MAC_ADDRESS };
178
-/*
179
- Create and initialize an HTTP server object.  The object is initialized
180
- to the TCP port the HTTP server will use to listen for clients.
181
-*/
182
-EthernetServer httpServer(HTTP_SERVER_PORT);
183
-/*
184
- Create a UDP client object for sending packets to
185
- and receiveing packets from an NTP time server.
186
-*/
187
-EthernetUDP Udp;
188
-/*
189
- Create a software serial port for receiving serial data from
190
- the MightyOhm. Note that the Uno pin 5 receives serial data
191
- FROM the MightyOhm.  The Uno's pin 6 is not used, as there is
192
- no need to send serial data to the MightyOhm. 
193
-*/
194
-SoftwareSerial MightyOhmTxOut(5, 6);
195
-/*
196
- Create global variables to store the MightOhm data, next heartbeat
197
- time, and next synchronization time.
198
-*/
199
-char mightOhmData[MIGHTYOHM_DATA_STRING_LENGTH + 1];
200
-unsigned long nextSerialUpdateTime = 0;
201
-unsigned long nextClockSynchTime = 0;
202
-/*
203
- Create global variables to store the verbose mode state (ON or OFF)
204
- and the IP address mode state (static or DHCP).
205
-*/
206
-boolean bVerbose;
207
-boolean bUseStaticIP;
208
-/*
209
- Create and initialize global arrays to hold the current IP address
210
- and the NTP server address.
211
-*/
212
-byte ipAddr[4];
213
-char timeServer[32];
214
-
215
-/*** SYSTEM STARTUP  ***/
216
-
217
-void setup()
218
-{
219
-  /*
220
-   Open serial communications to and from the Uno's USB port.
221
-  */
222
-  Serial.begin(9600);
223
-  /* 
224
-   Print to the USB port a header showing Radmon
225
-   version of this program and the copyright notice.
226
-  */
227
-  Serial.println(F(STARTUP_HEADER));
228
-  /*
229
-    Get the system configuration from EEPROM.
230
-  */
231
-  readSettingsFromEEPROM();
232
-  /*
233
-   Start up the Ethernet interface using either a static or
234
-   DHCP supplied address (depending on stored system configuration).
235
-  */
236
-  if (bUseStaticIP)
237
-  {
238
-    Ethernet.begin(mac, ipAddr);
239
-  }
240
-  else
241
-  {
242
-    if (Ethernet.begin(mac) == 0)
243
-    {
244
-      /* DHCP not responding so use APIPA address */
245
-      parseIpAddress(ipAddr, DEFAULT_APIPA_IP_ADDRESS);
246
-      Ethernet.begin(mac, ipAddr);
247
-      Serial.println(F("DHCP failed - using APIPA "));
248
-    }
249
-  }
250
-  Serial.print(F("IP address: ")); Serial.println(Ethernet.localIP());
251
-  /*
252
-    Synchronize the system clock to network time.
253
-  */
254
-  synchronizeSystemClock();
255
-  nextClockSynchTime = now() + NTP_SYNCH_INTERVAL;
256
-  /*
257
-   Start up the HTTP server.
258
-  */
259
-  Serial.println(F("Starting http server..."));
260
-  httpServer.begin();
261
-  /*
262
-    Open serial communications to the MightyOhm device.
263
-  */  
264
-  MightyOhmTxOut.begin(9600);
265
-  /*
266
-   Initialize MightyOhm data string to empty.
267
-  */
268
-  mightOhmData[0] = 0;
269
-  return;
270
-}
271
-
272
-/*** MAIN LOOP ***/
273
-
274
-void loop() {
275
-  /*
276
-   Check for user keyboard 'c' pressed.  This character switches
277
-   to command mode.
278
-  */   
279
-  if (Serial.available()) {
280
-    // get incoming byte
281
-    if (Serial.read() == 'c') {
282
-      commandMode();
283
-    }
284
-  }
285
-  
286
-  /*
287
-    Poll serial input buffer from MightyOhm for new data and 
288
-    process received bytes to form a complete data string.
289
-  */
290
-  while (MightyOhmTxOut.available()) {
291
-    processRxByte(MightyOhmTxOut.read());
292
-  }
293
-  
294
-  /*
295
-    In verbose mode, send the MightyOhm data string to the
296
-    serial port at regular intervals.
297
-  */
298
-  if (bVerbose) {
299
-    if (millis() > nextSerialUpdateTime) {
300
-      Serial.println(mightOhmData);
301
-      /* 
302
-       Set the time for the next serial update to occur.
303
-      */
304
-      nextSerialUpdateTime = millis() + SERIAL_UPDATE_INTERVAL;
305
-    }
306
-  }
307
-  
308
-  /*
309
-   Periodically synchronize local system clock to time
310
-   provided by NTP time server.
311
-  */
312
-  if (now() > nextClockSynchTime) {
313
-    synchronizeSystemClock();
314
-    /* 
315
-     Set the time for the next network NTP
316
-     time synchronization to occur.
317
-    */
318
-    nextClockSynchTime = now() + NTP_SYNCH_INTERVAL;
319
-  }
320
-  
321
-  /*
322
-   Listen for and and process requests from HTTP clients.
323
-  */  
324
-  listenForEthernetClients();
325
-
326
-  #ifdef DEBUG
327
-    Serial.print("lp time: "); Serial.println(millis() - currentTime);
328
-  #endif
329
-}
330
-
331
-/*
332
-  Handle HTTP GET requests from an HTTP client.
333
-*/  
334
-void listenForEthernetClients()
335
-{
336
-  // listen for incoming clients
337
-  EthernetClient client = httpServer.available();
338
-  if (client) {
339
-    char sBuf[REQUEST_STRING_BUFFER_LENGTH];
340
-    byte i;
341
-    char c, c_prev;
342
-    boolean processedCommand;
343
-    boolean firstLineFound;
344
-
345
-    Serial.println(F("\nclient request"));
346
-
347
-    i = 0;
348
-    c_prev = 0;
349
-    sBuf[0] = 0;
350
-    processedCommand = false;
351
-    firstLineFound = false;
352
-  
353
-    /*
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
-    */
360
-    
361
-    while (client.connected())  {
362
-      if (client.available()) {
363
-        c = client.read();
364
-
365
-        if (bVerbose) {
366
-          Serial.print(c);
367
-        }
368
-              
369
-        if (c == '\r') {
370
-          continue; // discard character
371
-        }  
372
-        else if (c == '\n') {
373
-          if (firstLineFound && c_prev == '\n') {
374
-             break;
375
-          }
376
-        } 
377
-        
378
-        if (!processedCommand) {
379
-          
380
-          if (c != '\n') {
381
-            if(i > REQUEST_STRING_BUFFER_LENGTH - 2) {
382
-              i = 0;
383
-              sBuf[0] = 0;
384
-            }
385
-            sBuf[i++] = c;
386
-            sBuf[i] = 0;
387
-          }
388
-
389
-          if (!firstLineFound && strstr(sBuf, "GET /") != NULL) {
390
-            firstLineFound = true;
391
-            strcpy(sBuf, "/");
392
-            i = 1;
393
-          }
394
-
395
-          if (firstLineFound && (c == '\n' || i >
396
-              REQUEST_STRING_BUFFER_LENGTH - 2))
397
-          {
398
-            processedCommand = true;
399
-          }
400
-        }
401
-        c_prev = c;
402
-      } // end single character processing
403
-    } // end character processing loop
404
-
405
-    /*
406
-     Send a standard HTTP response header to the
407
-     client's GET request.
408
-    */
409
-    transmitHttpHeader(client);
410
-    
411
-    char * pStr = strtok(sBuf, " ");
412
-    if (pStr != NULL) {
413
-      if (strcmp(pStr, "/rdata") == 0)
414
-        transmitRawData(client);
415
-      else if (strcmp(pStr, "/") == 0)
416
-        transmitWebPage(client);
417
-      else if(strcmp(pStr, "/reset") == 0) {
418
-        client.print(F("ok"));
419
-        delay(10);
420
-        // close the connection and reboot:
421
-        client.stop();
422
-        software_Reset();
423
-      }
424
-      else 
425
-        transmitErrorPage(client);
426
-    }
427
-    client.println();
428
-    #ifdef DEBUG
429
-      Serial.println(mightOhmData);  //debug
430
-    #endif
431
-    
432
-    // give the web browser time to receive the data
433
-    delay(20);
434
-    // close the connection:
435
-    client.stop();
436
-  }
437
-}
438
-
439
-/*
440
- Send standard http response header back to
441
- requesting client,
442
-*/
443
-void transmitHttpHeader(EthernetClient client) {
444
-  client.print(F("HTTP/1.1 200 OK\r\n"          \
445
-                 "Content-Type: text/html\r\n"  \
446
-                 "Connnection: close\r\n"       \
447
-                 "Refresh: 5\r\n"               \
448
-                 "\r\n"                         \
449
-                 ));
450
-}
451
-
452
-/*
453
- Send to the client the MightyOhm Geiger counter's
454
- current readings, embedded in an HTML document.
455
-*/
456
-void transmitWebPage(EthernetClient client) {
457
-  char strBuffer[MIGHTYOHM_DATA_STRING_LENGTH];
458
-
459
-  strcpy(strBuffer, mightOhmData);
460
-  /*
461
-   Send the actual HTML page the user will see in their web
462
-   browser.
463
-  */
464
-  client.print(F("<!DOCTYPE HTML>"                               \
465
-                 "<html><head><title>Radiation Monitor</title>"  \
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>"               \
472
-                 "<hr>"));
473
-  /* Data Items */             
474
-  client.print(F("<pre>UTC &#9;"));
475
-  client.print(strtok(strBuffer, ","));
476
-  client.print(F("<br>"));
477
-  client.print(strtok(NULL, ", "));
478
-  client.print(F(" &#9;"));
479
-  client.print(strtok(NULL, ", "));
480
-  client.print(F("<br>"));
481
-  client.print(strtok(NULL, ", "));
482
-  client.print(F(" &#9;"));
483
-  client.print(strtok(NULL, ", "));
484
-  client.print(F("<br>"));
485
-  client.print(strtok(NULL, ", "));
486
-  client.print(F(" &#9;"));
487
-  client.print(strtok(NULL, ", "));
488
-  client.print(F("<br>"));
489
-  client.print(F("Mode &#9;"));
490
-  client.print(strtok(NULL, ", "));
491
-  client.print(F("<br></pre></body></html>"));
492
-}  
493
-
494
-/*
495
- Send to the client the MightyOhm Geiger counter's
496
- current readings, embedded in a JSON compatible string.
497
-*/
498
-void transmitRawData(EthernetClient client) {
499
-  char strBuffer[MIGHTYOHM_DATA_STRING_LENGTH];
500
-
501
-  strcpy(strBuffer, mightOhmData);
502
-  /*
503
-   Format and transmit a JSON compatible data string.
504
-  */
505
-  client.print(F("$,UTC="));
506
-  client.print(strtok(strBuffer, " "));
507
-  client.print(F(" "));
508
-  client.print(strtok(NULL, ", "));
509
-  client.print(F(","));
510
-  client.print(strtok(NULL, ", "));
511
-  client.print(F("="));
512
-  client.print(strtok(NULL, ", "));
513
-  client.print(F(","));
514
-  client.print(strtok(NULL, ", "));
515
-  client.print(F("="));
516
-  client.print(strtok(NULL, ", "));
517
-  client.print(F(","));
518
-  client.print(strtok(NULL, ", "));
519
-  client.print(F("="));
520
-  client.print(strtok(NULL, ", "));
521
-  client.print(F(","));
522
-  client.print(F("Mode="));
523
-  client.print(strtok(NULL, ", "));
524
-  client.print(F(",#\n"));
525
-}
526
-
527
-/*
528
- Send an error message web page back to the requesting
529
- client when the client provides an invalid url extension.
530
-*/
531
-void transmitErrorPage(EthernetClient client) {
532
-  client.print(F("<!DOCTYPE HTML>"                                      \
533
-                 "<html><head><title>Radiation Monitor</title></head>"  \
534
-                 "<body><h2>404 Not Found</h2>"                         \
535
-                 "</body></html>"
536
-                 ));
537
-}
538
-
539
-/*
540
- Process bytes received from the MightyOhm Geiger counter,
541
- one at a time, to create a well formed string.
542
-*/
543
-void processRxByte( char RxByte )
544
-{
545
-  static char readBuffer[MIGHTYOHM_DATA_STRING_LENGTH];
546
-  static byte cIndex = 0;
547
-  
548
-  /*
549
-     Discard carriage return characters.
550
-  */
551
-  if (RxByte == '\r')
552
-  {
553
-    return;
554
-  }
555
-  /*
556
-   A new line character indicates the line of data from
557
-   the MightyOhm is complete and can be written to the
558
-   MightyOhm data buffer.
559
-  */
560
-  else if (RxByte == '\n')
561
-  {
562
-    /*
563
-     First copy the timestamp to the MightyOhm data buffer. The "CPS"
564
-     characters are not preserved in the temporary read buffer, so
565
-     restore them to the MightyOhm data buffer, as well.
566
-    */
567
-    sprintf( mightOhmData, "%d:%02d:%02d %d/%d/%d, %s",        \
568
-          hour(), minute(), second(), month(), day(), year(),  \
569
-          MIGHTYOHM_DATA_STRING_HEADER );
570
-    /*
571
-     Now copy the rest of the data in the temporary read buffer to the
572
-     MightyOhm data buffer.
573
-    */ 
574
-    strcat(mightOhmData, readBuffer);
575
-    /*
576
-     Flush the temporary read buffer.
577
-    */
578
-    cIndex = 0;
579
-    readBuffer[0] = 0;
580
-    return;
581
-  }
582
-  /* 
583
-   A new line of data will always have "CPS" as the first
584
-   three characters.  Therefore, when these characters occur in
585
-   sequence, the read buffer should begin collecting characters.
586
-   This is a kluge to deal with an inherent problem in the Software
587
-   Serial library implementation that results in characters dropped
588
-   from the software serial stream buffer.
589
-  */
590
-  if( strstr(readBuffer, MIGHTYOHM_DATA_STRING_HEADER) > 0 ) 
591
-  {
592
-    cIndex = 0;
593
-  }
594
-  /*
595
-   Read characters into a temporary buffer until
596
-   the line of data is complete or the buffer is full.
597
-  */
598
-  if(cIndex < MIGHTYOHM_DATA_STRING_LENGTH)
599
-  {
600
-    readBuffer[cIndex] = RxByte;
601
-    cIndex += 1;
602
-    readBuffer[cIndex] = 0;
603
-  }
604
-  return;
605
-} 
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
-
649
-/* 
650
-  Send a UDP request packet to an NTP time server and listen for a reply.
651
-  When the reply arrives, parse the received UPD packet and compute unix
652
-  epoch time.  Then set the local system clock to the epoch time.
653
-*/
654
-int syncToNetworkTime()
655
-{
656
-  /*
657
-   Send a request to the NTP time server.  Define a buffer to hold outgoing
658
-   and incoming packets.
659
-  */
660
-  byte packetBuffer[ NTP_PACKET_SIZE]; // buffer to hold packets
661
-  /*
662
-   Send an NTP packet to the time server and allow for network lag
663
-   before checking if a reply is available.
664
-  */
665
-  sendNTPpacket(timeServer, packetBuffer);
666
-  /*
667
-   Wait for response from NTP time server.
668
-  */
669
-  delay(1000);  // allow 1000 milli-seconds for network lag
670
-
671
-  if ( Udp.parsePacket() )
672
-  {  
673
-    /*
674
-     A UDP packet has arrived, so read the data from it.
675
-    */
676
-    Udp.read( packetBuffer, NTP_PACKET_SIZE );
677
-    /*
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.
680
-    */
681
-    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
682
-    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
683
-    /*  
684
-     Combine the four bytes (two words) into a long integer
685
-     this is NTP time (seconds since Jan 1 1900).
686
-    */
687
-    unsigned long secsSince1900 = highWord << 16 | lowWord;
688
-    /*  
689
-     Now convert NTP time into UTC time.  Note that
690
-     Unix time starts on Jan 1 1970. In seconds,
691
-     that's 2208988800.  Therfore,
692
-     
693
-         epoch = secsSince1900 - 2208988800UL
694
-         
695
-     Set the local system clock with this value.
696
-    */
697
-    setTime(secsSince1900 - 2208988800UL);
698
-    return 1;
699
-  }
700
-  else
701
-  {
702
-    return 0;
703
-  } /* end if */
704
-}
705
-
706
-/*
707
- Send an NTP request to the NTP time server.
708
-*/
709
-void sendNTPpacket( char * serverAddress, byte * packetBuffer )
710
-{
711
-  /*
712
-   Set all bytes in the buffer to 0.
713
-  */
714
-  memset( packetBuffer, 0, NTP_PACKET_SIZE );
715
-  /*
716
-   Initialize values needed to form NTP request.
717
-  */
718
-  packetBuffer[0] = 0b11100011;  // LI, Version, Mode
719
-  packetBuffer[1] = 0;           // Stratum, or type of clock
720
-  packetBuffer[2] = 6;           // Polling Interval
721
-  packetBuffer[3] = 0xEC;        // Peer Clock Precision
722
-  /*
723
-   Set the remaining 8 bytes to zero for Root Delay & Root Dispersion.
724
-  */
725
-  packetBuffer[12]  = 49; 
726
-  packetBuffer[13]  = 0x4E;
727
-  packetBuffer[14]  = 49;
728
-  packetBuffer[15]  = 52;
729
-  /*
730
-   All NTP fields have been given values, so now
731
-   send a packet requesting a timestamp.
732
-  */
733
-  Udp.beginPacket( serverAddress, 123 ); // NTP requests are to port 123
734
-  Udp.write( packetBuffer, NTP_PACKET_SIZE );
735
-  Udp.endPacket();
736
-  return;
737
-}
738
-
739
-/***  COMMAND LINE INTERFACE  ***/
740
-
741
-/*
742
- Print a command menu to the USB port.  Then wait for a
743
- response from the user.  When the response has been
744
- received, execute the command.
745
-*/
746
-void commandMode()
747
-{
748
-  char sCmdBuf[2];
749
-  
750
-  getCurrentIP();  // used for display of settings
751
-
752
-  Serial.println();
753
-  displayMenu(); // display the menu
754
-  
755
-  while(true)
756
-  {
757
-    /*
758
-     Get the command from the user.
759
-    */
760
-    Serial.print(F(">"));
761
-    getSerialLine(sCmdBuf, 2);
762
-    Serial.print(F("\n\r"));
763
-    /* 
764
-     Execute the command.
765
-    */
766
-    switch (sCmdBuf[0])
767
-    {
768
-      case '0':
769
-        displayMenu();
770
-        break;
771
-      case '1':
772
-        displaySettings();
773
-        break;
774
-      case '2':
775
-        setIP();
776
-        break;
777
-      case '3':
778
-        setNTPServer();
779
-        break;
780
-      case '4':
781
-        toggleVerbose();
782
-        break;
783
-      case '5':
784
-        readSettingsFromEEPROM();
785
-        return;
786
-      case '6':
787
-        writeSettingsToEEPROM();
788
-        /*
789
-         A software reboot is necessary to force the 
790
-         Arduino to request an IP address from a DHCP
791
-         server or to initialize the Ethernet interface
792
-         with a static IP address.
793
-        */
794
-        delay(100);
795
-        software_Reset();
796
-        return;
797
-      default:
798
-        Serial.println(F("invalid command"));
799
-    } /* end switch */
800
-  } /* end while */
801
-  return;
802
-}
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
-
823
-/*
824
- Displays the current system settings.  Displays
825
- RadMon software version, local IP address, NTP server
826
- address, and verbose mode setting.
827
-*/
828
-void displaySettings()
829
-{
830
-  char sBuf[16];
831
-  
832
-  // Display RadMon version
833
-  Serial.print(F("Firmware "));
834
-  Serial.print(F(RADMON_VERSION));
835
-  Serial.println();
836
-
837
-  // Display local IP address
838
-  sprintf(sBuf, "%d.%d.%d.%d", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
839
-  if (bUseStaticIP)
840
-  {
841
-    Serial.print(F("Static IP: "));
842
-  }
843
-  else
844
-  {
845
-    Serial.print(F("DHCP IP: "));
846
-  }
847
-  Serial.println(sBuf);
848
-  
849
-  // Display NTP server IP address
850
-  Serial.print(F("NTP server: ")); 
851
-  Serial.println(timeServer);
852
-
853
-  // Display verbose mode setting
854
-  printVerboseMode();
855
-  return;
856
-}
857
-
858
-/*
859
- Sets the local IP address. If the user sends a carriage
860
- return as the first character, then switch to acquiring
861
- IP address via DHCP server.
862
-*/
863
-void setIP()
864
-{
865
-  char sBuf[16];
866
-
867
-  Serial.print(F("enter IP (<CR> for DHCP): "));
868
-  getSerialLine(sBuf, 16);
869
-  
870
-  if(strlen(sBuf) == 0)
871
-  {
872
-    bUseStaticIP = false;
873
-    strcpy(sBuf, "0.0.0.0");
874
-    parseIpAddress(ipAddr, sBuf);
875
-  }
876
-  else
877
-  {
878
-    bUseStaticIP = true;
879
-    parseIpAddress(ipAddr, sBuf);
880
-  }
881
-  Serial.println();
882
-  return;
883
-}
884
-
885
-/*
886
- Sets the NTP server IP address.  If the user sends a
887
- carriage return as the first character, then use the
888
- default IP address for the NTP server.
889
-*/
890
-void setNTPServer()
891
-{
892
-  char sBuf[32];
893
-  
894
-  Serial.print(F("enter NTP server (<CR> for default): "));
895
-  getSerialLine(sBuf, 32);
896
-
897
-  if (strlen(sBuf) == 0)
898
-  {
899
-    strcpy(timeServer, DEFAULT_NTP_SERVER_ADDR);
900
-  }
901
-  else
902
-  {
903
-    strcpy(timeServer, sBuf);
904
-  }
905
-  Serial.println();
906
-  return;
907
-}
908
-
909
-/*
910
- Turns verbose mode ON or OFF.
911
-*/
912
-void toggleVerbose()
913
-{
914
-  bVerbose = !bVerbose;
915
-  printVerboseMode();
916
-  return;
917
-}
918
-
919
-/***  GENERAL HELPER FUNCTIONS  ***/
920
-
921
-/*
922
- Print current verbose mode.
923
-*/
924
-void printVerboseMode()
925
-{
926
-  Serial.print(F("Verbose: "));
927
-  if (bVerbose)
928
-  {
929
-    Serial.println(F("ON"));
930
-  }
931
-  else
932
-  {
933
-    Serial.println(F("OFF"));
934
-  }
935
-  return;
936
-}
937
-
938
-/*
939
- Get the current IP address from the Ethernet interface
940
-*/
941
-void getCurrentIP()
942
-{
943
-  ipAddr[0] = Ethernet.localIP()[0];
944
-  ipAddr[1] = Ethernet.localIP()[1];
945
-  ipAddr[2] = Ethernet.localIP()[2];
946
-  ipAddr[3] = Ethernet.localIP()[3];
947
-  return;
948
-}
949
-
950
-/*
951
- Gets a line of data from the user via USB port.
952
-*/
953
-char* getSerialLine(char* sBuffer, int bufferLength)
954
-{
955
-  byte index;
956
-  char cRx;
957
-
958
-  /* 
959
-   Discard extranious characters that may still be in the
960
-   USB serial stream read buffer.  Most often these characters
961
-   will be unprocessed carriage return or line feed characters.
962
-  */
963
-  delay(10);
964
-  while (Serial.available())
965
-  {
966
-    cRx = Serial.read();
967
-  }
968
-
969
-  /*
970
-   Read and process characters from the user as they arrive in
971
-   the USB serial read buffer.
972
-  */
973
-  index = 0;
974
-  while(true)
975
-  {
976
-    /*
977
-     Wait until the user starts pressing keys and bytes
978
-     arrive in the serial read buffer.
979
-    */
980
-    if (Serial.available())
981
-    {
982
-      cRx = Serial.read();
983
-      if (cRx == '\r' || cRx == '\n')
984
-      {
985
-        /*
986
-         The user has finished typing the command and
987
-         has pressed the Enter key. So, discard the
988
-         carriage return and newline characters and then
989
-         return control to the calling function.
990
-        */
991
-        break;
992
-      }
993
-      else if (cRx == 8 || cRx == 127)
994
-      {
995
-        if (index > 0)
996
-        {
997
-          /*
998
-           The user has hit the delete-backspace key,
999
-           so send out a backspace, followed by a space,
1000
-           followed by another backspace character.
1001
-           This allows for in-line ediiting.
1002
-          */
1003
-          Serial.write(8);
1004
-          Serial.write(32);
1005
-          Serial.write(8); 
1006
-          index -= 1;
1007
-        }
1008
-      }
1009
-      else if ( index < (bufferLength - 1) )
1010
-      {
1011
-        /*
1012
-         The received character is valid, so write it
1013
-         to the buffer. Once the buffer becomes full
1014
-         do not write any more characters to it.  When
1015
-         the user pressses the enter key, the string
1016
-         will be null terminated and control will pass
1017
-         back to the calling function.
1018
-        */
1019
-        Serial.write(cRx); // echo character to terminal
1020
-        sBuffer[index] = cRx;
1021
-        index += 1;
1022
-      } /* end if */
1023
-    } /* end if */
1024
-  } /* end while */
1025
-  sBuffer[index] = 0; // terminate the string
1026
-  return sBuffer;
1027
-}
1028
-
1029
-/*
1030
- Writes system configuration settings to non-volitile
1031
- EEPROM.  The items written are the local IP address,
1032
- the NTP server IP address, the state of verbose mode,
1033
- and local IP mode (static or DHCP).
1034
-*/
1035
-void writeSettingsToEEPROM()
1036
-{
1037
-  byte ix;
1038
-  char c;
1039
-  for (ix = 0; ix < 4; ix++)
1040
-  {
1041
-    EEPROM.write(ix, ipAddr[ix]);
1042
-  }
1043
-  EEPROM.write(4, bVerbose);
1044
-  EEPROM.write(5, bUseStaticIP);
1045
-  ix = 0;
1046
-  while(1) {
1047
-    c = timeServer[ix];
1048
-    EEPROM.write(6 + ix, c);
1049
-    if (c == 0 || ix > 31) break;
1050
-    ix++;
1051
-  }
1052
-  return;
1053
-}
1054
-
1055
-/*
1056
- Reads system configuration settings from non-volitile
1057
- EEPROM.  The items read are the local IP address,
1058
- the NTP server IP address, the state of verbose mode,
1059
- and local IP mode (static or DHCP).
1060
-*/
1061
-void readSettingsFromEEPROM()
1062
-{
1063
-  byte ix;
1064
-  char c;
1065
-  for (ix = 0; ix < 4; ix++)
1066
-  {
1067
-    ipAddr[ix] = EEPROM.read(ix);
1068
-  }
1069
-  bVerbose = EEPROM.read(4);
1070
-  bUseStaticIP = EEPROM.read(5);
1071
-  ix = 0;
1072
-  while(1) {
1073
-    c = EEPROM.read(6 + ix);
1074
-    timeServer[ix] = c;
1075
-    if (c == 0 || ix > 31) break;
1076
-    ix++;
1077
-  }
1078
-  return;
1079
-}
1080
-
1081
-/*
1082
- Parses an IP address given in "nnn.nnn.nnn.nnn" string
1083
- format into four bytes and stores them in an array. Note
1084
- that this function destroys the contents of the sIP
1085
- character array.  Therefore this array cannot be
1086
- reinitialized after calling this function.
1087
-*/
1088
-void parseIpAddress(byte* byBuf, char* sIP)
1089
-{
1090
-  byBuf[0] = atoi(strtok(sIP, "."));
1091
-  byBuf[1] = atoi(strtok(NULL, "."));
1092
-  byBuf[2] = atoi(strtok(NULL, "."));
1093
-  byBuf[3] = atoi(strtok(NULL, "."));
1094
-  return;
1095
-}
1096
-
1097
-/*
1098
- Restarts the Uno and runs this program from beginning.  This
1099
- function gets called after a change made from the user
1100
- interface when the user selects "save and restart".
1101
-*/
1102
-void software_Reset() 
1103
-{
1104
-  asm volatile ("  jmp 0");
1105
-  return; 
1106
-}  
1107 0
deleted file mode 100644
1108 1
Binary files a/DIY Radmon Project Description.pdf and /dev/null differ
... ...
@@ -6,7 +6,7 @@
6 6
 
7 7
 <p>Building the background radiation monitor will give you an excellent introduction to a variety of technologies. You will learn about programming Arduino micro-controllers and assembling electronic components. You will learn about Linux server software, scripting for Internet applications, and displaying information with a web page.</p>
8 8
 
9
-<p>You can find all the required software available under open source, GNU license at this project site. Please read the <a href="DIY Radmon Project Description.pdf">project description document</a> for details on procuring the materials, assembling the background radiation monitor and installing the software.</p>
9
+<p>You can find all the required software available under open source, GNU license at this project site. Please read the <a href="./docs/DIY Radmon Project Description.pdf">project description document</a> for details on procuring the materials, assembling the background radiation monitor and installing the software.</p>
10 10
 
11 11
 <p>This project encompasses two software components. One component runs on the Arduino Uno with attached Ethernet shield. The other component runs on a Linux web server. Besides the Arduino Uno with attached Ethernet shield, the other required component is a modified Mighty Ohm Geiger counter.  The server side component may be easily implement with a Raspberry Pi configured as a web server.</p>
12 12