How To: Setup a Sensorpedia Sensing Station (guide 1)

What is Sensorpedia without sensors? This article will be one of a series that detail how to talk to a few different types of sensors, calibrate their data, and interface them with Sensorpedia.

These are the sensors we will be connecting (this guide will focus on reading the LM34 analog temperature sensor):

sensing_station_sensors_annotated_2_600px1

One of the preferred data formats for Sensorpedia is GeoRSS.  Simply put, a GeoRSS feed contains entries of data readings tagged with a specific time and spatial location. We can serve RSS or GeoRSS feeds to Sensorpedia, but first we need to have data to display in the feed.  Since we can’t plug little sensor PCBs straight into the back of a server, we will need a bit of infrastructure to calibrate and relay the data.  An ARM microcontroller board designed for the DIY/tinkering crowd called the Make Controller (henceforth referred to as “MC”) will serve as a middleman and communicate with a host server computer and the various sensors. It can poll analog sensors, digital sensors, control TTL, communicate via USB and Ethernet, and even control servos. The MC is a powerful little device.

guide1_overview

Since Sensorpedia is designed to work with data wrapped as RSS or GeoRSS, we’ll need a server that can gather sensor readings, calibrate the data, and render them as an RSS file.  Rather than output the RSS ourselves, we will just have our server gather sensor readings and post them to Twitter, which will in turn provide an RSS feed of each reading we send. Rather than having the MC act as a server itself–an option ideal for doing things like deploying sensor stations– we will be using a host computer to control the MC and post the data. This is a bit easier, as we can implement the server in Python.  We will still need to program the MC itself for certain sensors, for a reason to be described in a later guide.  Serving RSS directly will also be covered in a later guide.

We will be issuing OpenSound Control Commands over USB to control the MC. These commands are simple to issue, and are an easy way to poll and set pins, read in the A/D, and control fundamental board actions.   They are specially-constructed messages that request certain addresses on the MC.  We will use SimpleOSC to allow us to send OSC messages.  Since the Make Controller is visible to the computer as a virtual serial port, we will also need the PySerial module to handle serial communication.

OSC addresses are constructed like this:

/subsystem/device/property

For example, the address used to set the state of the first LED of the zeroth-indexed array of onboard LEDs would look like this:

/appled/0/state 1

Using OSC commands, we can query the binary state of any of the digital inputs, and the value of the current voltage on any of the analog inputs.  There are a few more commands, but we are essentially limited to querying state (once again, more on complex operations in a later guide).  This first example will connect our analog temperature sensor, the LM34, and get its value from Python. First, we need to figure out how to connect it. The MC has quite a few inputs and outputs, and they are shown below (image from makingthings.com). The inputs can function as analog or digital inputs, while the outputs are strictly digital (Though they do support PWM.)

appboard_overview

We will wire it up to one of the analog inputs as shown below (image from makingthings.com)

lm34_connect

With the LM34 connected, we can use a small Python script to read in the value and output the corresponding temperature in degrees Fahrenheit. (Thanks to Oliver Smith at makingthings.com for the example from which this is derived)

import os
import sys
import time
import threading

import osc # simpleosc
import serial # pyserial

COM_PORT = '/dev/ttyACM0' # set this to the serial port to which the Make Controller is connected, something like "COM0" for windows or "/dev/ttyACM0" for POSIX
SLIP_END = '\xC0' #end of an OSC message with SLIP wrapping (USB only)
SLIP_ESC = '\xDB' # used to escape embedded OSC messages

class Lm34Read(object):

    def __init__(self, comPortId):
        self._initializeSerial(comPortId)
        self._inBuffer = ''

    def _initializeSerial(self, comPortId):
        self._serial = serial.Serial(comPortId, 115200, timeout = 0)  # open the virtual serial port created by the Make Controller's USB interface

    def _write(self, address, value = None):
        m = osc.OSC.OSCMessage() #create a new OSC messsage
        m.setAddress(address) #set the address to the one specified by the parameter

        if value is not None: # if we are passing a param along with the address...
            m.append(value)

        s = str(m).replace(SLIP_END, SLIP_ESC + SLIP_END) # escape any embedded SLIP_END characters in the message
        self._serial.write(SLIP_END + s + SLIP_END) # begin and end with SLIP_END; doesn't seem to work otherwise

    def _read(self):
        if self._serial.inWaiting():
            for c in self._serial.read(500): # read 500 bytes, or as much as is in the buffer
                if c == SLIP_END and (len(self._inBuffer) == 0 or self._inBuffer[-1] != SLIP_ESC): #if the EOM char is in the message, and the buffer is empty or the last buffer char is not an escape character
                    self._handleInput() #process the data
                else: #otherwise
                    self._inBuffer += c # append what was just read to the buffer

    def _handleInput(self):
        message = osc.OSC.decodeOSC(self._inBuffer) #decode the message and make an array of it
        if len(message) != 0: #if the message is present
            print str(message[2]*0.3222)+" degrees F"#print our temperature

        self._inBuffer = '' #wipe the serial buffer

    def run(self):
         self._write('/analogin/0/value') #send a message requesting the value of the zeroth analog input
         time.sleep(0.5) #give the Make Controller a chance to poll and respond (value in seconds)
         self._read() # check for any new messages from the Make Controller
         t = threading.Timer(1, self.run).start() #make a new timer to run 1 second after this function finishes execution

if __name__ == "__main__":
    app = Lm34Read(comPortId = COM_PORT)
    t = threading.Timer(1, app.run) #setup a timer to fire after 1.0 second
    t.start() #start the timer

