The Quest for Local Weather Data
You know how anyone can check a weather app and see what temperature it is outside? Well, what you can’t do is figure out what temperature it is inside your own apartment. Can you really trust that Nest to set a consistent temperature? I certainly don’t.
Time to build something to find out.
The Hardware
Here’s what I’m running for this little science experiment:
- Raspberry Pi 5 - The brains of the operation

Sensors
As with all things, more data is more fun:
| Sensor | Voltage | Current (mA) | What it does | Link |
|---|---|---|---|---|
| PMSA003I | 3.3/5V | 100 | Particulate matter (air quality) | Adafruit |
| SCD-41 | 3.3/5V | 17 | CO2. | Adafruit |
| TSL2591 | 3.3/5V | 0.4 | Light levels | Adafruit |
| BME688 | 3.3/5V | 1 | Gas, pressure, temp, humidity | Adafruit |
Wiring
The software
The first version of the weather station was a simple python script. Adafruit provides some fantastic python libraries for all the sensors I was using. As my first time working with I2C this seemed like the Golden Path.
Debugging i2c issues take 1
The weather station worked great for a while. But occasionally weird things would show up. Some sensors would stop showing up, while others would get stuck on a particular value. Restarting the data collection process wouldn’t fix these issues, the only solution was rebooting the Pi.
hot_pi exited with code 1 (restarting)
hot_pi | Traceback (most recent call last):
hot_pi | File "//app.py", line 13, in <module>
hot_pi | from hot_pi.seattle_weather import SeattleWeather
hot_pi | File "/hot_pi/seattle_weather.py", line 6, in <module>
hot_pi | from hot_pi.sensor import Sensor
hot_pi | File "/hot_pi/sensor.py", line 4, in <module>
hot_pi | import adafruit_bme680
hot_pi | File "/usr/local/lib/python3.10/dist-packages/adafruit_bme680.py", line 51, in <module>
hot_pi | from digitalio import DigitalInOut
hot_pi | File "/usr/local/lib/python3.10/dist-packages/digitalio.py", line 27, in <module>
hot_pi | from adafruit_blinka.microcontroller.bcm2712.pin import *
hot_pi | File "/usr/local/lib/python3.10/dist-packages/adafruit_blinka/microcontroller/bcm2712/pin.py", line 8, in <module>
hot_pi | D0 = Pin((4, 0))
hot_pi | File "/usr/local/lib/python3.10/dist-packages/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_pin_2_x.py", line 34, in __init__
hot_pi | self._chip = gpiod.Chip(chip_id)
hot_pi | File "/usr/local/lib/python3.10/dist-packages/gpiod/chip.py", line 58, in __init__
hot_pi | self._chip = _ext.Chip(path)
hot_pi | FileNotFoundError: [Errno 2] No such file or directory
hot_pi | Exception ignored in: <function Pin.__del__ at 0x7fff84b5b130>
hot_pi | Traceback (most recent call last):
hot_pi | File "/usr/local/lib/python3.10/dist-packages/adafruit_blinka/microcontroller/generic_linux/libgpiod/libgpiod_pin_2_x.py", line 38, in __del__
hot_pi | if self._line_request:
hot_pi | AttributeError: 'Pin' object has no attribute '_line_request'
I considered elaborate monitoring solutions with hooks to reboot any misbehaving Pis. But I convinced myself it was Python and/or the Adafruit SDKs, thus the great Rust re-write began. In hindsight, I should have realized it was a hardware issue since the only way to fix the issue was a reboot.
The great Rust rewrite
I had dabbled with Rust for years, but nothing major. I knew Rust had the performance I needed to collect high precision weather events in my apartment every minute.
Debugging i2c issues take 2
At first the great Rust re-write was great. But then tragedy struck, the sensors started to fail again. Had I wasted hundreds of dollars (CO2 sensors are expensive, especially when you buy 3 of them) for nothing? Would I ever get to the bottom of what temperature and humidity my apartment is? Would I slowly die to CO2 poisoning and not realize it?
But there in the Rust logs was a new clue:
2026-01-28T06:32:51.815211Z INFO Polling sensors...
2026-01-28T06:32:51.826657Z ERROR Error polling bme680: Failed to get sensor data: WriteReadError(I2CError { err: Errno(121) })
2026-01-28T06:32:51.836693Z ERROR Error polling tsl2591: Failed to read TSL2591 channels: I2cError(I2CError { err: Errno(121) })
2026-01-28T06:32:56.298361Z ERROR Error polling scd41: Failed to get measurement: I2c(I2CError { err: Errno(5) })
2026-01-28T06:32:56.298436Z ERROR Error polling pmsa003i: Failed to read PMSA003I sensor: I2C(I2CError { err: Errno(5) })
The almight Rust had given me a new
Everything still showing up according to i2c-detect:
sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- 12 -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- 29 -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- 62 -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
The smoking gun showed up in the kernel logs:
[336495.416190] i2c_designware 1f00074000.i2c: i2c_dw_handle_tx_abort: SDA stuck at low
Searching around for this issue quickly points to a hopefully easy fix:
- Pull-up resistors to help avoid I2C channels getting stuck
- Decoupling capacitors to smooth out power noise
What’s Next?
I’m planning to add the resistors and capacitors once they get delivered.