美文网首页Arduino熊爸的学习时间
玩转 ESP32 + Arduino (十六) 通过mqtt协议

玩转 ESP32 + Arduino (十六) 通过mqtt协议

作者: 熊爸天下_56c7 | 来源:发表于2020-09-11 12:13 被阅读0次

    需要用到的库:

    • PubSubClient : 发送和接收MQTT消息
    • ArduinoJson : Json字符串转换库
    • AliyunMqttArduino: 阿里云相关

    先做一件事!!!!!!

    引入"PubSubClient.h"后

    #include "PubSubClient.h"
    

    打开"PubSubClient.h"

    修改如下内容: \color{#FF3030}{否则绝对连不上阿里IOT}

    一. 阿里云MQTT协议

    关于MQTT协议, 请参考文章:https://zhuanlan.zhihu.com/p/89057819

    请参考我之前整理的文档:
    【腾讯文档】MQTT连接阿里云示例(1)https://docs.qq.com/sheet/DWXBaUE9nWmZVaGJX

    如果使用了AliyunMqttArduino, 就不需要关系协议的报文和加密方式了

    二. 主要函数

    首先创建一个mqtt客户端

    WiFiClient espClient;               //创建网络连接客户端
    PubSubClient mqttClient(espClient); //通过网络客户端连接创建mqtt连接客户端
    

    1. 连接阿里云 connectAliyunMQTT

    connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET)
    

    返回值: 连接结果

    2. 判断是否连接了 mqttClient.connected()

    mqttClient.connected()
    

    3. mqtt客户端状态 mqttClient.state()

    mqttClient.state()
    

    返回: 状态码

    状态码 含义
    0 连接成功
    1 不正确的协议版本
    2 无效的登录信息
    3 连接不上服务器
    4 用户名密码错误
    5 未授权
    6~255 备用

    4. 发布主题消息

    mqttClient.publish(主题, 内容)

    mqttClient.publish(ALINK_TOPIC_PROP_POST, jsonBuf)
    

    5. setCallback-- 设置收到命令下发时的回调

    mqttClient.setCallback(callback); //绑定收到set主题时的回调(命令下发回调)
    

    6. 客户端监听消息队列 mqttClient.loop();

    三. 一个点灯的例子(创建产品和设备)

    首先, 创建产品:

    其次, 为产品添加设备

    第三, 查看设备关键信息:

    四. 一个点灯的例子(ESP32连接上述产品和设备)

    #include <Arduino.h>
    #include <ArduinoJson.h>
    #include <aliyun_mqtt.h>
    #include "PubSubClient.h"
    #include "WiFi.h"
    #include "Ticker.h"
    
    #define WIFI_SSID "anny"       //wifi名
    #define WIFI_PASSWD "20141208" //wifi密码
    
    #define PRODUCT_KEY "a17lGhkKwXs"                        //产品ID
    #define DEVICE_NAME "esp32LightHome"                     //设备名
    #define DEVICE_SECRET "14fb77db62910b887dd28c5e449f406e" //设备key
    
    //设备下发命令的set主题
    #define ALINK_TOPIC_PROP_SET "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/service/property/set"
    //设备上传数据的post主题
    #define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"
    //设备post上传数据要用到一个json字符串, 这个是拼接postJson用到的一个字符串
    #define ALINK_METHOD_PROP_POST "thing.event.property.post"
    //这是post上传数据使用的模板
    #define ALINK_BODY_FORMAT "{\"id\":\"%u\",\"version\":\"1.0\",\"method\":\"%s\",\"params\":%s}"
    
    #define LED_B 2 //定义LED灯的引脚
    
    int postMsgId = 0; //记录已经post了多少条
    Ticker tim1;       //这个定时器是为了每5秒上传一次数据
    
    /*------------------------------------------------------------------------------------------*/
    
    WiFiClient espClient;               //创建网络连接客户端
    PubSubClient mqttClient(espClient); //通过网络客户端连接创建mqtt连接客户端
    
    //连接WIFI相关函数
    void setupWifi()
    {
      delay(10);
      Serial.println("连接WIFI");
      WiFi.begin(WIFI_SSID, WIFI_PASSWD);
      while (!WiFi.isConnected())
      {
        Serial.print(".");
        delay(500);
      }
      Serial.println("OK");
      Serial.println("Wifi连接成功");
    }
    
    //重连函数, 如果客户端断线,可以通过此函数重连
    void clientReconnect()
    {
      while (!mqttClient.connected()) //再重连客户端
      {
        Serial.println("reconnect MQTT...");
        if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
        {
          Serial.println("connected");
        }
        else
        {
          Serial.println("failed");
          Serial.println(mqttClient.state());
          Serial.println("try again in 5 sec");
          delay(5000);
        }
      }
    }
    //mqtt发布post消息(上传数据)
    void mqttPublish()
    {
      if (mqttClient.connected())
      {
        //先拼接出json字符串
        char param[32];
        char jsonBuf[128];
        sprintf(param, "{\"LightSwitch\":%d}", digitalRead(LED_B)); //我们把要上传的数据写在param里
        postMsgId += 1;
        sprintf(jsonBuf, ALINK_BODY_FORMAT, postMsgId, ALINK_METHOD_PROP_POST, param);
        //再从mqtt客户端中发布post消息
        if (mqttClient.publish(ALINK_TOPIC_PROP_POST, jsonBuf))
        {
          Serial.print("Post message to cloud: ");
          Serial.println(jsonBuf);
        }
        else
        {
          Serial.println("Publish message to cloud failed!");
        }
      }
    }
    //收到set主题的命令下发时的回调函数,(接收命令)
    void callback(char *topic, byte *payload, unsigned int length)
    {
      if (strstr(topic, ALINK_TOPIC_PROP_SET))
      //如果收到的主题里包含字符串ALINK_TOPIC_PROP_SET(也就是收到/sys/a17lGhkKwXs/esp32LightHome/thing/service/property/set主题)
      {
        Serial.println("收到下发的命令主题:");
        Serial.println(topic);
        Serial.println("下发的内容是:");
        payload[length] = '\0'; //为payload添加一个结束附,防止Serial.println()读过了
        Serial.println((char *)payload);
    
        //接下来是收到的json字符串的解析
        DynamicJsonDocument doc(100);
        DeserializationError error = deserializeJson(doc, payload);
        if (error)
        {
          Serial.println("parse json failed");
          return;
        }
        JsonObject setAlinkMsgObj = doc.as<JsonObject>();
        serializeJsonPretty(setAlinkMsgObj, Serial);
        Serial.println();
    
        //这里是一个点灯小逻辑
        int lightSwitch = setAlinkMsgObj["params"]["LightSwitch"];
        digitalWrite(LED_B, lightSwitch);
        mqttPublish(); //由于将来做应用可能要获取灯的状态,所以在这里发布一下
      }
    }
    
    void setup()
    {
      pinMode(LED_B, OUTPUT);
      Serial.begin(115200);
      setupWifi();
      if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
      {
        Serial.println("MQTT服务器连接成功!");
      };
      mqttClient.setCallback(callback); //绑定收到set主题时的回调(命令下发回调)
      tim1.attach(5, mqttPublish);      //启动每5秒发布一次消息
    }
    
    void loop()
    {
      //检测有没有断线
      if (!WiFi.isConnected()) //先看WIFI是否还在连接
      {
        setupWifi();
      }
      else //如果WIFI连接了,
      {
        if (!mqttClient.connected()) //再看mqtt连接了没
        {
          Serial.println("mqtt disconnected!Try reconnect now...");
          Serial.println(mqttClient.state());
          clientReconnect();
        }
      }
    
      //mqtt客户端监听
      mqttClient.loop();
    }
    

    五. 温湿度数据 GPS数据上传及命令下发(创建产品和设备)

    1. 创建产品

    创建完后,我们发现系统已经自动创建了一些功能定义

    2. 创建设备

    3. 找到设备的关键信息

    4. 如果想给产品增加功能可以...

    找到产品的功能页面, 点击"编辑草稿"

    最后发布新的物模型

    六. 温湿度数据 GPS数据上传及命令下发(创建产品和设备)

    非常简单的修改, 只修改了要发送json里的param
    注意同步修改param和json的内存占用大小

    #include <Arduino.h>
    #include <ArduinoJson.h>
    #include <aliyun_mqtt.h>
    #include "PubSubClient.h"
    #include "WiFi.h"
    #include "Ticker.h"
    
    #define WIFI_SSID "anny"       //wifi名
    #define WIFI_PASSWD "20141208" //wifi密码
    
    #define PRODUCT_KEY "a1AYa96sZMJ"                        //产品ID
    #define DEVICE_NAME "EspTempAndHumi_D001"                //设备名
    #define DEVICE_SECRET "a23249cb179feee41ca2f8f38525113d" //设备key
    
    //设备下发命令的set主题
    #define ALINK_TOPIC_PROP_SET "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/service/property/set"
    //设备上传数据的post主题
    #define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"
    //设备post上传数据要用到一个json字符串, 这个是拼接postJson用到的一个字符串
    #define ALINK_METHOD_PROP_POST "thing.event.property.post"
    //这是post上传数据使用的模板
    #define ALINK_BODY_FORMAT "{\"id\":\"%u\",\"version\":\"1.0\",\"method\":\"%s\",\"params\":%s}"
    
    #define LED_B 2 //定义LED灯的引脚
    
    int postMsgId = 0; //记录已经post了多少条
    Ticker tim1;       //这个定时器是为了每5秒上传一次数据
    
    /*------------------------------------------------------------------------------------------*/
    
    WiFiClient espClient;               //创建网络连接客户端
    PubSubClient mqttClient(espClient); //通过网络客户端连接创建mqtt连接客户端
    
    //连接WIFI相关函数
    void setupWifi()
    {
      delay(10);
      Serial.println("连接WIFI");
      WiFi.begin(WIFI_SSID, WIFI_PASSWD);
      while (!WiFi.isConnected())
      {
        Serial.print(".");
        delay(500);
      }
      Serial.println("OK");
      Serial.println("Wifi连接成功");
    }
    
    //重连函数, 如果客户端断线,可以通过此函数重连
    void clientReconnect()
    {
      while (!mqttClient.connected()) //再重连客户端
      {
        Serial.println("reconnect MQTT...");
        if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
        {
          Serial.println("connected");
        }
        else
        {
          Serial.println("failed");
          Serial.println(mqttClient.state());
          Serial.println("try again in 5 sec");
          delay(5000);
        }
      }
    }
    //mqtt发布post消息(上传数据)
    void mqttPublish()
    {
      if (mqttClient.connected())
      {
        //先拼接出json字符串
        char param[82];
        char jsonBuf[178];
        sprintf(param, "{\"CurrentHumidity\":%.1f,\"CurrentTemperature\":%.1f,\"ESPLight_Pin2\":%d}", 22.2, 35.5, digitalRead(LED_B)); //我们把要上传的数据写在param里
        postMsgId += 1;
        sprintf(jsonBuf, ALINK_BODY_FORMAT, postMsgId, ALINK_METHOD_PROP_POST, param);
        //再从mqtt客户端中发布post消息
        if (mqttClient.publish(ALINK_TOPIC_PROP_POST, jsonBuf))
        {
          Serial.print("Post message to cloud: ");
          Serial.println(jsonBuf);
        }
        else
        {
          Serial.println("Publish message to cloud failed!");
        }
      }
    }
    //收到set主题的命令下发时的回调函数,(接收命令)
    void callback(char *topic, byte *payload, unsigned int length)
    {
      if (strstr(topic, ALINK_TOPIC_PROP_SET))
      //如果收到的主题里包含字符串ALINK_TOPIC_PROP_SET(也就是收到/sys/a17lGhkKwXs/esp32LightHome/thing/service/property/set主题)
      {
        Serial.println("收到下发的命令主题:");
        Serial.println(topic);
        Serial.println("下发的内容是:");
        payload[length] = '\0'; //为payload添加一个结束附,防止Serial.println()读过了
        Serial.println((char *)payload);
    
        //接下来是收到的json字符串的解析
        DynamicJsonDocument doc(100);
        DeserializationError error = deserializeJson(doc, payload);
        if (error)
        {
          Serial.println("parse json failed");
          return;
        }
        JsonObject setAlinkMsgObj = doc.as<JsonObject>();
        serializeJsonPretty(setAlinkMsgObj, Serial);
        Serial.println();
    
        //这里是一个点灯小逻辑
        int lightSwitch = setAlinkMsgObj["params"]["ESPLight_Pin2"];
        digitalWrite(LED_B, lightSwitch);
        mqttPublish(); //由于将来做应用可能要获取灯的状态,所以在这里发布一下
      }
    }
    
    void setup()
    {
      pinMode(LED_B, OUTPUT);
      Serial.begin(115200);
      setupWifi();
      if (connectAliyunMQTT(mqttClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET))
      {
        Serial.println("MQTT服务器连接成功!");
      };
      mqttClient.setCallback(callback); //绑定收到set主题时的回调(命令下发回调)
      tim1.attach(5, mqttPublish);      //启动每5秒发布一次消息
    }
    
    void loop()
    {
      //检测有没有断线
      if (!WiFi.isConnected()) //先看WIFI是否还在连接
      {
        setupWifi();
      }
      else //如果WIFI连接了,
      {
        if (!mqttClient.connected()) //再看mqtt连接了没
        {
          Serial.println("mqtt disconnected!Try reconnect now...");
          Serial.println(mqttClient.state());
          clientReconnect();
        }
      }
    
      //mqtt客户端监听
      mqttClient.loop();
    }
    

    相关文章

      网友评论

        本文标题:玩转 ESP32 + Arduino (十六) 通过mqtt协议

        本文链接:https://www.haomeiwen.com/subject/loeeektx.html