物联网实战--驱动篇之(九)NB-IOT(BC260)

news2024/10/6 12:34:28

目录

一、NB-IOT简介

二、NB-IOT要素

三、代码详解

四、平台端


一、NB-IOT简介

        实际上,就是NB-Iot彻底引爆了物联网的,大概2018年左右,NB推广如火如荼,同时广域网、低功耗的LPWAN网络也逐渐传开,现在回头来看,还是有些过火了,实际上还是得结合市场的。目前NB的主要市场还是在市政领域,水表、气表、停车场地磁等等,基本上是政企推动的;技术上,NB和LoRa有交集,但是更多的互补关系。NB在推广上主要的是电信,移动也算还好,联通就比较少看到了,因为NB基站需要重新部署,涉及到投入产出的问题,就目前来看,电信应该县级城市都有覆盖了。

        在使用上,我之前主要还是使用电信的,电信刚开始只有电信IOT平台,开账号得企业,比较麻烦,说实话也不好用,后面电信又出了个AEP平台,这个整体体验还不错,所以我们这里也以AEP为主,驱动程序也主要还是框架,像移动的onenet和阿里平台还没具体实现,有时间再慢慢完善。AEP网址在这儿,自己去注册https://sso.ctwing.cn/login?service=https%3A%2F%2Fwww.ctwing.cn%2Flogin%2Fcas#/

        

二、NB-IOT要素

        NB要素主要有以下几个:

IMEI--模块识别码,平台一般当作身份信息;

ICCID--20位的卡号,装NB卡了才有;

NB卡--这个需要联系客户经理给你开卡,跟我们平时的4G卡外形一样,但有本质区别,不能通用,其中电信NB卡一般是按次收费,一般2万次/年,算下来差不多半小时发送一次,够用了,移动一般是一年360M,具体套餐由费用决定,要跟客户经理商谈;

模块费用:我们这里的模块型号是移远的BC260Y,支持电信跟移动,模组+外设元器件估计也是要20+的,我这里购买成品模块,36块,NB卡10块,购买可以参考这里https://item.taobao.com/item.htm?_u=kpfmfmg80d4&id=675137321790&skuId=5031188443830&spm=a1z09.2.0.0.67002e8d7mF2cG

低功耗:这里暂时没有涉及低功耗,因为这个最好用具体项目讲解比较好理解,NB的低功耗比较特别,不仅跟模块配置有关,还跟NB卡有关,不同的APN对应不同的低功耗水平,简单讲就是唤醒后多久进入睡眠是卡决定的,不过这里不同厂家可能不一样,仅做参考。

三、代码详解

        整个流程跟4G类似,差别在于,NB一般对接中间平台,如电信AEP、移动onenet,在平台上编辑插件对设备消息进行解析,解析完后推送给你自己的平台,整个流程也没有很轻松,刚开始电信的NB只能对接电信IOT平台,刚开始bug又很多,使用起来确实不太畅快。

        硬件连接上,使用串口2,波特率是9600,我采购的模块电源是3.3V的,STM32的PA2接模块RX,PA3接模块TX。

        这里的流程需要根据不同的平台分开定义,我们先看AEP的,在网络注册流程中,只要卡没问题,模块会自己注册,我们只要不断查询注册状态即可,注册成功一般会返回

+CEREG: 0,1

主要是后面的1这个状态值,有时候是5,是这两个值就可以认为注册成功了。

网络没问题之后,就要根据不同平台走连接流程了,AEP相对比较简单,如下图所示,IP和端口都是固定的,这里用的是LWM2M协议,COAP的端口是5682,连接成功后就可以进行数据收发了。


