基于STM32的智能衣柜系统设计与实现——温湿度调控+烟雾报警+远程监控

一、系统方案介绍

1.1 项目背景

传统衣柜功能单一,面对潮湿发霉、高温异味、火灾隐患等问题束手无策。随着物联网技术的普及,让衣柜"智能化"已成为提升生活品质的新趋势。本文设计了一套基于STM32的智能衣柜系统,实现以下目标:

  • 环境感知:实时监测衣柜内的温湿度及烟雾浓度;

  • 自动调控:温度过高自动通风降温,湿度过高自动烘干;

  • 安全报警:检测到烟雾立即蜂鸣器报警,防止火灾;

  • 远程监控:通过WiFi上云,手机App可查看数据并手动控制;

  • 人机交互:本地OLED显示 + 按键设置衣物存储位置。

1.2 核心方案

本系统以 STM32F103C8T6 为主控芯片,搭载 DHT11 温湿度传感器和 MQ-2 烟雾传感器采集环境数据,通过 ESP8266-01S 连接 OneNET 云平台(MQTT协议),实现与手机App的数据交互。本地配备 0.96寸OLED 显示屏实时反馈数据,执行机构包括 风扇(通风降温)、PTC暖风机(加热烘干)及 蜂鸣器(报警)。用户既可选择自动模式(系统根据阈值自行调控),也可通过App切换到手动模式,远程控制风扇和暖风机。此外,3个按键用于设置衣物存储位置(如外套区、衬衫区等),数据保存在STM32内部Flash中,掉电不丢失。

二、系统架构图

三、实现方法

系统实现分为硬件搭建、嵌入式软件编写、云平台配置、手机App开发四个部分。

  1. 硬件搭建:按照引脚连接表将传感器、执行器、ESP8266、OLED和按键与STM32主板相连。特别注意继电器模块用于隔离大功率设备,蜂鸣器需三极管驱动。

  2. 嵌入式软件:使用Keil MDK开发,基于STM32 HAL库编程。主要模块包括:

  • DHT11时序读取(单总线协议);

  • ADC采集MQ-2模拟量并换算为PPM值;

  • I²C驱动OLED显示;

  • 状态机处理按键输入;

  • 串口AT指令控制ESP8266连接WiFi并实现MQTT通信;

  • 自动/手动模式切换逻辑。

  1. 云平台配置:在OneNET平台上创建设备,选择MQTT协议,定义数据流(温度、湿度、烟雾、风扇状态、暖风机状态),并记录设备ID和API密钥。

  2. 手机App:采用微信小程序开发,调用OneNET的API获取设备数据,并下发控制指令(开关风扇/暖风机、切换模式)。

四、功能描述

|--------|----------|---------------------------------------------------------|
| 序号 | 功能模块 | 详细描述 |
| 1 | 温湿度检测 | DHT11每2秒采集一次衣柜内温度(℃)和相对湿度(%RH)。 |
| 2 | 烟雾检测 | MQ-2实时检测烟雾浓度(PPM),当浓度超过100PPM时触发报警。 |
| 3 | 本地显示 | 0.96寸OLED屏幕分两页显示:第一页实时温湿度+烟雾值,第二页显示当前工作模式及衣物存储位置。 |
| 4 | 自动模式 | ① 温度 > 30℃ → 自动开启风扇通风,直至温度 ≤ 28℃; |
| 4 | 自动模式 | ② 湿度 > 70%RH → 自动开启暖风机烘干,直至湿度 ≤ 60%RH; |
| 4 | 自动模式 | ③ 烟雾 > 100PPM → 蜂鸣器报警,同时向App推送报警信息。 |
| 5 | 手动模式 | 通过手机App远程开关风扇和暖风机,系统不再自动调节。 |
| 6 | 模式切换 | App上可一键切换自动/手动模式,模式状态实时同步至OLED。 |
| 7 | 衣物位置设置 | 按下按键1/2/3分别设置"外套区""衬衫区""毛衣区",存储位置名称在OLED第二页显示并保存至Flash。 |
| 8 | 云端数据上报 | 每5秒向OneNET上报温度、湿度、烟雾浓度、设备状态(风扇/暖风机开关、模式)。 |
| 9 | App远程监控 | 手机端实时查看衣柜环境数据,接收烟雾报警通知,并手动控制设备。 |

五、硬件架构

5.1 硬件清单

