玩转 ESP32 + Arduino (十三) 通过MQTTs协议上传数据至OneNet云平台

这里我们使用了一个库: PubSubClient

OneNet服务器地址:
开发者文档: https://open.iot.10086.cn/doc/mqtt/


token生成工具: https://open.iot.10086.cn/doc/mqtt/book/manual/auth/tool.html

一. OneNet上创建MQTTs协议产品

1. 添加

创建产品

创建新设备

2. 关键信息

(1). 产品ID, 用户ID,和登录key

(2). 设备名称, ID和 设备key

三. 计算token

OneNet MQTTS用携带token的方式进行鉴权

1. 常见的三种安全方案:

(1). 把信息和key固化在程序中,访问时计算token

访问者(可以为应用或者设备)固化访问密钥于软件中,在需要进行服务访问时,通过密钥计算临时token,通过临时token进行服务访问认证

(2). 访问者访问管理者获取临时token

访问者首先通过访问管理者获取临时访问token,访问管理者可根据需要自定义该token的访问有效期(即过期时间),访问者获取该token后方才能访问OneNET

(3). 固化token

访问管理者直接将密钥授权给访问者(例如,直接为设备烧写key),访问者通过密钥生成token进行访问

2. token计算方法

计算方法: https://open.iot.10086.cn/doc/mqtt/book/manual/auth/token.html
token生成工具: https://open.iot.10086.cn/doc/mqtt/book/manual/auth/tool.html

根据软件界面, 我们应提供 res et key method

(1). res

格式为: products/产品ID/devices/设备名

以我们刚刚创建的产品和设备为例:

products/370098/devices/esp_device001
(2). et

时间戳 (也叫:格林威治时间戳,或者UNIX时间戳)
计算地址:
https://tool.lu/timestamp/

我直接计算到了 2099年

4092512761
(3). key

设备的key (不是产品的access key)

vCrX2mFsOGNoOIyJrNJLJSpydnpNPeeijLiDQa3FKo8=

3. 软件生成token

version=2018-10-31&res=products%2F370098%2Fdevices%2Fesp_device001&et=4092512761&method=md5&sign=MUV%2BKFLzv81a4Bw6BDrChQ%3D%3D

四. 设置主题

相关文档: https://open.iot.10086.cn/doc/mqtt/book/device-develop/topics/introduce.html

MQTTS物联网套件中设备相关服务(存储、命令等)的面向设备的接口,均以 topic 的形式提供,设备可以通过 publish 消息到系统 topic 调用服务接口,也可以订阅系统 topic 用于接收服务消息通知,服务提供的系统 topic 的集合形成了 topic 簇

MQTTS物联网套件目前包含:数据点topic簇、命令topic簇、子设备topic簇、设备影子topic簇,如下图所示:

数据点topic簇

设备可以通过数据点 topic 簇上传数据存储并即时获取数据存储结果

簇中topic 以 $sys/{pid}/{device-name}/dp 开头
通过publish上传数据时,payload需要满足平台约定数据格式
支持一次上报多条数据,支持设备自带时间戳上报
即时通知数据处理结果(需订阅)

1. 数据点 topic 簇

MQTT物联网套件支持用户以数据流-数据点模型(模型详情)将数据上传至平台并进行存储,设备可以通过数据点 topic 簇调用数据点存储服务存储数据,可以通过订阅系统 topic 获取数据处理结果通知,如下图所示:

对于本文中的例子, 应如下订阅:

$sys/370098/esp_device001/dp/post/json  //上传数据应订阅此主题 (发布消息)
$sys/370098/esp_device001/dp/post/json/accepted  //系统通知订阅该主题者,数据上传成功(订阅消息)
$sys/370098/esp_device001/dp/post/json/rejected  //系统通知订阅该主题者, 数据上传失败(订阅消息)

上传的JSON数据必须采用以下规则

{
    "id": 123,        
    "dp": {             
        "temperatrue": [{     
            "v": 30,       
            "t": 1552289676
        }],
        "power": [{     
            "v": 4.5,        
            "t": 1552289676 
        }],
        "status": [{
                "v": {
                    "color": "blue"
                },
                "t": 1552289677
            },
            {
                "v": {
                    "color": "red"
                },
                "t": 1552289678
            }
        ]
    }
}

2. 设备命令 topic 簇

MQTT物联网套件支持应用通过API直接向设备发送单播命令,设备可以通过设备命令 topic 簇获取消息并进行消息应答

设备命令交互流程见下图:

topic中{cmdid}为变量,为每条命令的唯一id,可通过通配符的方式进行订阅,比如:$sys/{pid}/{device-name}/cmd/request/+,或者$sys/{pid}/{device-name}/cmd/#