/*		
================================================================================
描述 : 注册流程
输入 : 
输出 : 
================================================================================
*/
void drv_bc260_reg_process(void)
{
  static u32 last_sec_time=0, wait_time=2;
  u32 now_sec_time=drv_get_sec_counter();
  if(now_sec_time-last_sec_time>wait_time)
  {
    switch(g_sBc260Work.state)
    {
      case BC260_STATE_START:
      {
        drv_bc260_send_at("QRST=1");//复位模块  
        g_sBc260Work.state=BC260_STAT_INIT;
        wait_time=5;
        break;
      }
      case BC260_STAT_INIT:
      {
        drv_bc260_send_at("CSQ");
        delay_os(200);
        drv_bc260_send_at("QSCLK=0");//禁用休眠模式
        delay_os(200);
        drv_bc260_send_at("CSCON=1");//使能信令连接状态上报
        g_sBc260Work.state=BC260_STAT_CEREG;
        wait_time=2;
        break;
      }    
      case BC260_STAT_CEREG://网络注册状态
      { 
        drv_bc260_send_at("CEREG?");//查询
        wait_time=2;
        break;
      }       
      case BC260_STAT_AEP_INIT:
      { 
        drv_bc260_send_at("NNMI=1");//将数据接收模式设置为直吐模式
        delay_os(200);
        drv_bc260_send_at("NCFG=0,86400");//配置生命周期时间,秒
        g_sBc260Work.state=BC260_STAT_AEP_CONNECT;
        wait_time=2;
        break;
      }
      case BC260_STAT_AEP_CONNECT://连接到服务器
      { 
        drv_bc260_send_at("NCDPOPEN=\"221.229.214.202\",5683");
        wait_time=3;
        break;
      }     

      case BC260_STAT_OK:
      { 
        if(g_sBc260Work.imei_buff[0]==0)
        {
          drv_bc260_send_at("CGSN=1");
          delay_os(200);
        }
        else if(g_sBc260Work.iccid_buff[0]==0)
        {
          drv_bc260_send_at("QCCID");
          delay_os(200);          
        }
        else
        {
          drv_bc260_send_at("CSQ");
        }
        
        wait_time=5;
        break;
      }       
    }
    
    last_sec_time=drv_get_sec_counter();
  }
  
}

        数据解析也是匹配关键字,具体如下:


/*		
================================================================================
描述 : 十六进制字符串转字节数组
输入 : 
输出 : 
================================================================================
*/
void hex_str_to_array(char *hex_str, char *out_array, u16 array_len)
{
  for(u16 i=0; i<array_len; i++)
  {
    sscanf(hex_str+i*2, "%02hhX", &out_array[i]);
  }
}

