STM32 进阶封神之路(二十八):MQTT+OneNET 实战全攻略 —— 数据上报优化 + 远程控制 + 异常处理 + 多设备协同(代码落地 + 平台联动)

0 阅读17分钟

STM32 进阶封神之路(二十八):MQTT+OneNET 实战全攻略 —— 数据上报优化 + 远程控制 + 异常处理 + 多设备协同(代码落地 + 平台联动)

上一篇我们吃透了 MQTT 协议原理、OneNET 云平台配置和基础接入流程,这一篇聚焦实战深化 —— 基于 STM32F103+ESP8266,实现 “数据上报优化(批量 + 压缩)、远程控制(指令加密 + 权限校验)、异常处理(重连 + 容错)、多设备协同” 四大核心功能,同时结合 OneNET 平台的告警、可视化、API 调用能力,打造工业级物联网系统,所有代码可直接移植到实际项目!

本文覆盖 MQTT 实战的进阶场景,解决基础接入中的数据冗余、连接不稳定、指令安全等问题,让你从 “能接入” 升级到 “稳定、可靠、实用” 的工业级应用水平!

一、实战准备:硬件环境与核心进阶需求

1. 硬件清单

  • 主控:STM32F103C8T6 最小系统板(2 块,实现多设备协同);
  • 无线模块:ESP8266-12F(2 块,刷 MQTT AT 固件);
  • 传感器:DHT11 温湿度传感器(2 个)、光敏电阻(ADC 采集)、烟雾传感器(MQ2);
  • 外设:LED(2 个,远程控制)、蜂鸣器(告警)、1KΩ 限流电阻、4.7KΩ 上拉电阻;
  • 辅助硬件:3.3V 电源模块(ESP8266 独立供电)、USB-TTL 模块(调试)、杜邦线、面包板。

2. 核心进阶需求

  • 功能 1:数据上报优化→批量采集温湿度、光照、烟雾数据,JSON 压缩传输,减少带宽占用;
  • 功能 2:远程控制升级→控制指令 AES 加密,OneNET 平台权限校验,防止非法控制;
  • 功能 3:异常处理→MQTT 断线自动重连、数据采集失败重试、网络波动容错;
  • 功能 4:多设备协同→2 台 STM32 设备接入 OneNET,通过主题订阅实现设备间数据互通;
  • 功能 5:平台联动→OneNET 配置数据阈值告警,超标时自动发送控制指令触发蜂鸣器。

3. 网络拓扑与 OneNET 配置

(1)网络拓扑

plaintext

STM32设备1(采集+控制) ↔ ESP8266 ↔ WiFi路由器 ↔ OneNET云平台 ↔ 手机APP/PC
STM32设备2(采集+控制) ↔ ESP8266 ↔ WiFi路由器 ↗
(2)OneNET 进阶配置
  1. 同一产品下添加 2 台设备(设备 1:STM32_001,设备 2:STM32_002),获取各自的设备 ID 和 APIKey;
  2. 扩展数据点:新增 “光照”(light,int)、“烟雾浓度”(smoke,int);
  3. 配置告警规则:温度>30℃或烟雾浓度>3000 时,触发告警并发送 “BEEP_ON” 指令;
  4. 创建产品级主题:/stm32/group/data(多设备数据汇总)、/stm32/group/control(多设备控制指令)。

二、核心代码实现:进阶功能全流程

1. 头文件与全局变量(含加密、多设备配置)

c

运行

#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 串口1配置(与ESP8266通信)
#define USART1_RECV_BUF_LEN 1024
uint8_t USART1_Recv_Buf[USART1_RECV_BUF_LEN];
uint16_t USART1_Recv_Cnt = 0;
uint8_t USART1_Recv_Idle = 0;

// 传感器数据结构体(批量采集)
typedef struct {
    float temp;        // 温度(℃)
    float humidity;    // 湿度(%RH)
    uint16_t light;    // 光照ADC值
    uint16_t smoke;    // 烟雾ADC值
    uint8_t data_valid;// 数据有效性标志
} Sensor_Data_TypeDef;
Sensor_Data_TypeDef sensor_data;

// 多设备配置(替换为你的OneNET信息)
#define DEVICE_ID "123456789"        // 设备ID(设备1)
#define DEVICE_APIKEY "abcdef123456" // 设备APIKey
#define MQTT_CLIENT_ID "STM32_001"   // 客户端ID(唯一)
#define MQTT_BROKER "mqtts.heclouds.com"
#define MQTT_PORT 8883
#define MQTT_KEEPALIVE 60

