项目 基于MQTT协议的STM32设备接入ThingsCloud平台的设计与实现

项目介绍

本项目是一个基于 STM32 裸机开发的物联网边缘节点解决方案。通过串口(AT指令)驱动 4G 通信模组,利用 MQTT 协议将设备成功接入 ThingsCloud 物联网云平台。项目实现了传感器数据的定时上报、云端指令的下发解析,以及设备状态的实时同步(双向通信闭环),为低功耗、远距离的工业/农业物联网设备提供了一套稳定可靠的上云模板。

基于老师给我的板子,目前没有传感器,原理图什么都不知道。所以我通过变量通过keil5的调试功能的Witch来判断平台是不是有数据下发。

 

项目知识

一、串口底层驱动与数据接收机制

 

知识点 1:不定长网络数据包的接收(RXNE + IDLE 中断组合)

 

在串口通信中,如果采用传统的接收中断(RXNE),每收到一个字节就会打断一次 CPU。而 4G 模组通过 MQTT 下发的 JSON 报文长度是不固定的,无法预知包尾在哪里。

 

解决方案:引入串口空闲中断(IDLE)。当一帧数据发送完毕,串口总线会空闲(静默)一个字节的时间,此时触发 IDLE 中断。结合 RXNE 中断,就能实现“来一个字节存一个,总线一空闲就通知主循环处理整包”的高效机制。

 

代码体现

//==== USART2 中断服务函数 ====
void USART2_IRQHandler(void)
{
    u8 Res2;
    // 1. 接收数据中断
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
    {
        USART_ClearITPendingBit(USART2, USART_IT_RXNE);
        Res2 = USART_ReceiveData(USART2); // 只读一次!
        if (wifi.buff_length < BUFF_MAX - 1)
        {
            wifi.BUFF[wifi.buff_length++] = Res2;
            wifi.BUFF[wifi.buff_length]   = '\0'; // 时刻保持字符串结尾
        }
    }
    // 2. 串口空闲中断 (说明这一帧数据收完了)
    else if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
    {
        USART2->SR;
        USART2->DR; // 清除 IDLE 标志
        wifi.buff_flag = 1; // 告诉主循环:有完整数据到达了
    }
}

二、AT 指令通信与状态控制

知识点 2:同步请求-应答机制(超时重试)

4G 模组通过 AT 指令控制,这是一种典型的“一问一答”同步模型。单片机发送指令后,必须等待模组的响应(如 OK、CONNECT 等)。如果模组死机或断网,不能无限期等待,必须引入超时机制,超时则视为失败,交由主程序决策(如重试或报错)。

代码体现

// 发送指令并等待预期应答
uint8_t WIFI_CheckAckchar *src, char *dest, int timeout)
{
    char *check = NULL;
    WIFI_BUFF_MEMSET(); // 发送前清空缓存
    usart2_send_stringsrc);

    while (timeout > 0) // 阻塞等待
    {
        delay_nms10);
        timeout -= 10;
        if (wifi.buff_flag == 1) // 收到一包数据
        {
            check = strstrwifi.BUFF, dest);
            if (check != NULL)
            {
                WIFI_BUFF_MEMSET();
                return 1; // 匹配成功
            }
            wifi.buff_flag = 0; // 没匹配到,继续等下一包
        }
    }
    return 0; // 超时失败
}

三、C 语言工程规范与编译器特性(核心踩坑)

知识点 3:Keil 长字符串解析 Bug 与解耦拼装

当 snprintf 的格式化字符串过长且跨行,内部包含大量反斜杠转义符(如 \\22、\\2C)时,Keil 的解析器容易出现类型判断混乱(报 argument of type "int" is incompatible)。
解决方案:采用“分步拼装法”。先用一个 snprintf 拼装纯业务数据,再用第二个 snprintf 将数据套入 AT 指令模板,化繁为简。

代码体现

    // 1. 先拼装纯业务 JSON 数据
    snprintf(json_payload, sizeof(json_payload), 
             "{\\22temperature\\22:%d.%d\\2C\\22light\\22:%d\\2C\\22switch\\22:%s}", 
             t_i, t_d, sim_light, sim_switch ? "true" : "false");

    // 2. 再拼装完整的 AT 指令
    snprintf(mqtt_ack_msg, sizeof(mqtt_ack_msg), 
             "AT+MPUB=\"%s\",0,0,\"%s\"\r\n", 
             MQTT_PUB_TOPIC, json_payload);

四、4G 模组底层协议与数据组装

知识点 4:合宙模组的 AT 指令转义陷阱(致命坑)

当通过合宙模组的 AT+MPUB 发送多属性 JSON 时,模组固件会优先将 JSON 内部的普通逗号 , 识别为 AT 指令自身的参数分隔符,导致 JSON 被截断,数据静默失败。必须将 JSON 内部的双引号转义为 \22,逗号转义为 \2C

代码体现

    // 注意看这里的 \\22 (代表 "),以及 \\2C (代表逗号)
    // 这种写法是为了骗过 4G 模组底层的解析器,将标准 JSON 发出去
    snprintf(json_payload, sizeof(json_payload), 
             "{\\22temperature\\22:%d.%d\\2C\\22light\\22:%d\\2C\\22switch\\22:%s}", 
             ...);

五、物联网业务逻辑设计

知识点 5:轻量级 JSON 特征解析

单片机 RAM 极小,不适合使用庞大的 cJSON 库。对于格式相对固定的云端下发指令(如 {"switch":true}),可以利用 C 语言的字符串查找函数 strstr 寻找键值对,并用 atoi 或 strncmp 快速提取数据。

代码体现

static char *find_value_start(char *json, const char *key)
{
    char *p = strstr(json, key); // 找键名
    if (p == NULL) return NULL;
    p = strchr(p, ':');          // 找冒号
    if (p == NULL) return NULL;
    p++;
    // 跳过空格、引号和转义符,直达数值
    while (*p == ' ' || *p == '"' || *p == '\\') p++;
    return p;
}

知识点 6:状态闭环(State Closure)控制 

当用户在云端平台点击开关,平台会通过 MQTT 下发指令。单片机解析并改变本地变量后,不能仅仅执行动作就结束了。必须立刻主动上报一次最新状态。这样云端后台才能确认设备已执行,从而将 UI 开关真正点亮,形成真正的双向通信闭环。

代码体现

// 在 mqtt_rx_Control 函数中
if (strstr(wifi.BUFF, "switch") != NULL)
{
    // 1. 解析出 sim_switch 的新值
    parse_bool_field (p_json, "switch", &sim_switch);

    // 2. 立即调用上报函数,把刚才的值发回给云端
    WIFI_BUFF_MEMSET();
    report_simulated_attributes(); 
    return;
}

项目实施

数据上报

此项目我用常量进行模拟,如下图。其次我们需要服务器IP,用户名,密码。这个在上个博客有讲这里就不多说了。

 

image

 

打开keil5的调试,在watch1窗口添加config_ok和sim_switch。(这两个量一个是看我是不是连上,另一个是看云端的数据有没有下发)关于数据的下发,还需要单片机去订阅 "attributes/push" 这个主题

 

image

 

当程序下载进去的时候,云台收到消息

 

image

 

从下面两张图看目前sim_switch是0x00,接下来的是属性下发。

sim_switch从0x00更改为0x01

 

image

 

image

 

也可以为sim_switch设置以下的看板,实现可视化。

 

image

 

image

 

image

 

posted on 2026-10-31 17:17  咋瓦鲁多_食我压路机  阅读(3)  评论(0)    收藏  举报