Raspberry Pi One Room Weather Station

This little project brings together lots of bits and pieces that I’ve tinkered with over the past few months into what is grandly entitled the One Room Weather Station.  All this lashup does is derive the temperature and barometric pressure in my office.  It presents this information in various ways:

  1. LCD
  2. Twitter
  3. Graph


The concepts that I’ve tinkered with include:

  • Python
  • SQL
  • Unix Daemon Processes
  • Twitter
  • Unix syslog
  • Configuration files
  • matplotlib graphing
  • Character LCDs
  • Raspberry Pi GPIO & I2C
  • BMP085 Temperature and Barometric Pressure Sensor

The code is a learning exercise and is immature & inefficient in many areas.  Coding standards are not strictly required at home! However, it’s operated successfully over several days without issue.  The project is an experiment in these various concepts without spending the earth on hardware but demonstrates the early stages of what could be a more extensive and practical system.  This extension may happen in the future.

All the building blocks can be found on this site including the various Python libraries I’ve cobbled together along the way.

The source code for the One Room Weather Station is below:

import sys, time, syslog
from datetime import datetime, timedelta
import smbus
import sqlite3

from daemon import *
from RPiCharLCD import *
import BMP085
import tweeter

# Open syslog
syslog.openlog(logoption=syslog.LOG_PID)

# Create a tweeter object
myTweeter = tweeter.tweeter()

# Create I2C bus & BMP085 Objects
i2c = smbus.SMBus(0)
sensor = BMP085.BMP085(i2c, 0x77)

# Create SQLite database connection
conn = sqlite3.connect("/var/local/log/test.db",
                       detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)

# Create database cursor and create table if it doesn't exist
c = conn.cursor()
c.execute('CREATE TABLE IF NOT EXISTS logging (datetime TIMESTAMP, temp FLOAT, pressure INT)')

# Initialise the LCD display
myLCD = RPiCharLCD()
myLCD.lcd_init()

# Inherit from Daemon class
class MyDaemon(daemon):
    # implement run method
    def run(self):

        # record event times
        lastTimeUpd = datetime.now()
        lastSensorReading = datetime.now()
        lastTweet = datetime.now()
        lastLog = datetime.now()

        #initialise temperature and pressure
        t = None
        p = None

        # main loop
        while True:

            # Calculate elapsed time since last events
            now = datetime.now()
            timeElapsed = now - lastTimeUpd
            sensorElapsed = now - lastSensorReading
            tweetElapsed = now - lastTweet
            logElapsed = now - lastLog

            # Time to read temp & pressure
            if sensorElapsed > timedelta(seconds=5):
                t = sensor.readTemperature()
                p = (sensor.readPressure()/100)
#                myLCD.lcd_byte(myLCD.LCD_LINE_2, myLCD.LCD_CMD)
#                myLCD.lcd_string("{0!s} C {1!s} hPa".format(t, p))
                myLCD.lcd_write_string("{0!s} C {1!s} hPa".format(t, p), 2)
                lastSensorReading = now

            # Time to log to database
            if logElapsed > timedelta(hours=1):
                c.execute('INSERT INTO logging VALUES(?, ?, ?)', (now, t, p))
                conn.commit()
                lastLog = now

            # Time to tweet
            if tweetElapsed > timedelta(minutes=10):
                try:
                    myTweeter.tweet(datetime.now().strftime('%a %d %b %y %H:%M .. ') +
                                    "{0!s} C .. {1!s} hPa".format(t, p))
                except Exception as e:
                    pass
                lastTweet = now

            # Time to update LCD Display
            if timeElapsed > timedelta(seconds=1):
                myLCD.lcd_byte(myLCD.LCD_LINE_1, myLCD.LCD_CMD)
                myLCD.lcd_write_string(datetime.now().strftime('%H:%M:%S'), 1)
                lastTimeUpd = now

            time.sleep(0.3)

if __name__ == "__main__":
    daemon = MyDaemon('/tmp/LCDClock.pid', stdout='/tmp/clockout.log', stderr='/tmp/clockerr.log')
    if len(sys.argv) == 2:
        if 'start' == sys.argv[1]:
            syslog.syslog(syslog.LOG_INFO, "Starting")
            daemon.start()
        elif 'stop' == sys.argv[1]:
            syslog.syslog(syslog.LOG_INFO, "Stopping")
            daemon.stop()
        elif 'restart' == sys.argv[1]:
            syslog.syslog(syslog.LOG_INFO, "Restarting")
            daemon.restart()
        else:
            print "Unknown command"
            sys.exit(2)
        sys.exit(0)
    else:
        print "usage: %s start|stop|restart" % sys.argv[0]
        sys.exit(2)

Below is a graph of the data:

Below is the code to plot the graphs:

import matplotlib
matplotlib.use('Agg') # Must be before importing matplotlib.pyplot or pylab!
from matplotlib import pyplot
from matplotlib.dates import date2num
import matplotlib.dates as mdates
import sqlite3

# Data series
datestamps = []
temps = []
pressures = []

# Various formatters
# These will have to change as the volume of data to be plotted increases
# As the x-axis will become too cluttered
years    = mdates.YearLocator()   # every year
months   = mdates.MonthLocator()  # every month
days    = mdates.DayLocator()
hours   = mdates.HourLocator()
daysFmt = mdates.DateFormatter('%a %d %b %y')

# Connect to database
conn = sqlite3.connect("/var/local/log/test.db", detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)

# Set up SQL query and perform on the database
# Very poorly written 'exception' handling
sql = 'SELECT datetime as "[timestamp]", temp, pressure FROM logging'
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
try:
    cursor.execute(sql)
    rows = cursor.fetchall()
finally:
    cursor.close()

# Transfer SQL data into the data series
# Must be a better way to do this
for row in rows:
    datestamps.append(row[0])
    temps.append(row[1])
    pressures.append(row[2])

# Convert the dates into a format that matplotlib can handle
dates = date2num(datestamps)

# Create the figure and 2 sublots
fig = pyplot.figure()
plt0 = fig.add_subplot(111)
plt1 = fig.add_subplot(211)

# Hide all un-necessary adornments
plt0.spines['top'].set_color('none')
plt0.spines['bottom'].set_color('none')
plt0.spines['left'].set_color('none')
plt0.spines['right'].set_color('none')
plt0.tick_params(labelcolor='w', top='off', bottom='off', left='off', right='off')

# Plot Temperature sub-plot
plt1.plot_date(dates, temps, 'b-', xdate=True)
plt1.set_title("Temperature")
plt1.xaxis.set_major_locator(days)
plt1.xaxis.set_major_formatter(daysFmt)
plt1.set_ylabel('Deg C')

# Plot Pressure sub-plot
plt2 = fig.add_subplot(212)
plt2.plot_date(dates, pressures, 'g-', xdate=True)
plt2.set_title("Barometric Pressure")
plt2.set_ylabel('hPa')

# Set x-axis label & format
plt2.xaxis.set_major_locator(days)
plt2.xaxis.set_major_formatter(daysFmt)
plt2.set_xlabel('Date')
fig.autofmt_xdate()

# Save the the graph image
pyplot.savefig('weather-plot.png')
pyplot.show()

2 thoughts on “Raspberry Pi One Room Weather Station

  1. This is really cool! I found you from Reddit.

    Have you considered measuring other characteristics in tandem? ie Humidity, lux, etc?

Leave a Reply

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