移远NB模组(BC26)OPENCPU开发——MQTT上传接收
前言
NB模组在广域物联网领域发挥越来越重要的作用,有些NB模组有一个特性:OPENCPU。这个是降低成本和减少布板空间的利器。这里就以移远通信的NB模组——BC26模组来开发OPENCPU应用。
开发环境简介
移远的OPENCPU开发环境是GCC加APP烧录工具;虽然有优势,但是开发上做了限制,有些库和功能不能使用。
这里贴一个工程的早期demo的Git,完成了MQTT的连接上传接收等的URC的处理,一些外设的测试:
https://github.com/JetLinWork/BC26_MQTT_OPENCPU
这边做了OPENCPU的应用开发之后的一点经验和看法。
硬件上:虽然单模组的布板空间有所缩小,但没有想象那么多,因为模组电平是1.8V的域,基本的IO输出和外设交互都需要电平转换,这里占用空间就不少(分立或集成)。电源的驱动上确实低了2G模组很多,现在一个USB就能带起来。
软件上:在windows上开发,使用的是移远提供的SDK和Flash烧录工具。按照开发手册的步骤来就可以进行开发。虽然说是使用FreeRTOS但是开放的OS的API很少,只有信号量和TASK之间的message传输。在编译上限制了 像<string.h>等库的使用,提供了部分替代函数,第三方的C库移植带来了很大的麻烦。貌似触发任务调度的方式只有等待任务间message,其他的好像不行(待确认)。
详细的开发的环境介绍和开发步骤参见Git里面的/doc/下的手册 Quectel_BC26-OpenCPU_User_Guide_V10.pdf 开发手册里面有的内容我这里就不再赘述了。
在SDK里面也提供了很多外设驱动使用的例程。
URC开发
URC ( Unsolicited Result Code )就是不同于AT指令发了一个就会马上回复一个OK/ERR这种,而是一些类似中断不定时出现不知道对应那条AT发送指令的内容,例如MQTT接收到的数据就会以URC数据的方式发送到URC的处理任务。暂时有的URC的处理有TCP/LWM2M/ONENET等有限的API,不过开放了源码,你只需要按照这些例程去开发自己想要的URC接收处理函数就可以了。
关于URC的开发的内容手册里面基本没有提及,但是这个对于功能的开发来说是至关重要的,因为官方提供的功能不可能尽善尽美,总有需要自己去开发的地方。
这里就以开发MQTT组件的URC为例,URC的接收处理源文件为 ril_urc.c。
有一个结构体数组,存储了对应URC的特征字符和对应的处理函数
//片段1
/****************************************************/
/* Definitions for AT URCs and the handler */
/****************************************************/
const static ST_URC_HDLENTRY m_AtURCHdlEntry[] = {{"\r\n+QIURC:",OnURCHandler_QIURC_DATA},{"\r\n+QLWDATARECV:",OnURCHandler_LwM2M_RECV_DATA},{"\r\n+QLWOBSERVE:",OnURCHandler_LwM2M_OBSERVE},{"\r\n+MIPLEVENT:",OnURCHandler_ONENET_EVENT},{"\r\n+MIPLOBSERVE:",OnURCHandler_ONENET_OBSERVER},{"\r\n+MIPLDISCOVER:",OnURCHandler_ONENET_DISCOVER},{"\r\n+MIPLWRITE:",OnURCHandler_ONENET_WRITE},{"\r\n+MIPLREAD:",OnURCHandler_ONENET_READ},{"\r\n+MIPLEXECUTE:",OnURCHandler_ONENET_EXECUTE},{"\r\n+QIND: \"FOTA\"",OnURCHandler_DFOTA_Hander},{"\r\n+QMTRECV:",OnURCHandler_QMTRECV_Hander}, {"\r\n+QMTOPEN:",OnURCHandler_QMTOPEN_Hander}, {"\r\n+QMTCONN:",OnURCHandler_QMTCONNECT_Hander}, {"\r\n+QMTSUB:",OnURCHandler_QMTSUB_Hander},{"\r\n+QMTSTAT:",OnURCHandler_QMTSTAT_Hander},
};//片段2
/*****************************************************************
* Function: OnURCHandler
*
* Description:
* This function is the entrance for Unsolicited Result Code (URC) Handler.
*
* Parameters:
* strURC:
* [IN] a URC string terminated by '\0'.
*
* reserved:
* reserved, can be NULL.
* Return:
* The function returns "ptrUrc".
*****************************************************************/
void OnURCHandler(const char* strURC, void* reserved)
{s32 i;if (NULL == strURC){return;}// For system URCsfor (i = 0; i < NUM_ELEMS(m_SysURCHdlEntry); i++){if (Ql_strstr(strURC, m_SysURCHdlEntry[i].keyword)){m_SysURCHdlEntry[i].handler(strURC, reserved);return;}}// For AT URCsfor (i = 0; i < NUM_ELEMS(m_AtURCHdlEntry); i++){if (Ql_strstr(strURC, m_AtURCHdlEntry[i].keyword)) //这里处理接收到的指定URC{m_AtURCHdlEntry[i].handler(strURC, reserved);return;}}// For undefined URCsOnURCHandler_Undefined(strURC, reserved);
}
例如接收到的URC字符串包含"\r\n+QIURC:"字段,就执行OnURCHandler_QIURC_DATA(const char* strURC, void* reserved);函数处理对应URC;
static void OnURCHandler_QIURC_DATA(const char* strURC, void* reserved)
{/*----------------------------------------------------------------*//* Local Variables *//*----------------------------------------------------------------*/u8* p1 = NULL;s32 ret;u8 strTmp[10];u32 recv_length = *(char*)reserved;p1 = Ql_strstr(strURC, "+QIURC:");p1 += Ql_strlen("+QIURC: ");recv_length -= (Ql_strlen("+QIURC: ") + 2); // two means head '\r\n'char* param_buffer = (u8*)Ql_MEM_Alloc(SOCKET_RECV_BUFFER_LENGTH);char* param_list[20];/*----------------------------------------------------------------*//* Code Body *//*----------------------------------------------------------------*/if (p1){extern bool recv_data_format; u32 param_num = open_socket_push_json_param_parse_cmd(p1, recv_length, param_buffer, param_list, 20); char* prefix = param_list[0]; // recvQl_memset(strTmp, 0x0, sizeof(strTmp));ret = QSDK_Get_Str(p1,strTmp,0);if(Ql_memcmp(strTmp,"\"recv\"",Ql_strlen("\"recv\"")) == 0){socket_recv_param.connectID = Ql_atoi(param_list[1]);socket_recv_param.recv_length = Ql_atoi(param_list[2]);if ( param_num == 4){char* recv_buffer = param_list[3];Ql_memset(socket_recv_param.recv_buffer, 0x0, SOCKET_RECV_BUFFER_LENGTH);if ( recv_data_format == 0 ) //text{Ql_memcpy(socket_recv_param.recv_buffer, recv_buffer, socket_recv_param.recv_length);}else if ( recv_data_format == 1 ) //hex{Ql_memcpy(socket_recv_param.recv_buffer, recv_buffer, socket_recv_param.recv_length*2);}socket_recv_param.access_mode = SOCKET_ACCESS_MODE_DIRECT;}else {socket_recv_param.access_mode = SOCKET_ACCESS_MODE_BUFFER;}Ql_OS_SendMessage(URC_RCV_TASK_ID, MSG_ID_URC_INDICATION, URC_SOCKET_RECV_DATA, &socket_recv_param);}else if(Ql_memcmp(strTmp,"\"closed\"",Ql_strlen("\"closed\"")) == 0){Ql_memset(strTmp, 0x0, sizeof(strTmp));QSDK_Get_Str(p1,strTmp,1);socket_recv_param.connectID = Ql_atoi(strTmp);Ql_OS_SendMessage(URC_RCV_TASK_ID, MSG_ID_URC_INDICATION, URC_SOCKET_CLOSE,socket_recv_param.connectID);}}if ( param_buffer != NULL ){Ql_MEM_Free(param_buffer);param_buffer = NULL;}
}
自己写URC其实也就是依样画葫芦,将所需要的接收字段的特征字符和处理函数写好即可,可以参见给出的Git工程。
OPENCPU执行AT指令
其实OPENCPU的执行AT命令也是极为重要的功能,一下是该函数的片段,调用AT指令其实除了返回的结果有些URC的处理也是其一部分。这个函数相当于是涵盖所有AT手册包含的命令。
/******************************************************************************
* Function: Ql_RIL_SendATCmd
*
* Description:
* This function implements sending AT command with the result
* being returned synchronously.The response of the AT command
* will be reported to the callback function,you can get the results
* you want in it.
*
* Parameters:
* atCmd:
* [in]AT command string.
* atCmdLen:
* [in]The length of AT command string.
* atRsp_callBack:
* [in]Callback function for handle the response of the AT command.
* userData:
* [out]Used to transfer the customer's parameter.
* timeOut:
* [in]Timeout for the AT command, unit in ms. If set it to 0,
* RIL uses the default timeout time (3min).
*
* Return:
* RIL_AT_SUCCESS,succeed in executing AT command, and the response is OK.
* RIL_AT_FAILED, fail to execute AT command, or the response is ERROR.
* you may call Ql_RIL_AT_GetErrCode() to get the
* specific error code.
* RIL_AT_TIMEOUT,send AT timeout.
* RIL_AT_BUSY, sending AT.
* RIL_AT_INVALID_PARAM, invalid input parameter.
* RIL_AT_UNINITIALIZED, RIL is not ready, need to wait for MSG_ID_RIL_READY
* and then call Ql_RIL_Initialize to initialize RIL.
******************************************************************************/
typedef s32 (*Callback_ATResponse)(char* line, u32 len, void* userData);
extern s32 Ql_RIL_SendATCmd(char* atCmd, u32 atCmdLen, Callback_ATResponse atRsp_callBack, void* userData, u32 timeOut);
移远NB模组(BC26)OPENCPU开发——MQTT上传接收相关推荐
- 移远BC20模组使用LwM2M协议接入华为IoT平台(NB-IoT专栏—进阶篇2)
目录 1.背景 2.部署华为云 3.华为云与BC20模组进行数据收发实验 1.背景 最近在做一个智慧路灯项目,构思使用STM32结合NB-IoT模组实现数据上传和联动控制,并且可以使用GPS模块上传路 ...
- OpenHarmony3.1适配移远EC20模组4G上网功能
OpenHarmony3.1适配移远EC20模组4G上网功能 一.概述 通过阅读本篇文档,您将学习到如何适配移远EC20模组到OpenHarmony3.1(以下简称OHOS),并添加4G上网功能. 本 ...
- 移远BC35-G模组(NB-IoT 通信模组)AT指令测试 UDP 通信过程
移远BC35-G NB-IoT模组 BC35-G 是一款高性能.低功耗的多频段 LTE Cat NB1 (NB-IoT) 无线通信模块,支持 B1/B3/B8/B5/B20/B28 频段,尺寸仅为23 ...
- 移远 EC200S 模组(4G Cat.1 通信模组)AT指令测试 TCP/UDP 通信过程
移远EC200S 4G Cat.1 模组 EC200S-CN 是移远通信推出的LTE Cat 1 无线通信模块,支持最大下行速率10Mbps 和最大上行速率5Mbps,具有超高的性价比. 同时在封装上 ...
- 移远5G模组RM500U-CN在Win11下的短信和通话演示
关键词:移远 5G 展锐芯片组 RM500U-CN 短信 text 通话 拨号 概述:5G模组主要用于跑数据流量,但其实基础的短信功能也还是支持的.而且,虽然没有音频接口,但是呼入呼 ...
- 使用移远EC200N-CN模组PING谷歌
目录 概述 AT指令 数据处理 提取数据 注意事项 概述 本文记录下使用EC200N-CN模组ping谷歌官网的测试过程.ping谷歌主要是摸底下设备在海外的联网丢包.延迟等情况.其实主要是为了记录下 ...
- 移远BC35-G模组通过LWM2M协议接入OneNet教程
首先平台配置: 1.注册OneNet账号(通过访问OneNet官网进入注册): 2.进入控制台,选择"全部产品服务"--"NB-IoT物联网套件": 3.添加产 ...
- 树莓派4+lede+移远5G模组RM500Q
0. 环境 win10 树莓派4 + lede rm500q // 内核文件夹 // /home/xxjianvm/lede/build_dir/toolchain-aarch64_cortex-a7 ...
- 移远RM500Q_5G模组规格书
Quectel RM500Q是一个5G模块,专门为IoT/eMBB应用程序优化. 采用3GPP Rel. 15LTE技术,它支持5GNSA和SA模式. 设计在一个M.2的形式因素,RM500Q是 兼容 ...
- NB模组(BC28/NB86-G)使用域名接入华为云方法
现象 截止目前(2020-05-21),移远NB模组BC28在使用域名的情况下无法接入华为云平台,利尔达的NB模组NB86-G使用域名接入未测试. 方法 通过AT指令进行域名解析,得到IP后使用IP接 ...
最新文章
- MyBatis架构分层
- java bio_Java BIO及实现
- 京东网络开放之路——自研交换机探索与实践
- ASP.NET MVC Display Mode 移动端视图 配置对微信内置浏览器的识别
- couchdb 任意命令执行漏洞 cve-2017-12636
- SQL SERVER 通用分页存储过程
- How to change in the Cocos2d-x project from landscape to portrait both in iOS and Android
- error LNK2001: unresolved external symbol _WinMain@16
- 基础学习——C语言递归解决分鱼问题
- mysql 5.5 1366错误_laravel5.3 在 mysql5.1中运行出错 error: 1366 Incorrect integer
- P53:进化了8亿年的抑癌基因
- web前端前景近几年怎么样,是否饱和?
- HDU 3669 Cross the Wall(斜率DP+预处理)
- java 异常哪个包,这个提示包不存在的异常是咋回事
- 服务器搭建网站完整教程(宝塔面板+wordpress) 快速搭建网站 一键部署
- 关于setTimeout的面试题
- 【收藏】韦东山嵌入式Linux课程梳理|随时更新
- GB35114—③、证书和密钥要求、基本功能要求及性能要求
- iOS 9键盘类型合集
- 面向视频领域的边缘计算白皮书
热门文章
- 线性回归模型-误差分析
- 改进平滑滚动,修改音量调节级数实现音量微调【编译自XDA 适用于大部分设备】
- Number of ways to split should evenly divide the split dimension, but got split_dim 3 (size = 4) and
- 1.1 PMBOK指南的目的 -- 项目管理知识体系指南(PMBOK指南)(第五版)
- 深入理解各种图片格式
- 逆糖计划教大家一个轻松控糖的口诀
- 普通程序员如何正确学习人工智能方向的知识?
- win10商店打不开_win10应用商店的卸载和安装
- ecshop mysql 标题表_ECSHOP商城全站自定义TITLE标题设置
- ctf:xls加密_加密:爱丽丝和鲍勃的故事