【ESP32】利用 sscanf() 字符串参数 %n 解析AT+CNUM指令

    这两天在调试即将完成的Hands Free Profile的AG部分代码,在调试与HF Client设备收发AT指令部分时遇到了AT+CNUM指令HF Client端 “解析AT指令错误”的提示。由于HFP中,AT指令参数都是存放在字符串中进行收发的,字符串的解析就至关重要,而在解掉bug的同时,我也在不断地学习。本文便介绍一个在HF Client端利用sscanf()按格式读取字符串时的一个小技巧%n。
    首先,我们来看一眼HFP 1.7.1协议关于CNUM指令的介绍。

HFP CNUM
 

    其中字符串的格式如图所示," [,,<service>] " 为option选项。在我的AG模块中并不选择发送service,所以从AG发送到HF Client端的字符内容应该为 "\r\n +CNUM: ,<number>,<type>\r\n"。 AG和HF Client的字符串解析代码如下:

/* AG 端字符串内容*/

if (is_connected(bd_addr) && (idx != BTC_HF_INVALID_IDX)) {
    tBTA_AG_RES_DATA    ag_res;
    memset(&ag_res, 0, sizeof (ag_res));
    BTC_TRACE_EVENT("cnum_response: number = %s, type = %d", number, type);
    if (number) {
        sprintf(ag_res.str, ",\"%s\",%d",number, type);
    } else {
        sprintf(ag_res.str, ",\"\",%d",type);
    }
    ag_res.ok_flag = BTA_AG_OK_DONE;
    BTA_AgResult(hf_local_param[idx].btc_hf_cb.handle, BTA_AG_CNUM_RES, &ag_res);

    这里顺便解释一下,%32[^\"]的意思是:忽略掉所有的 “ 字符并最多读取32字节的数据。 

/* HF Client端字符串解析*/
AT_CHECK_EVENT(buffer, "+CNUM:");

res = sscanf(buffer, ",\"%32[^\"]\",%hu,,%hu%n", numstr, &type, &service, &offset);
if (res < 0) {
    return NULL;
}

if (res == 0) {
    res = sscanf(buffer, ",\"\",%hu,,%hu%n", &type, &service, &offset);
    if (res < 0) {
        return NULL;
    }

    /* numstr is not matched in second attempt, correct this */
    res++;
    numstr[0] = '\0';
}

if (res < 2) {
    return NULL;
}

buffer += offset;
printf("buffer :%s, offset %d\n",buffer,offset);
AT_CHECK_RN(buffer);

当使用上述代码进行测试时,HF Client端的错误信息如图:



 

    上图打印中,buffer内为HF Client端已经去除掉 “+CNUM: ”部分剩余的打印,可以看出并不符合协议要求的CNUM指令字符串,故出现报错。经过分析,原因处在offset的数值上。offset是HF Client端在解析字符串时进行赋值的,而HF Client端代码的offset仅仅出现在了option选项(service)的后面,这就导致在并不发送service的情况下offset是一个随机值,此处为2,也就是说,HF Client方面并不认为service是一个option选项,这与协议相违背。故,应该予以修改。
 

/* 原 HF Client 代码*/
res = sscanf(buffer, ",\"%32[^\"]\",%hu,,%hu%n", numstr, &type, &service, &offset);
/* 修改后 HF Client 代码*/
res = sscanf(buffer, ",\"%32[^\"]\",%hu%n,,%hu%n", numstr, &type, &offset, &service, &offset);

    从上端代码可以看出,我在type对应的%hu后面加上了一个%n来更新offset的值,并且保持了service对应%hu后面%n和offset不便,这样就可以利用一句代码适配协议中service选项的option要求。由于sscanf()在读取数据时遵循从左至右的方向性进行,所以只需要在type后对offset进行更新(更新后为16),便可以正确地进行解析。修改后效果如下:

=====================THE END=========================

如果觉得有用,请点赞、收藏、关注、或转发给你觉得有用的人。
本帐号会不定期记录与ESP-IDF调试小技巧,或者其他功能模块介绍。

LOVE AND SHARE.  PEACE.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章