12下一页
返回列表 发新帖本帖赏金 150.00元(功能说明)

[MM32生态] 手把手教你基于MM32与BC26搭建OneNET云平台应用

[复制链接]
 楼主| xld0932 发表于 2022-6-1 13:42 | 显示全部楼层 |阅读模式
本帖最后由 xld0932 于 2022-6-1 13:55 编辑

#申请原创#   @21小跑堂

物联网实现了万物互联,物联网应用也如雨后春笋般崛起发展迅速。随之而来的,各大物联网平台也孕育而生,OneNET、阿里云、腾讯云、百度云等等,为物联网的应用和发展提供了不同的解决方案。本文将基于MM32F031F6P6与NB-IoT模块BC26的硬件平台,结合OneNET物联网平台,来教大家如何一步步搭建一个物联网应用。本文主要内容分为如下几个部分:
  • OneNET介绍
  • OneNET Studio创建产品和添加设备
  • MQTT.fx配置并与OneNET进行交互
  • 使用MM32与BC26开发板连接OneNET


一、OneNET介绍
OneNET是中国移动发布的PaaS物联网开放平台,它是在物联网应用和真实设备之间搭建高效、稳定、安全的应用平台,以平台作为连接和数据的中心,能适应各种传感网络和通讯网络,能够帮助开发者实现设备接入与设备连接,快速完成产品开发部署,为智能硬件提供完善的物联网解决方案。
OneNET Studio是新一代物联网中台,向下接入设备,向上承载应用,为用户提供一站式“终端-平台-应用”整体解决方案,帮助企业实现海量设备的快速上云。设备侧提供物联网设备接入、设备管理、数据解析、数据转发、设备运维监控等服务;应用侧提供丰富的API接口、数据推送、消息队列、规则引擎场景联动等功能;同时提供语音通话、LBS定位、数据分析及可视化等增值服务。
OneNET Studio 平台架构图.png
OneNET Studio平台架构图
OneNET Studio 平台功能图.png
OneNET Studio平台功能图