This will output the temperature each second:

71.5284 degrees F
71.5284 degrees F
71.2062 degrees F
71.5284 degrees F
71.5284 degrees F
71.5284 degrees F
71.5284 degrees F
71.5284 degrees F
71.5284 degrees F

To figure out how to calculate the temperature, we need to calculate the gain. This value is merely a scaling factor that adjusts the output temperature value as a function of the precision of the A/D and the supply voltage to the LM34. We can calculate the gain as follows:

The MC has a 10-bit analog-to-digital (A/D) converter. This means that it can represent input voltages as 210 discrete levels, from 0 to 1023.  Since the SAM7 ARM microcontroller on the MC operates at LVTTL voltage levels, it expects a maximum of 3.3[V]. This means we should set the v_in jumper to supply 3.3[V] to LM34, just to be safe. The image below shows the jumper position for 3.3[V] (image from makingthings.com).

voltage_in_3v

Since the LM34 is designed to operate with a range of 0-5[V] and we will only be giving it 3.3[V], we will be unable to measure higher temperatures than about 200 [°F]. This is perfectly acceptable for measuring ambient room temperature. Now that we know the supply voltage and the number of values the A/D can output, we can figure out how to calculate the incoming voltage:

v_in_calc

Each increase in A/D value represents an increase in voltage of 3.22[mV]. The LM34′s responsivity is 10mv_per_degree_f . Knowing both the input voltage and the sensor responsivity, we can find the temperature in [°F] from a given A/D value; we just need to calculate the gain.  Since the LM34 is factory-calibrated, this value should be pretty accurate.

t_gainFor an example, let’s calculate the current room temperature when the A/D outputs a value of 229:

t_sample

Now let’s do something fun:  Make our Python script update Twitter with the Sensorpedia lab’s current temperature. We will need to import two new Python modules, python-twitter and its dependency, simplejson.  From here we are a simple few lines away from tweeting:

We’ll import the modules:

import simplejson #simplejson
import twitter # python-twitter

Make a Twitter API object:

    twitterObj = twitter.Api("username", "password")

Set up the tweet:

            temperature = "It is "+str(message[2]*0.3222)+" degrees F in the Sensorpedia lab."
            twitterObj.PostUpdate(temperature)

All together, it looks like this:

import os
import sys
import time
import threading

import osc # simpleosc
import serial # pyserial
import simplejson #simplejson
import twitter # python-twitter

COM_PORT = '/dev/ttyACM0' # set this to the serial port to which the Make Controller is connected, something like "COM0" for windows or "/dev/ttyACM0" for POSIX
SLIP_END = '\xC0' #end of an OSC message with SLIP wrapping (USB only)
SLIP_ESC = '\xDB' # used to escape embedded OSC messages

class Lm34Read(object):

    def __init__(self, comPortId):
        self._initializeSerial(comPortId)
        self._inBuffer = ''

    def _initializeSerial(self, comPortId):
        self._serial = serial.Serial(comPortId, 115200, timeout = 0)  # open the virtual serial port created by the Make Controller's USB interface

    def _write(self, address, value = None):
        m = osc.OSC.OSCMessage() #create a new OSC messsage
        m.setAddress(address) #set the address to the one specified by the parameter

        if value is not None: # if we are passing a param along with the address...
            m.append(value)

        s = str(m).replace(SLIP_END, SLIP_ESC + SLIP_END) # escape any embedded SLIP_END characters in the message
        self._serial.write(SLIP_END + s + SLIP_END) # begin and end with SLIP_END; doesn't seem to work otherwise

    def _read(self):
        if self._serial.inWaiting():
            for c in self._serial.read(500): # read 500 bytes, or as much as is in the buffer
                if c == SLIP_END and (len(self._inBuffer) == 0 or self._inBuffer[-1] != SLIP_ESC): #if the EOM char is in the message, and the buffer is empty or the last buffer char is not an escape character
                    self._handleInput() #process the data
                else: #otherwise
                    self._inBuffer += c # append what was just read to the buffer

    def _handleInput(self):
        message = osc.OSC.decodeOSC(self._inBuffer) #decode the message and make an array of it
        if len(message) != 0: #if the message is present
            temperature = "It is "+str(message[2]*0.3222)+" degrees F in the Sensorpedia lab."
            twitterObj.PostUpdate(temperature)
            print temperature#print our temperature

        self._inBuffer = '' #wipe the serial buffer

    def run(self):
         self._write('/analogin/0/value') #send a message requesting the value of the zeroth analog input
         time.sleep(0.5) #give the Make Controller a chance to poll and respond (value in seconds)
         self._read() # check for any new messages from the Make Controller
         t = threading.Timer(600, self.run).start() #make a new timer to run 1 second after this function finishes execution

if __name__ == "__main__":
    app = Lm34Read(comPortId = COM_PORT)
    twitterObj = twitter.Api("username", "password")
    t = threading.Timer(1, app.run) #setup a timer to fire after 1.0 second
    t.start() #start the timer

Now we can view the current temperature in the twitterstream:

twitter_capture

Since Twitter provides an RSS feed, we can import these readings straight into Sensorpedia!

This entry was posted in Sensorpedia and tagged , , , . Bookmark the permalink.

2 Responses to How To: Setup a Sensorpedia Sensing Station (guide 1)

  1. Pingback: Sensorpedia » Blog Archive » How To: Interface an ultrasonic rangefinder with Sensorpedia via Twitter (guide 2)

  2. Pingback: Sensorpedia » Blog Archive » Sensorpedia student returns to map sensors, work on mobile apps

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>