|----------|---------------|--------|------------------------------|
| 模块名称 | 型号 | 数量 | 主要参数 |
| 主控芯片 | STM32F103C8T6 | 1 | 72MHz, 64KB Flash, 20KB SRAM |
| 温湿度传感器 | DHT11 | 1 | 精度:±2%RH, ±0.5℃ |
| 烟雾传感器 | MQ-2 | 1 | 检测范围:100~10000ppm |
| WiFi模块 | ESP8266-01S | 1 | 802.11 b/g/n, UART通信 |
| 显示屏 | 0.96寸OLED | 1 | 128×64, I²C接口 |
| 风扇 | 5V直流 | 1 | 0.2A |
| 暖风机 | PTC加热器+继电器 | 1 | 12V/2A (继电器隔离) |
| 蜂鸣器 | 有源5V | 1 | 三极管8050驱动 |
| 按键 | 轻触开关 | 3 | 上拉输入 |

六、传感器介绍

6.1 DHT11温湿度传感器

工作原理:DHT11内部包含一个电阻式湿度测量元件和一个NTC温度测量元件,并由一个8位单片机将模拟信号转换为数字信号,通过单总线输出。其通信协议要求主机先拉低总线至少18ms,然后释放并等待DHT11拉低80μs再拉高80μs作为应答,之后DHT11连续输出40位数据(湿度整数、湿度小数、温度整数、温度小数、校验和)。

使用时注意:两次读取间隔应大于1秒,否则DHT11无法响应。数据线必须接上拉电阻,否则信号不稳定。本系统每2秒读取一次,读取失败则保留上一次有效值。

6.2 MQ-2烟雾传感器

工作原理:MQ-2采用二氧化锡(SnO₂)敏感材料,在洁净空气中电导率较低,当存在可燃气体或烟雾时,电导率随浓度升高而增大。通过简单的分压电路即可输出与浓度成正比的模拟电压。本系统使用ADC采集AO引脚电压,电压范围0~5V对应0~1023(STM32的12位ADC,3.3V参考电压下需换算)。

七、软件架构

7.1 软件模块划分

系统软件采用模块化分层设计,每个模块职责单一,通过主循环调度。工程文件结构如下:

Project/

├── Core/

│ ├── main.c # 主程序,状态机调度

│ ├── stm32f1xx_it.c # 中断服务函数

│ └── system_stm32f1xx.c

├── Drivers/ # HAL库驱动

├── BSP/ # 板级支持包

│ ├── dht11.c/h # DHT11读取

│ ├── mq2.c/h # MQ-2 ADC采集及换算

│ ├── oled.c/h # OLED显示驱动(I²C)

│ ├── fan.c/h # 风扇控制

│ ├── heater.c/h # 暖风机控制

│ ├── buzzer.c/h # 蜂鸣器控制

│ ├── key.c/h # 按键扫描+位置存储

│ └── esp8266.c/h # ESP8266 AT指令及MQTT

├── Middlewares/ # 中间件(如MQTT库)

└── User/ # 用户应用层

├── data_process.c/h # 数据处理及阈值判断

├── mode_switch.c/h # 自动/手动模式切换

└── cloud_report.c/h # 云端数据上报

7.2 主程序流程图

八、关键代码展示

由于篇幅限制,这里展示最核心的几个代码片段。

8.1 DHT11读取函数(单总线时序)

复制代码
void DHT11_Init ( void )
{
	DHT11_GPIO_Config ();
	
	DHT11_Dout_1;               // 拉高GPIOB10
}


/*
 * 函数名:DHT11_GPIO_Config
 * 描述  :配置DHT11用到的I/O口
 * 输入  :无
 * 输出  :无
 */
static void DHT11_GPIO_Config ( void )
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
	GPIO_InitTypeDef GPIO_InitStructure; 

	
	/*开启DHT11_Dout_GPIO_PORT的外设时钟*/
  DHT11_Dout_SCK_APBxClock_FUN ( DHT11_Dout_GPIO_CLK, ENABLE );	
 
	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure );		  
	
}


/*
 * 函数名:DHT11_Mode_IPU
 * 描述  :使DHT11-DATA引脚变为上拉输入模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_IPU(void)
{
 	  GPIO_InitTypeDef GPIO_InitStructure;

	  	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/	
	  GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;

	   /*设置引脚模式为浮空输入模式*/ 
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ; 

	  /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
	  GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);	 
	
}


/*
 * 函数名:DHT11_Mode_Out_PP
 * 描述  :使DHT11-DATA引脚变为推挽输出模式
 * 输入  :无
 * 输出  :无
 */
