💡 划重点

  • 用 SCD41 实时感知书桌 CO₂、温度与湿度,把看不见的空气质量变成灯光语言。
  • ESP32-C3 做主控,蓝牙 BLE 把浓度推送到手机,无需 Wi-Fi 也能看数据。
  • WS2812 灯环随 CO₂ 变色:绿光专注、黄光提醒、红光呼吸,按键一键静音。
  • 整机成本约 105 元,3D 打印或亚克力灯罩即可落地,适合放在显示器旁或书架夹层。

需求洞察

目标用户是备考学生、居家办公者和长时间伏案写代码的人。真实痛点很具体:门窗紧闭的房间里,人越坐越困,却总以为是“自己不够自律”。实际上,室内 CO₂ 浓度超过 1000 ppm 后,认知表现和警觉度会明显下降;超过 1500 ppm 时,头痛、嗜睡、注意力涣散接踵而来。传统台灯只负责照明,不负责提醒你“该开窗了”。这个项目值得做,因为它把环境数据变成即时、无感的灯光反馈,不打断学习节奏,却能帮你守住专注力。

方案设计

感知层:SCD41 通过 I2C 每 5 秒读取 CO₂、温度和湿度,精度 ±(30 ppm + 3%),适合桌面场景。

控制层:ESP32-C3 运行阈值状态机,把 CO₂ 分为三段——低于 800 ppm 为绿光,800 至 1200 ppm 为黄光,高于 1200 ppm 为红光呼吸。按键用于切换“常亮/静音/夜灯”模式。

执行层:WS2812 灯环 12 颗灯珠,颜色和亮度同步变化;红光时以 2 秒周期呼吸,避免刺眼。

日志/联网层:蓝牙 BLE 广播 CO₂ 数值,手机端可用 nRF Connect 或自写 App 查看;串口同时输出日志,方便调试和长期记录。

物料清单

物料 数量 用途 估算单价 合计
ESP32-C3 开发板 1 主控与蓝牙 15 元 15 元
SCD41 模块 1 CO₂/温湿度感知 60 元 60 元
WS2812 灯环 12 位 1 灯光反馈 8 元 8 元
轻触按键 1 模式切换 0.5 元 0.5 元
灯罩/外壳 1 结构件 12 元 12 元
USB 数据线+电源 1 供电与调试 5 元 5 元
杜邦线/洞洞板/电阻 若干 连接与保护 5 元 5 元
合计 105.5 元

总成本约 105.5 元,低于 120 元目标。若已有开发板和线材,实际支出可压到 80 元以内。

接线与结构

SCD41 接 3.3 V 和 GND,SDA 接 GPIO8,SCL 接 GPIO9。WS2812 的 DIN 接 GPIO10,5 V 和 GND 分别接电源;数据线串 330 Ω 电阻,电源并 1000 µF 电容。按键一端接 GPIO3,另一端接 GND,启用内部上拉。结构上,灯环套在灯罩顶部,SCD41 从底座侧面开孔伸出,ESP32-C3 藏入底座,按键放在正面。若 WS2812 用 5 V 供电且高电平不足,可改为 3.3 V 供电或加电平转换;GPIO 保持不变,便于代码统一。

核心代码

以下为 Arduino 骨架,基于 ESP32-C3,可编译上传。


#include <Wire.h>
#include <Adafruit_NeoPixel.h>
#include <SensirionI2CScd4x.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>

SensirionI2CScd4x scd4x;
Adafruit_NeoPixel strip(12, 10, NEO_GRB + NEO_KHZ800);
const int buttonPin = 3;
int mode = 0;
unsigned long lastRead = 0;
BLECharacteristic *pChar;

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9);
  scd4x.begin(Wire);
  strip.begin();
  strip.show();

  BLEDevice::init("CO2_Lamp");
  BLEServer *pServer = BLEDevice::createServer();
  BLEService *pService = pServer->createService("0000180f-0000-1000-8000-00805f9b34fb");
  pChar = pService->createCharacteristic(
    BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
  );
  pChar->addDescriptor(new BLE2902());
  pService->start();
  BLEAdvertising *pAdv = BLEDevice::getAdvertising();
  pAdv->addServiceUUID(pService->getUUID());
  pAdv->start();
}

void loop() {
  if (digitalRead(buttonPin) == LOW) {
    delay(50);
    if (digitalRead(buttonPin) == LOW) {
      mode = (mode + 1) % 3;
      while (digitalRead(buttonPin) == LOW);
    }
  }
  if (millis() - lastRead > 5000) {
    lastRead = millis();
    uint16_t co2;
    float temperature, humidity;
    if (co2 == 0) return;
    String msg = "CO2=" + String(co2);
    pChar->setValue(msg.c_str());
    pChar->notify();
    updateLED(co2);
  }
}

void updateLED(uint16_t co2) {
  if (mode == 1) { strip.clear(); strip.show(); return; }
  uint32_t color;
  if (co2 < 800) color = strip.Color(0, 80, 0);
  else if (co2 < 1200) color = strip.Color(80, 80, 0);
  else color = strip.Color(80, 0, 0);
  for (int i = 0; i < strip.numPixels(); i++) strip.setPixelColor(i, color);
  strip.setBrightness(80);
  strip.show();
}

调试步骤

  1. 用 I2C 扫描确认 SCD41 地址为 0x62,若未识别检查 GPIO8/9 接线。
  2. 首次上电让 SCD41 预热 3 分钟,必要时执行强制校准。
  3. 单独测试 WS2812:点亮红、绿、蓝,确认数据方向正确。
  4. 测试按键消抖,观察模式切换是否稳定。
  5. 用塑料袋罩住传感器并呼气,验证 CO₂ 上升时灯环变红。
  6. 手机打开 BLE 调试助手,确认能收到 CO2 通知。
  7. 连续运行 2 小时,检查温漂和日志稳定性。

设计图

CO₂ 学习专注监测台灯结构flow
1
感知输入
→
2
控制处理
→
3
执行反馈
→
4
数据上报