In this project, we'll monitor several parameters of indoor air quality with a Raspberry Pi and the following sensors:
- MH-Z19 -> CO2
- VMA342, consisting of:
- BME280 -> temperature + humidity + air pressure
- CCS811 -> volatile organic compounds (TVOC) [Work in progress]
A Streamlit dashboard will allow you to monitor the current air quality as well as the evolution over time:
- Raspberry Pi*, including:
- MicroSD card
- Micro USB power cable and adapter
- Protective case
- WiFi dongle (for RPi's older than model 3)
- VMA342 sensor
- MH-Z19 sensor**
- Small breadboard
- Jumper cables (male-female)
- Ethernet cable for the initial setup
*I used a Raspberry Pi model 2B for this project. Other models likely work as well, but weren't tested.
**The MH-Z19 sensor comes in multiple versions. I used the MH-Z19B. The mh-z19 python library seems to support at least the MH-Z19 and MH-Z19B. If your sensor doesn't come with output pins (like mine), you'll have to solder some stacking headers yourself.
- Install Raspberry Pi Imager.
- Insert micro SD card and install the Raspberry Pi OS using Raspberry Pi Imager.
- Add “SSH” File to the SD Card Root (
touch /Volumes/boot/ssh
). - Insert the SD card, connect your computer and Pi with an ethernet cable and boot the Pi by plugging in the power cord.
- Connect with the Pi via SSH:
ssh [email protected]
, default password =raspberry
.
The following steps will allow you to SSH into your Raspberry Pi over Wifi instead of over ethernet.
- On the Pi, edit the
wpa_supplicant.conf
file by adding your Wifi networks ssid and password as shown below:
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
Add the following content:
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=be
network={
ssid="my-ssid"
psk="my-password"
}
sudo reboot
- Check the WiFi connection:
ifconfig wlan0
An IP address should be visible if the connection was successful.
Connect the Raspberry Pi and MH-Z19 as follows:
RPi | MH-Z19 |
---|---|
5V | Vin |
GND | GND |
TXD | Rx |
RXD | Tx |
Note how the TXD and RXD are cross connected between the RPi and MH-Z19.
Connect the Raspberry Pi and VMA342 as follows:
RPi | VMA342 |
---|---|
3V3 | 3.3V |
GND | GND |
SDA | SDA |
SCL | SCL |
GND | WAKE |
Clone this repository in the Documents folder:
cd Documents
git clone https://github.com/StijnGoossens/rpi-airquality.git
Subsequently, install the packages below in order to interact with the sensors.
-
Enable Serial via
sudo raspi-config
(source) -
Install the mh-z19 package with
sudo pip install mh-z19
pip install wheel
(not sure whether really required)- Install RPi.GPIO with
export CFLAGS=-fcommon
andpip3 install RPi.GPIO
(source) - Enable I2C via
sudo raspi-config
(source) - Optionally adding the I2C module to the kernel:
sudo nano /etc/modules
and addi2c-dev
to the end of the file. - Reduce the baudrate in order to make the sensor compatible with Raspberry Pi:
sudo nano /boot/config.txt
and adddtparam=i2c_arm_baudrate=10000
(source).
Tip: i2cdetect -y 1
shows the current I2C connections.
sudo pip install RPi.bme280
Example to try out the CCS811 library:
import smbus2
import bme280
port = 1
address = 0x77
bus = smbus2.SMBus(port)
calibration_params = bme280.load_calibration_params(bus, address)
# the sample method will take a single reading and return a
# compensated_reading object
data = bme280.sample(bus, address, calibration_params)
# the compensated_reading class has the following attributes
print(data.id)
print(data.timestamp)
print(data.temperature)
print(data.pressure)
print(data.humidity)
# there is a handy string representation too
print(data)
pip3 install adafruit-circuitpython-ccs811
Example to try out the CCS811 library:
import board
import adafruit_ccs811
i2c = board.I2C() # uses board.SCL and board.SDA
ccs811 = adafruit_ccs811.CCS811(i2c, 0x5b)
# Wait for the sensor to be ready
while not ccs811.data_ready:
pass
while True:
print("CO2: {} PPM, TVOC: {} PPB".format(ccs811.eco2, ccs811.tvoc))
time.sleep(0.5)
Note that 0x5b
is the I2C address of the CCS811 on the VMA342 board (default is 0x5a
).
pip install streamlit==0.62.0
(Installing Streamlit>0.62 on Raspberry Pi isn't straightforward because of dependency on PyArrow)pip install numpy==1.20
sudo apt-get install libatlas-base-dev
export PATH="$HOME/.local/bin:$PATH"
(source)
Optional
For some reason a tornado.iostream.StreamClosedError: Stream is closed
error might occur after a running the Streamlit dashboard for a while. This can be resolved by increasing making the following changes to the Streamlit configuration:
- Change the
MESSAGE_SIZE_LIMIT
parameter insite-packages\streamlit\server\server_util.py
from50 * 1e6
to600 * 1e6
. - Change the
websocket_ping_timeout
parameter insite-packages\streamlit\server\Server.py
from60
to200
.
chmod 664 ~/Documents/rpi-airquality/src/monitor.pypi
- Run
crontab -e
and append the following command to the bottom of the file:
@reboot (/bin/sleep 30; sudo python3 /home/pi/Documents/rpi-airquality/src/monitor.py > /home/pi/cronjoblog-monitor 2>&1)
@reboot (/bin/sleep 30; export PATH=$PATH:/home/pi/.local/bin; streamlit run /home/pi/Documents/rpi-airquality/src/dashboard.py > /home/pi/cronjoblog-dashboard 2>&1)
*/5 * * * * /bin/ping -c 2 www.google.com > /home/pi/cronjoblog-ping.txt 2>&1
This will start the monitoring script and Streamlit dashboard on startup. Logs will be printed to the specified files in the /home/pi/
folder.
The third command will ping every five minutes in order to prevent the Raspberry Pi from losing internet connection ((source)[https://forums.raspberrypi.com/viewtopic.php?t=274966]).
Note that in order to streamlit to work from the cron job, the second statement adds the streamlit path to $PATH
. This streamlit path can be found by running which streamlit
. Which returns something like /home/pi/.local/bin/streamlit
. Addapt the above line accordingly. The reason is that in cron, PATH is restricted to /bin:/usr/bin
(source).
Extra: to make sure that the cron jobs have run, you can use the following command: grep CRON /var/log/syslog
Raspberry Pi 3
- Run
crontab -e
and append the following command to the bottom of the file (source):
@reboot (/bin/sleep 30; sudo sh -c 'echo 0 > /sys/class/leds/led0/brightness')
@reboot (/bin/sleep 30; sudo sh -c 'echo 0 > /sys/class/leds/led1/brightness')
Raspberry Pi 4
sudo nano /boot/config.txt
- Add the following lines below the
[Pi4]
settings (source):
# Disable the PWR LED
dtparam=pwr_led_trigger=none
dtparam=pwr_led_activelow=off
# Disable the Activity LED
dtparam=act_led_trigger=none
dtparam=act_led_activelow=off
# Disable ethernet port LEDs
dtparam=eth_led0=4
dtparam=eth_led1=4
- The lights will turn off once the Raspberry Pi has been restarted.
The Streamlit dashboard can be viewed from any device connected to the local network. Find out the IP address of the dashboard by viewing the log files of the dashboard script (nano /home/pi/cronjoblog-dashboard
). In there, you should see some output like below. The network URL is what you need. http://raspberrypi.local:8501/ might also work.
You can now view your Streamlit app in your browser.
Network URL: http://192.168.0.247:8501
External URL: http://81.82.78.41:8501