Browse code

added class methods for configuring sensor; added exception catching for json formatting code

Gandolf authored on 06/20/2021 00:40:59
Showing 6 changed files
... ...
@@ -7,7 +7,7 @@
7 7
 # Description: Creates a rrdtool database for use by the power agent to
8 8
 # store the data from the power and temperature sensors.  The agent uses
9 9
 # the data in the database to generate graphic charts for display in the
10
-# weather station web page.
10
+# node power web page.
11 11
 #
12 12
 # Copyright 2021 Jeff Owrey
13 13
 #    This program is free software: you can redistribute it and/or modify
... ...
@@ -38,32 +38,40 @@ CUR_REG = 0x1
38 38
 VOLT_REG = 0x2
39 39
 PWR_REG = 0x3
40 40
 
41
+# Define default sm bus address.
42
+DEFAULT_BUS_ADDRESS = 0x40
43
+DEFAULT_BUS_NUMBER = 1
44
+
45
+# Define the default sensor configuration.  See the INA260 data sheet
46
+# for meaning of each bit.  The following bytes are written to the
47
+# configuration register
48
+#     byte 1: 11100000
49
+#     byte 2: 00100111
50
+DEFAULT_CONFIG = 0xE027
51
+
41 52
 class ina260:
42 53
     # Initialize the INA260 sensor at the supplied address (default
43 54
     # address is 0x40), and supplied bus (default is 1).  Creates
44 55
     # a new SMBus object for each instance of this class.  Writes
45 56
     # configuration data (two bytes) to the INA260 configuration
46 57
     # register.
47
-    def __init__(self, sAddr=0x40, sbus=1):
48
-        # Instantiate a smbus object
58
+    def __init__(self, sAddr=DEFAULT_BUS_ADDRESS,
59
+                       sbus=DEFAULT_BUS_NUMBER,
60
+                       config=DEFAULT_CONFIG):
61
+        # Instantiate a smbus object.
49 62
         self.sensorAddr = sAddr
50 63
         self.bus = smbus.SMBus(sbus)
51
-        # Initialize INA260 sensor.  See the data sheet for meaning of
52
-        # each bit.  The following bytes are written to the configuration
53
-        # register
54
-        #     byte 1: 11100000
55
-        #     byte 2: 00100111
56
-        initData = [0b11100000, 0b00100111]
57
-        #initData = [0x60, 0x27]
64
+        # Initialize INA260 sensor.  
65
+        initData = [(config >> 8), (config & 0x00FF)]
58 66
         self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData)
59 67
     ## end def
60 68
 
61 69
     def status(self):
62
-        # Read configuration data
70
+        # Read manufacture identification data.
63 71
         mfcid = self.bus.read_i2c_block_data(self.sensorAddr, ID_REG, 2)
64 72
         mfcidB1 = format(mfcid[0], "08b")
65 73
         mfcidB2 = format(mfcid[1], "08b")
66
-        # Read configuration data
74
+        # Read configuration data.
67 75
         config = self.bus.read_i2c_block_data(self.sensorAddr, CONFIG_REG, 2)
68 76
         configB1 = format(config[0], "08b")
69 77
         configB2 = format(config[1], "08b")
... ...
@@ -97,9 +105,9 @@ class ina260:
97 105
         # Convert from two's complement to integer.
98 106
         # If d15 is 1, the the number is a negative two's complement
99 107
         # number.  The absolute value is 2^16 - 1 minus the value
100
-        # of d14-d0 taken as a positive number.
108
+        # of d15-d0 taken as a positive number.
101 109
         if bdata > 0x7FFF:
102
-            bdata = -(0xFFFF - bdata) # 0xFFFF is 2^16 - 1
110
+            bdata = -(0xFFFF - bdata) # 0xFFFF equals 2^16 - 1
103 111
         # Convert integer data to mAmps.
104 112
         mAmps = bdata * 1.25  # LSB is 1.25 mA
105 113
         return mAmps
... ...
@@ -154,14 +162,14 @@ def test():
154 162
     pwr1 = ina260(0x40, 1)
155 163
     # Read the INA260 configuration register and manufacturer's ID.
156 164
     data = pwr1.status()
157
-    print "manufacturer ID: %s %s\nconfiguration register: %s %s\n" % data
165
+    print("manufacturer ID: %s %s\nconfiguration register: %s %s\n" % data)
158 166
     # Print out sensor values.
159 167
     while True:
160
-        print "current register: %s %s" % pwr1.getCurrentReg()
161
-        print "%6.2f mA" % pwr1.getCurrent()
162
-        print "volt register: %s %s" % pwr1.getVoltageReg()
163
-        print "%6.2f V" % pwr1.getVoltage()
164
-        print "%6.2f mW\n" % pwr1.getPower()
168
+        print("current register: %s %s" % pwr1.getCurrentReg())
169
+        print("%6.2f mA" % pwr1.getCurrent())
170
+        print("volt register: %s %s" % pwr1.getVoltageReg())
171
+        print("%6.2f V" % pwr1.getVoltage())
172
+        print("%6.2f mW\n" % pwr1.getPower())
165 173
         time.sleep(2)
166 174
 ## end def
167 175
 
... ...
@@ -39,11 +39,15 @@ import signal
39 39
 import subprocess
40 40
 import multiprocessing
41 41
 import time
42
+import json
42 43
 
43 44
 # Import sensor libraries.
44 45
 import ina260 # power sensor
45 46
 import tmp102 # temperature sensor
46 47
 
48
+    ### ENVIRONMENT ###
49
+_USER = os.environ['USER']
50
+
47 51
     ### SENSOR BUS ADDRESSES ###
48 52
 
49 53
 # Set bus addresses of sensors.
... ...
@@ -55,8 +59,7 @@ _BUS_SEL = 1
55 59
 
56 60
     ### FILE AND FOLDER LOCATIONS ###
57 61
 
58
-_USER = os.environ['USER']
59
-# folder to contain dynamic data objects
62
+# folder to contain html
60 63
 _DOCROOT_PATH = "/home/%s/public_html/power/" % _USER
61 64
 # folder to contain charts and output data file
62 65
 _CHARTS_DIRECTORY = _DOCROOT_PATH + "dynamic/"
... ...
@@ -67,10 +70,10 @@ _RRD_FILE = "/home/%s/database/powerData.rrd" % _USER
67 70
 
68 71
     ### GLOBAL CONSTANTS ###
69 72
 
70
-# rrdtool database update interval in seconds
71
-_DATABASE_UPDATE_INTERVAL = 30
72 73
 # sensor data request interval in seconds
73 74
 _DEFAULT_DATA_REQUEST_INTERVAL = 2
75
+# rrdtool database update interval in seconds
76
+_DATABASE_UPDATE_INTERVAL = 30
74 77
 # chart update interval in seconds
75 78
 _CHART_UPDATE_INTERVAL = 600
76 79
 # standard chart width in pixels
... ...
@@ -85,6 +88,7 @@ _AVERAGE_LINE_COLOR = '#006600'
85 88
 # debug output options
86 89
 debugOption = False
87 90
 verboseDebug = False
91
+
88 92
 # frequency of data requests to sensors
89 93
 dataRequestInterval = _DEFAULT_DATA_REQUEST_INTERVAL
90 94
 # how often charts get updated
... ...
@@ -101,7 +105,7 @@ atmp = tmp102.tmp102(_AMB_TMP_SENSOR_ADDR, _BUS_SEL)
101 105
 
102 106
 def getTimeStamp():
103 107
     """
104
-    Set the error message time stamp to the local system time.
108
+    Get the local time and format as a text string.
105 109
     Parameters: none
106 110
     Returns: string containing the time stamp
107 111
     """
... ...
@@ -109,57 +113,58 @@ def getTimeStamp():
109 113
 ## end def
110 114
 
111 115
 def getEpochSeconds(sTime):
112
-    """Convert the time stamp supplied in the weather data string
113
-       to seconds since 1/1/1970 00:00:00.
114
-       Parameters: 
115
-           sTime - the time stamp to be converted must be formatted
116
+    """
117
+    Convert the time stamp to seconds since 1/1/1970 00:00:00.
118
+    Parameters: 
119
+        sTime - the time stamp to be converted must be formatted
116 120
                    as %m/%d/%Y %H:%M:%S
