ESP32物联网应用:MQTT控制及状态监测详解

ESP32物联网应用:MQTT控制与状态监测

引言

在物联网时代,远程监测和控制设备已经成为现实生活中常见的需求。本文将介绍如何使用ESP32微控制器配合MQTT协议,实现一个简单而强大的物联网应用:远程状态监测和设备控制。我们将以巴法云平台为例,展示如何通过互联网随时随地监控和控制家中的电器设备。

项目概述

本项目实现了以下功能:

  1. 状态监测:通过GPIO4引脚读取外部开关状态,并将状态实时上传到云平台
  2. 远程控制:通过MQTT订阅消息,远程控制GPIO2引脚的高低电平(可连接继电器控制电器)
  3. 供电输出:GPIO32引脚始终保持高电平,为外接设备供电

这样的设计可以应用于智能家居控制、远程设备监测和自动化控制系统等多种场景。

硬件准备

本项目需要以下硬件:

  • ESP32开发板(如NodeMCU-32S、WROOM-32等)
  • 面包板和连接线
  • 输入开关(连接到GPIO4)
  • LED指示灯或继电器(连接到GPIO2)
  • 可选:需要3.3V供电的传感器或模块(连接到GPIO32)
  • 软件环境

  • Arduino IDE
  • PubSubClient库(MQTT客户端)
  • WiFi库(已包含在ESP32开发板包中)
  • 核心代码解析

    1. 初始化与配置

    首先,我们需要引入必要的库并配置WiFi和MQTT连接信息:

    #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. 引脚定义与功能分配

    // 定义引脚
    #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消息处理回调函数

    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. 主循环中的状态监测与发布

    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始终保持高电平,为需要稳定电源的外接设备供电。

    #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);
    }
    

    实际应用场景

    这个项目可以应用于多种物联网场景:

    1. 智能照明

    2. GPIO4连接墙壁开关
    3. GPIO2连接继电器控制灯泡
    4. 手机APP可远程控制灯光,同时看到墙壁开关的状态
    5. 智能插座

    6. GPIO2控制继电器,切换电源
    7. 手机可随时查看和控制插座的开关状态
    8. 安防系统

    9. GPIO4连接门磁传感器
    10. 当门打开时,传感器状态变化会通过MQTT发送到云端
    11. 手机APP可接收实时通知
    12. 农业监测

    13. GPIO4连接土壤湿度传感器
    14. GPIO2控制灌溉系统
    15. 可设置自动或手动控制灌溉

    优化与扩展

    本项目还可以进行以下扩展:

    1. 添加多个传感器

    2. 利用ESP32的其他GPIO引脚,连接温度、湿度、光线等传感器
    3. 增加数据发布的维度
    4. 增加本地控制界面

    5. 添加OLED显示屏展示当前状态
    6. 添加按钮实现本地控制
    7. 实现条件触发

    8. 添加逻辑判断,根据传感器数据自动控制输出
    9. 例如,当检测到湿度过低时自动开启灌溉系统

    作者:hmywillstronger

    物联沃分享整理
    物联沃-IOTWORD物联网 » ESP32物联网应用:MQTT控制及状态监测详解

    发表回复