需求洞察

你有没有过这种体验:深夜加班盯着屏幕,桌面的灯光突然变得刺眼或太暗?或者开会时桌灯亮着,主持人一直以为你没在认真听?又或者离开工位去倒水,回来发现 PC 已经进入休眠,桌面一团黑?

桌面灯光其实是空间情绪的延伸。但市面上的智能灯具普遍不便宜:Yeelight 灵动灯带 ¥169,小米智能台灯 ¥199,Nanoleaf 奇光板更离谱到 ¥700+。对于一个"就想让桌面灯光随状态变一变"的极客来说,性价比不够。

本项目的目标就是用 ¥88 的 BOM 物料,做出一个能根据环境光、时间、PC 状态自动切换灯效的氛围灯——而且支持二次开发(颜色、模式、触发逻辑都可在 Arduino 代码里改)。

核心痛点

方案设计

本项目采用「感知 + 主控 + 显示 + 反馈」四件套架构,以 ESP32 为中心,外围 3 个模块各司其职:

📐 系统架构
模块型号功能
主控ESP32 DevKit V1WiFi/BT 双模,240MHz,34 个 GPIO
灯环WS2812B 12 颗环形单线 1670 万色,单 GPIO 13 控制全部
光感BH1750FVII2C 接口,0-65535 lux 精度,16bit
显示SSD1306 OLED 0.96"128x64,I2C 接口,实时显示模式/亮度/光照

4 种模式

🎨 模式定义与触发逻辑
模式灯效触发条件
专注 FOCUS冷白渐变(HSV 色相 160°,饱和 50%,亮度 200)亮度 > 50 lux 且时间 6:00-22:00
休息 REST暖橙渐变(HSV 色相 30°,饱和 200%,亮度 100)亮度 < 50 lux 或 22:00-06:00
会议 MEETING全关(FastLED.clear)串口收到字符 'm' 或 按钮按下
充电 CHARGING绿色呼吸(HSV 色相 120°,beatsin8 呼吸 15 次/分钟)USB 电流 < 100mA(PC 休眠)

模式优先级:会议 > 充电 > 休息 > 专注。即用户主动触发会议后会一直保持,直到串口收到 'r'(reset)才退出。

技术亮点

物料清单

总成本 ¥88,比 Yeelight 灵动灯带便宜 47%。所有物料在淘宝/嘉立创都能买到。

🛒 物料清单(2026 年 6 月淘宝均价)
数量物料单价小计
1ESP32 DevKit V1(Type-C 口)¥30¥30
1WS2812B 12 颗环形灯(外径 50mm)¥15¥15
1BH1750FVI 光照传感器模块¥6¥6
1SSD1306 OLED 0.96 寸显示屏(I2C 接口)¥12¥12
1 包杜邦线(公-公 40 根 + 公-母 40 根)¥5¥5
1面包板 400 点(SYB-170)¥5¥5
15V 3A 电源适配器(DC 5.5x2.1 母头)¥15¥15
总计¥88

采购建议:ESP32 建议买带 Type-C 口的 V1 改进版(丝印清楚);WS2812 选 12 颗环形(外径 50mm),不要选 16 颗或 24 颗(电流会超过 3A);OLED 选 0.96 寸 4 针脚版本(VCC/GND/SCL/SDA),不要买 7 针 SPI 版本(接线复杂)。

接线与结构

按 ESP32 DevKit V1 的丝印实际位置说明(不是抽象 GPIO 编号):

ESP32 排针位置参考

接线清单(带颜色 + 丝印)

🔌 接线表(务必确认共地)
模块引脚颜色连接目标丝印
WS2812 环形DIN(数据输入)白线ESP32 GPIO 13左侧排针第 3 脚,标 D13
WS2812 环形5V(电源)红线外部 5V 电源正极DC 5.5x2.1 母头内芯
WS2812 环形GND(地)黑线ESP32 GND + 外部 5V 电源负极ESP32 左侧排针第 2 脚;务必共地!
BH1750VCC红线ESP32 3.3V右侧排针第 1 脚,标 3V3
BH1750GND黑线ESP32 GND右侧排针第 2 脚,标 GND
BH1750SDA黄线ESP32 GPIO 21右侧排针第 11 脚,标 D21
BH1750SCL白线ESP32 GPIO 22右侧排针第 14 脚,标 D22
OLED 0.96"VCC红线ESP32 3.3V右侧排针第 1 脚,标 3V3
OLED 0.96"GND黑线ESP32 GND右侧排针第 2 脚
OLED 0.96"SDA黄线ESP32 GPIO 21与 BH1750 共用 I2C 总线
OLED 0.96"SCL白线ESP32 GPIO 22与 BH1750 共用 I2C 总线

⚠️ 关键提醒

核心代码

使用 Arduino 框架,需要安装 3 个库:FastLED、BH1750、Adafruit_SSD1306。

#include <FastLED.h>
#include <Wire.h>
#include <BH1750.h>
#include <Adafruit_SSD1306.h>

#define LED_PIN    13
#define NUM_LEDS   12
#define BRIGHTNESS 80
#define OLED_ADDR  0x3C

CRGB leds[NUM_LEDS];
BH1750 lightMeter(0x23);
Adafruit_SSD1306 display(128, 64, &Wire, -1);

enum Mode { FOCUS, REST, MEETING, CHARGING };
Mode currentMode = FOCUS;

void setup() {
  Serial.begin(115200);

  // WS2812 初始化
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();

  // BH1750 初始化
  lightMeter.begin();

  // OLED 初始化
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.display();
  delay(1500);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);

  // 开机自检:彩虹渐变
  for (int hue = 0; hue < 255; hue += 8) {
    fill_rainbow(leds, NUM_LEDS, hue, 8);
    FastLED.show();
    delay(20);
  }
  FastLED.clear();
}