117
-       Returns: epoch seconds
121
+    Returns: epoch seconds
118 122
     """
119 123
     try:
120 124
         t_sTime = time.strptime(sTime, '%m/%d/%Y %H:%M:%S')
121
-    except Exception, exError:
122
-        print '%s getEpochSeconds: %s' % (getTimeStamp(), exError)
125
+    except Exception as exError:
126
+        print('%s getEpochSeconds: %s' % (getTimeStamp(), exError))
123 127
         return None
124 128
     tSeconds = int(time.mktime(t_sTime))
125 129
     return tSeconds
126 130
 ## end def
127 131
 
128 132
 def terminateAgentProcess(signal, frame):
129
-    """Send a message to the log when the agent process gets killed
130
-       by the operating system.  Inform downstream clients
131
-       by removing output data files.
132
-       Parameters:
133
-           signal, frame - dummy parameters
134
-       Returns: nothing
133
+    """
134
+    Send a message to the log when the agent process gets killed
135
+    by the operating system.  Inform downstream clients
136
+    by removing output data files.
137
+    Parameters:
138
+        signal, frame - dummy parameters
139
+    Returns: nothing
135 140
     """
136 141
     # Inform downstream clients by removing output data file.
137 142
     if os.path.exists(_OUTPUT_DATA_FILE):
138 143
        os.remove(_OUTPUT_DATA_FILE)
139
-    print '%s terminating npw agent process' % \
140
-              (getTimeStamp())
144
+    print('%s terminating npw agent process' % getTimeStamp())
141 145
     sys.exit(0)
142 146
 ## end def
143 147
 
144 148
   ###  PUBLIC METHODS  ###
145 149
 
146 150
 def getSensorData(dData):
147
-    """Poll sensors for data. Store the data in a dictionary object for
148
-       use by other subroutines.  The dictionary object passed in should
149
-       an empty dictionary, i.e., dData = { }.
150
-       Parameters: dData - a dictionary object to contain the sensor data
151
-       Returns: True if successful, False otherwise
152 151
     """
152
+    Poll sensors for data. Store the data in a dictionary object for
153
+    use by other subroutines.  The dictionary object passed in should
154
+    an empty dictionary, i.e., dData = { }.
155
+    Parameters: dData - a dictionary object to contain the sensor data
156
+    Returns: True if successful, False otherwise
157
+    """
158
+    dData["time"] = getTimeStamp()
159
+ 
153 160
     try:
154
-        dData["time"] = getTimeStamp()
155 161
         dData["current"] = pwr.getCurrent()
156 162
         dData["voltage"] = pwr.getVoltage()
157 163
         dData["power"] = pwr.getPower()
158 164
         dData["battemp"] = btmp.getTempF()
159 165
         dData["ambtemp"] = atmp.getTempF()
160
-     
161
-    except Exception, exError:
162
-        print "%s sensor error: %s" % (getTimeStamp(), exError)
166
+    except Exception as exError:
167
+        print("%s sensor error: %s" % (getTimeStamp(), exError))
163 168
         return False
164 169
 
165 170
     return True
... ...
@@ -182,29 +187,33 @@ def updateDatabase(dData):
182 187
              dData['voltage'], dData['power'], dData['battemp'], \
183 188
              dData['ambtemp'])
184 189
 
185
-    if debugOption:
186
-        print "%s" % strCmd # DEBUG
190
+    if verboseDebug:
191
+        print("%s" % strCmd) # DEBUG
187 192
 
188 193
     # Run the command as a subprocess.
189 194
     try:
190
-        subprocess.check_output(strCmd, shell=True,  \
191
-                             stderr=subprocess.STDOUT)
192
-    except subprocess.CalledProcessError, exError:
193
-        print "%s: rrdtool update failed: %s" % \
194
-                    (getTimeStamp(), exError.output)
195
+        subprocess.check_output(strCmd, shell=True, \
196
+            stderr=subprocess.STDOUT)
197
+    except subprocess.CalledProcessError as exError:
198
+        print("%s: rrdtool update failed: %s" % \
199
+            (getTimeStamp(), exError.output))
195 200
         return False
196 201
 
202
+    if debugOption and not verboseDebug:
203
+        print("database updated")
204
+
197 205
     return True
198 206
 ## end def
199 207
 
200
-def writeOutputDataFile(dData):
201
-    """Write node data items to the output data file, formatted as 
202
-       a Javascript file.  This file may then be requested and used by
203
-       by downstream clients, for instance, an HTML document.
204
-       Parameters:
205
-           dData - a dictionary containing the data to be written
208
+def writeOutputFile(dData):
209
+    """
210
+    Write sensor data items to the output data file, formatted as 
211
+    a Javascript file.  This file may then be requested and used by
212
+    by downstream clients, for instance, an HTML document.
213
+    Parameters:
214
+        dData - a dictionary containing the data to be written
206 215
                    to the output data file
207
-       Returns: True if successful, False otherwise
216
+        Returns: True if successful, False otherwise
208 217
     """
