
简介本资源是一套基于STM32F10x系列单片机开发的智能衣柜嵌入式系统完整工程面向嵌入式初学者、物联网课程设计学生及智能家居项目开发者解决传统衣柜缺乏环境感知与远程交互能力的问题。项目集成DHT11温湿度、MQ-2烟雾、PMS5003空气质量等多传感器数据采集通过ESP8266 WIFI模块实现与手机APP双向通信并支持阈值告警、LCD本地数据显示及联动响应逻辑。压缩包含121个文件主体为49个C源文件与52个头文件.c/.h覆盖STM32标准外设库驱动如stm32f10x_rcc.c、stm32f10x_adc.c、Gizwits物联网协议对接gizwits_protocol.c、定时器与Flash操作等核心模块另有Keil工程文件.uvprojx、调试配置.dbgconf、批处理脚本.bat及固件镜像.hex总大小2.96MB。已有217人学习下载提供可直接编译烧录的完整工程框架、清晰的模块化代码结构与典型传感器WIFI协同开发范例是掌握STM32物联网终端开发的实用参考。 做嵌入式这些年说实话被问得最多的不是硬件怎么设计而是“衣柜返潮、衣服发霉怎么办”。我一开始也是随手回一句“买个除湿盒”直到自己动手把一个普通衣柜改造成带温湿度检测、自动除湿、消毒照明和远程监控的智能衣柜才发现这套东西拿来当STM32的实战项目真的太合适了。这几天一边整理代码一边把整个实现过程复盘了一遍从硬件选型到软件架构从传感器驱动到调试踩坑涉及的东西很全但又不至于复杂到劝退属于那种“花一个周末就能跑起来再花两周就能做到稳定可靠”的项目。我用的主控是STM32F103C8T6也就是大家最熟悉的“蓝丸”板网上资料一抓一大把照着这套思路完全可以做到开工不抓瞎。这个项目适合谁如果你刚学完STM32的基础外设想做点有完整产品形态的东西或者你家里正好有衣柜改造需求想做一个真正能用的小家电那这篇记录应该能帮你省掉不少弯路。下面我从需求拆解、硬件搭建、软件实现到问题排查把整个过程捋一遍。1. 项目概述与需求拆解1.1 智能衣柜到底智能在哪需求从感知开始先别急着想代码做项目的第一步永远是“需求要什么”。智能衣柜本质上是一个环境监测和自动控制系统它要解决的问题分几个层面。第一层是感知。衣柜里放几个传感器采集温度、湿度这是整个系统的基础。温度高但湿度也异常偏高多半是通风不畅梅雨季湿度长期超过70%就该考虑加强除湿力度。感知层做扎实了后面的所有决策才有依据。第二层是决策。拿到传感器数据后不是简单地和阈值比一下就完事而是要结合一段时间内的变化趋势做判断。比如刚开门那一瞬间外界空气涌入湿度可能瞬间冲高如果这时候立刻触发加热反而浪费电又没必要。实际做法是加一个简单的滤波和迟滞逻辑让系统不会因为瞬间波动而频繁启停。第三层是执行。除湿加热、通风风扇、LED照明、紫外消毒这些执行器通过继电器或MOS管驱动动作干脆利落同时要有超时保护防止某个模块卡死导致设备一直工作。第四层才是“智能感”的来源交互与远程。柜门一开自动亮灯手机小程序上能看到当前温湿度和设备状态甚至在办公室就能提前打开除湿到家时衣柜已经干燥清爽。这四层做下来一个真正能用的智能衣柜就出来了。1.2 功能清单与优先级划分先做稳定再做扩展在动手之前我把功能项拉了一个清单按照“必须做”和“有余力再做”分成两档。第一档必须做温湿度实时采集与显示湿度超阈值自动启动除湿加热片风扇柜门开关检测与自动照明OLED屏显示当前环境数据按键手动控制各执行器第二档进阶做紫外消毒定时控制手机远程监控与遥控数据趋势记录异常报警蜂鸣器/推送通知第一档功能用STM32裸机加状态机就能完成不引入操作系统代码逻辑清晰第二档就牵扯到WiFi模块和通信协议建议放到裸机跑稳之后再往上叠。很多朋友一上来就想把RTOS、微信小程序全部塞进去结果基础逻辑还没调通就先被bug淹没了。我一直建议的做法是先把第一档做成一个完整可复现的Demo再考虑扩展。2. 硬件选型与平台搭建2.1 主控选型分析为什么STM32F103C8T6刚刚好主控选了STM32F103C8T6原因很简单性价比高、资料多、外设够用。这颗芯片是Cortex-M3内核主频最高72MHz64KB Flash和20KB SRAM跑这种级别的项目绰绰有余。真正让它成为“新手毕业板”的原因是网上教程和例程实在太多了无论是寄存器版还是HAL库版几乎任何外设用法都能搜到现成代码。对比一下51单片机虽然51也能做但在处理多路ADC采样、PWM输出、串口通信同时并发的场景时51的资源和效率就有点捉急了。STM32能提供定时器、DMA、ADC、USART、I2C、SPI这些外设协同工作硬件上天然适合做这种多任务控制系统。如果预算充足也可以考虑STM32G0系列功耗更低但F103的生态优势太明显作为学习和开发首选没什么问题。2.2 传感器与执行器选型用过DHT11和SHT30才有发言权温湿度传感器用过DHT11和SHT30这两个我都实际测过说下差异。DHT11价格便宜一两块钱单总线协议数据线一根搞定但精度实在一般——湿度±5%RH温度±2°C而且采样周期至少要等1秒不适合做快速响应的控制适合预算有限的入门方案。SHT30要贵一些但是I2C接口精度高响应快长期稳定性和一致性都好很多。如果做实际产品我强烈推荐SHT30几十块钱的差价换来的是控制精度和用户体验的巨大提升。执行器方面除湿加热我用的是12V PTC加热片配合MOS管驱动功率大概30W左右放在衣柜底层靠热空气自然上升来驱散湿气。注意PTC本身有恒温特性不会超温安全性比普通电阻丝好很多。通风风扇12V涡轮风扇装在衣柜顶部开孔处负责把潮湿空气排出。选风扇时注意噪音实测下来带滚珠轴承的比含油轴承耐用噪音也小一些。照明LED灯带用三极管或MOS管控制通断门磁开关检测柜门状态。紫外线消毒一个紫外灯管继电器控制。这个模块特别强调安全必须加定时保护和独立开关人不在柜前才允许开启。2.3 硬件连接与引脚分配一个引脚冲突引发的教训我的实际接线是这样的模块接口STM32引脚DHT11/SHT30GPIO / I2CPB4 / PB6-PB7OLED(SPI)SPI1PA5-PA7风扇M1PWM(TIM2)PA0加热片M2PWM(TIM2)PA1LED灯带GPIOPB0门磁开关GPIO输入PB1紫外继电器GPIOPB3WiFi模块(ESP8266)USART2PA2-PA3按键x2GPIOPA8-PA9注意引脚分配不是随便写的。几个原则PWM输出尽量集中在同一个定时器上这样频率同步好控制I2C和SPI尽量用硬件外设对应的引脚虽然GPIO模拟也能跑但稳定性和效率差很多中断引脚避开跟其他外设冲突的复用功能。我第一次做的时候把PWM和OLED的SPI分配到了共用复用引脚结果怎么调都出不来波形后来查手册才发现是Mapping冲突这是新手最容易犯的错。硬件接线这块不要盲目照搬网上的图务必对照芯片数据手册的引脚复用表特别是HAL库模式下引脚的复用配置一旦有误外设初始化直接失败或者数据全乱。OLED如果用的是7针SPI版本CS/DC/RES三根控制线也要接对接反了屏幕会白屏或者显示错乱。3. 程序架构与驱动设计3.1 裸机状态机还是FreeRTOS我为什么选了裸机很多人拿到项目第一反应是“要不要学一下FreeRTOS”。我的回答很直接这个项目用裸机加状态机完全够而且在调试初期更直观。裸机环境下主循环按固定周期运行状态机负责切换当前的工作状态。比如主系统有“待机、除湿、消毒、故障”几个状态每个状态下执行不同的动作。状态之间用事件触发比如“湿度超过75%持续5秒”触发待机转除湿“手动关闭”触发返回待机。这样做的好处是整个程序流程一目了然出bug容易定位。FreeRTOS的优势在于任务阻塞和通信机制更完善适合多任务并发复杂、周期要求严格的产品。但在这个项目里传感器采集周期是秒级、控制逻辑也不复杂用RTOS反而增加了任务同步的复杂度。等你把裸机版本跑通了再移植到FreeRTOS那时对各个任务的理解会深得多迁移成本也很低。3.2 外设初始化顺序CubeMX生成代码后第一件事是什么我用的开发环境是STM32CubeIDE配合STM32CubeMX做初始化配置代码生成后直接在HAL库基础上补充业务逻辑。有人喜欢寄存器操作性能确实更极致但开发和调试效率低对于这种偏应用层的项目HAL库足够。初始化顺序有讲究。系统时钟→GPIO→定时器→ADC→I2C/SPI→串口→中断按依赖关系从上往下初始化。特别是时钟树配置如果晶振频率和配置不一致会导致串口波特率错乱、定时器计时不准这类“莫名奇妙”的问题。比如板上晶振明明是8MHzCubeMX默认配了HSE_VALUE8000000但如果你手动改了宏或者用了不同频率的板子就很可能踩坑。3.3 驱动设计闭环思路接口封装和异常降级驱动设计上我的习惯是每个外设单独一个.c/.h文件接口全部封装主程序不直接操作寄存器。比如SHT30的驱动提供sht30_read_temperature()和sht30_read_humidity()内部处理I2C时序和CRC校验OLED驱动提供oled_show_main_screen(温度, 湿度, 状态)。这样底层换了传感器上层调用代码不用动只是替换驱动文件业务逻辑完全不受影响。驱动层还要处理一个问题外设可能不稳定。比如I2C通信偶尔出错、传感器返回异常数据这不能直接往上抛而是在驱动内部做重试和校验最多重试3次都失败则返回一个错误标志上层根据标志进行降级处理比如最近一次有效数据继续沿用同时提示设备异常。这比直接死循环等待传感器响应健壮得多。4. 核心功能模块实现4.1 温湿度采集与数据处理CRC校验必须做采集本身不算难难在数据可靠性。SHT30的I2C通信要发送测量命令等一段时间后读回6个字节前两个字节是温度原始值后两个是湿度最后两个是CRC校验。驱动里必须做CRC校验不然偶尔跳变的脏数据会让你排查到崩溃。原始值转换为实际温度湿度的公式很简单温度 -45 175 * (原始值 / 65535.0)湿度 100 * (原始值 / 65535.0)如果你用DHT11时序则是单总线拉低总线发开始信号读回40位数据。DHT11的问题在于对时序要求严格容易受中断影响导致读取出错。我在过程中碰到过一个特别经典的场景当串口接收中断和DHT11读取同时发生时DHT11时序被中断打断读出来的数据偶尔是错的。解决办法是读取DHT11期间临时关中断或者用定时器主从模式配合不过这两者的切换需要谨慎避免长时间关中断导致其他任务卡顿。数据处理层面我做了三件事滑动平均滤波、迟滞比较、超时保护。滑动平均就是保留最近10次采样值取平均可以平滑掉瞬间扰动迟滞比较是避免阈值附近频繁启停——比如除湿启动湿度设为75%除湿停止湿度设为70%中间这5%的区间就是迟滞带防止继电器像抽风一样来回动作超时保护则是除湿连续运行超过2小时强制停机防止设备过热或异常时一直工作。4.2 自动除湿控制逻辑一个带超温保护的状态机除湿逻辑是整个程序的核心。我用的是一个带状态回环的有限状态机代码如下typedef enum { STATE_IDLE, STATE_DEHUMIDIFY, STATE_OVERHEAT_PROTECT } SystemState; SystemState current_state STATE_IDLE; void dehumidify_control(float temp, float humidity) { switch(current_state) { case STATE_IDLE: if(humidity 75.0f) { fan_on(); heater_on(); current_state STATE_DEHUMIDIFY; } break; case STATE_DEHUMIDIFY: if(humidity 70.0f) { fan_off(); heater_off(); current_state STATE_IDLE; } else if(temp 40.0f) { heater_off(); fan_on(); current_state STATE_OVERHEAT_PROTECT; } break; case STATE_OVERHEAT_PROTECT: if(temp 35.0f humidity 75.0f) { heater_off(); fan_off(); current_state STATE_IDLE; } break; } }这里有个细节值得展开为什么温度和湿度要分别判断因为柜内空间小加热片长时间工作可能导致局部温度过高虽然PTC有恒温特性但柜内不通风的话散热差温度还是可能升上来。所以我把超温保护单独作为一个状态只要检测到温度超过40°C就先把加热片停掉只保留风扇通风散热等温度降回35°C以下再决定是否重新进入除湿。这套逻辑我实测下来非常可靠既避免了衣物被高温损伤也保护了用电安全。4.3 OLED显示和按键交互UI简洁比花哨更重要OLED我用的是经典的0.96寸128x64SPI接口。主界面做三块区域顶部一行显示系统状态“除湿中”、“待机”、“消毒中”、温度告警中间两行大字显示温度和湿度底部显示WiFi连接状态和下一次消毒倒计时。内容不要贪多128x64的屏幕就那么大塞太多信息反而看不清。我用的是中文字库取模的时候务必注意取模方向和屏幕扫描方向一致不然字全反过来或者花屏。有一种常见的花屏原因是SPI的时钟极性和相位配置错这里给一个小技巧0.96寸OLED(SSD1306)通常配置为SPI模式0CPOL0, CPHA0但有的屏需要模式3最好先用厂家给的初始化代码跑通再改。按键我留了两个一个长按切换消毒、短按手动开关风扇另一个切换屏幕亮度。按键处理必须做去抖和长按/短按区分去抖用20ms延时就能搞定长按短按的阈值设在800ms左右。这里一定不能用阻塞式的延时去抖否则按一下整个系统卡20ms主循环和传感器采集全被拖累。正确做法是用定时器做时基在主循环里轮询按键状态记录按下时间再根据时间判断短按或长按。4.4 远程通信与微信小程序联动AT指令同步模式踩坑记远程控制这块我用ESP8266模块通过串口连接STM32采用AT指令转发数据。STM32的USART2接收ESP8266的数据处理完协议之后再返回执行结果。为了省电和稳定ESP8266工作在Station模式连接家里的WiFi路由通过MQTT协议和云服务器通信。这里踩过一个非常值得记录的坑AT指令发送过快会丢响应。ESP8266的AT固件处理速度没那么快必须等它返回“OK”之后再发下一条指令。刚开始我图快发送和读取分开了结果大量指令超时连接一直不稳定。后来改成“发一条、等响应、再发下一条”的同步模式稳定多了。串口配置上建议115200波特率要匹配好STM32的串口配置。小程序端我写了一个简单的温湿度监控页面可以查看实时数据、手动控制除湿和消毒、接收异常报警推送。开发证书、request合法域名这些其实都不难难的是云端和MCU协议的一致性。我一度因为协议字段大小端问题导致数据解析错乱排查了很久。考虑到这篇博文的重点在STM32程序本身微信小程序部分就先不展开细节了思路是STM32端定期把温度、湿度、设备状态封装成JSON字符串通过MQTT发布到指定Topic小程序端订阅Topic解析JSON并渲染页面。反向控制流程类似小程序发布控制命令到TopicESP8266订阅之后下发到STM32执行。4.5 安全保护与异常处理三层防护缺一不可安全是家电类产品绝对不能忽视的一环。我在程序里加了三层保护。第一层是输入侧保护传感器异常数据不参与控制逻辑。比如读到湿度大于100%或温度低于-20°C这类不可能的数据直接丢弃不触发任何执行器动作。第二层是执行侧超时保护除湿连续运行2小时强制关闭、消毒运行10分钟强制停止、照明长时间未关闭自动熄灭。这些超时值都在配置头文件里用宏定义管理方便后续调整。第三层是看门狗保护STM32内置独立看门狗IWDG主循环每500ms喂一次狗。一旦程序死循环或者卡在某个阻塞等待里看门狗超时后自动复位系统避免设备带病运行。看门狗的喂狗时间要和主循环周期匹配好主循环跑完一轮之后喂狗如果主循环卡死看门狗就会把设备拉回来。注意调试阶段不要直接开启IWDG否则在断点处系统会反复复位导致调试器无法连接。我一般先在代码里禁用看门狗或把超时时间调大等调通之后再打开。5. 调试过程与常见问题排查实录5.1 温湿度数据跳变的罪魁祸首从I2C通信层面排查第一个遇到的典型问题是SHT30的数据偶尔跳变不是每次都错而是隔一段时间冒出一个离谱值。用串口把原始数据和CRC校验值都打印出来之后发现跳变的数据CRC校验本来就不过关也就是说I2C通信在传输过程中出现了丢字节或错位。排查思路一步步来先换线、缩短I2C线缆长度、接上拉电阻故障依旧随后把I2C时钟从400kHz降到100kHz故障频率明显降低最后发现是地线接触不良加上干扰导致I2C通信错误。重新把地线焊牢、I2C数据线加上拉之后问题彻底消失。总结下来I2C通信不稳先查硬件连接再考虑初始化时序不要一上来就翻代码。5.2 串口通信丢帧问题DMA空闲中断是正解ESP8266和STM32的串口通信在长时间运行后偶尔丢帧。开始以为是串口波特率漂移后来打印调试信息发现发送端在连续发送数据时接收端如果处理不及时数据缓冲会被新到的数据覆盖。STM32的USART虽然自带缓冲区但只有1个字节必须在每个字节到达后及时搬走。解决办法是开启串口空闲中断或者DMA接收。最稳定的方案是串口DMA接收空闲中断当串口在一段时间内没有新数据时产生空闲中断CPU再把DMA收到的整包数据拿出来解析。这个方案处理不定长消息很舒服也几乎不丢数据。如果你用的是HAL库可以参考HAL_UARTEx_ReceiveToIdle_DMA这个函数。5.3 延时函数Delay卡死的真相中断里别乱调用这个问题在早期特别常见。很多人用HAL_Delay()做延时发现程序跑一会儿就卡死了。原因其实很简单HAL_Delay()是基于SysTick中断实现的如果在中断服务函数里调用HAL_Delay()会导致SysTick中断被自身阻塞从而死等。我的实际场景是按键中断里想做一个延时去抖直接在中断处理中使用HAL_Delay()结果整个系统卡死。查了资料才明白HAL_Delay()依赖SysTick中断它先关闭SysTick中断再忙等这个过程中任何其他中断里的HAL_Delay()调用都会死等。规范做法是不要在中断里调用任何阻塞延时按键去抖放到主循环中处理或者用定时器的非阻塞延时方式。5.4 ADC多通道采样偏移通道顺序和DMA重装载如果要监测多路传感器比如温湿度、光敏还有电流采样ADC多通道是必不可少的。用ADC扫描模式加DMA传输可以自动把多通道的结果按顺序搬到内存数组里。我第一次配置的时候DMA搬运的数据总是错位第二通道值跑到第一个坑里。排查后发现是ADC通道转换顺序配置的问题。ADC规则组的通道转换顺序是Rank决定的必须在CubeMX里把每个通道对应的Rank设置正确。另外DMA搬运完成中断里处理完数据之后要重新启动DMA传输不然下一次转换结果不会更新。加了DMA半传输中断和传输完成中断后数据处理逻辑要小心数组越界。6. 扩展方向与实际产品化建议6.1 小程序与云端方案的取舍微信小程序和云端方案有很多种选择从零开始搭MQTT服务器、用云厂商的物联网套件、或者用微信小程序自带的云开发能力各有优劣。对于学习项目来说我建议直接使用带云端的物联网平台大大降低服务器搭建成本把精力留在MCU端。云平台的选择要注意免费额度和数据保密。个人项目用免费额度基本足够但如果要做商业化一定要把数据安全性考虑进去。我在产品化过程中把协议字段做了加密传输这一步在开发阶段容易被忽略等设备数量上来之后才后悔。6.2 硬件改版与低功耗优化如果打算做成真实产品有几个方向值得深入。一是低功耗设计采用STM32L系列或者让系统在无操作时进入Stop模式整机功耗可以做到很低二是增加更多传感器比如灰尘传感器、衣柜门开关次数统计三是把加热片换成功率更高的同时配合温控器提高除湿效率。低功耗这块进入Stop模式前要把所有外设的时钟关闭保留RTC唤醒按键事件唤醒等。调试时特别需要注意GPIO在Stop模式下要保持正确的电平状态否则唤醒后会出现奇怪的逻辑错误。6.3 开源协议与代码管理最后聊一下项目管理经验。嵌入式项目同样需要良好的版本管理建议用Git管理工程代码每个功能模块的改动都提交一个独立commit写清楚commit message。我自己一开始也是改到哪算哪后期调bug时想回退版本都找不到后来老老实实补上了Git效率提升非常明显。代码层面建议把产品级代码和学习级代码分开维护。学习级代码可以写得随意注释猛堆产品级代码必须模块化每个文件职责单一编译时打开所有警告把警告清零再提测。写到这里关于“基于STM32的智能衣柜程序”整个实现过程基本讲完了。个人体会是这类项目最核心的价值不在于代码本身而在于从需求出发、设计架构、调试排查的完整训练。如果你也是正在学STM32的朋友建议先别急着把所有高级功能全塞进去按我前面说的第一档功能做扎实了看到柜内的湿度数据真能控制风扇和加热片运转那种“代码变成现实控制”的感觉比刷一百个例程都有成就感。最后再给一个建议把所有调试过程中遇到的问题记下来整理成自己的问题库。做项目踩坑不可怕可怕的是同样的坑踩两遍。等做完这个项目把这个文档更新到个人博客或者社区也是很有价值的积累。后续如果想继续扩展可以考虑加入衣柜内部空间分区控温、多柜门独立控制、联动家里的智能音箱语音控制等方向这块的想象力其实很大但一切都要建立在基础功能稳定可靠的前提之上。本文还有配套的精品资源点击获取