1. 项目概述:当窗帘遇上物联网
清晨的阳光透过窗帘缝隙洒进卧室,传统窗帘需要手动开合的不便,正是这个项目要解决的痛点。基于STM32的智能光控窗帘系统,通过光敏传感器实时监测环境亮度,自动控制电机完成窗帘开合动作,实现"天亮即开、天黑即闭"的智能化场景。
这个系统特别适合安装在卧室、办公室等需要规律采光的场所。作为开发者,我们选用STM32F103C8T6作为主控芯片,搭配L298N电机驱动模块和BH1750数字光强传感器,构建完整的硬件控制链路。系统支持手动/自动模式切换,在自动模式下可根据预设的光照阈值触发窗帘动作,同时保留手动按键控制功能作为备用方案。
提示:选择STM32F103系列是因为其性价比突出,72MHz主频完全能满足窗帘控制这类实时性要求不高的应用场景,同时丰富的外设接口方便扩展其他传感器。
2. 硬件系统设计详解
2.1 核心器件选型分析
主控芯片采用STM32F103C8T6(俗称"蓝莓派"),这款Cortex-M3内核的MCU具备:
- 64KB Flash + 20KB SRAM
- 3个USART、2个SPI、2个I2C接口
- 16通道12位ADC
- 7通道DMA控制器
光敏传感器选用BH1750FVI而非传统光敏电阻,主要考虑:
- 数字输出(I2C接口)避免模拟信号干扰
- 0-65535lx宽量程范围
- 1lx高分辨率
- 内置16bit AD转换器
电机驱动采用L298N双H桥模块,其关键参数:
- 驱动电压:5-35V
- 单路峰值电流2A
- 内置续流二极管
- 支持PWM调速
2.2 电路设计关键点
电源部分采用两级设计:
- 第一级:220V转12V/2A适配器
- 第二级:LM2596降压至5V给控制板供电
特别需要注意电机回路的EMC设计:
- 在电机两端并联104瓷片电容吸收高频干扰
- 电源输入端加装470μF电解电容
- 逻辑地与功率地通过0Ω电阻单点连接
注意:L298N的ENA/ENB使能端必须接PWM信号,直接接高电平会导致电机全速运行无法控制。
3. 软件架构与核心算法
3.1 系统状态机设计
定义5个主要状态:
- 待机状态:等待光强检测
- 检测状态:读取BH1750数据
- 判断状态:比较当前光强与阈值
- 执行状态:控制电机正/反转
- 异常状态:处理卡死等故障
状态转换逻辑用以下伪代码表示:
c复制while(1) {
switch(state) {
case STANDBY:
if(auto_mode) state = DETECT;
break;
case DETECT:
lux = BH1750_Read();
state = JUDGE;
break;
case JUDGE:
if(lux > OPEN_TH) state = ACT_OPEN;
else if(lux < CLOSE_TH) state = ACT_CLOSE;
else state = STANDBY;
break;
// 其他状态处理...
}
}
3.2 光强滤波算法
原始光强数据存在波动,采用滑动平均滤波:
c复制#define FILTER_LEN 5
static uint16_t filter_buf[FILTER_LEN];
static uint8_t filter_idx = 0;
uint16_t light_filter(uint16_t raw) {
filter_buf[filter_idx++] = raw;
if(filter_idx >= FILTER_LEN) filter_idx = 0;
uint32_t sum = 0;
for(uint8_t i=0; i<FILTER_LEN; i++) {
sum += filter_buf[i];
}
return (uint16_t)(sum / FILTER_LEN);
}
阈值判断引入迟滞比较,防止临界值抖动:
c复制#define OPEN_THRESHOLD 300 // 开窗帘阈值(lux)
#define CLOSE_THRESHOLD 100 // 关窗帘阈值(lux)
#define HYSTERESIS 50 // 迟滞区间
if(current_lux > (OPEN_THRESHOLD + HYSTERESIS)) {
open_curtain();
} else if(current_lux < (CLOSE_THRESHOLD - HYSTERESIS)) {
close_curtain();
}
4. 关键功能实现细节
4.1 电机PWM调速控制
使用TIM3通道1生成PWM控制电机速度:
c复制void pwm_init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
// 时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// GPIO配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 时基配置
TIM_TimeBaseStruct.TIM_Prescaler = 71; // 72MHz/72 = 1MHz
TIM_TimeBaseStruct.TIM_Period = 999; // 1MHz/1000 = 1kHz PWM
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);
// PWM模式配置
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 500; // 初始占空比50%
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStruct);
TIM_Cmd(TIM3, ENABLE);
}
4.2 BH1750传感器驱动
I2C接口的初始化与数据读取:
c复制#define BH1750_ADDR 0x23<<1 // 7位地址左移1位
void BH1750_Init(void) {
uint8_t cmd = 0x10; // 连续H分辨率模式
I2C_Write(BH1750_ADDR, &cmd, 1);
delay_ms(180); // 等待首次测量完成
}
uint16_t BH1750_Read(void) {
uint8_t buf[2];
I2C_Read(BH1750_ADDR, buf, 2);
return (buf[0]<<8) | buf[1];
}
5. 系统调试与优化
5.1 光强阈值校准方法
- 将传感器置于需要开窗帘的环境亮度下
- 读取串口输出的实时lux值
- 记录10次读数取平均作为OPEN_THRESHOLD
- 同理确定CLOSE_THRESHOLD
- 烧录前建议预留20%安全余量
5.2 电机堵转检测方案
通过电流检测判断是否堵转:
- 在电机电源回路串联0.1Ω采样电阻
- 使用ADC检测电阻两端电压
- 正常运行时电流约0.5A(对应50mV)
- 堵转时电流超过1.2A(120mV)触发保护
实现代码片段:
c复制#define MOTOR_NORMAL_CURRENT 50
#define MOTOR_BLOCK_CURRENT 120
void motor_check(void) {
uint16_t adc_val = ADC_Read(ADC_Channel_5);
float voltage = adc_val * 3.3 / 4096;
float current = voltage / 0.1 * 1000; // mV转mA
if(current > MOTOR_BLOCK_CURRENT) {
motor_stop();
system_error = MOTOR_BLOCKED;
}
}
6. 常见问题与解决方案
6.1 窗帘运行不到位
可能原因及对策:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 窗帘未完全打开 | 行程时间设置过短 | 增加motor_run_time参数 |
| 窗帘关闭后有缝隙 | 机械限位不准 | 调整导轨末端挡板位置 |
| 偶尔停止在中间 | 电源干扰导致MCU复位 | 加强电源滤波,添加看门狗 |
6.2 光强检测异常
典型故障排查流程:
- 检查I2C总线是否正常
- 用逻辑分析仪抓取波形
- 确认上拉电阻(4.7kΩ)已安装
- 验证传感器供电
- BH1750需要2.4-3.6V供电
- 测量VCC引脚电压
- 测试基础功能
c复制BH1750_Init(); while(1) { uint16_t lux = BH1750_Read(); printf("Light: %d lux\r\n", lux); delay_ms(1000); }
7. 系统扩展方向
7.1 增加WiFi远程控制
通过ESP8266模块实现:
- 使用AT指令连接路由器
- 搭建简易TCP服务器
- 手机APP发送控制命令
- STM32解析执行相应动作
关键代码结构:
c复制void wifi_process(void) {
if(USART2_RX_STA) { // 收到WiFi数据
if(strstr(USART2_RX_BUF, "OPEN")) {
open_curtain();
} else if(strstr(USART2_RX_BUF, "CLOSE")) {
close_curtain();
}
USART2_RX_STA = 0;
}
}
7.2 加入时间控制功能
利用RTC模块实现定时控制:
- 初始化PCF8563等RTC芯片
- 设置开启/关闭时间点
- 比较当前时间与预设时间
- 触发相应窗帘动作
时间判断逻辑示例:
c复制typedef struct {
uint8_t hour;
uint8_t min;
} TimePoint;
TimePoint open_time = {7, 30}; // 早上7:30开
TimePoint close_time = {19, 0}; // 晚上7:00关
void time_check(void) {
RTC_TimeTypeDef current;
RTC_GetTime(RTC_Format_BIN, ¤t);
if(current.RTC_Hours == open_time.hour &&
current.RTC_Minutes == open_time.min) {
open_curtain();
}
// 同理处理close_time...
}
在窗帘导轨两端加装霍尔传感器实现精确定位:
- 导轨两端安装磁铁
- 窗帘滑块上安装3144霍尔元件
- 检测到磁信号立即停止电机
- 配合软件去抖算法(建议50ms检测周期)
硬件连接示意图:
code复制+5V ---[10k]---+--- OUT → PA1
|
HALL3144 -----+
|
GND -----------+
实际调试中发现,不同材质的窗帘对系统有以下影响:
- 厚重绒布窗帘:需要更高扭矩电机(建议≥5kg·cm)
- 轻薄纱帘:可降低PWM频率减少噪音(建议500Hz)
- 双开式窗帘:需同步控制两个电机(建议使用带编码器的步进电机)