2025年6月9日 星期一

IOT_PCB CODE 4G LTE 數據機狀態機分析

4G LTE 數據機狀態機分析器

4G LTE 數據機狀態機分析器

本頁提供 `modem_main()` 函式的核心流程控制邏輯解析、相關輔助函式的功能查詢,以及關鍵資料結構的詳細說明。

modem_main() 流程控制邏輯

modem_main() 函式主要透過一個大的 switch 語句來根據 info->session (當前數據機會話狀態) 執行不同的邏輯。每個 case 代表一個特定的數據機操作階段。

case _AT_SESSION_RESET: (數據機重置階段)

目的: 等待數據機完成硬體重置後的初始化延遲。

邏輯:

  • 檢查自 at_cmd_reset_tick 以來的時間是否超過 AT_RESET_INITIAL_TIMEOUT_MS (5 秒)。
  • 如果超過,表示重置延遲已滿足,則將會話切換到 _AT_SESSION_INIT (初始化階段)。
  • 同時初始化 at_cmd_polling_tickat_cmd_poll_gps_tick,為後續的命令輪詢和 GPS 數據讀取計時。
case _AT_SESSION_INIT: (數據機初始化階段)

目的: 執行一系列初始化的 AT 命令,包括基本設定、讀取 UUID、檢查訊號品質、設定 RTC 時間、以及 MQTT 相關參數載入和訂閱主題等。

邏輯:

  • 呼叫 cmd_processing(info):這是處理單個 AT 命令的核心函式,負責發送命令、等待響應、解析響應並更新命令狀態。
  • if(info->status == STATUS_CMD_OK)
    • 重置 info->statusSTATUS_CMD_NONE
    • 檢查訊號品質 (chk_cmd_response()==CMD_STATUS_BAD_SQ): 如果當前命令響應指示訊號品質不佳 (例如 AT+CSQ 命令返回的值低於預設閾值),則:
      • 增加 _modem.chk_sq_cnt
      • 根據當前會話是否為 _AT_SESSION_CHK_SQ,決定是直接切換到 _AT_SESSION_WAIT_TO_CHK_SQ 還是保留原會話並切換到 _AT_SESSION_WAIT_TO_CHK_SQ (使用 switchATSessionK 保留原會話)。
      • 更新 at_cmd_reset_tick
    • 檢查是否為命令集的末尾 (commands[info->index+1].mode == _CMD_MODE_END): 如果當前命令是會話中的最後一個命令:
      • 增加 info->index
      • 根據不同的會話類型執行相應的收尾工作:
        • _AT_SESSION_CHK_SQ: 恢復到之前的會話 (restoreToOrgSession())。
        • _AT_SESSION_TX: 將 _modem.lte_4G_TX_flag 清零,表示數據已發送,然後切換到 _AT_SESSION_FREE_RUN
        • _AT_SESSION_POLLING: 更新 at_cmd_polling_tick,然後切換到 _AT_SESSION_FREE_RUN
        • _AT_SESSION_GET_GPS: 更新 at_cmd_poll_gps_tick,然後切換到 _AT_SESSION_FREE_RUN
        • _AT_SESSION_INIT / _AT_SESSION_LOG_OUT_IN: 設定 _send_ping_data 為 1 (表示需要發送 PING 數據)、設定 _connected 為 1 (表示已連接 MQTT)、清除 _relog_request 旗標。
        • default: 切換到 info->cmdSet[info->index].sleeps 所指定的下一個會話。
  • else if (info->status == STATUS_CMD_ERROR)
    • 重置 info->statusSTATUS_CMD_NONE
    • 如果 _modem.error_count 超過 20 次,則執行 modem_reset() (硬體重置數據機)。
  • else if (info->status == STATUS_CMD_TIMEOUT)
    • STATUS_CMD_ERROR 處理方式類似,如果超時次數過多,也執行 modem_reset()
case _AT_SESSION_WAIT_TO_CHK_SQ: (等待檢查訊號品質階段)

目的: 在訊號品質不佳時,暫停其他操作,等待一段時間後重新檢查訊號。