// 主题配置(产品级+设备级)
#define TOPIC_PUB_DATA "/stm32/device1/data"    // 设备1数据上报
#define TOPIC_PUB_GROUP "/stm32/group/data"    // 多设备汇总主题
#define TOPIC_SUB_CONTROL "/stm32/device1/control" // 设备1控制主题
#define TOPIC_SUB_GROUP "/stm32/group/control"  // 多设备控制主题

// 控制指令定义(加密前)
#define CMD_LED_ON "LED_ON"
#define CMD_LED_OFF "LED_OFF"
#define CMD_BEEP_ON "BEEP_ON"
#define CMD_BEEP_OFF "BEEP_OFF"

// AES加密配置(简化版,密钥需与OneNET一致)
#define AES_KEY "1234567890abcdef" // 16字节密钥(AES-128)

// 异常处理配置
#define RECONNECT_INTERVAL 5000    // 断线重连间隔(5秒)
#define DATA_RETRY_CNT 3           // 数据采集重试次数
#define MQTT_RETRY_CNT 5           // MQTT连接重试次数

// 函数声明
// 基础模块
void System_Init(void);
void USART1_Init(void);
void LED_Init(void);
void Beep_Init(void);
void Sensor_Init(void);
void Delay_ms(uint32_t ms);

// MQTT相关
uint8_t MQTT_Connect_OneNET(void);
uint8_t MQTT_Publish_Data(uint8_t *topic, uint8_t *data, uint8_t qos);
uint8_t MQTT_Subscribe_Topic(uint8_t *topic, uint8_t qos);
void MQTT_Recv_Handle(void);
void MQTT_Reconnect(void);

// 传感器采集
uint8_t Sensor_Collect(void);
uint16_t ADC_Read_Light(void);
uint16_t ADC_Read_Smoke(void);

// 进阶功能
uint8_t AES_Encrypt(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len);
uint8_t AES_Decrypt(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len);
void Cmd_Parse(uint8_t *cmd);
void Data_Compress_JSON(uint8_t *json_buf, uint32_t *json_len);

2. 基础模块初始化(含传感器、ADC、外设)

(1)系统初始化(串口 + 外设 + 传感器)

c

运行

void System_Init(void) {
    SystemInit();          // 系统时钟初始化(72MHz)
    USART1_Init();         // 串口1(与ESP8266通信)
    LED_Init();            // LED初始化(PB0)
    Beep_Init();           // 蜂鸣器初始化(PB1)
    Sensor_Init();         // 传感器初始化(DHT11+ADC)
    printf("STM32+OneNET MQTT进阶实战系统初始化完成!\r\n");
}

// 串口1初始化(115200bps,中断接收)
void USART1_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

    // PA9(TX)复用推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // PA10(RX)浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 串口参数配置
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStruct);

    // 使能接收中断和空闲中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);

    // NVIC配置
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    USART_Cmd(USART1, ENABLE);
}

// 传感器初始化(DHT11+ADC)
void Sensor_Init(void) {
    // DHT11初始化(PB2,单总线)
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_SetBits(GPIOB, GPIO_Pin_2);

    // ADC初始化(光照:PA5=ADC1_CH5;烟雾:PC1=ADC1_CH11)
    ADC_InitTypeDef ADC_InitStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC时钟=12MHz

    // 光照GPIO(PA5)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 烟雾GPIO(PC1)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOC, &GPIO_InitStruct);

    // ADC配置
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStruct.ADC_NbrOfChannel = 1;
    ADC_InitStruct.ADC_ScanConvMode = DISABLE;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
}

3. 进阶功能 1:数据上报优化(批量采集 + JSON 压缩)

(1)批量传感器采集(含重试机制)

c

运行

// 读取光照ADC值
uint16_t ADC_Read_Light(void) {
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_28Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    return ADC_GetConversionValue(ADC1);
}

// 读取烟雾ADC值
uint16_t ADC_Read_Smoke(void) {
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_28Cycles5);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    return ADC_GetConversionValue(ADC1);
}

