💡 划重点

  • 主控:ESP32,自带 Wi-Fi,可跑本地 Web Server。
  • 感知:BME280 读取温度、湿度、气压,I2C 接 GPIO21/22。
  • 执行:GPIO25 输出 PWM 驱动小风扇,温湿度超阈值自动调速。
  • 提示:GPIO2 接 LED 心跳,方便判断程序是否卡死。
  • 成本:物料控制在 70 元内,适合阳台、花园、小型温室。
  • 结果:手机连同一 Wi-Fi,打开 ESP32 的 IP 即可看实时数据。

需求洞察

目标用户是阳台种植者、花园爱好者、小型温室玩家。他们通常种番茄、草莓、多肉、香草或兰花,面积不大,但环境变化很敏感。真实痛点有三个:第一,阳台和花园的温湿度与室内差异大,闷热高湿容易导致霉病、烂根,但人不可能一直守着。第二,很多成品气象站只能看数据,不能联动通风,发现时已经晚了。第三,商用物联网设备依赖云平台,断网就失效,还存在隐私和长期费用问题。

为什么值得做?ESP32 加 BME280 的方案成本低、资料多、可本地运行。网页直接由 ESP32 提供,不依赖外部服务器。风扇按阈值自动启停,既能降温除湿,又能减少人工干预。对新手来说,这是一个完整的“感知—控制—执行—上报”闭环项目,学到 I2C、PWM、Web Server 和 Wi-Fi 联网,后续还能加土壤湿度、光照、水泵。

方案设计

感知层:BME280 通过 I2C 与 ESP32 通信,周期读取温度、湿度、气压。气压可辅助判断天气趋势,例如短时下降可能预示降雨。

控制层:ESP32 做主循环,每 2 秒采样一次。设定两级阈值:温度大于 28℃或湿度大于 70% 时风扇低速;温度大于 30℃或湿度大于 75% 时风扇高速。阈值可写在代码顶部,方便调整。

执行层:GPIO25 输出 PWM,经过小功率 MOSFET 驱动 5V 风扇。LED 接 GPIO2,每秒翻转一次作为心跳。风扇启停时网页同步显示状态。

日志/联网层:ESP32 连接家庭 Wi-Fi,启动本地 Web Server。根路径返回自动刷新的 HTML 页面,/data 返回 JSON。最近 60 条采样可保存在内存环形缓冲区,网页显示趋势简表。串口同时输出日志,便于调试。

物料清单

物料 数量 用途 估算单价
ESP32 开发板 1 主控与 Wi-Fi 25 元
BME280 模块 1 温湿度气压 15 元
5V 小风扇 1 通风降温 10 元
MOSFET 模块 1 风扇 PWM 驱动 3 元
LED 与电阻 1 套 心跳指示 1 元
杜邦线、面包板 1 套 接线 6 元
USB 电源与线 1 套 供电 10 元
合计 70 元

接线与结构

结构上,ESP32 和模块放入小防水盒,BME280 开百叶通风口,避免阳光直射和雨水。风扇对准盒内或植物区。电源线从盒底进出,留滴水弯。

核心代码


#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_BME280.h>

#define SDA_PIN 21
#define SCL_PIN 22
#define FAN_PWM 25
#define LED_PIN 2

const char* ssid = "你的WiFi";
const char* password = "你的密码";

Adafruit_BME280 bme;
WebServer server(80);

float temp = 0, hum = 0, pres = 0;
int fanDuty = 0;
unsigned long lastSample = 0;

void handleRoot() {
  String html = "<meta http-equiv='refresh' content='3'>";
  html += "<h2>小型气象站</h2>";
  html += "<p>温度: " + String(temp) + " C</p>";
  html += "<p>湿度: " + String(hum) + " %</p>";
  html += "<p>气压: " + String(pres) + " hPa</p>";
  html += "<p>风扇 PWM: " + String(fanDuty) + "</p>";
  server.send(200, "text/html; charset=utf-8", html);
}

void handleData() {
  String json = "{\"t\":" + String(temp) + ",\"h\":" + String(hum) +
                ",\"p\":" + String(pres) + ",\"fan\":" + String(fanDuty) + "}";
  server.send(200, "application/json", json);
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  ledcSetup(0, 25000, 8);
  ledcAttachPin(FAN_PWM, 0);
  Wire.begin(SDA_PIN, SCL_PIN);
  if (!bme.begin(0x76)) {
    while (1) { digitalWrite(LED_PIN, !digitalRead(LED_PIN)); delay(200); }
  }
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  server.on("/", handleRoot);
  server.on("/data", handleData);
  server.begin();
}

void loop() {
  server.handleClient();
  if (millis() - lastSample > 2000) {
    lastSample = millis();
    temp = bme.readTemperature();
    hum = bme.readHumidity();
    pres = bme.readPressure() / 100.0F;
    if (temp > 30 || hum > 75) fanDuty = 180;
    else if (temp > 28 || hum > 70) fanDuty = 120;
    else fanDuty = 0;
    ledcWrite(0, fanDuty);
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    Serial.printf("T=%.1f H=%.1f P=%.1f FAN=%d\n", temp, hum, pres, fanDuty);
  }
}

调试步骤

  1. 先只接 BME280,烧录 I2C 扫描程序,确认地址为 0x76 或 0x77。
  2. 打开串口监视器,检查温度、湿度、气压是否合理,用手捂住传感器看湿度变化。
  3. 接入 LED,确认每秒闪烁,说明主循环未阻塞。
  4. 接入风扇和 MOSFET,先用固定 PWM 测试,再改回阈值控制。
  5. 连接 Wi-Fi,记录串口打印的 IP,手机浏览器访问根路径。
  6. 访问 /data 检查 JSON 格式,便于后续接入其他页面。
  7. 长时间运行 2 小时,观察是否掉线、发热、风扇启停是否频繁。

设计图

小型气象站上报网页结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报