209 218
     # Write a JSON formatted file for use by html clients.  The following
210 219
     # data items are sent to the client file.
... ...
@@ -213,54 +222,58 @@ def writeOutputDataFile(dData):
213 222
     #    * The sensor values
214 223
 
215 224
     # Create a JSON formatted string from the sensor data.
216
-    sData = "[{\"period\":\"%s\", " % \
217
-           (chartUpdateInterval)
218
-    for key in dData:
219
-        sData += '\"%s\":\"%s\", ' % (key, dData[key])
220
-    sData = sData[:-2] + '}]\n'
225
+    try:
226
+        jsData = json.loads("{}")
227
+        for key in dData:
228
+            jsData.update({key:dData[key]})
229
+        jsData.update({"chartUpdateInterval": chartUpdateInterval})
230
+        sData = "[%s]" % json.dumps(jsData)
231
+    except Exception as exError:
232
+        print("%s writeOutputFile: %s" % (getTimeStamp(), exError))
233
+        return False
234
+
235
+    if verboseDebug:
236
+        print(sData)
221 237
 
222 238
     # Write the JSON formatted data to the output data file.
239
+
223 240
     try:
224 241
         fc = open(_OUTPUT_DATA_FILE, "w")
225 242
         fc.write(sData)
226 243
         fc.close()
227
-    except Exception, exError:
228
-        print "%s write output file failed: %s" % \
229
-              (getTimeStamp(), exError)
244
+    except Exception as exError:
245
+        print("%s write output file failed: %s" % \
246
+              (getTimeStamp(), exError))
230 247
         return False
231 248
 
232
-    if verboseDebug:
233
-        print sData[:-1]
234
-    if debugOption:
235
-        print "writing output data file: %d bytes" % len(sData)
236
-
237 249
     return True
238 250
 ## end def
239 251
 
240 252
 def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
241 253
                 lower=0, upper=0, trendLine=0, scaleFactor=1,
242 254
                 autoScale=True, alertLine=""):
