Raspberry Pi Weather Station in Python
Adam L January 30, 2017Building a Raspberry Pi Weather station with Python.
I’ve used Python before but not extensively or terribly in depth, just bits and pieces here and there. Just enough to get by.
And since I’m the kind who learns by doing I decided that, I would find a way to put this Raspberry Pi Model B that I’ve had lying around to use.
So I read around some forums for some project ideas and a common theme always seemed to be these Weather stations.
People have built some great stations but clearly had a far larger budget than I.
So I set out to see what was available to the budget conscious hardware hacker.
Adafruit and many other such companies had some great offerings, but were quite pricey and Amazon and weren’t available on Prime delivery.
Now, I’m fully aware of the proverb “You get what you pay for”, but I was quite happy to buy the sensors and solder them myself.
With a little digging I came across this Sensor kit from Kookye for about £20 on Amazon.co.uk that included a wide variety of things to do.
But what I was most interested in was the DS18B20, the DHT11 and the BMP180.
The DS18B20 is a digital 1-wire thermometer that was made by Maxim Integrated. Datasheet here. The DHT11 is a digital temperature and humidity sensor which has many clones and originated from Aosong Electronics Co. Translated datasheet here. The BMP180 is a digital barometer, altitude and temperature sensor which uses the I2C Bus, made by electronics giant Bosch. Datasheet here.
So I had my sensors, but a couple of things needed to be done before I could use them.
The other two sensors came pre-soldered on board but the BMP180 lacked header pins.
This was super fiddly as the contact pads were so small and even though the soldering iron tip I used was the smallest I had it still made the job a bit of a pain.
Like me, you’ll probably need a clamp stand to hold it steady as you should not keep heat near it for long.
Pull up your SSH or local terminal and switch to root, then type raspi-config and press enter,
Enter the “Advanced” menu and choose I2C, answer yes to enable and no to rebooting.
Exit raspi-config and at the terminal do:
<pre class="wp-block-preformatted lang:sh decode:true">sudo apt-get install -y python-smbus i2c-tools
Then use your favourite text edit to edit /boot/config.txt and add: dtoverlay=w1-gpio
Save and shutdown.
Wiring
Next up was cabling, The kit I purchased had a 16 pin, 30 cm ribbon cable inside.
WARNING: In the following pin numbers I state the actual header pin number for my Pi!
Please see this guide for your Pi’s header layout.
Please check your own datasheets, wiring and diagrams to see how to cable the sensors,
Always turn off the Pi before adjusting cables, Not doing so could destroy the sensor(s) and your Pi.
I cannot be held responsible for any damage from the guide below.
The BMP180 uses I2C, so it needs 4 pins, In my case the board supported 5v, so the pins were; VCC to 5v (pin 2), GND to GND (pin 6), SCL to SCL (pin 5) and SDA to SDA (pin 3).
Lastly was the DHT11, this sensor uses a non-standard protocol so I opted to use GPIO 23 (pin 16) for DATA, GND to GND (pin 20) and VCC to 3.3v (pin 17).
There are some great websites with header diagrams for all versions of the RPi available here and here
Great, so I had the sensors cabled, now it was time to find if they worked.
Code
For reading the data from the sensors I used 2 Python libraries from Adafruit;
Adafruit_DHT and Adafruit_BMP.
Both of which can be installed through Python PIP.
<pre class="wp-block-preformatted lang:sh decode:true">pip install Adafruit_Python_DHT
pip install Adafruit-BMP
The source can be found on GitHub here and here
The DHT library implements the sensors protocol by bit-banging on the GPIO pin.
For reading the DS18B20 I used a great library from Timo Furrer called w1thermsensor (source) and can installed via PIP.
<pre class="wp-block-preformatted lang:sh decode:true">pip install w1thermsensor
For acquiring the sensor data, I used Python to query the sensors every 5 seconds and looks a little something like this:
<pre class="wp-block-preformatted lang:python decode:true">#!/usr/bin/python
import time, datetime, sys, os
import Adafruit_DHT
import Adafruit_BMP.BMP085 as BMP085
from w1thermsensor import W1ThermSensor
#Configure DHT sensor
pin = 23 # the GPIO that the DHT11 is connected to.
sensor = Adafruit_DHT.DHT11
#Configure BMP180
pressure_sensor = BMP085.BMP085()
#Configure DS18B20
accurate_sensor = W1ThermSensor()
print 'Starting Up Temperature Monitor'
_debug = 1
# Continuously get values
while True:
try:
# Read values from the DHT sensor if we can
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
# Read value from DS18B20 for more accurate temperature
temperature_in_celsius = accurate_sensor.get_temperature()
# Read data from BMP180
pressure = pressure_sensor.read_sealevel_pressure()
pressure = pressure / 100.0 # 1 mbar = 100 Pa.
if _debug:
print('BMP180 Data Readout:')
print('Temp = {0:0.2f} *C'.format(pressure_sensor.read_temperature()))
print('Pressure = {0:0.2f} Pa'.format(pressure_sensor.read_pressure()))
print('Altitude = {0:0.2f} m'.format(pressure_sensor.read_altitude()))
print('Sealevel Pressure = {0:0.2f} mb'.format(pressure))
if humidity is not None and temperature is not None:
if _debug:
print('DHT Temp={0:0.1f}*C, DS18B20 Temp={1:0.1f}*C Humidity={2:0.1f}%'.format(temperature, temperature_in_celsius, humidity))
time.sleep(5)
else:
if _debug:
print('Failed to get reading. Waiting 2 seconds.')
time.sleep(2)
continue
except KeyboardInterrupt:
break
In my complete acquisition script there is a lot more code, for computing averages and saving the database etc but you get the idea.
UPDATE: Source code added to TCC GitLab
As for the Web “App” side of things I took a lot of inspiration from Brett DangerField (website) who used Flask, The Python Web Micro framework to serve the content and the Twitter Bootstrap CSS template.
I opted for gunicorn, A Python WSGI HTTP Server, Flask, Graph.js and jQuery to liven things up.
I’ve added many additional features such as:
- Ability to view not just a single sensor but all three or one at a time,
- Real-time data from sensors every 5 seconds,
- Graphs and Real-time update using AJAX and not page refreshes,
- Supports HTML5 and Mobile devices,
- Multiple duration periods, 6 hours to 1 week of data,
- Multiple granularity levels for data points, 1 minute, 15 minute or 30 minutes.
Page load times are about 1 second without caching.
Next I plan to add a dashboard with Gauge widgets, pressure forecasting and possibly add Wind Speed/Direction and Sunlight if I can find the right sensors.
<pre class="wp-block-preformatted lang:sh decode:true">top - 17:53:09 up 10 days, 8:19, 2 users, load average: 0.11, 0.10, 0.27
Tasks: 93 total, 1 running, 92 sleeping, 0 stopped, 0 zombie
%Cpu(s): 17.1 us, 4.3 sy, 0.0 ni, 78.3 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem: 445088 total, 103144 used, 341944 free, 5056 buffers
KiB Swap: 102396 total, 2624 used, 99772 free. 26652 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
16089 20 0 30456 15516 5300 S 6.9 3.5 162:12.23 gunicorn
31929 20 0 30372 15372 5360 S 7.5 3.5 7:03.10 gunicorn
11578 rt 0 18812 14820 6980 S 0.0 3.3 27:32.34 python
15816 20 0 16012 12456 6456 S 0.3 2.8 2:20.33 gunicorn
24040 20 0 16160 9856 3784 S 0.3 2.2 4:04.58 supervisord