首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

透過序列埠收發簡訊 (下):PDU 全串編解碼、AT 指令實作與 Windows 序列埠 API 封裝

     編輯:VC 教學

PDU 參數結構

typedef struct {
    char SCA[16];      // 簡訊中心號碼
    char TPA[16];      // 目標/回覆號碼
    char TP_PID;       // 協定識別碼
    char TP_DCS;       // 編碼方式 (7bit/8bit/UCS2)
    char TP_SCTS[16];  // 服務時間戳
    char TP_UD[161];   // 用戶資料 (編碼前/解碼後)
    char index;        // 簡訊索引
} SM_PARAM;

號碼/時間字串雙位元組反轉

PDU 中號碼與時間皆為「兩兩交換」且奇數長度補 'F'。提供 gsmInvertNumbers (正序→反序) 與 gsmSerializeNumbers (反序→正序) 兩工具函式。

PDU 編碼 (發送用) gsmEncodePdu

  1. SMSC 地址段:長度 + TON/NPI(0x91) + 反序號碼
  2. TPDU 基礎參數:0x11 (SUBMIT, VP相對格式) + MR=0 + 目標號碼長度 + TON/NPI + 反序目標號碼
  3. PID/DCS/VP/UD:根據 TP_DCS 呼叫 gsmEncode7bit/8bit/UCS2 處理用戶資料
  4. 整段轉十六進位字串輸出

PDU 解碼 (接收用) gsmDecodePdu

  1. 解析 SMSC 長度與號碼
  2. 解析 TPDU 第一字節判斷是否含 RP (Reply Path) 與 UDH
  3. 提取 PID、DCS、SCTS (固定 14 字節)、UDL
  4. 依 DCS 分派 gsmDecode7bit/8bit/UCS2 還原用戶資料

AT 指令實作

發送 gsmSendMessage

BOOL gsmSendMessage(const SM_PARAM* p) {
    char pdu[512]; int len = gsmEncodePdu(p, pdu);
    strcat(pdu, "\x1A"); // Ctrl-Z 結束
    // 計算不含 SMSC 的 PDU 位元組數
    BYTE smscLen; gsmString2Bytes(pdu, &smscLen, 2); smscLen++;
    char cmd[32]; sprintf(cmd, "AT+CMGS=%d ", len/2 - smscLen);
    WriteComm(cmd, strlen(cmd));
    ReadComm(ans, 128); // 等待 "> "
    if(strncmp(ans, ">", 1)!=0) return FALSE;
    WriteComm(pdu, strlen(pdu));
    ReadComm(ans, 128);
    return (strncmp(ans, "+CMS ERROR", 10)!=0);
}

讀取全部 gsmReadMessage (用 CMGL 替代 CMGR)

int gsmReadMessage(SM_PARAM* buf) {
    WriteComm("AT+CMGL ", 8);
    ReadComm(ans, 1024);
    if(strstr(ans, "+CMS ERROR")) return 0;
    int cnt=0; char* p=strstr(ans, "+CMGL:");
    while(p) {
        p+=6; sscanf(p, "%d", &buf->index);
        p=strstr(p, "\n"); p+=2; // 跳到 PDU 行
        gsmDecodePdu(p, buf);
        buf++; cnt++;
        p=strstr(p, "+CMGL:");
    }
    return cnt;
}

刪除 gsmDeleteMessage

BOOL gsmDeleteMessage(int index) {
    char cmd[16]; sprintf(cmd, "AT+CMGD=%d ", index);
    WriteComm(cmd, strlen(cmd));
    ReadComm(ans, 128);
    return (strncmp(ans, "+CMS ERROR", 10)!=0);
}

Windows 序列埠同步阻塞封裝

HANDLE hComm;
BOOL OpenComm(LPCSTR port, int baud, int parity, int byteSize, int stopBits) {
    hComm = CreateFile(port, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if(hComm==INVALID_HANDLE_VALUE) return FALSE;
    DCB dcb = {0}; dcb.DCBlength=sizeof(DCB);
    GetCommState(hComm, &dcb);
    dcb.BaudRate=baud; dcb.Parity=parity; dcb.ByteSize=byteSize; dcb.StopBits=stopBits;
    SetCommState(hComm, &dcb);
    COMMTIMEOUTS to = {100, 100, 0, 100, 0}; // 讀寫逾時
    SetCommTimeouts(hComm, &to);
    return TRUE;
}
int WriteComm(const void* buf, int len) { DWORD w; WriteFile(hComm, buf, len, &w, NULL); return w; }
int ReadComm(void* buf, int max) { DWORD r; ReadFile(hComm, buf, max, &r, NULL); return r; }
void CloseComm() { if(hComm!=INVALID_HANDLE_VALUE) CloseHandle(hComm); }

可移植性說明

核心編解碼僅用 ANSI C,無 MFC 相依;序列埠層可替換為 Linux termios 或嵌入式 UART 驅動,極易移植。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved