Browse code

revised 2016.10.03

Fractal authored on 10/03/2016 22:52:07
Showing 3 changed files
1 1
Binary files a/DIY Radmon Project Description.pdf and b/DIY Radmon Project Description.pdf differ
... ...
@@ -2,7 +2,7 @@
2 2
 <body>
3 3
 <h4>Background Radiation Monitoring Device</h4>
4 4
 
5
-<p>Building a simple background radiation monitor provides an excellent introduction to "Internet of Things" ideas and concepts. This project site contains all you need to know in order to build your own background radiation monitor - a Geiger counter "Internet Thing" - for under $150 in parts.</p>
5
+<p>Building a simple background radiation monitor provides an excellent introduction to "Web of Things" ideas and concepts. This project site contains all you need to know in order to build your own background radiation monitor - a Geiger counter "Internet Thing" - for under $150 in parts.</p>
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
 
... ...
@@ -10,7 +10,7 @@
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.</p>
12 12
 
13
-<img src="docs/RadiationMonitor.jpg"><br>
13
+<img src="MtyOhmGeigerCounter.jpg"><br>
14 14
 <b>Radiation monitor electronics assembly mounted in box.</b>
15 15
 <br>
16 16
 </body>
17 17
old mode 100644
18 18
new mode 100755
... ...
@@ -49,19 +49,22 @@ _OUTPUT_DATA_FILE = "/tmp/radmon/radmonData.js" # output file used by HTML docs
49 49
 
50 50
     ### GLOBAL CONSTANTS ###
51 51
 
52
-_DEFAULT_WEB_DATA_UPDATE_INTERVAL = 10
52
+_DEFAULT_DATA_REQUEST_INTERVAL = 10 # interval between data requests to radiation monitor
53 53
 _CHART_UPDATE_INTERVAL = 300 # defines how often the charts get updated
54 54
 _DATABASE_UPDATE_INTERVAL = 30 # defines how often the database gets updated
55 55
 _HTTP_REQUEST_TIMEOUT = 5 # number seconds to wait for a response to HTTP request
56
+_MAX_RADIATION_MONITOR_OFFLINE_COUNT = 3 # max number of failed data requests allowed
56 57
 _CHART_WIDTH = 600
57 58
 _CHART_HEIGHT = 150
59
+_DEFAULT_RADIATION_MONITOR_URL = "{your radiation monitor url}"
58 60
 
59 61
    ### GLOBAL VARIABLES ###
60 62
 
61
-webUpdateInterval = _DEFAULT_WEB_DATA_UPDATE_INTERVAL  # web update frequency
62
-deviceUrl = "{your device url}"  # radiation monitor network address
63
-deviceOnline = True
64 63
 debugOption = False
64
+radiationMonitorOnline = True
65
+radiationMonitorOfflineCount = 0
66
+dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL  # web update frequency
67
+radiationMonitorUrl = _DEFAULT_RADIATION_MONITOR_URL  # radiation monitor network address
65 68
 
66 69
 
67 70
   ###  PRIVATE METHODS  ###
... ...
@@ -72,7 +75,7 @@ def getTimeStamp():
72 75
     Parameters: none
73 76
     Returns string containing the time stamp.
74 77
     """
75
-    return time.strftime( "%Y/%m/%d %T", time.localtime() )
78
+    return time.strftime( "%m/%d/%Y %T", time.localtime() )
76 79
 ##end def
77 80
 
78 81
 def setOfflineStatus(dData):
... ...
@@ -82,13 +85,18 @@ def setOfflineStatus(dData):
82 85
            dData - dictionary object containing weather data
83 86
        Returns nothing.
84 87
     """
85
-    global deviceOnline
88
+    global radiationMonitorOnline, radiationMonitorOfflineCount
89
+
90
+    radiationMonitorOfflineCount += 1
91
+
92
+    if radiationMonitorOfflineCount < _MAX_RADIATION_MONITOR_OFFLINE_COUNT:
93
+        return
86 94
 
87 95
     # If the radiation monitor was previously online, then send a message
88 96
     # that we are now offline.
89
-    if deviceOnline:
90
-        print "%s: radmon offline" % getTimeStamp()
91
-        deviceOnline = False
97
+    if radiationMonitorOnline:
98
+        print "%s: radiation monitor offline" % getTimeStamp()
99
+        radiationMonitorOnline = False
92 100
 
93 101
     # Set data items to blank.
94 102
     dData['UTC'] = ''
... ...
@@ -104,40 +112,48 @@ def setOfflineStatus(dData):
104 112
 
105 113
   ###  PUBLIC METHODS  ###
106 114
 
107
-def getRadmonData(deviceUrl, HttpRequestTimeout):
115
+def getRadiationData():
108 116
     """Send http request to radiation monitoring device.  The response
109 117
        from the device contains the radiation data.  The data is formatted
110 118
        as an html document.
111 119
     Parameters: 
112
-        deviceUrl - url of radiation monitoring device
120
+        radiationMonitorUrl - url of radiation monitoring device
113 121
         HttpRequesttimeout - how long to wait for device
114 122
                              to respond to http request
115 123
     Returns a string containing the radiation data, or None if
116 124
     not successful.
117 125
     """
118
-    global deviceOnline
126
+    global radiationMonitorOnline, radiationMonitorOfflineCount
119 127
 
120 128
     try:
121
-        conn = urllib2.urlopen(deviceUrl + "/rdata", timeout=HttpRequestTimeout)
129
+        conn = urllib2.urlopen(radiationMonitorUrl + "/rdata",
130
+                               timeout=_HTTP_REQUEST_TIMEOUT)
131
+
132
+        # Format received data into a single string.
133
+        content = ""
134
+        for line in conn:
135
+            content += line.strip()
136
+        del conn
137
+
122 138
     except Exception, exError:
123 139
         # If no response is received from the device, then assume that
124 140
         # the device is down or unavailable over the network.  In
125 141
         # that case set the status of the device to offline.
126 142
         if debugOption:
127
-            print "getRadmonData: %s\n" % exError
143
+            print "http error: %s" % exError
128 144
         return None
129 145
 
146
+    radiationMonitorOfflineCount = 0
147
+
130 148
     # If the radiation monitor was previously offline, then send a message
131 149
     # that we are now online.
132
-    if not deviceOnline:
133
-        print "%s radmon online" % getTimeStamp()
134
-        deviceOnline = True
135
-
136
-    # Format received data into a single string.
137
-    content = ""
138
-    for line in conn:
139
-         content += line.strip()
140
-    del conn
150
+    if not radiationMonitorOnline:
151
+        print "%s radiation monitor online" % getTimeStamp()
152
+        radiationMonitorOnline = True
153
+
154
+    if debugOption:
155
+        print "http request successful"
156
+        #print content
141 157
     return content
142 158
 ##end def
143 159
 
... ...
@@ -236,7 +252,7 @@ def updateDatabase(dData):
236 252
     strCmd = "rrdtool update %s %s:%s:%s" % \
237 253
                        (_RRD_FILE, dData['UTC'], dData['CPM'], Svvalue)
238 254
     if debugOption:
239
-        print "%s\n" % strCmd # DEBUG
255
+        print "%s" % strCmd # DEBUG
240 256
 
241 257
     # Run the command as a subprocess.
242 258
     try:
... ...
@@ -250,13 +266,22 @@ def updateDatabase(dData):
250 266
     return True
251 267
 ##end def
252 268
 
253
-def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTrend):
269
+def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
270
+                lower, upper, addTrend, autoScale):
254 271
     """Uses rrdtool to create a graph of specified weather data item.
255 272
        Parameters:
256 273
            fileName - name of graph image file
257 274
            dataItem - data item to be graphed
258
-           gTitle - a title for the graph
259
-           gStart - beginning time of the data to be graphed
275
+           gLabel - string containing a graph label for the data item
276
+           gTitle - string containing a title for the graph
277
+           lower - lower bound for graph ordinate #NOT USED
278
+           upper - upper bound for graph ordinate #NOT USED
279
+           addTrend - 0, show only graph data
280
+                      1, show only a trend line
281
+                      2, show a trend line and the graph data
282
+           autoScale - if True, then use vertical axis auto scaling
283
+               (lower and upper parameters are ignored), otherwise use
284
+               lower and upper parameters to set vertical axis scale
260 285
        Returns true if successful, false otherwise.
261 286
     """
262 287
     gPath = _TMP_DIRECTORY + '/' + fileName + ".png"
... ...
@@ -270,26 +295,29 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTre
270 295
     strCmd = "rrdtool graph %s -a PNG -s %s -e now -w %s -h %s " \
271 296
              % (gPath, gStart, _CHART_WIDTH, _CHART_HEIGHT)
272 297
    
273
-    # Set the range of the chart ordinate dataum.
298
+    # Set the range and scaling of the chart y-axis.
274 299
     if lower < upper:
275
-        strCmd  +=  "-l %s -u %s " % (lower, upper)
276
-    else:
277
-        #strCmd += "-A -Y "
278
-        strCmd += "-Y "
300
+        strCmd  +=  "-l %s -u %s -r " % (lower, upper)
301
+    elif autoScale:
302
+        strCmd += "-A "
303
+    strCmd += "-Y "
279 304
 
280 305
     # Set the chart ordinate label and chart title. 
281 306
     strCmd += "-v %s -t %s " % (gLabel, gTitle)
282
-
307
+ 
283 308
     # Show the data, or a moving average trend line over
284 309
     # the data, or both.
285
-    strCmd += "DEF:%s=%s:%s:LAST " % (dataItem, _RRD_FILE, dataItem)
286
-
287
-    if addTrend == 0 or addTrend == 2:
288
-        strCmd += "LINE1:%s\#0400ff " % (dataItem)
289
-    if addTrend == 1 or addTrend == 2:
290
-        strCmd += "CDEF:smoothed=%s,%s,TREND LINE1:smoothed#ff0000" \
291
-                  % (dataItem, trendWindow[gStart])
292
-       
310
+    strCmd += "DEF:dSeries=%s:%s:LAST " % (_RRD_FILE, dataItem)
311
+    if addTrend == 0:
312
+        strCmd += "LINE1:dSeries#0400ff "
313
+    elif addTrend == 1:
314
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
315
+                  % trendWindow[gStart]
316
+    elif addTrend == 2:
317
+        strCmd += "LINE1:dSeries#0400ff "
318
+        strCmd += "CDEF:smoothed=dSeries,%s,TREND LINE3:smoothed#ff0000 " \
319
+                  % trendWindow[gStart]
320
+     
293 321
     if debugOption:
294 322
         print "%s\n" % strCmd # DEBUG
295 323
     
... ...
@@ -308,6 +336,27 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart, lower, upper, addTre
308 336
 
309 337
 ##end def
310 338
 