243
-    """Uses rrdtool to create a graph of specified node data item.
244
-       Parameters:
245
-           fileName - name of file containing the graph
246
-           dataItem - data item to be graphed
247
-           gLabel - string containing a graph label for the data item
248
-           gTitle - string containing a title for the graph
249
-           gStart - beginning time of the graphed data
250
-           lower - lower bound for graph ordinate #NOT USED
251
-           upper - upper bound for graph ordinate #NOT USED
252
-           trendLine 
253
-                0, show only graph data
254
-                1, show only a trend line
255
-                2, show a trend line and the graph data
256
-           scaleFactor - amount to pre-scale the data before charting
257
-                the data [default=1]
258
-           autoScale - if True, then use vertical axis auto scaling
259
-                (lower and upper parameters must be zero)
260
-           alertLine - value for which to print a critical
261
-                low voltage alert line on the chart. If not provided
262
-                alert line will not be printed.
263
-       Returns: True if successful, False otherwise
255
+    """
256
+    Uses rrdtool to create a graph of specified sensor data item.
257
+    Parameters:
258
+        fileName - name of file containing the graph
259
+        dataItem - data item to be graphed
260
+        gLabel - string containing a graph label for the data item
261
+        gTitle - string containing a title for the graph
262
+        gStart - beginning time of the graphed data
263
+        lower - lower bound for graph ordinate #NOT USED
264
+        upper - upper bound for graph ordinate #NOT USED
265
+        trendLine 
266
+            0, show only graph data
267
+            1, show only a trend line
268
+            2, show a trend line and the graph data
269
+        scaleFactor - amount to pre-scale the data before charting
270
+            the data [default=1]
271
+        autoScale - if True, then use vertical axis auto scaling
272
+            (lower and upper parameters must be zero)
273
+        alertLine - value for which to print a critical
274
+            low voltage alert line on the chart. If not provided
275
+            alert line will not be printed.
276
+    Returns: True if successful, False otherwise
264 277
     """
265 278
     gPath = _CHARTS_DIRECTORY + fileName + ".png"
266 279
     trendWindow = { 'end-1day': 7200,
... ...
@@ -302,27 +315,28 @@ def createGraph(fileName, dataItem, gLabel, gTitle, gStart,
302 315
         strCmd += "HRULE:%s#FF0000:Critical\ Low\ Voltage " % (alertLine)
303 316
      
304 317
     if verboseDebug:
305
-        print "%s" % strCmd # DEBUG
318
+        print("%s" % strCmd) # DEBUG
306 319
     
307 320
     # Run the formatted rrdtool command as a subprocess.
308 321
     try:
309 322
         result = subprocess.check_output(strCmd, \
310 323
                      stderr=subprocess.STDOUT,   \
311 324
                      shell=True)
312
-    except subprocess.CalledProcessError, exError:
313
-        print "rrdtool graph failed: %s" % (exError.output)
325
+    except subprocess.CalledProcessError as exError:
326
+        print("rrdtool graph failed: %s" % (exError.output))
314 327
         return False
315 328
 
316 329
     if debugOption:
317
-        print "rrdtool graph: %s\n" % result,
330
+        print("rrdtool graph: %s" % result.decode('utf-8'))
318 331
     return True
319 332
 
320 333
 ## end def
321 334
 
322 335
 def generateGraphs():
323
-    """Generate graphs for display in html documents.
324
-       Parameters: none
325
-       Returns: nothing
336
+    """
337
+    Generate graphs for display in html documents.
338
+    Parameters: none
339
+    Returns: nothing
326 340
     """
327 341
 
328 342
     # 24 hour stock charts
... ...
@@ -381,12 +395,13 @@ def generateGraphs():
381 395
 ## end def
382 396
 
383 397
 def getCLarguments():
384
-    """Get command line arguments.  There are three possible arguments
385
-          -d turns on debug mode
386
-          -v turns on verbose debug mode
387
-          -p sets the sensor query period
388
-          -c sets the chart update period
389
-       Returns: nothing
398
+    """
399
+    Get command line arguments.  There are three possible arguments
400
+        -d turns on debug mode
401
+        -v turns on verbose debug mode
402
+        -p sets the sensor query period
403
+        -c sets the chart update period
404
+    Returns: nothing
390 405
     """
391 406
     global debugOption, verboseDebug, dataRequestInterval, chartUpdateInterval
392 407
 
... ...
@@ -401,36 +416,37 @@ def getCLarguments():
401 416
             try:
402 417
                 dataRequestInterval = abs(int(sys.argv[index + 1]))
403 418
             except:
404
-                print "invalid sensor query period"
419
+                print("invalid sensor query period")
405 420
                 exit(-1)
406 421
             index += 1
407 422
         elif sys.argv[index] == '-c':
408 423
             try:
409 424
                 chartUpdateInterval = abs(int(sys.argv[index + 1]))
410 425
             except:
411
-                print "invalid chart update period"
426
+                print("invalid chart update period")
412 427
                 exit(-1)
413 428
             index += 1
414 429
         else:
415 430
             cmd_name = sys.argv[0].split('/')
416
-            print "Usage: %s [-d | v] [-p seconds] [-c seconds]" \
417
-                  % cmd_name[-1]
431
+            print("Usage: %s [-d | v] [-p seconds] [-c seconds]" \
432
+                  % cmd_name[-1])
418 433
             exit(-1)
419 434
         index += 1
420 435
 ##end def
421 436
 
422 437
 def main():
423
-    """Handles timing of events and acts as executive routine managing
424
-       all other functions.
425
-       Parameters: none
426
-       Returns: nothing
438
+    """
439
+    Handles timing of events and acts as executive routine managing
440
+    all other functions.
441
+    Parameters: none
442
+    Returns: nothing
427 443
     """
