AT组件设置
路径:RT-Thread Settings / 组件 / 网络 / AT命令
【使能调试日志的输出】:开启后可以看到日志级别为debug的相关日志。
【使能打印通信数据的RAW格式】:开启后可以看到每次执行的 AT 指令以及返回的执行结果。下图为开启后,运行中的打印数据左侧为原始hex数据,右侧为hex数据的ASCII形式
使用建议:
建议在开发、调试、故障排查、故障复现时开启【使能打印通信数据的RAW格式】
由于远程升级过程中数据量巨大且这里是同步打印,会严重影响远程升级功能,发布正式版本、无人值守的现场调试版本必须关闭此选项
AT软件包设置
路径:RT-Thread Settings / 软件包 / IoT-物联网 / AT设备 / 移远EC20
【使能在线程中初始化】:
关闭:AT初始化将在RT-Thread的main函数前,系统初始化中进行,这种情况下AT初始化将非常耗时(初始化失败还会重试5次),从设备上电到程序正常运行(屏幕点击可操作)约需要2分钟。
开启:AT初始化需要自己创建线程进行,从设备上电到程序正常运行耗时短,但是这样会与应用程序中判断离线的逻辑存在一定的冲突。
【电源引脚、电源状态引脚】-1表示不配置,建议不使用软件包控制引脚,最好是在在应用程序中配合应用层联网逻辑控制4G模组得电断电
GPRS网络注册状态检查
AT组件中会自动创建一个网络注册状态检查线程,使用 “AT+CGREG?” 指令进行GPRS网络注册状态的检查,并根据指令返回的结果修改网卡设备的标志位。
线程的入口函数是 ec20_check_link_status_entry(packages/at_device-v2.0.4/class/ec20/at_device_ec20.c),该线程每间隔 EC20_LINK_DELAY_TIME毫秒(当前版本为30s)发送一条 AT 指令进行 GPRS 网络注册状态的检查,并根据返回的结果在函数 netdev_low_level_set_link_status() 中修改 netdev->flags。各种情况的执行结果分析如下所示
上次状态 | 本次状态 | 操作 |
未注册 | 未注册 | 无 |
未注册 | 注册 | netdev->flags |= NETDEV_FLAG_LINK_UP 执行check_netdev_internet_up_work()函数测试联网 |
注册 | 未注册 | netdev->flags &= ~NETDEV_FLAG_LINK_UP netdev->flags &= ~NETDEV_FLAG_INTERNET_UP 若开启网络自动切换,则执行通过check_netdev_internet_up_work()函数切换到默认网络设备 |
注册 | 注册 | 无 |
使用建议:
建议关闭,这个线程仅能判断网络注册状态,无法判断TCP连接状态,应用层现有检测离线的逻辑(连续90s未收到数据、连续5帧发送失败(每帧重发3次)等)能够满足使用需求。
关闭方式:
在文件:packages / at_device-v2.0.4 / class / ec20 / at_device_ec20.c
找到函数:void ec20_init_thread_entry(void *parameter)
屏蔽行:ec20_netdev_check_link_status(device->netdev);
连接外网检查,实际使用一定要屏蔽
rt-thread/components/net/sal_socket/src/sal_socket.c 中的 check_netdev_internet_up_work() 函数会自动与 “link.rt-thread.org:8101” 创建UDP连接,进行数据收发测试,从而判断是否可以连接外网。
该函数的执行的过程大致为在 ec20 初始化线程 ec20_init_thread_entry() 中将将外网检查任务提交到 sys_work 工作队列中,系统工作队列处理线程 _workqueue_thread_entry() 会不断的检测是否有需要运行的任务,如果有则执行相应的任务。
建议关闭,这个连接外网检查通过会置网络设备状态为“成功连接至互联网”,由于这个IP的问题,使用定向卡的设备会检查失败,从而影响后续收发的逻辑
关闭方式:
在文件:rt-thread/components/net/sal_socket/src/sal_socket.c
找到函数:
static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)
修改为直接置标志位为成功,如下:
static void check_netdev_internet_up_work(struct rt_work *work, void *work_data)
{
struct netdev *netdev = (struct netdev *)work_data;
netdev->flags |= NETDEV_FLAG_INTERNET_UP;
return;
}
数据接收流程
解析+QIURC时,存在无法正常解析数据的情况,需要增加以下兜底方式,实测有效。
数据接收流程中的修改:
文件:packages / at_device-v2.0.4 / class / ec20 / at_socket_ec20.c
函数:
static void urc_recv_func(struct at_client *client, const char *data, rt_size_t size)
行:
sscanf(data, "+QIURC: \"recv\",%d,%d", &device_socket, &bfsz);
此行作用是解析需要接收的数据长度。
从网络接收数据时,数据会以如下格式直接输 出到 COM 口上:
+QIURC: "recv",<connectID>, <currectrecvlength><CR><LF><data>
RT-Thread中的接收逻辑:
- 解析<currectrecvlength>数据,获取有效数据的字节数
- 根据字节数来接收数据
因此,如果这个<currectrecvlength>解析出现错误,则接收不到正确的串口数据,导致应用层无法处理数据。
为此,增加了两种不同的<currectrecvlength>解析方式:
- 数逗号:<currectrecvlength>一定是在第二个数字的位置,即第一个逗号后
- 直接解析对应位置的字符:由于我们所涉及的<connectID>仅一个,不涉及2位数及以上的情况,因此可以确定
sscanf(data, "+QIURC: \"recv\",%d,%d", &device_socket, &bfsz);
if (0 == bfsz && size)
{
LOG_E("[xx]urc_recv_func: size is %d, data is:%s", size, data);
LOG_E("get bfsz is 0, retry");
char *result = NULL;
int count = 0;
int start_urc_len = -1;
int urc_len = rt_strlen(data);
for (int i = 0; i < urc_len; i++)
{
if (data[i] == ',')
{
count++;
if (count == 2)
{
start_urc_len = i;
}
}
}
if (start_urc_len != -1)
{
result = (char *)rt_malloc(sizeof(char) * (urc_len - start_urc_len + 2));
rt_strncpy(result, data + start_urc_len + 1, urc_len - start_urc_len);
result[urc_len - start_urc_len] = '\0';
bfsz = atoi(result);
rt_free(result);
}
LOG_E("[xx]urc_recv_func: (manual parsing)bfsz is %d, data is:%s", bfsz, data);
if (bfsz == 0)
{
if (data[17] >= '0' && data[17] <= '9' && data[18] >= '0' && data[18] <= '9')
{
bfsz = (data[17] - '0') * 10 + (data[18] - '0');
LOG_E("[ZGX]urc_recv_func: (manual parsing)bfsz is 0, second is %d", bfsz);
}
}
}