使用MQTT.fx和ESP32连接Onenet平台
一、为什么要使用MQTT.fx连接平台呢?
当我们的下位机或者其他客户端想要连接MQTT服务器之前,我们可以用一MQTT.fx来模拟一个客户端进行简单的连接测试,防止一开始的通信出现问题。
二、Onenet平台建立设备
1、新建设备
进入开发者中心。
点击产品开发,创建产品。
随便选择一个种类。
选择设备接入。
选择WiFi连接,MQTT协议。
点击产品开发。
设置物模型。
添加自定义功能点。
物模型就是我们上传和订阅的数据属性。
进入设备管理,添加设备。
这个设备对应着实际的设备,例如ESP32就是一个设备,智能家居系统中的台灯也是一个设备。
点击设备详情。
我们要记录下着三个数值,一会在连接Onenet的时候,鉴权需要。
在产品开发中点击下一步,如果没有看见的话,需要将下拉条往下拉一些。
继续无脑下一步。
点击发布。
2、token解释
当我们连接Onenet平台的时候,需要进行鉴权,就是判断是否能够合法连接服务器。
鉴权需要三个数值,{产品id,设备id,token}。
token就是就是密码,由key、设备id、时间戳等由特定的算法生成。
需要下载token生成工具,在这个网址下。
文档中心https://open.iot.10086.cn/doc/aiot/fuse/detail/1487
①是产品id,②是设备id,③是时间戳,④是设备秘钥
其中①、②、④对应下图。
时间戳去这个网站上进行转换,转换时间需要大于当前时间。时间戳(Unix timestamp)转换工具 - 在线工具https://tool.lu/timestamp/
3、OneJson格式
在Onenet平台上,需要以这种标准的JSON格式传递消息。
三、MQTT.fx连接Onenet平台
进入MQTT.fx,点击齿轮,进行设置。
填写信息。
password就是token。
点击apply后,叉掉这个页面。
出现绿灯表示连接成功。
我们向该设备指定的topic,发送一个数据进行测试。
对我们之前建立的属性,进行修改,数值变成1。
需要严格注意是否缺花括号,是数值型还是字符串型
最后我们的属性变成1,说明发送成功了。
下面我们sub一下,测试一下平台是否能向客户端发送数据(模拟下位机发送数据)。
进入设备调试,向接受方发送数字2。
我们的MQTT.fx也监听到了该数据,表明可以接受。
既可以发送又可以接受,说明设备和平台间的通路打通。
我原本是想让MQTT.fx作为esp32的上位机来测试整个数据通路,想法很好,但是在Onenet平台可能实现不了。
首先如果将MQTT.fx和esp32设为两个不同的设备,但是Onenet默认只有同设备间可以pub和sub,也就是说不同设备间不能通信,可能可以更改这个规则,但是我还没了解。
其次如果MQTT.fx和esp32作为一个设备,由于OneNet的鉴权三元组,导致他们不能用一套信息来连接Onenet,也就是登录不上。
所以,没办法将MQTT.fx作为上位机,来和esp32进行通信。
四、ESP32连接Onenet平台
esp32使用Arduino开发语言。
下面的代码实现每3s,使Onenet平台的number属性加1,以及当收到平台发来的命令时,通过串口打印。
#include <WiFi.h>
#include <PubSubClient.h>
#include <Ticker.h>
#include <ArduinoJson.h>void connectWifi();
void subscribeTopic();
void pubMQTTmsg();
void receiveCallback(char *topic, byte *payload, unsigned int length);
void connectMQTTserver();
void tickerCount();
String createJson();// 设置wifi接入信息(请根据您的WiFi信息进行修改)
const char *ssid = "zzzz";
const char *password = "12345678";
const char *mqttServer = "mqtts.heclouds.com";
// 如以上MQTT服务器无法正常连接,请前往以下页面寻找解决方案
// http://www.taichi-maker.com/public-mqtt-broker/WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
Ticker ticker;
int count = 0; // Ticker计数用变量// ****************************************************
// 注意!以下需要用户根据然也物联平台信息进行修改!否则无法工作!
// ****************************************************
const char *mqttUserName = "E4T74j642D"; // 服务端连接用户名(需要修改)
const char *mqttPassword = "version=2018-10-31&res=products%2FE4T74j642D%2Fdevices%2FESP32_01&et=1914842691&method=md5&sign=fihNaW0MO3cr4SblF%2FjUKQ%3D%3D"; // 服务端连接密码(需要修改)
const char *clientId = "ESP32_01"; // 客户端id (需要修改)
const char *subTopic = "$sys/E4T74j642D/ESP32_01/thing/property/set"; // 订阅主题(需要修改)
const char *pubTopic = "$sys/E4T74j642D/ESP32_01/thing/property/post"; // 订阅主题(需要修改)
const char *willTopic = "/broadcast/h9sj0dFIZzO/test1"; // 遗嘱主题名称(需要修改)
// ****************************************************// 遗嘱相关信息
const char *willMsg = "esp8266 offline"; // 遗嘱主题信息
const int willQos = 0; // 遗嘱QoS
const int willRetain = false; // 遗嘱保留const int subQoS = 1; // 客户端订阅主题时使用的QoS级别(截止2020-10-07,仅支持QoS = 1,不支持QoS = 2)
const bool cleanSession = false; // 清除会话(如QoS>0必须要设为false)bool ledStatus = HIGH;void setup()
{Serial.begin(115200); // 启动串口通讯ticker.attach(1, tickerCount); // Ticker定时对象// 设置ESP8266工作模式为无线终端模式WiFi.mode(WIFI_STA);// 连接WiFiconnectWifi();Serial.println(ESP.getChipModel());// 设置MQTT服务器和端口号mqttClient.setServer(mqttServer, 1883);mqttClient.setCallback(receiveCallback);// 连接MQTT服务器connectMQTTserver();
}void loop()
{// 如果开发板未能成功连接服务器,则尝试连接服务器if (!mqttClient.connected()){connectMQTTserver();Serial.println("正在重连");}if (count >= 3){pubMQTTmsg(); // 每隔3秒钟发布一次信息count = 0;}// 定期发送心跳 检查服务器回复的心跳//如果有订阅主题的消息到达 触发回调函数mqttClient.loop();delay(10);
}
// 计时器
void tickerCount()
{count++;
}
// 连接MQTT服务器并订阅信息
void connectMQTTserver()
{if (mqttClient.connect(clientId, mqttUserName,mqttPassword)){Serial.print("MQTT Server Connected. ClientId: ");Serial.println(clientId);Serial.print("MQTT Server: ");Serial.println(mqttServer);subscribeTopic(); // 订阅指定主题}else{Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(5000);}
}// 收到信息后的回调函数
void receiveCallback(char *topic, byte *payload, unsigned int length)
{Serial.print("Message Received [");Serial.print(topic);Serial.print("] ");for (int i = 0; i < length; i++){Serial.print((char)payload[i]);}Serial.println("");Serial.print("Message Length(Bytes) ");Serial.println(length);if ((char)payload[0] == '1'){ // 如果收到的信息以“1”为开始ledStatus = LOW;}else{ledStatus = HIGH;}pubMQTTmsg();
}// 订阅指定主题
void subscribeTopic()
{// 通过串口监视器输出是否成功订阅主题以及订阅的主题名称// 请注意subscribe函数第二个参数数字为QoS级别。这里为QoS = 1if (mqttClient.subscribe(subTopic, subQoS)){Serial.print("Subscribed Topic: ");Serial.println(subTopic);}else{Serial.print("Subscribe Fail...");}
}// 发布信息
void pubMQTTmsg()
{char *pubMessage1 = "{\"id\":\"123\",\"version\":\"1.0\",\"params\":{\"number\":{\"value\":2}}}";String jsonStr = createJson(); // 保持String对象存活const char *pubMessage = jsonStr.c_str();// 实现ESP8266向主题发布信息if (mqttClient.publish(pubTopic, pubMessage)){Serial.println("Publish Topic:");Serial.println(pubTopic);Serial.println(pubMessage);}else{Serial.println("Message Publish Failed.");}
}// 根据时间动态创建Json
// 1~100
String createJson()
{DynamicJsonDocument doc(1024);doc["id"] = "123";doc["version"] = "1.0";JsonObject params = doc.createNestedObject("params");JsonObject number = params.createNestedObject("number");static int num = 0;if (count >= 3){num++;num %= 100;}number["value"] = num;String result;serializeJson(doc, result);return result;
}// ESP8266连接wifi
void connectWifi()
{WiFi.begin(ssid, password);// 等待WiFi连接,成功连接后输出成功信息while (WiFi.status() != WL_CONNECTED){delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");Serial.println("");
}
发送数据。
接受数据。