DIY Arduino PM10/温湿度监测仪 —— 关注空气质量与病毒传播风险
零部件与物料
| Arduino Nano R3 | ×1 |
| SDS018 激光 PM2.5/PM10 传感器 | ×1 |
| DHT22 温湿度传感器模块(含上拉电阻/电容) | ×1 |
| OLED 显示屏 I2C SSD1306 128×32 | ×1 |
| 3mm LED 绿/黄/红 | 各 1 |
| 390 Ω 电阻(LED 限流) | ×3 |
| 4.7 kΩ 电阻、100 nF 电容(DHT22 备用) | 各 1 |
| 移动电源(USB 供电) | ×1 |
| M3×16/M3×6 螺丝、热缩管、排针、跳线等 | 若干 |
所需工具
- 烙铁、无铅锡丝
- 3D 打印机(PLA 橙色)
- 热熔胶枪、电钻
应用与在线服务
- Arduino IDE(需安装 Adafruit SSD1306、DHT sensor library)
项目背景
多项研究表明,颗粒物(PM10/PM2.5)可能作为病毒「载体」,将新冠病毒等带至更远距离并在空气中存活数小时至数周。意大利北部封城期间因集约化养殖场持续排放粪便氨气与颗粒物,PM10 浓度未显著下降,且疫情严重程度与 PM10 超标高度相关。低湿度(<40%) 或高湿度(>60%) 亦利于病毒存活。本项目旨在低成本复刻监测装置,实时显示 PM10、温度、湿度,并通过 LED 分级报警,提醒公众采取防护。
报警分级逻辑
| 等级 | PM10 (µg/m³) | 湿度 (%RH) | LED 指示 |
|---|---|---|---|
| 正常 | 0–25 | 40–60 | 全灭 |
| 低风险 | 0–25 | <40 或 >60 | 绿亮 |
| 低风险 | 26–50 | 40–60 | 绿亮 |
| 预警 | 26–50 | <40 或 >60 | 绿+黄亮 |
| 预警 | >51 | 40–60 | 绿+黄亮 |
| 报警 | >51 | <40 或 >60 | 绿+黄+红全亮 |
传感器接线
SDS018 (UART)
- Pin3(5V) → Nano 5V
- Pin5(GND) → Nano GND
- Pin7(TX) → Nano RX (D0) —— **需改装 Nano:拆除 RXD SMD 电阻并引出跳线帽,编程时接上,运行时拔下**
DHT22
- VDD → 5V, GND → GND
- DATA → D2,VDD-DATA 间接 4.7k 上拉,VDD-GND 并联 100nF
OLED I2C
- VCC→5V, GND→GND, SCL→A5, SDA→A4
LED
- 红→D3, 绿→D4, 黄→D5,阴极串 390Ω 接 GND
3D 打印外壳
双件式橙色 PLA 外壳:下盖固定 Nano 与 SDS018(含 Micro-USB 口),上盖固定 DHT22、OLED、三个 LED(孔径 3mm,可扩至 5mm 用灯座)。热熔胶固定模块,导线收纳整理。附 SketchFab 模型链接。
固件流程
- 上电初始化:外设初始化、LED 自检
- 主循环 (2s 周期):
- 读 DHT22 温湿度 → 串口打印
- 读 SDS018 10 字节帧 (0xAA 0xC0 ... 0xAB) → 校验和通过 → 解析 PM2.5/PM10
- 按分级逻辑点亮 LED
- OLED 轮询显示:PM10+湿度 / PM2.5+温度(报警时显示 "Alarm!!!")
完整固件代码
#include
#include
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
int startDHT = 0, switchDisplay = 0;
const int ledR = 3, ledG = 4, ledY = 5;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(ledR, OUTPUT); pinMode(ledG, OUTPUT); pinMode(ledY, OUTPUT);
dht.begin();
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) for(;;);
display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE);
// LED 自检
digitalWrite(ledG, HIGH); delay(500);
digitalWrite(ledY, HIGH); delay(500);
digitalWrite(ledR, HIGH); delay(500);
digitalWrite(ledG, LOW); digitalWrite(ledY, LOW); digitalWrite(ledR, LOW); delay(500);
}
void loop() {
if (!startDHT) { delay(2000); startDHT = 1; }
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) return;
Serial.print("Humidity: "); Serial.print(h); Serial.print("% Temp: "); Serial.print(t); Serial.println("°C");
// SDS018 读取
uint8_t buf[10] = {0};
float pm25 = 0, pm10 = 0;
while (Serial.available() > 0) {
for (int i=0; i<10; i++) buf[i] = Serial.read();
if (buf[0]==0xAA && buf[1]==0xC0 && buf[9]==0xAB) {
uint8_t sum = 0;
for (int i=2; i<=7; i++) sum += buf[i];
if (sum == buf[8]) {
pm25 = ((buf[3]*256.0 + buf[2])) / 10.0;
pm10 = ((buf[5]*256.0 + buf[4])) / 10.0;
Serial.print("PM2.5: "); Serial.print(pm25); Serial.println(" ug/m3");
Serial.print("PM10: "); Serial.print(pm10); Serial.println(" ug/m3");
}
}
Serial.flush();
}
// LED 报警逻辑
if (pm10 <= 25 && h >= 40 && h <= 60) { digitalWrite(ledG,LOW); digitalWrite(ledY,LOW); digitalWrite(ledR,LOW); }
else if (pm10 <= 25 && (h < 40 || h > 60)) { digitalWrite(ledG,HIGH); digitalWrite(ledY,LOW); digitalWrite(ledR,LOW); }
else if (pm10 > 25 && pm10 <= 50 && h >= 40 && h <= 60) { digitalWrite(ledG,HIGH); digitalWrite(ledY,LOW); digitalWrite(ledR,LOW); }
else if (pm10 > 25 && pm10 <= 50 && (h < 40 || h > 60)) { digitalWrite(ledG,HIGH); digitalWrite(ledY,HIGH); digitalWrite(ledR,LOW); }
else if (pm10 > 50 && h >= 40 && h <= 60) { digitalWrite(ledG,HIGH); digitalWrite(ledY,HIGH); digitalWrite(ledR,LOW); }
else { digitalWrite(ledG,HIGH); digitalWrite(ledY,HIGH); digitalWrite(ledR,HIGH); }
// OLED 交替显示
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
if (!switchDisplay) {
if (pm10 > 50) display.print("Alarm!!!");
else { display.print("PM10:"); display.println(pm10); }
display.print("HR%:"); display.println(h);
switchDisplay = 1;
} else {
display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // 反色
display.print("PM25:"); display.println(pm25);
display.print("T[C]:"); display.println(t);
switchDisplay = 0;
}
display.display();
digitalWrite(LED_BUILTIN, HIGH); delay(500);
digitalWrite(LED_BUILTIN, LOW); delay(1500);
}
原理图与外壳模型
电路原理图展示所有模块连接;上/下盖 3D 模型见 SketchFab 链接。
制造工艺