为什么需要自动调光?
你有没有过这样的经历:晚上躺在床上玩手机,屏幕亮度太高刺得眼睛疼;或者白天在办公室里,显示器亮度太低看不清?
固定亮度的照明设备其实很不人性化。环境光变化时,我们需要手动调节——但大多数人根本懒得这么做。结果就是:要么太亮费电又刺眼,要么太暗伤眼睛。
今天我们要做的,就是一个能"看懂"环境亮度的智能调光系统。它使用 BH1750 数字光照传感器,实时检测周围光线强度,自动调节 LED 或屏幕亮度。
这个项目适合用在:
- 智能台灯
- 显示器自动背光调节
- 手机/平板环境光适配
- 智能家居照明系统
BH1750 传感器简介
BH1750 是一款数字型光照传感器,来自罗姆半导体(ROHM)。相比模拟光敏电阻,它有这些优势:
| 特性 | BH1750 | 光敏电阻 |
|---|---|---|
| 输出类型 | 数字 I2C | 模拟电压 |
| 测量范围 | 1-65535 Lux | 依赖电路设计 |
| 精度 | ±20% | 较差 |
| 波长响应 | 接近人眼 | 不一致 |
| 校准 | 出厂校准 | 需要手动校准 |
| 价格 | 约 3-5 元 | 约 0.5-1 元 |
BH1750 的测量范围是 1-65535 Lux,覆盖了从黑暗室内(约 100 Lux)到晴朗户外(约 50000 Lux)的场景。
关键参数:
- 工作电压:3.0-3.6V(部分模块带稳压,可接 5V)
- 接口:I2C(默认地址 0x23,可切换到 0x5C)
- 分辨率:16 位
- 响应时间:最快 120ms
硬件清单
| 组件 | 型号 | 数量 | 单价 | 总价 |
|---|---|---|---|---|
| 光照传感器 | BH1750FVI 模块 | 1 | ¥4.5 | ¥4.5 |
| 主控板 | Arduino Nano | 1 | ¥12 | ¥12 |
| LED 灯珠 | 5mm 白光 LED | 3 | ¥0.3 | ¥0.9 |
| 限流电阻 | 220Ω | 3 | ¥0.1 | ¥0.3 |
| 面包板 | 400 孔 | 1 | ¥5 | ¥5 |
| 杜邦线 | 公对公 20cm | 10 | ¥0.2 | ¥2 |
| 合计 | ¥24.7 |
购买建议:淘宝搜索"BH1750 模块",选择带稳压和上拉电阻的版本,接线更简单。
电路连接
BH1750 使用 I2C 接口,只需要 4 根线:
| BH1750 | Arduino Nano |
|---|---|
| VCC | 5V(模块带稳压) |
| GND | GND |
| SCL | A5(I2C 时钟) |
| SDA | A4(I2C 数据) |
LED 连接(以 3 颗 LED 为例):
- LED 正极 → 数字引脚 D9(PWM)→ 220Ω电阻 → LED 负极 → GND
接线图说明:
BH1750 Arduino Nano
┌────────┐ ┌────────────┐
│ VCC │──────│ 5V │
│ GND │──────│ GND │
│ SCL │──────│ A5 │
│ SDA │──────│ A4 │
└────────┘ └────────────┘
LED (通过 220Ω电阻)
┌────────┐ ┌────────────┐
│ + │──────│ D9 (PWM) │
│ - │──────│ GND │
└────────┘ └────────────┘
代码实现
1. 安装依赖库
使用 Adafruit_BH1750 库,它比原始 I2C 读写更方便:
# Arduino IDE 中搜索 "BH1750" 安装
# 或手动下载:https://github.com/claws/BH1750
2. 完整代码
#include
#include
// 定义引脚
#define LED_PIN 9
// 创建传感器对象
BH1750 lightSensor;
// 调光参数
const int MIN_LUX = 50; // 最低光照阈值(暗环境)
const int MAX_LUX = 1000; // 最高光照阈值(亮环境)
const int PWM_MIN = 30; // 最低亮度(避免完全熄灭)
const int PWM_MAX = 255; // 最高亮度
// 平滑滤波
float smoothedLux = 0;
const float ALPHA = 0.3; // 滤波系数(0.1-0.5,越小越平滑)
void setup() {
Serial.begin(9600);
// 初始化 LED
pinMode(LED_PIN, OUTPUT);
// 初始化传感器
if (lightSensor.begin(BH1750::CONTINUOUS_HIGH_RES_MODE, 0x23)) {
Serial.println(F("BH1750 初始化成功"));
} else {
Serial.println(F("BH1750 初始化失败,检查接线!"));
while (1);
}
// 设置测量时间(68ms 默认,可调整)
lightSensor.setMeasurementTime(BH1750::MT_69MS);
}
void loop() {
// 读取光照值
if (lightSensor.hasValue()) {
float lux = lightSensor.readLightLevel();
// 指数移动平均滤波
smoothedLux = ALPHA * lux + (1 - ALPHA) * smoothedLux;
// 计算 PWM 值(映射到亮度范围)
int pwmValue = calculatePWM(smoothedLux);
// 输出到 LED
analogWrite(LED_PIN, pwmValue);
// 串口调试输出
Serial.print("光照:");
Serial.print(smoothedLux);
Serial.print(" Lux | PWM: ");
Serial.println(pwmValue);
}
delay(200); // 200ms 采样间隔
}
// 根据光照计算 PWM 值
int calculatePWM(float lux) {
// 限制在有效范围内
lux = constrain(lux, MIN_LUX, MAX_LUX);
// 线性映射:光照越强,LED 越亮
// 注意:人眼对亮度感知是对数的,这里用线性简化
int pwm = map(lux, MIN_LUX, MAX_LUX, PWM_MIN, PWM_MAX);
return constrain(pwm, PWM_MIN, PWM_MAX);
}
3. 代码解析
关键部分说明:
-
测量模式选择
CONTINUOUS_HIGH_RES_MODE:连续高分辨率模式(1 Lux 精度)CONTINUOUS_HIGH_RES_MODE_2:更高分辨率(0.5 Lux)CONTINUOUS_LOW_RES_MODE:低分辨率但更快(4ms)
-
平滑滤波
使用指数移动平均(EMA)滤波,避免亮度跳变:smoothedLux = ALPHA * lux + (1 - ALPHA) * smoothedLux;ALPHA越小,变化越平滑,但响应越慢。 -
亮度映射
使用map()函数将光照值映射到 PWM 范围。注意人眼对亮度的感知是对数的,如果需要更自然的调光效果,可以用对数映射:// 对数映射(更自然) float logLux = log10(lux); float logMin = log10(MIN_LUX); float logMax = log10(MAX_LUX); int pwm = map(logLux * 100, logMin * 100, logMax * 100, PWM_MIN, PWM_MAX);
常见问题排查
问题 1:传感器返回 0 或固定值
可能原因:
- 接线错误(SCL/SDA 接反)
- I2C 地址不对(尝试 0x5C)
- 上拉电阻缺失(模块应自带)
解决方法:
// I2C 扫描代码,查找传感器地址
#include
void setup() {
Serial.begin(9600);
Wire.begin();
Serial.println("I2C 扫描中...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.print("找到设备,地址:0x");
Serial.println(addr, HEX);
}
}
}
void loop() {}
问题 2:亮度变化不平滑
原因: 采样太快或滤波系数太大
解决:
- 增加
delay()时间到 500ms - 减小
ALPHA系数到 0.1-0.2 - 增加采样次数取平均:
float readAverageLux() { float sum = 0; for (int i = 0; i < 5; i++) { sum += lightSensor.readLightLevel(); delay(50); } return sum / 5; }
问题 3:LED 闪烁或抖动
原因: PWM 频率或电源问题
解决:
- 检查电源是否稳定(加 100μF 电容)
- 确保 LED 限流电阻正确(220Ω 适合 5V)
- 避免共用电源导致压降
问题 4:测量值与环境不符
校准方法:
// 使用已知光源校准
// 手机 Lux 计 App 作为参考
float calibrationFactor = 1.0; // 根据实测调整
float calibratedLux = lightSensor.readLightLevel() * calibrationFactor;
进阶扩展
1. 多通道调光
控制 RGB 三色灯,根据时间调整色温:
// 早晨:冷白光(高色温)
// 晚上:暖黄光(低色温)
void setWarmLight(int brightness) {
analogWrite(LED_RED, brightness);
analogWrite(LED_GREEN, brightness * 0.8);
analogWrite(LED_BLUE, brightness * 0.5);
}
2. 加入手动覆盖
添加按钮,允许用户临时覆盖自动调光:
#define BUTTON_PIN 2
bool manualMode = false;
void checkButton() {
if (digitalRead(BUTTON_PIN) == LOW) {
manualMode = !manualMode;
delay(200); // 防抖
}
}
3. 连接智能家居
通过 ESP8266/ESP32 将光照数据上传到 Home Assistant:
// ESP32 + BH1750 + MQTT
#include
#include
void publishLux(float lux) {
char payload[10];
dtostrf(lux, 4, 1, payload);
mqttClient.publish("home/sensor/lux", payload);
}
实际应用场景
场景 1:智能台灯
- 白天自动调亮,晚上自动调暗
- 深夜保持最低亮度(30%),避免完全熄灭
场景 2:显示器背光
- 配合电脑使用,根据房间亮度调整
- 减少眼睛疲劳,节能 30-50%
场景 3:植物生长灯
- 根据自然光补充光照
- 保持植物接收恒定光照强度
场景 4:汽车仪表照明
- 隧道进出自动调整
- 避免突然变亮/变暗影响驾驶
成本与节能分析
成本: 硬件总成本约 25 元,量产可降至 15 元以内。
节能效果: 相比固定亮度照明,自动调光可节能 30-60%。以 10W LED 灯为例:
- 固定亮度:10W × 8 小时 = 80Wh/天
- 自动调光:平均 5W × 8 小时 = 40Wh/天
- 年节省:(80-40) × 365 = 14.6 kWh
按电费 0.6 元/kWh 计算,单灯年节省约 8.8 元。大规模部署(如办公室 100 盏灯)年节省近 900 元。
总结
BH1750 自动调光系统是一个实用且低成本的项目。它解决了固定亮度照明的痛点,既能提升舒适度,又能节能。
核心要点回顾:
- BH1750 是数字 I2C 传感器,精度高、使用简单
- 使用平滑滤波避免亮度跳变
- 根据实际场景调整 MIN_LUX/MAX_LUX 阈值
- 可扩展为 RGB 色温调节或智能家居节点
动手试试吧,让你的照明设备真正"看懂"环境!
希望这篇博客文章对您有所帮助!