ESP32 实战 · 2026-06-07 配图
AI Know 自有配图 · 嵌入式 · IoT · 硬件编程

ESP32 智能环境监测站实战教程:温湿度采集 + OLED 显示 + MQTT 上云

一、项目背景

在物联网(IoT)应用场景中,环境监测是最常见的需求之一。无论是家庭温室、智能家居、机房温控,还是工业仓储的温湿度报警系统,都离不开一套稳定、低成本、易扩展的数据采集终端。

本项目基于 ESP32 主控,搭配 DHT22 温湿度传感器 和 0.96 寸 SSD1306 OLED 显示屏,实现本地实时显示 + 远程 MQTT 上传。ESP32 凭借双核 240MHz 主频、内置 Wi-Fi/BLE、丰富 GPIO 以及超低功耗模式,成为此类监测节点的理想选择。

通过本教程,你将学会:

整个项目硬件成本约 30 元,代码可编译可直接烧录,零基础也能跑通。

---

二、硬件清单

序号 元件 型号 数量 说明
1 主控板 ESP32-DevKitC(30P) 1 推荐 NodeMCU-32S 兼容款
2 温湿度传感器 DHT22(AM2302) 1 比 DHT11 精度更高,量程 -40~80℃
3 OLED 屏 0.96 寸 SSD1306(I2C,128×64) 1 蓝色或白色任选
4 面包板 830 孔 1 免焊接,方便调试
5 杜邦线 公-公 / 公-母 若干 建议备 20 根
6 电阻 10KΩ 1 DHT22 上拉用
7 电源 USB 5V/1A 1 或 3.7V 锂电池
小贴士:如果手头只有 DHT11,把代码中 `DHT22` 改成 `DHT11` 即可,其余代码完全兼容。

---

三、接线图说明

ESP32 DevKitC 引脚分布丰富,我们使用以下默认分配,方便后期外接更多传感器。

3.1 引脚分配表

模块 引脚 ESP32 GPIO 说明
DHT22 DATA 信号线 GPIO 4 需 10KΩ 上拉到 3.3V
DHT22 VCC 电源 3V3 切忌接 5V,会烧坏传感器
DHT22 GND 地 GND —
OLED SDA 数据线 GPIO 21 I2C 默认 SDA
OLED SCL 时钟线 GPIO 22 I2C 默认 SCL
OLED VCC 电源 3V3 或 5V SSD1306 通常兼容两者
OLED GND 地 GND —

3.2 实物连接描述

想象你手中的面包板横向长、纵向短:

  1. 左侧电源轨:红色线接入 ESP32 的 3V3 引脚,蓝色线接入 GND。整条电源轨即 3.3V 公共端。
  2. DHT22 放置:将 DHT22 插在面包板中间区域,三个引脚朝下。从左到右依次为 `VCC`、`DATA`、`NC`、`GND`(DHT22 通常四脚,第二个脚为数据,第三个悬空)。
  3. 上拉电阻:10KΩ 电阻一端接 DHT22 的 DATA 脚,另一端接到 3.3V 电源轨。电阻紧贴传感器焊接或直接插在同列插孔。
  4. 杜邦线连接:用公-母线从 DHT22 的 DATA 脚接到 ESP32 的 GPIO 4。
  5. OLED 屏:四个排针依次插入面包板,GND 接蓝线,VCC 接红线,SDA 接 GPIO 21,SCL 接 GPIO 22。
  6. 整体布局:ESP32 放在面包板上方,传感器与屏幕分列左右,避免相互遮挡。
接线完成后,检查是否有短路。建议先用万用表通断档确认 3V3 与 GND 之间电阻不为零。

---

四、完整代码(Arduino IDE)

4.1 准备工作

  1. Arduino IDE 安装 ESP32 核心(开发板管理器搜索 `esp32`,安装 2.x 版本)。
  2. 库管理器依次安装:

- `DHT sensor library`(Adafruit)

