🌡️ 基于Zigbee的智能环境监控系统 - ESP8266/ESP32-C6双节点实战 📡
分享作者:XIUYUAN
作者昵称:XIUYUAN
评测品牌:iCEasy
评测型号:CC2530无线串口模块/DL-20
发布时间:2025-10-16 09:20:00
1
视频链接
【基于Zigbee的智能环境监控系统 - ESP8266/ESP32-C6双节点实战-哔哩哔哩】 https://b23.tv/bI8ZXcP
前言
项目实现了一个完整的无线传感器网络系统,使用Zigbee技术实现双节点通信: 节点一(发送端):ESP8266 NodeMCU + DHT11温湿度传感器 + MQ-2气体传感器 节点二(接收端):ESP32-C6 + OLED显示屏 通过Zigbee DL-20模块实现远程数据传输,实时监控环境温度、湿度和气体浓度,并在OLED屏幕上可视化显示。
开源口碑分享内容

📋 项目概述

本项目实现了一个完整的无线传感器网络系统,使用Zigbee技术实现双节点通信:

       节点一(发送端):ESP8266 NodeMCU + DHT11温湿度传感器 + MQ-2气体传感器

       节点二(接收端):ESP32-C6 + OLED显示屏

通过Zigbee DL-20模块实现远程数据传输,实时监控环境温度、湿度和气体浓度,并在OLED屏幕上可视化显示。


🛠️ 硬件清单

 节点一(数据采集端)

| 组件 | 型号 | 数量 | 作用 |
|------|------|------|------|
| 微控制器 | ESP8266 NodeMCU | 1 | 主控芯片 |
| 温湿度传感器 | DHT11 | 1 | 采集环境温湿度 |
| 气体传感器 | MQ-2 | 1 | 检测可燃气体 |
| Zigbee模块 | DL-20 | 1 | 无线数据发送 |
| 面包板 + 杜邦线 | - | 若干 | 电路连接 |

节点二(数据接收显示端)

| 组件 | 型号 | 数量 | 作用 |
|------|------|------|------|
| 微控制器 | ESP32-C6 | 1 | 主控芯片 |
| OLED显示屏 | SSD1306 (128x64) | 1 | 数据可视化 |
| Zigbee模块 | DL-20 | 1 | 无线数据接收 |
| 面包板 + 杜邦线 | - | 若干 | 电路连接 |

🔌 接线说明

节点一(ESP8266)接线图

DHT11温湿度传感器:
  VCC  →  ESP8266 3.3V
  GND  →  ESP8266 GND
  DATA →  ESP8266 D4 (GPIO2)

MQ-2气体传感器:
  VCC  →  ESP8266 5V
  GND  →  ESP8266 GND
  AO   →  ESP8266 A0 (模拟输出)
  DO   →  ESP8266 D5 (GPIO14, 数字输出)

Zigbee DL-20模块:
  VCC  →  ESP8266 3.3V
  GND  →  ESP8266 GND
  RX   →  ESP8266 D7 (GPIO13)
  TX   →  ESP8266 D6 (GPIO12)

节点二(ESP32-C6)接线图

OLED显示屏 (I2C):
  VCC  →  ESP32-C6 3.3V
  GND  →  ESP32-C6 GND
  SDA  →  ESP32-C6 GPIO6
  SCL  →  ESP32-C6 GPIO7

Zigbee DL-20模块:
  VCC  →  ESP32-C6 3.3V
  GND  →  ESP32-C6 GND
  RX   →  ESP32-C6 GPIO5
  TX   →  ESP32-C6 GPIO4

💻 软件实现

节点一代码(ESP8266 - 数据采集发送)

// Node1_ESP8266_Sensor_Transmitter.ino
#include <DHT.h>
#include <SoftwareSerial.h>

// DHT11配置
#define DHTPIN D4        // DHT11数据引脚 (GPIO2)
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

// MQ-2配置
#define MQ2_AO A0        // MQ-2模拟输出
#define MQ2_DO D5        // MQ-2数字输出 (GPIO14)

// Zigbee模块配置 (DL-20)
#define ZIGBEE_RX D6     // GPIO12 - 连接到Zigbee的TX
#define ZIGBEE_TX D7     // GPIO13 - 连接到Zigbee的RX
SoftwareSerial zigbeeSerial(ZIGBEE_RX, ZIGBEE_TX);

// 发送计数器
unsigned long sendCount = 0;

void setup() {
  // 主串口用于调试
  Serial.begin(115200);
  delay(1000);
  Serial.println("\n=== Node1 ESP8266 发送节点启动 ===");
  
  // 初始化Zigbee串口
  zigbeeSerial.begin(9600);
  Serial.println("Zigbee串口初始化: 9600波特率");
  Serial.printf("  RX引脚: D6 (GPIO12)\n");
  Serial.printf("  TX引脚: D7 (GPIO13)\n");
  
  // 初始化DHT11
  dht.begin();
  Serial.println("DHT11初始化完成");
  
  // 初始化MQ-2
  pinMode(MQ2_DO, INPUT);
  Serial.println("MQ-2初始化完成");
  
  Serial.println("\n开始发送数据...\n");
  delay(2000);
}

void loop() {
  // 读取DHT11传感器
  float temperature = dht.readTemperature();
  float humidity = dht.readHumidity();
  
  // 读取MQ-2传感器
  int gasValue = analogRead(MQ2_AO);
  int gasDigital = digitalRead(MQ2_DO);
  
  // 检查DHT11读取是否成功
  if (isnan(temperature) || isnan(humidity)) {
    Serial.println("⚠ DHT11读取失败,使用默认值");
    temperature = 25.0;
    humidity = 60.0;
  }
  
  // 构建数据包(使用简单的分隔符格式)
  String dataPacket = "T:" + String(temperature, 1) + 
                      ",H:" + String(humidity, 1) + 
                      ",G:" + String(gasValue) + 
                      ",A:" + String(gasDigital) + "\n";
  
  // 通过Zigbee发送数据
  zigbeeSerial.print(dataPacket);
  
  // 串口监视器显示
  sendCount++;
  Serial.printf("[%lu] 发送: %s", sendCount, dataPacket.c_str());
  Serial.printf("     温度=%.1f°C, 湿度=%.1f%%, 气体=%d, 报警=%d\n", 
                temperature, humidity, gasValue, gasDigital);
  
  // LED闪烁指示发送(ESP8266内置LED在GPIO2,即D4)
  digitalWrite(LED_BUILTIN, LOW);   // 点亮(低电平有效)
  delay(100);
  digitalWrite(LED_BUILTIN, HIGH);  // 熄灭
  
  delay(2000);  // 每2秒发送一次
}

节点二代码(ESP32-C6 - 数据接收显示)

// Node2_ESP32C6_Receiver_Display.ino
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// OLED配置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// ESP32-C6 I2C引脚
#define I2C_SDA 6
#define I2C_SCL 7

// Zigbee模块配置 (使用Serial1)
#define ZIGBEE_RX 4     // ESP32-C6的GPIO4 - 连接Zigbee的TX
#define ZIGBEE_TX 5     // ESP32-C6的GPIO5 - 连接Zigbee的RX

// 数据变量
float temperature = 0.0;
float humidity = 0.0;
int gasValue = 0;
int gasAlert = 0;

// 状态指示
unsigned long lastReceiveTime = 0;
unsigned long receiveCount = 0;
bool dataReceived = false;

void setup() {
  Serial.begin(115200);
  delay(1000);
  Serial.println("\n=== Node2 ESP32-C6 接收节点启动 ===");
  
  // 初始化I2C
  Wire.begin(I2C_SDA, I2C_SCL);
  Serial.printf("I2C初始化: SDA=GPIO%d, SCL=GPIO%d\n", I2C_SDA, I2C_SCL);
  
  // 初始化Zigbee串口 (ESP32-C6使用Serial1)
  Serial1.begin(9600, SERIAL_8N1, ZIGBEE_RX, ZIGBEE_TX);
  Serial.printf("Zigbee串口初始化: 9600波特率\n");
  Serial.printf("  RX引脚: GPIO%d\n", ZIGBEE_RX);
  Serial.printf("  TX引脚: GPIO%d\n", ZIGBEE_TX);
  
  // 初始化OLED
  Serial.println("初始化OLED...");
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("0x3C失败,尝试0x3D...");
    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
      Serial.println("!!! OLED初始化失败 !!!");
      Serial.println("请检查接线:");
      Serial.println("  SDA -> GPIO6");
      Serial.println("  SCL -> GPIO7");
      Serial.println("  VCC -> 3.3V");
      Serial.println("  GND -> GND");
    } else {
      Serial.println("✓ OLED初始化成功 (地址0x3D)");
    }
  } else {
    Serial.println("✓ OLED初始化成功 (地址0x3C)");
  }
  
  // 显示初始画面
  displayWelcome();
  
  Serial.println("\n等待接收数据...\n");
  delay(2000);
}

