💡 划重点

  • ESP32‑C3 为核心,通过 SCD41 实时检测书桌周围 CO₂ 浓度
  • 根据浓度驱动 WS2812 灯环变色,提醒通风或专注状态
  • 支持蓝牙数据上报与无感记录,所有物料严格控制在 120 元以内
  • 全文提供可编译的 Arduino / PlatformIO 骨架代码与接线细节

需求洞察

目标用户:长期在家或在宿舍书桌前学习、编程、阅读的学生及知识工作者。

真实痛点:密闭房间内,人呼吸产生的 CO₂ 浓度会快速上升至 1500–2500 ppm,导致头晕、注意力下降、犯困,而用户通常察觉不到。现有空气检测仪缺乏即时、直观的桌面提醒,也难以与学习专注状态联动。

为什么值得做:将 CO₂ 监测与桌面台灯融合,不增加额外设备;用灯环颜色无声告警,既能保护专注力,又能提醒开窗通风,成本低廉、功能聚焦,真实改善学习环境。

方案设计

感知层

控制层

执行层

- CO₂ < 600 ppm:柔和绿光呼吸,表示空气优良,可专注。

- 600–1000 ppm:黄光呼吸,提醒即将超标。

- > 1000 ppm:红光快闪,强烈提醒开窗通风。

日志 / 联网层

物料清单

名称 数量 用途 估算单价(元) 小计(元)
ESP32‑C3 开发板 (合宙或同等) 1 主控与蓝牙通信 18 18
SCD41 传感器模块 1 CO₂、温湿度检测 78 78
WS2812 灯环 (12灯) 1 空气质量视觉反馈 9 9
轻触按键 (6×6mm) 1 模式切换 0.5 0.5
10kΩ 电阻、电容、排针 若干 按键上拉、去耦、连接 2 2
亚克力外壳 + 3D 打印底座 1 结构支撑与光线柔化 10 10
杜邦线与焊锡 少量 接线 2 2
合计 119.5

接线与结构

VCC → 3.3V,GND → GND,SDA → GPIO 8,SCL → GPIO 9

模块自身含上拉电阻,I2C 总线无需外接。

VCC → 5V(可由 USB 供电),GND → GND,DIN → GPIO 10

使用 5 V 供电时需确保所有 GND 共地。

一端接 GND,另一端接 GPIO 3,同时通过 10kΩ 电阻上拉至 3.3V,实现低电平触发。

全部使用指定引脚,未做改动,因为这些 IO 可直接映射为硬件 I2C 与普通输出,且无复用冲突。

核心代码

以下为 PlatformIO / Arduino 骨架,使用 SparkFun_SCD4x_Arduino_LibraryAdafruit_NeoPixel,BLE 基于 ESP32 NimBLE 库。


#include <Wire.h>
#include <SparkFun_SCD4x_Arduino_Library.h>
#include <Adafruit_NeoPixel.h>
#include <NimBLEDevice.h>

#define LED_PIN     10
#define NUM_LEDS    12
#define BUTTON_PIN  3

SCD4x scd4x;
Adafruit_NeoPixel ring(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
NimBLEServer *pServer;
NimBLECharacteristic *pChar;

byte currentMode = 0; // 0: normal, 1: focus
uint16_t co2 = 0;

void updateLEDs() {
  uint32_t color = ring.Color(0, 255, 0); // green
  uint8_t brightness = 50;
  if (co2 > 1000) {
    color = ring.Color(255, 0, 0); // red
  } else if (co2 > 600) {
    color = ring.Color(255, 255, 0); // yellow
  }
  if (currentMode == 1) {
    color = ring.Color(255, 140, 0); // warm white focus
    brightness = 20;
  }
  for (int i = 0; i < NUM_LEDS; i++) {
    ring.setPixelColor(i, color);
  }
  ring.show();
}

void setup() {
  Serial.begin(115200);
  Wire.begin(8, 9); // SDA=8, SCL=9

  if (scd4x.begin(false, true, false)) { // auto calibrate off
    Serial.println("SCD41 ready");
  }

  ring.begin();
  ring.clear();
  ring.setBrightness(50);
  ring.show();

  pServer = NimBLEDevice::createServer();
  NimBLEService *pService = pServer->createService("181A"); // Environmental Sensing
  pChar = pService->createCharacteristic("2A6E", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
  pChar->setValue((uint16_t)0);
  pService->start();
  NimBLEAdvertising *pAdv = NimBLEDevice::getAdvertising();
  pAdv->addServiceUUID(pService->getUUID());
  pAdv->start();
}

void loop() {
  static uint32_t lastRead = 0;
  if (millis() - lastRead > 5000) {
    if (scd4x.readMeasurement()) {
      co2 = scd4x.getCO2();
      Serial.printf("CO2: %d ppm\n", co2);
      pChar->setValue(co2);
      pChar->notify();
      updateLEDs();
    }
    lastRead = millis();
  }

  // 按键短按切换模式
  static uint32_t btnPressTime = 0;
  if (digitalRead(BUTTON_PIN) == LOW) {
    if (btnPressTime == 0) btnPressTime = millis();
  } else {
    if (btnPressTime != 0) {
      uint32_t duration = millis() - btnPressTime;
      if (duration > 3000) { // 长按关闭灯环
        ring.clear();
        ring.show();
      } else if (duration > 50) {
        currentMode = !currentMode;
        updateLEDs();
      }
      btnPressTime = 0;
    }
  }
  delay(10);
}

调试步骤

  1. 硬件检查:按接线表连接,万用表确认 3.3 V、5 V 与 GND 无短路,USB 供电后 ESP32‑C3 电源灯亮起。
  2. 传感器测试:烧录前期仅含串口输出的代码,观察串口监视器是否每 5 秒输出 CO₂、温度、湿度数值,确认 I2C 通信正常。
  3. 灯环调试:单独驱动灯环显示彩虹色,验证数据线连接,调整电源电流。
  4. 按键响应:短按切换模式,长按 3 秒关灯,通过串口打印当前模式确认按键消抖与逻辑。
  5. 蓝牙验证:手机安装 nRF Connect,扫描设备 CO2_LAMP,连接后读取 Environmental Sensing 服务下的 CO₂ 特征值,检查数值更新。
  6. 整机联调:在不同浓度环境下观察灯环颜色变化,确认阈值合理,必要时修改代码中的 ppm 界限。

设计图

CO₂ 学习专注监测台灯结构flow
1
SCD41 采集 CO₂/T/RH
2
ESP32‑C3 I2C 读取
3
按键切换模式
4
灯环颜色反馈
5
BLE 数据上报手机