Just a guy with a love for tech

Raspberry Pi Weather Station in Python

Adam L January 30, 2017
**UPDATE: Source code added to [TCC GitLab](https://git.thecodecache.net/aleyshon/pi-weather-station)**

Building 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.

Before soldering, the battery and soldering iron tip are for scale.
#### Preperation

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.

The raspi-config advanced menu.
Secondly, I needed to enable the I2C bus and 1 wire interfaces on the Raspberry Pi, Fortunately this is made very simple by the Raspberry team and [raspi-config](https://www.raspberrypi.org/documentation/configuration/raspi-config.md).

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.


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.

Sensors in my make shift housing.
My DS18B20 had the pins, marked “+”, “-” and “S”. Making it fairly easy to wire, Plus went to 3.3V (pin 1), Minus to GND (pin 9) and S to GPIO 4 (pin 7).

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.

Test the sensors at CLI
They did! The BMP180 was show at address 0x77 with i2cdetect and the 1-wire DS18B20 gave a reading of 8.875 degrees C.


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:
        # 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))
            if _debug:
                print('Failed to get reading. Waiting 2 seconds.')

    except KeyboardInterrupt:

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:

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.

Even with all that going on with including polling sensors, the CPU is barely touched and there is still plenty of free RAM.
<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

Back to top