Tasmota 中的 Sensirion SCD30 I²C 传感器库:从 Arduino 接线到固件集成的完整指南
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
本文围绕仓库 lib/lib_i2c/arduino-i2c-scd30/README.md 展开,完整讲解 Sensirion SCD30 双通道 NDIR CO₂ 传感器在 Arduino 与 Tasmota 平台下的 I²C 驱动用法。你将掌握:SCD30 的引脚定义与各主流开发板接线方法、库的安装与依赖处理、exampleUsage示例程序的运行流程、完整 API 方法语义,以及该库如何在 Tasmota 固件(tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino)中被封装为可直接使用的传感器驱动与Scd30系列控制命令。
SCD30 传感器与 I²C 驱动库概览
SCD30 是 Sensirion 推出的一款高精度 CO₂ 传感器模块,内置 NDIR(非色散红外)CO₂ 测量单元以及温度、湿度传感器,能够同时输出 CO₂ 浓度(ppm)、温度(℃)与相对湿度(%RH)三组数据。它支持 I²C 与 UART 两种接口,本库(Sensirion I2C SCD30,版本 1.1.1,见 library.properties)专门负责通过I²C总线与该传感器通信。
库在 SensirionI2cScd30.h 中定义了默认 I²C 地址:
#define SCD30_I2C_ADDR_61 0x61SCD30 的默认 I²C 地址为0x61(7 位地址)。该地址固定在芯片内部,无法像部分传感器那样通过引脚跳线修改,因此在接线与代码中始终使用SCD30_I2C_ADDR_61即可。
从源码结构看,本库由sensirion-driver-generator 1.6.1自动生成(模型版本 1.1.1),基于 Sensirion Core 提供的SensirionI2CTxFrame/SensirionI2CRxFrame帧封装与SensirionI2CCommunication通信原语实现,因此必须安装依赖库 Sensirion Core。
SCD30 传感器模块实物图
库的安装与依赖
方式一:通过 Arduino 库管理器安装
启动 Arduino IDE,依次打开:
Sketch(项目) ➔Include Library(加载库) ➔Manage Libraries...(库管理)
在Filter your search...(筛选)输入框中搜索Sensirion I2C SCD30,找到后点击install(安装)按钮即可。
方式二:通过 ZIP 包安装
如果在库管理器中找不到该库,可下载最新 release 的 .zip 文件,然后通过:
Sketch➔Include Library➔Add .ZIP Library...
将其添加进 Arduino IDE。
依赖项
安装完成后,不要忘记以同样方式安装依赖库:
- Sensirion Core:提供 I²C 帧构造、CRC 校验与总线通信的底层实现,是编译本库的必备前提。
这一点在 library.properties 中也有明确声明:depends=Sensirion Core、includes=SensirionI2cScd30.h。同时architectures=*表示该库不限制具体单片机架构,AVR(Uno、Nano、Micro、Mega)、SAM(MKR 系列)、ESP32 等均可使用。
连接传感器:引脚定义与接线表
SCD30 引脚总览
SCD30 共有 7 个引脚,与 Arduino 标准 I²C 总线的连接关系如下(接线下图所示):
SCD30 引脚定义图
| 引脚 | 线缆颜色 | 名称 | 描述 | 备注 |
|---|---|---|---|---|
| 1 | 红 | VDD | 供电电压 | 3.3V ~ 5.5V |
| 2 | 黑 | GND | 接地 | |
| 3 | 黄 | SCL | I²C 串行时钟输入 | |
| 4 | 绿 | SDA | I²C 串行数据输入/输出 | |
| 5 | RDY | 数据就绪指示 | 高电平表示数据可用——不要连接 | |
| 6 | PWM | PWM 输出 | 不要连接 | |
| 7 | 蓝 | SEL | 接口选择 | 拉低或悬空选择 I²C 模式 |
接线要点:
- 推荐供电电压为 3.3V(虽然 VDD 范围是 3.3V~5.5V,但为了与 I²C 电平兼容及降低自热误差,官方建议使用 3.3V)。
- SEL 引脚必须拉低或悬空才能启用 I²C 接口;若接高电平则进入 UART 模式。
- RDY 与 PWM 引脚在本库的 I²C 场景下不要连接。
常见开发板接线对照表
原文档为以下 5 款开发板提供了官方验证过的接线示意,本文完整保留并转换为仓库内图片路径:
Arduino Uno
| SCD30 | SCD30 引脚 | 线缆颜色 | 开发板引脚 |
|---|---|---|---|
| VDD | 1 | 红 | 3.3V |
| GND | 2 | 黑 | GND |
| SCL | 3 | 黄 | D19/SCL |
| SDA | 4 | 绿 | D18/SDA |
| RDY | 5 | ||
| PWM | 6 | ||
| SEL | 7 | 蓝 | GND |
Arduino Nano
| SCD30 | SCD30 引脚 | 线缆颜色 | 开发板引脚 |
|---|---|---|---|
| VDD | 1 | 红 | 3.3V |
| GND | 2 | 黑 | GND |
| SCL | 3 | 黄 | A5 |
| SDA | 4 | 绿 | A4 |
| RDY | 5 | ||
| PWM | 6 | ||
| SEL | 7 | 蓝 | GND |
Arduino Micro
| SCD30 | SCD30 引脚 | 线缆颜色 | 开发板引脚 |
|---|---|---|---|
| VDD | 1 | 红 | 3.3V |
| GND | 2 | 黑 | GND |
| SCL | 3 | 黄 | ~D3/SCL |
| SDA | 4 | 绿 | D2/SDA |
| RDY | 5 | ||
| PWM | 6 | ||
| SEL | 7 | 蓝 | GND |
Arduino Mega 2560
| SCD30 | SCD30 引脚 | 线缆颜色 | 开发板引脚 |
|---|---|---|---|
| VDD | 1 | 红 | 3.3V |
| GND | 2 | 黑 | GND |
| SCL | 3 | 黄 | D21/SCL |
| SDA | 4 | 绿 | D20/SDA |
| RDY | 5 | ||
| PWM | 6 | ||
| SEL | 7 | 蓝 | GND |
ESP32 DevKitC
| SCD30 | SCD30 引脚 | 线缆颜色 | 开发板引脚 |
|---|---|---|---|
| VDD | 1 | 红 | 3V3 |
| GND | 2 | 黑 | GND |
| SCL | 3 | 黄 | GPIO 22 |
| SDA | 4 | 绿 | GPIO 21 |
| RDY | 5 | ||
| PWM | 6 | ||
| SEL | 7 | 蓝 | GND |
其中 ESP32 DevKitC 的接线与 Tasmota 默认 I²C GPIO 配置一致(SCL=GPIO22、SDA=GPIO21),这是将 SCD30 直接接入 ESP32 运行 Tasmota 时最常用的硬件方案。
快速开始:运行 exampleUsage 示例
按照原文档的快速开始流程,共 5 步:
按照上文「库的安装与依赖」安装库及其依赖;
按照「连接传感器」章节完成硬件接线;
在 Arduino IDE 中打开示例工程:
File(文件) ➔Examples(示例) ➔Sensirion I2C SCD30➔exampleUsage点击
Upload(上传)按钮,或使用Sketch➔Upload编译烧录;烧录完成后,通过
Tools(工具)菜单打开Serial Monitor(串口监视器)或Serial Plotter(串口绘图器)观察测量值。注意:串口波特率必须设置为115200 baud。
示例程序完整解读
示例源码位于 examples/exampleUsage/exampleUsage.ino,其核心流程如下:
#include <SensirionI2cScd30.h> #include <Wire.h> // 保证 NO_ERROR 定义正确(避免与其它头文件冲突) #ifdef NO_ERROR #undef NO_ERROR #endif #define NO_ERROR 0 SensirionI2cScd30 sensor; static char errorMessage[64]; static int16_t error; void setup() { Serial.begin(115200); while (!Serial) { delay(100); } Wire.begin(); sensor.begin(Wire, SCD30_I2C_ADDR_61); // 绑定 I²C 总线与 0x61 地址 sensor.stopPeriodicMeasurement(); // 先停止周期测量 sensor.softReset(); // 软复位(内部阻塞 delay(2000)) delay(2000); int8_t serialNumber[32] = {0}; error = sensor.readSerialNumber(serialNumber, 32); // 读取序列号(v1.1.0 新增) if (error != NO_ERROR) { Serial.print("Error trying to execute readSerialNumber(): "); errorToString(error, errorMessage, sizeof errorMessage); Serial.println(errorMessage); return; } Serial.print("serialNumber: "); Serial.print((const char*)serialNumber); Serial.println(); uint8_t major = 0; uint8_t minor = 0; error = sensor.readFirmwareVersion(major, minor); // 读取固件版本 if (error != NO_ERROR) { Serial.print("Error trying to execute readFirmwareVersion(): "); errorToString(error, errorMessage, sizeof errorMessage); Serial.println(errorMessage); return; } Serial.print("major: "); Serial.print(major); Serial.print("\t"); Serial.print("minor: "); Serial.print(minor); Serial.println(); error = sensor.startPeriodicMeasurement(0); // 启动周期测量,环境气压 0 = 关闭气压补偿 if (error != NO_ERROR) { Serial.print("Error trying to execute startPeriodicMeasurement(): "); errorToString(error, errorMessage, sizeof errorMessage); Serial.println(errorMessage); return; } } void loop() { float co2Concentration = 0.0; float temperature = 0.0; float humidity = 0.0; delay(1500); error = sensor.blockingReadMeasurementData(co2Concentration, temperature, humidity); if (error != NO_ERROR) { Serial.print("Error trying to execute blockingReadMeasurementData(): "); errorToString(error, errorMessage, sizeof errorMessage); Serial.println(errorMessage); return; } Serial.print("co2Concentration: "); Serial.print(co2Concentration); Serial.print("\t"); Serial.print("temperature: "); Serial.print(temperature); Serial.print("\t"); Serial.print("humidity: "); Serial.print(humidity); Serial.println(); }程序的关键节点:
sensor.begin(Wire, SCD30_I2C_ADDR_61):绑定 ArduinoTwoWire对象与传感器地址,其实现仅保存总线与地址指针(见 SensirionI2cScd30.cpp 的begin())。setup()阶段完成「停止测量 → 软复位 → 读序列号 → 读固件版本 → 启动周期测量」的完整初始化链路,每一步都通过返回的int16_t error与NO_ERROR (0)比对来判错。loop()中调用blockingReadMeasurementData()阻塞等待数据就绪并一次性读出 CO₂、温度、湿度三个浮点值;串口输出格式为co2Concentration: xxx temperature: xx.x humidity: xx.x。
库 API 全景:方法与命令码
库的公开接口全部声明在 SensirionI2cScd30.h 中,并在 SensirionI2cScd30.cpp 中实现。下表汇总了所有方法及其对应的 SCD30 I²C 命令码(命令码定义见头文件中的SCD30CmdId枚举):
| 库方法 | 功能 | 命令码 |
|---|---|---|
begin(TwoWire&, uint8_t) | 绑定 I²C 总线与从机地址 | — |
startPeriodicMeasurement(ambientPressure) | 启动周期测量,可选环境气压补偿(mBar) | 0x0010 |
stopPeriodicMeasurement() | 停止周期测量 | 0x0104 |
setMeasurementInterval(interval) | 设置测量间隔(秒),默认 2s,掉电不丢失 | 0x4600 |
getMeasurementInterval(interval) | 读取当前测量间隔 | 0x4600 |
getDataReady(dataReadyFlag) | 查询数据就绪标志(1=可读) | 0x0202 |
readMeasurementData(co2, temp, hum) | 读取 CO₂/温度/湿度测量数据 | 0x0300 |
blockingReadMeasurementData(co2, temp, hum) | 轮询就绪标志后读取(阻塞) | 组合 |
awaitDataReady() | 阻塞轮询数据就绪标志 | 组合 |
activateAutoCalibration(doActivate) | 开启/关闭自动自校准 ASC | 0x5306 |
getAutoCalibrationStatus(isActive) | 读取 ASC 状态 | 0x5306 |
forceRecalibration(co2RefConcentration) | 强制校准 FRC(400~2000 ppm) | 0x5204 |
getForceRecalibrationStatus(co2RefConcentration) | 读取 FRC 参考浓度(默认 400 ppm) | 0x5204 |
setTemperatureOffset(temperatureOffset) | 设置温度偏移(单位 ℃×100) | 0x5403 |
getTemperatureOffset(temperatureOffset) | 读取温度偏移 | 0x5403 |
setAltitudeCompensation(altitude) | 设置海拔补偿(米),设定气压后失效 | 0x5102 |
getAltitudeCompensation(altitude) | 读取海拔补偿值 | 0x5102 |
readFirmwareVersion(major, minor) | 读取固件主/次版本号 | 0xD100 |
softReset() | 软复位(设备约 2 秒不可用) | 0xD304 |
readSerialNumber(serialNumber[], size) | 读取序列号(最多 32 字符,v1.1.0 新增) | 0xD033 |
关键方法语义详解(来自头文件文档)
startPeriodicMeasurement(ambientPressure):启动 CO₂/湿度/温度的连续测量。未被读取的测量数据会被覆盖;ambientPressure单位 mBar,传入 0 表示关闭环境气压补偿(默认气压 1013.25 mBar);设置气压会覆盖此前设置的海拔补偿;测量运行中若需更新气压,必须完整重发该命令。awaitDataReady():反复轮询get_data_ready()直到就绪标志为 1。最小测量间隔为 2s 时最多轮询 200 次,会长时间阻塞系统。在 SensirionI2cScd30.cpp 中体现为每 100ms 轮询一次getDataReady()的循环。getDataReady():查询传感器内部缓冲区是否有新测量;读走数据后标志自动归 0。注意:读序列应在写序列之后延迟 >3ms 再发送,实现中统一使用delay(10)。activateAutoCalibration(doActivate):ASC 首次激活后需要至少 7 天才能建立初始参数集,期间传感器需每天暴露于新鲜空气至少 1 小时且不得断电,否则校准流程中止并需重新开始;参数存入非易失存储,掉电后仍然生效。ASC 只在连续测量模式下工作,默认关闭。forceRecalibration(co2RefConcentration):用于存在参考 CO₂ 浓度时的漂移补偿。最佳做法是:先在 2s 间隔连续模式下稳定运行至少 2 分钟,再发送 FRC 与参考值;参考值范围400 ppm ≤ cref ≤ 2000 ppm;FRC 会永久更新校准曲线并掉电保持,最近一次参考值保存在易失内存中,重新上电后读回为 400 ppm;FRC 与 ASC 相互覆盖。setTemperatureOffset(temperatureOffset):SCD30 板载温湿度传感器会受到芯片自热与周边器件发热影响,通过写入在连续运行中实测得到的偏移值(单位 ℃×100,如 2.0℃ 写 200)进行补偿;值保存在非易失存储中。setAltitudeCompensation(altitude):NDIR 原理的 CO₂ 测量受海拔影响,此命令按米设置海拔补偿;一旦设置了环境气压则该设置失效;同样非易失保存。softReset():强制传感器回到上电状态(重启内部系统控制器),会重新加载全部校准数据;设备约 2 秒不可用(实现中delay(2000))。传感器在任何内部状态下都能接收该命令。
底层通信实现特征
从 SensirionI2cScd30.cpp 可以看到所有方法统一遵循「构造命令帧 → 发送 →delay(10)(softReset 为 2000ms)→ 接收应答帧」的模式,例如readMeasurementData()先以SCD30_READ_MEASUREMENT_DATA_CMD_ID (0x0300)发送 2 字节命令帧,再接收 18 字节数据帧,依次解析出 CO₂(float)、温度(float)、湿度(float)三个 IEEE-754 浮点值;命令与应答均通过 Sensirion Core 的SensirionI2CCommunication::sendFrame / receiveFrame完成,内部自带 CRC 校验。帧数据复用静态缓冲区communication_buffer[48],不涉及动态内存分配。
在 Tasmota 固件中的集成:xsns_42 驱动
该库在 Tasmota 中被封装为第 42 号传感器驱动 tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino,通过编译开关启用:
// tasmota/my_user_config.h // #define USE_SCD30 // [I2cDriver29] Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code)取消 tasmota/my_user_config.h 中USE_SCD30的注释并重新编译即可启用(需同时启用USE_I2C)。I²C 设备编号为 29,对应 I2CDEVICES.md 中的条目:29 | USE_SCD30 | xsns_42 | SCD30 | 0x61 | Yes | CO2 sensor。
驱动初始化与轮询机制
驱动在 Tasmota 启动 3 秒后通过FUNC_EVERY_SECOND触发Scd30Init()完成初始化:
PowerOnDelay(2000)等待传感器上电启动(上电到 I²C 可通信时间 < 2s);- 在全部 I²C 总线上以50 kHz 波特率(
SCD30_I2C_BUS_SPEED 50000)扫描SCD30_I2C_ADDR_61——Sensirion 官方建议 SCD30 在50 kHz 或更低速率下运行,且主机必须支持时钟拉伸(最大 150ms/天); - 依次执行
stopPeriodicMeasurement()→softReset()→readFirmwareVersion()→readSerialNumber()→getMeasurementInterval()→startPeriodicMeasurement(0),全部成功后才登记设备并分配SCD30DATA结构体。
运行期Scd30Update()按测量间隔(默认 2s)轮询getDataReady(),数据就绪后调用readMeasurementData()读取并缓存;若连续错过SCD30_MAX_MISSED_READS (3)个周期则判定数据无效,自动执行停止测量 → 软复位 → 重新启动测量的恢复流程(ESP8266 平台还会先执行I2cClearBus清总线,规避下文已知问题)。
Scd30 控制命令
驱动通过FUNC_COMMAND注册了 6 条控制命令(前缀Scd30):
| 命令 | 参数范围 | 说明 |
|---|---|---|
Scd30Alt <米> | ≥0 | 设置/读取海拔补偿(如Scd30Alt 440) |
Scd30Auto <0/1> | 0~1 | 开启/关闭自动自校准 ASC,无参时读取状态 |
Scd30Cal <ppm> | 400~2000 | 强制校准 FRC(如Scd30Cal 420),无参时读取当前参考值 |
Scd30Int <秒> | 2~1800 | 设置/读取测量间隔(如Scd30Int 4) |
Scd30Pres <mBar> | 0 或 700~1400 | 设置/读取环境气压补偿(0 表示关闭) |
Scd30TOff <℃> | 0~20.00 | 设置/读取温度偏移(内部换算为 ℃×100) |
测量结果通过FUNC_JSON_APPEND输出为 JSON 中的"SCD30":{"CO2":…}字段并附带温湿度(ResponseAppendTHD),同时支持 Web 界面传感器页展示;启用USE_DOMOTICZ时还可推送空气质量与温湿度到 Domoticz(见Scd30Show())。若启用USE_LIGHT,CO₂ 读数还会驱动灯光信号指示(LightSetSignal(CO2_LOW, CO2_HIGH, co2))。
已知问题与注意事项
原文档记录了一个已知问题,Tasmota 驱动中也有对应处理:
softReset()在 Arduino MKR WIFI 1010 上的问题:调用softReset()后,后续命令不再被应答,I²C 线在收到第一个命令字节后保持低电平(该板使用软件 I²C)。解决方法:在该平台上移除示例中的softReset()调用及紧随其后的delay()。 与之呼应,Tasmota 的 xsns_42_scd30.ino 在 ESP8266 平台执行软复位后主动调用I2cClearBus(scd30_bus)清理总线状态,以规避同类软件 I²C 的挂死问题。
其他实操注意事项汇总:
- 串口监视器波特率务必为
115200; - SCD30 上电后约 2 秒内无法进行 I²C 通信,初始化前需要足够的启动延时(示例与 Tasmota 驱动均有体现);
- I²C 总线速率建议 ≤ 50 kHz 且主机需支持时钟拉伸;
- 库的代码风格统一使用
clang-format管理(见 README「Contributing」章节),提交.cpp/.h前可用clang-format -i src/*.cpp src/*.h自动格式化,否则 CI 构建会因格式差异失败。
许可证
本库采用 BSD 3-Clause 许可证,完整条款见 lib/lib_i2c/arduino-i2c-scd30/LICENSE;Tasmota 侧的 xsns_42_scd30.ino 驱动则遵循 GPL-3.0 许可。库的版本演进记录(从 0.1.0 初始发布到 1.1.1 增加readSerialNumber)可查阅 lib/lib_i2c/arduino-i2c-scd30/CHANGELOG.md。
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考