文章目錄
- 概述
- 指令調用順序
- 具體接入指令分析
- ATE0 關閉回顯
- AT+QREGSWT 設置(平臺)注冊模式
- AT+QLWSREGIND=0 手動注冊平臺
- set_autoconnect / AT+NCONFIG
- AT+NBAND=x,x
- set_plmn / AT+COPS
- _set_apn / AT+CGDCONT
- (安全)接入參數 CDP+DTLS+PSK
- AT+NNMI 設置新消息指示
- _check_netattach 網絡附著狀態
- AT+COPS=0 和 AT+CGATT=1
- _check_observe網絡附著狀態
- urc_qlwevtind
- 總結和后記
概述
在一個不斷探索的過程中,我們已經可以真正的將NB-IoT設備以安全的方式接入打破IoTDA平臺,包括使用PC-AT指令、使用MCU-AT指令及其數據處理代碼,也已經了解了NB-IoT-AT的實現機制。本文我們將繼續深入,詳談 NB-IoT設備是如何通過不同的AT指令,先接入運營商網絡,再接入華為云物聯網平臺的。
@HISTORY
我們已經從<實驗5 基于NB-IoT的智慧農業實驗> 的烏龍坑里爬了出來。并有了以下知識點作為后文分析的支撐知識:
0、PC-AT指令的部分設置是存儲在模組內的,即使MCU-AT不執行這些操作,也會成功連接!
1、NB-IoT通信模組通過串口以AT指令形式與MCU通信。
1、注冊NB-IoT設備時,設備標識只能是IMEI國際移動設備識別碼?Yes
2、在當下連接到IoTDA,是否必須啟用DTLS?不再支持5683非安全接入?Yes
3、NB-IoT設備密鑰格式,在代碼中以數組字符串定義,和Wifi設備不一樣?Yes
@NOTE
轉載請標明出處,https://blog.csdn.net/quguanxin/category_12929470.html
指令調用順序
在 #<IoT/實現和分析 NB-IoT+DTLS+PSK 接入華為云物聯網平臺IoTDA過程,總結避坑攻略># 文中,我們已經實現了較完整和準確的設備安全(PSK+DTLS)接入IoTDA的程序,并詳細分析了 boudica150_boot 上層的調用關系,在 boudica150_boot 函數中被循環執行的指令序列如下。結合上文以及 #<IoT/基于NB28-A/BC28-CNV通信模組使用AT指令連接華為云IoTDA平臺># 中的表述,我們將展開更細致的NB-IoT設備AT指令接入運營商網絡和接入華為云物聯網平臺過程和原理的分析。
//如下過程的外層是while(1)//Do:執行命令 AT+NRB 重啟UE 即重啟我們的NB-IoT通信模組 并延時等待10sboudica150_reboot();
//Do:執行ATE0,等待返回OK /禁用模組對接收到的AT指令的回顯(Echo)/什么是回顯下文另談boudica150_set_echo (0);
//Do: 設置UE是自動還是手動觸發以注冊IoT平臺 /這就是一個本地設置操作,些模組內存或KV? 應該會立即返回Okboudica150_set_regmode(1);
//Do:使能 +CME ERROR: <err> 結果碼 https://blog.csdn.net/quguanxin/article/details/146547709boudica150_set_cmee(1);
//Do:關閉AUTOCONNECT自動連接,本質上是想關閉CFUN射頻
//cgatt and cfun must be called if autoconnect is falseboudica150_set_autoconnect(0);
//Do:設置通信模組支持的頻帶(依據SIM卡運營商選擇,或默認選擇全部支持的頻段)/Band20并不被支持,需要修改掉boudica150_set_bands(bands);
//Do:AT+CFUN=1 使能射頻boudica150_set_fun(1);
//Do:AT+COPS=0 設置自動注冊網絡/源碼中實參plmn==NULLboudica150_set_plmn(plmn);
//Do:由于apn==NULL,呼應plmn==NULL;本質上該函數內部什么也沒干,直接返回trueboudica150_set_apn(apn);
//Do:參見PC-AT指令,設置CDP服務器boudica150_set_cdp(server,port);
//Do:開啟dtls /river.qu 202506boudica150_set_dtls(1);
//Do:設置 PSK ID 和 PSK /river.qu 202506boudica150_set_psk(s_boudica150_oc_cb.oc_param.app_server.psk_id, s_boudica150_oc_cb.oc_param.app_server.psk);
//Do:AT+CGATT=1boudica150_set_cgatt(1);
//Do:AT+NNMI=1 /使能新消息指示和數據,會返回當前所有緩存的消息boudica150_set_nnmi(1);
//Do:檢測網絡附著狀態/重復執行AT+CGATT?查詢指令if(false == boudica150_check_netattach(16)) {continue;}
//Do:等待模組程序成功創建所需要的lwm2m對象if(false == boudica150_check_observe(16)) {continue; //we should do the reboot for the nB}
@NOTE 要意識到一個事情是,以上函數內部幾乎都有等待反饋返回的過程,器本質上是在等信號量(后續其他文會繼續講述這塊)。這些返回大多是瞬時完成的,因為它們不涉及到與基站或平臺的交互,是由模組固件程序本地直接處理并完成的。
具體接入指令分析
部分指令分析請參考 #<PC-AT實驗># 。
ATE0 關閉回顯
所謂的回顯,是在主機端顯示你發送出去的AT指令。
AT+QREGSWT 設置(平臺)注冊模式
AT+QREGSWT=1 該命令用于在重啟模塊后設置注冊模式:
如果不需要使用 LwM2M 物聯網平臺,必須使用命令 AT+QREGSWT=2 禁用注冊功能。該命令只有在使用 AT+NRB 重啟 UE 后才生效,若不禁用注冊功能會使 UE 從網絡中分離,造成相關服務(例如 TCP/UDP)失敗。
連接到網絡后自動觸發注冊,這里的網絡是指?這里的注冊是像誰發起?
這里的“網絡”指 蜂窩移動網絡,具體為NB-IoT無線接入網(RAN)和核心網(EPC/5GC),包括以下關鍵節點:
基站(eNodeB/gNB):提供無線信號覆蓋。
移動管理實體(MME/AMF):負責終端接入認證和位置管理。
數據網關(PGW/UPF):提供互聯網或專網連接。
連接過程: 模組通過PLMN(公共陸地移動網絡)搜索并附著到運營商網絡,完成空口同步、鑒權、默認承載建立等流程。
而注冊對象是,LwM2M物聯網平臺(如華為OceanConnect、移動OneNET)
無論是 =0(手動注冊)、當 =1(自動注冊),其注冊對象都是物聯網平臺。手動注冊時,要通過AT+QLWSREGIND命令顯式觸發。而自動觸發的時機在,模組重啟后成功連接蜂窩網絡(完成PSM激活或eDRX喚醒),此時,模組通過CoAP協議向LwM2M服務器(如coap://lwm2m.iot.platform.com)發送Register請求,攜帶Endpoint Client Name、Lifetime等參數,服務器返回2.01 Created響應,分配注冊ID并維護在線狀態。
AT+QLWSREGIND=0 手動注冊平臺
在PC-AT指令的實驗中,我曾經有個疑問就是,我的NB-IoT是在什么時候注冊勾搭上的物聯網平臺,當時只知道這個過程自動完成了,并不知道所以然。現在大約明白了,在手動注冊模式(AT+QREGSWT=0)下,用戶要發送AT+QLWSREGIND=0 指令,然后模組的固件程序會解析此指令,將其轉換為LwM2M協議交互過程,這在固件實現內,其與自動注冊模式下的LwM2M客戶端和服務端建立連接的過程應該是同源的。這個大體過程可參考lwm2m_tiny源碼實現。
set_autoconnect / AT+NCONFIG
static bool_t boudica150_set_autoconnect(int enable) {if(enable) mode = "AUTOCONNECT,TRUE";else //源碼中傳遞的是0mode = "AUTOCONNECT,FALSE";(void) memset(resp,0,64);ret = boudica150_atcmd_response("AT+NCONFIG?\r","+NCONFIG",resp,64);//which means we need to set it if((false == ret)||(NULL == strstr(resp,mode))) {(void) memset(cmd,0,64);(void) snprintf(cmd,64,"AT+NCONFIG=%s\r",mode);ret = boudica150_atcmd(cmd,"OK");//上述設置后,需要再次軟重啟boudica150_reboot();}//如果檢測到已經設置過,則直接返回成功
在小熊派社區/或HCIP_Lab源碼中,這里的實參都是是0哈,不是1,即失能配置。此時,指令展開為:
AT+NCONFIG=AUTOCONNECT,FALSE
上述操作可能起到關閉CFUN的作用,這么猜想,主要是因為從后續代碼來看,還會有CFUN射頻開啟過程。另外猜測注釋 cgatt and cfun must be called if autoconnect is false 的含義可能是,若上述操作沒有關閉成功,則需要手動去關閉 cgatt和cfun,起初這只是猜測哈,下一小節中,這一猜測被驗證是正確的,NBAND操作時要求 CFUN 是關閉狀態。
@OTHER
NCONFIG 指令的 參數有超級多的選項,這里就不列舉了,哈哈,感覺我一年內就只能折騰這第一個選項了。通過指令手冊,結合觀察實際實驗過程中的日志輸出,我們可以看到,NCONFIG 查詢指令,會輸出 類型的設置值,我們可以先只關注AUTOCONNECT 功能類型的設置值是否為true即可。
@NOTE
我們在PC-AT實驗中并沒有使用到該指令,但是我們手動執行了 CFUN 的關閉操作過程。
AT+NBAND=x,x
在 boudica150_oc.c 源碼中的 int oc_lwm2m_imp_init(void) 函數下,bands 已經被賦值,
//實測中,如下BAND配置是設置失敗的,因為20不被我的模組支持。合適的配置是3,5,8
#define CONFIG_BOUDICA150_BANDS "5,8,20"
//from oc_lwm2m_imp_init
s_boudica150_oc_cb.bands = CONFIG_BOUDICA150_BANDS;
先看看,讓我高興的一句話,
這個備注說明了,前邊boudica150_set_autoconnect傳0,進行失能操作,試圖關閉CFUN射頻功能的猜想是對的。
所謂頻段,是指3GPP標準定義的全球蜂窩網絡頻率范圍,直接影響模組與運營商基站的通信能力,每個頻段對應特定的上行(UL)和下行(DL)頻率,確保模組與運營商基站在同一頻段上收發信號,避免干擾。如,
Band 5:上行824-849MHz,下行869-894MHz
Band 8:上行880-915MHz,下行925-960MHz
不同國家/地區的運營商根據工信部或國際頻譜管理機構的授權使用特定頻段。以中國為例:
要注意的是頻段由模組的射頻芯片(如高通MDM9205、海思Boudica 200)硬件支持,指令僅啟用/禁用特定頻段,這個事情或能力與SIM關系不大,SIM卡僅提供運營商身份認證(IMSI/Ki密鑰)和簽約數據,不參與物理頻段選擇。綜合上述分析,一個更合適的BAND配置應該是 3,5,8,而不是源碼中的那樣,但是有Band8就基本夠用了,所以早期實驗是成功的。我們通過NBAND測試指令,查詢指令等,也是可以確認我們SIM卡的頻段支持的,并確定源代碼示例中的設置過程是不生效的,
set_plmn / AT+COPS
AT+COPS指令,用來選擇和注冊 EPS 網絡運營商。PLMN(Public Land Mobile Network,公共陸地移動網絡) 是由政府或運營商建立的移動通信網絡,用于為公眾提供無線通信服務。其核心標識由 MCC(移動國家碼) 和 MNC(移動網絡碼) 組成,例如中國移動的 PLMN 為 46000,中國聯通為 46001。我們在PC-AT實驗中也是用過的。這里首先看看,我的卡是哪個運營商的呢?
AT+CIMI 請求國際移動用戶識別碼,其返回的是IMSI。
IMSI(國際移動用戶識別碼)和IMEI(國際移動設備識別碼)是兩種完全不同的標識符,分別用于識別SIM卡和物理設備。
通過 CIMI號、COPS查詢結果,都可以斷定此SIM卡是中國移動運營商的。COPS/PLMN 選擇,在老文章中有講過,
要注意的一點是,
當我們執行AT+CFUN=0關閉設備后,執行COPS查詢指令,結果為+COPS:2,即mode=2 注銷網絡。之后實驗中再執行AT+CFUN=1 打開射頻,COPS查詢結果不變,還是注銷狀態。只有執行AT+COPS=0后,才會注冊網絡,COPS才能查詢到有效信息,相關驗證過程,
AT+CFUN=0
OKAT+COPS?
+COPS:2
OKAT+CFUN=1
OK
AT+COPS?
+COPS:2
OKAT+COPS=0
OK
+QDTLSSTAT:0
+QLWEVTIND:0
+QLWEVTIND:3AT+COPS?
+COPS:0,2,"46000"
OK
接下來,我們回到boudica150源碼中,
static bool_t boudica120_set_plmn(const char *plmn) {if (NULL != plmn) {ret = boudica120_atcmd_response("AT+COPS?\r","+COPS",resp,64);...//which means we need to set itif((false == ret)||(NULL == strstr(resp,cmd))) {(void) snprintf(cmd,63,"AT+COPS=1,2,\"%s\"\r",plmn);}//HCIP-Lab源碼中,走的就是這個分支else {(void) snprintf(cmd,64,"AT+COPS=0\r");ret = boudica150_atcmd(cmd,"OK");}...
}//實際上該宏在代碼中并未被使用,//初始化函數中s_boudica150_oc_cb.plmn = NULL;
#define CONFIG_BOUDICA150_PLMN "460001"
在源碼中,BOUDICA150_PLMN,被配置為46001,這是中國聯通,好在源碼中并沒有使用,否則就又多一個烏龍。前文我們已經談到過,我使用的SIM卡其是46008中國移動物聯網號段,COPS查詢到的國家代碼和運營商代碼是46000,中國移動。
一開始我沒有注意到 NULL != plmn 的判斷,還擔心了會,因為我知道,
C99/C11標準(§7.21.6.5)和C++標準均未明確定義%s接收NULL時的行為,屬于未定義行為,這是非常危險的。
好在虛驚一場,_set_plmn處理了plmn是空的情況,且boudica150初始化函數中,使用的就是NULL。在此場景下,上述函數就是執行了一次 AT+COPS=0,這與我們在PC-AT實驗中的方式是一致的。
我們也要看懂手動模式的PLMN選擇,其3個參數如下,
_set_apn / AT+CGDCONT
該過程名為設置APN。APN 即 Access Point Name,接入點名稱。 APN是移動通信網絡中的一個關鍵參數,用于標識設備接入運營商數據網絡的方式。它決定了設備如何連接到互聯網、企業專網或其他數據服務,并影響IP地址分配、計費策略和網絡服務質量。
源碼中出現的 _APN “cmnet” 是未被使用的,即使用了也不對。CMNET 是中國移動(China Mobile)的傳統移動互聯網APN,主要用于2G/3G/4G數據業務,而非專為NB-IoT設計,可能以前是能用的,現在不行了估計。
AT+CGDCONT,我們打開BC28的AT指令使用手冊,該指令看上去非常復雜,
從設置指令格式上看,源碼中的格式似乎不咋對啊,在PC串口終端中進行嘗試,也確實總返回ERROR。好在我們的源碼中,apn字符串是被初始化為NULL的,與plmn被初始化為NULL交相呼應。在我們的源碼中,本質上_set_apn函數啥也沒干,太累不展開了。
(安全)接入參數 CDP+DTLS+PSK
參見 #<IoT/實現和分析 NB-IoT+DTLS+PSK 接入華為云物聯網平臺IoTDA過程,總結避坑攻略>#
AT+NNMI 設置新消息指示
在移遠通信的AT指令手冊中,這個指令的功能表述非常的爛,讓人不好理解,甚至是錯誤的。一個修正后的表述如下,
該命令用于配置模組在接收到LwM2M平臺下發的消息后,如何向主機(Host)發送新消息到達的指示。UE 從 LwM2M 物聯網平臺接收到一個下行消息后會發送新消息指示。這里,所謂的消息(可含數據),是指平臺到NB通信模組的消息數據,而消息指示是模組對MCU主機的一種異步通信機制。NNMI 默認值為 1,模塊重啟后 會還原到默認值。
AT+NNMI=1 使能新消息指示和數據,會返回當前所有緩存的消息,格式為 +NNMI:,。
例如,+NNMI:5,48656C6C6F。
AT+NNMI=2 僅使能新消息指示,每次收到新的消息都會觸發指示 URC,響應結果格式為:+NNMI。
可以通過命令 AT+NMGR 接收緩存消息。
_check_netattach 網絡附著狀態
上述函數的本質是在循環執行 AT+CGATT? 查詢操作,
我們在前文操作中執行了AT+CGATT=1,試圖將 MT 附著于 PS 域,這個附著過程是需要時間的,_check_netattach是在等待成功。這里重點關注上圖中紅色標注的部分,其表明在網絡附著一旦成功后,將自動執行 AT+COPS=0,即
AT+COPS=0 和 AT+CGATT=1
按照源碼中的接入流程,先被執行的是 AT+COPS=0 自動選擇和注冊 EPS 網絡運營商,之后又執行了一次 AT+CGATT=1 將 MT 附著于 PS 域,最后再循環執行AT+CGATT?查詢用戶設備網絡附著狀態。同時,我們在CGATT的指令備注中可以看到,AT+CGATT=1 的過程會自動選擇 AT+COPS=0。實際上,在NB-IoT模組的網絡注冊流程中,AT+CGATT=1 和 AT+COPS=0 的功能既有重疊又有分工。AT+CGATT=1 設置工作的第一步是檢查與COPS指令操作結果相關的EPS注冊狀態,若不符合,則會先執行 AT+COPS=0 指令。詳細過程如下:
AT+COPS=0 的獨立功能,僅完成EPS網絡注冊(無線接入網和核心網控制面),主要包含以下工作:
1、PLMN選擇:自動搜索并注冊優先級最高的運營商網絡(如中國移動46000)。
2、RRC/NAS層連接:與基站(eNodeB)和MME完成控制面信令交互(鑒權、安全模式激活)。
結果狀態對應如上兩個功能:
AT+COPS? 返回運營商名稱(如"CHINA MOBILE") AT+CEREG? 返回EPS注冊狀態(如+CEREG: 1,1表示已注冊)
AT+CGATT=1 的本職功能,即 PS 域專屬功能(扣除AT+COPS=0后)
1、PDP上下文激活:向PGW申請IP地址(依賴APN配置,如cmnbiot)。核心網建立SGW→PGW的數據通道(默認承載QCI=9)。
2、IP地址分配:動態獲取IPv4/IPv6地址(可通過AT+CGPADDR查詢)。
3、數據就緒狀態:AT+CGACT?返回1,1(默認承載已激活)。模組可進行TCP/UDP通信(仍需APN正確)。
總結下來,設備接入運營商的主要的幾個步驟,PLMN選擇、EPS注冊、PS附著、IP地址分配,大約流程如下:
其他補充:
PS域(Packet Switched Domain,分組交換域)是移動通信網絡中用于處理數據業務的邏輯域,其核心特點是通過分組交換技術實現高效的數據傳輸。PS域采用分組交換(Packet Switching)機制,將數據拆分為多個帶地址標識的“分組”(數據包),每個分組獨立傳輸并通過最優路徑到達目的地,接收端重新組裝為完整數據。PS域并非物理上的地理區域,而是邏輯上的業務承載劃分。其覆蓋范圍由核心網設備互聯構成,用戶通過無線接入網(如基站)接入PS域,實現數據業務的端到端傳輸。運營商需在核心網中部署PS域設備(如SGSN、GGSN、SGW/PGW等),并通過策略控制(如PCRF)實現流量管理、計費和服務質量(QoS)保障。用戶通過AT+CGATT命令附著PS域時,終端與核心網完成鑒權(AT+COPS的作用)、IP地址分配等流程,建立數據會話(PDP上下文),從而接入互聯網。
_check_observe網絡附著狀態
網絡附著成功后,也即 CGATT查詢到狀態1后,即數據會話(PDP上下文)已經被成功建立。接下來設備就要真正去和物聯網平臺握手交互了啦,我最初的問題是,psk、dtls等配置信息是在哪個指令環節中使用的呢?AT+QREGSWT指令只是設置了一種平臺注冊模式,并沒有真正啟動注冊過程。我們以自動觸發模式為例,結合我對lwm2m_tiny(wifi通信時使用的)的已有的部分理解,
當網絡附著CGATT成功后,NB模組內的固件程序就會,
通過CoAP協議向LwM2M服務器(如coap://lwm2m.iot.platform.com)發送Register請求,攜帶 Endpoint Client Name、Lifetime、dtls、psk 等參數。模組固件的實際代碼實現我們暫時看不到,但我猜測,其與lwm2m_tiny源碼的如下實現基本一致,
static int agent_add_objects(void *handle, atiny_param_t *lwm2m_params) {int ret = (int) ATINY_ERR;ret = agent_add_security_object(handle, lwm2m_params); // 安全對象(ID=0) //psk秘鑰用在這里哦ret = agent_add_server_object(handle, lwm2m_params); // 服務器對象(ID=1)ret = agent_add_acc_ctrl_object(handle); // 訪問控制對象(ID=2)ret = agent_add_device_object(handle); // 設備對象(ID=3)ret = agent_add_conn_m_object(handle); // 連接監控對象(ID=4)ret = agent_add_firmware_object(handle); // 固件對象(ID=5)ret = agent_add_location_object(handle); // 位置對象(ID=6)// 二進制應用數據對象(ID=19)ret = agent_add_binary_app_data_object(handle, &(lwm2m_params->server_params.storing_cnt)); return ret;
}
上述操作,添加了7種標準對象和1種擴展標準對象,具體如下表,
呢?在上述理解的基礎上,我們再回到NB源碼的 _check_observe 函數,就好理解多了,
//unit second
static bool_t boudica150_check_observe(int time) {//wait for the server observedbool_t ret = false; int times;for(times =0;times <time;times++ ) {if(s_boudica150_oc_cb.lwm2m_observe) {ret = true;break;}osal_task_sleep(1000);}return ret;
}
函數很簡單,就是在循環著檢查 s_boudica150_oc_cb.lwm2m_observe 標志字,該標記只在如下回調函數實現中賦值,
#define cn_urc_qlwevtind "\r\n+QLWEVTIND:"//wait for the lwm2m observe
static int urc_qlwevtind(void *args,void *msg,size_t len) { ...index_str = strlen(cn_urc_qlwevtind);if(len > index_str) {ind = data[index_str]-'0';LINK_LOG_DEBUG("GET THE LWM2M:ind:%d\n\r",ind);if(ind == 3) { //這是關鍵哦,s_boudica150_oc_cb.lwm2m_observe = true;}}return len;
}
urc_qlwevtind 函數是在 boudica150_boot 函數的一開始被注冊的,它是urc消息處理函數,與前邊我們進行NNMI設置密切相關。
urc_qlwevtind
在 #<IoT/基于NB28-A/BC28-CNV通信模組使用AT指令連接華為云IoTDA平臺># 中,我們已經知道,
若設備注冊平臺成功,就會看到兩條異步消息+QLWEVTIND:0和+QLWEVTIND:3,表明我們可以使用數據傳輸相關AT指令和平臺進行通訊啦,平臺設備也會變為在線。
URC(Unsolicited Result Code,非請求結果碼)是AT指令通信中的一種異步通知機制,用于設備(如NB-IoT模組)主動向主機(MCU/處理器)推送狀態變化或事件,無需主機主動查詢。URC廣泛應用于網絡狀態、短信接收、數據到達等場景。
+QLWEVTIND:0
通知主機,平臺注冊流程已啟動,但尚未獲得平臺最終確認,主機應等待+QLWEVTIND:3(注冊成功)后再發送業務數據。此時的底層狀態大約是模組已附著PS域(AT+CGATT=1成功)、已解析平臺地址。
+QLWEVTIND:3
通知主機,平臺注冊完成,可安全使用數據傳輸指令。此時底層的狀態大約為,平臺已分配資源路徑(如/12345/0/1)、設備狀態在平臺顯示為在線。LwM2M平臺注冊成功,模組已收到平臺的最終確認(如2.01 Created響應)時,會觸發此URC消息。
如下 NB-IoT-AT 代碼實現,就是上述理論和工作機制的一種具體實踐,
//
static bool_t boudica150_boot(const char *plmn, const char *apn, const char *bands,const char *server,const char *port) {at_oobregister("qlwevind",cn_urc_qlwevtind,strlen(cn_urc_qlwevtind),urc_qlwevtind,NULL);...
}//
int at_oobregister(const char *name,const void *index,size_t len,fn_at_oob func,void *args) {...oob->func = func;oob->args = args;
}//check if any out of band method could deal the data
static int __oob_match(void *data,size_t len) {int ret = -1;at_oob_item *oob;int i = 0;for(i =0;i<CONFIG_AT_OOBTABLEN;i++) {oob = &g_at_cb.oob[i];if((oob->func != NULL)&&(oob->index != NULL) && (0 == memcmp(oob->index,data,oob->len))) {ret = oob->func(oob->args,data,len); //執行回調函數urc_qlwevtind break;}}return ret;
}
//任務入口函數
static int __rcv_task_entry(void *args) {while(NULL != g_at_cb.devhandle) {if (1 == g_at_cb.streammode) {rcvlen += __resp_rcv(g_at_cb.rcvbuf+ rcvlen,CONFIG_AT_RECVMAXLEN,cn_osal_timeout_forever);...oobret = __oob_match(g_at_cb.rcvbuf,rcvlen);...
函數 __oob_match 實現在 \iot_link\at\at.c 文件中,而 _oob_match 函數在 __rcv_task_entry 接收任務入口函數中調用。上述接收任務器消息來源可能是串口數據接收任務或中斷服務,這個問題我們在后續新文章中繼續探討。
總結和后記
boudica150_boot 函數上層相關的源碼解析,請參考#<IoT/實現和分析 NB-IoT+DTLS+PSK 接入華為云物聯網平臺IoTDA過程,總結避坑攻略>#
boudica150_boot 函數下層相關的源碼解析,請參考#<…>#