DX-CT511N-B

元器件
通信模块
库存 500

介绍

大夏龙雀 4G+GNSS 多模通信模块,支持 LTE Cat.1 全网通 + GPS/BDS 双模定位。硬件接口 UART,支持 TCP/UDP/HTTP/MQTT 协议栈,发射功率 23dBm±2dB,工作电压 5V-16V,已通过 CCC/SRRC 认证。模块尺寸 28mm×28mm,适用于物联网、车载终端、共享单车、远程监控等场景。

规格参数

参数
SIM卡Micro SIM (3V/1.8V)
品牌大夏龙雀
定位GPS/BDS 双模 GNSS
认证CCC, SRRC
AT指令标准AT指令集扩展
发射功率23dBm ± 2dB
天线接口LTE主天线 + GNSS天线(IPEX)
工作温度-40°C ~ +85°C
工作电压5V-16V
支持协议TCP, UDP, HTTP, MQTT
模块型号DX-CT511N-B
模块尺寸28mm × 28mm
硬件接口UART
通信制式LTE Cat.1 + GSM

代码例程

DX-CT511N-B STM32 HAL 驱动代码例程 - 头文件与驱动实现.md

# DX-CT511N-B STM32 HAL 驱动代码例程

## 环境说明

| 项目 | 说明 |
|------|------|
| MCU | STM32F103C8T6(兼容全系列 STM32) |
| HAL 库 | STM32Cube_FW_F1 |
| IDE | Keil MDK / STM32CubeIDE |
| 模块串口 | USART2 (PA2-TX / PA3-RX),115200bps |
| 调试串口 | USART1 (PA9-TX / PA10-RX) |
| PWRKEY | PB9 |

---

## 1. 头文件 `module_4g.h`

```c
#ifndef __MODULE_4G_H__
#define __MODULE_4G_H__

#include "stm32f1xx_hal.h"
#include <stdbool.h>
#include <stdint.h>

/* ============== 宏定义 ============== */

#define UART4G_RX_BUF_SIZE      1024
#define AT_TIMEOUT              3000
#define AT_LONG_TIMEOUT         15000

#define SERVER_HOST             "mqtt.your-broker.com"
#define SERVER_PORT             1883
#define HTTP_API_BASE           "http://api.your-server.com/register"

#define PWRKEY_PORT             GPIOB
#define PWRKEY_PIN              GPIO_PIN_9

/* ============== 设备状态结构体 ============== */

typedef struct {
    char     mac_addr[18];
    char     device_id[32];
    bool     network_ready;
    bool     mqtt_connected;
    uint32_t last_mqtt_ping;
    uint32_t last_gps_fix;
} device_ctx_t;

/* ============== API 接口 ============== */

int  Module4G_Init(void);
int  Module4G_Reset(void);
int  AT_SendRaw(const char *cmd);
int  AT_SendCmd(const char *cmd, char *resp, uint16_t resp_size,
                uint32_t timeout, const char *expect);
int  MQTT_Connect(const char *host, uint16_t port,
                  const char *client_id, uint16_t keepalive);
int  MQTT_Disconnect(void);
int  MQTT_Publish(const char *topic, const char *payload);
int  MQTT_Subscribe(const char *topic);
int  MQTT_Unsubscribe(const char *topic);
bool MQTT_HasMessage(char *topic_out, uint16_t topic_size,
                     char *payload_out, uint16_t payload_size);
int  HTTP_Post(const char *url, const char *body,
               char *resp, uint16_t resp_size);
int  HTTP_Get(const char *url, char *resp, uint16_t resp_size);
int  GPS_Enable(void);
int  GPS_Disable(void);
int  GPS_GetPosition(float *lat, float *lon, float *alt,
                     float *speed, float *course);
int  Network_GetSignalQuality(int *rssi, int *ber);
int  Network_GetIP(char *ip, uint16_t size);
void Module4G_Poll(void);

#endif /* __MODULE_4G_H__ */
```

---

## 2. 驱动实现 `module_4g.c`(上篇:串口 + AT指令 + MQTT)

```c
#include "module_4g.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

extern UART_HandleTypeDef huart2;
extern UART_HandleTypeDef huart1;

device_ctx_t g_device;

/* ---- 环形缓冲区 ---- */
static uint8_t  uart4g_rx_buf[UART4G_RX_BUF_SIZE];
static volatile uint16_t uart4g_rx_wr = 0;
static volatile uint16_t uart4g_rx_rd = 0;
static char at_resp_buf[UART4G_RX_BUF_SIZE];

#ifdef DEBUG_ENABLE
#define LOG(fmt, ...)  do { \
    char _log[128]; \
    snprintf(_log, sizeof(_log), "[4G] " fmt "\r\n", ##__VA_ARGS__); \
    HAL_UART_Transmit(&huart1, (uint8_t*)_log, strlen(_log), 100); \
} while(0)
#else
#define LOG(fmt, ...)
#endif

/* ============== 串口中断处理 ============== */

static void UART4G_StartReceive(void)
{
    HAL_UART_Receive_IT(&huart2,
                        &uart4g_rx_buf[uart4g_rx_wr], 1);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2) {
        uart4g_rx_wr = (uart4g_rx_wr + 1) % UART4G_RX_BUF_SIZE;
        HAL_UART_Receive_IT(&huart2,
                            &uart4g_rx_buf[uart4g_rx_wr], 1);
    }
}

static uint16_t UART4G_ReadAll(uint8_t *buf, uint16_t buf_size)
{
    uint16_t idx = 0;
    __disable_irq();
    while (uart4g_rx_rd != uart4g_rx_wr && idx < buf_size - 1) {
        buf[idx++] = uart4g_rx_buf[uart4g_rx_rd];
        uart4g_rx_rd = (uart4g_rx_rd + 1) % UART4G_RX_BUF_SIZE;
    }
    __enable_irq();
    buf[idx] = '\0';
    return idx;
}

static void UART4G_Flush(void)
{
    __disable_irq();
    uart4g_rx_wr = 0;
    uart4g_rx_rd = 0;
    __enable_irq();
}

/* ============== AT 指令底层 ============== */

int AT_SendRaw(const char *cmd)
{
    HAL_StatusTypeDef ret = HAL_UART_Transmit(&huart2,
                              (uint8_t *)cmd, strlen(cmd), 500);
    return (ret == HAL_OK) ? 0 : -1;
}

int AT_SendCmd(const char *cmd, char *resp, uint16_t resp_size,
               uint32_t timeout, const char *expect)
{
    UART4G_Flush();
    char send_buf[256];
    int len = snprintf(send_buf, sizeof(send_buf), "%s\r\n", cmd);
    if (len < 0) return -1;
    HAL_UART_Transmit(&huart2, (uint8_t *)send_buf, len, 500);

    uint32_t start = HAL_GetTick();
    uint16_t total = 0;
    uint8_t  tmp[128];

    while ((HAL_GetTick() - start) < timeout) {
        uint16_t n = UART4G_ReadAll(tmp, sizeof(tmp));
        if (n > 0) {
            if ((total + n) >= resp_size) n = resp_size - total - 1;
            if (n > 0) {
                memcpy(resp + total, tmp, n);
                total += n;
                resp[total] = '\0';
            }
            if (expect != NULL && strstr(resp, expect) != NULL)
                return 0;
            if (strstr(resp, "ERROR") != NULL) {
                LOG("AT ERROR: %s", cmd);
                return -1;
            }
        }
        HAL_Delay(10);
    }
    if (total > 0 && expect == NULL) return 0;
    LOG("AT TIMEOUT: %s", cmd);
    return -1;
}

/* ============== MQTT 操作 ============== */

int MQTT_Connect(const char *host, uint16_t port,
                 const char *client_id, uint16_t keepalive)
{
    char cmd[256];
    snprintf(cmd, sizeof(cmd),
             "AT+MQTTCONN=0,\"%s\",%d,\"%s\",%d",
             host, port, client_id, keepalive);
    return AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                      AT_LONG_TIMEOUT, "OK");
}

int MQTT_Disconnect(void)
{
    return AT_SendCmd("AT+MQTTDISC=0", at_resp_buf,
                      sizeof(at_resp_buf), AT_TIMEOUT, "OK");
}

int MQTT_Publish(const char *topic, const char *payload)
{
    char cmd[512];
    snprintf(cmd, sizeof(cmd),
             "AT+MQTTPUB=0,\"%s\",\"%s\",0,0", topic, payload);
    return AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                      AT_TIMEOUT, "OK");
}

int MQTT_Subscribe(const char *topic)
{
    char cmd[256];
    snprintf(cmd, sizeof(cmd), "AT+MQTTSUB=0,\"%s\",0", topic);
    return AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                      AT_TIMEOUT, "OK");
}

int MQTT_Unsubscribe(const char *topic)
{
    char cmd[256];
    snprintf(cmd, sizeof(cmd), "AT+MQTTUNSUB=0,\"%s\"", topic);
    return AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                      AT_TIMEOUT, "OK");
}

bool MQTT_HasMessage(char *topic_out, uint16_t topic_size,
                     char *payload_out, uint16_t payload_size)
{
    uint8_t buf[512];
    uint16_t n = UART4G_ReadAll(buf, sizeof(buf));
    if (n == 0) return false;

    char *p = strstr((char *)buf, "+MQTTSUBRECV:");
    if (p == NULL) return false;

    char *t1 = strchr(p, '"');
    if (t1 == NULL) return false;
    char *t2 = strchr(t1 + 1, '"');
    if (t2 == NULL) return false;
    uint16_t tlen = (uint16_t)(t2 - t1 - 1);
    if (tlen >= topic_size) tlen = topic_size - 1;
    memcpy(topic_out, t1 + 1, tlen);
    topic_out[tlen] = '\0';

    char *p1 = strchr(t2 + 1, '"');
    if (p1 == NULL) return false;
    char *p2 = strchr(p1 + 1, '"');
    if (p2 == NULL) return false;
    uint16_t plen = (uint16_t)(p2 - p1 - 1);
    if (plen >= payload_size) plen = payload_size - 1;
    memcpy(payload_out, p1 + 1, plen);
    payload_out[plen] = '\0';

    LOG("MQTT RX [%s]: %s", topic_out, payload_out);
    return true;
}
```
DX-CT511N-B STM32 HAL 驱动代码例程 - HTTP/GNSS/初始化/主循环.md

# DX-CT511N-B 驱动实现(下篇):HTTP、GNSS、初始化与主循环

> 接上篇 `module_4g.c`,以下为 HTTP、GNSS、网络状态、模块初始化及主循环示例。

---

## 2. 驱动实现 `module_4g.c`(下篇)

```c
/* ================================================================
 *  HTTP 操作
 * ================================================================ */

int HTTP_Post(const char *url, const char *body,
              char *resp, uint16_t resp_size)
{
    int ret;
    ret = AT_SendCmd("AT+HTTPINIT", at_resp_buf, sizeof(at_resp_buf),
                     AT_TIMEOUT, "OK");
    if (ret != 0) goto http_exit;

    {
        char cmd[512];
        snprintf(cmd, sizeof(cmd), "AT+HTTPPARA=\"URL\",\"%s\"", url);
        ret = AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, "OK");
        if (ret != 0) goto http_exit;
    }

    ret = AT_SendCmd("AT+HTTPPARA=\"CONTENT\",\"application/json\"",
                     at_resp_buf, sizeof(at_resp_buf),
                     AT_TIMEOUT, "OK");
    if (ret != 0) goto http_exit;

    {
        char cmd[64];
        uint16_t body_len = (uint16_t)strlen(body);
        snprintf(cmd, sizeof(cmd), "AT+HTTPDATA=%d,10000", body_len);
        ret = AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, "DOWNLOAD");
        if (ret != 0) goto http_exit;
    }

    HAL_UART_Transmit(&huart2, (uint8_t *)body, strlen(body), 2000);
    HAL_Delay(300);

    ret = AT_SendCmd("AT+HTTPACTION=1", at_resp_buf, sizeof(at_resp_buf),
                     AT_LONG_TIMEOUT, "+HTTPACTION:");
    if (ret != 0) goto http_exit;

    ret = AT_SendCmd("AT+HTTPREAD", at_resp_buf, sizeof(at_resp_buf),
                     AT_LONG_TIMEOUT, "OK");
    if (resp && resp_size > 0) {
        strncpy(resp, at_resp_buf, resp_size - 1);
        resp[resp_size - 1] = '\0';
    }

http_exit:
    AT_SendCmd("AT+HTTPTERM", at_resp_buf, sizeof(at_resp_buf),
               AT_TIMEOUT, "OK");
    return ret;
}

int HTTP_Get(const char *url, char *resp, uint16_t resp_size)
{
    int ret;
    ret = AT_SendCmd("AT+HTTPINIT", at_resp_buf, sizeof(at_resp_buf),
                     AT_TIMEOUT, "OK");
    if (ret != 0) goto http_exit;

    {
        char cmd[512];
        snprintf(cmd, sizeof(cmd), "AT+HTTPPARA=\"URL\",\"%s\"", url);
        ret = AT_SendCmd(cmd, at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, "OK");
        if (ret != 0) goto http_exit;
    }

    ret = AT_SendCmd("AT+HTTPACTION=0", at_resp_buf, sizeof(at_resp_buf),
                     AT_LONG_TIMEOUT, "+HTTPACTION:");
    if (ret != 0) goto http_exit;

    ret = AT_SendCmd("AT+HTTPREAD", at_resp_buf, sizeof(at_resp_buf),
                     AT_LONG_TIMEOUT, "OK");
    if (resp && resp_size > 0) {
        strncpy(resp, at_resp_buf, resp_size - 1);
        resp[resp_size - 1] = '\0';
    }

http_exit:
    AT_SendCmd("AT+HTTPTERM", at_resp_buf, sizeof(at_resp_buf),
               AT_TIMEOUT, "OK");
    return ret;
}

/* ================================================================
 *  GNSS/GPS 操作
 *  响应格式: +CGPSINFO: lat,NS,lon,EW,date,time,alt,speed,course
 *  例: +CGPSINFO: 2232.4567,N,11356.7890,E,240225,143000,45.2,0.0,180.5
 * ================================================================ */

int GPS_Enable(void)
{
    return AT_SendCmd("AT+CGPS=1", at_resp_buf, sizeof(at_resp_buf),
                      AT_TIMEOUT, "OK");
}

int GPS_Disable(void)
{
    return AT_SendCmd("AT+CGPS=0", at_resp_buf, sizeof(at_resp_buf),
                      AT_TIMEOUT, "OK");
}

int GPS_GetPosition(float *lat, float *lon, float *alt,
                    float *speed, float *course)
{
    int ret = AT_SendCmd("AT+CGPSINFO", at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, "+CGPSINFO:");
    if (ret != 0) return -1;

    char *p = strstr(at_resp_buf, "+CGPSINFO:");
    if (p == NULL) return -1;
    p += strlen("+CGPSINFO:");
    while (*p == ' ') p++;

    if (strncmp(p, ",,,,,,,", 7) == 0) return -1;

    char token[32];
    int  deg;
    float minutes;
    char *comma;

    /* 纬度 ddmm.mmmm */
    comma = strchr(p, ',');
    if (comma == NULL) return -1;
    memcpy(token, p, comma - p);
    token[comma - p] = '\0';
    deg = atoi(token) / 100;
    minutes = atof(token + (deg >= 10 ? 2 : 1));
    *lat = deg + minutes / 60.0f;
    p = comma + 1;

    /* N/S */
    comma = strchr(p, ',');
    if (comma == NULL) return -1;
    if (*p == 'S') *lat = -*lat;
    p = comma + 1;

    /* 经度 dddmm.mmmm */
    comma = strchr(p, ',');
    if (comma == NULL) return -1;
    memcpy(token, p, comma - p);
    token[comma - p] = '\0';
    deg = atoi(token) / 100;
    minutes = atof(token + (deg >= 100 ? 3 : 2));
    *lon = deg + minutes / 60.0f;
    p = comma + 1;

    /* E/W */
    comma = strchr(p, ',');
    if (comma == NULL) return -1;
    if (*p == 'W') *lon = -*lon;
    p = comma + 1;

    /* 跳过 date, time */
    p = strchr(p, ','); if (p) p++;
    p = strchr(p, ','); if (p) p++;

    /* 海拔 */
    comma = strchr(p, ',');
    if (comma) {
        memcpy(token, p, comma - p);
        token[comma - p] = '\0';
        *alt = atof(token);
        p = comma + 1;
    } else { *alt = 0; }

    /* 速度 */
    comma = strchr(p, ',');
    if (comma) {
        memcpy(token, p, comma - p);
        token[comma - p] = '\0';
        *speed = atof(token);
        p = comma + 1;
    } else { *speed = 0; }

    /* 方位角 */
    *course = atof(p);
    return 0;
}

/* ================================================================
 *  网络状态查询
 * ================================================================ */

int Network_GetSignalQuality(int *rssi, int *ber)
{
    int ret = AT_SendCmd("AT+CSQ", at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, "+CSQ:");
    if (ret != 0) return -1;

    char *p = strstr(at_resp_buf, "+CSQ:");
    if (p == NULL) return -1;
    int r, b;
    if (sscanf(p, "+CSQ: %d,%d", &r, &b) == 2) {
        if (rssi) *rssi = (r == 99) ? -113 : (-113 + r * 2);
        if (ber)  *ber  = b;
        return 0;
    }
    return -1;
}

int Network_GetIP(char *ip, uint16_t size)
{
    int ret = AT_SendCmd("AT+CIFSR", at_resp_buf, sizeof(at_resp_buf),
                         AT_TIMEOUT, ".");
    if (ret != 0) return -1;
    if (ip && size > 0) {
        strncpy(ip, at_resp_buf, size - 1);
        ip[size - 1] = '\0';
    }
    return 0;
}

/* ================================================================
 *  模块初始化 (上电 → AT测试 → SIM → 注网 → MQTT → GNSS)
 * ================================================================ */

int Module4G_Init(void)
{
    LOG("Module4G Init start...");

    /* 1. PWRKEY 开机:拉低 1.2s 后释放 */
    HAL_GPIO_WritePin(PWRKEY_PORT, PWRKEY_PIN, GPIO_PIN_RESET);
    HAL_Delay(1200);
    HAL_GPIO_WritePin(PWRKEY_PORT, PWRKEY_PIN, GPIO_PIN_SET);

    /* 2. 等待模块启动 */
    LOG("Waiting for module boot...");
    HAL_Delay(6000);

    /* 3. 启动中断接收 */
    UART4G_StartReceive();

    /* 4. AT 测试 */
    if (AT_SendCmd("AT", at_resp_buf, sizeof(at_resp_buf),
                   AT_TIMEOUT, "OK") != 0) {
        LOG("Module AT test FAILED!");
        return -1;
    }
    LOG("AT test OK");

    /* 5. SIM 卡检测 */
    if (AT_SendCmd("AT+CPIN?", at_resp_buf, sizeof(at_resp_buf),
                   AT_TIMEOUT, "+CPIN: READY") != 0) {
        LOG("SIM card not ready!");
        return -2;
    }
    LOG("SIM card ready");

    /* 6. 信号强度 */
    {
        int rssi;
        if (Network_GetSignalQuality(&rssi, NULL) == 0)
            LOG("Signal RSSI: %d dBm", rssi);
    }

    /* 7. 网络注册 */
    AT_SendCmd("AT+CGREG?", at_resp_buf, sizeof(at_resp_buf),
               AT_TIMEOUT, "OK");

    /* 8. 设置 APN */
    if (AT_SendCmd("AT+CSTT=\"CMNET\"", at_resp_buf, sizeof(at_resp_buf),
                   AT_TIMEOUT, "OK") != 0) {
        LOG("APN set failed");
        return -3;
    }

    /* 9. 激活 PDP */
    if (AT_SendCmd("AT+CIICR", at_resp_buf, sizeof(at_resp_buf),
                   AT_LONG_TIMEOUT, "OK") != 0) {
        LOG("PDP activation failed");
        return -4;
    }
    g_device.network_ready = true;
    LOG("Network ready");

    /* 10. 获取 IP */
    {
        char ip[32];
        if (Network_GetIP(ip, sizeof(ip)) == 0)
            LOG("Device IP: %s", ip);
    }

    /* 11. 连接 MQTT */
    {
        char client_id[48];
        snprintf(client_id, sizeof(client_id), "dev_%s", g_device.mac_addr);
        if (MQTT_Connect(SERVER_HOST, SERVER_PORT, client_id, 60) == 0) {
            g_device.mqtt_connected = true;
            LOG("MQTT connected to %s:%d", SERVER_HOST, SERVER_PORT);
        } else {
            LOG("MQTT connect failed");
        }
    }

    /* 12. 开启 GNSS */
    GPS_Enable();
    LOG("GNSS enabled");
    LOG("Module4G Init complete!");
    return 0;
}

int Module4G_Reset(void)
{
    LOG("Resetting module...");
    if (g_device.mqtt_connected) {
        MQTT_Disconnect();
        g_device.mqtt_connected = false;
    }
    AT_SendCmd("AT+CFUN=0", at_resp_buf, sizeof(at_resp_buf),
               AT_TIMEOUT, "OK");
    HAL_Delay(1000);
    AT_SendCmd("AT+CFUN=1", at_resp_buf, sizeof(at_resp_buf),
               AT_TIMEOUT, "OK");
    HAL_Delay(5000);
    g_device.network_ready = false;
    return 0;
}

void Module4G_Poll(void)
{
    if (g_device.mqtt_connected) {
        char topic[64];
        char payload[256];
        if (MQTT_HasMessage(topic, sizeof(topic),
                            payload, sizeof(payload))) {
            LOG("MQTT CMD: %s -> %s", topic, payload);
            /* 在此处理下行业务指令 */
        }
    }
}
```