428 444
     global dataRequestInterval
429 445
 
430 446
     signal.signal(signal.SIGTERM, terminateAgentProcess)
447
+    signal.signal(signal.SIGINT, terminateAgentProcess)
431 448
 
432
-    print '%s starting up node power agent process' % \
433
-                  (getTimeStamp())
449
+    print('%s starting up node power agent process' % getTimeStamp())
434 450
 
435 451
     # last time output JSON file updated
436 452
     lastDataRequestTime = -1
... ...
@@ -444,9 +460,9 @@ def main():
444 460
 
445 461
     ## Exit with error if rrdtool database does not exist.
446 462
     if not os.path.exists(_RRD_FILE):
447
-        print 'rrdtool database does not exist\n' \
448
-              'use createArednsigRrd script to ' \
449
-              'create rrdtool database\n'
463
+        print('rrdtool database does not exist\n' \
464
+              'use createPowerRrd script to ' \
465
+              'create rrdtool database\n')
450 466
         exit(1)
451 467
  
452 468
     ## main loop
... ...
@@ -454,8 +470,8 @@ def main():
454 470
 
455 471
         currentTime = time.time() # get current time in seconds
456 472
 
457
-        # Every web update interval request data from the aredn
458
-        # node and process the received data.
473
+        # Every data request interval read the sensors and process the
474
+        # data from the sensors.
459 475
         if currentTime - lastDataRequestTime > dataRequestInterval:
460 476
             lastDataRequestTime = currentTime
461 477
             dData = {}
... ...
@@ -466,17 +482,14 @@ def main():
466 482
  
467 483
             # If get data successful, write data to data files.
468 484
             if result:
469
-                result = writeOutputDataFile(dData)
470
-                pass
485
+                result = writeOutputFile(dData)
471 486
 
472 487
             # At the rrdtool database update interval, update the database.
473
-            if currentTime - lastDatabaseUpdateTime > \
474
-                    _DATABASE_UPDATE_INTERVAL:   
488
+            if result and (currentTime - lastDatabaseUpdateTime > \
489
+                           _DATABASE_UPDATE_INTERVAL):   
475 490
                 lastDatabaseUpdateTime = currentTime
476 491
                 ## Update the round robin database with the parsed data.
477
-                if result:
478
-                    updateDatabase(dData)
479
-                    pass
492
+                result = updateDatabase(dData)
480 493
 
481 494
         # At the chart generation interval, generate charts.
482 495
         if currentTime - lastChartUpdateTime > chartUpdateInterval:
... ...
@@ -490,10 +503,11 @@ def main():
490 503
         elapsedTime = time.time() - currentTime
491 504
         if debugOption:
492 505
             if result:
493
-                print "%s update successful:" % getTimeStamp(),
506
+                print("update successful: %6f sec\n"
507
+                      % elapsedTime)
494 508
             else:
495
-                print "%s update failed:" % getTimeStamp(),
496
-            print "%6f seconds processing time\n" % elapsedTime 
509
+                print("update failed: %6f sec\n"
510
+                      % elapsedTime)
497 511
         remainingTime = dataRequestInterval - elapsedTime
498 512
         if remainingTime > 0.0:
499 513
             time.sleep(remainingTime)
... ...
@@ -502,8 +516,4 @@ def main():
502 516
 ## end def
503 517
 
504 518
 if __name__ == '__main__':
505
-    try:
506
-        main()
507
-    except KeyboardInterrupt:
508
-        print '\n',
509
-        terminateAgentProcess('KeyboardInterrupt','Module')
519
+    main()
... ...
@@ -1,5 +1,6 @@
1 1
 #!/bin/bash
2
-#
2
+# Starts up the node power agent as a background process
3
+# and redirects output to a log file.
3 4
 
4 5
 APP_PATH="/home/$USER/bin"
5 6
 LOG_PATH="/home/$USER/log"
... ...
@@ -1,5 +1,5 @@
1 1
 #!/bin/bash
2
-# Stop the radmon agent process and clean up environment.
2
+# Stop the node power agent process and clean up environment.
3 3
 
4 4
 AGENT_NAME="[n]pwAgent.py"
