xld0932 发表于 2022-6-1 13:42

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

本帖最后由 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平台架构图OneNET Studio平台功能图

二、OneNET Studio创建产品和添加设备打开OneNET控制台首页(https://open.iot.10086.cn/console),完成账号注册与登录,在Studio使用概览栏点击前往Studio,如下图所示:
在OneNET Studio界面,左侧的菜单项目我们使用到了设备接入与管理和运维监控这2项,其它的可以后续再深入研究,在右侧提供了常用入口部分,如下图所示:
在创建一个项目时,我们需要先添加一个产品,然后在对应的产品中添加相应的设备;所以我们可以点击左侧菜单项设备接入与管理下的产品管理中的添加产品,也可以点击右侧常用入口中的添加产品,填写相应的项目名称、产品类别、接入协议等参数,最后点击确定即可。我们可以点击详情查看产品的详细信息、物模型管理等操作,如下图所示:
接下来我们点击左侧菜单项设备接入与管理下的设备管理中的添加设备,也可以点击右侧常用入口中的添加设备,填写设备名称和选择所属产品,点击确定即可。我们可以点击详情查看设备的详细信息和物模型属性值,如下图所示:

三、MQTT.fx配置并与OneNET进行交互MQTT.fx是一个MQTT客户端工具,支持Windows、Linux和Mac系统,这样可以基于PC软件的方式通过配置连接MQTT云服务器进行测试,方便了开发测试。对应的Windows版本的软件在后面附件中有下载。
MQTT.fx配置在配置与OneNET服务器进行通讯测试时,我们需要在上面添加产品的时候,将接入协议选择为MQTT,数据协议建议使用常用的OneJson;另外我们还需要设备的详情信息和OneNET的专有token工具,配置如下图所示:

[*]Profile Name可以任意取名,我们这边对应的是产品名称OneNet_BC26_001
[*]Profile Type默认选择MQTT Broker
[*]Broker Address和Broker Port需要根据接入协议有不同的设置参数,如下表所示:

连接协议地址端口说明
MQTTstudio-mqtt.heclouds.com1883非加密端口接入
MQTTSstudio-mqtts.heclouds.com8883加密端口接入
在上图中我们选用的是非加密端口接入的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。
使用菜鸟工具的在线UNIX时间戳转换(https://c.runoob.com/front-end/852/),如下图所示可以将UNIX时间戳和北京时间进行相互转换;在使用Token工具时,我们需要设定一个有效截止日期,在线转换页面上将北京时间设定为2022年12月31日23时59分59秒,然后点击转换按键,我们就得到了对应的UNIX时间戳1672502399,这个就是我们在Token工具软件上需要填充的数值。
在配置好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的订阅主题和发布主题了……
对于设备的物模型可以在产品详情中进行查看,如下图所示:
对于MQTT.fx与OneNET之间通讯的数据格式,我们可以参考物模型中的定义再结合OneJson自己来编写;但我们有一个更简便的方式来自动获取,就是使用右侧菜单项运维监控的设备调试功能:选择相应的产品,选择产品中任意一个处于离线状态的设备,点击启动调试;在启动调试后,设备的属性等信息就会列举出来,选择其中的某些项进行设置后,点击底下的属性上报或者获取按键,在右侧调试日志中就会显示出通讯数据了,我们可以直接拷贝这些数据进行调试、编程和应用。
MQTT.fx订阅主题:
MQTT.fx发布主题并查看OneNET设备属性:Lock_status -> true
MQTT.fx发布主题并查看OneNET设备属性:Lock_status -> false

四、使用MM32与BC26开发板连接OneNETBC26模块是移远于2017年发布的基于MTK平台的NB-IoT模组,基于UART串口与MM32之间使用AT指令集进行通讯交互;BC26支持普通的TCP方式与远程进行通讯,也可以通过内部集成的MQTT协议与MQTT服务器进行连接通讯。BC26的MQTT支持多版本的协议,可以通过AT指令进行配置。
MCU选用MM32F031F6P6这个型号,LQFP20的小封装形式节省了PCB产品设计尺寸,同时带有32KB的FLASH空间和4KB的SRAM空间,结合多种低功耗模式可供选择,特别适用于物联网产品中的节点设备,用于处理不太复杂的产品应用。
硬件原理图:
软件源代码:void BC26_InitBuffer(void)
{
    BC26_Length = 0;
    memset(BC26_Buffer, 0, sizeof(BC26_Buffer));
}

void BC26_SendData(uint8_t Data)
{
    UART_SendData(UART1, Data);
    while(UART_GetFlagStatus(UART1, UART_IT_TXIEN) == RESET);
}

void BC26_SendString(char *str)
{
    while(*str)
    {
      BC26_SendData(*str++);
    }
}

uint8_t BC26_AT_WaitForReply(char *Keyword)
{
    while(QUEUE_EMPTY(UART1_RX_IDX) == 0)
    {
      BC26_Buffer = QUEUE_READ(UART1_RX_IDX);

      if(strstr((char *)BC26_Buffer, Keyword) != NULL)
      {
            return 1;
      }
    }

    return 0;
}

void BC26_AT_DisplayReply(void)
{
    OLED_Clear(0x00);OLED_Display((char *)BC26_Buffer);SysTick_DelayMS(999);
}

uint32_t BC26_AT_SendCommand(char *Command, char *Response, uint32_t Timeout)
{
    if(Command != NULL)
    {
      QUEUE_INIT(UART1_RX_IDX);

      BC26_SendString(Command);
      BC26_SendString("\r\n" );
    }

    if(Timeout != 0)
    {
      BC26_InitBuffer();

      while(Timeout--)
      {
            if(BC26_AT_WaitForReply(Response) != 0)
            {
                BC26_AT_DisplayReply();   return 1;
            }

            SysTick_DelayMS(1);
      }

      return 0;
    }

    return 1;
}

void BC26_InitUART(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    UART_InitTypeDef UART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE);

    UART_StructInit(&UART_InitStructure);
    UART_InitStructure.UART_BaudRate            = 9600;
    UART_InitStructure.UART_WordLength          = UART_WordLength_8b;
    UART_InitStructure.UART_StopBits            = UART_StopBits_1;
    UART_InitStructure.UART_Parity            = UART_Parity_No;
    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
    UART_InitStructure.UART_Mode                = UART_Mode_Rx | UART_Mode_Tx;
    UART_Init(UART1, &UART_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);
    UART_Cmd(UART1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,GPIO_AF_1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void UART1_IRQHandler(void)
{
    if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)
    {
      QUEUE_WRITE(UART1_RX_IDX, UART_ReceiveData(UART1));
      UART_ClearITPendingBit(UART1, UART_IT_RXIEN);
    }
}

void BC26_StartProcess(void)
{
    BC26_AT_SendCommand("AT",         "OK", 500);

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

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

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

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

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

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

    /* 连接客户端至MQTT服务器 */
    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);

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

void BC26_Init(void)
{
    BC26_InitUART();

    BC26_StartProcess();

    TASK_Append(TASK_ID_BC26, BC26_Handler, 10000);
}

void BC26_Handler(void)
{
    static uint8_t Control = 0;
    uint8_t Result = 0;

    if(Control == 0)
    {
      Control = 1;
      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);
    }
    else
    {
      Control = 0;
      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);
    }

    if(Result != 0)
    {
      OLED_Display("Control Publish OK");
    }
    else
    {
      OLED_Display("Control Publish Error");
    }
}

void BC26_KEY_Handler(uint8_t Flag)
{
    uint8_t Result = 0;

    /* 发布消息:设备属性上报 */
    if(Flag)
    {
      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);
    }
    else
    {
      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);
    }

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

五、附件软件工程源代码:原理图:产品图:MQTT.fx软件:https://pan.baidu.com/s/1PWnCO30hujk2tswt-6VOxg      提取码:1234 OneNET token工具:

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

物联网是以后的应用趋势了。
页: [1] 2
查看完整版本: 手把手教你基于MM32与BC26搭建OneNET云平台应用