ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32智能家居照明系统:PWM调光与ESP8266远程控制实战

STM32智能家居照明系统:PWM调光与ESP8266远程控制实战 简介毕设课程作业_智能家居照明系统.zip 是一套以 Android 客户端为主的智能家居照明系统项目源码面向计算机类毕业设计或课程作业场景适合需要完成智能家居相关选题的学生参考可覆盖从需求分析、系统架构到界面与控制逻辑实现的核心环节。压缩包共 82 个文件以 kt 源码、xml 布局与配置、png 图片、gradle 构建脚本等为主体整体约 451KBkt 文件承载主要业务逻辑xml 负责界面布局与资源描述png 多为截图或图标素材gradle 用于工程构建与依赖管理结构虽精简但脉络清晰。目前已有 184 人浏览学习。资源内含完整的 Android 工程目录、Gradle 配置、界面资源与系统源码可对应学习 Kotlin 开发、UI 布局、模块划分以及智能家居常见的开关控制、亮度调节、定时任务等逻辑描述中涉及的 AI 自动化控制、安全与测试等内容也能为毕设文档撰写和答辩准备提供参考适合在源码基础上进行功能扩展与二次开发。1. 智能家居照明系统从题目到可演示代码很多人以为“智能家居照明系统”就是手机远程开关灯真正拿它做毕设或课程作业时才发现答辩高频问题全在细节上亮度怎么调的、传感器误触发怎么处理、断电重启后状态会不会乱。这个标题背后其实是一套软硬联动的完整系统主控选择、光照与人体的信号采集、PWM调光、通信协议、上位机或小程序控制至少跨了嵌入式、通信和简单应用三层。本文按一线工程师做这类方案的常见路径展开从选型到调试只讲能落地的做法给代码、给参数、给送分项。适合正在做STM32方向毕设、或者课程设计需要出实物演示的读者看完整篇可以直接拼出一套属于自己的版本。2. 系统架构与选型先定“智能”的定义再选主控与通信2.1 需求分解照明系统的“智能”体现在哪几个模式毕设评审不会只看灯亮不亮而是看系统是否有清晰的场景逻辑。我一般会把照明系统的智能拆成三个层次自动模式环境光暗到阈值且检测到人体活动时开灯人离开后延时熄灭或根据环境亮度自动调节输出手动模式本地按键或手机App随时覆盖自动状态支持开关和亮度调节场景模式例如“回家模式”“离家模式”一次控制一到多路灯光属于加分项。注意“智能”不需要堆砌功能反而要把自动翻车的场景兜住。最典型的坑是光敏传感器检测到“暗”就开灯但人体红外传感器没有触发导致白天灯也亮。正确逻辑是光照和人体两个条件做“与”判断而不是只靠其中一个。2.2 主控方案对比STM32为什么是毕设的稳妥选择课程作业可以用51单片机 继电器糊一个但智能家居照明系统要做到“调光”“传感器融合”“联网上报”51的资源边界很快摸到。主流候选如下主控方案适合场景成本开发效率答辩观感STC89C52纯开关控制无调光最低低偏弱STM32F103C8T6调光 多传感器 联网约10元高CubeMX强ESP32自带WiFi/蓝牙免外挂模块约15元高强但容易被追问WiFi协议栈细节Arduino快速原型验证约20元极高偏弱工程性不够一般做法是选STM32F103C8T6 作为主控外挂ESP8266做网络透传。原因有三其一STM32的外设资源足够覆盖PWM调光、ADC采集、串口通信、定时器消抖不用为资源做妥协其二CubeMX生成初始化代码能省下大量时间把精力放在业务逻辑上其三答辩时评审通常会问“中断怎么处理的”“PWM频率怎么定的”STM32相关的知识积累更容易回答。2.3 通信方案选型WiFi透传 与 蓝牙的取舍智能照明系统最常见的控制方式是手机这就涉及短距离通信选型蓝牙BLE配对简单延迟约在几十毫秒以内缺点是控制距离小且手机配对和重连逻辑在毕设答辩现场容易出意外WiFiESP8266/ESP32能接入路由器配合MQTT或TCP实现真正的“远程控制”演示效果好网络断线时还能降级为本地模式稳得多Zigbee组网能力强但网关开发量和成本都高不适合课程周期不推荐。实际项目中我选WiFi方案架构是“STM32 主控 ESP8266 网络模块”STM32与ESP8266之间用串口通信ESP8266负责接入路由器。这样即使网络崩溃本地光照与人感联动照明依然工作演示时也不会全盘崩溃。2.4 驱动电路要点不要指望PA0引脚直接推动灯带有同学直接把LED灯串到STM32引脚上结果亮度上不去、引脚发热甚至直接把MCU烧了。照明负载通常需要额外的驱动电路小功率LED几十毫安用三极管8050做开关或线性驱动PWM通过三极管控制LED电流大功率LED灯带或白炽灯用MOS管如AO3400或继电器控制PWM加在栅极通过MOS管调节回路等效电压或平均电流。继电器控制方式简单可靠但问题是噪声大、响应慢不适合做平滑调光只适合做开关场景。PWM调光的核心是先选对负载驱动方式再谈软件调光算法。硬件上建议给LED驱动部分加续流二极管和RC吸收电路否则PWM切换瞬间的反向电动势可能让MOS管过压击穿。3. 照明控制核心实现PWM调光、光照采集与串口协议3.1 CubeMX工程里的关键引脚分配建议用STM32CubeMX生成基础工程手动改代码的成本最低。引脚分配参考功能引脚外设LED调光PWMPA0TIM2_CH1光敏电阻ADCPA1ADC1_IN1人体红外输入PB0GPIO_EXTI0按键PB1GPIO_INPUT调试串口PA9/PA10USART1ESP8266串口PA2/PA3USART2注意PA9/PA10的USART1 用于调试输出USART2 用于和ESP8266通信两个串口职责分开避免调试日志混进业务数据。CubeMX中需要把TIM2的Channel1设置为PWM Generation CH1ADC设置为Continuous Conversion模式时钟树默认配置即可。3.2 PWM调光初始化与参数计算PWM调光的核心参数是频率和占空比。人眼对低频闪烁敏感低于1kHz会有明显频闪感因此一般将PWM频率设定在1kHz20kHz。频率过高时MOS管开关损耗增加电源纹波也会变大10kHz是一个折中选择。// tim2_pwm_init.c void TIM2_PWM_Init(void) { TIM_OC_InitTypeDef sConfigOC {0}; TIM_HandleTypeDef htim2 {0}; htim2.Instance TIM2; htim2.Init.Prescaler 72 - 1; // 72MHz / 72 1MHz 计数 htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 100 - 1; // 1MHz / 100 10kHz htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_PWM_Init(htim2); sConfigOC.OCMode TIM_OCMODE_PWM1; sConfigOC.Pulse 0; // 初始占空比0% sConfigOC.OCPolarity TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(htim2, sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); }代码说明Prescaler设为71把72MHz的定时器时钟分频成1MHzPeriod设为99这样计数器从0数到99正好100个计数周期输出频率是10kHz占空比调节步长就是1%。调用HAL_TIM_PWM_Start后修改占空比使用__HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, value)value范围是099。若想实现呼吸灯效果在定时中断里每10ms把value先加一到99后再减一即可形成平滑渐变。3.3 光敏电阻采样与多级滤波光敏电阻和固定电阻串联形成分压电路PWM产生的光照波动和市电频闪都会叠加在ADC采样值上直接采样会造成灯光“呼吸抖动”。常见做法是连续采样8次去掉最大值和最小值后取平均再做一次一阶低通滤波让传感器响应平缓。// adc_sensor.c uint16_t ReadLux_Processed(void) { uint32_t sum 0; uint16_t raw[8]; for (int i 0; i 8; i) { raw[i] HAL_ADC_GetValue(hadc1); HAL_Delay(2); // 两次采样间隔防止ADC内部电容未充放完 } // 冒泡排序后去掉最大最小值 for (int i 0; i 7; i) { for (int j 0; j 7 - i; j) { if (raw[j] raw[j 1]) { uint16_t tmp raw[j]; raw[j] raw[j 1]; raw[j 1] tmp; } } } for (int i 1; i 7; i) { sum raw[i]; } uint16_t avg sum / 6; // 一阶低通滤波本次结果 0.7*上次 0.3*本次 static uint16_t filtered 0; if (filtered 0) { filtered avg; } else { filtered (uint16_t)((uint16_t)(filtered * 0.7) (uint16_t)(avg * 0.3)); } return filtered; }ADC必须先调用HAL_ADC_Start(hadc1)再执行HAL_ADC_GetValue否则读到的永远是0。阈值判断不要用单一固定值给一个2030的滞回区间例如低于800判定为暗、高于900才算亮避免环境光处于临界值时灯来回开关。3.4 串口指令协议设计STM32与ESP8266之间、或STM32与调试电脑之间需要一个简洁明了的协议。推荐使用字符串指令加换行符作为帧结束标志方便用串口助手直接调试格式如下SM_CMD,CH1,BRIGHTNESS,STATUS SM_CMD,CH1,128,1 SM_QUERY,CH1第1段是命令类型第2段是灯路编号第3段是亮度值0255第4段是手动/自动状态标识。STM32端用串口接收中断逐字节缓存检测到换行符\n后用strtok按逗号分割逐字段校验。解析示例如下// uart_parser.c void UART_Parse_Command(char *cmd) { char *token strtok(cmd, ,); if (token NULL) { return; } if (strcmp(token, SM_CMD) 0) { char *ch strtok(NULL, ,); // 通道号 char *brightness strtok(NULL, ,); // 亮度 0-255 char *status strtok(NULL, ,); // 0自动 1手动 if (ch ! NULL brightness ! NULL status ! NULL) { uint8_t ch_num atoi(ch); uint8_t bright atoi(brightness); uint8_t mode atoi(status); if (mode 1) { __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, scale(bright, 0, 255, 0, 99)); } } } }scale函数把0255的亮度值映射到099的PWM占空比范围。这样设计协议的好处是和真实IoT系统的JSONTopic思路一致但大大简化了解析成本调试时直接用手机串口App或电脑串口助手发字符串即可。协议固定在代码注释里答辩时可以直接展示指令格式和通信时序。4. 接入手机控制ESP8266与MQTT做远程照明控制4.1 让ESP8266做协处理器不占用STM32主逻辑MCU侧最好不要既跑传感器逻辑又维护TCP和MQTT协议栈这样代码耦合严重、出问题难排查。推荐结构是STM32负责传感器采集、PWM调光和本地自动模式ESP8266只做网络透明传输它的串口一端连路由器另一端连STM32的USART2。ESP8266收到网络消息后通过串口原样转发给STM32STM32要上报状态时通过USART2把字符串发给ESP8266再由ESP8266推给服务器。如果使用官方AT固件简单方案下ESP8266将TCP客户端收到的数据原样转发给STM32STM32按上一章的协议解析字符串即可。不用在STM32上组装MQTT CONNECT报文这是很多教程没有明说但极其省事的细节。4.2 使用AT指令配置ESP8266连接路由器ESP8266上电后通过串口发送AT指令注意必须采用“发送指令→等待返回结果→发送下一条”的方式AT固件对并发指令有严格约束少于200ms间隔容易导致指令丢失。ATCWMODE1 ATCWJAPYourSSID,YourPassword ATCIPMUX0 ATCIPSTARTTCP,你的服务器或局域网IP,1883 ATCIPMODE1 ATCIPSEND指令说明CWMODE1将ESP8266设为Station模式仅连接路由器但不创建热点CIPMUX0设为单连接模式CIPSTART建立TCP连接IP写电脑或云服务器的局域网地址端口对应MQTT或自定义TCP服务端口CIPMODE1进入透传模式之后所有串口数据都会直接发送到远端服务器。使用“公共测试Broker”时例如broker.emqx.io需要先在本地浏览器打开官方管理页面确认服务器可用。公共Broker存在消息泄露风险仅适合演示不适合真实居家环境这是答辩时会被追问的点最好主动交代。4.3 手机端快速实现MQTT面板App与简易网关网络控制部分的标配是手机装MQTT面板App如MQTT Dash或IoT MQTT Panel填写Broker地址、Topic和Payload后直接发送控制指令。Topic定义参考Topic描述Payload示例light/cmd下发的控制指令SM_CMD,CH1,128,1light/status设备上报状态SM_STATUS,CH1,ON,128light/auto自动模式开关1或0如果不想依赖云Broker也可以在阿里云或腾讯云买一台轻量服务器安装Mosquitto或其他MQTT代理服务器供毕设演示使用。我用这种方式搭建过测试链路核心是安全性和断线重连逻辑电脑端用mosquitto_sub -t light/status订阅验证STM32上报的状态mosquitto_sub -h broker.emqx.io -t light/status -v硬件端ESP8266和STM32上电时序需要注意STM32要先进入等待模式ESP8266完成联网后再启动业务循环否则STM32的传感器控制指令会因为ESP8266尚未入网而积压在串口缓冲区造成开局就“卡死”的假象。常见做法是STM32上电后给ESP8266预留3秒再通过AT指令ATCIPSTATUS查询网络状态确认已连接后再开启正常任务。5. 调试方法与答辩演示从能亮到有水平的几个加分细节5.1 验证清单逐项测试再进答辩教室答辩前至少过一遍下面的功能验证表每项都要有现象和测试方法光是“演示通过”是不够的测试项操作预期结果本地按键开关灯按下按键灯状态切换串口输出日志PWM调光发送亮度50灯光明显变暗自动模式用手遮住光敏电阻并模拟人体感应灯自动点亮人走灯灭人体红外消失后保持高电平触发超时5秒后熄灯OLED显示倒计时手机远程控制App发送MQTT指令灯响应并上报状态断网降级关闭路由器本地逻辑正常工作5.2 用逻辑分析仪看PWM波形确认实际输出频率常见做法是用定时器溢出中断做软件延时调光这种方案在单路PWM时没问题但多路并行时中断抢占会导致波形抖动严重时肉眼可见灯光闪烁。改用硬件PWM后即前文基于TIM2的实现PWM波形不再占用CPU逻辑分析仪可以抓出稳定的10kHz方波信号。打开逻辑分析仪软件后在PA0引脚上能看到固定频率、占空比可变的波形。若波形频率变成几Hz或抖动剧烈先检查Prescaler和Period是否配置正确再检查是否调用了HAL_TIM_PWM_Start。5.3 调试过程中最有效的技巧把状态透出到OLED我从课程设计到实际项目一直保留一个习惯给系统加一块0.96寸OLED实时显示当前亮度值、PWM占空比、人体感应状态和运行模式。这样做有三个直接好处一是现场调试时不需要反复插拔USB连串口看屏幕就能定位问题出在传感器还是控制逻辑上二是评审路过展台时OLED上的动态数据让系统看起来有信息量三是可以随时操作按键把系统从自动模式切到手动模式演示出“人机协同”的感觉这在答辩中比单纯亮灯更有说服力。OLED驱动不在本方案的硬件复杂度提升范围之内属于“送分项”务必保留。5.4 一个容易踩的坑传感器触点抖动与上电初始状态人体红外感应模块HC-SR501上电后有一段约1分钟左右的预热时间预热期间会随机输出高电平且输出回动次数较多。如果这期间恰好满足“暗环境 有人”灯就会误亮给人一种系统失控的感觉。解决办法是软件上加上开机延时系统启动后等待60秒再启用自动模式期间只处理手动指令。另外一个容易忽略的是PWM初始化时把Pulse设置为0但LED驱动电路如果采用“高电平驱动”和“低电平驱动”设计不同灯会反向熄灭或常亮。上电时先主动拉低PWM输出确认灯灭再进入正常控制流程可以避免演示现场灯光先亮后灭的尴尬。引脚分配的细节也值得深究如果人体红外传感器和高电平触发的声光报警同时复用同一个GPIO两个器件的上拉/下拉电阻可能互相影响。建议给人体传感器单独分配一个外部中断引脚并在传感器输出端加一个10kΩ下拉电阻确保无触发时读到确定的低电平这能省掉不少半夜查Bug的时间。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进