static void DHT11_Mode_Out_PP(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;

	 	/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
  	GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);	 	 
	
}


/* 
 * 从DHT11读取一个字节,MSB先行
 */
static uint8_t DHT11_ReadByte ( void )
{
	uint8_t i, temp=0;
	

	for(i=0;i<8;i++)    
	{	 
		/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/  
		while(DHT11_Dout_IN()==Bit_RESET);

		/*DHT11 以26~28us的高电平表示"0",以70us高电平表示"1",
		 *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 
		 */
		delay_us(40); //延时x us 这个延时需要大于数据0持续的时间即可	   	  

		if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据"1" */
		{
			/* 等待数据1的高电平结束 */
			while(DHT11_Dout_IN()==Bit_SET);

			temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1,MSB先行 
		}
		else	 // x us后为低电平表示数据"0"
		{			   
			temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
		}
	}
	
	return temp;
	
}


/*
 * 一次完整的数据传输为40bit,高位先出
 * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
 */
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{  
	/*输出模式*/
	DHT11_Mode_Out_PP();
	/*主机拉低*/
	DHT11_Dout_0;
	/*延时18ms*/
	delay_ms(18);

	/*总线拉高 主机延时30us*/
	DHT11_Dout_1; 

	delay_us(30);   //延时30us

	/*主机设为输入 判断从机响应信号*/ 
	DHT11_Mode_IPU();

	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
	if(DHT11_Dout_IN()==Bit_RESET)     
	{
		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
		while(DHT11_Dout_IN()==Bit_RESET);

		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
		while(DHT11_Dout_IN()==Bit_SET);

		/*开始接收数据*/   
		DHT11_Data->humi_int= DHT11_ReadByte();

		DHT11_Data->humi_deci= DHT11_ReadByte();

		DHT11_Data->temp_int= DHT11_ReadByte();

		DHT11_Data->temp_deci= DHT11_ReadByte();

		DHT11_Data->check_sum= DHT11_ReadByte();


		/*读取结束,引脚改为输出模式*/
		DHT11_Mode_Out_PP();
		/*主机拉高*/
		DHT11_Dout_1;

		/*检查读取的数据是否正确*/
		if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
			return SUCCESS;
		else 
			return ERROR;
	}
	
	else
		return ERROR;
	
}

九、完整资料获取方式

> 由于文章篇幅限制,完整工程源码(Keil工程、机智云数据点配置、ESP8266固件、AD原理图+PCB)无法全部贴出。

需要全套资料的同学:

  • 方式1:关注后评论区留言"智能衣柜",我会私信发送百度网盘链接。

  • 方式2:关注后私信我,直接获取。

资料包包含:

  • ✅ STM32完整Keil工程(标准库版本,编译无报错)

  • ✅ 机智云MCU代码包(含数据点定义)

  • ✅ ESP8266 GAgent固件及烧录工具

  • ✅ AD17原理图

另外,如果你需要全流程辅导,或者想定制其他功能(如增加传感器、改用阿里云),也可以私信联系我。本人已辅导过200+物联网/嵌入式实物,包教包会,不跑路。

十、总结与互动

评论区聊聊:你在调试过程中遇到的最棘手的问题是什么?或者你想基于这个系统增加什么功能?点赞最高的,我下一期专门写一篇解决方案。

> 最后,希望这个系统能帮你顺利通过,也让你真正掌握物联网开发的完整流程。加油,未来的工程师!

版权声明:本文原创,转载请附原文链接。代码仅供学习参考,请勿直接用于商业用途。

相关推荐
才知道的3 小时前
stm32F407学习DAY.27 ADC
stm32·嵌入式硬件·学习
senijusene3 小时前
i.MX6ULL 裸机 ECSPI 驱动开发详解:
arm开发·驱动开发·嵌入式硬件
Tomhex3 小时前
ARMv7与Cortex-M3关系解析
stm32
傻童:CPU4 小时前
stm32程序的启动过程
stm32
JaneHan_5 小时前
STM32CubeMX+HAL+Keil5 PWM呼吸灯
c语言·stm32·单片机
Ww.xh6 小时前
STM32嵌入AI模型全流程指南
stm32
Ww.xh6 小时前
STM32嵌入AI模型实战指南
stm32
傻童:CPU6 小时前
板级支持包的构建
stm32
进击的小头6 小时前
第7篇:嵌入式芯片运算核心:ALU_MAC_FPU的工作原理与性能差异
单片机·嵌入式硬件