- `Adafruit Unified Sensor`

- `Adafruit SSD1306`

- `PubSubClient`(Nick O'Leary)

  1. 修改代码顶部的 `WIFI_SSID`、`WIFI_PASS`、`MQTT_SERVER` 三个宏定义。

4.2 完整源码


/* ===========================================================
 *  ESP32 智能环境监测站
 *  功能:DHT22 温湿度采集 + OLED 显示 + MQTT 上传
 *  平台:Arduino-ESP32 2.x
 *  作者:AI Know
 * =========================================================== */

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <Adafruit_SSD1306.h>

/* -------------------- 用户配置区 -------------------- */
#define WIFI_SSID     "你的WiFi名称"
#define WIFI_PASS     "你的WiFi密码"

#define MQTT_SERVER   "broker.emqx.io"   // 公共测试 Broker
#define MQTT_PORT     1883
#define MQTT_USER     ""                 // 若 Broker 无认证可留空
#define MQTT_PASS     ""
#define MQTT_CLIENT_ID  "esp32_env_01"
#define MQTT_TOPIC_T  "home/room/temp"
#define MQTT_TOPIC_H  "home/room/humi"

/* -------------------- 硬件引脚 -------------------- */
#define DHT_PIN   4
#define DHT_TYPE  DHT22

#define OLED_SDA  21
#define OLED_SCL  22
#define SCREEN_W  128
#define SCREEN_H  64

/* -------------------- 对象初始化 -------------------- */
DHT dht(DHT_PIN, DHT_TYPE);
Adafruit_SSD1306 display(SCREEN_W, SCREEN_H, &Wire;, -1);
WiFiClient espClient;
PubSubClient mqtt(espClient);

/* -------------------- 函数声明 -------------------- */
void wifiInit();
void mqttInit();

/* ============================================================ */
void setup() {
  Serial.begin(115200);
  delay(200);
  Serial.println("\n[BOOT] ESP32 环境监测站启动");

  // I2C 与 OLED 初始化
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("[ERR] OLED 初始化失败,请检查 I2C 地址(0x3C/0x3D)");
    while (true);
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("Booting...");
  display.display();

  // DHT 初始化
  dht.begin();

  // 网络
  wifiInit();
  mqttInit();
}

/* -------------------- 主循环 -------------------- */
void loop() {
  // 1. 保持 MQTT 连接
  if (!mqtt.connected()) mqttInit();
  mqtt.loop();

  // 2. 读取传感器(每 2 秒一次)
  static unsigned long lastRead = 0;
  if (millis() - lastRead > 2000) {
    lastRead = millis();

    float t = dht.readTemperature();
    float h = dht.readHumidity();

    if (isnan(t) || isnan(h)) {
      Serial.println("[WARN] DHT 读取失败");
      return;
    }

    Serial.printf("[DATA] T=%.1f C  H=%.1f %%\n", t, h);
    publishData(t, h);
  }
}

/* -------------------- WiFi 连接 -------------------- */
void wifiInit() {
  WiFi.mode(WIFI_STA);
  Serial.printf("[WIFI] 连接中: %s", WIFI_SSID);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.print("WiFi:");
  display.println(WIFI_SSID);
  display.display();

  int tryCnt = 0;
  while (WiFi.status() != WL_CONNECTED && tryCnt < 30) {
    delay(500);
    Serial.print('.');
    tryCnt++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.printf("\n[WIFI] 已连接,IP: %s\n", WiFi.localIP().toString().c_str());
  } else {
    Serial.println("\n[WIFI] 连接失败,将继续尝试");
  }
}

/* -------------------- MQTT 连接 -------------------- */
void mqttInit() {
  mqtt.setKeepAlive(60);

  while (!mqtt.connected()) {
    Serial.print("[MQTT] 连接 Broker...");
    if (mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS)) {
      Serial.println(" OK");
    } else {
      Serial.printf(" 失败 rc=%d,5 秒后重试\n", mqtt.state());
      delay(5000);
    }
  }
}

