💡 划重点

  • 主控采用 ESP32-C3,成本低、体积小,自带低功耗蓝牙。
  • SCD41 监测书桌 CO₂ 与温湿度,WS2812 灯环用颜色反馈专注状态。
  • 按键用于静音与模式切换,手机通过低功耗蓝牙查看趋势。
  • 整机物料控制在 118 元内,适合学生与居家办公桌面改造。
  • 接线沿用给定引脚:SCD41 的 I2C 使用 8/9,灯环数据使用 10,按键使用 3。

需求洞察

目标用户是居家自习的中学生、大学生、备考人群和远程办公者。真实痛点是:书桌门窗关闭后 CO₂ 逐渐升高,人却很难立刻察觉,常见表现是犯困、烦躁、注意力涣散;番茄钟只能提醒休息,不能反映空气环境;商用 CO₂ 监测仪价格偏高,也缺少与台灯联动的即时反馈。这个项目值得做,是因为台灯本来就是书桌必备品,把传感、灯光和蓝牙记录叠加进去,不额外占用桌面空间;用颜色变化提示通风或休息,比数字仪表更不打断学习;成本低,便于复制和迭代。

方案设计

感知层由 SCD41 与按键组成。SCD41 每 5 秒读取 CO₂、温度和湿度,I2C 地址通常为 0x62;按键用于短按静音、长按进入校准流程。控制层由 ESP32-C3 完成滤波、阈值分级、按键状态机和蓝牙数据封装。执行层是 12 位 WS2812 灯环:CO₂ 低于 800ppm 显示绿色,800 至 1200ppm 显示黄色,高于 1200ppm 显示红色;夜间降低亮度,静音时熄灭灯环。日志与联网层通过低功耗蓝牙自定义服务上报“CO₂,温度,湿度”,串口同步输出日志;不依赖路由器,隐私风险低。供电采用 USB-C 5V,灯环亮度限制在较低水平,避免电流过大。

物料清单

物料 数量 用途 估算单价 合计
ESP32-C3 开发板 1 主控与蓝牙 25 元 25 元
SCD41 模块 1 CO₂、温湿度采集 65 元 65 元
WS2812 12 位灯环 1 状态灯光反馈 8 元 8 元
轻触按键 2 静音、校准 0.5 元 1 元
电阻电容 若干 限流、滤波 2 元 2 元
洞洞板与杜邦线 1 套 焊接与连接 8 元 8 元
USB-C 电源线 1 供电 5 元 5 元
外壳与遮光片 1 套 固定与柔光 4 元 4 元
合计 118 元

接线与结构

SCD41 的 VCC 接 3V3,GND 接 GND,SDA 接 GPIO8,SCL 接 GPIO9;WS2812 的 VCC 接 5V,GND 接 GND,DIN 接 GPIO10,数据线串联约 330Ω 电阻,电源端并联 1000µF 电容;按键一端接 GPIO3,另一端接 GND,使用内部上拉。结构上,灯环固定在灯臂顶部并加遮光片,SCD41 放在底座靠近桌面处,外壳开透气孔并远离发热元件;按键布置在底座侧面。按给定接线即可工作,无需改脚;若灯环数据不稳定,可加电平转换,但会增加成本,因此优先缩短数据线并做好共地。

核心代码


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

#define SDA_PIN 8
#define SCL_PIN 9
#define LED_PIN 10
#define KEY_PIN 3
#define LED_COUNT 12

SensirionI2CScd4x scd4x;
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
BLEServer* pServer = nullptr;
BLECharacteristic* pChar = nullptr;
uint16_t co2 = 0;
float temp = 0, hum = 0;
unsigned long lastRead = 0, lastKeyTime = 0;
bool muted = false;
int lastKey = HIGH;

void setRing(uint8_t r, uint8_t g, uint8_t b) {
  for (int i = 0; i < LED_COUNT; i++) strip.setPixelColor(i, strip.Color(r, g, b));
  strip.show();
}

void updateColor(uint16_t v) {
  if (muted) { setRing(0, 0, 0); return; }
  if (v < 800) setRing(0, 80, 20);
  else if (v < 1200) setRing(120, 70, 0);
  else setRing(160, 0, 0);
}

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN, SCL_PIN);
  strip.begin();
  strip.setBrightness(60);
  strip.show();
  scd4x.begin(Wire);

  pServer = BLEDevice::createServer();
  BLEService* pService = pServer->createService("12345678-1234-1234-1234-1234567890ab");
  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() {
  int key = digitalRead(KEY_PIN);
  if (key == LOW && lastKey == HIGH && millis() - lastKeyTime > 200) {
    muted = !muted;
    lastKeyTime = millis();
  }
  lastKey = key;

  if (millis() - lastRead > 5000) {
    lastRead = millis();
    uint16_t v; float t, h;
    if (scd4x.readMeasurement(v, t, h) == 0 && v > 0) {
      co2 = v; temp = t; hum = h;
    }
    updateColor(co2);
    String payload = String(co2) + "," + String(temp, 1) + "," + String(hum, 1);
    pChar->setValue(payload.c_str());
    if (pServer->getConnectedCount() > 0) pChar->notify();
    Serial.println(payload);
  }
}

调试步骤

先烧录程序并打开 115200 串口,检查 I2C 是否能发现 SCD41;预热后观察 CO₂ 数值是否稳定。再单独测试灯环颜色与亮度,确认 5V 供电和共地正常。随后测试按键短按静音、消抖是否可靠。手机用低功耗蓝牙调试工具连接设备,查看自定义服务与通知数据。用密闭房间验证阈值切换,并加入迟滞逻辑避免颜色频繁跳动。最后检查外壳开孔、传感器位置和整机发热,必要时在通风环境下执行 400ppm 基线校准。

设计图

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