📋 项目概述
本项目实现了一个完整的无线传感器网络系统,使用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/)
💡 如有问题或建议,欢迎交流讨论!
🌟 如果这个项目对您有帮助,欢迎点赞和分享!