邏輯:

  • 如果 _modem.chk_sq_cnt 小於 5 次 (表示還在初始的快速重試階段),且自 at_cmd_reset_tick 以來超過 5 秒,則切換到 _AT_SESSION_CHK_SQ
  • 如果 _modem.chk_sq_cnt 大於等於 5 次 (表示已經進行多次重試,需要更長的等待時間),且自 at_cmd_reset_tick 以來超過 30 秒,則切換到 _AT_SESSION_CHK_SQ
case _AT_SESSION_FREE_RUN: (自由運行階段)

目的: 釋放數據機物件的控制權 (semaphore),並切換到下一個自由運行階段。

邏輯:

  • 呼叫 releaseSemaObj(_OWNER_MODEM) 釋放數據機的互斥鎖。
  • 切換到 _AT_SESSION_FREE_RUN_1
  • 更新 at_cmd_reset_tick
case _AT_SESSION_FREE_RUN_1: (自由運行子階段)

目的: 提供一個短暫的窗口,允許其他模組 (例如 CAN) 處理數據,然後再進入數據機的輪詢階段。

邏輯:

  • 如果自 at_cmd_reset_tick 以來超過 1 秒,則切換到 _AT_SESSION_WAIT_TO_GET_GPS
case _AT_SESSION_WAIT_TO_GET_GPS: (等待獲取 GPS 或輪詢數據階段)

目的: 這是數據機在穩定連接後的主要運行階段,負責根據不同的條件觸發數據傳輸、重新登錄或 GPS/訊息輪詢。

邏輯:

  • 優先處理數據發送 (_modem.lte_4G_TX_flag==1): 如果有待發送的數據:
    • 嘗試獲取數據機的互斥鎖 (acquireSemaObj(_OWNER_MODEM))。
    • 如果成功,則切換到 _AT_SESSION_TX
  • 處理重新登錄請求 (chk_relog_request()): 如果有 MQTT 重新登錄請求:
    • 嘗試獲取數據機的互斥鎖。
    • 如果成功,則切換到 _AT_SESSION_LOG_OUT_IN,並將 _connected 設為 0。
  • 平衡 GPS 數據讀取和訊息輪詢: 這部分邏輯比較複雜,旨在交替執行 GPS 請求和輪詢接收到的數據,以避免阻塞:
    • 高頻率同時觸發 (chk_timeup_ms(timer1ms,at_cmd_poll_gps_tick, _modem.at_gps_read_time) && chk_timeup_ms(timer1ms, at_cmd_polling_tick, 5*1000)): 如果 GPS 讀取時間和輪詢時間都到期:
      • 嘗試獲取互斥鎖。
      • 根據 _prev_action_gps 的值,決定是切換到 _AT_SESSION_POLLING (輪詢) 還是 _AT_SESSION_GET_GPS (獲取 GPS),並更新 _prev_action_gps
    • 僅輪詢時間到期 (chk_timeup_ms(timer1ms, at_cmd_polling_tick, 5*1000)): 如果只有輪詢時間到期:
      • 嘗試獲取互斥鎖。
      • 切換到 _AT_SESSION_POLLING,並將 _prev_action_gps 設為 0。
    • 僅 GPS 時間到期 (chk_timeup_ms(timer1ms,at_cmd_poll_gps_tick, _modem.at_gps_read_time)): 如果只有 GPS 時間到期:
      • 嘗試獲取互斥鎖。
      • 切換到 _AT_SESSION_GET_GPS,並將 _prev_action_gps 設為 1。
cmd_processing(COMMAND_STATE_INFO *info) (輔助函式但與主邏輯緊密相關)

功能: 這是執行單個 AT 命令的核心。

邏輯: 根據 info->state (當前命令狀態) 進行處理:

  • _AT_CMD_TO_SEND: 準備並發送 AT 命令到 UART 0。如果命令類型是 _CMD_MODE_PUB, _CMD_MODE_DOWN, _CMD_MODE_UP,則需要處理 UUID 或將 IoT 數據轉換為十六進制格式發送。
  • _AT_CMD_WAIT_TO_RECEIVE: 等待 UART 0 接收到足夠的數據 (通過判斷 \r\n 的數量)。如果超時,則將 info->status 設為 STATUS_CMD_TIMEOUT
  • _AT_CMD_TO_RECEIVE: 檢查接收到的數據是否包含 "OK" 或 "ERROR"。
    • 如果包含 "OK",表示命令成功,清除錯誤計數,複製數據到 platformrxbuffer,並將狀態切換到 _AT_CMD_WAIT_TO_SEND
    • 如果包含 "ERROR",增加錯誤計數,如果錯誤是 MQTT 登錄相關且不影響後續流程,則切換到 _AT_CMD_WAIT_TO_SEND;否則切換到 _AT_CMD_ERROR_RESEND
    • 如果超時,則將 info->status 設為 STATUS_CMD_TIMEOUT
  • _AT_CMD_WAIT_TO_SEND: 等待一段時間後,將狀態切換到 _AT_CMD_TO_SEND 並增加命令索引,準備發送下一個命令。
  • _AT_CMD_ERROR_RESEND: 等待一段時間後,將狀態切換到 _AT_CMD_TO_SEND (重發當前命令)。
chk_cmd_response() (輔助函式但與主邏輯緊密相關)

功能: 解析 platformrxbuffer 中接收到的 AT 命令響應,並根據響應內容更新 _modem 結構中的相關資訊 (如 UUID, 訊號品質, RTC 時間, GPS 數據)。

邏輯: 根據解析結果返回 CMD_STATUS_OK, CMD_STATUS_BAD_SQCMD_STATUS_BAD_GPS

switchATSessionK(), switchATSession()restoreToOrgSession() (會話切換輔助函式)
  • switchATSessionK(AT_SESSION_STATES session):切換到新的 AT 會話,並保留當前會話的狀態資訊,以便後續恢復。
  • switchATSession(AT_SESSION_STATES session):切換到新的 AT 會話,不保留當前會話的狀態資訊。
  • restoreToOrgSession():恢復到之前保存的 AT 會話狀態。
modem_reset() (數據機硬體重置輔助函式)

功能: 執行數據機的硬體重置,包括 GPIO 控制電源和重置引腳,並初始化 _modem 結構中的所有相關變數。

acquireSemaObj()releaseSemaObj() (互斥鎖輔助函式)

功能: 用於實現對數據機操作的互斥鎖,確保在同一時間只有一個模組可以控制數據機,避免競爭條件。

輔助函式與資料結構查詢

請從左側列表選擇一個項目,或在上方搜尋。

核心資料結構定義

以下是 4G LTE 數據機狀態機中使用的核心資料結構定義,您可以展開查看詳細內容。

typedef enum COMMAND_STATUS

功能: 定義 AT 命令的執行狀態。

typedef enum
{
    STATUS_CMD_NONE=0,
    STATUS_CMD_OK,
    STATUS_CMD_ERROR,
    STATUS_CMD_TIMEOUT,
    STATUS_CMD_IN_PROCESS,
} COMMAND_STATUS;
  • STATUS_CMD_NONE: 無狀態,或初始狀態。
  • STATUS_CMD_OK: 命令執行成功。
  • STATUS_CMD_ERROR: 命令執行失敗或返回錯誤。
  • STATUS_CMD_TIMEOUT: 命令執行超時。
  • STATUS_CMD_IN_PROCESS: 命令正在處理中。
typedef enum AT_SESSION_STATES

功能: 定義數據機狀態機的各個會話階段。

typedef enum
{
    _AT_SESSION_INIT =0,
    _AT_SESSION_RESET,              //1
    _AT_SESSION_CHK_SQ,             //2 check signal quality when error or timeout happens.
    _AT_SESSION_WAIT_TO_CHK_SQ,     //3
    _AT_SESSION_FREE_RUN,           //4
    _AT_SESSION_TX,                 //5 MQTT up send json message
    _AT_SESSION_WAIT_TO_GET_GPS,    //6
    _AT_SESSION_GET_GPS,            //7
    _AT_SESSION_LOG_OUT_IN,         //8
    _AT_SESSION_POLLING,            //9
    _AT_SESSION_FREE_RUN_1,         //10
}AT_SESSION_STATES;
  • _AT_SESSION_INIT: 初始化會話。
  • _AT_SESSION_RESET: 數據機重置後等待階段。
  • _AT_SESSION_CHK_SQ: 檢查訊號品質會話。
  • _AT_SESSION_WAIT_TO_CHK_SQ: 等待檢查訊號品質的延遲階段。
  • _AT_SESSION_FREE_RUN: 自由運行階段 (釋放鎖)。
  • _AT_SESSION_TX: 數據上傳 (MQTT) 會話。
  • _AT_SESSION_WAIT_TO_GET_GPS: 等待獲取 GPS 或輪詢數據的決策階段。
  • _AT_SESSION_GET_GPS: 獲取 GPS 數據會話。
  • _AT_SESSION_LOG_OUT_IN: MQTT 登出再登入會話。
  • _AT_SESSION_POLLING: 輪詢接收數據會話。
  • _AT_SESSION_FREE_RUN_1: 自由運行子階段 (短暫延遲)。
typedef enum AT_CMD_STATES

功能: 定義單個 AT 命令的執行狀態,用於 cmd_processing() 函式。

typedef enum
{
    _AT_CMD_NONE = 0,
    _AT_CMD_TO_SEND,
    _AT_CMD_TO_RECEIVE,
    _AT_CMD_TO_RECEIVE1,
    _AT_CMD_WAIT_TO_SEND,
    _AT_CMD_ERROR_RESEND,
    _AT_CMD_WAIT_TO_RECEIVE,
}AT_CMD_STATES;
  • _AT_CMD_NONE: 無命令狀態。
  • _AT_CMD_TO_SEND: 準備發送命令。
  • _AT_CMD_TO_RECEIVE: 準備接收響應。
  • _AT_CMD_TO_RECEIVE1: 另一種接收響應狀態。
  • _AT_CMD_WAIT_TO_SEND: 等待下次發送命令。
  • _AT_CMD_ERROR_RESEND: 錯誤後等待重發。
  • _AT_CMD_WAIT_TO_RECEIVE: 等待接收響應數據。
typedef enum _CMD_MODES

功能: 定義 AT 命令的處理模式,影響命令的發送和響應解析方式。

typedef enum {
    _CMD_MODE_0 = 0,    //normal command, just send it to modem
    _CMD_MODE_PUB,
    _CMD_MODE_DOWN,
    _CMD_MODE_UP,
    _CMD_MODE_P0,
    _CMD_MODE_CSQ,
    _CMD_MODE_TM,
    _CMD_MODE_E,        //ignore the error
    _CMD_MODE_TEST,
    _CMD_MODE_GPS,
    _CMD_MODE_TX,
    _CMD_MODE_POLL,         //poll reading the data of Link Commands from uBlox catched buffer.
    _CMD_MODE_MQTTLOGIN,        //=== 20241111 handel check mqtt login === "+UUMQTTC: 1 ,1"
    _CMD_MODE_END,
}_CMD_MODES;
  • _CMD_MODE_0: 普通命令,直接發送。
  • _CMD_MODE_PUB: MQTT 發布相關命令。
  • _CMD_MODE_DOWN: MQTT 訂閱下載相關命令。
  • _CMD_MODE_UP: MQTT 上傳相關命令。
  • _CMD_MODE_P0: 可能用於特定參數設定或檔案操作。
  • _CMD_MODE_CSQ: 查詢訊號品質命令。
  • _CMD_MODE_TM: 查詢時間命令。
  • _CMD_MODE_E: 忽略錯誤的命令模式。
  • _CMD_MODE_TEST: 測試模式命令。
  • _CMD_MODE_GPS: GPS 相關命令。
  • _CMD_MODE_TX: 數據傳輸命令。
  • _CMD_MODE_POLL: 輪詢 Link Command 數據命令。
  • _CMD_MODE_MQTTLOGIN: MQTT 登錄檢查命令。
  • _CMD_MODE_END: 命令集的結束標誌。
typedef struct AT_COMMANDS

功能: 定義單個 AT 命令的結構,包括其模式、延遲時間和命令字串。

typedef struct{
    _CMD_MODES mode;
    uint8_t sleeps;
    char *command;
} AT_COMMANDS;
  • mode: 命令的處理模式 (參考 _CMD_MODES)。
  • sleeps: 命令發送後的延遲時間或切換到的下一個會話狀態。
  • command: AT 命令字串。
typedef struct COMMAND_STATE_INFO

功能: 儲存數據機狀態機的當前運行資訊。

typedef struct{
    AT_SESSION_STATES   session;
    AT_SESSION_STATES   prev_session;
    AT_CMD_STATES       state;
    uint8_t             index;
    uint8_t             prev_index;
    COMMAND_STATUS      status;
    AT_COMMANDS* cmdSet;
    uint8_t             signal_bad; //the quality of 4G signal, 0: good, 1:bad
}COMMAND_STATE_INFO;
  • session: 當前會話狀態。
  • prev_session: 上一個會話狀態 (用於恢復)。
  • state: 當前命令執行狀態。
  • index: 當前命令在命令集中的索引。
  • prev_index: 上一個命令的索引。
  • status: 當前命令的執行結果狀態。
  • cmdSet: 指向當前使用的 AT 命令集陣列。
  • signal_bad: 4G 訊號品質標誌 (0: 好, 1: 差)。
typedef struct GPS_LOCATION

功能: 儲存 GPS 定位資訊。

typedef struct {
    char utcTime[16];
    char utcDate[16];
    char latitude[32];
    char longitude[32];
} GPS_LOCATION;
  • utcTime: UTC 時間字串。
  • utcDate: UTC 日期字串。
  • latitude: 緯度字串。
  • longitude: 經度字串。
typedef struct MODEM_DATA

功能: 數據機的整體數據和狀態儲存結構,包含了數據機運行所需的所有關鍵資訊。

typedef struct MODEM_DATA
{
    uint8_t modem_uuid_md5[33];
    uint8_t lte_4G_TX_flag;
    uint8_t lte_4G_TX_data[UART_TX_RX_SIZE*2+100];

    uint8_t lte_4G_RX_index1;
    uint8_t lte_4G_RX_index2;
    uint8_t lte_4G_RX_data[LTE_4G_RX_DATA_COUNT][UART_TX_RX_SIZE];
    uint8_t error_count;
    uint8_t relog_request;

    uint8_t chk_sq_cnt;

    COMMAND_STATE_INFO at_cmd_info;

    uint8_t  lte_4G_gps_first_flag;
    uint8_t  lte_4G_gps_read_flag;
    uint32_t lte_4G_gps_tick;

    GPS_LOCATION gpsLoc;

    uint32_t at_gps_read_time;

    UTC_TIME rtc_time;
    uint8_t chk_mqtt_login;
    uint8_t lte_4G_csqval;
} MODEM_DATA;
  • modem_uuid_md5: 數據機的 UUID (MD5 格式)。
  • lte_4G_TX_flag: 4G 數據發送標誌。
  • lte_4G_TX_data: 用於發送 IoT 數據的緩衝區。
  • lte_4G_RX_index1, lte_4G_RX_index2: 接收數據緩衝區的索引。
  • lte_4G_RX_data: 接收到的訊息列表。
  • error_count: 錯誤計數。
  • relog_request: 重新登錄請求標誌。
  • chk_sq_cnt: 檢查訊號品質的計數。
  • at_cmd_info: 數據機狀態機的當前資訊。
  • lte_4G_gps_first_flag, lte_4G_gps_read_flag, lte_4G_gps_tick: GPS 讀取相關的標誌和計時。
  • gpsLoc: GPS 定位資訊。
  • at_gps_read_time: GPS 讀取時間間隔。
  • rtc_time: RTC 時間資訊。
  • chk_mqtt_login: MQTT 登錄狀態 (1: 登錄, 0: 登出)。
  • lte_4G_csqval: 4G 訊號品質 (CSQ 值)。
typedef enum CMD_STATUS (不同於 COMMAND_STATUS 的狀態)

功能: 定義命令響應的狀態,特別用於 chk_cmd_response() 函式的返回結果。

typedef enum {
    CMD_STATUS_OK = 0,
    CMD_STATUS_BAD_SQ,
    CMD_STATUS_BAD_GPS,
}CMD_STATUS;
  • CMD_STATUS_OK: 命令響應正常。
  • CMD_STATUS_BAD_SQ: 訊號品質不佳。
  • CMD_STATUS_BAD_GPS: GPS 數據不佳。
靜態 AT 命令集陣列

以下是數據機初始化、檢查訊號、獲取 GPS、發送數據、登出登入和輪詢等不同會話所使用的 AT 命令集定義。

這些陣列包含了 _CMD_MODESsleeps (延遲或下一會話) 和實際的 command 字串。

例如:

static AT_COMMANDS atCmdInit[]={
    // ...
    {_CMD_MODE_CSQ    ,2  ,"AT+CSQ\r\n"},
    {_CMD_MODE_TM     ,2  ,"AT+CCLK?\r\n"},
    // ...
    {_CMD_MODE_END    ,_AT_SESSION_FREE_RUN ,""}
};

static AT_COMMANDS atCmdChkSQ[] = {
    {_CMD_MODE_CSQ    ,1  ,"AT+CSQ\r\n"},
    {_CMD_MODE_END    ,99 ,""} //back to previous session
};

static AT_COMMANDS atCmdGetGPS[] = {
    {_CMD_MODE_TM     ,1  ,"AT+CCLK?\r\n"},
    {_CMD_MODE_GPS    ,1  ,"AT+UGRMC?\r\n"},
    {_CMD_MODE_END    ,_AT_SESSION_FREE_RUN  ,""}
};

static AT_COMMANDS atTxData[] = {
    {_CMD_MODE_TM     ,1  ,"AT+CCLK?\r\n"},
    {_CMD_MODE_UP     ,1  ,"AT+UMQTTC=2,0,0,1,\"EMOTO/DEV/"},
    {_CMD_MODE_END    ,_AT_SESSION_FREE_RUN  ,""}
};

static AT_COMMANDS atCmdLogOutIn[] = {
    {_CMD_MODE_0      ,2  ,"AT+UMQTTC=0\r\n"},
    {_CMD_MODE_0      ,2  ,"AT+UMQTTC=1\r\n"},
    {_CMD_MODE_DOWN   ,2  ,"AT+UMQTTC=4,0,\"EMOTO/DEV/"},
    {_CMD_MODE_PUB    ,2  ,"AT+UMQTTC=4,0,\"EMOTO/DEV/"},
    {_CMD_MODE_END    ,_AT_SESSION_FREE_RUN  ,""}
};

static AT_COMMANDS atCmdPolling[] = {
    {_CMD_MODE_TM     ,1  ,"AT+CCLK?\r\n"},
    {_CMD_MODE_POLL   ,1  ,"AT+UMQTTC=6\r\n"},
    {_CMD_MODE_END    ,_AT_SESSION_FREE_RUN ,""}
};

static void* atCommands[] = {
    atCmdInit,          //_AT_SESSION_INIT
    0,                  //_AT_SESSION_RESET
    atCmdChkSQ,         //_AT_SESSION_CHK_SQ
    0,                  //_AT_SESSION_WAIT_TO_CHK_SQ
    0,                  //_AT_SESSION_FREE_RUN
    atTxData,           //_AT_SESSION_TX
    0,                  //_AT_SESSION_WAIT_TO_GET_GPS
    atCmdGetGPS,        //_AT_SESSION_GET_GPS
    atCmdLogOutIn,      //_AT_SESSION_LOG_OUT_IN
    atCmdPolling,       //_AT_SESSION_POLLING
    0                   //_AT_SESSION_FREE_RUN1
};

沒有留言:

張貼留言