
做物联网毕业设计或竞赛项目很多人不是卡在代码难写而是卡在“整条链路太长”。以厨房安全监测系统为例你需要让 STM32 采集温湿度、烟雾浓度通过 ESP01S 将数据推到 OneNET 云平台再在微信小程序里显示并远程控制继电器。单看任何一个节点网上都能找到教程一旦要求它们联调跑通各种问题就冒出来了——串口乱码、AT 指令不返回、MQTT 连接失败、小程序收不到数据……这篇文章的目的很明确帮你把这条链路完整走通。我会从系统架构、硬件选型、STM32 驱动代码、ESP01S 接入 OneNET、小程序对接、PCB 设计到常见排错按真实开发顺序讲一遍。内容以 STM32F103C8T6 ESP01S OneNET 这套主流方案为主线同样适用于智能家居、环境监测、宿舍安防等类似选题。1. 这个项目解决什么问题物联网毕设的真正难点先说一个观察很多做 STM32 毕设或竞赛项目的同学容易陷入“局部自嗨”的怪圈——传感器读取写得滚瓜烂熟OLED 屏幕显示也做得很好看但一提到上云就卡壳。为什么因为前面这些只是单片机基本功物联网项目真正考核的是“端-云-应用”三层链路的数据闭环。所谓“链路打通”指的是传感器数据能被 MCU 正确采集MCU 能把数据通过串口交给 WiFi 模块WiFi 模块能连上路由器、能连上云平台云平台上能正确创建产品、设备、数据流小程序/APP 能通过云平台接口读到设备数据、下发控制指令。任何一环断了整个项目都不算完成。这套基于 STM32 的厨房安全监测系统适合的读者包括物联网、电子信息、计算机相关专业的毕业生正在挑毕设题目准备参加电子设计竞赛、互联网、挑战杯的学生团队想完整掌握“STM32 云平台 小程序”全链路开发但又缺乏项目经验的开发者。项目完成后你的成果物不只是“一个能报警的设备”而是一整套可以演示、答辩、扩展的方案一块自研 PCB 底板集成了 STM32 最小系统、传感器接口、继电器驱动、ESP01S 座子一套 STM32 标准库工程包含传感器驱动、串口通信、AT 指令控制 WiFi 模块的逻辑一个 OneNET 云平台设备实例能生成 APIKey、数据流、命令下发一个微信小程序界面能实时显示温度、湿度、烟雾浓度并远程触发声光报警或排风扇。这套链路本身也是物联网行业里非常通用的能力。做完这个项目你已经摸清了嵌入式上云的基本方法后续换传感器、换 WiFi 模块、换云平台都是同一套思路的迁移。2. 系统总体架构与核心选型逻辑2.1 四层架构从数据流的角度看系统分为四层层级作用本项目的具体实现感知层采集厨房环境数据DHT11 温湿度、MQ-2 烟雾浓度、火焰传感器控制层处理数据、执行本地逻辑STM32F103C8T6 主控、继电器控制排风扇/电磁阀平台层数据中转、存储、API 输出OneNET 云平台通过 MQTT 协议接入应用层用户远程查看与控制微信小程序这四层不是孤立的。感知层把物理信号变成数字量控制层负责“本地判断 远程上传”平台层负责把设备与小程序解耦应用层则给用户提供可视化界面。2.2 控制逻辑本地逻辑可以这样设计STM32 每 2 秒采集一次温度、湿度、烟雾浓度如果烟雾浓度或温度超过阈值立即打开继电器控制排风扇或燃气电磁阀并驱动蜂鸣器报警无论是否报警数据都会通过 ESP01S 上传到 OneNET。这个“本地闭环”设计非常重要。它保证即使 WiFi 断开、云平台不可用厨房的安全机制依然能工作。很多初学者容易忽略这一点把所有判断都交给云端结果网络一抖设备就“失灵”了。2.3 为什么用 ESP01S 而不是 ESP8266 开发板ESP01S 本质上是 ESP8266 芯片的一个最小模组引出 8 个引脚其中可用 GPIO 只有 2 个。它没有独立的 USB 转串口电路也没有稳压和下载电路看起来对新手不太友好。但它有两个明显优势便宜单模块成本低适合做产品化的 PCB出厂自带 AT 固件不需要额外写 ESP8266 的固件把它当“串口转 WiFi 透明通道”来用就行。在毕设场景里ESP01S 是最省事的方案STM32 通过串口发 AT 指令ESP01S 负责 WiFi 连接与 MQTT 协议处理所有业务逻辑都在 STM32 上完成。如果你选用的是整块 NodeMCU 开发板也不是不行但体积大、集成度低做 PCB 时不好安排位置。2.4 为什么选 OneNETOneNET 是中移物联网的开放平台对校园用户和开发者比较友好。它的优势在于不需要自己买服务器、搭后端直接在平台上创建设备生成鉴权信息提供 MQTT 接入方式兼容性好数据流和命令下发清晰适合展示毕设的“端到端”能力。相比自建 MQTT 服务器OneNET 明显更适合课程设计和竞赛场景。它让项目可以聚焦在“设备端怎么发数据、应用端怎么读数据”而不是花大量精力去维护服务器。3. 硬件设计与原理图要点3.1 主控与核心器件本系统主控采用 STM32F103C8T6这是 STM32 家族里最常用的入门型号之一。它基于 Arm Cortex-M3 内核主频 72MHz内置 64KB Flash、20KB SRAM拥有 3 个 USART、2 个 SPI、2 个 I2C、多个定时器和 ADC。为什么够用传感器采集是模拟量 单总线协议PWM 或 ADC 就能解决与 ESP01S 通信只需要一个串口控制继电器、蜂鸣器只需要 GPIO不需要跑操作系统裸机轮询即可。其他核心器件包括器件作用备注DHT11采集温度、湿度单总线协议3 根线MQ-2 烟雾传感器检测可燃气体/烟雾浓度模拟量输出接到 ADC火焰传感器检测明火红外光谱数字或模拟量输出继电器模块控制排风扇、电磁阀5V 或 3.3V 触发注意驱动电流蜂鸣器本地声光报警有源蜂鸣器更简单ESP01SWiFi 通信3.3V 供电注意电流需求3.2 传感器选型逻辑厨房安全的核心是“燃气泄漏”和“温度异常”。MQ-2 对液化气、丙烷、氢气等可燃气体比较敏感而且价格低、资料多非常适合毕设演示。它输出模拟电压烟雾浓度越高电压越大接 STM32 的 ADC 引脚即可读取。DHT11 精度虽然不高但胜在协议简单、驱动例程到处都是用来做环境温湿度采集完全够用。如果你想让项目更有“工程感”可以换成 DHT22 或 SHT30但这属于后期优化不影响整体架构。火焰传感器主要有两种一种输出数字量检测到火焰时输出低电平一种输出模拟量可以判断火焰强度。建议接 ADC 引脚这样既能判断“有火”也能看“火势”。它的检测距离有限实际摆放时要靠近灶台区域。3.3 电源方案与继电器控制整个系统的供电可以选择 USB 5V 输入也可以选择 220V 转 5V 的电源模块。这里有一个关键点ESP01S 的工作电流瞬间可能达到 200mA 以上如果用 STM32 板载的 3.3V LDO 给它供电WiFi 连接瞬间大概率会复位。可靠的做法是5V 输入通过独立的 AMS1117-3.3 稳压芯片给 ESP01S 供电STM32 的 3.3V 也建议单独用一路 LDO。继电器驱动不要直接用 STM32 的 GPIO 去推。继电器线圈属于感性负载驱动时需要三极管或 ULN2003并且线圈两端要并联续流二极管否则断开瞬间的反向电动势可能损坏 GPIO。常用驱动结构GPIO 输出高电平经限流电阻接 NPN 三极管基极继电器线圈接在三极管集电极与 5V 之间线圈两端并联 1N4007 二极管负极接 5V。3.4 电路设计要点原理图设计需要注意几个细节STM32F103C8T6 的 BOOT0 接 10K 电阻到 GND避免上电后意外进入系统存储器模式NRST 引脚接 100nF 电容到地VDD 每个引脚就近放一个 100nF 去耦电容8MHz 晶振的两个负载电容通常选 22pF 左右但准确值要根据芯片数据手册中的负载电容参数计算公式是 CL (C1 * C2) / (C1 C2) 寄生电容ESP01S 的 VCC 引脚需要 3.3V且建议在模块附近放一个 10uF 或 100uF 电解电容应对 WiFi 发射时的电流尖峰。如果你用的是立创EDA嘉立创EDA画完原理图后可以直接检查 ERC、生成 PCB然后在线下单打样整套流程对新手比较友好网上相关教程也很多。4. STM32 开发环境搭建4.1 开发工具链本文代码基于 STM32 标准外设库Standard Peripheral Library开发环境是 Keil MDK。这套组合虽然是“老配方”但资料多、社区成熟非常适合课程设计和竞赛快速推进。需要准备的工具工具说明Keil MDK 5.x编写、编译、下载代码STM32F1xx 器件支持包在 Keil 中安装包含芯片型号定义ST-Link 驱动 / J-Link 驱动下载调试器驱动STM32 标准库提供外设驱动源码STM32 ST-LINK Utility用于连接异常时的芯片解锁与烧录串口调试助手查看 MCU 打印日志、调试 AT 指令4.2 Keil 安装 C51 与 STM32 共存问题很多同学电脑上之前装过 Keil C51再装 Keil MDK 时两个软件会默认装到不同目录。这里容易出的问题是打开一个项目时界面显示没有对应的编译器或芯片包。解决办法如果以 C51 为主先安装 Keil C51再安装 Keil MDK并把 C51 的安装目录指定为 Keil 安装目录下的 UV4 文件夹这样两者可以共存在 MDK 中打开项目时需要在 Project 窗口右键选择 Manage Project Items确认编译器版本是 ARM Compiler而不是 C51 编译器下载报错Error: Flash Download failed - Cortex-M3通常是因为没有在 Options for Target 的 Debug 页面配置 Flash Download或者芯片型号选错。这些问题的本质是“两个版本 Keil 的工程文件结构不同 芯片支持包缺失”。排查时先看 Tools 菜单下的 Pack Installer确认是否安装了对应的器件支持包。4.3 STM32 标准库工程结构标准库工程的典型结构如下Project/ ├── Core/ │ ├── inc/ │ └── src/ ├── HAL/ # 标准库使用 SystemInit 启动文件 ├── STM32F10x_FWLib/ │ ├── inc/ │ └── src/ ├── User/ │ ├── main.c │ ├── stm32f10x_it.c │ └── system_stm32f10x.c └── MDK-ARM/新建工程时建议手动建立以下几个用户文件bsp_usart.c/h串口初始化与重定向 printfbsp_dht11.c/hDHT11 驱动bsp_adc.c/hADC 采集模拟量esp01s_at.c/h封装 AT 指令发送与 MQTT 连接流程app_task.c/h主循环业务逻辑。这样组织的好处是传感器驱动、WiFi 通信、业务逻辑相互独立后期更换传感器或云平台时不用大面积改代码。5. 传感器驱动与数据采集代码5.1 DHT11 温湿度读取DHT11 是单总线器件通信时序相对固定主机先拉低总线至少 18ms再释放总线然后读取 40 位数据湿度整数、湿度小数、温度整数、温度小数、校验和。下面的代码是一个简化版示例重点看协议流程而不是追求完整产品级容错。// 文件路径User/bsp_dht11.c #include bsp_dht11.h #include stm32f10x.h #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_PIN GPIO_Pin_0 #define DHT11_RCC RCC_APB2Periph_GPIOB static uint8_t dht11_data[5]; static void DHT11_GPIO_Output(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin DHT11_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(DHT11_GPIO_PORT, GPIO_InitStructure); } static void DHT11_GPIO_Input(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin DHT11_GPIO_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(DHT11_GPIO_PORT, GPIO_InitStructure); } static uint8_t DHT11_ReadByte(void) { uint8_t i, byte 0; for (i 0; i 8; i) { while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) RESET); delay_us(40); if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) SET) { byte (byte 1) | 0x01; } else { byte (byte 1); } while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) SET); } return byte; } uint8_t DHT11_ReadData(float *temp, float *humi) { uint8_t i; DHT11_GPIO_Output(); GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); delay_ms(20); GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); delay_us(30); DHT11_GPIO_Input(); while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) SET); while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) RESET); while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) SET); for (i 0; i 5; i) { dht11_data[i] DHT11_ReadByte(); } if ((dht11_data[0] dht11_data[1] dht11_data[2] dht11_data[3]) dht11_data[4]) { *humi (float)((dht11_data[0] 8 | dht11_data[1]) 8); *temp (float)((dht11_data[2] 8 | dht11_data[3]) 8); return 1; } return 0; }这里有几个容易出错的地方起始信号的低电平时间必须足够短了传感器不响应常见问题是时序函数延时不准读取每一位时40us 的判断是区分“0”和“1”的关键不能死等校验和的判断不能省否则干扰环境下会出现离谱数据。5.2 MQ-2 烟雾浓度采集MQ-2 输出的是模拟电压STM32 采集它只需要 ADC 外设。// 文件路径User/bsp_adc.c #include bsp_adc.h #include stm32f10x.h void ADC_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, GPIO_InitStructure); ADC_InitStructure.ADC_Mode ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode DISABLE; ADC_InitStructure.ADC_ContinuousConvMode DISABLE; ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel 1; ADC_Init(ADC1, ADC_InitStructure); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); } uint16_t MQ2_Read(void) { ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) RESET); return ADC_GetConversionValue(ADC1); }ADC 采样值是一个 0 到 4095 的数字量对应 0 到 3.3V。如果你需要电压值可以折算float voltage (float)adc_value * 3.3f / 4095.0f;在实际项目里直接把原始 ADC 值作为“烟雾浓度指标”也可以因为报警阈值只需要一个相对量标定好“正常环境下的基准值”和“报警环境下的采样值”在这个区间内配置阈值即可。5.3 串口调试信息输出调试离不开串口。推荐把 printf 重定向到串口 1这样可以直接在串口助手看数据。// 文件路径User/bsp_usart.c #include bsp_usart.h #include stm32f10x.h #include stdio.h void USART1_Init(uint32_t baudrate) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate baudrate; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); } int fputc(int ch, FILE *f) { while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }串口调试时建议保持固定波特率比如 115200。如果发现乱码优先检查串口助手的波特率和单片机初始化参数是否一致。6. ESP01S 接入 OneNET 云平台的完整实现6.1 ESP01S 出厂 AT 固件的使用前提ESP01S 出厂一般刷好了 AT 固件使用前先用 USB 转 TTL 模块连接它确认能收到AT回复OK。常见的 AT 固件版本不同指令集也有差异。这里用一个保守但稳妥的说法ESP8266 官方 AT 指令集经过了多个版本迭代支持 MQTT 的指令在不同版本中格式不完全一致。如果你的模块不支持ATMQTTCONN这类指令可以先用串口助手把固件升级到支持 MQTT 的版本或者使用旧的 TCP 透传方式连接 OneNET。无论固件版本如何调试顺序都是固定的配置 WiFi 模式连接路由器建立 TCP 或 MQTT 连接发送/接收数据。6.2 先用串口助手手动联调 AT 指令在写 STM32 代码之前强烈建议先用 USB 转 TTL 模块把 ESP01S 接电脑用串口助手手动敲一遍指令。这一步能帮你确认 ESP01S 的 AT 指令格式和 OneNET 接入参数避免后面代码调试时分不清是“指令写错”还是“网络问题”。典型流程如下AT # 回复 OK ATCWMODE1 # 回复 OK设置为 STA 模式 ATCWJAP你的WiFi名,你的WiFi密码 # 回复 WIFI GOT IP # 连接 OneNET 服务器 ATMQTTUSERCFG0,1,clientId,username,password,0,0, ATMQTTCONN0,host地址,端口,1关于 OneNET 的接入参数这里需要特别注意。OneNET 平台有多个接入版本旧版多协议接入的 MQTT 参数与新版 OneNET Studio 不完全相同旧版多协议接入时clientId、username、password 由平台提供的设备信息生成新版 Studio 则更强调产品 ID、设备名称、设备密钥的组合关系不同版本的接入域名和端口也可能不同。所以我的建议是不要凭网上某篇文章里的 IP 和端口直接抄而是登录 OneNET 控制台找到“设备接入”或“MQTT 接入”页面以平台给出的实际接入点为准。这个细节很多初学者会踩坑也是 ESP8266/ESP01S 连接 OneNET 失败的高频原因之一。6.3 STM32 通过串口控制 ESP01S 的代码手动调试通过后就可以把 AT 指令封装到 STM32 代码里。ESP01S 与 STM32 的接线方式STM32ESP01SPA2USART2 TXRXPA3USART2 RXTX3.3V独立稳压VCCGNDGND10K 上拉电阻CH_PD/EN下面这段代码演示了如何使用串口 2 发送 AT 指令并等待 ESP01S 返回指定关键字。// 文件路径User/esp01s_at.c #include esp01s_at.h #include stm32f10x.h #include string.h #include stdio.h extern UART_HandleTypeDef huart2; // 标准库项目请替换为 USART2 static uint8_t rx_buffer[512]; static uint16_t rx_len 0; void ESP01S_SendCmd(const char *cmd) { USART_SendString(USART2, (uint8_t *)cmd); } uint8_t ESP01S_WaitResponse(const char *keyword, uint16_t timeout) { // 简化实现轮询接收缓冲区判断是否包含关键字 // 真实项目中建议使用状态机 环形缓冲区 } uint8_t ESP01S_ConnectWiFi(const char *ssid, const char *pwd) { char cmd[128]; sprintf(cmd, ATCWJAP\%s\,\%s\\r\n, ssid, pwd); ESP01S_SendCmd(cmd); return ESP01S_WaitResponse(OK, 10000); } uint8_t ESP01S_ConnectMQTT(const char *clientId, const char *username, const char *password, const char *host, int port) { char cmd[256]; sprintf(cmd, ATMQTTUSERCFG0,1,\%s\,\%s\,\%s\,0,0,\\\r\n, clientId, username, password); ESP01S_SendCmd(cmd); if (ESP01S_WaitResponse(OK, 3000)) { sprintf(cmd, ATMQTTCONN0,\%s\,%d,1\r\n, host, port); ESP01S_SendCmd(cmd); return ESP01S_WaitResponse(CONNACK, 10000); } return 0; } uint8_t ESP01S_PublishData(float temp, float humi, uint16_t gas) { char payload[128]; sprintf(payload, {\temperature\:%.1f,\humidity\:%.1f,\gas\:%d}, temp, humi, gas); char cmd[192]; sprintf(cmd, ATMQTTPUB0,\数据流主题名\,\%s\,0,0\r\n, payload); ESP01S_SendCmd(cmd); return ESP01S_WaitResponse(OK, 3000); }这段代码里的USART_SendString和USART2在本示例中约定在bsp_usart.c中实现。标准库环境下需要为串口 2 单独写初始化与串口 1 布局一致。实际应用中AT 指令的响应不是一次就发完的。最推荐的方案是串口接收中断 环形缓冲区在主循环中解析缓冲区内容。纯阻塞式等待在局域网环境下问题不大但 Wi-Fi 环境不稳定时一旦丢包程序就会卡死。6.4 OneNET 平台配置与数据流OneNET 平台侧的操作大致是注册账号并登录 OneNET 控制台创建产品选择 MQTT 协议在产品下添加设备记录设备 ID、鉴权信息查看接入文档确定 MQTT broker 地址、端口和 Topic 格式在一段时间内收集设备上报的数据然后在平台添加或确认数据流模板。这里有几个容易混淆的概念数据流Datastream描述一个设备的某一类数据比如 “temperature”数据点Datapoint某个时间点的具体数值命令/消息下发从云平台或小程序向设备发送控制指令。联动小程序时上报数据可以直接按 JSON 格式打包平台侧根据数据流模板解析。如果平台没有预定义模板只要在发布时使用相同的 Topic 和字段名也能在设备日志里看到原始数据。6.5 ESP01S 发送 TCP 消息与手机接收热搜词里有“esp01s发送tcp消息 手机”这其实是 ESP01S 最基础的用法通过ATCIPSTART建立 TCP 连接把数据发到某个服务器或手机 App 监听端口。它在局域网里很容易验证比如用手机上的网络调试助手工具监听端口ESP01S 就能把数据推过去。但在本项目里这种“点对点 TCP”并不适合做完整毕设原因有二手机没有公网 IP 时无法直接接收局域网外的 ESP01S 数据TCP 透传没有鉴权、没有历史存储不够“平台化”。OneNET 的价值正是解决这两个问题把数据存储在云端小程序通过 API 订阅设备侧只维护一条 MQTT 长连接。7. 微信小程序控制端设计与对接思路7.1 小程序架构微信小程序端有两种常见做法直连 OneNET API通过 HTTP 请求获取设备最新数据点再展示到页面上。优点是架构简单缺点是直接暴露平台访问密钥到前端有安全风险。个人演示项目可以接受但正式项目不建议。自建后端中转小程序请求自己的后端接口后端再与 OneNET 交互。更安全但需要一台服务器或云函数。对课程设计和竞赛而言第一种方案演示速度最快。OneNET 的 API 一般支持按产品设备和数据流查询最新数据返回 JSON 后在小程序里渲染即可。7.2 页面与功能一个典型的厨房安全监测小程序包含三个页面数据总览页卡片式展示温度、湿度、烟雾浓度设备控制页排风扇开关、报警复位按钮设置页上报阈值、WiFi 信息如果设备端支持远程配置。需要特别提醒小程序端必须处理“设备离线”状态。如果 ESP01S 断开连接OneNET 会标记设备离线小程序显示的数据可能是旧数据此时界面要提示“设备离线”而不是给用户一个误以为实时的数值。7.3 命令下发原理远程控制的本质是“向设备发布一条消息”。在 OneNET 里这通常表现为云平台向设备的订阅 Topic 下发消息ESP01S 收到后通过串口发给 STM32STM32 解析后控制继电器或蜂鸣器。因此STM32 端不仅要有“上报数据”的代码还要有“接收下行消息”的解析逻辑。这需要用到串口接收中断和消息队列复杂度比单向上报高一些。做的时候先保证上报能通再扩展下行控制不要一上来就同时做双向通信。8. PCB 设计从原理图到打板8.1 设计流程如果项目要出实物板推荐用嘉立创EDA立创EDA这类国产工具流程短、上手快而且可以直接对接打板服务。基本流程新建工程画原理图添加 STM32F103C8T6、晶振、复位、电源、传感器接口、继电器驱动等器件进行 ERC 电气规则检查修正未连接网络生成 PCB先摆好核心器件位置再进行布线铺铜、添加丝印、检查开路短路导出 Gerber 文件上传打板也可以直接在 EDA 内使用在线下单。8.2 PCB 布局与布线要点厨房安全监测板属于混合电路板既有 3.3V 数字电路又有继电器这样的功率电路。布局时最忌讳把大电流线路与敏感传感器线搅在一起。几个实用原则按模块分区STM32 最小系统放中间ESP01S 放一侧继电器放另一侧传感器接口放边缘电源走线加粗5V 和 GND 走线建议 30mil 以上继电器控制线至少在 20mil 以上信号线 12mil 即可晶振尽量靠近 STM32 的 OSC 引脚走线要短且不要平行长距离走线模拟地和数字地可以先用磁珠或 0Ω 电阻连接重点保证 MQ-2 的 ADC 采样稳定丝印字号不要太小打板回来后要能看清接口标识继电器下方铜皮要留足必要时加开窗避免散热和机械应力导致焊盘脱落。8.3 常见 PCB 设计问题热搜词里有很多 PCB 相关问题比如“gerber 文件转成 pcb 文件”“pcb 板厂钻孔工序”“pcb 添加 ole 文件对象”等。站在新手角度最容易忽略的是下面几个Gerber 文件是生产文件不是可以继续编辑的 PCB 工程文件。需要修改板子时要回到 EDA 工程里改而不是去处理 Gerber打板时板厂会按厂商件和标准工艺区分最小线宽、线距、孔径。下单前先查看所选工艺参数不能想画多细都行钻孔文件Drill通常和 Gerber 一起打包转格式时不要把钻孔文件漏掉否则板子无法打孔PCB 丝印模糊通常是因为字体太小或线宽过细设计阶段就要检查丝印规则。9. 常见问题与排查思路整理一份完整排查表覆盖这套系统从焊接、烧录到上云过程中最高频的问题问题现象可能原因排查方式解决方案error: no stm32 target found! if your product embeds debug authenticationST-Link 连接不稳定、芯片锁死、供电不足检查 SWDIO/SWCLK 和 GND 接线测 3.3V 电压按住复位再点下载用 STM32 ST-LINK Utility 连接按复位期间烧录必要时设置 BOOT0 拉高Keil 编译报错找不到器件未安装 STM32F1 器件支持包打开 Pack Installer 确认在 Pack Installer 中安装 STM32F1xx 系列支持包Keil 打开 C51 项目后无法编译 STM32编译器版本不对查看 Options for Target 中的编译器设置选择 ARM Compiler确保工程文件是 MDK 工程串口打印乱码波特率不匹配检查串口助手和 MCU 初始化波特率统一为 115200ESP01S 上电后不响应 AT供电不足 或 CH_PD 未拉高测量 VCC 电压是否稳定 3.3V检查 CH_PD 引脚使用独立 3.3V 稳压CH_PD 接 10K 上拉ESP01S 连接 WiFi 失败SSID/密码错误、路由器是 5GHz 或加密方式不支持检查 AT 返回的 ERROR 码使用 2.4GHz 路由器确认账号密码必要时重置 WiFi 配置OneNET 连接失败设备鉴权信息不对、接入地址端口错误查看 MQTT CONNECT 返回码以 OneNET 控制台接入文档的参数为准不要照抄老教程数据上传成功但平台无数据Topic 或数据流字段名不一致在平台查看设备日志和原始上报数据统一 Topic 与 JSON 字段名小程序设备离线ESP01S 掉线或网络异常检查 ESP01S 供电与 WiFi 信号增加断线重连机制定时发送心跳继电器不动作GPIO 驱动能力不足或三极管接线错误测量继电器控制端电压使用三极管/ULN2003 驱动并联续流二极管PCB 下载程序时报芯片找不到焊接虚焊、BOOT0 未接地、晶振不起振检查 STM32 最小系统焊接和供电重新焊接关键引脚检查晶振和复位电路PCB 打样回来后丝印看不清丝印线宽过细在 EDA 中检查丝印层调整丝印最小线宽一般在 10mil 以上这里特别要提一下stm32 virtual com port 叹号这个问题。很多 STM32 板子通过 USB 虚拟串口连接电脑时设备管理器里会出现黄色感叹号本质是缺少驱动。遇到这种情况先装 STM32 的 VCPVirtual COM Port驱动再检查 USB 线是否只供电没有信号线。还有一种情况是板子的 USB D 上拉电阻没有接好这种属于硬件问题。10. 最佳实践与工程建议10.1 代码层面的工程化建议不要把所有逻辑都塞在 main 函数的 while 循环里。推荐至少拆成几个模块传感器采集模块负责读取原始数据并校验数据上报模块负责拼装 JSON、通过串口发送 AT 指令控制模块负责本地阈值判断、继电器和蜂鸣器动作串口接收模块负责处理 ESP01S 返回的下行数据。每个模块之间通过结构体或全局状态机传递数据。这样做的好处是调试时能快速定位问题也方便答辩时讲清楚代码结构。另外一定要打开编译器的 warning 提示尽量做到零 warning。很多人把 warning 当回车键跳过结果上电才发现某个变量未初始化产生随机值导致系统行为不可预期。10.2 安全边界厨房安全系统的核心价值是“不安全时能报警”而不是“网络通畅时才安全”。因此设计时要注意本地判断优先即使无法上云也要保证本地蜂鸣器和继电器能响应断电恢复意外断电后重新上电系统能自动恢复初始化而不是停在异常状态传感器脏污处理MQ-2 长时间使用会漂移要定期校准基准值或加自动标定逻辑阈值可配置默认阈值只是演示值正式使用要根据安装环境重新标定。10.3 不断线重连ESP01S 在长时间运行时可能掉线。工程上要有一个“连接状态检测 自动重连”的机制定期检查是否收到 OneNET 的响应如果超过 N 秒没有消息就重新执行 WiFi 连接和 MQTT 连接流程。这里要注意反复快速重连可能会被路由器或云平台临时封禁所以重连间隔要加退避比如第一次 5 秒、第二次 10 秒、之后 30 秒。10.4 答辩与文档准备如果是毕业设计除了代码跑通还要提前准备好以下材料系统框图、时序图、状态机图关键模块的测试记录比如 AT 指令联调日志、数据上报日志PCB 原理图、Gerber 文件、BOM 清单小程序截图和演示视频。关键是讲清楚“我为什么这么做”而不是只讲“我做了什么”。比如为什么选择 MQTT 而不是 HTTP为什么本地也要做判断为什么电源要独立稳压这些问题在答辩时被问到的概率极高。10.5 扩展方向这套系统做完之后可扩展的方向很多把 DHT11 换成 SHT30提高温湿度精度加入 OLED 显示屏本地可视化数据接入摄像头实现远程厨房看护换成 4G 模块如合宙 Air724 等摆脱 WiFi 依赖把 OneNET 换成自建 MQTT 服务器学习更底层的数据流转过程。11. 总结STM32 厨房安全监测系统不是一个“高大上”的选题但它非常典型它把单片机编程、传感器接口、串口通信、WiFi 模块 AT 指令、云平台 MQTT 接入、小程序 API 调用、PCB 设计这些物联网核心技术点串成了一个完整闭环。真正值得你投入精力的不是背下某一条 AT 指令也不是记住某个平台按钮的位置而是理解“端-云-应用”这条数据链路的连接方式。做完这个项目你手里就有一套可以迁移到很多物联网场景的“骨架”。建议动手顺序也很明确先用最小舵机跑通单点上报再做本地控制最后接小程序和 PCB。这样每一步都有可验证的产出不容易卡在最后联调阶段。建议把这篇文章收藏备用按章节逐步实践。如果你遇到某个环节卡住优先对照“常见问题与排查思路”表格绝大多数问题出在供电、接线、串口参数和平台鉴权这四类原因上。祝你的厨房安全系统顺利落地。