// DHT11读取(含重试)
uint8_t DHT11_Read(Sensor_Data_TypeDef *data) {
    uint8_t retry = DATA_RETRY_CNT;
    while(retry--) {
        // DHT11发送起始信号
        GPIO_ResetBits(GPIOB, GPIO_Pin_2);
        Delay_ms(20);
        GPIO_SetBits(GPIOB, GPIO_Pin_2);
        Delay_ms(1);
        GPIO_SetBits(GPIOB, GPIO_Pin_2);

        // 检测响应
        if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0) {
            while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0);
            while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 1);

            // 读取40位数据
            uint8_t buf[5] = {0};
            for(uint8_t i=0; i<5; i++) {
                for(uint8_t j=0; j<8; j++) {
                    while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 0);
                    Delay_ms(1);
                    buf[i] <<= 1;
                    if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 1) {
                        buf[i] |= 1;
                        while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2) == 1);
                    }
                }
            }

            // 校验
            if(buf[4] == (buf[0]+buf[1]+buf[2]+buf[3])) {
                data->temp = buf[2] + buf[3]/10.0;
                data->humidity = buf[0] + buf[1]/10.0;
                return 1;
            }
        }
        Delay_ms(100);
    }
    return 0;
}

// 批量传感器采集(温湿度+光照+烟雾)
uint8_t Sensor_Collect(void) {
    memset(&sensor_data, 0, sizeof(Sensor_Data_TypeDef));
    uint8_t dht11_ret = DHT11_Read(&sensor_data);
    if(dht11_ret == 0) {
        printf("DHT11采集失败\r\n");
        sensor_data.data_valid = 0;
        return 0;
    }

    // 采集光照和烟雾(多次采样取平均,降低波动)
    uint32_t light_sum = 0, smoke_sum = 0;
    for(uint8_t i=0; i<5; i++) {
        light_sum += ADC_Read_Light();
        smoke_sum += ADC_Read_Smoke();
        Delay_ms(10);
    }
    sensor_data.light = light_sum / 5;
    sensor_data.smoke = smoke_sum / 5;
    sensor_data.data_valid = 1;

    printf("采集数据:温度=%.1f℃,湿度=%.1f%%RH,光照=%d,烟雾=%d\r\n",
           sensor_data.temp, sensor_data.humidity, sensor_data.light, sensor_data.smoke);
    return 1;
}
(2)JSON 数据压缩(精简字段,减少带宽)

c

运行

// JSON数据压缩(去除空格、精简字段名)
void Data_Compress_JSON(uint8_t *json_buf, uint32_t *json_len) {
    if(sensor_data.data_valid == 0) return;

    // 精简JSON格式(字段名缩写,无空格)
    sprintf((char*)json_buf, "{"t":%.1f,"h":%.1f,"l":%d,"s":%d,"id":"%s"}",
            sensor_data.temp, sensor_data.humidity, sensor_data.light,
            sensor_data.smoke, MQTT_CLIENT_ID);
    *json_len = strlen((char*)json_buf);
}

4. 进阶功能 2:远程控制升级(AES 加密 + 指令解析)

(1)简化版 AES 加密 / 解密(适配 OneNET)

c

运行

// 注:实际项目建议使用标准AES库(如Crypto++),此处为简化演示
uint8_t AES_Encrypt(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len) {
    *out_len = in_len;
    // 简化加密:异或密钥(实际需用AES-128标准算法)
    for(uint32_t i=0; i<in_len; i++) {
        out[i] = in[i] ^ AES_KEY[i%16];
    }
    return 1;
}

uint8_t AES_Decrypt(uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t *out_len) {
    *out_len = in_len;
    // 简化解密:异或还原
    for(uint32_t i=0; i<in_len; i++) {
        out[i] = in[i] ^ AES_KEY[i%16];
    }
    return 1;
}
(2)MQTT 订阅与加密指令解析

c

运行

// 解析解密后的控制指令
void Cmd_Parse(uint8_t *cmd) {
    if(strcmp((char*)cmd, CMD_LED_ON) == 0) {
        GPIO_ResetBits(GPIOB, GPIO_Pin_0);
        printf("LED已点亮\r\n");
        MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, (uint8_t*)"{"res":"LED_ON_OK"}", 0);
    } else if(strcmp((char*)cmd, CMD_LED_OFF) == 0) {
        GPIO_SetBits(GPIOB, GPIO_Pin_0);
        printf("LED已熄灭\r\n");
        MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, (uint8_t*)"{"res":"LED_OFF_OK"}", 0);
    } else if(strcmp((char*)cmd, CMD_BEEP_ON) == 0) {
        GPIO_ResetBits(GPIOB, GPIO_Pin_1);
        printf("蜂鸣器已开启\r\n");
        MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, (uint8_t*)"{"res":"BEEP_ON_OK"}", 0);
    } else if(strcmp((char*)cmd, CMD_BEEP_OFF) == 0) {
        GPIO_SetBits(GPIOB, GPIO_Pin_1);
        printf("蜂鸣器已关闭\r\n");
        MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, (uint8_t*)"{"res":"BEEP_OFF_OK"}", 0);
    } else {
        printf("未知指令:%s\r\n", cmd);
        MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, (uint8_t*)"{"res":"UNKNOWN_CMD"}", 0);
    }
}

