Wednesday, 4 February 2015

Raspberry Pi Multiple Temperature Measurements 2: Recording

The following post is a section of the book 'Raspberry Pi: Measure, Record, Explore'.  The entire book can be downloaded in pdf format for free from Leanpub or you can read it online here.
Since this post is a snapshot in time. I recommend that you download a copy of the book which is updated frequently to improve and expand the content.
---------------------------------------

This is the second of three posts working through a project looking at Measuring Recording and Exploring temperature measurements with the Raspberry Pi.

Record the temperature values

The following Python code (which is based on the code that is part of the great temperature sensing tutorial on iot-project) is a script which allows us to check the temperature reading from multiple sensors and write them to our database with a separate entry for each sensor.
The full code can be found in the code samples bundled with this book (m_temp.py).
#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import fnmatch
import time
import MySQLdb as mdb
import logging

logging.basicConfig(filename='/home/pi/DS18B20_error.log',
  level=logging.DEBUG,
  format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger=logging.getLogger(__name__)

# Load the modules (not required if they are loaded at boot) 
# os.system('modprobe w1-gpio')
# os.system('modprobe w1-therm')

# Function for storing readings into MySQL
def insertDB(IDs, temperature):

  try:

    con = mdb.connect('localhost',
                      'pi_insert',
                      'xxxxxxxxxx',
                      'measurements');
    cursor = con.cursor()

    for i in range(0,len(temperature)):
      sql = "INSERT INTO temperature(temperature, sensor_id) \
      VALUES ('%s', '%s')" % \
      ( temperature[i], IDs[i])
      cursor.execute(sql)
      sql = []
      con.commit()

    con.close()

  except mdb.Error, e:
    logger.error(e)

# Get readings from sensors and store them in MySQL

temperature = []
IDs = []

for filename in os.listdir("/sys/bus/w1/devices"):
  if fnmatch.fnmatch(filename, '28-*'):
    with open("/sys/bus/w1/devices/" + filename + "/w1_slave") as f_obj:
      lines = f_obj.readlines()
      if lines[0].find("YES"):
        pok = lines[1].find('=')
        temperature.append(float(lines[1][pok+1:pok+6])/1000)
        IDs.append(filename)
      else:
        logger.error("Error reading sensor with ID: %s" % (filename))

if (len(temperature)>0):
  insertDB(IDs, temperature)
This script can be saved in our home directory (/home/pi) and can be run by typing;
python m_temp.py
While we won’t see much happening at the command line, if we use our web browser to go to the phpMyAdmin interface and select the ‘measurements’ database and then the ‘temperature’ table we will see a range of temperature measurements for the different sensors and their associated time of reading.
Now you can be forgiven for thinking that this is not going to collect the sort of range of data that will let us ‘Explore’ very much, but let’s do a quick explanation of the Python code first and then we’ll work out how to record a lot more data :-).
Save the MySQL Table Columns
Save the MySQL Table Columns
Code Explanation
The script starts by importing the modules that it’s going to use for the process of reading and recording the temperature measurements;
import os
import fnmatch
import time
import MySQLdb as mdb
import logging
Python code in one module gains access to the code in another module by the process of importing it. The import statement invokes the process and combines two operations; it searches for the named module, then it binds the results of that search to a name in the local scope.
Then the code sets up the logging module. We are going to use the basicConfig() function to set up the default handler so that any debug messages are written to the file /home/pi/DS18B20_error.log.
logging.basicConfig(filename='/home/pi/DS18B20_error.log',
  level=logging.DEBUG,
  format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger=logging.getLogger(__name__)
Later in the section where we are getting our readings or writing to the database we write to the log file if there is an error.
The program can then issues the modprobe commands that start the interface to the sensor;
# os.system('modprobe w1-gpio')
# os.system('modprobe w1-therm'))
Our code has them commented out since we have already edited the /etc/modules file, but if you didn’t want to start the modules at start-up (for whatever reasons), you can un-comment these.
We then declare the function that will insert the readings into the MySQL database;
def insertDB(IDs, temperature):

  try:

    con = mdb.connect('localhost',
                      'pi_insert',
                      'xxxxxxxxxx',
                      'measurements');
    cursor = con.cursor()

    for i in range(0,len(temperature)):
      sql = "INSERT INTO temperature(temperature, sensor_id) \
      VALUES ('%s', '%s')" % \
      ( temperature[i], IDs[i])
      cursor.execute(sql)
      sql = []
      con.commit()

    con.close()

  except mdb.Error, e:
    logger.error(e)
This is a neat piece of script that uses arrays to recall all the possible temperature and ID values.
Then we have the main body of the script that finds all our possible sensors and reads the IDs and the temperatures;
temperature = []
IDs = []

for filename in os.listdir("/sys/bus/w1/devices"):
  if fnmatch.fnmatch(filename, '28-*'):
    with open("/sys/bus/w1/devices/" + filename + "/w1_slave") as f_obj:
      lines = f_obj.readlines()
      if lines[0].find("YES"):
        pok = lines[1].find('=')
        temperature.append(float(lines[1][pok+1:pok+6])/1000)
        IDs.append(filename)
      else:
        logger.error("Error reading sensor with ID: %s" % (filename))

if (len(temperature)>0):
  insertDB(IDs, temperature)
After declaring our two arrays temperature and IDs we start the for loop that checks all the file names in /sys/bus/w1/devices;
for filename in os.listdir("/sys/bus/w1/devices"):
If it finds a filename that starts with 28- then it processes it;
  if fnmatch.fnmatch(filename, '28-*'):
First it opens the w1_slave file in the 28-* directory…
    with open("/sys/bus/w1/devices/" + filename + "/w1_slave") as f_obj:
… then it pulls out the lines in the file;
      lines = f_obj.readlines()
If it finds the word “YES” in the first line (line 0) of the file…
      if lines[0].find("YES"):
…then it uses the position of the equals (=) sign in the second line (line 1)…
        pok = lines[1].find('=')
… to pull out the characters following the ‘=’, and manipulate them to form the temperature in
degrees Centigrade;
        temperature.append(float(lines[1][pok+1:pok+6])/1000)
We then add the filename to the IDs array;
        IDs.append(filename)
If we didn’t find a “yes” in the first line we log the error in the log file
        logger.error("Error reading sensor with ID: %s" % (filename))
Then finally if we have been successful in reading at least one temperature value, we push the IDs and temperature array to the insertDB function;
if (len(temperature)>0):
  insertDB(IDs, temperature)

Recording data on a regular basis with cron

As mentioned earlier, while our code is a thing of beauty, it only records a single entry for each sensor every time it is run.
What we need to implement is a schedule so that at a regular time, the program is run. This is achieved using cron via the crontab. While we will cover the requirements for this project here, you can read more about the crontab in the Glossary.
To set up our schedule we need to edit the crontab file. This is is done using the following command;
crontab -e
Once run it will open the crontab in the nano editor. We want to add in an entry at the end of the file that looks like the following;
*/1 * * * * /usr/bin/python /home/pi/m_temp.py
This instructs the computer that exert minute of every hour of every day of every month we run the command /usr/bin/python /home/pi/m_temp.py (which if we were at the command line in the pi home directory we would run as python m_temp.py, but since we can’t guarantee where we will be when running the script, we are supplying the full path to the python command and the m_temp.py script.
Save the file and the next time the computer boots up is will run out program on its designated schedule and we will have sensor entries written to our database every minute.

The post above (and heaps of other stuff) is in the book 'Raspberry Pi: Measure, Record, Explore' that can be downloaded for free (or donate if you really want to :-)).

No comments:

Post a Comment