【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.

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