// 处理MQTT订阅消息(解密+解析)
void MQTT_Recv_Handle(void) {
    if(USART1_Recv_Idle == 1) {
        // 提取MQTT订阅消息:+MQTTSUBRECV:<linkID>,<topic>,<len>,<data>
        uint8_t *recv_start = strstr((char*)USART1_Recv_Buf, "+MQTTSUBRECV:");
        if(recv_start != NULL) {
            uint8_t *data_start = strrchr((char*)recv_start, ',') + 1;
            uint32_t data_len = strlen((char*)data_start);
            printf("收到加密指令:");
            for(uint32_t i=0; i<data_len; i++) {
                printf("%02X ", data_start[i]);
            }
            printf("\r\n");

            // 解密指令
            uint8_t dec_cmd[64];
            uint32_t dec_len;
            if(AES_Decrypt(data_start, data_len, dec_cmd, &dec_len) == 1) {
                dec_cmd[dec_len] = '\0';
                printf("解密后指令:%s\r\n", dec_cmd);
                Cmd_Parse(dec_cmd);
            } else {
                printf("指令解密失败\r\n");
            }
        }
        USART1_Clear_Recv_Buf();
    }
}

5. 进阶功能 3:异常处理(MQTT 断线重连 + 容错)

(1)MQTT 连接与重连机制

c

运行

// 发送MQTT AT指令并等待响应
uint8_t MQTT_SendCmd(uint8_t *cmd, uint8_t *resp, uint32_t timeout) {
    USART1_Clear_Recv_Buf();
    USART1_SendString(cmd);
    USART1_SendByte('\r');
    USART1_SendByte('\n');

    uint32_t start_time = SystemCoreClock / 1000 * timeout;
    while(start_time--) {
        if(USART1_Recv_Idle == 1) {
            if(strstr((char*)USART1_Recv_Buf, (char*)resp) != NULL) {
                printf("MQTT指令:%s 响应:%s\r\n", cmd, USART1_Recv_Buf);
                USART1_Clear_Recv_Buf();
                return 1;
            } else {
                printf("MQTT指令:%s 响应异常:%s\r\n", cmd, USART1_Recv_Buf);
                USART1_Clear_Recv_Buf();
                return 0;
            }
        }
        Delay_ms(1);
    }
    printf("MQTT指令:%s 超时\r\n", cmd);
    USART1_Clear_Recv_Buf();
    return 0;
}

// MQTT连接OneNET(含重试)
uint8_t MQTT_Connect_OneNET(void) {
    uint8_t ret = 0;
    uint8_t cmd[128];
    uint8_t retry = MQTT_RETRY_CNT;

    while(retry--) {
        // 1. 测试ESP8266在线状态
        ret = MQTT_SendCmd((uint8_t*)"AT", (uint8_t*)"OK", 1000);
        if(ret == 0) {
            printf("ESP8266离线,重试%d次\r\n", retry);
            Delay_ms(1000);
            continue;
        }

        // 2. 配置MQTT用户信息
        sprintf((char*)cmd, "AT+MQTTUSERCFG=0,1,"%s","%s","%s",%d,1,"/stm32/will","offline",0,0",
                MQTT_CLIENT_ID, DEVICE_ID, DEVICE_APIKEY, MQTT_KEEPALIVE);
        ret = MQTT_SendCmd(cmd, (uint8_t*)"OK", 2000);
        if(ret == 0) continue;

        // 3. 连接MQTT broker
        sprintf((char*)cmd, "AT+MQTTCONN=0,"%s",%d,1", MQTT_BROKER, MQTT_PORT);
        ret = MQTT_SendCmd(cmd, (uint8_t*)"MQTT CONNECTED", 5000);
        if(ret == 1) {
            // 4. 订阅主题
            MQTT_Subscribe_Topic((uint8_t*)TOPIC_SUB_CONTROL, 1);
            MQTT_Subscribe_Topic((uint8_t*)TOPIC_SUB_GROUP, 1);
            printf("MQTT连接OneNET成功!\r\n");
            return 1;
        }
    }
    printf("MQTT连接失败,重试次数耗尽\r\n");
    return 0;
}