/*		
================================================================================
描述 : 接收处理
输入 : 
输出 : 
================================================================================
*/
void drv_bc260_recv_process(void)
{
	u16 recv_len;
	char *pData=NULL;
	
	if(g_sBc260Work.pUART->iRecv>0)
	{
		recv_len=0;
		while(recv_len<g_sBc260Work.pUART->iRecv)
		{
			recv_len=g_sBc260Work.pUART->iRecv;
			delay_ms(5);
		}
		char *pBuff=(char*)g_sBc260Work.pUART->pBuff;
		printf("bc260 recv=%s\n", g_sBc260Work.pUART->pBuff);
    
    if( (pData=strstr(pBuff, "+NNMI: "))!=NULL )//AEP接收到数据
    {
      pData+=strlen("+NNMI: ");
      u16 data_len=atoi(pData);
      if( (pData=strstr(pData, ","))!=NULL && data_len<100)
      {
        pData+=1;
        char data_buff[100]={0};
        hex_str_to_array(pData, data_buff, data_len);
        printf_hex("data=", (u8*)data_buff, data_len);
        if(g_sBc260Work.fun_recv_parse!=NULL)
        {
          g_sBc260Work.fun_recv_parse((u8*)data_buff, data_len);
        }
      }
    }
    else if((pData=strstr(pBuff, "+QLWEVTIND: "))!=NULL )
    {
      pData+=strlen("+QLWEVTIND: ");
      u8 type=atoi(pData);
      if(type==3)//AEP服务器订阅成功
      {
        g_sBc260Work.state=BC260_STAT_OK;
      }
    }
    else if((pData=strstr(pBuff, "+CEREG: "))!=NULL )
    {
      pData+=strlen("+CEREG: ");
      pData+=2;
      u8 state=atoi(pData);
      if(state==1 || state==5)//网络已注册
      {
        switch(g_sBc260Work.server_type)
        {
          case BC260_SERVER_ONENET://移动OneNet
          {
            
            break;
          }
          case BC260_SERVER_CHINANET_IOT://电信IOT
          {
            
            break;
          }
          case BC260_SERVER_CHINANET_AEP://电信AEP
          {
            g_sBc260Work.state=BC260_STAT_AEP_INIT;//进入AEP的初始化
            break;
          }
          case BC260_SERVER_ALI://阿里平台
          {
            
            break;
          }          
        }
        
      }
    }    
    else if((pData=strstr(pBuff, "+CSQ: "))!=NULL )
    {
      pData+=strlen("+CSQ: ");
      g_sBc260Work.rssi=atoi(pData);
      printf("***rssi=%d\n", g_sBc260Work.rssi);
    }    
    else if((pData=strstr(pBuff, "+CGSN: "))!=NULL )
    {
      pData+=strlen("+CGSN: ");
      memcpy(g_sBc260Work.imei_buff, pData, 15);
      printf("***imei=%s\n", g_sBc260Work.imei_buff);
    }      
    else if((pData=strstr(pBuff, "+QCCID: "))!=NULL )
    {
      pData+=strlen("+QCCID: ");
      memcpy(g_sBc260Work.iccid_buff, pData, 20);
      printf("***iccid=%s\n", g_sBc260Work.imei_buff);
    }  

    UART_Clear(g_sBc260Work.pUART);
  }

}

        这里比较特殊的是,跟ESP8266和AIR780相比,NB返回的数据是十六进制的字符串,需要转换,以上的hex_str_to_array()函数可以看下,对于发送也是如此,要把字节数组转成字符串,具体如下:


/*		
================================================================================
描述 :
输入 : 
输出 : 
================================================================================
*/
void drv_bc260_send_data(u8 *buff, u16 len)
{
  if(g_sBc260Work.state!=BC260_STAT_OK)
    return;
  
	static char send_buff[200]={0};
	if(len*2+10<sizeof(send_buff))
	{
		memset(send_buff, 0, sizeof(send_buff));
		sprintf(send_buff, "AT+NMGS=%d,", len);
    for(u16 i=0; i<len; i++)
    {
      char temp_buff[5]={0};
      sprintf(temp_buff, "%02X", buff[i]);
      strcat(send_buff, temp_buff);
    }
    strcat(send_buff, "\r\n");
//    printf("send data=%s\n", send_buff);
		drv_bc260_uart_send(send_buff);	
	}  
}

        应用数据接收后依然采用回调函数的方式进行处理,自己应用层去定义和注册,跟之前的WIFI和4G都差不多。

        软件方面基本上就这些了,对于AEP平台没有很复杂,后面有时间再继续完善Onenet等其它平台的流程。应用层其实就是初始化+主程序循环了。

这里我在user_app.c里加了点测试代码,这样就可以随时发送测试了。

四、平台端

        平台端注册完后要创建产品,具体流程可以参考文档,

        注意点是协议要选LWM2M,然后添加设备的IMEI要是你测试模块的IMEI,这样才能收得到数据,整体页面如下:

        设备上线后指示灯会由灰转绿色,数据查看和指令设置如下图所示,点击后会有响应显示。

数据格式可以选择16进制,便于观察;指令下发直接输入就行了,但是下发后模块一般不会马上收到,需要在模块主动上报数据后一个区间内才收的到,这也是低功耗的代价了,以后具体项目再深入讲解。

        由上图可知,主动发送后大概过了10秒才收到刚才下发的指令。

        NB设备端的整个开发流程大概就是这样了,平台端还有个物模型,就是解析插件,用来解析数据,具体可以看教程https://www.ctwing.cn/sbgl/539#see,这里贴出以前做过的示例:

驱动程序:https://download.csdn.net/download/ypp240124016/89180826

文档资料:https://download.csdn.net/download/ypp240124016/89181042

 

本项目的交流QQ群:701889554

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1611279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flyweight 享元

意图 运用共享技术有效地支持大量细粒度的对象。 结构 其中 Flyweight描述一个接口&#xff0c;通过这个接口Flyweight可以接受并作用于外部状态。ConcreteFlyweight实现Flyweight接口&#xff0c;并作为内部状态&#xff08;如果有&#xff09;增加存储空间。ConcreteFlywe…

[Java基础揉碎]集合

目录 集合的理解和好处 数组 集合的理解和好处 继承图 ​编辑 简单实例 Collection接口和常用方法 1) add:添加单个元素 2) remove:删除指定元素 3) contains:查找元素是否存在 4) size:获取元素个数 5) isEmpty:判断是否为空 ​编辑 6) clear:清空 7) addAll:添…

Prior Knowledge-Guided Transformer for Remote Sensing Image Captioning

Prior Knowledge-Guided Transformer for Remote Sensing Image Captioning 1. 摘要 遥感图像(RSI)字幕生成旨在为遥感图像生成有意义且语法正确的句子描述。然而,相比于自然图像字幕,RSI字幕生成面临着由于RSI特性而产生的额外挑战。第一个挑战源于这些图像中存在大量物体。…

ssh-key关于authorized_keys电脑与linux互相认证

思路&#xff1a; 在A上生成公钥私钥。将公钥拷贝给server B&#xff0c;要重命名成authorized_keys(从英文名就知道含义了)Server A向Server B发送一个连接请求。Server B得到Server A的信息后&#xff0c;在authorized_key中查找&#xff0c;如果有相应的用户名和IP&#xf…

冰达ROS机器人快速使用指南

欢迎来到《冰达ROS机器人极简使用指南》 Q&#xff1a;这份教程适合谁&#xff1f; A&#xff1a;适合完全0基础新手&#xff0c;需要快速跑起来机器人的基本功能。也适合技术大佬需要快速的了解冰达ROS机器人的使用方法。 Q&#xff1a;这份教程内容很少&#xff0c;是不是…

路由器热备份

HSRP HSRP&#xff08;Hot Standby Routing Protocol&#xff09;热备份路由选择协议 HSRP是思科私有的协议&#xff0c;HSRP起到一个双网关热备份的一个目的&#xff0c;不考虑线路问题针对设备而言&#xff0c;一个设备挂了还有另外一台设备&#xff0c;所以双网关也叫双机…

动力学重构/微分方程参数拟合 - 基于模型

这一篇文章&#xff0c;主要是给非线性动力学&#xff0c;对微分方程模型参数拟合感兴趣的朋友写的。笼统的来说&#xff0c;这与混沌系统的预测有关&#xff1b;传统的机器学习的模式识别虽然也会谈论预测结果&#xff0c;但他们一般不会涉及连续的预测。这里我们考虑的是&…

URL地址解析至页面展示全过程(面试详细解答)

目录 1、解析URL 2、缓存判断 ​编辑3、DNS解析 ​编辑4、获取MAC地址 5、TCP三次握手 6、HTTP请求 7、服务器处理请求&#xff0c;返回HTTP响应 8、页面渲染 9、TCP四次挥手 10、浏览器解析HTML 11、浏览器布局渲染 1、解析URL 首先会对 URL 进行解析&#xff0c;…

C++_类型转换

文章目录 学习目标&#xff1a;1.static_cast2. reinterpret_cast3.const_cast4. dynamic_cast 学习过程1.static_cast2. reinterpret_cast3.const_cast在这里插入图片描述4. dynamic_cast 学习目标&#xff1a; 标准C为了加强类型转换的可视性&#xff0c;引入了四种命名的强…

