监控室内空气质量

1. 背景

爱否出品的新风消费报告[1]里提到了一个概念:室内空气的四个终极目标是恒温、恒湿、恒净、恒氧。笔者对此深感认同,可惜笔者生活的北京并没有足够宜人的气候[2]

空气质量指数[3]也堪忧:

更糟糕的是,为了保证室内空气含氧量,笔者的窗户常年是半开的。这就导致室内空气很容易受室外影响,不仅容易积灰,而且往往要等空气质量显著糟糕时笔者才会想到要关窗。

为了能更好地评估室内空气质量,并指导改进的方向,笔者决定搭建一套监控室内空气质量的解决方案。需要监控的指标包括:温度/湿度/二氧化碳/PM2.5。

2. 硬件部分

首先是物美价廉的米家蓝牙温湿度计2,只要29即可实现温湿度测量,并可在APP端查看历史数据。然而实际体验并不好,打开APP后很难直接加载成功数据(笔者甚至要重启蓝牙才行),查看历史数据功能也不能自定义时间范围。不清楚使用蓝牙网关后体验能否得到提升。

image-20221206092203598 image-20221206092129994

然后是功能相对完善的青萍空气检测仪Lite、淘宝某品牌空气检测仪,在500元的价位上都能做到温度/湿度/二氧化碳/PM2.5的检测。

img image-20221206093206207

青萍这款接入了米家生态,能很好和其余设备联动,外形也更为简约。某品牌这款优点是自定义程度高,和店家咨询后得知其能通过USB将监控数据发送主机,并提供了一份源码[4]作为参考。笔者考虑到使用米家APP的体验不佳,且有较多自定义需求,所以购入了后者。

后者到手后笔者发现了一些小问题:

  • 由于需要通过USB发送检测数据,所以检测仪不能离主机太远,而摆放的位置可能会影响检测准确性
  • 由于需要始终连接USB,各部件的发热(包括内置电池)会影响温度检测准确性
  • USB发送的检测数据中不包括PM10,商家解释这是因为国内的PM10传感器普遍不准确

好在这些问题笔者都能接受。有了数据源后,下面就开始正式施工。

3. 软件部分

prometheus架构图

参考Prometheus的架构图,笔者列出了自己这套监控方案的架构图:定期抓取空气检测数据并上报、Grafana做展示、告警则用Telegram触达。

flowchart LR
    A[空气检测仪]--USB-->B[PC]
    B-.push.->C[Pushgateway]
    D[Promethues]-.pull.->C
    D-->E[Alertmanager]
    E-->G[Telegram]
    F[Grafana]-->D
    style C stroke-dasharray:5
    style E stroke-dasharray:5

3.1 上报检测数据

releases页下载最新Pushgateway后用Supervisor等工具运行,即可在9091端口上提供API服务。

将检测仪连接到PC后,PC会自动识别到该设备(win10/ubuntu20.04都无需额外安装驱动,好评)。用波特率57600连接后,笔者发现检测仪将PM2.5/CO2/温度/湿度等数据以CSV格式周期性发送至PC。

于是可以编写如下脚本,用于解析检测仪提供的数据并将其发送至Pushgateway:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import time
from datetime import datetime
import serial
import socket
import requests

PORT = '/dev/ttyUSB0'
BRATE = 57600
HOST = socket.gethostname()
alias = {0: '0.3um', 1: 'pm2.5', 2: 'hcho', 3: 'co2', 4: 'temp', 5: 'humidity'}

session = requests.Session()
device = serial.Serial(port=PORT, baudrate=BRATE, timeout=2)
while True:
time.sleep(0.1)
if device.in_waiting > 0:
text = device.readline().decode('utf-8').strip() # type: str
lines = ['# TYPE b36_air_monitor gauge']
for i, val in enumerate(text.split(',')):
name = alias.get(i)
if not name:
continue
lines.append(
f'b36_air_monitor{{instance="{PORT}",alias="{name}",index="{i}"}} {val}')
lines.append('')
url = f'http://127.0.0.1:9091/metrics/job/{HOST}' # Pushgateway地址
try:
session.post(url, data='\n'.join(lines))
except Exception as ex:
print(datetime.now())
print(str(ex))

运行后可读取Pushgateway的输出进行验证(由于笔者购买的检测仪没有搭配甲醛模块,所以hcho的值始终为0):

1
2
3
4
5
6
7
8
$ curl 127.0.0.1:9091/metrics
# TYPE b36_air_monitor gauge
b36_air_monitor{alias="0.3um",index="0",instance="/dev/ttyUSB0",job="nas"} 250
b36_air_monitor{alias="co2",index="3",instance="/dev/ttyUSB0",job="nas"} 687
b36_air_monitor{alias="hcho",index="2",instance="/dev/ttyUSB0",job="nas"} 0
b36_air_monitor{alias="humidity",index="5",instance="/dev/ttyUSB0",job="nas"} 28
b36_air_monitor{alias="pm2.5",index="1",instance="/dev/ttyUSB0",job="nas"} 1
b36_air_monitor{alias="temp",index="4",instance="/dev/ttyUSB0",job="nas"} 26

3.2 接入Prometheus

数据上报到Pushgateway后,下一步是由Prometheus进行抓取。在scrape_configs下增加配置即可:

1
2
3
4
5
scrape_configs:
- job_name: "pushgateway"
honor_labels: true
static_configs:
- targets: ["localhost:9091"]

重启Prometheus后可在Web页面验证:

3.3 配置告警

从笔者查阅的资料来看,PM2.5浓度小于35μg/m3、CO2浓度小于1000ppm是比较舒适的室内空气标准[5]

于是笔者基于这两个数值为抓取到的数据配置了告警。首先是在releases页下载最新Alertmanager并用Supervisor工具运行,配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
route:
group_by: ['alertname']
receiver: 'tg'
receivers:
- name: 'tg'
telegram_configs:
- api_url: "https://api.telegram.org"
bot_token: "xx:xx"
chat_id: xxx
parse_mode: "HTML"
http_config:
proxy_url: "xxx"

然后是在Promethues的配置中加入告警规则:

1
2
3
# prometheus.yml
rule_files:
- "prometheus_alert.yml"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# prometheus_alert.yml
groups:
- name: air_monitor
rules:
- alert: HighCO2
expr: avg(b36_air_monitor{job="nas",instance="/dev/ttyUSB0",alias="co2"}) > 1000
for: 10m
annotations:
summary: "室内CO2浓度超过1000"

- alert: HighPM2.5
expr: avg(b36_air_monitor{job="nas",instance="/dev/ttyUSB0",alias="pm2.5"}) > 35
for: 10m
annotations:
summary: "室内PM2.5浓度超过35"

当过去10分钟PM2.5浓度/CO2浓度超过设定值时,Promethues会通过Alertmanager向笔者的Telegram发送告警。

4. 效果展示

最后来看看效果:

Grafana监控看板

Telegram告警

笔者表示很满意!

PS:其实从上图的监控可以看出,笔者的室内PM2.5/CO2浓度都比较低,这是因为笔者在写下这篇文章时已经通过一些手段大幅改善空气状况了,笔者会在后续的文章进行分享~

引用


监控室内空气质量
https://www.yooo.ltd/2022/12/05/监控室内空气质量/
作者
OrangeWolf
发布于
2022年12月5日
许可协议