---

## 3. 主程序示例 `main.c`

```c
#include "main.h"
#include "module_4g.h"
#include <stdio.h>

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
extern device_ctx_t g_device;

/* ---- 外设初始化 (由 CubeMX 生成骨架) ---- */

static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitStruct.Pin   = GPIO_PIN_9;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}

static void MX_USART2_UART_Init(void)
{
    huart2.Instance          = USART2;
    huart2.Init.BaudRate     = 115200;
    huart2.Init.WordLength   = UART_WORDLENGTH_8B;
    huart2.Init.StopBits     = UART_STOPBITS_1;
    huart2.Init.Parity       = UART_PARITY_NONE;
    huart2.Init.Mode         = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart2);
}

static void MX_USART1_UART_Init(void)
{
    huart1.Instance          = USART1;
    huart1.Init.BaudRate     = 115200;
    huart1.Init.WordLength   = UART_WORDLENGTH_8B;
    huart1.Init.StopBits     = UART_STOPBITS_1;
    huart1.Init.Parity       = UART_PARITY_NONE;
    huart1.Init.Mode         = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    HAL_UART_Init(&huart1);
}

/* ============== 主函数 ============== */

int main(void)
{
    HAL_Init();
    SystemClock_Config();   /* CubeMX 生成 */

    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();

    /* 设置设备唯一标识 */
    strcpy(g_device.mac_addr, "AABBCCDDEEFF");

    printf("=== DX-CT511N-B 4G+GNSS Demo ===\r\n");

    /* 初始化模块,失败则复位重试 */
    if (Module4G_Init() != 0) {
        printf("[ERR] Init failed, retrying...\r\n");
        Module4G_Reset();
        HAL_Delay(3000);
        Module4G_Init();
    }

    /* 订阅指令主题 */
    if (g_device.mqtt_connected) {
        char cmd_topic[64];
        snprintf(cmd_topic, sizeof(cmd_topic),
                 "dev/%s/cmd", g_device.mac_addr);
        MQTT_Subscribe(cmd_topic);
    }

    /* ============== 主循环 ============== */
    uint32_t last_report = 0;
    uint32_t last_gps    = 0;

    while (1) {
        uint32_t now = HAL_GetTick();

        /* 后台轮询 MQTT 消息 */
        Module4G_Poll();

        /* 每 30s 上报遥测 */
        if (g_device.mqtt_connected && (now - last_report) >= 30000) {
            last_report = now;
            int rssi;
            Network_GetSignalQuality(&rssi, NULL);
            char payload[256];
            snprintf(payload, sizeof(payload),
                     "{\"rssi\":%d,\"uptime\":%lu}", rssi, now / 1000);
            char tele_topic[64];
            snprintf(tele_topic, sizeof(tele_topic),
                     "dev/%s/telemetry", g_device.mac_addr);
            MQTT_Publish(tele_topic, payload);
        }

        /* 每 5s 获取 GPS 并上报 */
        if (g_device.mqtt_connected && (now - last_gps) >= 5000) {
            last_gps = now;
            float lat = 0, lon = 0, alt = 0, speed = 0, course = 0;
            if (GPS_GetPosition(&lat, &lon, &alt, &speed, &course) == 0) {
                char gps_payload[256];
                snprintf(gps_payload, sizeof(gps_payload),
                         "{\"lat\":%.6f,\"lon\":%.6f,\"alt\":%.1f,"
                         "\"speed\":%.1f,\"course\":%.1f}",
                         lat, lon, alt, speed, course);
                char gps_topic[64];
                snprintf(gps_topic, sizeof(gps_topic),
                         "dev/%s/gps", g_device.mac_addr);
                MQTT_Publish(gps_topic, gps_payload);
            }
        }

        HAL_Delay(100);
    }
}
```

---

## 4. 关键调试技巧

| 问题 | 排查方法 |
|------|---------|
| AT 无响应 | 检查 TX/RX 是否交叉、共地、波特率 115200 |
| SIM 卡不识别 | 测 SIM_VCC 是否为 1.8V/3V,检查 SIM 座焊接 |
| 注网失败 | `AT+CSQ` 看信号,`AT+CGREG?` 看注册状态 |
| MQTT 连不上 | 先用 `AT+PING=broker_ip` 测网络可达性 |
| GPS 不定位 | 确认天线为有源天线,放到室外开阔处冷启动需 30s~2min |
| HTTP 超时 | `AT_LONG_TIMEOUT` 至少 15s,弱信号环境可能更长 |

---

## 5. 工程结构建议

```
Project/
├── Core/
│   ├── Inc/
│   │   └── module_4g.h
│   └── Src/
│       ├── main.c
│       ├── module_4g.c
│       └── stm32f1xx_it.c      ← HAL_UART_RxCpltCallback 在此
├── Drivers/
│   └── STM32F1xx_HAL_Driver/
└── .ioc                          ← CubeMX 工程文件
```

参考资料

暂无参考文献