In my last post, I wrote about using a Raspberry Pi Pico to create a wireless thermometer that would capture and send data to a server for later analysis and record keeping. This time, I increased the complexity and built an entire weather station.
Why a Pico?
I've seen a few articles on building a weather station with a regular Raspberry Pi, but I decided to use a Pico for a few reasons:
To consolidate everything into a smaller form factor
Reduce power consumption
Eliminate the need to run an entire OS
I didn't like the idea of wasting so many pins and resources on a Raspberry Pi compared to the Pico
Pi's have started to become more expensive compared to the basic $35.00 versions
To see if I could make it work
Resources
I did follow Raspberry Pi's "Build your own weather station" article to build this, but there were some deviations and changes due to using a Pico and CircuitPython rather than a full Raspberry Pi and Python.
This article from Adafruit was also quite insightful and helped me get multiple tasks running concurrently within the program.
Bill of Materials
To build the weather station, I purchased the following:
The true cost is actually a bit higher, since you have to take shipping into account along with things like a bread board and jumper cables.
Building the Circuit
There are a number of ways to break this out, but I think it makes sense to start with the circuit, and then move into each component within the weather station. The circuit involves connecting four main components to the pico:
BME280 (temperature / humidity / pressure)
DS18B20 (temperature)
RJ11 (rain guage)
RJJ11 (anemometer & wind vane)
BME280
Pico
VIN
3v3
GND
GND
SCK
GP1
SD1
GP0
DS18B20
Pico
Red
3v3
Yellow
GP2
Blue
GND
💡
The DS18B20 should also have a 4.7k resistor between the red and yellow connectors
Rain Guage
Pico
2
GND
3
GP4
Anemometer & Wind Vane
Pico
1
GND
2
GP3
3
GND
4
GP26
💡
There should also be a 4.7k resistor from GP26 and 3v3
Project Structure
Now that the circuit's complete, let's take a look at the project structure.
The weather station is coded with CircuitPython, so the entry point is code.py and the lib directory contains some Adafruit libraries, which can be found here. To keep things organized, the code is separated by function:
anemometer.py - responsible for the anemometer and measuring the wind speed
ground_sensor.py - responsible for measuring temperature from the one-wire temperature sensor
rain_guage.py - responsible for measuring rainfall
thp_sensor.py - responsible for reading temperature and humidity from the BME280 sensor
wind_vane.py - responsible for the wind vane and measuring the wind direction
weather_station.py - the overall parent class containing logic for running the weather station, collecting sensor readings, and submitting them to an API
The Code
Entry point
The entry point for running the weather station is actually very simple, as the core logic is split up into separate files.
All we're doing is setting some properties, initializing a WeatherStation object, and starting the weather station.
Weather Station
In the weather station file, we're setting up references to each device that's attached to the pico, starting the process to capture sensor readings, and submitting those readings to my API.
Lets take a look at what's happening in the main functions:
__init__ - Here, we're initializing each of our child sensors, connecting to wireless, and setting up our connection pool in order to make http requests.
start - This is the function called from code.py, and actually kicks everything off. All we're doing is starting the main() function asynchronously.
main - the main function creates an asynchronous task for reading the rain gauge, anemometer, windspeed, and wind direction as well as the core function of querying sensors. Once the tasks are created, we start them.
query_sensors - query_sensors runs on an interval, gathering sensor readings and submitting the telemetry every 15 seconds, though this could be changed to any duration.
generate_telemetry - this method creates and returns the json object that contains sensor readings. In my case, I'm setting the timestamp to a UTC value for storage, which will then display an appropriate time zone when viewed through the react front end for managing everything.
post_telemetry - post_telemetry is responsible for creating the authorized http request to my api which then stores the telemetry in a database.
Anemometer
The anemometer is responsible for measuring the wind speed. In effect, what I'm doing is:
counting the number of times that the sensor triggers while spinning over a 5 second duration
keeping track of the values
calculating an average and resetting the counts any time the readings are requested
The constants at the top came from the raspberry pi article for building a weather station, and there's a good explanation of what they are and the logic of how this works if you're interested in taking a look.
Logic in spin_handler is a way to imitate IRCs in CircuitPython. Every time the input goes high, the count is increased, which prevents us from having to continuously poll for a high or low value from the pin on some interval.
Ground Sensor
The ground sensor is pretty simple compared to the anemometer. We can take measurements at will, and Adafruit's libraries make this trivial.
Rain Gauge
For the rain gauge, there's a tilting device that tips once enough water has been collected, so all we have to do is count the number of tips to calculate the quantity of rainfall. Here, we're utilizing the same technique as in anemommeter.py, where we setup an event handler to count the number of tips.
THP Sensor
Just like the onewire / ground temperature sensor, the THP sensor is fairly trivial thanks to Adafruit's libraries. We can calculate the temperature, pressure, humidity, and altitude any time we want.
Wind Vane
The wind vane was the most challenging part in my opinion. There's an explanation of how it works in the raspberry pi article, but essentially, you have to connect a resistor to the circuit to measure voltages, and then associate each voltage to an angle to get the direction from which the wind is coming.
If you want to know the actual cardinal direction, you then have to assign each numerical angle to the correct cardinal direction, but this could change based on the rotation of the wind vane itself.
For the conversion factor, I'm utilizing 3.3 instead of 5 because the pico's output is only 3.3 volts instead of the raspberry pi's 5 volts.
Seeing it in action
With the circuit built and everything coded, what does it look like in reality? Let's startup Thonny and take a look.
Printing out the results, we can see both the API response, as well as the data that was sent with each request. We can also see the results looking at the database itself.
Next Steps
I'm excited that the weather station is working, but it currently resides on a breadboard and isn't in production. The next steps will be:
Creating a PCB design to solder the components
Use a CNC machine to cut out / create the PCB
Design an enclosure to fit all of the pieces
3D print the enclosure
Mount the weather station outside for production use
I'm looking forward to posting about those results in a future article 😄