基于 FreeRTOS + ESP8266(AT 指令)+ MQTT的实现方案

基于 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

 

专注于matlab/simulink,电子电路,编程