void loop() {
  // 检查是否有数据接收
  if (Serial1.available()) {
    String receivedData = Serial1.readStringUntil('\n');
    receivedData.trim();  // 去除首尾空格和换行符
    
    if (receivedData.length() > 0) {
      receiveCount++;
      lastReceiveTime = millis();
      
      Serial.printf("[%lu] 接收到数据: %s\n", receiveCount, receivedData.c_str());
      
      // 解析数据
      if (parseData(receivedData)) {
        dataReceived = true;
        // 更新显示
        updateDisplay();
        
        Serial.printf("     解析成功: T=%.1f°C, H=%.1f%%, G=%d, A=%d\n", 
                      temperature, humidity, gasValue, gasAlert);
      } else {
        Serial.println("     ⚠ 数据解析失败");
      }
    }
  }
  
  // 如果超过10秒没收到数据,显示等待状态
  if (dataReceived && (millis() - lastReceiveTime > 10000)) {
    Serial.println("⚠ 超过10秒未收到数据,显示等待状态...");
    displayWaiting();
    dataReceived = false;
  }
  
  delay(50);  // 短暂延迟避免CPU占用过高
}

// 解析数据包
bool parseData(String data) {
  // 预期格式: T:25.5,H:60.0,G:123,A:0
  
  int tIndex = data.indexOf("T:");
  int hIndex = data.indexOf("H:");
  int gIndex = data.indexOf("G:");
  int aIndex = data.indexOf("A:");
  
  // 检查是否所有字段都存在
  if (tIndex == -1 || hIndex == -1 || gIndex == -1 || aIndex == -1) {
    return false;
  }
  
  // 解析温度
  int tEnd = data.indexOf(",", tIndex);
  if (tEnd == -1) return false;
  temperature = data.substring(tIndex + 2, tEnd).toFloat();
  
  // 解析湿度
  int hEnd = data.indexOf(",", hIndex);
  if (hEnd == -1) return false;
  humidity = data.substring(hIndex + 2, hEnd).toFloat();
  
  // 解析气体值
  int gEnd = data.indexOf(",", gIndex);
  if (gEnd == -1) return false;
  gasValue = data.substring(gIndex + 2, gEnd).toInt();
  
  // 解析警报状态
  gasAlert = data.substring(aIndex + 2).toInt();
  
  return true;
}

// 显示欢迎界面
void displayWelcome() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 20);
  display.println("iCEasy");
  display.setTextSize(1);
  display.setCursor(20, 45);
  display.println("Waiting...");
  display.display();
}

// 显示等待状态
void displayWaiting() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.println("iCEasy");
  display.drawLine(0, 10, SCREEN_WIDTH, 10, SSD1306_WHITE);
  
  display.setTextSize(1);
  display.setCursor(10, 28);
  display.println("No Signal...");
  display.display();
}

// 更新显示内容
void updateDisplay() {
  display.clearDisplay();
  
  // 第一行:iCEasy + 接收计数
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("iCEasy");
  display.setCursor(80, 0);
  display.print("#");
  display.print(receiveCount);
  
  // 分隔线
  display.drawLine(0, 10, SCREEN_WIDTH, 10, SSD1306_WHITE);
  
  // 温度
  display.setTextSize(1);
  display.setCursor(0, 15);
  display.print("Temp: ");
  display.print(temperature, 1);
  display.print(" C");
  
  // 湿度
  display.setCursor(0, 28);
  display.print("Humi: ");
  display.print(humidity, 1);
  display.print(" %");
  
  // 气体传感器值
  display.setCursor(0, 41);
  display.print("Gas:  ");
  display.print(gasValue);
  
  // 气体警报状态
  display.setCursor(0, 54);
  if (gasAlert == 1) {
    // 反色显示警报
    display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);
    display.print(" ALERT! ");
    display.setTextColor(SSD1306_WHITE);
  } else {
    display.print("Normal");
  }
  
  display.display();
}

📊 数据通信协议

       系统使用简单的文本格式进行数据传输,便于调试和扩展:

数据包格式:

T:<温度>,H:<湿度>,G:<气体值>,A:<警报状态>\n

示例数据包:

T:24.1,H:68.0,G:458,A:1

字段说明:

       `T`: 温度(摄氏度,保留1位小数)

       `H`: 湿度(百分比,保留1位小数)

       `G`: 气体传感器模拟值(0-1023)

       `A`: 警报状态(0=正常,1=报警)


🔧 开发环境配置

Arduino IDE设置

1. 安装ESP8266开发板支持

       文件 → 首选项 → 附加开发板管理器网址

       添加:`http://arduino.esp8266.com/stable/package_esp8266com_index.json`

       工具 → 开发板 → 开发板管理器 → 搜索"ESP8266"并安装

