STFN

Raspberry Pi Pico Weather Station - part I

15 minutes

Collecting data is fun [citation needed], and one of the most ubiqtous and easiest to collect information around us is environment data, meaning things like temperature or atmospheric pressure. Sensors design to collect it are cheap and easy to use, and the data can be saved and manipulated using even the simplest machines.

This post will be the start of a series in which we'll make a preliminary weather station that will be able to share our local weather data with the world, be it a private service, or a feed to a public site like Weather Underground.

Here’s how I am presenting weather data on my local Grafana instance:

The weather station will be using two sensors to gather the environment data: BMP280 and DHT11. BMP280 will provide us with atmospheric pressure and temperature readings. DHT11 provides temperature and humidity, but we will be only taking the latter from it, as BMP280 can measure how hot it is (or we are) with higher precision.

Part list

List of required components:

A word on BME280 vs BMP280

Some of you might wonder why I am using BMP280, a pressure and humidity sensor, and DHT11, a temperature and humidity sensor, when I could be using BME280, which measures all three aspects at once. The simple explanation is, this is a learning project, and BME280 is around two-three times more expensive than BMP280 and DHT11 combined. It is natural that in the course of learning mistakes happen, and breaking such an expensive part would be painful. Also, using two different sensors with different communication protocols makes us learn twice as much!

Connecting Everything

Below you'll find the Raspberry Pi Pico pinout (list of pins) taken from https://datasheets.raspberrypi.com/. All of the pin names and numbers will taken from it, use it as a reference.

Let's start with connecting BMP280. That sensors uses the I2C communication protocol, so it'll need four pins: power (VCC), ground (GND), and two communication pins, SCL and SDA.

Now moving onto DHT11. DHT11 uses a single cable protocol, but still needs power and ground.

Yes, both DHT and BMP280 need to be connected to the 3V3 pin as they both rely on 3 volt input.

Connecting with a breadboard

The easiest, but not solid way is to connect everything using a breadbord and male-to-male or male-to-female cables. Even if you plan to do a permanent solution later, it’s good to start with a breadbord to make sure things work. It’s super simple to correct any wrong connections at this point. If you’re happy with how it works right now, just skip the soldering part and go straight to programming.

Soldering on a dev board

This is how I did the soldering, surely could have been better, but it does work.

Preparing the Pico

I started with soldering GPiO headers to the Pico, and then the Pico to the development board.

Adding BMP280

Now it was time to solder the BMP280 and connect it to the Pico with short leads.

Adding DHT11

Finally I soldered DHT11. I made double sure no cables were touching each other, or other pins or the Pico, that would lead to unpredicatble results. I also planned out the space on the board so that later I can add a connetion to power the board not using the USB header.

Making it run

Now as everything is connected, let's do some code writing! For development I will be using Thonny, a simple code editor that has built-in support for Raspberry Pi Picos and other microcontrollers. You can install Thonny from thonny.org/ I won't dive into how to setup and use Thonny for RPi Pico, as there is already a great explanation done by the RPi team itself: https://projects.raspberrypi.org/en/projects/getting-started-with-the-pico/2

There is however one functionality that those docs do not cover, and that is installing additional packages to our Pico. Support for BMP280 is not included in Micropython, and so we'll need to install the library that will allow us to use it.

In Thonny, go to Tools -> Manage packages in the menu bar and type "bme280" in the search field. Second on the list of results there should be "micropython-bme280" and that is the package we'll need. This library supports both BMP280 and BME280, so no worries. Click on it and select Install. Once it is installed, we're fully ready to go. Make sure you install the BME280 library for Micropython, the version for the regular version of Python will not work. The library for DHT is built in in micropython so no need to do anything here.

And now the part we have all been waiting for, the code. In Thonny, create a file called weather.py (or any other, filenames don't matter for now) and paste this code snippet:

# https://github.com/SebastianRoll/mpy_bme280_esp8266
# https://docs.micropython.org/en/latest/esp8266/tutorial/dht.html

import dht
import bme280

SCL_PIN = machine.Pin(1)
SDA_PIN = machine.Pin(0)
DHT_PIN = machine.Pin(15)

i2c = machine.I2C(0, scl=SCL_PIN, sda=SDA_PIN)
bme_sensor = bme280.BME280(i2c=i2c)
d_sensor = dht.DHT11(DHT_PIN)

temperature, pressure, _ = bme_sensor.read_compensated_data()
temperature = temperature / 100
pressure = pressure / 256 / 100

d_sensor.measure()
humidity = d_sensor.humidity()

payload = {
    "temperature": temperature,
    "pressure": pressure,
    "humidity": humidity,
}
print(payload)

Don't worry, we'll now go throught it line by line so that everything is clear and understandable. The first two lines are comments with links to the libraries we are using in the snippet, you can checkt their documentation if you're curious.

import dht
import bme280

SCL_PIN = machine.Pin(1)
SDA_PIN = machine.Pin(0)
DHT_PIN = machine.Pin(15)

Now it's time to import the two libraries we are using, one for BMP280, second one for DHT11. Afterwards we are declaring three constants that will store the information about the pins that we are using to connect the Pico to the sensors. Remember to alter those lines if you connected your Pico differently that I did!

i2c = machine.I2C(0, scl=SCL_PIN, sda=SDA_PIN)
bme_sensor = bme280.BME280(i2c=i2c)
d_sensor = dht.DHT11(DHT_PIN)

temperature, pressure, _ = bme_sensor.read_compensated_data()
temperature = temperature / 100
pressure = pressure / 256 / 100

d_sensor.measure()
humidity = d_sensor.humidity()

Preparation's done, now it's time for the main work. The first block of the code above initializes the I2C protocol, the BME280 sensor, and the DHT11 sensor. The second block is reading from BME280. One thing to remember is that BMP280 measures two entities, temperature and pressure, and the more expensive BME280 measures also humidity. However, the read_compensated_data() method always returns three values, temperature, pressure, and humidity. The third value in our case will be alwas zero, so we just discard it. Simple. The next two lines are converting the returned values to a human readable standard, degrees Celsius in case of temperature and hectoPascals for atmospheric pressure. In the third block it's time for DHT11 to do it's job, we trigger the measuring operation and save its result.

payload = {
    "temperature": temperature,
    "pressure": pressure,
    "humidity": humidity,
}
print(payload)

The grand finale, the cream de la cream! The values that we got from the sensors are saved in a Python dictionary and printed to the screen. Voila! When the magic green Run button is pressed, you should get something like this in the console:

{'pressure': 999.5192, 'humidity': 48, 'temperature': 22.58}

Of course, showing numbers in the console is not the most useful way of presenting data. A better way it to show them on a graph to present how they change in time, or show them on your mobile phone, or maybe make other devices automatically do stuff based on them (e.g. turn on the AC if temperature is <20C). Patience, all that will be covered in the next parts of this series. Thanks for reading!