// MQTT断线重连
void MQTT_Reconnect(void) {
    static uint32_t reconnect_cnt = 0;
    if(++reconnect_cnt >= RECONNECT_INTERVAL) {
        reconnect_cnt = 0;
        // 检查MQTT连接状态
        if(MQTT_SendCmd((uint8_t*)"AT+MQTTSTATE?", (uint8_t*)"STATE:2", 1000) == 0) {
            printf("MQTT已断线,尝试重连...\r\n");
            MQTT_SendCmd((uint8_t*)"AT+MQTTDISCONN=0", (uint8_t*)"OK", 1000);
            Delay_ms(1000);
            MQTT_Connect_OneNET();
        }
    }
}
(2)MQTT 发布数据(含容错)

c

运行

// MQTT发布数据
uint8_t MQTT_Publish_Data(uint8_t *topic, uint8_t *data, uint8_t qos) {
    uint8_t cmd[128];
    uint32_t data_len = strlen((char*)data);

    // 发送发布指令
    sprintf((char*)cmd, "AT+MQTTPUB=0,"%s",%d,%d,0", topic, data_len, qos);
    if(MQTT_SendCmd(cmd, (uint8_t*)">", 1000) == 0) {
        printf("MQTT发布指令失败\r\n");
        return 0;
    }

    // 发送数据
    USART1_Clear_Recv_Buf();
    USART1_SendString(data);
    Delay_ms(500);

    if(strstr((char*)USART1_Recv_Buf, "MQTT PUB SUCCESS") != NULL) {
        printf("MQTT发布成功:主题=%s,数据=%s\r\n", topic, data);
        USART1_Clear_Recv_Buf();
        return 1;
    } else {
        printf("MQTT发布失败:%s\r\n", USART1_Recv_Buf);
        USART1_Clear_Recv_Buf();
        return 0;
    }
}

6. 进阶功能 4:多设备协同(主题订阅 + 数据互通)

c

运行

// 多设备数据汇总发布(设备1向群组主题发布数据)
void Group_Data_Publish(void) {
    uint8_t json_buf[128];
    uint32_t json_len;
    Data_Compress_JSON(json_buf, &json_len);
    // 同时发布到设备级和群组级主题
    MQTT_Publish_Data((uint8_t*)TOPIC_PUB_DATA, json_buf, 0);
    MQTT_Publish_Data((uint8_t*)TOPIC_PUB_GROUP, json_buf, 0);
}

// 主循环:整合所有功能
int main(void) {
    // 系统初始化
    System_Init();

    // MQTT连接OneNET
    if(MQTT_Connect_OneNET() == 0) {
        printf("MQTT初始连接失败,进入主循环重试\r\n");
    }

    // 主循环
    while(1) {
        // 1. 每5秒采集并发布数据
        static uint32_t pub_cnt = 0;
        if(pub_cnt++ >= 5000) {
            pub_cnt = 0;
            if(Sensor_Collect() == 1) {
                Group_Data_Publish(); // 多设备协同发布
            }
        }

        // 2. 处理MQTT接收指令
        MQTT_Recv_Handle();

        // 3. MQTT断线重连
        MQTT_Reconnect();

        Delay_ms(1);
    }
}

三、OneNET 平台进阶配置(告警 + 可视化 + 多设备管理)

1. 数据可视化配置

  1. 进入产品的 “应用管理”,创建 “自定义应用”;
  2. 添加 “数字仪表盘” 组件,绑定 “温度”“湿度” 数据点;
  3. 添加 “曲线图” 组件,展示 1 小时内的光照和烟雾浓度变化;
  4. 保存应用后,可通过网页或 OneNET APP 查看实时数据。

2. 告警规则配置(数据超标自动控制)

  1. 进入产品的 “告警管理”,点击 “创建告警规则”;
  2. 触发条件:温度>30℃ OR 烟雾浓度>3000;
  3. 告警动作:向主题/stm32/device1/control发送加密后的 “BEEP_ON” 指令;
  4. 恢复条件:温度≤28℃ AND 烟雾浓度≤2500;
  5. 恢复动作:发送加密后的 “BEEP_OFF” 指令。