对于本位中 的例子,应如下订阅:

$sys/370098/esp_device001/cmd/request/# //订阅此主题可以接收系统下发的命令 (订阅消息)
$sys/370098/esp_device001/cmd/response/#  //订阅此主题可以应答命令,向系统回复自己收到了命令(发布消息)

$sys/370098/esp_device001/cmd/request/{cmdid}/accepted  //订阅此主题系统会告诉你"你的应答成功" (订阅消息)
$sys/370098/esp_device001/cmd/response/{cmdid}/rejected  //订阅此主题系统会告诉你"你的应答失败"(订阅消息)

五. 连接示例

本连接实现了设备模拟上传温湿度数据, 设备接收平台下发的命令
(但没有给平台回复收到命令的消息)

#include 
#include "WiFi.h"
#include "PubSubClient.h"
#include "Ticker.h"

const char *ssid = "anleng";               //wifi名
const char *password = "al77776666";       //wifi密码
const char *mqtt_server = "183.230.40.96"; //onenet 的 IP地址
const int port = 1883;                     //端口号

#define mqtt_devid "esp_device001" //设备ID
#define mqtt_pubid "370098"        //产品ID
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F370098%2Fdevices%2Fesp_device001&et=4092512761&method=md5&sign=MUV%2BKFLzv81a4Bw6BDrChQ%3D%3D" //鉴权信息

WiFiClient espClient;           //创建一个WIFI连接客户端
PubSubClient client(espClient); // 创建一个PubSub客户端, 传入创建的WIFI客户端

char msgJson[75]; //发送信息缓冲区
//信息模板
char dataTemplate[] = "{\"id\":123,\"dp\":{\"temp\":[{\"v\":%.2f}],\"humi\":[{\"v\":%.2f}]}}";
Ticker tim1; //定时器,用来循环上传数据

//连接WIFI相关函数
void setupWifi()
{
  delay(10);
  Serial.println("连接WIFI");
  WiFi.begin(ssid, password);
  while (!WiFi.isConnected())
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("OK");
  Serial.println("Wifi连接成功");
}

//收到主题下发的回调, 注意这个回调要实现三个形参 1:topic 主题, 2: payload: 传递过来的信息 3: length: 长度
void callback(char *topic, byte *payload, unsigned int length)
{
  Serial.println("message rev:");
  Serial.println(topic);
  for (size_t i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

//向主题发送模拟的温湿度数据
void sendTempAndHumi()
{
  if (client.connected())
  {
    snprintf(msgJson, 75, dataTemplate, 22.5, 35.6); //将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson
    Serial.print("public the data:");
    Serial.println(msgJson);
    client.publish("$sys/370098/esp_device001/dp/post/json", (uint8_t *)msgJson, strlen(msgJson));
    //发送数据到主题
  }
}

//重连函数, 如果客户端断线,可以通过此函数重连
void clientReconnect()
{
  while (!client.connected()) //再重连客户端
  {
    Serial.println("reconnect MQTT...");
    if (client.connect(mqtt_devid, mqtt_pubid, mqtt_password))
    {
      Serial.println("connected");
      client.subscribe("$sys/370098/esp_device001/cmd/request/#"); //订阅命令下发主题
    }
    else
    {
      Serial.println("failed");
      Serial.println(client.state());
      Serial.println("try again in 5 sec");
      delay(5000);
    }
  }
}

void setup()
{
  Serial.begin(115200);                                  //初始化串口
  delay(3000);                                           //这个延时是为了让我打开串口助手
  setupWifi();                                           //调用函数连接WIFI
  client.setServer(mqtt_server, port);                   //设置客户端连接的服务器,连接Onenet服务器, 使用6002端口
  client.connect(mqtt_devid, mqtt_pubid, mqtt_password); //客户端连接到指定的产品的指定设备.同时输入鉴权信息
  if (client.connected())
  {
    Serial.println("OneNet is connected!");//判断以下是不是连好了.
  }
  client.setCallback(callback);                                //设置好客户端收到信息是的回调
  client.subscribe("$sys/370098/esp_device001/cmd/request/#"); //订阅命令下发主题
  tim1.attach(10, sendTempAndHumi);                            //定时每10秒调用一次发送数据函数sendTempAndHumi
}

void loop()
{
  if (!WiFi.isConnected()) //先看WIFI是否还在连接
  {
    setupWifi();
  }
  if (!client.connected()) //如果客户端没连接ONENET, 重新连接
  {
    clientReconnect();
    delay(100);
  }
  client.loop(); //客户端循环检测
}

老样子,写个小点灯.

你可能感兴趣的:(玩转 ESP32 + Arduino (十三) 通过MQTTs协议上传数据至OneNet云平台)