二、OneNET Studio创建产品和添加设备
打开OneNET控制台首页(https://open.iot.10086.cn/console),完成账号注册与登录,在Studio使用概览栏点击前往Studio,如下图所示:
10.png

在OneNET Studio界面,左侧的菜单项目我们使用到了设备接入与管理和运维监控这2项,其它的可以后续再深入研究,在右侧提供了常用入口部分,如下图所示:
11.png

在创建一个项目时,我们需要先添加一个产品,然后在对应的产品中添加相应的设备;所以我们可以点击左侧菜单项设备接入与管理下的产品管理中的添加产品,也可以点击右侧常用入口中的添加产品,填写相应的项目名称、产品类别、接入协议等参数,最后点击确定即可。我们可以点击详情查看产品的详细信息、物模型管理等操作,如下图所示:
0.png
1.png

接下来我们点击左侧菜单项设备接入与管理下的设备管理中的添加设备,也可以点击右侧常用入口中的添加设备,填写设备名称和选择所属产品,点击确定即可。我们可以点击详情查看设备的详细信息和物模型属性值,如下图所示:
3.png
4.png


三、MQTT.fx配置并与OneNET进行交互
MQTT.fx是一个MQTT客户端工具,支持Windows、Linux和Mac系统,这样可以基于PC软件的方式通过配置连接MQTT云服务器进行测试,方便了开发测试。对应的Windows版本的软件在后面附件中有下载。

MQTT.fx配置
在配置与OneNET服务器进行通讯测试时,我们需要在上面添加产品的时候,将接入协议选择为MQTT,数据协议建议使用常用的OneJson;另外我们还需要设备的详情信息和OneNET的专有token工具,配置如下图所示:
设备详情.png
6.png
7.png

  • Profile Name可以任意取名,我们这边对应的是产品名称OneNet_BC26_001
  • Profile Type默认选择MQTT Broker
  • Broker Address和Broker Port需要根据接入协议有不同的设置参数,如下表所示:
连接协议
地址
端口
说明
MQTT
studio-mqtt.heclouds.com
1883
非加密端口接入
MQTTS
studio-mqtts.heclouds.com
8883
加密端口接入
在上图中我们选用的是非加密端口接入的MQTT连接协议,如果是MQTTS连接协议的话,是需要配置TLS选项卡中的内容的。

  • Client ID必须与设备名称相一致。
  • OneNET设备接入支持的是标准的MQTT3.1.1版本,支持TLS加密,所以需要在General中将MQTT Version设置为3.1.1版本,否则会因为接入协议不一致,导致无法通讯,这个需要注意一下!
  • 在User Credentials中,我们需要设置User Name和Password,这边的User Name必须与所属产品ID保持一致,而Password则需要使用到OneNET的token计算工具计算得来。

在OneNET token工具中,res使用固定的格式:products/产品所属ID/devices/设备名称,et是有效时间戳,key使用的是设备详情中的设备密钥,method默认选择md5,然后点击Generate产生一串序列号,这一串序列号就是Password。
5.png

使用菜鸟工具的在线UNIX时间戳转换(https://c.runoob.com/front-end/852/),如下图所示可以将UNIX时间戳和北京时间进行相互转换;在使用Token工具时,我们需要设定一个有效截止日期,在线转换页面上将北京时间设定为2022年12月31日23时59分59秒,然后点击转换按键,我们就得到了对应的UNIX时间戳1672502399,这个就是我们在Token工具软件上需要填充的数值。
12.png

在配置好MQTT.fx参数后,我们就可以点击OK,保存退出。在MQTT.fx主界面选择刚刚的OneNet_BC26_001,点击Connect后就可以成功连接了。

在MQTT.fx连接上OneNET云平台后,我们要如何进行交互呢,这个时候我们需要做三件事:要知道MQTT.fx与OneNET之间的通讯主题;要知道设备的物模型,有哪些参数以及相应的属性;MQTT.fx与OneNET之间通讯的数据格式;

对于MQTT.fx与OneNET之间的通讯主题在OneNET开发文档中的描述,也可以参考下表:
功能
类别
行为
描述
Topic类
操作权限
物模型通信Topic
属性
设备属性上报
请求
$sys/{pid}/{device-name}/thing/property/post
发布
响应
$sys/{pid}/{device-name}/thing/property/post/reply
订阅
设备属性设置(同步)
请求
$sys/{pid}/{device-name}/thing/property/set
订阅
响应
$sys/{pid}/{device-name}/thing/property/set_reply
发布
设备获取属性期望值
请求
$sys/{pid}/{device-name}/thing/property/desired/get
发布
响应
$sys/{pid}/{device-name}/thing/property/desired/get/reply
订阅
清除属性期望值
请求
$sys/{pid}/{device-name}/thing/property/desired/delete
发布
响应
$sys/{pid}/{device-name}/thing/property/desired/delete/reply
订阅
设备属性获取
请求
$sys/{pid}/{device-name}/thing/property/get
订阅
响应
$sys/{pid}/{device-name}/thing/property/get_reply
发布
事件
设备事件上报
请求
$sys/{pid}/{device-name}/thing/event/post
发布
响应
$sys/{pid}/{device-name}/thing/event/post/reply
订阅
服务
设备服务调用
请求
$sys/{pid}/{device-name}/thing/service/{identifier}/invoke
订阅
响应
$sys/{pid}/{device-name}/thing/service/{identifier}/invoke_reply
发布

功能
类别
行为
描述
Topic类
操作权限
网关与子设备通信Topic
上下线
子设备上线
请求
$sys/{pid}/{device-name}/thing/sub/login
发布
响应
$sys/{pid}/{device-name}/thing/sub/login/reply
订阅
子设备下线
请求
$sys/{pid}/{device-name}/thing/sub/**ut
发布
响应
$sys/{pid}/{device-name}/thing/sub/**ut/reply
订阅
属性和事件
批量上报属性和事件(网关上报或代理子设备上报)
请求
$sys/{pid}/{device-name}/thing/pack/post
发布
响应
$sys/{pid}/{device-name}/thing/pack/post/reply
订阅
子设备属性获取
请求
$sys/{pid}/{device-name}/thing/sub/property/get
订阅
响应
$sys/{pid}/{device-name}/thing/sub/property/get_reply
发布
子设备属性设置
请求
$sys/{pid}/{device-name}/thing/sub/property/set
订阅
响应
$sys/{pid}/{device-name}/thing/sub/property/set_reply
发布
历史属性和事件上报(网关上报或代理子设备上报)
请求
$sys/{pid}/{device-name}/thing/history/post
发布
响应
$sys/{pid}/{device-name}/thing/history/post/reply
订阅
服务
子设备服务调用
请求
$sys/{pid}/{device-name}/thing/sub/service/invoke
订阅
响应
$sys/{pid}/{device-name}/thing/sub/service/invoke_reply
发布
拓扑关系
添加子设备
请求
$sys/{pid}/{device-name}/thing/sub/topo/add
发布
响应
$sys/{pid}/{device-name}/thing/sub/topo/add/reply
订阅
删除子设备
请求
$sys/{pid}/{device-name}/thing/sub/topo/delete
发布
响应
$sys/{pid}/{device-name}/thing/sub/topo/delete/reply
订阅
获取拓扑关系
请求
$sys/{pid}/{device-name}/thing/sub/topo/get
发布
响应
$sys/{pid}/{device-name}/thing/sub/topo/get/reply
订阅
网关同步结果响应
$sys/{pid}/{device-name}/thing/sub/topo/get/result
发布
通知网关拓扑关系变化
请求
$sys/{pid}/{device-name}/thing/sub/topo/change
订阅
响应
$sys/{pid}/{device-name}/thing/sub/topo/change_reply
发布
在获知了这些主题后,我们就可以设置MQTT.fx的订阅主题和发布主题了……

对于设备的物模型可以在产品详情中进行查看,如下图所示:
产品详情.png

对于MQTT.fx与OneNET之间通讯的数据格式,我们可以参考物模型中的定义再结合OneJson自己来编写;但我们有一个更简便的方式来自动获取,就是使用右侧菜单项运维监控的设备调试功能:选择相应的产品,选择产品中任意一个处于离线状态的设备,点击启动调试;在启动调试后,设备的属性等信息就会列举出来,选择其中的某些项进行设置后,点击底下的属性上报或者获取按键,在右侧调试日志中就会显示出通讯数据了,我们可以直接拷贝这些数据进行调试、编程和应用。
8.png 9.png

MQTT.fx订阅主题:
14.png

MQTT.fx发布主题并查看OneNET设备属性:Lock_status -> true
15.png
16.png
17.png

MQTT.fx发布主题并查看OneNET设备属性:Lock_status -> false
18.png
19.png
20.png


四、使用MM32与BC26开发板连接OneNET
BC26模块是移远于2017年发布的基于MTK平台的NB-IoT模组,基于UART串口与MM32之间使用AT指令集进行通讯交互;BC26支持普通的TCP方式与远程进行通讯,也可以通过内部集成的MQTT协议与MQTT服务器进行连接通讯。BC26的MQTT支持多版本的协议,可以通过AT指令进行配置。

MCU选用MM32F031F6P6这个型号,LQFP20的小封装形式节省了PCB产品设计尺寸,同时带有32KB的FLASH空间和4KB的SRAM空间,结合多种低功耗模式可供选择,特别适用于物联网产品中的节点设备,用于处理不太复杂的产品应用。
A.jpg

硬件原理图:
原理图.jpg

软件源代码:
  1. void BC26_InitBuffer(void)
  2. {
  3.     BC26_Length = 0;
  4.     memset(BC26_Buffer, 0, sizeof(BC26_Buffer));
  5. }

  6. void BC26_SendData(uint8_t Data)
  7. {
  8.     UART_SendData(UART1, Data);
  9.     while(UART_GetFlagStatus(UART1, UART_IT_TXIEN) == RESET);
  10. }

  11. void BC26_SendString(char *str)
  12. {
  13.     while(*str)
  14.     {
  15.         BC26_SendData(*str++);
  16.     }
  17. }

  18. uint8_t BC26_AT_WaitForReply(char *Keyword)
  19. {
  20.     while(QUEUE_EMPTY(UART1_RX_IDX) == 0)
  21.     {
  22.         BC26_Buffer[BC26_Length++] = QUEUE_READ(UART1_RX_IDX);

  23.         if(strstr((char *)BC26_Buffer, Keyword) != NULL)
  24.         {
  25.             return 1;
  26.         }
  27.     }

  28.     return 0;
  29. }

  30. void BC26_AT_DisplayReply(void)
  31. {
  32.     OLED_Clear(0x00);  OLED_Display((char *)BC26_Buffer);  SysTick_DelayMS(999);
  33. }

  34. uint32_t BC26_AT_SendCommand(char *Command, char *Response, uint32_t Timeout)
  35. {
  36.     if(Command != NULL)
  37.     {
  38.         QUEUE_INIT(UART1_RX_IDX);

  39.         BC26_SendString(Command);
  40.         BC26_SendString("\r\n" );
  41.     }

  42.     if(Timeout != 0)
  43.     {
  44.         BC26_InitBuffer();

  45.         while(Timeout--)
  46.         {
  47.             if(BC26_AT_WaitForReply(Response) != 0)
  48.             {
  49.                 BC26_AT_DisplayReply();   return 1;
  50.             }

  51.             SysTick_DelayMS(1);
  52.         }

  53.         return 0;
  54.     }

  55.     return 1;
  56. }

  57. void BC26_InitUART(void)
  58. {
  59.     GPIO_InitTypeDef GPIO_InitStructure;
  60.     NVIC_InitTypeDef NVIC_InitStructure;
  61.     UART_InitTypeDef UART_InitStructure;

  62.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);

  63.     UART_StructInit(&UART_InitStructure);
  64.     UART_InitStructure.UART_BaudRate            = 9600;
  65.     UART_InitStructure.UART_WordLength          = UART_WordLength_8b;
  66.     UART_InitStructure.UART_StopBits            = UART_StopBits_1;
  67.     UART_InitStructure.UART_Parity              = UART_Parity_No;
  68.     UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
  69.     UART_InitStructure.UART_Mode                = UART_Mode_Rx | UART_Mode_Tx;
  70.     UART_Init(UART1, &UART_InitStructure);

  71.     NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
  72.     NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
  73.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  74.     NVIC_Init(&NVIC_InitStructure);

  75.     UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);
  76.     UART_Cmd(UART1, ENABLE);

  77.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  78.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,  GPIO_AF_1);
  79.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);

  80.     GPIO_StructInit(&GPIO_InitStructure);
  81.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
  82.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  83.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
  84.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  85.     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
  86.     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;
  87.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  88. }

  89. void UART1_IRQHandler(void)
  90. {
  91.     if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
  92.     {
  93.         QUEUE_WRITE(UART1_RX_IDX, UART_ReceiveData(UART1));
  94.         UART_ClearITPendingBit(UART1, UART_IT_RXIEN);
  95.     }
  96. }

  97. void BC26_StartProcess(void)
  98. {
  99.     BC26_AT_SendCommand("AT",           "OK", 500);

  100.     BC26_AT_SendCommand("AT+CPIN?",     "OK", 500);

  101.     BC26_AT_SendCommand("AT+CSQ",       "OK", 500);

  102.     BC26_AT_SendCommand("AT+CEREG?",    "OK", 500);

  103.     BC26_AT_SendCommand("AT+CGPADDR?",  "OK", 500);

  104.     /* 设置MQTT协议版本 */
  105.     BC26_AT_SendCommand("AT+QMTCFG="version",0,1", "OK", 500);

  106.     /* 打开MQTT客户端网络 */
  107.     BC26_AT_SendCommand("AT+QMTOPEN=0,"studio-mqtt.heclouds.com",1883", "+QMTOPEN: 0,0", 5000);

  108.     /* 连接客户端至MQTT服务器 */
  109.     BC26_AT_SendCommand("AT+QMTCONN=0,"BC26_001","OV9DtD7QIm","version=2018-10-31&res=products%2FOV9DtD7QIm%2Fdevices%2FBC26_001&et=1672502399&method=md5&sign=QMYMEAx06v6zgoXB%2Fh5zgA%3D%3D"", "+QMTCONN: 0,0,0", 5000);

  110.     /* 订阅主题:设备属性上报的响应 */
  111.     BC26_AT_SendCommand("AT+QMTSUB=0,1,"$sys/OV9DtD7QIm/BC26_001/thing/property/post/reply",0", "OK", 5000);
  112. }

  113. void BC26_Init(void)
  114. {
  115.     BC26_InitUART();

  116.     BC26_StartProcess();

  117.     TASK_Append(TASK_ID_BC26, BC26_Handler, 10000);
  118. }

  119. void BC26_Handler(void)
  120. {
  121.     static uint8_t Control = 0;
  122.     uint8_t Result = 0;

  123.     if(Control == 0)
  124.     {
  125.         Control = 1;
  126.         Result = BC26_AT_SendCommand("AT+QMTPUB=0,0,0,0,"$sys/OV9DtD7QIm/BC26_001/thing/property/post",72,"{"id":"12345","version":"1.0","params":{"Lock_control":{"value":true}}}}"",  "OK", 5000);
  127.     }
  128.     else
  129.     {
  130.         Control = 0;
  131.         Result = BC26_AT_SendCommand("AT+QMTPUB=0,0,0,0,"$sys/OV9DtD7QIm/BC26_001/thing/property/post",73,"{"id":"12345","version":"1.0","params":{"Lock_control":{"value":false}}}}"", "OK", 5000);
  132.     }

  133.     if(Result != 0)
  134.     {
  135.         OLED_Display("Control Publish OK");
  136.     }
  137.     else
  138.     {
  139.         OLED_Display("Control Publish Error");
  140.     }
  141. }

  142. void BC26_KEY_Handler(uint8_t Flag)
  143. {
  144.     uint8_t Result = 0;

  145.     /* 发布消息:设备属性上报 */
  146.     if(Flag)
  147.     {
  148.         Result = BC26_AT_SendCommand("AT+QMTPUB=0,0,0,0,"$sys/OV9DtD7QIm/BC26_001/thing/property/post",71,"{"id":"12345","version":"1.0","params":{"Lock_status":{"value":true}}}}"",  "OK", 5000);
  149.     }
  150.     else
  151.     {
  152.         Result = BC26_AT_SendCommand("AT+QMTPUB=0,0,0,0,"$sys/OV9DtD7QIm/BC26_001/thing/property/post",72,"{"id":"12345","version":"1.0","params":{"Lock_status":{"value":false}}}}"", "OK", 5000);
  153.     }

  154.     if(Result != 0)
  155.     {
  156.         OLED_Display("Status Publish OK");
  157.     }
  158.     else
  159.     {
  160.         OLED_Display("Status Publish Error");
  161.     }
  162. }

视频操作演示:
https://www.bilibili.com/video/BV1JT4y1B7WM/
基于MM32开发板板载的BC26模块实现与OneNET云平台的应用,视频中过程分别为:查看OneNET中当前产品管理和设备管理、开发板上电后通过Keil MDK软件编译下载程序、开发板启动初始化过程,并连接OneNET平台、刷新OneNET平台中当前设备的状态、在线设备详情显示,打开实时刷新功能:正常状态下会每间隔一段时间刷新一下门锁控制的状态;当按键按下后,LED亮,门锁状态显示为true;当按键抬起后,LED灭,门锁状态为false。


五、附件
软件工程源代码: MM32F031F6P6_NB-IoT(BC26).zip (448.38 KB, 下载次数: 30)
原理图: 原理图.zip (1.39 MB, 下载次数: 27)
产品图: 高清产品图.zip (4.18 MB, 下载次数: 24)
MQTT.fx软件:https://pan.baidu.com/s/1PWnCO30hujk2tswt-6VOxg      提取码:1234
OneNET token工具: token.zip (11.04 MB, 下载次数: 34)

打赏榜单

21小跑堂 打赏了 150.00 元 2022-06-02
理由:恭喜通过原创文章审核!请多多加油哦!

评论

简介详细介绍了OneNET Studio创建产品和添加设备的操作步奏,以MM32为控制核心,通过USART与BC26模块进行通信,通过AT指令与模块交互,使BC26以MQTT的方式与服务器通信。AT指令简单易用,同时也达到了控制灯的目的。  发表于 2022-6-2 10:41
www5911839 发表于 2022-6-1 15:36 | 显示全部楼层
大佬,NB_Plus,写得太好了

 楼主| xld0932 发表于 2022-6-1 15:37 | 显示全部楼层
