Apart from building a weather station, another thing I had been wanting to do for a long time was to learn Python. With this Weather Station project I satisfied both :-)
Check the source code of this project in Codeberg.
Learning the Pimoroni's WeatherHAT library
The first thing I did, after having everything installed, was to learn how the Pimoroni's WeatherHAT library works in order to:
- Read the weather data from the sensors
- Display the weather data in the WeatherHAT's screen
So I opened an SSH connection and started playing!
Apart from being my first steps with Python what I learned about the library was:
- It is necessary to call the "update" method of the sensor before getting the weather readings
- The first reading is "crap", it is necessary to perform an initial update and wait a few seconds before getting good reading values.
- There are two attributes for temperature: device_temperature and temperature
- The temperature attribute is calculated by applying a default offset of 7.5ΒΊC to the device_temperature reading.
Calibrating the Temperature
The first thing I focused on was to properly calibrate the temperature. For this I spent some days gathering temperature data from the sensor and from another nearby weather station at the same time, taking readings every hour.
What I noticed was that the difference between both sensors wasn't a fixed value. The readings from the WeatherHAT's sensor were always between 12ΒΊC and 16ΒΊC higher than those from the nearby station.
So instead of applying a fixed offset I tried a linear regression. I created a class to calculate the offset based on a given device_temperature value.
In the future I could find a better way to calculate the offset. That is why I created a TemperatureOffsetInterface.
class LinearRegressionOffset(TemperatureOffsetInterface):
def __init__(self, config):
self.a = 0
self.b = 0
self.config = config
self.doLinearRegression()
def getOffset(self, x):
offset = (self.b * x) + self.a
return offset
def doLinearRegression(self):
x = self.config['data']['x'].split(",")
y = self.config['data']['y'].split(",")
x = list(map(float, x))
y = list(map(float, y))
medianX = statistics.median(x)
medianY = statistics.median(y)
numSum = 0
denSum = 0
for index, xValue in enumerate(x):
numSum = numSum + (xValue - medianX) * (y[index] - medianY)
denSum = denSum + ((xValue - medianX) * (xValue - medianX))
self.b = numSum / denSum
self.a = medianY - (self.b * medianX)
I could have used a Python library to perform the linear regression but, remember, the goal of this project was to learn Python ;-).
Decoupling From The Hardware
Having solved the calibration of the temperature sensor it was time to start coding the reading system.
The process should run periodically, for example every 5 minutes. On each iteration the system should read the data from the sensor, apply the temperature offset, display the data on the screen and send it to an external service.
I work on my own PC, as it is more comfortable than coding directly in the Pi. That is why I created some mocks to simulate access to the WeatherHAT hardware.
This way I could code the main algorithm decoupled from the hardware.
sensor = factory.createSensor()
display = factory.createDisplay()
Once the main algorithm was done I simply had to code the hardware-specific parts. I had to upload each change to the Pi but using the examples provided by Pimoroni it was more or less easy to get the data from the sensor and show it in the WeatherHAT screen.
Sending The Data To An External Service
I wanted to see the current temperature from anywhere and also store the readings in order to generate statistic from the weather data.
So the next step was to send the data to an external service. As I hadn't yet implemented my Weather Data Webapp, I started sending the data to Adafruit. I created a dashboard to see current data and also a graph to easily compare the temperature readings with the nearby station. This way I could check the quality of the linear regression.
I created an ConnectorInterface to first create the Adafruit connector and later the implementation to send the weather data to my Weather Data Webapp.
class ConnectorInterface:
def send(self, WeatherData):
pass
I also created a MockConnector to avoid sending bad data while working on the main flow of the algorithm.
Implementing An Abstract Factory
Everything was working fine, but changing which implementations to use while testing the system locally (mock) and in "production" (on the Pi) was a bit messy.
To simplify the mode selection I applied the Abstract Factory Pattern. This way, with a single configuration setting, I could load the right set of components (Sensor, Display and Connector).
class SystemFactoryInterface:
def createDisplay(self) -> DisplayInterface:
pass
def createConnector(self, config) -> ConnectorInterface:
pass
def createSensor(self, config) -> SensorInterface:
pass
Conclusion
I enjoyed this part a lot. Learn Python, read the data from the sensor, calibrate the temperature, apply some design patterns, ... It brought back the feeling I had when I first started learning to program.
Maybe because it is a project that is really different to the ones I'm more used to develop, maybe because it mixes different things or maybe both :-).
In the next article I'll try to explain the implementation of the Weather Data Webapp. This time with PHP and Symfony, a language and a framework that I'm more used to work with.

Top comments (2)
π¦
I like how you have segmented this all into parts.
Thanks Anna! Actually, divide & conquer always works! π