/* -------------------- OLED 显示 -------------------- */
void oledShow(float t, float h, bool online) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("=== Env Station ===");

  display.setTextSize(2);
  display.setCursor(0, 16);
  if (t < -50) display.print("T: --.-");
  else         display.printf("T:%2.1fC", t);

  display.setCursor(0, 40);
  if (h < -50) display.print("H: --.-");
  else         display.printf("H:%2.1f%%", h);

  display.setTextSize(1);
  display.setCursor(90, 0);
  display.print(online ? "MQTT:OK" : "MQTT:X");
  display.display();
}

/* -------------------- 发布数据 -------------------- */
void publishData(float t, float h) {
  char buf[16];
  snprintf(buf, sizeof(buf), "%.1f", t);
  mqtt.publish(MQTT_TOPIC_T, buf, true);   // retained

  snprintf(buf, sizeof(buf), "%.1f", h);
}

4.3 代码要点说明

---

五、调试步骤

  1. 串口监视器:打开 Arduino IDE 串口监视器,波特率 115200,观察 `[WIFI]`、`[MQTT]` 日志。
  2. MQTT 客户端验证:用电脑或手机安装 MQTTX / MQTTool,连接同一 Broker,订阅主题 `home/room/#`,即可看到实时数据。
  3. OLED 无显示:

- 确认 I2C 地址(I2C Scanner 扫描 0x3C 或 0x3D)。

- 检查 SDA/SCL 是否接反。

- 测量 3.3V 电源是否正常。

  1. DHT22 一直返回 NaN:

- 确认上拉电阻已焊接。

- 数据线过长(>20cm)建议加屏蔽或换更短接线。

- 连续读取至少间隔 2 秒,否则传感器会响应失败。

  1. MQTT 连接失败 `rc=-2`:网络不通,检查 Wi-Fi 与外网访问;`rc=5` 通常是 Broker 鉴权问题。
  2. JSON 化扩展:若想一次性发布温湿度,可改用 `ArduinoJson` 库组装 JSON 字符串,例如:

   StaticJsonDocument<64> doc;
   doc["t"] = t; doc["h"] = h;
   serializeJson(doc, buf);
   mqtt.publish("home/room/state", buf);

---

六、进阶方向

完成基础版本后,可以从以下方向继续扩展:

6.1 接入 Home Assistant / Node-RED

将 MQTT 数据通过 Home Assistant 的 MQTT 集成接入,可自动生成温湿度卡片,并配合 `automation.yaml` 设置阈值告警(如温度 > 35℃ 推送手机通知)。

6.2 增加低功耗模式

将 DHT22 替换为 SHT30(I2C 接口,精度更高),并利用 ESP32 的 `deepSleep`:


esp_sleep_enable_timer_wakeup(60 * 1000000); // 60 秒唤醒
esp_deep_sleep_start();

配合电池供电,节点续航可达数月。

6.3 多传感器融合

在 I2C 总线上并联:

  • BMP280:气压与海拔
  • CCS811:eCO₂ 与 TVOC(空气质量)
  • BH1750:光照强度

通过唯一 I2C 地址区分设备,OLED 切换页面显示,构建一个真正的小型环境监测站。

---

七、结语

本项目从硬件到代码完整呈现了一个最小可运行的 ESP32 物联网节点。核心要点是 Wi-Fi 通信 + I2C 总线 + 单总线传感器 的协同工作。掌握这一范式后,你可以将 `DHT22` 替换成任何传感器,将 `MQTT` 替换成 `HTTP` 或 `Modbus`,快速复用到工业、农业、智能家居等各种场景。

下一步,建议你尝试用 ESP-IDF 框架 重写该工程,理解 FreeRTOS 双核任务调度与底层驱动模型,这会让你对嵌入式开发有更深的认知。祝玩得开心!