2. 安装ESP32开发板支持

       文件 → 首选项 → 附加开发板管理器网址

       添加:`https://espressif.github.io/arduino-esp32/package_esp32_index.json`

       工具 → 开发板 → 开发板管理器 → 搜索"ESP32"并安装

3. 安装必需库

       DHT sensor library (by Adafruit)

       Adafruit Unified Sensor

       Adafruit GFX Library

       Adafruit SSD1306

开发板选择

       节点一:选择 "NodeMCU 1.0 (ESP-12E Module)"

       节点二:选择 "ESP32C6 Dev Module"


🐛 调试技巧

1. I2C设备扫描(检测OLED地址)

#include <Wire.h>

void setup() {
  Serial.begin(115200);
  Wire.begin(6, 7);  // ESP32-C6: SDA=6, SCL=7
  Serial.println("I2C扫描器");
}

void loop() {
  for(byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.print("发现设备: 0x");
      Serial.println(addr, HEX);
    }
  }
  delay(5000);
}

2. 串口调试输出

两个节点都配置了详细的串口调试信息(115200波特率):

       节点一:显示发送的数据包和传感器读数

       节点二:显示接收到的数据和解析结果

3. 常见问题排查

| 问题 | 可能原因 | 解决方案 |
|------|----------|----------|
| OLED不显示 | I2C地址错误 | 运行I2C扫描器确认地址(通常为0x3C或0x3D) |
| DHT11读取失败 | 接线错误/供电不足 | 检查DATA引脚连接,确保使用3.3V供电 |
| Zigbee无数据 | 模块未配对 | 检查两个模块的PAN ID和信道设置 |
| 编译错误 | 开发板选择错误 | 节点一选ESP8266,节点二选ESP32C6 |

✨ 功能特性

实时监控:每2秒采集一次传感器数据  

无线传*:Zigbee 2.4GHz远程通信  

可视化显示:OLED屏幕实时显示监测数据  

气体警报:MQ-2检测到可燃气体时反色显示警告  

接收计数:显示已接收的数据包数量  

连接状态监控:超过10秒无数据显示"No Signal"  


🚀 扩展方向

1. 数据存储:添加SD卡模块,记录历史数据

2. 云端上传:通过WiFi将数据上传到物联网平台(如ThingSpeak、阿里云IoT)

3. 多节点网络:扩展为多个传感器节点的Mesh网络

4. 移动应用:开发手机APP远程监控

5. 报警功能:添加蜂鸣器或LED指示灯

6. 太阳能供电:实现户外长期部署

7. 其他传感器:集成PM2.5、CO2等更多传感器


📸 运行效果

节点一/二串口输出:

=== Node1 ESP8266 发送节点启动 ===
Zigbee串口初始化: 9600波特率
  RX引脚: D6 (GPIO12)
  TX引脚: D7 (GPIO13)
DHT11初始化完成
MQ-2初始化完成

开始发送数据...

[1] 发送: T:24.1,H:68.0,G:458,A:1
     温度=24.1°C, 湿度=68.0%, 气体=458, 报警=1
[2] 发送: T:24.1,H:68.0,G:459,A:1
     温度=24.1°C, 湿度=68.0%, 气体=459, 报警=1

节点二OLED显示:

┌─────────────────┐
│ iCEasy      #15 │
├─────────────────┤
│ Temp: 24.1 C    │
│ Humi: 68.0 %    │
│ Gas:  458       │
│ ▓ ALERT! ▓      │
└─────────────────┘

节点一/二串口监视器


📝 总结

   本项目展示了如何使用Zigbee技术构建一个完整的无线传感器网络系统。通过ESP8266和ESP32-C6的组合,实现了低成本、高可靠性的环境监控解决方案。

项目亮点:

       🎯 双MCU协作,各展所长

       📡 Zigbee无线通信,稳定可靠

       📊 实时数据可视化

       🔔 智能气体报警

       🛠️ 代码结构清晰,易于扩展


希望这个项目能为您的物联网开发之路提供灵感和参考!


📚 参考资料

[ESP8266 Arduino Core文档](https://arduino-esp8266.readthedocs.io/)

[ESP32-C6技术规格书](https://www.espressif.com/en/products/socs/esp32-c6)

[DHT11传感器数据手册](https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf)

[SSD1306 OLED显示屏规格](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf)

[Zigbee协议简介](https://zigbeealliance.org/)


💡 如有问题或建议,欢迎交流讨论!

🌟 如果这个项目对您有帮助,欢迎点赞和分享!

全部评论
暂无评论
0/144