基于LoRa的智慧城市空气质量监测系统
元器件与物料
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
![]() |
|
× | 1 | |||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
|
× | 1 | ||||
![]() |
|
× | 1 |
必需工具与设备
![]() |
|
软件与在线服务
![]() |
|
|||
![]() |
|
|||
![]() |
|
项目概述
智慧城市与空气质量
据世界银行定义,智慧城市利用技术提供高效公共服务与可持续城市环境。核心在于实时环境监测,识别污染源并支撑数据驱动决策——无论优化交通流还是建议居民最安全步行路线。
问题与方案
城市空气污染构成重大健康风险,及时数据对缓解至关重要。低成本传感器节点组成的物联网(IoT)网络可持续采集空气质量与气象参数。结合可靠低功耗通信层,这些网络使城市管理者能检测污染热点并主动响应。
项目规划
本方案监测关键环境指标——噪声、温度、湿度、空气质量——并通过无线传输至云平台。整个节点由1W太阳能板配锂电池备份供电,保障自主运行。云端仪表盘实时可视化数据,支持即时洞察与长期趋势分析。
避免每个传感器直连云端——该方式增加功耗与成本——我们聚合多个邻近节点数据,经单一LoRa网关上传。LoRa的远距离、低功耗特性使其成为密集城市部署的理想选择。

选择Grandeur作为后端平台,因其提供Arduino、NodeMCU、Raspberry Pi友好SDK及拖拽式仪表盘构建器。
太阳能提供远程节点可持续免维护能源,锂电池保障低光照期运行。
构建传感器节点
节点集成Grove空气质量传感器、Grove响度传感器、DHT11温湿度传感器——均免焊接插入Seeed Base Shield,简化组装维护。

供电由1W太阳能板与Seeed LiPo Rider Pro充电器提供,内含5V升压转换器维持Arduino工作电压。
Reyax RYLR998 LoRa收发器处理无线传输. 因模块接受3.6V逻辑,简单分压电路保护Arduino 5V TX线。

下图为LoRa模块通过定制转接板连接Base Shield的接线图。

连接LoRa模块时,确保Base Shield电源开关置于3.3V档位。

组装完成后,从Arduino向LoRa模块发送AT指令验证通信。

上传与测试
上传提供的Arduino草图,打开串口监视器确认传感器读数正常传输。

外壳组装
打印附件区提供的STL文件。确认贴合后,双面胶或热熔胶固定组件于外壳内。太阳能板安装顶部,电源线通过预留孔走线。


所有组件固定后,节点即可现场部署。

配置Grandeur
Grandeur是将设备与用户配对并提供实时仪表盘的IoT后端。按步骤搭建项目:
- 在grandeur.dev创建新项目。
- 注册用户账号;该账号后续用于配对设备。
- 定义设备模型JSON schema:
{"temp":0, "humid":0, "air":0, "noise":0} - 实例化设备,获取唯一ID和访问令牌,存入Arduino固件。
- 通过仪表盘或API(如
pairDevice())配对设备与用户。 - 构建Canvas仪表盘:添加四个图表组件——分别绑定温度、湿度、噪声、空气质量变量。
Canvas授权后,用户登录即可查看实时数据。网关固件需API密钥和设备令牌。
网关搭建
NodeMCU作为LoRa网关,接收传感器节点UART数据并经Wi-Fi转发至Grandeur。虽Raspberry Pi可胜任,但NodeMCU以紧凑低成本满足带宽延迟需求。

网关固件核心逻辑
固件解析LoRa模块接收的逗号分隔载荷,转浮点数,经SDK推送至Grandeur。以下为数据提取传输核心代码:
void process_received_data(){
start_pos = inputString.indexOf(start_val);
end_pos = inputString.indexOf(end_val);
String data_string = inputString.substring(start_pos+1, end_pos-1);
int firstCommaIndex = data_string.indexOf(',');
int secondCommaIndex = data_string.indexOf(',', firstCommaIndex+1);
int thirdCommaIndex = data_string.indexOf(',', secondCommaIndex+1);
String temperature = data_string.substring(0, firstCommaIndex);
String humidity = data_string.substring(firstCommaIndex+1, secondCommaIndex);
String noise_level = data_string.substring(secondCommaIndex+1, thirdCommaIndex);
String air_auality = data_string.substring(thirdCommaIndex+1);
temp = temperature.toFloat();
humid = humidity.toFloat();
noise = noise_level.toFloat();
air = air_auality.toFloat();
inputString = "";
stringComplete = false;
}
仪表盘视图


