小型气象站上报网页
💡 划重点
- 芯片选择:ESP32,自带Wi-Fi与蓝牙,主频240MHz,性价比高,内置Web Server能力避免了外接网络模块的开销。
- 环境感知:BME280单芯片集成温度、湿度、气压三要素,I2C总线仅占用两个GPIO口,为系统留出充裕扩展空间。
- 主动通风:PWM控制风扇按湿度阈值自动启停,避免传感器因长期潮湿环境导致读数漂移,延长器件寿命。
- 本地网页上报:通过ESP32的Web Server在局域网内生成实时数据看板,减轻传感器待机功耗。
- 整机成本控制在70元内,低于市售同功能成品的三分之一。
需求洞察
目标用户是阳台种植爱好者、小花园主以及户外设备DIY玩家。这类用户购买市售气象站常遇到误报率高、显示模块光看数据却无法接入自己的控制逻辑、电池续航短等问题。阳台和花园的微气候与城市气象站播报的宏观数据经常严重不符,而主流成品气象站无法按局部环境控制通风散热设备。本项目80元以下即可搭建一个“感知-控制-上报”闭环系统,可自定义逻辑且数据完全私有,痛点在于市售产品贵又不开放。
方案设计
- 感知层:BME280通过I2C(SDA=21, SCL=22)采集温湿度与大气压,数据刷新周期设为5秒,兼顾实时性与总线负载。
- 控制层:ESP32开发板作为主控,内部任务调度分为三块:传感器轮询任务、PWM控制任务、Web服务任务。三者通过FreeRTOS消息队列解耦。
- 执行层:GPIO25输出PWM信号驱动风扇MOS管栅极。当湿度超过设定阈值(如65%RH)则风扇按占空比线性提速。同 时GPIO2驱动LED心跳闪烁,代码正常运行时期亮50ms、灭950ms。
- 日志与联网层:ESP32连接本地路由器后启动mDNS服务,用户可通过
http://weather.local访问。Web页面包含实时读数、24小时迷你趋势(基于内存滚动存储)、风扇状态符号。
物料清单
| 物料 | 数量 | 用途 | 估算单价(元) |
|---|---|---|---|
| ESP32 DevKitC 开发板 | 1 | 主控核心 | 28 |
| BME280 模块(I2C) | 1 | 温湿度气压测量 | 18 |
| 5V 涡轮风扇 | 1 | 通风执行器 | 7 |
| NPN三极管(S8050) | 1 | 风扇功率开关 | 0.5 |
| 1N5819 肖特基二极管 | 1 | 风扇反向电动势保护 | 0.5 |
| 面包板 + 跳线 | 若干 | 快速原型布线 | 8 |
| 5V/1A USB电源 | 1 | 供电(手机旧电源可替代) | 0(闲置) |
| 合计 | 62 |
接线与结构
- BME280 VIN接3.3V,GND接GND,SDA接GPIO21,SCL接GPIO22。
- 风扇电源正极接5V,负极接三极管集电极;三极管发射极接地,基极串1kΩ电阻接GPIO25。二极管反向并联在风扇电机两端。
- LED心跳灯正极经220Ω电阻接GPIO2,负极接地。
- 结构采用防水接线盒打孔安装,传感器放于百叶窗式通风罩内,风扇侧装在罩体一侧形成主动抽风道。
核心代码
以下是PlatformIO(Arduino框架)编写的可运行骨架,采用非阻塞式任务轮询:
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <WebServer.h>
#include <WiFi.h>
#define SEALEVELPRESSURE_HPA (1013.25)
#define PWM_PIN 25
#define LED_PIN 2
#define HUMID_THRESHOLD 65.0
Adafruit_BME280 bme;
WebServer server(80);
const char* ssid = "你的WiFi名";
const char* password = "你的密码";
bool fanState = false;
float tempC, humi, presPa;
void handleRoot() {
String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>";
html += "<meta http-equiv='refresh' content='5'></head><body>";
html += "<h2>阳台小型气象站</h2>";
html += "<p>温度: " + String(tempC) + " ℃</p>";
html += "<p>湿度: " + String(humi) + " %</p>";
html += "<p>气压: " + String(presPa / 100.0) + " hPa</p>";
html += "<p>风扇: " + String(fanState ? "ON" : "OFF") + "</p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void updateActuator() {
int duty = 0;
if (humi > HUMID_THRESHOLD) {
float ratio = (humi - HUMID_THRESHOLD) / 20.0;
duty = (int)(ratio * 255);
duty = constrain(duty, 40, 255); // 最小占空比防止风扇堵转
fanState = true;
} else {
fanState = false;
}
analogWrite(PWM_PIN, duty);
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
pinMode(LED_PIN, OUTPUT);
pinMode(PWM_PIN, OUTPUT);
if (!bme.begin(0x76)) {
Serial.println("BME280 not found!");
while (1) delay(10);
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(200);
}
server.on("/", handleRoot);
server.begin();
}
void loop() {
static uint32_t lastSensor = 0;
server.handleClient();
if (millis() - lastSensor > 2000) {
lastSensor = millis();
tempC = bme.readTemperature();
humi = bme.readHumidity();
presPa = bme.readPressure();
updateActuator();
digitalWrite(LED_PIN, HIGH);
delay(30);
digitalWrite(LED_PIN, LOW);
}
}
调试步骤
- 模块验证:先单独运行I2C扫描程序,确认BME280设备地址为0x76(部分模块为0x77)并出现在总线上。
- Wi-Fi 连接排障:注意ESP32开发板只能连接2.4GHz频段。若长时间连不上,用手机热点做A/B测试确认。
- PWM驱动波形验证:用万用表测量GPIO25在湿度阈值上下变化时是否有电平跳变,听风扇是否均匀转动、有无啸叫。
- 网页访问测试:打开串口监视器查看分配的IP地址,交叉验证
weather.local是否能正确解析。若不行则清除浏览器DNS缓存。 - 长时间稳定性观察:持续运行12小时,检查温度读数较手持温湿度计的偏差,以及Web Server是否有内存泄漏导致拒绝服务的现象。
设计图
小型气象站上报网页结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报