Advertisement
andrum99

temp_py2.py

Jan 24th, 2015
315
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 11.28 KB | None | 0 0
  1. # Raspberry Pi temperature monitoring script
  2. # for Python 2
  3. # Andrew Pattison, 11/10/2012 - 16/2/2015
  4.  
  5. import Adafruit_GPIO.I2C as I2C
  6. import Adafruit_CharLCD as LCD
  7. import sys
  8. import syslog
  9. import subprocess
  10. import httplib
  11. import json
  12. import time
  13. import signal
  14. import threading
  15. import sqlite3
  16. import os.path
  17. import datetime
  18.  
  19. # log exceptions to stdout
  20. EXC_STDOUT = True
  21. # log exceptions to syslog
  22. EXC_SYSLOG = True
  23. # special value to indicate invalid temperature
  24. TEMP_INVALID = 9999
  25. # i2c address of temperature sensor
  26. I2C_ADDRESS = 0x48
  27. # name of hard disk to monitor temperature of
  28. HDD_NAME = "/dev/disk/by-label/expansion"
  29. # xively API keys
  30. XIVELY_KEY = "JR2G_KZu9bdYNC3nsT0TP0x4kPuSAKx5dUFTd0wvZm4vVT0g"
  31. # xively feed location
  32. XIVELY_FEED = "/v2/feeds/79499/"
  33. # xively feed host
  34. XIVELY_HOST = "api.xively.com"
  35. # absolute path to directory containing sqlite3 database
  36. DB_DIR = "/home/pi/temperature/"
  37.  
  38. def signal_term_handler(signal, frame):
  39.     # signal handler for SIGTERM
  40.     print "exiting on SIGTERM"
  41.     syslog.syslog("Exiting on SIGTERM")
  42.     # send thread the stop signal
  43.     thread1.stop()
  44.     # wait until thread has stopped
  45.     thread1.join()
  46.     lcdExit()
  47.     sys.exit(0)
  48.    
  49. def logMsg(message):
  50.     # log a message to stdout and/or syslog
  51.     # message: the message string
  52.     if EXC_STDOUT: print message
  53.     if EXC_SYSLOG: syslog.syslog(message)
  54.  
  55. def logException(name, str1, str2, str3):
  56.     # log an exception
  57.     # name: name of location where exception occurred
  58.     # str1, str2, str3: strings with info about exception
  59.     msgString = "Exception in " + name + ": " + str1 + ", " + str2 + ", " + str3
  60.     logMsg(msgString)
  61.    
  62. def turnLCDOff():
  63.     lcd.clear()
  64.     lcd.set_backlight(False)
  65.    
  66. def turnLCDOn():
  67.     lcd.clear()
  68.     lcd.set_backlight(True)
  69.    
  70. def lcdExit():
  71.     # display an "exiting" message on the LCD for 2 seconds
  72.     # then switch off the display
  73.     lcd.clear()
  74.     lcd.message("Exiting...")
  75.     time.sleep(2)
  76.     turnLCDOff()
  77.     lcdOn = False
  78.    
  79. def getCPUTemp():
  80.     try:
  81.         file = open("/sys/class/thermal/thermal_zone0/temp", "r")
  82.         string = file.readline()
  83.         file.close()
  84.         cputemp = float(string)
  85.         cputemp = cputemp / 1000
  86.         # basic sanity check for CPU temperature
  87.         if cputemp < 100:
  88.             return cputemp
  89.         else:
  90.             return TEMP_INVALID
  91.     except Exception:
  92.         logException("getCPUTemp", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  93.         return TEMP_INVALID
  94.        
  95. def getHDDTemp(device):
  96.     try:
  97.         s = subprocess.check_output(["/sbin/hdparm","-H",device])  
  98.         string2 = str(s)
  99.         # get 3rd line of output
  100.         templine = string2.split('\n')[2]
  101.         # get last 2 characters of line - the temperature
  102.         hddtempstring = templine[-2:]
  103.         # convert to integer
  104.         return int(hddtempstring)
  105.     except Exception:
  106.         logException("getHDDTemp", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  107.         return TEMP_INVALID
  108.        
  109. def getI2CTemp(device):
  110.     try:
  111.         rawvalue = device.readS16(0x00, False)
  112.        
  113.         rawvalue = int(rawvalue * 0.0625)
  114.         rawvaluestring = "{0:b}".format(rawvalue)
  115.  
  116.         if rawvalue >= 2048:
  117.             # temperature is negative
  118.             tempstring = ""
  119.             # calculate 2s compliment of rawvalue
  120.             # We do it manually since Python's compliment
  121.             # function doesn't do what I expect it to.
  122.             for i in range(len(rawvaluestring)):
  123.                 if rawvaluestring[i] == "1":
  124.                     tempstring = tempstring + "0"
  125.                 else:
  126.                     if rawvaluestring[i] == "0":
  127.                         tempstring = tempstring + "1"
  128.             negvalue = int(tempstring,2)
  129.             negvalue = negvalue + 1
  130.             negativevalue = negvalue * -0.0625
  131.             return negativevalue
  132.         else:
  133.             # temperature is positive
  134.             positivevalue = rawvalue * 0.0625
  135.             return positivevalue
  136.     except Exception:
  137.         logException("getI2CTemp", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  138.         return TEMP_INVALID
  139.        
  140. def sendToXively(data, apiKey, feed):
  141.     try:
  142.         connection = httplib.HTTPConnection(XIVELY_HOST)
  143.         datastring = json.dumps(data)
  144.         header = { "X-ApiKey":apiKey }
  145.         connection.request("PUT", feed, datastring, header)
  146.         return connection.getresponse()
  147.     except Exception:
  148.         logException("sendToXively", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  149.         return 0
  150.        
  151. def doesDBExist():
  152.     try:
  153.         return os.path.exists(DB_DIR + "observation.db")
  154.     except Exception:
  155.         logException("doesDBExist", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  156.         return 0
  157.    
  158. def createDB(connection):
  159.     try:
  160.         ddl = "CREATE TABLE temperature (datetime CHAR(16), ambient REAL, cpu REAL, hdd REAL, PRIMARY KEY (datetime))"
  161.         connection.execute(ddl)
  162.     except Exception:
  163.         logException("createDB", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  164.         return 0
  165.    
  166. def writeRow(connection, values):
  167.     try:
  168.         insert = "INSERT INTO temperature VALUES (" + values + ")"
  169.         print(insert)
  170.         connection.execute(insert)
  171.         connection.commit()
  172.     except Exception:
  173.         logException("writeRow", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  174.         return 0
  175.        
  176. class buttonThread(threading.Thread):
  177.  
  178.     # Buttons on the Adafruit LCD Plate are used for:
  179.     # SELECT: shut down the Pi
  180.     # LEFT  : reboot the Pi
  181.     # RIGHT : switch the LCD display on/off
  182.  
  183.     def __init__(self):
  184.         super(buttonThread, self).__init__()
  185.         self._stop = threading.Event()
  186.        
  187.     def stop(self):
  188.         self._stop.set()
  189.  
  190.     def stopped(self):
  191.         return self._stop.isSet()
  192.        
  193.     def run(self):
  194.         # poll buttons and respond to any that are pressed
  195.        
  196.         # pressedSelect, pressedLeft and pressedRight are used to indicate that the
  197.         # corresponding button has been pressed AND that we have already carried out
  198.         # the required action. These are then used to stop the script repeatedly carrying
  199.         # out the same action.
  200.        
  201.         try:
  202.             global lcdOn
  203.             global msgUpdated
  204.             lcdOn = True
  205.             pressedSelect = False
  206.             pressedLeft = False
  207.             pressedRight = False
  208.             msgOld = False
  209.             # take a note of when this thread starts running
  210.             startTime = time.time()
  211.            
  212.             while True:
  213.                 # check if this thread has been signalled to stop and do so
  214.                 if self._stop.isSet():
  215.                     return()
  216.                    
  217.                 # allow 10 seconds for main thread to get first lot of temperatures at startup
  218.                 if time.time() - startTime > 10:
  219.                     # msgOld is set to indicate that the message is old AND that we have already
  220.                     # output an error message on the display.
  221.                     # We check that the temperature message is not older than 120 seconds.
  222.                     # This handles the case where the main thread hangs.
  223.                     if msgOld == False and time.time() - msgUpdated > 120:
  224.                         errorMessage = "Temperatures old\n" + time.strftime("%b %d %H:%M:%S")
  225.                         logMsg(errorMessage)
  226.                         lcd.clear()
  227.                         lcd.message(errorMessage)
  228.                         msgOld = True
  229.                     # check if message we are to display is old (more than 2 minutes old)
  230.                     if time.time() - msgUpdated > 120:
  231.                         msgOld = True
  232.                     else:
  233.                         msgOld = False
  234.                        
  235.                 if lcd.is_pressed(LCD.SELECT):
  236.                     if pressedSelect:
  237.                         pass
  238.                     else:
  239.                         turnLCDOn()
  240.                         lcd.clear()
  241.                         lcd.message("Shutting down...")
  242.                         s = subprocess.check_output(["/sbin/poweroff"])
  243.                         lcd.message("\n" + s)
  244.                         time.sleep(2)
  245.                         turnLCDOff()
  246.                         lcdOn = False
  247.                         # exit from the thread
  248.                         return()
  249.                     pressedSelect = True
  250.                 else:
  251.                     pressedSelect = False
  252.                    
  253.                 if lcd.is_pressed(LCD.LEFT):
  254.                     if pressedLeft:
  255.                         pass
  256.                     else:
  257.                         turnLCDOn()
  258.                         lcd.clear()
  259.                         lcd.message("Rebooting...")
  260.                         s = subprocess.check_output(["reboot"])
  261.                         lcd.message("\n" + s)
  262.                         time.sleep(2)
  263.                         turnLCDOff()
  264.                         lcdOn = False
  265.                         # exit from the thread
  266.                         return()
  267.                     pressedLeft = True
  268.                 else:
  269.                     pressedLeft = False
  270.                    
  271.                 if lcd.is_pressed(LCD.RIGHT):
  272.                     if pressedRight:
  273.                         pass
  274.                     else:
  275.                         if lcdOn == True:
  276.                             turnLCDOff()
  277.                             lcdOn = False
  278.                         else:
  279.                             turnLCDOn()
  280.                             lcdOn = True
  281.                             lcd.set_cursor(0,0)
  282.                             global lcdMessage
  283.                             lcd.message(lcdMessage)
  284.                     pressedRight = True
  285.                 else:
  286.                     pressedRight = False
  287.                 # Do nothing for 0.1 seconds.
  288.                 # Hopefully this will fix the hangs.
  289.                 # It also reduces the CPU usage to around 1%.
  290.                 time.sleep(0.1)
  291.         except Exception:
  292.             logException("buttonHandler", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  293.             logMsg("Shutting down button handler thread")
  294.             lcdExit()
  295.             return()
  296.  
  297. logMsg("Starting up")
  298. # insert signal handler
  299. signal.signal(signal.SIGTERM, signal_term_handler)
  300.  
  301. # check to see if database exists
  302. exists = doesDBExist()
  303.    
  304. conn = sqlite3.connect(DB_DIR + "observation.db")
  305.    
  306. # if database did not previously exist then we need to
  307. # create the table to store our data
  308. if not exists:
  309.     logMsg("Database does not exist, creating")
  310.     createDB(conn)
  311.  
  312. lcd = LCD.Adafruit_CharLCDPlate()
  313.  
  314. lcd.clear()
  315. lcd.set_backlight(True)
  316. lcd.set_cursor(0,0)
  317.  
  318. lcdOn = True
  319. lcdMessage = ""
  320.  
  321. thread1 = buttonThread()
  322. thread1.start()
  323. # initialise this variable so buttonThread does not throw an exception when it finds
  324. # that it has not been initialised. This can occur at startup.
  325. msgUpdated = 0
  326.        
  327. while True:
  328.     try:
  329.         cputemp = getCPUTemp()
  330.         hddtemp = getHDDTemp(HDD_NAME)
  331.         device = I2C.get_i2c_device(I2C_ADDRESS)
  332.         i2ctemp = getI2CTemp(device)
  333.         observations = list()
  334.         if cputemp == TEMP_INVALID:
  335.             cpudisplay = "E! "
  336.         else:
  337.             observations.append({ "id":"CPUTemperature", "current_value":"%.2f" % cputemp })
  338.             cpudisplay = "%dC" % cputemp
  339.         if i2ctemp == TEMP_INVALID:
  340.             i2cdisplay = "E!    "
  341.         else:
  342.             observations.append({ "id":"RoomTemperature", "current_value":"%.2f" % i2ctemp })
  343.             i2cdisplay = "%.2fC" % i2ctemp
  344.         # HDD temperature is an integer, so don't need to limit to 2 decimal places
  345.         if hddtemp == TEMP_INVALID:
  346.             hdddisplay = "E! "
  347.         else:
  348.             observations.append({ "id":"HDDTemperature", "current_value":str(hddtemp) })
  349.             hdddisplay = "%dC" % hddtemp
  350.         lcdMessage = "Ambient: " + i2cdisplay + "  \nCPU: " + cpudisplay + " HD: " + hdddisplay + "  "
  351.         # store UNIX timestamp so buttonThread can determine if temperature values are old
  352.         msgUpdated = time.time()
  353.         if lcdOn:
  354.             lcd.set_cursor(0,0)
  355.             lcd.message(lcdMessage)
  356.         upload = { "version": "1.0.0", "datastreams": observations }
  357.         print time.strftime("%c")
  358.         print json.dumps(upload)
  359.                            
  360.         result = sendToXively(upload, XIVELY_KEY, XIVELY_FEED)
  361.         print "HTTP status code from xively server: " + str(result.status)
  362.  
  363.         if result.status != 200:
  364.             logMsg("Error sending data to xively. HTTP response code: " + str(result.status))
  365.            
  366.         # get current date and time
  367.         now = datetime.datetime.now()
  368.         # since we are logging temperature once a minute we don't need seconds
  369.         nowstring = now.strftime("'%Y-%m-%d %H:%M'")
  370.        
  371.         writeRow(conn, nowstring + ", " + "%.2f" % i2ctemp + ", " + str(cputemp) + ", " + str(hddtemp))
  372.        
  373.         time.sleep(60)
  374.     except KeyboardInterrupt:
  375.         logMsg("Exiting on CTRL+C")
  376.         # send thread the stop signal
  377.         thread1.stop()
  378.         # wait until thread has stopped
  379.         thread1.join()
  380.         lcdExit()
  381.         sys.exit(0)
  382.     except Exception:
  383.         # N.B. we are still within the while loop!
  384.         logException("main", str(sys.exc_info()[0]), str(sys.exc_info()[1]), str(sys.exc_info()[2]))
  385.         syslog.syslog("Pausing for 60 seconds to see if error condition clears...")
  386.         time.sleep(60)
  387.         syslog.syslog("Attempting to continue where I left off...")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement