中国移动ML302模组(4G Cat.1 通信模组)TencentOS-tiny AT模组框架适配
关于AT模组框架,之前学习TencentOS-tiny是有写过一篇文章的,链接如下:
还在用传统的方式驱动一个通信模组?不如一起来学习下TOS的AT模组框架吧!
最近查看了TencentOS-tiny官方仓库,发现device目录下并没有ML302这个模组的驱动,于是我就利用这个机会学习了模组驱动编写与AT模组框架适配,期间遇到了一些技术疑惑请教了世伟兄(mculover666),最后顺利的将代码提交到了TencentOS-tiny官方仓库;能够得以顺利的完成,得感谢世伟兄提供技术支持。
在此之前,我已经给TencentOS-tiny
和TencentOS-Tiny-IoT-Projects
贡献了一些项目,如下所示,有兴趣可持续关注并提ISSUE。
ML302 4G Cat.1模组资料仓库地址:
https://gitee.com/morixinguan/open-source-of-cloud/tree/master/04.4G_Cat.1
ML302 TencentOS-tiny驱动仓库地址:
https://github.com/Tencent/TencentOS-tiny/tree/master/devices/ml302
ML302 4G Cat.1小熊派测试例程仓库地址:
https://github.com/Tencent/TencentOS-tiny/tree/master/board/BearPi_STM32L431RC/KEIL/tcp_through_ml302_4g_cat_1_module
https://github.com/Tencent/TencentOS-tiny/tree/master/examples/tcp_through_ml302_4g_cat_1_module
TencentOS-tiny官方仓库地址:
https://gitee.com/Tencent/TencentOS-tinyhttps://github.com/Tencent/TencentOS-tiny
TencentOS-Tiny-IoT-Projects官方仓库地址:
https://gitee.com/Tencent/TencentOS-Tiny-IoT-Projects
在使用AT框架适配模组前,需要先用串口终端测试一下,于是我们找到了模组 的通信流程示例手册:
接下来根据手册要求发送以下指令来进行上电初始化,如果初始化流程没有问题的话,接下来就可以和远程服务器进行通信了,这里的测试平台我用的是中国移动OneNet。
1、移动4G Cat.1 ML302上电初始化流程
1.1、测试模块初始化正常
AT
OK
1.2、查询SIM卡状态
AT+CPIN?
+CPIN: READY
OK
1.3、查询协议栈是否打开
AT+CFUN?
+CFUN: 1
OK
1.4、查询当前信号质量
AT+CSQ
+CSQ: 31,99
OK
1.5、设置 GPRS的APN
AT+CGDCONT=1,'IP','CMIOT'
OK
1.6、激活PDP
AT+CGACT=1,1
+CGACT: 1,1
OK
2、建立TCP连接
测试连接移动OneNet云平台==>透传模式
2.1、进入透传模式
AT+MIPMODE=0,1
OK
2.2、连接服务器
AT+MIPOPEN=0,'TCP','183.230.40.33',80
OK
CONNECT OK
2.3、发送HTTP透传数据
POST /devices/604618155/datapoints?type=3 HTTP/1.1api-key:NGSRfMY43gLebqieuaX4UfPgoac= Host:api.heclouds.com Content-Length:11
{'ppm':100}
以下为接收到服务器的信息回复HTTP/1.1 200 OKDate: Mon, 22 Mar 2021 09:00:30 GMTContent-Type: application/jsonContent-Length: 26Connection: keep-aliveServer: Apache-Coyote/1.1Pragma: no-cache
{'errno':0,'error':'succ'}
2.4、退出数据模式,此时连接依然保持
+++
2.5、关闭连接,释放资源
AT+MIPCLOSE=0
0,CLOSE OK
2.6、退出透传模式
AT+MIPMODE=0,0
OK
我的串口调试助手显示如下:
为了调试方便,这里我用是透传数据的方式来上传数据到OneNet。
3、TencentOS-tiny AT模组框架适配
AT模组驱动位于TencentOS-tiny根目录下的device目录:
在这个目录下,已经适配了很多市面上常用的AT模组,如下所示,在这个目录下创建一个ML302的文件夹,向里面添加ML302模组驱动文件:
接下来根据其它模组的驱动套路添加对应的代码,完成ML302适配SAL层,这里我们根据官方文档描述,找到SAL
相关适配的结构体:
//枚举===>协议类型,TCP、UDPtypedef enum sal_protocol_en { TOS_SAL_PROTO_TCP, TOS_SAL_PROTO_UDP,} sal_proto_t;//需要对接SAL的接口typedef struct sal_module_st { int (*init)(void);
int (*get_local_mac)(char *mac);
int (*get_local_ip)(char *ip, char *gw, char *mask);
int (*parse_domain)(const char *host_name, char *host_ip, size_t host_ip_len);
int (*connect)(const char *ip, const char *port, sal_proto_t proto);
int (*send)(int sock, const void *buf, size_t len);
int (*recv_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
int (*recv)(int sock, void *buf, size_t len);
int (*sendto)(int sock, char *ip, char *port, const void *buf, size_t len);
int (*recvfrom)(int sock, void *buf, size_t len);
int (*recvfrom_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
int (*close)(int sock);} sal_module_t;
其中,send、recv、recv_timeout
是TCP协议使用的收发接口,而sendto、recvfrom、recvfrom_timeout
是UDP协议使用的收发接口,按照要求,实现模组对接SAL层常用的一些接口,然后将这个结构体注册到SAL上。
以init接口为例,我们要实现模组上电初始化要做的一些事情,具体指令请参考对应的手册:
static int ml302_init(void)
{
printf('Init ML302 ...\n' );
//测试模组是否上电
if (ml302_wait_ok() != 0)
{
printf('ml302 start FAILED\n');
return -1;
}
//关闭回显
if (ml302_echo_close() != 0)
{
printf('ml302 echo close FAILED\n');
return -1;
}
//测试SIM卡是否已经插上
if (ml302_pin_ready() != 0)
{
printf('ml302 pin not ready\n');
return -1;
}
//测试是否注网成功
if(ml302_gsm_network_check() != 0)
{
printf('ml302 GSM network register status check fail\n');
return -1 ;
}
//测试模组信号强度
if (ml302_signal_quality_check() != 0)
{
printf('ml302 signal quality check status failed\n');
return -1;
}
//设置APN
if (ml302_set_gprs_apn() != 0)
{
printf('ml302 set gprs_apn failed\n');
return -1;
}
//激活PDP
if (ml302_activate())
{
printf('ml302 activate FAILED\n');
return -1;
}
printf('Init ML302 Done\n' );
return 0;
}
这么多指令里面,其实包含了三类AT指令:
1、只需要判断是否返回OK的AT指令
以ml302_wait_ok
函数为例,这是第一类AT指令的实现,如下:
static int ml302_wait_ok(void){
int try = 0;
at_echo_t echo; /* 创建一个echo 对象,缓冲区为NULL,期望字符串为NULL */ tos_at_echo_create(&echo, NULL, 0, NULL); /* 尝试检测10次,一旦有一次正常,返回 */ while (try++ < 10) { /* 执行AT命令,超时时长1000ms */ tos_at_cmd_exec(&echo, 1000, 'AT\r\n'); /* 判断执行结果是否为OK */ if (echo.status == AT_ECHO_STATUS_OK) { return 0; } } return -1;}
2、需要判断是否OK,也需要解析执行结果的AT指令
以ml302_signal_quality_check
函数为例,这是第一类AT指令的实现,如下:
static int ml302_signal_quality_check(void)
{
int rssi, ber;
at_echo_t echo;
char echo_buffer[32], *str;
int try = 0;
/* 尝试检测10次,一旦有一次正常,返回 */
while (try++ < 10)
{
/* 创建echo对象,传入一个缓冲区存放AT命令执行结果 */
tos_at_echo_create(&echo, echo_buffer, sizeof(echo_buffer), NULL);
/* 执行AT命令,超时时长1000ms */
tos_at_cmd_exec(&echo, 1000, 'AT+CSQ\r\n');
/* 判断执行结果是否返回了OK */
if (echo.status != AT_ECHO_STATUS_OK)
{
return -1;
}
/* 从AT指令的执行结果中解析提取CSQ值进行判断 */
str = strstr(echo.buffer, '+CSQ:');
if (!str)
{
return -1;
}
sscanf(str, '+CSQ:%d,%d', &rssi, &ber);
if (rssi != 99)
{
return 0;
}
}
return -1;
}
3、模组主动上报的数据处理
这一类AT指令对应TCP/IP协议栈接收数据的上报机制,使用AT框架的事件机制进行处理,也就是我们前面提到的这个事件:
at_event_t ml302_at_event[] ={ { '+MIPURC: \'recv\',', ml302_incoming_data_process },};
这个对应下发处理的事件回调函数需要我们自己来实现,如ml302_incoming_data_process
,实现如下:
__STATIC__ void ml302_incoming_data_process(void)
{
uint8_t data;
int channel_id = 0, data_len = 0, read_len;
uint8_t buffer[128];
/*
//模组上报的数据格式
+MIPURC: 'recv',<sockid>,<datalen>
<data content>
*/
/* 读取解析socket id */
while (1)
{
if (tos_at_uart_read(&data, 1) != 1)
{
return;
}
if (data == ',')
{
break;
}
channel_id = channel_id * 10 + (data - '0');
}
/* 读取解析数据长度 */
while (1)
{
if (tos_at_uart_read(&data, 1) != 1)
{
return;
}
if (data == '\r')
{
break;
}
data_len = data_len * 10 + (data - '0');
}
if (tos_at_uart_read(&data, 1) != 1)
{
return;
}
/* 根据解析出的数据长度和缓冲区的长度,循环读取数据内容,写入到对应 socket id 的channel中 */
do
{
#define MIN(a, b) ((a) < (b) ? (a) : (b))
read_len = MIN(data_len, sizeof(buffer));
if (tos_at_uart_read(buffer, read_len) != read_len)
{
return;
}
if (tos_at_channel_write(channel_id, buffer, read_len) <= 0)
{
return;
}
data_len -= read_len;
}
while (data_len > 0);
return;
}
模组适配完毕后,编写对接后台例程,由于篇幅限制,详细代码请访问TencentOS-tiny官方仓库获取。
测试例程效果如下:
OneNet平台展示效果: