STM32智能家居系统毕设全解析:从硬件选型到源码实现与答辩 📅 发布时间:2026/9/12 8:05:42 👁 浏览次数: 简介基于STM32的智能家居系统毕业设计资料包面向电子、通信、自动化及相关专业学生可作为期末课程设计、课程大作业或毕业设计项目参考。项目以STM32单片机为核心围绕家居环境监测与设备控制展开适合初学者通过完整项目学习外设驱动与系统集成。压缩包以zip格式打包整体大小15.02MB内含源程序代码、毕业论文文档和答辩演示PPT源码已经过调试测试可直接运行验证便于对照学习。这项毕业设计在答辩评审中获得98分说明其方案完整性与实现质量较高具有很好的借鉴价值基础较好的读者还可以在现有框架上扩展功能完成更丰富的智能控制逻辑。目前已有355人在CSDN学习/下载该项目可作为入门参考或项目模板帮助理解嵌入式系统设计到论文撰写的完整流程。1. 这套 STM32 智能家居系统到底在做什么在毕业设计选型时最容易被老师判定为「工作量足、难度适中、演示效果好」的题目就是基于 STM32 的智能家居系统。它用一个 32 位单片机读几路传感器控制几路继电器输出再挂一个 Wi-Fi 模块把数据传出去软件上覆盖 GPIO、定时器、串口、中断和 PWM。这五个外设恰好是嵌入式开发最常用的基本功所以它在每年本科毕业设计里出现频率极高也是招聘时面试官最喜欢往深里问的地方。本文从硬件选型开始把源码里真正能跑通的模块逐个拆开最后落到论文框架和答辩 PPT 的结构上让拿高分这件事变得可复现。2. 硬件架构与选型怎样用最少外设搭出完整闭环2.1 分层思路传感器、主控和执行器各干各的先画一张系统框图。这个步骤不能省因为论文第三章和开题报告都要用PPT 第一张技术页也要放。系统从上到下分三层感知层温湿度传感器 DHT11、光照检测光敏电阻配合 ADC、人体红外检测模块 HC-SR501。控制层STM32F103C8T6 最小系统板负责读取传感器数据、执行控制逻辑、通过 USART 与 Wi-Fi 模块通信。执行层继电器驱动照明和插座、蜂鸣器做本地报警、0.96 寸 OLED 屏显示实时环境数据。为什么先画这张图因为它决定了源码模块怎么拆。读传感器的代码只和传感器打交道控制继电器的代码只操作 GPIO两者通过一个全局结构体交换数据。这样模块化写出来的代码在论文里可以拆成几个小节PPT 每页对应一个模块代码、章节、答辩逻辑全部对齐能省下大量整理时间。2.2 主控选型为什么是 STM32F103C8T6常见的替代选项是 STM32F407 和 GD32F103但毕业设计里选择 F103C8T6 有三点理由。第一是成本可控。最小系统板价格在十几到二十几元整机 BOM 控制在 150 元以内老师问预算时答案干净利落。第二是外设数量刚好匹配。C8T6 提供 37 个 GPIO、3 路 USART、2 个 I2C、2 个 SPI、1 个 12 位 ADC 和 4 个 16 位定时器覆盖智能家居模块后还留有冗余。第三是软件生态成熟。标准外设库、HAL 库、LL 库都能直接用网上参考代码数量充足碰到诡异现象时更容易找到前人排错记录。F407 的 FPU、DSP 指令和更大的 Flash 对这套系统是纯浪费GD32 兼容性虽好但时钟配置和部分外设校准存在差异出问题时排查成本高。毕业设计追求的是「可控」不是「炫技」所以推荐 F103C8T6。2.3 传感器和执行器的选型对比2.3.1 温湿度传感器DHT11 还是 DHT22指标DHT11DHT22AM2302温度精度±2℃±0.5℃湿度精度±5%RH±2%RH采样周期1 秒2 秒总线协议单总线单总线典型价格3~5 元8~12 元毕设演示场景是室内温湿度DHT11 的精度完全够用。DHT22 的优势在恒温控制类实验里才有价值只做展示的话多花的钱换不来答辩加分。一个容易忽略的细节是采样周期DHT11 两次读取至少间隔 1 秒主循环轮询频率别高于 0.5Hz否则会读到连续相同值或校验失败。这部分单总线驱动逻辑可以复用到基于 STM32 的智能台灯、STM32 鱼缸这类相近选题里属于典型的「会一个等于会三个」的模块。2.3.2 继电器和电源回路继电器选 5V 低电平触发单路模块内部已带三极管驱动和续流二极管STM32 GPIO 输出低电平时继电器吸合高电平断开。要注意两个坑。第一个坑是反电动势。如果买到的是裸继电器而不是模块必须在线圈两端反向并联 1N4007 二极管否则断电瞬间线圈产生的反向电压会击穿 GPIO。第二个坑是负载电源隔离。继电器触点回路用 220V 交流给负载供电控制板用 5V USB 供电两边地线不能连在一起。实物接线时最容易在这里出错轻则跳闸重则烧板。2.4 通信方案ESP8266 加本地 OLED 双通道远程控制常见做法是 ESP8266-01S 刷 AT 固件走 UART 与 STM32 交互。注意 ESP8266 工作电压是 3.3V但模块峰值电流接近 300mA不能直接用主板的 3.3V 稳压器供电要用单独的 AMS1117-3.3 或带电平转换的适配器。直接插到开发板 3.3V 引脚上会出现重启、AT 无响应、Wi-Fi 连不上等一堆连锁问题这是整套系统排错难度最高的一个点。本地显示用 SSD1306 驱动的 0.96 寸 OLEDI2C 接口只需要两根数据线和两根电源线。OLED 和 ESP8266 形成「本地看数据、远程下命令」的双通道分工演示时即使现场没有公网环境也能在屏幕上看到实时数据规避网络不稳定的风险。2.5 引脚分配与连接表外设接口STM32 引脚备注DHT11单总线 DATAPA6外部上拉 4.7kΩ继电器模块2 路IN1 / IN2PB0 / PB1低电平触发有源蜂鸣器IOPB2高电平响OLEDSSD1306I2C1 SCL / SDAPB6 / PB73.3V 供电ESP8266-01SUSART2 TX / RXPA2 / PA3模块独立 3.3V 供电人体感应 HC-SR501OUTPC13高电平输出风扇调速PWMTIM3_CH2PA7需三极管或 MOS 驱动所有模块的 GND 必须连到一起ESP8266 电源单独接这样串口通信才稳定。杜邦线长度控制在 20cm 以内DHT11 数据线超过 30cm 时波形会出现毛刺届时要调上拉电阻或换屏蔽线。3. HAL 库工程落地GPIO、定时器、DHT11 和 ESP8266 的源码实现3.1 工程骨架用 CubeMX 生成基础工程再改代码毕设里很少有人从零手写启动文件。常见做法是先用 STM32CubeMX 配置外设生成工程然后往里写业务逻辑这种方式既稳妥又符合论文「基于 CubeMX 与 HAL 库」的技术路线。工程类型选 MDK-ARMKeil uVision5目标芯片 STM32F103C8Tx配置时钟树为外部 8MHz 晶振、PLL 倍频到 72MHz调试端口选 SWD。然后依次使能 USART2115200-8-N-1、I2C1400kHz、ADC1PA0 采集光敏电阻、TIM3PWM 输出到 PA7在 GPIO 配置里把 PA6 设为推挽输出、PB0/PB1/PB2 设为输出、PC13 设为输入上拉。生成工程后把 main.c 里的空转代码整理干净工作就完成一大半了。对外设配置的细节要写进论文第三章描述套路是「引脚功能分配 → 时钟频率计算 → 参数配置 → 初始化结果验证」。这部分属于老师重点关注的工作量体现写的时候不要用「自动生成」一笔带过要说明每个参数的选值依据。3.2 GPIO 驱动继电器翻转与按键防抖3.2.1 GPIO 初始化代码CubeMX 生成的MX_GPIO_Init已经完成大部分工作但裸数字GPIO_PIN_0让业务代码的可读性很差。我习惯单独建一个board.h把所有引脚封装成宏/* board.h */ #ifndef BOARD_H #define BOARD_H #include main.h #define RELAY1_Pin GPIO_PIN_0 /* PB0 低电平吸合 */ #define RELAY1_Port GPIOB #define RELAY2_Pin GPIO_PIN_1 /* PB1 低电平吸合 */ #define RELAY2_Port GPIOB #define BUZZER_Pin GPIO_PIN_2 /* PB2 有源蜂鸣器 */ #define BUZZER_Port GPIOB #define RELAY1_ON() HAL_GPIO_WritePin(RELAY1_Port, RELAY1_Pin, GPIO_PIN_RESET) #define RELAY1_OFF() HAL_GPIO_WritePin(RELAY1_Port, RELAY1_Pin, GPIO_PIN_SET) #define RELAY1_TOGGLE() HAL_GPIO_TogglePin(RELAY1_Port, RELAY1_Pin) #define BUZZER_ON() HAL_GPIO_WritePin(BUZZER_Port, BUZZER_Pin, GPIO_PIN_SET) #define BUZZER_OFF() HAL_GPIO_WritePin(BUZZER_Port, BUZZER_Pin, GPIO_PIN_RESET) #endif参数说明RELAY1_Pin和RELAY1_Port定义端口和引脚号RELAY1_ON里为什么是GPIO_PIN_RESET因为继电器模块是低电平触发。如果换成高电平触发的固态继电器只需要把 ON 和 OFF 的宏定义反过来业务层代码不用动。HAL_GPIO_TogglePin用于按键切换场景读写一次ODR寄存器完成翻转中间不带读改写操作原子性在单核 MCU 上足够可靠。3.2.2 继电器翻转动作与按键消抖直接在主循环里判断按键电平就执行翻转会遇到电平抖动导致一次按键误触多次。标准做法是加 20ms 软件防抖同时做边沿检测保证按下一次只触发一次uint8_t Key_Scan(GPIO_TypeDef* key_port, uint16_t key_pin) { static uint8_t key_last GPIO_PIN_SET; uint8_t key_now HAL_GPIO_ReadPin(key_port, key_pin); /* 检测下降沿上一次是高这一次变低 */ if (key_last GPIO_PIN_SET key_now GPIO_PIN_RESET) { HAL_Delay(20); /* 消抖延时 */ if (HAL_GPIO_ReadPin(key_port, key_pin) GPIO_PIN_RESET) { key_last GPIO_PIN_RESET; return 1; /* 有效按键事件 */ } } key_last key_now; return 0; }逻辑说明第一次读到低电平时不立即返回延迟 20ms 再读一次确认引脚仍然为低才认定有效按下。key_last记录上一次电平状态按住不松手时不会反复触发只有松开后再次按下才会产生新事件。主循环中这样调用if (Key_Scan(KEY_PORT, KEY_Pin)) { RELAY1_TOGGLE(); /* 单击翻转继电器 */ }这里把按键事件和继电器动作完全解耦了后续如果想改成「双击开灯、长按关机」只需要在Key_Scan上叠加计时逻辑不用动继电器部分。3.3 定时器与 PWM风扇调速与 OLED 刷新节拍PWM 在系统里的作用是直流风扇调速。初始化参数来自 CubeMX 自动生成的MX_TIM3_Init但有两个参数需要自己理解。配置代码如下void MX_TIM3_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim3.Instance TIM3; htim3.Init.Prescaler 72 - 1; /* 72MHz / 72 1MHz 计数频率 */ htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 1000 - 1; /* PSC和ARR配合输出1kHz PWM */ htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim3); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 500; /* 初始占空比50% */ sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim3, sConfigOC, TIM_CHANNEL_2); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_2); } void Fan_SetSpeed(uint8_t percent) { if (percent 100) percent 100; /* ARR999比较值0~999对应0~100%占空比 */ __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_2, percent * 10); }参数说明预分频系数 72-1 把 72MHz 时钟降为 1MHz自动重装值 1000-1 让 PWM 频率为 1kHz。Fan_SetSpeed里把 0~100 的百分比乘以 10映射到 0~999 的比较寄存器方便在 OLED 或调试串口直接显示。注意风扇不能直接接在 GPIO 上。PA7 用 NPN 三极管常见 8050做开关驱动或者用 NMOSAO3400直接驱动PWM 信号接基极或栅极负载接集电极/漏极发射极/源极接地。如果直接用 GPIO 驱动风扇很快会出现端口烧毁这种问题在答辩演示时当场翻车率最高。频率上如果在意风扇噪音建议把周期改为 49也就是 20kHz此时占空比换算公式变成percent * 50Pulse 范围 0~49。1kHz 在演示时完全能转只是电机线圈会有轻微啸叫。OLED 刷新也可以挂在定时器中断里用 100ms 中断刷新一次屏幕避免主循环里频繁调用刷屏函数拉低系统响应速度。用 HAL 的定时器中断方式时HAL_TIM_PeriodElapsedCallback里加一个节拍标志位主循环检测到标志后执行OLED_ShowData()。3.4 DHT11 驱动单总线时序与数据校验DHT11 使用单总线协议所有通信由主机发起MCU 先输出起始信号再切换为输入读取数据。起始信号是拉低至少 18ms、拉高 20~30μs之后把引脚切到输入模式。DHT11 响应时输出一个 80μs 低电平和 80μs 高电平随后连续输出 40 位数据湿度整数、湿度小数、温度整数、温度小数、校验和。每一位都以 50μs 低电平开头低电平结束后的高电平宽度决定位值26~28μs 表示 070μs 表示 1。一个容易出错的点是 GPIO 方向切换。HAL 库里没有直接切换方向的函数常见做法是重新调用HAL_GPIO_Init把 Mode 改成GPIO_MODE_INPUT或GPIO_MODE_OUTPUT_PP。完整读取代码uint8_t DHT11_ReadByte(void) { uint8_t i, data 0; for (i 0; i 8; i) { /* 等待50us低电平结束 */ while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET); /* 延时40us后采样区分0和1 */ delay_us(40); if (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET) { data (data 1) | 0x01; /* 等待高电平结束避免提前进入下一bit */ while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET); } else { data data 1; } } return data; } uint8_t DHT11_Read(float* temp, float* humi) { uint8_t buf[5] {0}; uint8_t i; HDC_HandleStart(); /* 拉低18ms拉高30us切输入 */ /* 等待响应先低后高 */ while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET); while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_RESET); while (HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN) GPIO_PIN_SET); for (i 0; i 5; i) { buf[i] DHT11_ReadByte(); } /* 校验和前4字节相加低8位等于第5字节 */ if ((uint8_t)(buf[0] buf[1] buf[2] buf[3]) ! buf[4]) { return 0; } *humi buf[0] buf[1] / 10.0f; *temp buf[2] buf[3] / 10.0f; return 1; /* 1表示读取成功 */ }逻辑说明读每一位时先等当前低电平结束再延时 40μs 采样。这样做的理由是 0 位的高电平只有 26~28μs40μs 后已经回到低电平而 1 位的高电平持续 70μs40μs 时仍然为高。以 40μs 作为判决点不需要精确定时 70μs 就能稳定区分。HDC_HandleStart函数负责起始信号和 GPIO 方向切换实际实现时记得在读响应前把引脚切为输入。输出时把整数和小数拼成浮点数校验和错误时返回 0主循环里只在返回 1 时才刷新显示避免脏数据上屏。DHT11 的时序对中断延迟敏感如果读取过程被优先级更高的中断打断导致超时常见应对是把delay_us用DWT-CYCCNT做高精度延时代替软件循环这样可以容忍更高的硬件中断负载。3.5 ESP8266 数据上报与指令解析ESP8266-01S 出厂固件的默认波特率通常是 115200与 USART2 配置一致。服务器端用网络调试助手在 PC 上开 TCP Server或者用物联网平台提供的接入地址。把 AT 指令按顺序整理成一张配置表步骤AT 指令期望返回作用关闭回显ATE0OK减少串口数据量降低解析压力配置 STA 模式ATCWMODE1OK让模块连接路由器连接热点ATCWJAPSSID,PWDOK替换成实际热点名和密码单连接模式ATCIPMUX0OKTCP 一对一通信使能透传模式ATCIPMODE1OK后续串口数据直接透明转发建立 TCP 连接ATCIPSTARTTCP,IP,portCONNECT OKIP 填服务器地址初始化流程可以封装成一个函数每一步都等待OK返回再执行下一步。没有正确处理返回值的典型症状是热启动时模块还没就绪就开始发 AT 指令导致ATE0没有返回后续全部失联。解决办法是在ESP8266_Init开头加 1~2 秒延时等待模块上电稳定。数据上报用 JSON 格式字符串。STM32 内存有限不用解析库直接snprintf拼一串char report[80]; snprintf(report, sizeof(report), {\type\:\env\,\temp\:%.1f,\humi\:%.1f,\light\:%d,\pir\:%d}\r\n, temp, humi, light_val, human_val); HAL_UART_Transmit(huart2, (uint8_t*)report, strlen(report), 200);逻辑说明snprintf的好处是自动截断避免超长字符串溢出缓冲区。浮点%f在嵌入式库中默认不走浮点格式化需要在 Keil 里勾选Use MicroLIB或者把温度湿度先乘以 10 变成整数传输服务器端再除以 10这也是很多工程里更常见的做法。指令接收用串口空闲中断配合状态机。USART2 接收回调里把字节放入环形缓冲主循环每轮扫描缓冲里是否有\r\n结尾的字符串收到完整一行后按规则解析void UART_RxHandler(uint8_t byte) { if (byte \n) { cmd_buf[cmd_len] \0; Process_RemoteCommand(cmd_buf); /* 解析并执行命令 */ cmd_len 0; } else { if (cmd_len CMD_BUF_SIZE - 1) { cmd_buf[cmd_len] byte; } } }Process_RemoteCommand里做字符串匹配比如LED1_ON对应RELAY1_ON()LED1_OFF对应RELAY1_OFF()匹配不到就直接丢弃这一行。这里用strncmp而不是strstr防止「A 命令是 B 命令前缀」时误触发。4. 论文框架与答辩 PPT 的落地产出4.1 论文框架七章结构里每章放什么论文写作围绕「方案可行、数据可复现、工作量可见」三个原则展开。合理的大纲安排是摘要和绪论交代背景第二章做系统总体方案设计和对比选型第三章写硬件电路设计第四章写软件设计第五章写系统调试与测试最后是总结与展望。第二章里的方案对比表格要写清楚为什么选模块化裸机方案而不是 RTOS为什么选 Wi-Fi 而不是蓝牙或 ZigBee——标准答法是从传输距离、功耗、成本、组网复杂度四个维度拉一张表。第三章硬件设计要放原理图关键部分的截图包括最小系统、电源、传感器电路、继电器驱动电路。第四章软件设计要放主程序流程图和几个关键函数的代码片段用的就是本文 3.2 到 3.5 节这些模块。4.2 答辩 PPT 的十二页结构答辩 PPT 建议控制在 12 页超出这个数量观众容易疲劳少于 10 页又显得工作量不足。十二页是封面、选题背景与意义、需求分析、系统总体架构、硬件选型对比、硬件电路设计、软件流程与模块划分、核心代码说明、实物演示照片、测试数据与结果分析、创新点与不足、总结与致谢。演示环节比 PPT 本身更重要。提前录一段完整视频存在手机里现场演示失败时立刻切换到视频这不算作弊叫风险评估。蜂鸣器、继电器这类有明显声音和动作反馈的模块现场演示时演示者本人不要碰手机控制端由台下同学操作否则会被质疑预先录入了指令。4.3 答辩问答的四个高频问题答辩老师最常问的四个问题是为什么选 STM32 而不是 MSP430 或 ArduinoWi-Fi 模块的透传模式和正常模式有什么区别继电器隔离电路的工作原理是什么如果增加 20 个设备这个架构哪里会先成为瓶颈。前三个问题在本文对应章节都能找到答案第四个问题要提到 C8T6 的引脚数和定时器数量限制、ESP8266 单连接模式限制、以及改用 ESP8266 AP 模式或增加协调器节点做多级拓扑的演进方向。回答时不需要背概念把代码里的实际处理方式讲出来就是最好的回答。4.4 源码整理的一件小事论文提交时要附核心代码注意 Keil 工程里的DebugConfig和Listings文件夹会包含本机信息提交前清理掉换行隔离的无用文件。源码注释建议保留中英混杂风格不要全删干净答辩时老师可能直接打开代码看注释率。头文件里把板级配置、外设配置、业务逻辑分开三个目录这比堆一个main.c要好读得多也是答辩时展示工程素养最快的一步。本文还有配套的精品资源点击获取