黑马程序员Linux简单入门学习笔记

Linux介绍 内核提供系统最核心的功能&#xff0c;如: 调度CPU、调度内存、调度文件系统、调度网络通讯、调度等系统级应用程序&#xff0c;可以理解为出厂自带程序&#xff0c;可供用户快速上手操作系统&#xff0c;如:文件管理器、任务管理器、图片查看、音乐播放等 目录结构 …

在PostgreSQL中如何实现递归查询,例如使用WITH RECURSIVE构建层次结构数据?

文章目录 解决方案使用WITH RECURSIVE进行递归查询示例代码 总结 在PostgreSQL中&#xff0c;递归查询是一种非常强大的工具&#xff0c;它可以用来查询具有层次结构或树形结构的数据。例如&#xff0c;你可能会在员工-经理关系、目录结构或组织结构图中遇到这样的数据。为了处…

*Linux系统的进程和计划任务管理

目录 一、查看进程 1、程序和进程的关系 *2、ps查看静态进程信息 1&#xff09;ps aux 2&#xff09;ps -elf *3、top查看动态进程信息 4、pgrep查看进程信息 5、pstree查看进程树 二、控制进程 1、进程启动方式 2、进程的前后台调度 3、终止进程的运行 三、计划任…

VSCode搭建内核源码阅读开发环境

0. 参考链接 使用VSCode进行linux内核代码阅读和开发_vscode阅读linux内核-CSDN博客 1. 搭建Linux内核源码阅读环境 现状&#xff0c;Linux内核源码比较庞大文件非常多&#xff0c;其中又包含的众多的宏定义开关配置选项&#xff0c;这使得阅读内核源代码称为一件头疼的事。 …

Android AIDL接口

一.AlDI接口简介 AIDL&#xff08;Android Interface Definition Language&#xff09;是一种 IDL 语言&#xff0c;用于生成可以在 Android 设备上两个进程之间进行进程间通信&#xff08;IPC&#xff09;的代码。 通过 AIDL&#xff0c;可以在一个进程中获取另一个进程的数据…

Go源码--Strings库

1. 简介 strings库 存储了 一些针对 字符串的具体操作 其 代码短小精悍 可以学习到很多编程的思路 尤其是 涉及到字符串使用性能的方面&#xff0c;其源码库有好多的优秀案例可以学习。向强者对齐不一定成为强者&#xff0c;但向弱者对齐一定变为弱者。 介绍思路是先介绍 stri…

.net6项目模板

1.集成log4net 安装依赖包&#xff1a; 安装扩展依赖即可&#xff0c;已经包含了log4net依赖&#xff1a; Microsoft.Extensions.Logging.Log4Net.AspNetCore 添加日志配置文件&#xff1a; 日志配置文件属性设置为始终复制&#xff1a; 注入服务&#xff1a; #region 注入…

React【Day4】

路由快速上手 1. 什么是前端路由 一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候&#xff0c;path 对应的组件会在页面中进行渲染 2. 创建路由开发环境 # 使用CRA创建项目 npm create-react-app react-router-pro# 安装最新的ReactRouter包 …

软件设计师软考中项学习(二)之计算机系统基础知识

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 学习目标学习内容学习笔记学习总结 学习目标 计算机系统硬件基本组成 中央处理…

「51媒体」新闻媒体邀约如何进行媒体宣传(方法)

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 新闻媒体邀约进行媒体宣传是一个策略性的过程&#xff0c;旨在吸引媒体的注意力并促使其对特定事件、产品发布或企业活动进行报道。以下是一些关键步骤和策略&#xff1a; 制定媒体传播方…

Advanced RAG 04:重排序(Re-ranking)技术探讨

编者按&#xff1a;重排序&#xff08;Re-ranking&#xff09;技术在检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;系统中扮演着关键角色。通过对检索到的上下文进行筛选和排序&#xff0c;可以提高 RAG 系统的有效性和准确性&#xff0…