339
+def generateGraphs():
340
+    """Generate graphs for display in html documents.
341
+       Parameters: none
342
+       Returns nothing.
343
+    """
344
+    autoScale = False
345
+
346
+    createGraph('radGraph1', 'CPM', 'counts\ per\ minute', 
347
+                'CPM\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
348
+    createGraph('radGraph2', 'SvperHr', 'Sv\ per\ hour',
349
+                'Sv/Hr\ -\ Last\ 24\ Hours', 'end-1day', 0, 0, 2, autoScale)
350
+    createGraph('radGraph3', 'CPM', 'counts\ per\ minute',
351
+                'CPM\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
352
+    createGraph('radGraph4', 'SvperHr', 'Sv\ per\ hour',
353
+                'Sv/Hr\ -\ Last\ 4\ Weeks', 'end-4weeks', 0, 0, 2, autoScale)
354
+    createGraph('radGraph5', 'CPM', 'counts\ per\ minute',
355
+                'CPM\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
356
+    createGraph('radGraph6', 'SvperHr', 'Sv\ per\ hour',
357
+                'Sv/Hr\ -\ Past\ Year', 'end-12months', 0, 0, 2, autoScale)
358
+##end def
359
+
311 360
 def getCLarguments():
312 361
     """Get command line arguments.  There are three possible arguments
313 362
           -d turns on debug mode
... ...
@@ -315,7 +364,7 @@ def getCLarguments():
315 364
           -u sets the url of the radiation monitoring device
316 365
        Returns nothing.
317 366
     """
318
-    global debugOption, webUpdateInterval, deviceUrl
367
+    global debugOption, dataRequestInterval, radiationMonitorUrl
319 368
 
320 369
     index = 1
321 370
     while index < len(sys.argv):
... ...
@@ -323,34 +372,21 @@ def getCLarguments():
323 372
             debugOption = True
324 373
         elif sys.argv[index] == '-t':
325 374
             try:
326
-                webUpdateInterval = abs(int(sys.argv[index + 1]))
375
+                dataRequestInterval = abs(int(sys.argv[index + 1]))
327 376
             except:
328 377
                 print "invalid polling period"
329 378
                 exit(-1)
330 379
             index += 1
331 380
         elif sys.argv[index] == '-u':
332
-            deviceUrl = sys.argv[index + 1]
381
+            radiationMonitorUrl = sys.argv[index + 1]
333 382
             index += 1
334 383
         else:
335 384
             cmd_name = sys.argv[0].split('/')
336
-            print "Usage: %s {-v} {-d}" % cmd_name[-1]
385
+            print "Usage: %s [-d] [-t seconds] [-u url}" % cmd_name[-1]
337 386
             exit(-1)
338 387
         index += 1
339 388
 ##end def
340 389
 
341
-def generateGraphs():
342
-    """Generate graphs for display in html documents.
343
-       Parameters: none
344
-       Returns nothing.
345
-    """
346
-    createGraph('radGraph1', 'CPM', "'counts per minute'", "'CPM - Last 24 Hours'", 'end-1day', 0, 0, 2)
347
-    createGraph('radGraph2', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 24 Hours'", 'end-1day', 0, 0, 2)
348
-    createGraph('radGraph3', 'CPM', "'counts per minute'", "'CPM - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
349
-    createGraph('radGraph4', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Last 4 Weeks'", 'end-4weeks', 0, 0, 2)
350
-    createGraph('radGraph5', 'CPM', "'counts per minute'", "'CPM - Past Year'", 'end-12months', 0, 0, 2)
351
-    createGraph('radGraph6', 'SvperHr', "'Sv per hour'", "'Sv/Hr - Past Year'", 'end-12months', 0, 0, 2)
352
-##end def
353
-
354 390
 def main():
355 391
     """Handles timing of events and acts as executive routine managing all other
356 392
        functions.
... ...
@@ -358,11 +394,10 @@ def main():
358 394
        Returns nothing.
359 395
     """
360 396
 
397
+    lastDataRequestTime = -1 # last time output JSON file updated
361 398
     lastChartUpdateTime = - 1 # last time charts generated
362 399
     lastDatabaseUpdateTime = -1 # last time the rrdtool database updated
363
-    lastWebUpdateTime = -1 # last time output JSON file updated
364 400
     dData = {}  # dictionary object for temporary data storage
365
-    lsData = [] # list object for temporary data storage
366 401
 
367 402
     ## Get command line arguments.
368 403
     getCLarguments()
... ...
@@ -371,24 +406,25 @@ def main():
371 406
     if not os.path.isdir(_TMP_DIRECTORY):
372 407
         os.makedirs(_TMP_DIRECTORY)
373 408
 
374
-    ## Exit with error if cannot find the rrdtool database file.
409
+    ## Exit with error if rrdtool database does not exist.
375 410
     if not os.path.exists(_RRD_FILE):
376
-        print "cannot find rrdtool database file: terminating"
411
+        print "cannot find rrdtool database\nuse createWeatherRrd script to" \
412
+              " create rrdtool database\n"
377 413
         exit(1)
378 414
  
379 415
     ## main loop
380 416
     while True:
381 417
 
382
-        currentTime = time.time()
418
+        currentTime = time.time() # get current time in seconds
383 419
 
384
-        # At the radiation device query interval request and process
385
-        # the data from the device.
386
-        if currentTime - lastWebUpdateTime > webUpdateInterval:
387
-            lastWebUpdateTime = currentTime
420
+        # Every web update interval request data from the radiation
421
+        # monitor and process the received data.
422
+        if currentTime - lastDataRequestTime > dataRequestInterval:
423
+            lastDataRequestTime = currentTime
388 424
             result = True
389 425
 
390 426
             # Get the data string from the device.
391
-            sData = getRadmonData(deviceUrl, _HTTP_REQUEST_TIMEOUT)
427
+            sData = getRadiationData()
392 428
             if sData == None:
393 429
                 setOfflineStatus(dData)
394 430
                 result = False
... ...
@@ -422,12 +458,12 @@ def main():
422 458
 
423 459
         elapsedTime = time.time() - currentTime
424 460
         if debugOption:
425
-            print "web update: %6f sec\n" % elapsedTime
426
-        remainingTime = webUpdateInterval - elapsedTime
427
-        if remainingTime > 0:
461
+            print "processing time: %6f sec\n" % elapsedTime
462
+        remainingTime = dataRequestInterval - elapsedTime
463
+        if remainingTime > 0.0:
428 464
             time.sleep(remainingTime)
429
-             
430 465
     ## end while
466
+    return
431 467
 ## end def
432 468
 
433 469
 if __name__ == '__main__':