固件代码
- 传感器节点代码
- 网关代码
传感器节点 (Arduino)
#include "AirQuality.h" // Grove空气质量库
#include "Arduino.h"
#include "DHT.h"
#define DHTPIN 4 // DHT11数字引脚
#define DHTTYPE DHT11 // 传感器型号
DHT dht(DHTPIN, DHTTYPE);
AirQuality airqualitysensor;
int current_quality =-1;
const int sampleWindow = 50; // 20Hz采样
unsigned int sample;
float noise;
float temp, humid;
int airQuality;
void setup() {
Serial.begin(115200); // UART连接LoRa
airqualitysensor.init(14);
dht.begin();
}
void loop() {
noise = calculate_sound_in_db();
airQuality = calculate_air_quality();
calculate_temp_humid();
String values = String(temp)+","+String(humid)+","+String(noise)+","+String(airQuality);
String cmd = "AT+SEND=0,"+String(values.length())+","+values;
Serial.println(cmd);
delay(15000);
}
float calculate_sound_in_db(){
unsigned long startMillis= millis();
float peakToPeak = 0;
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
while (millis() - startMillis < sampleWindow){
sample = analogRead(A1);
if (sample < 1024){
if (sample > signalMax) signalMax = sample;
else if (sample < signalMin) signalMin = sample;
}
}
peakToPeak = signalMax - signalMin;
float db = map(peakToPeak,0,1000,48,120);
return db;
}
int calculate_air_quality(){
current_quality=airqualitysensor.slope();
return current_quality;
}
void calculate_temp_humid(){
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) return;
temp = t;
humid = h;
}
ISR(TIMER1_OVF_vect){
if(airqualitysensor.counter==61){
airqualitysensor.last_vol=airqualitysensor.first_vol;
airqualitysensor.first_vol=analogRead(A0);
airqualitysensor.counter=0;
airqualitysensor.timer_index=1;
PORTB=PORTB^0x20;
}else{
airqualitysensor.counter++;
}
}
网关 (Arduino/NodeMCU)
/* 引入SDK与WiFi库 */ #include#include #include SoftwareSerial LORA(D7, D8); /* 配置参数 */ String deviceID = "your device id"; String apiKey = "your api key"; String token = "your device token"; /* WiFi凭据 */ String ssid = "your wifi ssid"; String password = "your wifi password"; /* 创建项目与设备对象 */ Grandeur::Project project; Grandeur::Project::Device device; /* 连接状态与时间追踪 */ int connected = 0; uint32_t lastUpdate = 0; String inputString = ""; boolean stringComplete = false; // :和%为发射端设定的数据分隔符 char start_val = ':'; char end_val = '%'; int start_pos = 0; int end_pos = 0; float temp = 0; float humid = 0; float air = 0; float noise = 0; /* 设备连接状态回调 */ void onConnection(bool status) { switch(status) { case CONNECTED: Serial.println("Device is connected to the cloud."); connected = 1; lastUpdate = millis(); return; case DISCONNECTED: Serial.println("Device is disconnected from the cloud."); connected = 0; return; } } /* WiFi连接 */ void connectWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println(WiFi.localIP()); } /* 数据更新确认回调 */ void dataCallback(const char* code, Var res) { if(code == "DEVICE-DATA-UPDATED") { double temp_data = (double) res["update"]["temp"]; double humid_data = (double) res["update"]["humid"]; double noise_data = (double) res["update"]["noise"]; double air_data = (double) res["update"]["air"]; Serial.printf("Temperature: %f Humidity: %f Noise Level: %f Air Quality: %f\n", temp_data, humid_data, noise_data, air_data); return; } Serial.println("Failed to Update DATA"); return; } /* 发送更新数据 */ void sendUpdate() { Var data; data["temp"] = temp; data["humid"] = humid; data["noise"] = noise; data["air"] = air; device.data().set("temp", temp, dataCallback); device.data().set("humid", humid, dataCallback); device.data().set("noise", noise, dataCallback); device.data().set("air", air, dataCallback); } void setup() { Serial.begin(9600); LORA.begin(115200); inputString.reserve(200); connectWiFi(); project = grandeur.init(apiKey, token); device = project.device(deviceID); project.onConnection(onConnection); } void loop() { serialEvent(); project.loop(WiFi.status() == WL_CONNECTED); if (stringComplete) { process_received_data(); if (connected) sendUpdate(); } } void serialEvent() { while (LORA.available()) { char inChar = (char)LORA.read(); if (inChar == '\n') { stringComplete = true; } else inputString += inChar; } }
制造工艺
- 使用 OpenSensors 监测空气质量:Arduino 与 Python 实战指南
- DIY 空气质量监测仪:低成本 EPA 合规检测器
- 基于 Helium 网络的太阳能供电空气质量监测站:设计与部署全指南
- 智能植物物联网:Arduino 与 Azure 构建连接温室
- 智能室内空气质量与垃圾监测系统
- Bosch Sensortec 推出 AI 赋能 BME688 空气质量传感器,降低病毒传播风险
- 瑞萨升级室内空气质量传感器平台,嵌入式AI增强异味检测
- 微型 PM2.5 与 VOC 传感器实现实时个人空气质量监测
- 先进碳基 NO₂ 传感器提升室内空气质量监测水平
- 物联网传感器革新空气污染监测与公共健康
- 物联网赋能环境空气质量监测:提升安全、生产力与可持续性
- 工业物联网空气质量监测:提升生产力与降本









