基于华为云IOT平台实现多节点温度采集(STM32+NBIOT)【华为云IoT端边云体验】
一、前言
当前的场景是,在高速公路上部署温度采集设备,在高速路地表安装温度检测传感器,检测当前路段的路面实际温度。一段高速路上有多个地点需要采集温度数据。 采集温度数据需要上传到云平台进行数据存储,并且通过可视化界面展示温度变化曲线,支持查询最近几天的温度信息。
二、设计思路
(1)云平台选型:使用华为云物联网云平台。
(2)云数据存储: 使用OBS存储,存放设备上传的历史数据。
(3)设备选项:NBIOT模块+温度采集模块,实现温度采集上报。
(4)数据可视化:采用华为云IoT应用侧接口,获取传感器设备上传到云端的数据,在本地设计界面进行可视化显示温度数据。
下面是温度数据可视化展示效果:
本篇文章主要介绍设备上云的详细流程,介绍华为云物联网云端产品、设备创建流程,数据转存方式,应用侧开发接口等等。
硬件选型:
(1)STM32开发板: STM32F103C8T6
(2)NBIOT模块--BC26
BC26模块是一款高性能、低功耗、多频段LTE Cat NB1无线通信模块。
(3)温度采集模块
pt100是铂热电阻,它的阻值会随着温度的变化而改变。PT后的100即表示它在0℃时阻值为100欧姆,在100℃时它的阻值约为138.5欧姆。其工作原理:当PT100在0℃时,其电阻为100欧姆。它的电阻会随着温度的升高而上升,并且它的电阻会匀速增加。热电阻是一种常用于中低温的温度传感器。它的工作原理是基于电阻的热效应,即电阻的阻值随温度的变化而变化。铂金热敏电阻的热阻精度最高,具有抗振动、稳定性好、耐高压等特点。因此,它被制成各种标准温度计进行测量和校准。
三、华为云IOT平台
3.1 创建产品
官网地址: https://www.huaweicloud.cn/
(1)设备接入IOTDA
在产品页面找到iot物联网,选择设备接入IOTDA
设备接入服务(IoT Device Access)是华为云的物联网平台,提供海量设备连接上云、设备和云端双向消息通信、批量设备管理、远程控制和监控、OTA升级、设备联动规则等能力,并可将设备数据灵活流转到华为云其他服务,帮助物联网行业用户快速完成设备联网及行业应用集成,基础版每月一百万条消息免费。
在页面上选择免费试用。
点击后,会进入到设备接入控制台页面。
(2)设备接入地址
在基础版详情页面,点击右边的按需计费详情
,可以查看物联网服务器接入的IP地址,端口号、接入方式。
如果是设备接入,可以选择MQTT或者MQTTS协议,在单片机上只有MQTT协议验证比较方便,通过MQTT三元组即可完成设备连接,当前我这里的设备选择是MQTT协议连接华为云平台。
域名:a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
IP地址: 121.36.42.100
端口号: 1883
(3)创建产品
在左边选项卡里点击产品,进入产品页面,点击右上角创建产品。
根据自己的产品信息,填写表单:
(4)完成产品创建
点击查看详情
,可以进入到创建成功的产品页面。
(4)定义产品模型
产品创建之后,接着需要在平台上构建一款设备的抽象模型,使平台理解该款设备支持的功能。
比如:温度采集设备肯定会向云平台上传采集的温度信息;在产品模型里就可以定义一个温度的属性字段。
选择下面的自定义模型
。
添加服务ID。
添加属性。
新增属性字段。当前在多节点温度采集的设备里,主要是采集温度上传,这里就新增一个温度的属性。
产品模型创建完成。
3.2 创建设备
(1)创建单个设备
设备注册的方式有很多:
【1】创建支持单个设备手动创建。
【2】如果设备特别多可以选择批量注册。
【3】通过API接口进行动态注册。
当前为了演示流程,这里选择第一种方式,手动创建单个设备。
在设备页面,选择所有设备
选项,点击右边的注册设备
按钮。
(2)单设备注册填写信息
点击注册设备之后,会弹出一个表单填写信息。
其中产品就选择刚才创建的产品,设备标识码这一项一般是填设备的ID(设备的唯一标识符,方便绑定设备)。目前还没有对接硬件,我这里就填dev1,方便接下来的测试。 下面的设备ID这一项如果不填,会自动生成,可以不管;最后输入密匙(这个密匙的作用:通过对每个设备进行身份验证,可以安全地将每个设备连接到平台,并且安全地管理这些设备),填好之后点击确定。
(3)设备创建完成
创建好之后,保存生成的设备ID和密匙。
得到的密匙和ID的格式文本如下:
{
"device_id": "6353a8163ec34a6d03c8dfe5_dev1",
"secret": "12345678"
}
这个设备密匙和ID,后面生成MQTT登录参数时需要用到。
(4)创建多个温度设备节点
由于本项目是实现多节点温度上传到云平台,每个节点都是一个独立的温度采集设备,为了方便演示效果,还需要多创建几个设备。
接下来的创建流程,和刚才第一个设备一样,这里就不再截图演示了。
点击创建设备按钮,继续注册。
目前一共创建了4个设备,其中main_dev
设备是用来作为显示终端,在本地用显示屏显示其他温度采集节点采集的温度信息。 剩下3个设备dev1
,dev2
,dev3
是表示3个独立的温度采集节点。
这4个设备的密匙和ID信息如下:
{
"device_id": "6353a8163ec34a6d03c8dfe5_dev1",
"secret": "12345678"
}
{
"device_id": "6353a8163ec34a6d03c8dfe5_dev2",
"secret": "12345678"
}
{
"device_id": "6353a8163ec34a6d03c8dfe5_dev3",
"secret": "12345678"
}
{
"device_id": "6353a8163ec34a6d03c8dfe5_main_dev",
"secret": "12345678"
}
3.3 模拟设备上云测试
目前设备创建之后,这些设备都还没有激活。接下来会使用MQTT客户端来模拟真实设备上云,上传温度数据。
这里的上云,包括数据交互都采用MQTT客户端来模拟实现,不涉及到实际的硬件,只要模拟能测试成功,并且能得到自己想要的结果,那硬件就没有问题了。
(1)生成MQTT鉴权三元组
设备要连接华为云平台的方式,在第一节创建产品的时候就已经介绍了,本次项目里的设备是采用MQTT协议接入云平台。 在完成设备模拟上云之前,需要先生成设备的MQTT协议鉴权三元组。
华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
工具打开的界面如下效果:
前面两行就是填设备创建后生成的设备ID和设备密匙,填好之后,生成下面3行信息,生成的3行就是MQTT协议登录需要用的参数。
按照格式分别生成4个设备的鉴权信息:
得到的三元组如下:
ClientId 6353a8163ec34a6d03c8dfe5_dev1_0_0_2022102209
Username 6353a8163ec34a6d03c8dfe5_dev1
Password c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388
ClientId 6353a8163ec34a6d03c8dfe5_dev2_0_0_2022102209
Username 6353a8163ec34a6d03c8dfe5_dev2
Password c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388
ClientId 6353a8163ec34a6d03c8dfe5_dev3_0_0_2022102209
Username 6353a8163ec34a6d03c8dfe5_dev3
Password c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388
ClientId 6353a8163ec34a6d03c8dfe5_main_dev_0_0_2022102209
Username 6353a8163ec34a6d03c8dfe5_main_dev
Password c58c45d514832b119b4302eafb3e74854849ca94079e9aed75efedf176e9c388
(2)MQTT客户端模拟设备登录
得到MQTT三元组之后,接下来用MQTT客户端模拟设备登录云平台。
按照软件的输入框提示,输入对应的信息,点击登录。 其中的IP地址和端口号在第一小节的产品创建里就介绍过了。 后面输入的这3行就是上一步生成的MQTT鉴权三元组。
登录成功。
然后打开云平台的控制台,查看设备在线情况:
可以看到dev1已经在线了, 刚才模拟的设备就是dev1。
(3)主题订阅与发布
目前设备已经成功登录,接下来要解决的问题就是数据传输问题了。
MQTT协议里要理解的两个概念就是主题订阅,主题发布。 设备上传数据到平台,属于 主题发布。 设备想要知道其他设备的数据或者云平台下发的指令,需要进行主题订阅。
帮助文档的地址: https://support.huaweicloud.cn/api-iothub/iot_06_v5_3002.html
MQTT消息由固定报头(Fixed header)、可变报头(Variable header)和有效载荷(Payload)三部分组成。
其中固定报头(Fixed header)和可变报头(Variable header)格式的填写请参考MQTT标准规范,有效载荷(Payload)的格式由应用定义,即设备和物联网平台之间自己定义。
常见MQTT消息类型主要有CONNECT、SUBSCRIBE、PUBLISH。
CONNECT:指客户端请求和服务端连接。有效载荷(Payload)的主要参数,参考设备连接鉴权填写。
SUBSCRIBE:指客户端订阅请求。有效载荷(Payload)中的主要参数“Topic name”,参考Topic定义中订阅者为设备的Topic。
PUBLISH:平台发布消息。
可变报头(Variable header)中的主要参数“Topic name”,指设备上报到物联网平台时发布者为设备的Topic。详细请参考Topic定义。
有效载荷(Payload)中的主要参数为完整的数据上报和命令下发的消息内容,目前是一个JSON对象。
上行Topic是指设备向平台发送请求,或上报数据,或回复响应。
下行Topic是指平台向设备下发指令,或回复响应。
设备与平台建立连接后,需要订阅下行Topic,否则无法收到平台下发的指令或回复的响应。应用侧接口的调用,需要设备侧的配合,例如应用侧下发命令,设备侧需要先订阅“平台命令下发”的下行Topic,否则设备无法收到平台命令,应用下发命令的接口也会报超时。
在产品页面,可以看到主题的格式,以及对于主题的用途:**
【1】订阅主题
对于设备而言,一般会订阅平台下发消息给设备
这个主题。
设备想接收平台下发的消息,就需要订阅平台下发消息给设备
的主题,订阅后,平台下发消息给设备,设备就会收到消息。
主题的格式如下:
$oc/devices/{device_id}/sys/messages/down
以dev1设备1为例,最终的格式:
$oc/devices/6353a8163ec34a6d03c8dfe5_dev1/sys/messages/down
【2】发布主题
对于设备,发布主题,也就显示向云平台上传数据。
发布的主题格式如下:
$oc/devices/{device_id}/sys/properties/report
以dev1设备1为例最终的格式:
$oc/devices/6353a8163ec34a6d03c8dfe5_dev1/sys/properties/report
发布主题时,需要上传数据,这个数据格式是JSON格式。
上传的JSON数据格式如下:
{
"services": [
{
"service_id": <填服务ID>,
"properties": {
"<填属性名称1>": <填属性值>,
"<填属性名称2>": <填属性值>,
..........
}
}
]
}
根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。
根据这个格式,组合温度节点一次上传的数据:
{"services": [{"service_id": "temp","properties":{"temp":24.6}}]}
(4)MQTT客户端模拟设备上报数据
打开MQTT客户端填入订阅主题,发布主题,和需要发布的数据,然后分别点击订阅主题
按钮,发布主题
按钮。 右边提示成功之后,就可以打开云平台看上传的效果了。 (这里是以dev1,设备1为例)
打开云平台看到dev1已经在线。
点击dev1,进去查看设备上传的数据。 可以看到,刚才上传的数据已经收到了。
到此,设备上传数据到云平台已经完成。
四、设备数据转存
如果设备上传的数据需要进行保存,后续进行分析做其他用途,可以利用数据转发服务,让平台将设备上报数据推送给自己的应用服务器,由应用服务器进行保存;也可以选择让平台将设备上报数据转发给OBS对象存储服务,由OBS进行存储,进行永久保存,非常方便。如果存储在OBS里,自己设计应用侧界面时,也可以直接拉取OBS里的数据下来进行显示,处理,分析。
4.1 创建OSB存储桶
地址: https://www.huaweicloud.cn/product/obs.html
对象存储服务(Object Storage Service,OBS)是一个基于对象的存储服务,提供了海量、安全、高可靠、低成本的数据存储能力。
(1)选择管理控制台
(2)创建桶
填充桶信息: 我这里选择的是 华北-北京一
我这里因为要长期使用,这里选择1年的购买权。
(3)创建成功
4.2 配置数据转发规则
(1)创建规则
选择左侧导航栏的规则>数据转发
,单击右上角的创建规则
。
填充规则转发的信息:
参数名 | 参数说明 |
---|---|
规则名称 | 自定义,如: led_obs。 |
规则描述 | 自定义,如数据转发至OBS服务。 |
数据来源 | 选择“设备属性”。 |
触发事件 | 自动匹配“设备属性上报”。 |
资源空间 | 和上一步创建的产品所在的资源空间保持一致。 |
(2)设置转发目标
单击添加
,设置转发目标。
这里的区域选择--华北-北京一
。 因为前面的OBS桶创建的时候,设置区域设置的是北京一
,然后点击授权。
授权之后,选择刚才创建的OBS桶,设置存储数据的目录和文件名字。
存储的数据可以直接转存JSON数据到OBS存储桶,也可以存放成CSV文件到存储桶。
如果选择存储JSON,就是直接将设备上传的数据存放到OBS存储里,如果选择存储CSV文件,可以自己选择需要存储的字段。 下面我演示一下存储成CSV文件时如何进行设置。 (选择JSON文件不需要进行任何设置,直接将设备上传的数据JSON存储进去了)。
下面设置转发的字段: (如果提示没有授权,点击授权即可)
这个转发字段就是表示需要存放的数据是那些,对于路灯而言,肯定是需要存储上报的温度、湿度、电量、光照强度的属性的。 这些属性在创建产品的时候设置,设备上报的也是这些属性。
这是设备上传一次云平台的完整JSON数据格式:
{
"resource": "device.property",
"event": "report",
"event_time": "20221018T131627Z",
"request_id": "5ee95a0c-262d-43c3-8d31-af453f9952ef",
"notify_data": {
"header": {
"app_id": "7211833377cf435c8c0580de390eedbe",
"device_id": "634e3e423ec34a6d03c84bfb_1126626497",
"node_id": "1126626497",
"product_id": "634e3e423ec34a6d03c84bfb",
"gateway_id": "634e3e423ec34a6d03c84bfb_1126626497"
},
"body": {
"services": [
{
"service_id": "temp",
"properties": {
"temp": 28,
},
"event_time": "20221018T131627Z"
}
]
}
}
}
当前设备上传到云端服务器有一个温度属性字段temp
,如果想转发设备上传的这个温度字段,就可以这样写:
notify_data.body.services[0].properties.temp
后面的存储目标字段,为了好区分,直接填temp
即可。
如果设备还上传了其他属性字段也想转发,按照上面格式设置即可。
如果存储数据时,想知道这个数据是那个产品,那个设备上传的,也可以将设备ID和产品ID转发存储起来:格式如下。
notify_data.header.app_id
notify_data.header.device_id
下面是我设置好,转发存储的字段:
确定后,点击设置完成。
(3)测试规则
设置好之后,在设置转发目标的页面点击测试。
输入数据模板,然后点击连通性测试。如果测试结果显示成功,说明整体流程没有问题了。
(4)启用规则
设置完成后,点击启用规则。
(6)数据上报测试
为了验证转发规则是否生效,接下来使用MQTT客户端多上报几次数据到云平台。
上传之后,打开OBS存储桶的控制台页面。打开转发规则存储的OBS桶。
找到存储数据的文件,点击下载。
下载下来,打开可以看到存储的数据:
到此,数据转发,存储已经成功了。
五、应用侧-可视化大屏开发
对于数据的可视化显示,华为云提供了(Data Lake Visualization)一站式数据可视化平台,数据可视化服务(DLV)可以从OBS文件读取数据呈现为可视化报表,实现数据的可视化显示。
下面是华为云的DLV大屏从OBS读取数据显示的流程:
当前我这里的需求是需要在本地自己设计界面显示数据,没有采用华为云的DLV大屏,如果自己本地软件需要显示设备上传的数据,就需要使用华为云物联网的平台的应用侧API接口,读取设备上传的数据进行,本地进行显示;如果需要历史数据,可以读取OBS存储桶里存储的数据进行显示。
5.1 应用侧接口
帮助文档地址: https://support.huaweicloud.cn/usermanual-iothub/iot_01_0045.html
5.2 查询设备影子数据接口
应用侧接口可以查询发送指令给设备查询属性,也可以读取设备的影子数据。
【1】查询设备查询: 这个是实时查询,相当于应用侧接口发送指令给在线设备,设备收到指令,将当前最新的数据再上传。这个需要保证设备在线,离线是无法调用的。
【2】影子数据:影子数据相当于保存设备上传的最新一次数据。 读取读取影子设备数据,是不需要设备在线。
(1)接口URI地址
请求方法 | GET |
---|---|
URI | /v5/iot/{project_id}/devices/{device_id}/shadow |
传输协议 | HTTPS |
(2)请求参数说明
名称 | 必选/可选 | 类型 | 位置 | 说明 |
---|---|---|---|---|
X-Auth-Token | 必选 | String | Header | 参数说明:用户Token。通过调用IAM服务 获取IAM用户Token接口获取,接口返回的响应消息头中“X-Subject-Token”就是需要获取的用户Token。 |
Instance-Id | 可选 | String | Header | 参数说明:实例ID。物理多租下各实例的唯一标识,一般华为云租户无需携带该参数,仅在物理多租场景下从管理面访问API时需要携带该参数。 |
project_id | 必选 | String | Path | 参数说明:项目ID。获取方法请参见 获取项目ID。 |
device_id | 必选 | String | Path | 参数说明:设备ID,用于唯一标识一个设备。在注册设备时直接指定,或者由物联网平台分配获得。由物联网平台分配时,生成规则为"product_id" + " " + "node_id"拼接而成。取值范围:长度不超过128,只允许字母、数字、下划线( )、连接符(-)的组合。 |
(3)响应参数
名称 | 类型 | 说明 |
---|---|---|
device_id | String | 设备ID,用于唯一标识一个设备。在注册设备时直接指定,或者由物联网平台分配获得。由物联网平台分配时,生成规则为"product_id" + "_" + "node_id"拼接而成。 |
shadow | List[DeviceShadowData]() | 设备影子数据结构体。 |
名称 | 类型 | 说明 |
---|---|---|
service_id | String | 设备的服务ID,在设备关联的产品模型中定义。 |
desired | DeviceShadowProperties Object | 用户最近一次对设备下发的预期数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name)。 |
reported | DeviceShadowProperties Object | 设备最近一次上报的属性数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name)。 |
version | Long | 设备影子的版本,携带该参数时平台会校验值必须等于当前影子版本,初始从0开始。 |
名称 | 类型 | 说明 |
---|---|---|
properties | Object | 设备影子的属性数据,Json格式,里面是一个个键值对,每个键都是产品模型中属性的参数名(property_name),目前如样例所示只支持一层结构。注意:JSON结构的key当前不支持特殊字符:点(.)、dollar符号($)、空char(十六进制的ASCII码为00),key为以上特殊字符无法正常刷新设备影子 |
event_time | String | 事件操作时间,格式:yyyyMMdd'T'HHmmss'Z',如20151212T121212Z。 |
(4)请求示例
GET https://{Endpoint}/v5/iot/{project_id}/devices/{device_id}/shadow
Content-Type: application/json
X-Auth-Token: ********
Instance-Id: ********
(5)响应示例
Status Code: 200 OK
Content-Type: application/json
{
"device_id" : "40fe3542-f4cc-4b6a-98c3-61a49ba1acd4",
"shadow" : [ {
"service_id" : "WaterMeter",
"desired" : {
"properties" : {
"temperature" : "60"
},
"event_time" : "20151212T121212Z"
},
"reported" : {
"properties" : {
"temperature" : "60"
},
"event_time" : "20151212T121212Z"
},
"version" : 1
} ]
}
(6)错误码
HTTP状态码 | 错误码 | 错误码英文描述 | 错误码中文描述 | 处理建议 |
---|---|---|---|---|
403 | IOTDA.000021 | Operation not allowed. User not found by IAM token or the authorized user has not subscribed IOTDA service. | 没有找到IAM Token所对应的用户信息或该用户没有订阅设备接入服务(IOTDA) | 请排查IAM Token所在用户是否订阅了设备接入服务(IOTDA)。 |
404 | IOTDA.014000 | The device does not exist. | 设备不存在 | 请排查请求参数是否有误并确认是否有在平台注册该设备。 |
500 | IOTDA.000020 | Decrypt IAM token failed. |
5.3 接口调试
在线调试地址:https://apiexplorer.developer.huaweicloud.cn/apiexplorer/debug?product=IoTDA&api=ShowDeviceShadow
下面是调试影子 数据查询接口,查询设备影子数据: 右边返回的是设备上传的最新数据。
5.5 接口总结
请求地址:
https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/{project_id}/devices/{device_id}/shadow
请求方式: GET
请求头:
{
"X-Auth-Token": "这个需要自己获取",
"Content-Type": "application/json"
}
5.5 如何获取X-Subject-Token
使用API访问华为云的所有服务接口,都需要填X-Subject-Token参数,下面介绍步骤:
(1)创建一个新的IAM帐户
鼠标悬停在右上角的用户名称上,弹出下拉框,选择统一身份认证。
(2)选择创建用户
(3)使用调试接口测试获取oken
右边响应头里的X-Subject-Token
就是获取的token。
(4)上面的这些账户名称从哪里获取?
(5)请求地址和数据格式
获取X-Subject-Token请求的地址: https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens
请求头数据:
{
"User-Agent": "API Explorer",
"X-Auth-Token": "******",
"Content-Type": "application/json;charset=UTF-8"
}
请求体数据:
{
"auth": {
"identity": {
"methods": [
"password"
],
"password": {
"user": {
"domain": {
"name": "xxxxx" //这里填当前主账户名称
},
"name": "xxxx", //这个新建的子账户名称
"password": "xxxxx" //这个是新建的子账户密码
}
}
},
"scope": {
"project": {
"name": "cn-north-4"
}
}
}
}
(6)代码实现
/*
功能: 获取token
*/
void Widget::GetToken()
{
//表示获取token
function_select=3;
QString requestUrl;
QNetworkRequest request;
//设置请求地址
QUrl url;
//获取token请求地址
requestUrl = QString("https://iam.%1.myhuaweicloud.com/v3/auth/tokens")
.arg(SERVER_ID);
//自己创建的TCP服务器,测试用
//requestUrl="http://10.0.0.6:8080";
//设置数据提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=UTF-8"));
//构造请求
url.setUrl(requestUrl);
request.setUrl(url);
QString text =QString("{"auth":{"identity":{"methods":["password"],"password":"
"{"user":{"domain": {"
""name":"%1"},"name": "%2","password": "%3"}}},"
""scope":{"project":{"name":"%4"}}}}")
.arg(MAIN_USER)
.arg(IAM_USER)
.arg(IAM_PASSWORD)
.arg(SERVER_ID);
//发送请求
manager->post(request, text.toUtf8());
}
5.6 查询设备影子数据代码
(1)这是请求代码
//查询设备属性
void Widget::Get_device_properties()
{
//表示获取token
function_select=0;
QString requestUrl;
QNetworkRequest request;
//设置请求地址
QUrl url;
//获取token请求地址
requestUrl = QString("https://iotda.%1.myhuaweicloud.com/v5/iot/%2/devices/%3/shadow")
.arg(SERVER_ID)
.arg(PROJECT_ID)
.arg(device_id);
//设置数据提交格式
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
//设置token
request.setRawHeader("X-Auth-Token",Token);
//构造请求
url.setUrl(requestUrl);
request.setUrl(url);
//发送请求
manager->get(request);
}
//更新设备属性
void Widget::on_pushButton_update_device_clicked()
{
Get_device_properties();
}
(2)这是请求一次返回的JSON数据
{
"device_id": "6353a8163ec34a6d03c8dfe5_dev1",
"shadow": [
{
"service_id": "temp",
"desired": {
"properties": null,
"event_time": null
},
"reported": {
"properties": {
"temp": 45.6
},
"event_time": "20221024T013607Z"
},
"version": 49
}
]
}
5.7 界面设计最终效果
六、硬件部分
6.1 MQTT版本
注意:华为云、OneNet、腾讯IOT 等平台规定接入的MQTT协议版本必须是3.1.1 。
在BC26里,需要执行这行代码配置MQTT协议版本为3.1.1
AT+QMTCFG="version",0,4
6.2 BC26上云配置代码
#include "BC26.h"
//BC26复位
//引脚是PC0
//高电平有效
void BC26_Reset(void)
{
//开时钟
RCC->APB2ENR|=1<<4;
//配置GPIO口
GPIOC->CRL&=0xFFFFFFF0;
GPIOC->CRL|=0x00000003;
//开始复位
GPIOC->ODR|=1<<0;
DelayMs(2000);
GPIOC->ODR&=~(1<<0);
}
/*
函数功能:向BC26模块发送指令
函数参数:
char *cmd 发送的命令
char *check_data 检测返回的数据
返回值: 0表示成功 1表示失败
*/
u8 BC26_SendCmd(char *cmd,char *check_data)
{
u16 i,j;
for(i=0;i<5;i++) //测试的总次数
{
USART2_RX_FLAG=0;
USART2_RX_CNT=0;
memset(USART2_RX_BUFFER,0,sizeof(USART2_RX_BUFFER));
USARTx_StringSend(USART2,cmd); //发送指令
for(j=0;j<500;j++) //等待的时间(ms单位)
{
if(USART2_RX_FLAG)
{
USART2_RX_BUFFER[USART2_RX_CNT]='\0';
if(strstr((char*)USART2_RX_BUFFER,check_data))
{
return 0;
}
else break;
}
delay_ms(30); //一次的时间
}
}
return 1;
}
//初始化BC26模块
int BC26_Init(void)
{
if(BC26_SendCmd("AT\r\n","OK"))
{
USART1_Printf("BC26模块不存在.\r\n");
return 1;
}
else
{
USART1_Printf("BC26模块正常!\r\n");
}
if(BC26_SendCmd("AT+CIMI\r\n","OK"))
{
USART1_Printf("模块未插卡.\r\n");
return 3;
}
else
{
USART1_Printf("卡已经插好.\r\n");
}
if(BC26_SendCmd("AT+CGATT=1\r\n","OK"))
{
USART1_Printf("配置:网络激活失败.\r\n");
return 3;
}
else
{
USART1_Printf("配置:网络激活成功.\r\n");
}
if(BC26_SendCmd("AT+CGATT?\r\n","OK"))
{
USART1_Printf("状态:网络激活失败.\r\n");
return 4;
}
else
{
USART1_Printf("状态:网络激活成功.\r\n");
}
if(BC26_SendCmd("AT+CSQ\r\n","OK"))
{
USART1_Printf("查询信号质量失败.\r\n");
return 5;
}
else
{
USART1_Printf("信号质量:%s\r\n",USART2_RX_BUFFER);
}
// if(BC26_SendCmd("AT+QGNSSC=1\r\n","OK"))
// {
// USART1_Printf("激活GPS定位失败.\r\n");
// return 6;
// }
// else
// {
// USART1_Printf("激活GPS定位成功.\r\n");
// }
//
// if(BC26_SendCmd("AT+QGNSSAGPS=1\r\n","OK"))
// {
// USART1_Printf("开启AGPS定位失败.\r\n");
// return 6;
// }
// else
// {
// USART1_Printf("开启AGPS定位成功.\r\n");
// }
//
// if(BC26_SendCmd("AT+CGPADDR=1\r\n","OK"))
// {
// USART1_Printf("激活GPRS场景失败.\r\n");
// return 7;
// }
// else
// {
// USART1_Printf("激活GPRS场景成功.\r\n");
// }
//
// DelayMs(1000);
// DelayMs(1000);
if(BC26_SendCmd("AT+CEREG?\r\n","+CEREG: 0,1"))
{
USART1_Printf("网络注册状态:失败.\r\n");
return 9;
}
else
{
USART1_Printf("网络注册状态:成功.\r\n");
}
return 0;
}
//发送使用的缓冲区
char BC26_SEND_BUFF[500];
//MQTT协议登录服务器
int BC26_MQTT_Connect(void)
{
//1. 先关闭之前的连接
USART1_Printf("正在关闭之前的连接...\r\n");
BC26_SendCmd("AT+QMTCLOSE=0\r\n","OK");
DelayMs(4000);
//关闭服务
BC26_SendCmd("AT+QMTCONN?\r\n","OK");
DelayMs(4000);
//2. 连接MQTT服务器
USART1_Printf("正在连接MQTT服务器..\r\n");
sprintf(BC26_SEND_BUFF,"AT+QMTOPEN=0,"%s",%s\r\n",MQTT_SERVER_ADDR,MQTT_SERVER_PORT);
if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))
{
USART1_Printf("MQTT服务器连接失败:%s\r\n",BC26_SEND_BUFF);
return 1;
}
else
{
USART1_Printf("MQTT服务器连接成功.\r\n");
}
DelayMs(3000);
//3. 登录MQTT服务器
USART1_Printf("正在登录MQTT服务器...\r\n");
sprintf(BC26_SEND_BUFF,"AT+QMTCONN=0,"%s","%s","%s"\r\n",MQTT_CLIENT_ID,MQTT_USERNAME,MQTT_PASSWORD);
if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))
{
USART1_Printf("MQTT服务器登录失败:%s\r\n",BC26_SEND_BUFF);
return 2;
}
else
{
USART1_Printf("MQTT服务器登录成功.\r\n");
}
DelayMs(3000);
//4. 订阅主题
USART1_Printf("正在订阅主题...\r\n");
sprintf(BC26_SEND_BUFF,"AT+QMTSUB=0,1,"%s",2\r\n",MQTT_TOPIC_SUB_GET);
if(BC26_SendCmd(BC26_SEND_BUFF,"OK"))
{
USART1_Printf("MQTT主题订阅失败:%s\r\n",BC26_SEND_BUFF);
}
else
{
USART1_Printf("MQTT主题订阅成功.\r\n");
}
return 0;
}
//MQTT发布主题
int MQTT_PublishTheme(char *text)
{
char send_buf[3];
sprintf(BC26_SEND_BUFF,"AT+QMTPUB=0,0,0,0,"%s"\r\n",MQTT_TOPIC_SUB_SET);
if(BC26_SendCmd(BC26_SEND_BUFF,">"))
{
USART1_Printf("发布主题等待输入失败:%s\r\n",BC26_SEND_BUFF);
return 1;
}
USARTx_StringSend(USART2,text); //发送主题内容
//发送结束符
send_buf[0] = 0x1a;
send_buf[1] = '\0';
if(BC26_SendCmd(send_buf,"OK"))
{
USART1_Printf("发布主题内容失败:%s\r\n",BC26_SEND_BUFF);
return 2; //发送结束符号
}
else
{
USART1_Printf("发布主题内容成功.\r\n");
}
USART1_Printf("发布主题内容:%s\r\n",text);
return 0;
}
//MQTT响应应用层的属性请求
int MQTT_SendAttribute(char *text,char *request_id)
{
char send_buf[3];
sprintf(BC26_SEND_BUFF,"AT+QMTPUB=0,0,0,0,"%s%s"\r\n",MQTT_TOPIC_SUB_SET_RUN,request_id);
if(BC26_SendCmd(BC26_SEND_BUFF,">"))
{
USART1_Printf("(响应)发布主题等待输入失败:%s\r\n",BC26_SEND_BUFF);
return 1;
}
USARTx_StringSend(USART2,text); //发送主题内容
//发送结束符
send_buf[0] = 0x1a;
send_buf[1] = '\0';
if(BC26_SendCmd(send_buf,"OK"))
{
USART1_Printf("(响应)发布主题内容失败:%s\r\n",BC26_SEND_BUFF);
return 2; //发送结束符号
}
else
{
USART1_Printf("(响应)发布主题内容成功.\r\n");
}
USART1_Printf("(响应)发布主题内容:%s\r\n",text);
return 0;
}
/*
函数功能: 获取一次GPS经纬度数据
函数参数:
double *Longitude :经度
double *latitude :纬度
返回值: 0表示定位成功,1表示数据接收失败,2表示定位失败
*/
u8 BC26_GetGPS_Data(double *Longitude,double *latitude)
{
/*1. 发送获取GPS数据的指令*/
if(BC26_SendCmd("AT+QGNSSRD="NMEA/RMC"\r\n", "OK\r\n"))return 1;
/*2. 对GPS数据进行解码*/
if(GPS_GNRMC_Decoding((char *)USART2_RX_BUFFER,Longitude,latitude))return 2;
//解码成功
return 0;
}
/*
函数功能: 开启GPS功能
返 回 值:0表示成功 1表示失败
*/
u8 BC26_StartGPS(void)
{
//先判断GPS功能是否启动
if(BC26_SendCmd("AT+QGNSSC?\r\n","+QGNSSC: 1"))
{
//没有启动就启动GPS功能
if(BC26_SendCmd("AT+QGNSSC=1\r\n","OK\r\n"))
{
USART1_Printf("开启GPS功能失败.\r\n");
return 1; //GPS功能启动失败
}
else
{
USART1_Printf("开启GPS功能成功.\r\n");
}
}
return 0;
}
6.3 MQTT 3.1协议介绍
地址: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
规范共分七章:
第一章 - 介绍
第二章 - MQTT控制包格式
第三章 - MQTT控制包
第四章 - 操作行为
第五章 - 安全
第六章 - 使用WebSocket进行网络传输
第七章 - 一致性目标
七、总结
通过温度采集的项目,介绍了一个物联网设备如何上云,云平台如何配置,设备数据如何永久转存,应用侧API接口,应用侧界面发等知识点。如果没有硬件,也可以直接利用软件方式模拟设备上传,实现最终的效果。
【我的IoT端边云体验】有奖征文火热进行中:https://bbs.huaweicloud.cn/blogs/378687 此外,在(https://bbs.huaweicloud.cn/activity/suggestion.html)提出您的宝贵建议,标题以【云驻计划-定向征文】开头,还有机会赢取额外奖励。
- 点赞
- 收藏
- 关注作者
评论(0)