介绍
大夏龙雀 4G+GNSS 多模通信模块,支持 LTE Cat.1 全网通 + GPS/BDS 双模定位。硬件接口 UART,支持 TCP/UDP/HTTP/MQTT 协议栈,发射功率 23dBm±2dB,工作电压 5V-16V,已通过 CCC/SRRC 认证。模块尺寸 28mm×28mm,适用于物联网、车载终端、共享单车、远程监控等场景。
大夏龙雀 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 驱动代码例程
## 环境说明
| 项目 | 说明 |
|------|------|
| 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 驱动实现(下篇):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 工程文件
```
暂无参考文献