基于 FreeRTOS + ESP8266(AT 指令)+ MQTT的实现方案
一、整体系统架构
┌─────────────────────────────────────────────┐
│ Host MCU (dsPIC33 / STM32) │
│ │
│ ┌────────────┐ UART ┌──────────────┐ │
│ │ FreeRTOS │◀───────▶│ ESP8266 │ │
│ │ App Task │ │ AT Firmware │ │
│ └─────┬──────┘ └──────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────┐ ┌──────────────┐ │
│ │ MQTT API │ │ WiFi / TCP │ │
│ │ (Publish/ │ │ TLS(可选) │ │
│ │ Subscribe) │ │ │ │
│ └────────────┘ └──────────────┘ │
└─────────────────────────────────────────────┘
ESP8266 只做通信
业务逻辑全部在 MCU 端(FreeRTOS)
二、FreeRTOS 任务划分
| 任务 | 优先级 | 作用 |
|---|---|---|
AT_Cmd_Task |
High | 发送 AT 指令 & 解析响应 |
AT_Recv_Task |
High | 串口接收 + UR 解析 |
MQTT_Client_Task |
Medium | MQTT 连接 / 保活 |
App_Publish_Task |
Low | 业务数据上报 |
App_Subscribe_Task |
Low | 下行控制 |
三、ESP8266 AT 指令基础
ESP8266 固件 ≥ AT v2.2.0 才支持 MQTT
常用 AT 指令
AT+RST ; 复位
AT+CWMODE=1 ; Station 模式
AT+CWJAP="SSID","PASSWORD" ; 连接 WiFi
AT+CIPMUX=0 ; 单连接
AT+CIPSTART="TCP","broker.emqx.io",1883
AT+MQTTUSERCFG=0,1,"client_id","user","password",0,0,""
AT+MQTTCONN=0,"broker.emqx.io",1883,1
AT+MQTTPUB=0,"topic","payload",1,0
AT+MQTTSUB=0,"topic",1
四、AT 驱动层设计(FreeRTOS 安全写法)
1、AT 命令结构体
typedef struct {
const char *cmd;
const char *expect;
uint32_t timeout_ms;
} at_cmd_t;
2、AT 发送接口(线程安全)
SemaphoreHandle_t at_mutex;
bool AT_SendCmd(const at_cmd_t *at)
{
xSemaphoreTake(at_mutex, portMAX_DELAY);
HAL_UART_Transmit(&huart1, (uint8_t *)at->cmd, strlen(at->cmd), 100);
HAL_UART_Transmit(&huart1, (uint8_t *)"\r\n", 2, 10);
bool ret = AT_WaitResponse(at->expect, at->timeout_ms);
xSemaphoreGive(at_mutex);
return ret;
}
五、ESP8266 MQTT 封装
1、MQTT 客户端结构体
typedef struct {
char broker[64];
uint16_t port;
char client_id[32];
char user[32];
char password[32];
} mqtt_client_t;
2、MQTT 连接
bool MQTT_Connect(mqtt_client_t *cfg)
{
char cmd[128];
sprintf(cmd, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"",
cfg->client_id, cfg->user, cfg->password);
if (!AT_SendCmd(&(at_cmd_t){cmd, "OK", 2000}))
return false;
sprintf(cmd, "AT+MQTTCONN=0,\"%s\",%d,1",
cfg->broker, cfg->port);
return AT_SendCmd(&(at_cmd_t){cmd, "OK", 5000});
}
3、MQTT 发布(带 QoS)
bool MQTT_Publish(const char *topic, const char *payload, int qos)
{
char cmd[256];
sprintf(cmd, "AT+MQTTPUB=0,\"%s\",\"%s\",%d,0", topic, payload, qos);
return AT_SendCmd(&(at_cmd_t){cmd, "OK", 3000});
}
4、MQTT 订阅
bool MQTT_Subscribe(const char *topic, int qos)
{
char cmd[128];
sprintf(cmd, "AT+MQTTSUB=0,\"%s\",%d", topic, qos);
return AT_SendCmd(&(at_cmd_t){cmd, "OK", 3000});
}
六、FreeRTOS 示例任务
MQTT 客户端任务
void MQTT_Client_Task(void *arg)
{
mqtt_client_t mqtt = {
.broker = "broker.emqx.io",
.port = 1883,
.client_id = "esp8266_freertos",
.user = "",
.password = ""
};
while (1) {
if (!MQTT_Connect(&mqtt)) {
vTaskDelay(pdMS_TO_TICKS(5000));
continue;
}
MQTT_Subscribe("/device/ctrl", 1);
while (1) {
// 心跳
MQTT_Publish("/device/ping", "online", 1);
vTaskDelay(pdMS_TO_TICKS(30000));
}
}
}
业务发布任务(如电源数据)
void App_Publish_Task(void *arg)
{
char payload[128];
while (1) {
float vout = Read_Voltage();
float iout = Read_Current();
snprintf(payload, sizeof(payload),
"{\"v\":%.2f,\"i\":%.2f}", vout, iout);
MQTT_Publish("/device/telemetry", payload, 1);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
参考代码 基于Freertos的ESP8266 AT指令实现MQTT www.youwenfan.com/contentcsv/72586.html
七、ESP8266 主动推送(URC)处理
ESP8266 会主动发:
+MQTTSUBRECV:0,"/device/ctrl",5,hello
必须单独一个高优先级任务解析
void AT_Recv_Task(void *arg)
{
uint8_t ch;
while (1) {
if (HAL_UART_Receive(&huart1, &ch, 1, 10) == HAL_OK) {
Parse_AT_URC(ch);
}
}
}
void Parse_AT_URC(uint8_t ch)
{
static char buf[256];
static uint16_t idx = 0;
buf[idx++] = ch;
if (strstr(buf, "+MQTTSUBRECV")) {
// 提取 topic / payload
Handle_MQTT_Command(buf);
idx = 0;
}
}
八、常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
| MQTT 断线 | ESP8266 自动掉线 | 定时 AT+MQTTCONN? 检查 |
| 数据乱码 | 波特率不匹配 | 115200 8N1 |
| 卡死 | AT 阻塞 | 所有 AT 必须超时 |
| 丢包 | URC 覆盖 | 环形缓冲区 |
| OTA 失败 | Flash 不够 | 用 ESP-12F |
九、推荐 AT 固件版本
ESP8266 AT v2.2.0+
关闭回显:ATE0