别再只做点灯了!用STM32和常见传感器做个智能家居原型:我的晾衣架项目复盘与代码优化

张开发
2026/4/14 4:38:23 15 分钟阅读

分享文章

别再只做点灯了!用STM32和常见传感器做个智能家居原型:我的晾衣架项目复盘与代码优化
从零构建智能晾衣架STM32多传感器融合与代码架构优化实战去年夏天我阳台上的晾衣架在暴雨中淋湿了三件衬衫后终于下定决心用STM32打造一个真正的智能晾衣系统。这不是简单的温度高了就收衣服项目而是一个融合了六种传感器、三种控制模式和云端监控的完整解决方案。本文将分享如何从零开始构建这样一个系统并重点剖析那些教科书上不会告诉你的实战经验——特别是如何让杂乱的传感器代码变得优雅可维护。1. 硬件架构设计与传感器选型智能晾衣架的核心在于环境感知的准确性与实时性。经过三个版本的迭代我的硬件方案最终确定为主控芯片STM32F103C8T6性价比之王72MHz主频足够处理多传感器数据环境感知层DHT11温湿度传感器成本低±2℃精度满足需求BH1750数字光照传感器比光敏电阻更精准0-65535lx范围雨滴检测模块带比较器输出的型号可减少MCU负担风速传感器采用三杯式风速计霍尔元件脉冲计数执行机构28BYJ-48步进电机ULN2003驱动板成本30元扭矩足够人机交互0.96寸OLED三个轻触按键比1602液晶更适合参数设置提示DHT11上电后需要1s稳定时间建议在初始化时先读取一次丢弃数据实际部署时发现几个关键问题雨滴传感器在阳台边缘易受蜘蛛网干扰→增加定期自检逻辑步进电机在低温环境下容易失步→加入堵转检测电路多个I2C设备地址冲突→使用PCA9548A I2C多路复用器传感器数据采集周期优化方案传感器类型初始采样间隔优化后间隔触发条件温湿度2s10s温度变化1℃时自动加速光照1s5s光照突变时立即采样雨滴连续监测连续监测-风速5s30s风速3m/s时加速采样2. 状态机设计与模式切换优化原始代码中使用简单的if(m)判断自动/手动模式当功能扩展后会导致逻辑混乱。我重构为明确的状态模式typedef enum { MODE_MANUAL 0, MODE_AUTO, MODE_SETTING, MODE_CALIBRATION } SystemMode; typedef struct { SystemMode current_mode; uint8_t motor_status; // 0:停止 1:展开中 2:收拢中 uint32_t last_sensor_update; SystemSettings settings; } SystemState;状态转换采用事件驱动机制通过统一的消息队列处理void handle_event(SystemEvent event) { switch(state.current_mode) { case MODE_MANUAL: if(event EV_BUTTON_OPEN) start_motor(OPEN); break; case MODE_AUTO: if(event EV_RAIN_DETECTED) { if(check_safe_to_retract()) start_motor(CLOSE); } break; // 其他状态处理... } }关键改进点将模式切换与具体操作解耦增加设置和校准专用模式使用位域存储传感器告警状态引入300ms的去抖周期防止误触发3. 传感器数据融合算法单一传感器容易误判比如光照突变可能是云层变化而非下雨我采用多传感器数据融合策略雨量综合判断雨滴传感器持续高电平(5s)光照强度突降50%以上湿度上升速率5%/min满足其中两项则判定为下雨大风预警算法# 伪代码风速平滑处理 window_size 5 wind_speeds [0]*window_size def get_safe_speed(): global wind_speeds current read_wind_sensor() wind_speeds.pop(0) wind_speeds.append(current) avg sum(wind_speeds)/window_size return avg if avg 2 else current # 低速时取实时值更敏感温湿度补偿DHT11在高温高湿环境下精度下降当温度30℃时湿度读数5%建立校准对照表通过实验室温湿度计校准4. 代码架构优化实践原始代码将所有功能堆在main.c里我将其重构为模块化架构├── Drivers │ ├── motor.c # 电机驱动与堵转检测 │ ├── sensor_io.c # 统一传感器接口 ├── Middlewares │ ├── state_machine # 核心状态机逻辑 │ ├── data_fusion # 传感器算法 ├── Application │ ├── oled_ui.c # 菜单系统 │ ├── cloud_comm.c # ESP8266通信关键技巧使用硬件抽象层(HAL)封装传感器操作// sensor_io.h typedef struct { int (*init)(void); int (*read)(SensorData*); uint32_t min_interval; } SensorDriver; // dht11.c const SensorDriver DHT11 { .init dht11_init, .read dht11_read, .min_interval 1000 };配置参数集中管理#pragma pack(1) typedef struct { uint8_t temp_threshold; uint8_t humidity_threshold; uint16_t light_threshold; uint8_t wind_threshold; uint8_t reserved[3]; // 对齐填充 } SystemSettings; #pragma pack()使用RTOS任务划分功能模块FreeRTOS内存占用约6KBvoid vSensorTask(void *pvParameters) { while(1) { update_all_sensors(); vTaskDelay(pdMS_TO_TICKS(1000)); } }5. 人机交互升级方案原始按键设置方式非常反人类我设计了多级OLED菜单系统旋转编码器替代按键EC11编码器中断方式检测操作体验提升明显菜单数据结构typedef struct { const char* title; MenuItemType type; void* data; int16_t min_val; int16_t max_val; void (*callback)(int16_t); } MenuItem; const MenuItem main_menu[] { {温度阈值, NUMBER, settings.temp_th, 10, 40, NULL}, {湿度阈值, NUMBER, settings.humidity_th, 30, 90, NULL}, {保存设置, ACTION, NULL, 0, 0, save_settings} };防误触设计重要设置需要长按确认数值调节时有加速滚动效果10秒无操作自动退出设置模式6. 从本地到云端ESP8266接入实战使用AT指令与ESP8266通信的关键代码片段void wifi_send_data(const SensorData* data) { char buffer[128]; snprintf(buffer, sizeof(buffer), GET /update?t%.1fh%.1fr%d HTTP/1.1\r\n Host: api.thingspeak.com\r\n\r\n, >#define SENSOR_PWR_ON() GPIO_WriteHigh(SENSOR_PWR_GPIO) #define SENSOR_PWR_OFF() GPIO_WriteLow(SENSOR_PWR_GPIO)动态调整MCU主频72MHz↔8MHzvoid set_cpu_freq(FreqLevel level) { RCC_SYSCLKConfig(level FREQ_HIGH ? RCC_SYSCLKSource_PLLCLK : RCC_SYSCLKSource_HSI); }创新性的天气预报休眠策略晴天夜晚10分钟唤醒一次阴天5分钟唤醒雨季1分钟唤醒基于历史数据动态调整最终平均功耗从85mA降至8.3mA18650电池续航从1天提升到2周。

更多文章