XBee Temperature Monitor

My apartment is hot. How hot is my apartment? Great question, that's what I'd like to know as well. This project utilizes the ADC on the XBee Series 1 wireless modules to periodically sample temperature data from an analog temperature sensor (LM335A). The data is sent back to a Raspberry Pi which logs the data. A cronjob on the Raspberry Pi periodically runs Octave to generate an up to date plot of the data (seen in Figure below).

Temp (F) vs Sample #

Hardware

I plan on running the XBee on a LiPo battery for the small form factor and high power density. In order to avoid damaging the battery (and lighting my apartment on fire), I added an undervoltage protection circuit to the board. When the battery voltage dips below ~3v the voltage regulator is disabled. When the regulator is disabled, the entire board is effectively put into in a ultra lowpower sleep mode. A LM3671 DC-DC regulator is used in combination with a STM811T reset circuit.

Temp Monitor Board v1.0 - Top

Temp Monitor Board v1.0 - Bottom

XBee Series 1 modules can be configured to periodically sample ADC/Digital IO data and transmit samples, without the use of an external microcontroller. Neat huh? They also can be configured to enter a low power sleep mode between samples. In this manner, XBee Series 1 modules can be used as low power data collection devices. With a reasonably sized battery, a device can run several months before recharging. XBee configuration is done with the X-CTU tool from Digi. For this project four XBees will be used, three configured as data collecting "end-points" and one as a "coordinator". The end-point devices will be battery powered and will utilize the low power sleep mode. At set intervals (currently 45 seconds), the devices will wake up, sample the temperature sensor, transmit the data back to the coordinator, then go back to sleep. My XCTU profile for the endpoint is here, the "MY" 16-bit source address needs to be changed to a unique value for each endpoint.

The LM335A temperature sensor has a range of -40C to 100C. The sample circuit runs off a 5v supply, however, with a 3.3v supply the maximum temperature is 330K aka 134F. I certainly hope that my apartment never gets quite so hot! The calculations below show how the resistor, R1, is selected based on a 3.3v supply and a target 1 mA current. The LM335A operates with between 400uA and 5mA of current. I read that "self-heating" is a factor, as such, I figured lower currents were less likely to heat the device up and introduce measurement error. I selected a 300 ohm resistor primarly because I had three 100 ohm resistors on hand.

LM335A calculations!

The LM335A can connect to any of the ADC inputs on the XBee series 1 modules. As seen in the Eagle schematic below, I selected ADC1. The STM811 chip in the schematic disables the LM3671 DC-DC regulator if the battery voltage drops below 3.08v. This undervoltage protection circuit prevents the LiPo battery from being damaged from being overdrawn.

Eagle Schematic

Software

The python-xbee library provides an easy to use interface for receiving XBee sample data. The code below runs on the Raspberry Pi recording data samples to a log file for futureplotting.


from xbee import XBee
import serial
from datetime import datetime
import calendar

PORT = '/dev/ttyUSB0'
BAUD_RATE = 9600

f = open('tempData0.log','a',0)
f1 = open('tempData1.log','a',0)

# Open serial port
ser = serial.Serial(PORT, BAUD_RATE)

# Create API object
xbee = XBee(ser)

# Continuously read and print packets
while True:
    try:
        response = xbee.wait_read_frame()
        print response
        temp = float(0.0)
        samples = response['samples']
        for ea in samples:
            temp += float(ea['adc-1'])
        avgADC = temp/len(samples)
        print "# Samples ",len(samples)
        print avgADC
        #tempStr = response['samples'][0]['adc-1']

        tempK = (avgADC/1024.0)*3.24*100
        tempF = ((tempK-273.15)*1.8)+32.00
        d = datetime.utcnow()
        now = calendar.timegm(d.utctimetuple())
        ps = str(tempK) + "," + str(tempF) + "," + str(now)
        print ps

        if(response['source_addr'] == "\x00\x00"):
            f.write(ps)
            f.write("\n")
        elif(response['source_addr'] == "\x00\x01"):
            f1.write(ps)
            f1.write("\n")
            #print "Received data from other than node 0"

    except KeyboardInterrupt:
        break
        
f.close()
f1.close()
ser.close()

A cronjob runs every 5 minutes:

# m h  dom mon dow  command
*/5 * * * * /home/pi/getTemp.sh
The script "getTemp.sh" takes the last 10000 samples gathered and plots them in octave.

#!/bin/bash

cd /home/pi/
tail -n 10000 tempData0.log > temp0_10000.log
tail -n 10000 tempData1.log > temp1_10000.log
octave temp.m
The octave script "temp.m" reads the 10000 samples in (formatted in CSV), generates a plot and saves it as a JPG image.

z = csvread('temp0_10000.log');
z1 = csvread('temp1_10000.log');
y = z(:,2);
x = z(:,3);
y1 = z1(:,2);
x1 = z1(:,3);
h = figure(1);
plot(x,y,'b');
hold on
plot(x1,y1,'r');
xlim([min(x1(1),x(1)) max(x(length(x)),x1(length(x1)))])
set(gca,'xgrid','on');
set(gca,'ygrid','on');
set(gca,'yminortick','on','yminorgrid','on','xminorgrid','off');
saveas(h,'autoTemp','jpeg');
exit

The image, "autoTemp.jpg" is symbolically linked in the Apache server directory, thereby hosting the most up to date image.