移远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传输。在编译上限制了 像等库的使用,提供了部分替代函数,第三方的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 URCs
    for (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 URCs
    for (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 URCs
    OnURCHandler_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];   //  recv
		
		Ql_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);  

 

你可能感兴趣的:(物联网)