登录腾讯云, 搜索"云产品"下的"物联网通信"产品, 或直接访问:
https://console.cloud.tencent.com/iotcloud
对于认证方式, 指定了设备通过何种方式和云端进行双向认证. 默认的证书方式相对于密钥认证安全性高一点, 但是问题在于证书方式需要在嵌入式设备端存储证书同时实现证书的相关处理, 对设备的RAM和ROM要求较高, 相对而言, 密钥认证的方式资源占用量就小点, 由于我们主要支持的设备都是小型嵌入式设备, 因此选用密钥认证。
数据格式指的是设备和云端进行数据交互时候使用的格式, json格式为文本字符串, 可读性高, 并且便于解析, 对于功能复杂的设备交互而已比较理想, 但是对于小型设备或是定制设备, 数据单一, 或是有自定义的格式(二进制或是文本), 这种时候, 用自定义的数据格式, 一方面节约流量, 另一方面比较灵活.。
添加完设备后, 会告知设备对应的密钥. 该密钥将会用于之后设备与平台通信时的认证:
为了实现设备间的通信, 我们还需要创建第二个设备, 操作同上, 将其命名为"dev2":
平台默认配置了三类的Topic, 用于执行发布和订阅.,这里之所以是三类而不是三个, 是因为Topic里使用了变量.。
WDRRDCF1TE
实际上是productID;${deviceName}
为平台设置的变量, 即设备名;control
和data
以及event
为Topic名字.;所以, 在我们创建了2个设备dev1和dev2的情况下, 在 BearPiTest 产品下, 即存在6个Topic, 分别为:
这里默认的Topic已经足够我们使用, 不需要额外添加Topic和权限了。
规则引擎本身不属于MQTT协议的范畴, 但是平台侧出于安全角度考虑添加了规则引擎, 实现了Topic之间的转发操作, 我们需要合理的设置规则引擎才能实现多个设备之间的数据收发, 由于理解起来比较复杂, 我们这里简要讲解下为什么需要规则引擎, 规则引擎的作用, 如何设置规则引擎.
为什么需要规则引擎
在上节的Topic中, 我们知道, 在平台侧, 对于不同的Topic, 规定了不同的权限, 例如, 对于25KCIUIR1G/dev1/event
这个Topic, 只具有发布权限, 而对于25KCIUIR1G/dev1/control
这个Topic, 只具有订阅权限. 对于设备dev1, 很自然的, 会朝25KCIUIR1G/dev1/event
这个Topic发送数据, 并且订阅25KCIUIR1G/dev1/control
这个Topic的消息. 但是这里就会涉及到, event的数据最后到哪去, control的数据从哪里来的问题.
在本文的例子中, 我们希望dev1和dev2发生交互, 即相互收发消息. 由于MQTT是基于Topic的发布订阅机制, 因此, dev1想要获得dev2的数据, 直觉上, 需要订阅dev2发布消息的那个Topic. 假定dev2朝25KCIUIR1G/dev2/event
Topic上发送数据, 那么dev1想要获得dev2发布的消息, 最直接的办法是订阅同样的Topic, 即25KCIUIR1G/dev2/event
, 但是这里存在几个问题, 首先, event Topic只具有发布权限, 没有订阅权限, 其次, 在平台侧, 规定了, 不允许跨设备发布或是订阅Topic, 也就是说, 对于dev1, 只能看到或只允许访问25KCIUIR1G/dev1
这个Topic以及其下属的Topic, 不能访问25KCIUIR1G/dev2
及其下属Topic.
平台侧添加不允许跨设备访问Topic的规则虽然不直观, 但却是合理的. 如果不添加这条限制, 那么一个设备可以不加限制的订阅同一个产品下所有其他设备的Topic, 获取其上报的消息, 这存在潜在的安全漏洞.
规则引擎的作用
因为不允许直接跨设备访问Topic, 所以需要依靠"规则引擎"来手动添加规则, 将指定的Topic消息转发到另一个Topic上, 实现不同设备之间的通信.
上图介绍了规则引擎的主要作用"republish", 即将一个Topic下的消息republish到另一个Topic下. 从图中我们可以看到, 规则引擎将WDRRDCF1TE/dev2/event
的消息republish到了WDRRDCF1TE/dev1/control
下. 将WDRRDCF1TE/dev1/event
的消息republish到了WDRRDCF1TE/dev2/control
下。
这样, 对于dev1而言, 只需要订阅WDRRDCF1TE/dev1/control
就可以接收来自25KCIUIR1G/dev2/event
的消息了,dev2同理。
在物联网通信界面选择"规则引擎"–“新建规则”, 随意指定一个规则名称, 我们这里不妨设置为"1to2":
这里, 我们看到规则的详细设置信息, 主要包括"筛选数据"和"行为操作"。
“筛选数据"针对指定Topic接收到的消息内容进行进一步的筛选, 比如匹配消息中的字段来决定是否执行之后的设置的"行为操作”.,而"行为操作"则是指定对通过匹配的消息进行何种操作, 主要的操作有"数据转发到另一个Topic(Republish)", "转发到第三方服务(Forward)"以及转发到腾讯云各个对应组件中。
上图是设置好的规则, 这里, 我们将"筛选数据"部分的筛选字段设置为*
, 筛选的Topic为WDRRDCF1T/dev1/event
, 条件设置为空, 即不筛选数据, 全部匹配,然后, 执行的操作是将数据转发到WDRRDCF1T/dev2/control
, 设置完这条规则, 就实现了dev2通过订阅control就能收到dev1发送到event的数据。
关于"筛选数据"的设定:
由于我们在新建产品, 设置数据格式的时候选择了自定义数据格式, 在自定义数据格式的情况下, 当前平台将其当做二进制流来处理, 也就无法通过匹配字段进行数据筛选.
如果在进行产品的时候, 使用数据格式是json, 那么此处就可以根据json中的字段进行SQL的匹配和筛选.
同理, 我们再设置新的一个规则"2to1", 实现WDRRDCF1T/dev2/event
到WDRRDCF1T/dev1/control
的转发:
规则引擎都设置好后,记得点启用按钮,这样, 在平台侧dev1到dev2的双向数据通路就打通了:
在平台侧都设置好后, 我们在之后的测试过程或是通信过程中, 往往还需要查看平台是否收到了设备发送上来的消息, 对消息执行了哪些操作, 消息的具体内容(payload)是什么. 腾讯云提供了物联网通信产品的"云日志"功能和腾讯云组件"消息队列CMQ"。
日志中可以看到日志记录了设备的连接, 连接断开, 发布, 订阅等行为, 也记录了规则引擎的操作, 还有CMQ队列的一些行为日志.。
可以在产品列表中找到"消息队列"选项, 设置队列所想要接收的消息类型后保存配置, 即可将平台侧收到的设备消息额外发送到腾讯云消息队列CMQ组件中:
在密钥认证下, 消息的内容(payload)是经过base64编码的, 所以在平台侧看到的数据类似乱码实际上是经过编码后的结果, 想要查看具体的内容, 可以在linux下,
echo
.| base64 --decode
腾讯 TencentOS tiny 集成了对MQTT和腾讯云的支持, 开发者只需要通过简单的设置即可实现与腾讯云的通信, 整体步骤主要包含:
可以参考TencentOS_tiny\board\TencentOS_tiny_EVB_MX\KEIL\tencent_os_mqtt工程。
先准备一份移植好TencentOS tiny的helloworld工程,复制出来一份开始添加。
在工程中新建devices
、hal
、at
和mqtt
组, 并添加下列源文件:
还要添加mqtt_example.c文件:
上述文件分别位于TencentOS-tiny\devices\esp8266
,TencentOS_tiny\net\at
, TencentOS_tiny\components\connectivity\Eclipse-Paho-MQTT
和TencentOS_tiny\platform\hal\st\stm32l4xx\src
,TencentOS-tiny\examples\tencent_os_mqtt
目录下。
上层MQTT的操作最终都会转为为对底层通信设备的socket操作, OS集成了sal_module_wrapper层, 但是由于各个开发板硬件不同, 因此需要对sal_module_wrapper进行适配. 适配过程的核心数据结构为sal_module_t
, 在TencentOS_tiny\net\sal_module_wrapper\sal_module_wrapper.h
文件中定义:
typedef struct sal_module_st{
int (*init)(void);
int (*get_local_mac)(char *mac);
int (*get_local_ip)(char *ip, char *gw, char *mask);
int (*parse_domain)(const char *host_name, char *host_ip, size_t host_ip_len);
int (*connect)(const char *ip, const char *port, sal_proto_t proto);
int (*send)(int sock, const void *buf, size_t len);
int (*recv_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
int (*recv)(int sock, void *buf, size_t len);
int (*sendto)(int sock, char *ip, char *port, const void *buf, size_t len);
int (*recvfrom)(int sock, char *ip, char *port, void *buf, size_t len);
int (*recvfrom_timeout)(int sock, char *ip, char *port, void *buf, size_t len, uint32_t timeout);
int (*close)(int sock);
}sal_module_t;
在使用的时候, 并不需要全部实现, 只需实现下列几个核心的接口即可:
sal_module_t sal_module_esp8266 = {
.init = esp8266_init,
.connect = esp8266_connect,
.send = esp8266_send,
.recv_timeout = esp8266_recv_timeout,
.recv = esp8266_recv,
.sendto = esp8266_sendto,
.recvfrom = esp8266_recvfrom,
.recvfrom_timeout = esp8266_recvfrom_timeout,
.close = esp8266_close,
.parse_domain = esp8266_parse_domain,
};
这些接口在添加的
esp8266.c
中都已经实现。
该结构填充完后, 需要在使用MQTT之前, 调用一下初始化接口注册串口和网络API函数:
esp8266_sal_init(hal_uart_port_t uart_port);
在mqtt_example.c中,已经指定了esp8266_sal的串口为lpuart1:
从之前介绍腾讯云端配置的章节中可以知道, 在MQTT客户端, 所需要的核心信息只包含:
之前移植的Helloworld工程中,没有做lpuart1的接收中断处理,所以进行如下操作。
至此,移植OK,编译下载即可。
后续篇:
接收更多精彩文章及资源推送,欢迎订阅我的微信公众号:『mculover666』。