首页 > 解决方案 > 从完整的响应字符串中解析 A​​T 命令的有效负载

问题描述

我想从 AT 命令的输出中解析实际的有效载荷。

例如:在下面的示例中,我只想阅读"2021/11/16,11:12:14-32,0"

AT+QLTS=1                          // command
+QLTS: "2021/11/16,11:12:14-32,0"  // response

OK

在以下情况下,我只需要阅读12345678.

AT+CIMI     // command
12345678   // example response

所以重点是:并非所有命令的输出格式都相同。我们可以假设响应存储在一个字符串数组中。

GetAtCmdRsp()已经实现了将响应存储在 char 数组中。

void GetPayload()
{
  char rsp[100] = {0};
  GetAtCmdRsp("AT+QLTS=1", rsp);
  // rsp now contains +QLTS: "2021/11/16,11:12:14-32,0"
  // now, I need to parse "2021/11/16,11:12:14-32,0" out of the response
  
  memset(rsp, 0, sizeof(rsp));

  GetAtCmdRsp("AT+CIMI", rsp);
  // rsp now contains 12345678   
  // no need to do additional parsing since the output already contains the value I need
}

我正在考虑如何char *start = strstr(rsp, ":") + 1;启动有效负载,但某些响应可能只包含有效负载,因为情况就是这样AT+CIMI

也许正则表达式是确定+<COMMAND>:字符串中模式的好主意?

标签: cstringat-commandstring-parsing

解决方案


为了解析 AT 命令响应,一个好的起点是了解它们可以具有的所有可能格式因此,我不会通过“响应类型”
来区分命令,而不是实现特定于命令的例程:

  1. 例如,答案中没有有效负载的命令

     AT
     OK
    
  2. 答案中没有标题的命令,例如

     AT+CIMI
     12345678
    
     OK
    
  3. 答案中只有一个标题的命令

     AT+QLTS=1
     +QLTS: "2021/11/16,11:12:14-32,0"
    
     OK
    
  4. 具有多行响应的命令。
    每行都可以是“单标题”类型,例如+CGDCONT

    AT+CDGCONT?
    +CGDCONT: 1,"IP","epc.tmobile.com","0.0.0.0",0,0
    +CGDCONT: 2,"IP","isp.cingular","0.0.0.0",0,0
    +CGDCONT: 3,"IP","","0.0.0.0",0,0
    
    OK
    

    或者我们甚至可以有混合类型,例如+CGML

     AT+CMGL="ALL"
    
     +CMGL: 1,"REC READ","+XXXXXXXXXX","","21/11/25,10:20:00+00"
     Good morning! How are you?
    
     +CMGL: 2,"REC READ","+XXXXXXXXXX","","21/11/25,10:33:33+00"
     I'll come a little late. See you. Bruce Wayne
    
     OK
    

    (请注意它如何也有“空”行,即\r\n)。

目前我无法考虑任何其他情况。
通过这种方式,您将能够定义一个枚举,例如

typedef enum
{
    AT_RESPONSE_TYPE_NO_RESPONSE,
    AT_RESPONSE_TYPE_NO_HEADER,
    AT_RESPONSE_TYPE_SINGLE_HEADER,
    AT_RESPONSE_TYPE_MULTILINE,
    AT_RESPONSE_TYPE_MAX
}

并将其传递给您的GetAtCmdRsp( )函数,以便相应地解析响应。如果在该函数中实现微分,或者在它之后(或在外部函数中是你的选择。


没有明确分类的解决方案

一旦你清楚了所有可能发生的场景,你就可以考虑一个适用于所有场景的通用算法:

  1. resp在命令 echo 之后和关闭OKor之前获取完整响应ERROR确保\r\n\r\nOK删除尾随(或\r\nERROR. 或\r\nNO CARRIER. 或响应的终止消息可能是什么)。
    还要确保删除命令 echo

  2. 如果strlen( resp ) == 0我们属于该NO_RESPONSE类别,并且工作完成

  3. 如果响应中包含\r\ns,我们就有了MULTILINE答案。因此,对其进行标记并将每一行放入一个数组元素resp_arr[i]中。确保删除尾随\r\n

  4. 对于响应中的每一行(对于每个resp_arr[i]元素),搜索<CMD> :模式(不仅:,它也可能包含在有效负载中!)。像这样的东西:

     size_t len = strlen( resp_cur_line );
     char *payload;
    
     if( strstr( "+YOURCMD: ", resp_cur_line) == NULL )
     {
         // We are in "NO_HEADER" case
         payload = resp_cur_line;
     }
     else
     {
         // We are in "HEADER" case
         payload = resp_cur_line + strlen( "+YOURCMD: " );
     }
    

    现在payload指针指向实际的有效载荷。

    请注意,在MULTILINE回答的情况下,在将行拆分为数组元素后,每个循环将如何正确处理混合场景,例如 in 中的场景+CMGL,因为您将能够区分包含标题的行和包含数据的行(以及当然是空行)。有关+CMGL响应解析的更深入分析,请查看此答案


推荐阅读