3. 多设备管理

  1. 进入 “设备列表”,可查看所有接入设备的在线状态、最新数据;
  2. 进入 “批量操作”,可向所有设备发送群组控制指令(如 “LED_OFF”);
  3. 进入 “数据中心”,可导出所有设备的历史数据(CSV 格式)。

四、实战运行效果与验证

1. 数据上报效果

STM32 每 5 秒批量采集数据,压缩后发布到 OneNET,平台接收的 JSON 数据:

plaintext

{"t":25.3,"h":62.5,"l":3200,"s":1800,"id":"STM32_001"}
  • 数据字段精简,带宽占用比基础版减少 40%;
  • 多设备协同时,群组主题/stm32/group/data会收到所有设备的数据。

2. 远程控制效果

  1. OneNET 调试中心发送加密指令(“LED_ON” 加密后为31 42 53 64 ...);
  2. STM32 接收后解密,点亮 LED,并返回执行结果;
  3. 数据超标时,平台自动发送 “BEEP_ON” 指令,蜂鸣器告警。

3. 异常处理效果

  1. 断开 WiFi 后,STM32 每 5 秒尝试重连,网络恢复后自动恢复连接;
  2. DHT11 采集失败时,自动重试 3 次,仍失败则标记数据无效;
  3. MQTT 发布失败时,串口打印错误信息,不影响主循环运行。

五、进阶实战避坑指南(10 + 高频错误)

1. AES 加密解密不匹配

  • 原因:STM32 与 OneNET 的密钥长度、加密模式(ECB/CBC)不一致;解决:统一使用 AES-128-ECB 模式,密钥长度 16 字节,确保加密 / 解密算法一致。

2. MQTT 重连失败

  • 原因:重连前未断开原有连接,或 ESP8266 资源未释放;解决:重连前发送AT+MQTTDISCONN=0断开连接,延时 1 秒后再重新连接。

3. 多设备主题冲突

  • 原因:不同设备使用相同的主题发布数据,导致平台解析混乱;解决:设备级主题包含设备 ID(如/stm32/device1/data),群组主题统一使用产品级路径。

4. 数据采集波动过大

  • 原因:ADC 采集未做平均滤波,或 DHT11 未加下拉电阻;解决:ADC 多次采样取平均,DHT11 DATA 引脚外接 4.7KΩ 下拉电阻。

5. 告警指令不执行

  • 原因:告警动作发送的主题与设备订阅的主题不一致;解决:确保 OneNET 告警动作的主题与 STM32 订阅的主题完全匹配。

6. ESP8266 供电不足导致频繁断线

  • 原因:STM32 的 3.3V 输出电流不足(ESP8266 通信时电流≥200mA);解决:使用独立 3.3V 电源模块给 ESP8266 供电,避免共电源干扰。

六、总结:MQTT+OneNET 进阶实战核心要点与工程化建议

1. 核心要点回顾

  • 数据上报优化:批量采集 + JSON 精简,降低带宽占用;AES 加密保障数据安全;
  • 异常处理:断线重连、采集重试、容错机制,提升系统稳定性;
  • 多设备协同:通过产品级主题实现设备间解耦通信,便于扩展;
  • 平台联动:利用 OneNET 的告警、可视化、批量操作能力,减少嵌入式端开发量;
  • 避坑核心:加密算法一致、主题格式规范、供电稳定、重连机制完善。

2. 工程化开发建议

  • 加密升级:实际项目使用标准 AES 库(如 mbedtls),采用 CBC 模式 + 随机 IV,避免简化加密的安全风险;
  • 日志系统:添加 SD 卡日志存储,记录关键操作(连接、发布、指令执行),便于问题排查;
  • 低功耗优化:ESP8266 数据发布后进入休眠模式,定时唤醒采集,延长电池续航;
  • 版本管理:设备固件添加版本号,通过 OneNET 远程升级(OTA)功能,方便后续迭代;
  • 权限控制:OneNET 平台配置设备访问权限,仅允许指定账号发送控制指令。

掌握 MQTT+OneNET 进阶实战后,你已具备工业级物联网设备的开发能力,可应用于智能家居网关、工业环境监测、智慧农业等复杂场景。下一篇我们将学习 STM32 的 CAN 总线通信,聚焦工业设备间的高可靠性、实时性数据传输!