5 5
 
... ...
@@ -1,4 +1,4 @@
1
-#!/usr/bin/python
1
+#!/usr/bin/python2
2 2
 #
3 3
 # Module: tmp102.py
4 4
 #
... ...
@@ -31,12 +31,23 @@ import smbus
31 31
 import time
32 32
 
33 33
 # Define constants
34
-DEGSYM = u'\xb0'
34
+DEGSYM = u'\xB0'
35 35
 
36 36
 # Define TMP102 Device Registers
37 37
 CONFIG_REG = 0x1
38 38
 TEMP_REG = 0x0
39 39
 
40
+# Define default sm bus address.
41
+DEFAULT_BUS_ADDRESS = 0x48
42
+DEFAULT_BUS_NUMBER = 1
43
+
44
+# Define the default sensor configuration.  See the TMP102 data sheet
45
+# for meaning of each bit.  The following bytes are written to the
46
+# configuration register
47
+#     byte 1: 01100000
48
+#     byte 2: 10100000
49
+DEFAULT_CONFIG = 0x60A0
50
+
40 51
 class tmp102:
41 52
 
42 53
     # Initialize the TMP102 sensor at the supplied address (default
... ...
@@ -44,16 +55,14 @@ class tmp102:
44 55
     # a new SMBus object for each instance of this class.  Writes
45 56
     # configuration data (two bytes) to the TMP102 configuration
46 57
     # register.
47
-    def __init__(self, sAddr=0x48, sbus=1): 
58
+    def __init__(self, sAddr=DEFAULT_BUS_ADDRESS,
59
+                 sbus=DEFAULT_BUS_NUMBER,
60
+                 config=DEFAULT_CONFIG): 
48 61
         # Instantiate a smbus object
49 62
         self.sensorAddr = sAddr
50 63
         self.bus = smbus.SMBus(sbus)
51
-        # Initialize TMP102 sensor.  See the data sheet for meaning of
52
-        # each bit.  The following bytes are written to the configuration
53
-        # register
54
-        #     byte 1: 01100000
55
-        #     byte 2: 10100000
56
-        initData = [0x60, 0xA0]
64
+        # Initialize TMP102 sensor.  
65
+        initData = [(config >> 8), (config & 0x00FF)]
57 66
         self.bus.write_i2c_block_data(self.sensorAddr, CONFIG_REG, initData)
58 67
     ## end def
59 68
 
... ...
@@ -95,7 +104,7 @@ class tmp102:
95 104
         # Convert from two's complement to integer.
96 105
         # If d11 is 1, the the number is a negative two's complement
97 106
         # number.  The absolute value is 2^12 - 1 minus the value
98
-        # of d10-d0 taken as a positive number.
107
+        # of d11-d0 taken as a positive number.
99 108
         if bData > 0x7FF:  # all greater values are negative numbers
100 109
             bData = -(0xFFF - bData)  # 0xFFF is 2^12 - 1
101 110
         # convert integer data to Celsius
... ...
@@ -124,14 +133,14 @@ def testclass():
124 133
         tempF = ts1.getTempF()
125 134
         if bAl:
126 135
             bAl = False
127
-            print "\033[42;30mTemperature Reg: %s %s\033[m" % regdata
128
-            print "\033[42;30m%6.2f%sC  %6.2f%s                 \033[m" % \
129
-                  (tempC, DEGSYM, tempF, DEGSYM)
136
+            print("\033[42;30mTemperature Reg: %s %s\033[m" % regdata)
137
+            print("\033[42;30m%6.2f%sC  %6.2f%s                 \033[m" % \
138
+                  (tempC, DEGSYM, tempF, DEGSYM))
130 139
         else:
131 140
             bAl = True
132
-            print "Temperature Reg: %s %s" % regdata
133
-            print "%6.2f%sC  %6.2f%sF" % \
134
-                  (tempC, DEGSYM, tempF, DEGSYM)
141
+            print("Temperature Reg: %s %s" % regdata)
142
+            print("%6.2f%sC  %6.2f%sF" % \
143
+                  (tempC, DEGSYM, tempF, DEGSYM))
135 144
         time.sleep(2)
136 145
     ## end while
137 146
 ## end def