MQTT 是基于发布 / 订阅模式的轻量级物联网通信协议,核心角色包括:服务端(Broker)发布者(Publisher)订阅者(Subscriber)。主要用于传感器数据上传至云平台,或从平台接收远程控制指令,广泛应用于低带宽、低功耗的嵌入式设备中。

QoS 消息质量等级:

        0:最多传输一次 → 发送后不确认,丢包不重发(速度快、适合周期性传感器数据)

        1:至少传输一次 → 保证消息送达,未收到应答会重发(稳定、适合控制指令)

         2:仅传输一次 → 严格确保消息只送达一次(最安全、复杂度高,普通项目一般不使用)

项目通常使用 ESP8266/ESP32/4G/EC20/EC600 等网络模块,通过 串口 + AT 指令 实现联网与 MQTT 通信。不同模块的 AT 指令略有差异,但整体流程完全一致。

串口:串口通讯

串口通信要求

  1. 串口初始化必须正常,波特率匹配;
  2. 每条 AT 指令结尾必须加 \r\n(回车换行);
  3. 每发送一条指令,必须等待模块返回响应(OK/ERROR)。
void Send_AT_Command(uint8_t *at_cmd)
{
    USART_SendStr_Reg(at_cmd);  // 发送AT指令
    USART_SendStr_Reg("\r\n");  // 添加回车换行符
}
 

流程(以ESP8266为例):

        一、模块初始化

                1.测试模块,串口发送AT
sprintf(cmd, "AT");
Send_AT_Command(cmd);
                2.关闭回显(避免干扰数据解析)
sprintf(cmd, "ATE0");
Send_AT_Command(cmd);
               3.恢复出厂/复位(可选)
sprintf(cmd, "AT+RST");
Send_AT_Command(cmd);

        二、接入网络(WiFi类(ESP8266/ESP32)、4G类(EC20/EC600))

                1.WiFi类
                         (1)设置STA模式
sprintf(cmd, "AT+CWMODE=1");  //1:STA   0:AP
Send_AT_Command(cmd);
                        (2)连接路由器
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"", "wifi名", "WiFi密码");
Send_AT_Command(cmd);

        三、建立传输链路

                1.TCP连接
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%S\",%d",服务器IP,端口号);
Send_AT_Command(cmd);
                 2.开启透传模式
sprintf(cmd, "AT+CIPMODE=1");
Send_AT_Command(cmd);
                 3.进入透传(开启后不再解析AT指令,串口数据直接发送到网络)
sprintf(cmd, "AT+CIPSEND");
Send_AT_Command(cmd);

        四、MQTT业务流程

                1.配置MQTT信息
//0:链路编号(固定)  1:MQTT协议版本
//client123:客户端id   user:用户名   pwd:用户密码
//0:标志位    60:心跳包(保活)
sprintf(cmd, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,%d,\"\"",客户端id,用户名,用户密码,心跳包);
Send_AT_Command(cmd);
                2.连接MQTT服务器
//0:链路编号   服务器ip         端口号  0:清理会话标志
sprintf(cmd, "AT+MQTTCONN=0,\"%s\",%d,0",服务器ip,端口号);
Send_AT_Command(cmd);
               3.订阅主题(单片机发送数据到服务器)
// 0:链路编号   topic:主题名   1:Qos消息质量等级
sprintf(cmd, "AT+MQTTSUB=0,\"%s\",1",topic);
Send_AT_Command(cmd);
               4.发布主题(发布JSON数据)

适合少量数据(和大量数据发送的AT指令不同):

//0:链路编号   发布主题                 消息内容           Qos  保留  
sprintf(cmd, "AT+MQTTPUB=0,\"%s\",\"{\\\"weight\\\":%d}\",1,0",发布主题,体重值);
Send_AT_Command(cmd);

适合大量数据:使用JSON官方库(cJSON)

cjson官方库

/**********构建JSON数据*********/
// 创建JSON根对象
cJSON *root = cJSON_CreateObject();
if(root == NULL) 
{
    USART_SendStr_Reg((uint8_t *)"JSON创建失败\r\n");
    return NULL;
}

//添加JSON字段
cJSON_AddStringToObject(root, "device_id", "stm32f103");
cJSON_AddNumberToObject(root, "temperature", temp);

//JSON转字符串
char *json_str = cJSON_PrintUnformatted(root);
if (json_str != NULL)
{
    uint16_t payload_len = strlen(json_str); // 消息长度
    uint8_t timeout = 0;
    USART1_RX_LEN = 0;
    memset(USART1_RX_BUF, 0, sizeof(USART1_RX_BUF));

    // 格式:AT+MQTTPUBRAW=链路号,"主题",长度,Qos,保留
    char at_cmd[256];
    sprintf(at_cmd, "AT+MQTTPUBRAW=0,\"topic\",%d,0,0", payload_len);
    Send_AT_Command((uint8_t *)at_cmd);  // 发送指令

    while(timeout < 100)  // 1秒超时
    {
        HalCore_DelayMS(10); 
        if(strstr((char*)USART1_RX_BUF, ">") != NULL)
        {
            Send_AT_Command((uint8_t*)json_str); 
            break;
        }
        timeout++;
    }
    cJSON_free(json_str);
}

cJSON_Delete(root); // 释放JSON对象

           五、JSON数据解析(订阅主题后服务器发送的数据),建议使用json库(容错性强),也可使用如下代码(容错性弱)

#include <string.h>
#include <stdio.h>
//键值解析函数
// 从JSON中,根据 键(key) 提取 值(value)
// json: 完整JSON字符串
// key: 要找的键名 如 "led"
// value: 提取到的值存这里
int GetJsonValue(char *json, char *key, char *value)
{
    char buf[64];
    char *start, *end;

    // 1. 拼接查找键: "key"
    sprintf(buf, "\"%s\":", key);

    // 2. 查找键的位置
    start = strstr(json, buf);
    if(start == NULL) return -1; // 没找到

    // 3. 跳过键,指向值开始
    start += strlen(buf);

    // 4. 去掉字符串的引号
    if(*start == '"') start++;

    // 5. 找值的结束位置
    if(*start == '"')
    {
        start++;
        end = strchr(start, '"');
    }
    else
    {
        end = strchr(start, ',');
        if(end == NULL) end = strchr(start, '}');
    }

    if(end == NULL) return -2;

    // 6. 把值复制出来
    strncpy(value, start, end - start);
    value[end - start] = '\0';

    return 0; // 成功
}
GetJsonValue(rx_buf, "led", value);
printf("led = %s\n", value); // 输出 on

        六、异常处理机制

       当设备出现网络断开、MQTT 掉线、指令发送失败、模块无响应等异常时,执行自动重连:

1. 关闭旧的MQTT连接

sprintf(cmd, "AT+MQTTCLEAN=0");   // 关闭编号为0的MQTT连接
Send_AT_Command(cmd);
HAL_Delay(500);                   // 等待模块执行完成

2. 关闭TCP链路

sprintf(cmd, "AT+CIPCLOSE");      // 断开TCP连接
Send_AT_Command(cmd);
HAL_Delay(1000);                  // 等待断开完成

3. 重新连接WiFi/4G网络

4. 重新配置MQTT参数

5. 重新连接服务器并订阅主题

目前物联网设备主流通信方案为 MQTT + JSON

  • MQTT 负责稳定、低功耗的发布 / 订阅消息传输;
  • JSON 负责标准化数据封装与解析;
  • 配合 AT 指令控制网络模块,可快速实现设备上云、数据上传、远程控制等功能。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