ESP32物联网应用:MQTT控制与状态监测
引言
在物联网时代,远程监测和控制设备已经成为现实生活中常见的需求。本文将介绍如何使用ESP32微控制器配合MQTT协议,实现一个简单而强大的物联网应用:远程状态监测和设备控制。我们将以巴法云平台为例,展示如何通过互联网随时随地监控和控制家中的电器设备。
项目概述
本项目实现了以下功能:
- 状态监测:通过GPIO4引脚读取外部开关状态,并将状态实时上传到云平台
- 远程控制:通过MQTT订阅消息,远程控制GPIO2引脚的高低电平(可连接继电器控制电器)
- 供电输出:GPIO32引脚始终保持高电平,为外接设备供电
这样的设计可以应用于智能家居控制、远程设备监测和自动化控制系统等多种场景。
硬件准备
本项目需要以下硬件:
- ESP32开发板(如NodeMCU-32S、WROOM-32等)
- 面包板和连接线
- 输入开关(连接到GPIO4)
- LED指示灯或继电器(连接到GPIO2)
- 可选:需要3.3V供电的传感器或模块(连接到GPIO32)
软件环境
- Arduino IDE
- PubSubClient库(MQTT客户端)
- WiFi库(已包含在ESP32开发板包中)
核心代码解析
1. 初始化与配置
首先,我们需要引入必要的库并配置WiFi和MQTT连接信息:
cpp
#include <WiFi.h> // ESP32 WiFi库
#include <PubSubClient.h> // MQTT客户端库
// WiFi配置
const char *ssid = "601B"; // WiFi名称
const char *password = "12345678"; // WiFi密码
// MQTT服务器配置
const char* mqtt_server = "bemfa.com"; // MQTT服务器地址
const int mqtt_server_port = 9501; // MQTT服务器端口
#define ID_MQTT "0c876e1774cc3bd27de1d5d1bfc61c90" // 客户端ID
这部分代码设置了我们的WiFi连接参数和MQTT服务器信息。巴法云是一个免费的物联网平台,提供MQTT服务,使我们能够轻松实现设备间的通信。
2. 引脚定义与功能分配
cpp
// 定义引脚
#define SWITCH_PIN 4 // 开关连接的GPIO引脚
#define OUTPUT_PIN_D2 2 // D2引脚,根据MQTT消息控制
#define OUTPUT_PIN_D32 32 // D32引脚,保持高电平
我们使用三个GPIO引脚,各自负责不同的功能:
- GPIO4:读取外部开关状态
- GPIO2:由MQTT消息控制的输出引脚
- GPIO32:始终保持高电平的电源输出引脚
3. MQTT消息处理回调函数
cpp
void callback(char* topic, byte* payload, unsigned int length) {
// 将接收到的字节数组转换为字符串
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
// 根据接收到的消息控制D2引脚
if (String(topic) == mqtt_sub_topic) {
if (message == "ON") {
digitalWrite(OUTPUT_PIN_D2, HIGH);
Serial.println("D2引脚设置为高电平");
} else if (message == "OFF") {
digitalWrite(OUTPUT_PIN_D2, LOW);
Serial.println("D2引脚设置为低电平");
}
}
}
这个回调函数是实现远程控制的核心。当ESP32收到MQTT消息时,会触发此函数。如果消息内容是"ON",则将D2引脚设为高电平;如果是"OFF",则设为低电平。这样,我们就可以通过手机或电脑发送指令来控制连接到D2引脚的设备。
4. 主循环中的状态监测与发布
cpp
void loop() {
// 检查MQTT连接状态
if (!client.connected()) {
reconnect();
}
// 处理MQTT消息
client.loop();
// 读取开关状态
bool val = digitalRead(SWITCH_PIN);
// 限制发布频率
static unsigned long lastPublishTime = 0;
if (millis() - lastPublishTime > 500) {
lastPublishTime = millis();
// 发布开关状态到MQTT
if (val == 0) {
client.publish(mqtt_pub_topic, "OFF");
} else {
client.publish(mqtt_pub_topic, "ON");
}
}
// 确保D32保持高电平
digitalWrite(OUTPUT_PIN_D32, HIGH);
}
在主循环中,我们持续监测连接到GPIO4的开关状态,并将其发布到MQTT主题"sendiot"。为了避免消息发送过于频繁,我们限制了发布频率,每500毫秒最多发布一次。同时,我们确保GPIO32始终保持高电平,为需要稳定电源的外接设备供电。
c
#include <WiFi.h> // ESP32 WiFi库
#include <PubSubClient.h> // MQTT客户端库
// WiFi配置
const char *ssid = "601B"; // WiFi名称
const char *password = "12345678"; // WiFi密码
// MQTT服务器配置
const char* mqtt_server = "bemfa.com"; // MQTT服务器地址
const int mqtt_server_port = 9501; // MQTT服务器端口
#define ID_MQTT "0c876e1774cc3bd27de1d5d1bfc61c90" // 您的客户端ID
// 创建WiFi和MQTT客户端实例
WiFiClient espClient;
PubSubClient client(espClient);
// 定义引脚
#define SWITCH_PIN 4 // 开关连接的GPIO引脚
#define OUTPUT_PIN_D2 2 // D2引脚,根据MQTT消息控制
#define OUTPUT_PIN_D32 32 // D32引脚,保持高电平
// MQTT主题
const char* mqtt_sub_topic = "reciot"; // 订阅的主题
const char* mqtt_pub_topic = "sendiot"; // 发布的主题
// WiFi连接函数
void setup_wifi() {
delay(10);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
// 重连函数
void reconnect() {
// 循环直到重新连接
while (!client.connected()) {
Serial.print("尝试连接MQTT...");
// 尝试连接
if (client.connect(ID_MQTT)) {
Serial.println("connected");
// 连接成功后,订阅主题并检查结果
boolean subResult = client.subscribe(mqtt_sub_topic);
if(subResult) {
Serial.print("订阅主题成功: ");
Serial.println(mqtt_sub_topic);
} else {
Serial.print("订阅主题失败: ");
Serial.println(mqtt_sub_topic);
}
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(1000);
}
}
}
// MQTT消息接收回调函数
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("订阅的信息为 [");
Serial.print(topic);
Serial.print("] ");
// 将接收到的字节数组转换为字符串
String message;
for (int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message);
Serial.println("*** 收到MQTT消息!***");
// 根据接收到的消息控制D2引脚
if (String(topic) == mqtt_sub_topic) {
if (message == "ON") {
digitalWrite(OUTPUT_PIN_D2, HIGH);
Serial.println("D2引脚设置为高电平");
} else if (message == "OFF") {
digitalWrite(OUTPUT_PIN_D2, LOW);
Serial.println("D2引脚设置为低电平");
}
}
}
void setup() {
// 初始化串口
Serial.begin(115200);
// 设置引脚模式
pinMode(SWITCH_PIN, INPUT);
pinMode(OUTPUT_PIN_D2, OUTPUT);
pinMode(OUTPUT_PIN_D32, OUTPUT);
// 将D32设置为高电平
digitalWrite(OUTPUT_PIN_D32, HIGH);
Serial.println("D32引脚已设置为高电平");
// 初始化D2为低电平
digitalWrite(OUTPUT_PIN_D2, LOW);
Serial.println("D2引脚初始化为低电平");
// 连接WiFi
setup_wifi();
// 设置MQTT服务器和回调
client.setServer(mqtt_server, mqtt_server_port);
client.setCallback(callback);
// 连接MQTT服务器并订阅主题
reconnect();
// 设置完成提示
Serial.println("设置完成,ESP32已准备就绪");
}
void loop() {
// 检查MQTT连接状态
if (!client.connected()) {
Serial.println("MQTT连接断开,正在重连...");
reconnect();
}
// 处理MQTT消息
client.loop();
// 读取开关状态
bool val = digitalRead(SWITCH_PIN);
// 限制发布频率
static unsigned long lastPublishTime = 0;
if (millis() - lastPublishTime > 500) {
lastPublishTime = millis();
// 发布开关状态到MQTT
if (val == 0) {
Serial.println("发布: OFF");
client.publish(mqtt_pub_topic, "OFF");
} else {
Serial.println("发布: ON");
client.publish(mqtt_pub_topic, "ON");
}
}
// 循环延时
delay(100);
// 确保D32保持高电平
digitalWrite(OUTPUT_PIN_D32, HIGH);
}
实际应用场景
这个项目可以应用于多种物联网场景:
-
智能照明:
- GPIO4连接墙壁开关
- GPIO2连接继电器控制灯泡
- 手机APP可远程控制灯光,同时看到墙壁开关的状态
-
智能插座:
- GPIO2控制继电器,切换电源
- 手机可随时查看和控制插座的开关状态
-
安防系统:
- GPIO4连接门磁传感器
- 当门打开时,传感器状态变化会通过MQTT发送到云端
- 手机APP可接收实时通知
-
农业监测:
- GPIO4连接土壤湿度传感器
- GPIO2控制灌溉系统
- 可设置自动或手动控制灌溉
优化与扩展
本项目还可以进行以下扩展:
-
添加多个传感器:
- 利用ESP32的其他GPIO引脚,连接温度、湿度、光线等传感器
- 增加数据发布的维度
-
增加本地控制界面:
- 添加OLED显示屏展示当前状态
- 添加按钮实现本地控制
-
实现条件触发:
- 添加逻辑判断,根据传感器数据自动控制输出
- 例如,当检测到湿度过低时自动开启灌溉系统