www5911839 发表于 2022-6-1 15:36
大佬,NB_Plus,写得太好了

谢谢
caigang13 发表于 2022-6-2 08:30 来自手机 | 显示全部楼层
写的太详细了,帮顶一下。
 楼主| xld0932 发表于 2022-6-2 11:10 | 显示全部楼层
caigang13 发表于 2022-6-2 08:30
写的太详细了,帮顶一下。

谢谢
onlycook 发表于 2022-6-27 15:44 来自手机 | 显示全部楼层
大佬就是大佬,牛X克拉斯
tpgf 发表于 2022-7-3 11:01 | 显示全部楼层
我总感觉自己周边物联网涉及不大啊
nawu 发表于 2022-7-3 12:31 | 显示全部楼层
现在常用云平台都有哪些家啊
aoyi 发表于 2022-7-3 12:42 | 显示全部楼层
太高端了 很多都没有接触过
zljiu 发表于 2022-7-3 12:55 | 显示全部楼层
这个平台可以免费使用吗
 楼主| xld0932 发表于 2022-7-3 13:01 | 显示全部楼层
aoyi 发表于 2022-7-3 12:42
太高端了 很多都没有接触过

跟着一步一步操作,很容易上手的
gwsan 发表于 2022-7-3 13:15 | 显示全部楼层
有些工具我都没有用过
tfqi 发表于 2022-7-3 13:32 | 显示全部楼层
现在的云平台挺多哈
robertesth 发表于 2022-7-8 09:41 | 显示全部楼层
这个通信的速度怎么样   
pentruman 发表于 2022-7-8 10:32 | 显示全部楼层
如何下行数据呢   
lzmm 发表于 2022-7-8 15:38 | 显示全部楼层
这个来学习一下。  
wengh2016 发表于 2022-7-8 16:30 | 显示全部楼层
OneNET怎么搭建的
averyleigh 发表于 2022-7-9 12:02 | 显示全部楼层
可以跟阿里云通信吗  
juliestephen 发表于 2022-7-9 12:48 | 显示全部楼层
使用wifi可以吗  
elsaflower 发表于 2022-7-9 13:40 | 显示全部楼层
物联网是以后的应用趋势了。  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:King.Xu

77

主题

3023

帖子

38

粉丝
快速回复 返回顶部 返回列表