void loop() {
  float lux = lightMeter.readLightLevel();
  int hour = hour();  // NTP 同步后可用

  // 模式自动切换
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    if (cmd == 'm') currentMode = MEETING;
    else if (cmd == 'r') currentMode = FOCUS;
  }
  if (currentMode != MEETING) {
    if (lux < 50) currentMode = REST;
    else if (hour >= 22 || hour < 6) currentMode = REST;
    else currentMode = FOCUS;
  }

  applyMode();
  displayStatus(lux);
  FastLED.show();
  delay(1000);
}

void applyMode() {
  switch (currentMode) {
    case FOCUS:
      // 冷白渐变
      for (int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CHSV(160, 50, 200);
      }
      break;
    case REST:
      // 暖橙渐变
      for (int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CHSV(30, 200, 100);
      }
      break;
    case MEETING:
      // 静默全关
      FastLED.clear();
      break;
    case CHARGING:
      // 绿色呼吸
      uint8_t bri = beatsin8(15, 30, 200);
      for (int i = 0; i < NUM_LEDS; i++) {
        leds[i] = CHSV(120, 255, bri);
      }
      break;
  }
}

void displayStatus(float lux) {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.print(F("Mode: "));
  switch (currentMode) {
    case FOCUS:    display.println(F("FOCUS")); break;
    case REST:     display.println(F("REST")); break;
    case MEETING:  display.println(F("MEETING")); break;
    case CHARGING: display.println(F("CHARGING")); break;
  }
  display.setCursor(0, 20);
  display.print(F("Lux: "));
  display.println(lux, 0);
  display.setCursor(0, 40);
  display.print(F("Bri: "));
  display.println(BRIGHTNESS);
  display.display();
}

代码说明

调试步骤

按以下顺序逐步调试,每步验证通过再进下一步(避免一次接 5 根线发现全错)。

第 1 步:WS2812 自检(5 分钟)

  1. 接线:仅接 WS2812 的 5V、GND、DIN 三根线(电源用外部 5V,ESP32 仅接 DIN)
  2. 烧录下面这段最小代码(FastLED 库自带的 Blink 示例)
  3. 看到 12 颗灯全亮红色 → 接线正确
  4. 看不到 → 检查:电源正负极是否接反、DIN 是否接对引脚、GND 是否共地

第 2 步:BH1750 自检(3 分钟)

  1. 接线:BH1750 的 4 根线(VCC/GND/SDA/SCL)全接
  2. 烧录下面这段代码(I2C Scanner)
  3. 串口监视器看到 I2C device found at address 0x23 → 接线正确
  4. 看不到 → 检查 I2C 地址(可能是 0x5C)、SDA/SCL 是否接反

第 3 步:OLED 自检(3 分钟)

  1. 接线:OLED 的 4 根线全接(与 BH1750 共用 I2C 总线)
  2. 烧录 Adafruit_SSD1306 库自带的 ssd1306_128x64_i2c 示例
  3. 看到 OLED 显示一个跳动的方块 → 接线正确
  4. 看不到 → 检查 I2C 地址(用 Scanner 扫,0x3C 或 0x3D)

第 4 步:整合测试(10 分钟)

  1. 烧录上面的核心代码
  2. 打开串口监视器(波特率 115200)
  3. 应该看到 OLED 显示 Mode: FOCUSLux: xxxBri: 80
  4. 用手机手电筒照 BH1750,Lux 数字应明显上升
  5. 用手遮住 BH1750,Lux 数字应降到 0-10,且模式应自动切到 REST
  6. 串口发送 m,灯应全关,OLED 显示 Mode: MEETING
  7. 串口发送 r,灯恢复,OLED 显示 Mode: FOCUS

常见故障

设计图

架构图(ESP32 中心 + 4 个外围)

🔧 系统架构图
       ┌──────────────────────┐
       │   ESP32 DevKit V1    │
       │   (主控 + WiFi)      │
       └──────────┬───────────┘
                  │
      ┌───────────┼───────────┐
      │           │           │
   GPIO 13     I2C(21/22)   5V+3.3V
      │           │           │
      ▼           ▼           ▼
  ┌────────┐  ┌──────────┐  ┌────────┐
  │WS2812B │  │ BH1750 + │  │ 外部5V │
  │ 12颗环 │  │ OLED     │  │ 3A电源 │
  │ (显示) │  │(感知+UI) │  │(供电)  │
  └────────┘  └──────────┘  └────────┘

模式切换状态机

🔄 模式切换状态机
当前状态触发条件切换到
任何状态串口收到 'm'MEETING
任何状态串口收到 'r'FOCUS
FOCUS / RESTLux < 50REST
FOCUS / REST时间 22:00-06:00REST
RESTLux > 50 且 6:00-22:00FOCUS

灯效色相对照表

🎨 4 种模式灯效对照
模式色相饱和度亮度色温(近似)适合场景
FOCUS 专注160°(青蓝)50%200/2556000K+白天工作、写代码、阅读
REST 休息30°(暖橙)200%100/2553000K夜间、放松、睡前
MEETING 会议0视频会议、不想被打扰
CHARGING 充电120°(绿)255%30-200 呼吸PC 休眠、离开工位

扩展玩法

金句

"光线是空间的表情,而 ESP32 让你成为光线的造型师。"

本项目总成本 ¥88,比 Yeelight 灵动灯带便宜 47%,4 种模式自动切换无需手动操作,BH1750 + WS2812 + OLED 三大件都是常用模块,二次开发空间大。动手做——这是属于极客的桌面美学。