1. 项目概述:当物联网遇上气象监测
去年夏天帮朋友改造阳台种植系统时,发现市面上成品气象站要么功能单一,要么价格昂贵。于是用ESP32开发板+传感器+网页可视化,花两天时间搭建了一套成本不到200元的智能气象站。这套系统不仅能实时监测温湿度、气压、光照等数据,还能通过网页远程查看历史曲线,异常数据自动触发微信推送告警。
气象监测看似简单,但要把数据采集、传输、存储、展示全链路跑通,涉及嵌入式开发、无线通信、前后端交互等多个技术领域。ESP32凭借双核处理器、WiFi/蓝牙双模和丰富的外设接口,成为这类物联网项目的性价比首选。下面从硬件选型到代码实现,完整复盘这个项目的技术细节。
2. 硬件设计与核心器件选型
2.1 主控芯片:为什么选择ESP32?
ESP32-WROOM-32D模块是这个项目的核心大脑,选择理由很实在:
- 双核240MHz处理器:比传统Arduino性能强10倍以上,能同时处理传感器数据采集和网络通信
- 内置WiFi/蓝牙:省去额外的通信模块,实测在-40°C~85°C范围内工作稳定
- 超低功耗设计:深度睡眠模式下电流仅5μA,适合太阳能供电场景
- 丰富外设接口:包含18个12位ADC通道、3个SPI接口等,轻松连接各类传感器
注意:购买时认准乐鑫官方模组(丝印有ESP32字样),市面上有些廉价兼容版存在射频性能缩水问题。
2.2 传感器阵列配置方案
根据气象监测需求,选配了以下传感器组件:
| 传感器类型 | 具体型号 | 测量范围 | 精度 | 接口方式 |
|---|---|---|---|---|
| 温湿度 | SHT30 | -40~125°C / 0~100%RH | ±0.2°C / ±2%RH | I2C |
| 气压 | BMP280 | 300~1100hPa | ±0.12hPa | I2C/SPI |
| 光照 | BH1750 | 1~65535lux | ±1lux | I2C |
| 雨量 | 自制翻斗式 | 0~4mm/min | 0.2mm/次 | GPIO中断 |
其中雨量传感器采用DIY方案:用3D打印的翻斗机构配合霍尔传感器(每翻转一次触发计数),成本不到20元。关键是要在结构上增加防尘网和自清洁斜面,避免长期户外使用导致机械卡顿。
2.3 供电与防护设计
- 电源方案:采用5V/2A防水电源适配器+TP4056充电模块+18650电池组成UPS系统,断电时可维持至少12小时运行
- 防护措施:
- 使用IP65防水盒作为外壳
- 传感器接口涂抹三防漆
- 风速计采用不锈钢轴承
- 防雷设计:在信号线入口处并联TVS二极管(如SMBJ5.0CA)
3. 软件架构与关键技术实现
3.1 系统通信流程图解
plaintext复制[传感器群] --I2C/GPIO--> [ESP32] --WiFi--> [MQTT Broker]
↑ ↓
[本地缓存] [Web服务器]
↓ ↑
[SD卡备份] [手机/PC浏览器]
3.2 嵌入式端关键代码解析
传感器数据采集(Arduino核心代码):
cpp复制#include <Wire.h>
#include <Adafruit_SHT31.h>
#include <Adafruit_BMP280.h>
#include <BH1750.h>
Adafruit_SHT31 sht30 = Adafruit_SHT31();
Adafruit_BMP280 bmp;
BH1750 lightMeter;
void setupSensors() {
if (!sht30.begin(0x44)) { // SHT30默认I2C地址
Serial.println("SHT30未找到!");
}
if (!bmp.begin(0x76)) { // BMP280地址可能为0x76或0x77
Serial.println("BMP280未找到!");
}
lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE);
}
void readSensors() {
float temp = sht30.readTemperature();
float humi = sht30.readHumidity();
float pressure = bmp.readPressure() / 100.0F; // 转换为hPa
float lux = lightMeter.readLightLevel();
// 数据打包为JSON格式
String payload = "{\"temp\":" + String(temp) +
",\"humi\":" + String(humi) +
",\"press\":" + String(pressure) +
",\"lux\":" + String(lux) + "}";
publishToMQTT(payload); // 发送到MQTT服务器
}
WiFi连接优化技巧:
- 实现自动重连机制,在WiFi断开时切换为蓝牙配置模式
- 使用
WiFi.setSleep(false)禁用睡眠模式,避免数据发送延迟 - 对MQTT报文采用QoS1级别,确保数据可靠传输
3.3 云端服务搭建方案
选用EMQX作为MQTT Broker,搭配InfluxDB时序数据库存储数据。关键配置参数:
yaml复制# EMQX配置片段
listeners.tcp.default {
bind = "0.0.0.0:1883"
max_connections = 1024
zone = "default"
}
# InfluxDB配置
[http]
enabled = true
bind-address = ":8086"
数据存储采用"分表+滚动保留"策略:
- 原始数据表:保留30天
- 小时聚合表:保留1年
- 日聚合表:永久保存
3.4 前端可视化实现
基于Vue.js+ECharts构建响应式仪表盘,核心功能包括:
- 实时数据卡片展示
- 历史趋势曲线(支持缩放)
- 异常数据阈值告警
- 移动端自适应布局
关键代码片段(数据订阅部分):
javascript复制const client = mqtt.connect('ws://your-server-ip:8083/mqtt')
client.subscribe('weather/station1', (err) => {
if (!err) {
client.on('message', (topic, message) => {
const data = JSON.parse(message.toString())
updateDashboard(data) // 更新UI组件
})
}
})
4. 部署优化与问题排查
4.1 户外部署注意事项
- 温度补偿:在代码中对传感器读数进行温度补偿(特别是气压传感器)
cpp复制float correctedPressure = bmp.readPressure() / 100.0F + (25.0 - currentTemp) * 0.12; - 防电磁干扰:
- 给ESP32天线周围留出至少15mm净空区
- 在电源输入端增加π型滤波器
- 数据校验:增加CRC校验防止传输错误
cpp复制uint16_t crc = calcCRC32((uint8_t*)payload.c_str(), payload.length());
4.2 常见故障排查指南
| 故障现象 | 可能原因 | 解决方案 |
|---|---|---|
| 数据上传中断 | WiFi信号弱 | 使用WiFi.RSSI()检测信号强度,<-70dBm需加装天线 |
| 温湿度读数异常 | 传感器结露 | 在外壳内放置硅胶干燥剂 |
| 网页加载缓慢 | 浏览器缓存问题 | 在index.html中添加<meta http-equiv="Cache-Control" content="no-cache"> |
| 电池耗电快 | 未启用睡眠模式 | 在无数据传输时调用esp_deep_sleep_start() |
4.3 性能优化记录
通过以下改进将系统功耗降低63%:
- 将数据上报间隔从10秒调整为60秒
- 启用ESP32的轻睡眠模式(
esp_light_sleep_start()) - 用中断唤醒替代轮询检测(如雨量传感器)
- 前端采用WebSocket替代HTTP轮询
5. 项目扩展方向
这套基础框架可以衍生出多种应用场景:
- 农业温室:增加土壤湿度传感器,联动灌溉系统
- 室内环境监测:添加CO2/VOC传感器评估空气质量
- 灾害预警:结合历史数据预测暴雨/高温天气
最近正在尝试将数据接入Home Assistant,实现与其他智能设备的联动控制。比如当光照强度持续低于200lux时,自动开启植物补光灯。