Tasmota 仓库中的 Sensirion Core 库:SHDLC 与 I2C 传感器通信协议实现深度解析

📅 发布时间:2026/9/12 13:13:28
Tasmota 仓库中的 Sensirion Core 库:SHDLC 与 I2C 传感器通信协议实现深度解析
Tasmota 仓库中的 Sensirion Core 库SHDLC 与 I2C 传感器通信协议实现深度解析【免费下载链接】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本篇技术指南聚焦于 Tasmota 仓库lib/lib_i2c/arduino-core目录下 Sensirion 官方 Arduino Core 库的完整技术实现。该库为 SCD4x、SEN5x、SPS30、SGP41、STC4x 等 Sensirion 传感器驱动提供统一的 SHDLC串口与 I2C 协议层、帧构造/解析、CRC 校验与错误码体系是 Tasmota 中多个传感器驱动如xsns_92_scd40.ino、xsns_103_sen5x.ino的底层依赖。读完本文你将掌握 Sensirion 传感器两套通信协议的帧格式、核心类 API 的调用方式以及在 Tasmota 固件中这些驱动如何依托该核心库完成真实测量。一、库的定位为什么需要一份核心库Sensirion Corelibrary.properties版本 0.7.3在官方文档中的定位非常明确它提供 SHDLC 和 I2C 两套协议实现供上层传感器驱动库使用一般不需要被应用程序直接调用。上层驱动例如 arduino-i2c-scd4x、arduino-i2c-sen5x、arduino-i2c-sps30、arduino-i2c-sgp41、arduino-i2c-stcc4都以其为公共代码底座实现动态帧构造、校验和计算、缓冲区管理等 Sensirion 特有的通信细节。这一点在源码组织上体现得十分清楚SensirionCore.h 作为统一入口一次性引入 CRC、错误码、收发帧与两个通信类#include SensirionCrc.h #include SensirionErrors.h #include SensirionRxFrame.h #include SensirionShdlcCommunication.h #include SensirionShdlcRxFrame.h #include SensirionShdlcTxFrame.h #include SensirionI2CCommunication.h #include SensirionI2CRxFrame.h #include SensirionI2CTxFrame.h在 Tasmota 固件中上层传感器驱动的调用链路可以清晰印证这一分层xsns_92_scd40.ino只#include SensirionI2cScd4x.h而SensirionI2cScd4x类的readMeasurement()等实现内部正是通过SensirionI2cTxFrame/SensirionI2CCommunication完成与传感器芯片的字节级交互。二、SHDLC 协议实现面向质量流量控制器等串口设备的帧通信2.1 协议背景SHDLCSensirion High-Level Data Link Control是一种基于 ISO HDLC 的字节型主从通信协议用于控制 Sensirion 的部分设备典型如质量流量控制器。README 特别提示SHDLC 的详细协议文档目前并未公开如需要需联系 Sensirion 客户支持本库已经将帧的构造、发送与接收封装完毕用户无需自行实现协议细节。2.2 核心类SHDLC 通信链路由三个类协同完成定义见 SensirionShdlcTxFrame.h、SensirionShdlcRxFrame.h、SensirionShdlcCommunication.h类职责SensirionShdlcTxFrame构造发送帧写帧头、追加各类数据、写帧尾含校验和与结束标志SensirionShdlcRxFrame保存并解析接收帧将原始字节解码为整数、浮点、布尔、字节数组等类型SensirionShdlcCommunication静态工具类负责经Stream对象Serial、UART等发送/接收帧2.3 帧结构从源码看字节级实现阅读 SensirionShdlcTxFrame.cpp 可完整还原一帧 SHDLC 报文的组装过程帧头begin(command, address, dataLength)先写入帧起始标志字节0x7e随后依次写入address地址、command命令、dataLength数据长度三个字节数据段通过addUInt8/addUInt16/addUInt32/addInt8/addInt16/addInt32/addFloat/addBool/addBytes追加负载数据多字节类型统一按大端Big-Endian高位在前写入float通过 union 位转换后按 32 位整数写入字节填充byte stuffingaddUInt8内部对0x11、0x13、0x7d、0x7e四个保留字节做转义——插入0x7d并将原字节第 5 位取反data ^ (1 5)防止数据段与标志字节冲突校验和addUInt8在写每个数据字节时累加_checksumfinish()时写入其按位取反值~_checksum帧尾最后再写一个0x7e结束标志并置_isFinished true。缓冲区安全由_bufferSize边界检查保障begin()与addUInt8()在写入前都会判断_index是否越界越界时返回TxFrameError | BufferSizeError组合错误码。2.4 发送与接收流程SensirionShdlcCommunication提供三个静态方法详见 SensirionShdlcCommunication.hsendFrame(txFrame, serial)把已finish()的帧写入串口receiveFrame(rxFrame, serial, timeoutMicros)带微秒级超时地读取应答帧sendAndReceiveFrame(serial, txFrame, rxFrame, rxTimeoutMicros)一次完成发送 接收超时值需查阅传感器数据手册确定。2.5 官方示例代码SHDLCREADME 给出的 SHDLC 使用范式如下注意缓冲区大小的经验公式为2 * (n 6)其中n为要发送的字节数这是最坏情况下的估算考虑了字节填充与帧头帧尾开销uint8_t txBuffer[256]; uint8_t rxBuffer[256]; SensirionShdlcTxFrame txFrame(txBuffer, 256); SensirionShdlcRxFrame rxFrame(rxBuffer, 256); txFrame.begin(COMMAND, ADDRESS, DATALENGTH); // COMMAND/ADDRESS 见传感器数据手册 txFrame.addUInt8(UINT8); txFrame.addUInt32(UINT32); txFrame.finish(); // 写校验和与结束标志 SensirionShdlcCommunication::sendAndReceiveFrame(STREAMOBJECT, txFrame, rxFrame, TIMEOUT); rxFrame.getUInt16(UINT16); // 解析应答 rxFrame.getFloat(FLOAT);三、I2C 协议实现SCD4x / SEN5x 等传感器的片上总线通信3.1 核心类I2C 链路同样由三个类构成SensirionI2CTxFrame.h、SensirionI2CRxFrame.h、SensirionI2CCommunication.h类职责SensirionI2cTxFrame构造发送帧写命令字、追加数据并自动插入 CRCSensirionI2cRxFrame保存并解析接收帧SensirionI2CCommunication静态工具类封装TwoWire总线上的sendFrame/receiveFrame3.2 命令字与 CRC 的自动插入机制SensirionI2cTxFrame与 SHDLC 版本有两点关键差异均可在 SensirionI2CTxFrame.cpp 中验证命令字2 字节构造时通过addCommand(COMMAND)将 16 位命令按大端写入缓冲区前两个字节同时提供createWithUInt8Command()1 字节命令与createWithUInt16Command()2 字节命令默认两种工厂方法。构造函数还允许通过numCommandBytes指定命令宽度addCommand会先检查_bufferSize 2。CRC 自动插入_addByte()在写入每个数据字节后判断(_index - _numCommandBytes) % 3 2即每写完 2 个数据字节自动追加 1 字节 CRCSensirion I2C 传感器的标准做法。这就是 README 要求接收缓冲区按需读取字节数 ×1.5 预留的原因——多出的 50% 正是 CRC 开销。CRC 多项式可通过构造参数poly选择默认CRC31_ff见 SensirionCrc.h另有CRC31_00可选。SensirionI2CCommunication的静态接口SensirionI2CCommunication.hstatic uint16_t sendFrame(uint8_t address, SensirionI2CTxFrame frame, TwoWire i2cBus); static uint16_t receiveFrame(uint8_t address, size_t numBytes, SensirionI2CRxFrame frame, TwoWire i2cBus, CrcPolynomial poly CRC31_ff);receiveFrame需要显式告知期望接收的原始字节数不含 CRC并按同一多项式校验收到的 CRC。3.3 官方示例代码I2CREADME 的 I2C 示例与其说教逻辑如下先addCommand()写命令再追加参数数据随后经sendFrame(ADDRESS, txFrame, WIREOBJECT)发送等待数据手册规定的READ_DELAY后再receiveFrame()读取应答。需要注意的是README 该示例代码片段中误用了SensirionShdlc*前缀的类名实际可编译的完整范例应以仓库内的示例程序 AllCommandsI2c.ino 为准#include SensirionCore.h #include Wire.h #include stdint.h uint8_t txBuffer[256]; uint8_t rxBuffer[256]; SensirionI2CTxFrame txFrame(txBuffer, 256); SensirionI2CRxFrame rxFrame(rxBuffer, 256); void setup() { Wire.begin(); } void loop() { uint16_t mockCommand 42; uint16_t error txFrame.addCommand(mockCommand); uint32_t mockUInt32 42; error | txFrame.addUInt32(mockUInt32); int32_t mockInt32 42; error | txFrame.addInt32(mockInt32); uint16_t mockUInt16 42; error | txFrame.addUInt16(mockUInt16); int16_t mockInt16 42; error | txFrame.addInt16(mockInt16); uint8_t mockUInt8 42; error | txFrame.addUInt8(mockUInt8); int8_t mockInt8 42; error | txFrame.addInt8(mockInt8); float mockFloat 42.0f; error | txFrame.addFloat(mockFloat); bool mockBool true; error | txFrame.addBool(mockBool); uint8_t mockBytes[] {42, 42, 42, 42}; error | txFrame.addBytes(mockBytes, 4); uint8_t mockAddress 42; error | SensirionI2CCommunication::sendFrame(mockAddress, txFrame, Wire); size_t mockNumBytes 42; error | SensirionI2CCommunication::receiveFrame(mockAddress, mockNumBytes, rxFrame, Wire); error | rxFrame.getUInt32(mockUInt32); error | rxFrame.getInt32(mockInt32); error | rxFrame.getUInt16(mockUInt16); error | rxFrame.getInt16(mockInt16); error | rxFrame.getUInt8(mockUInt8); error | rxFrame.getInt8(mockInt8); error | rxFrame.getFloat(mockFloat); error | rxFrame.getBytes(mockBytes, 4); }该示例覆盖了全部add*/get*API并对每一步的返回值做|累积——这与 README 强调的所有函数出错时返回非零错误码成功返回 0的约定一致。四、接收帧解析SensirionRxFrame公共基类SHDLC 与 I2C 两个RxFrame共享同一个基类 SensirionRxFrame.h它负责把通信类填充进缓冲区的原始字节解码为业务数据类型提供getUInt8 / getInt8 / getUInt16 / getInt16 / getUInt32 / getInt32按大端序读取定长整数getFloat将 4 字节按 IEEE 754 解释为单精度浮点getBool读取 1 字节布尔getBytes(data, maxBytes)读取任意长度的字节数组getInteger(destination, type, nrOfBytes)通用整数读取支持Byte/Short/Integer/LongInteger四种宽度当实际字节数小于目标类型宽度时自动以 0 填充。五、错误码体系高/低两层编码设计SensirionErrors.h 定义了清晰的两级错误码任何 API 返回的uint16_t错误码都由高层错误类型高字节 低层错误原因低字节组成高层错误HighLevelError常量值含义NoError0x0000无错误WriteError0x0100写入失败ReadError0x0200读取失败TxFrameError0x0300发送帧构造/发送错误RxFrameError0x0400接收帧解析错误ExecutionError0x0500设备执行错误SHDLCSensorSpecificError0x8000传感器特定错误更高位由具体传感器定义低层错误LowLevelErrorUndefined、NonemptyFrameError、NoDataError、BufferSizeError缓冲区不足、StopByteError、ChecksumError、TimeoutError、RxCommandError、RxAddressError、SerialWriteError、WrongNumberBytesError、CRCError、I2cAddressNack、I2cDataNack、I2cOtherError、NotEnoughDataError、InternalBufferSizeError。例如前面源码中出现的TxFrameError | BufferSizeError即0x0300 | 低字节即发送帧缓冲区越界这一组合错误。库还提供errorToString(error, buffer, size)将错误码转换为人类可读的字符串便于调试输出。六、在 Tasmota 固件中的实际应用6.1 上层驱动依赖关系在 Tasmota 仓库中Sensirion Core 并不被应用代码直接调用而是作为 I2C 传感器驱动的底层依赖。可以确认的驱动有xsns_92_scd40.inoSCD40/41/42/43 二氧化碳、温湿度传感器#include SensirionI2cScd4x.hI2C 地址0x69xsns_103_sen5x.inoSEN5x 空气质量传感器xsns_44_sps30.inoSPS30 颗粒物传感器xsns_120_stcc4.inoSTC4x 温度传感器。以SensirionI2cScd4x::readMeasurement()的实现为例见 arduino-i2c-scd4x 源码驱动先调用readMeasurementRaw()通过核心库构造命令帧并收发 9 字节原始数据再经signalTemperature()-45.0 175.0 * raw / 65535.0、signalRelativeHumidity()100.0 * raw / 65535.0等信号转换函数还原为物理量。这正是核心库做协议、驱动库做信号处理、Tasmota 做控制台与 MQTT 集成的三层分工。6.2 控制台命令示例依托 SCD4x 驱动Tasmota 控制台可直接下发命令验证传感器功能见 xsns_92_scd40.ino 头部注释SCD40Strt启动周期测量每 5 秒一次SCD40StLp启动低功耗周期测量每 30 秒一次SCD40Sing/SCD40SRHT单次测量 / 仅温湿度单次测量SCD41/SCD43SCD40Cal x强制校准以 ppm 为单位的 CO2 参考值执行约 400msSCD40Test自检约 10 秒SCD40Pers将设置持久化到 EEPROM保证 2000 次写入周期SCD40Fact恢复出厂设置约 1200ms。这些命令的返回值遵循统一约定数据类命令失败返回-1执行类命令失败返回错误码、成功返回0。七、扩展更多驱动与适用场景README 还列出了依赖该核心库的其他官方驱动SVM40 环境传感器、SFA3x 甲醛传感器等的 I2C/UART 版本以及面向非 Arduino 平台的 Embedded 与 Python 驱动。本仓库的 lib/lib_i2c 目录下已内置了这些驱动中的多份 I2C 实现arduino-i2c-scd30、arduino-i2c-scd4x、arduino-i2c-sen5x、arduino-i2c-sgp41、arduino-i2c-sps30、arduino-i2c-stcc4、Sensirion_I2C_SEN6X_Tasmota等它们共同构成 Tasmota 对 Sensirion 环境传感产品线的完整支持矩阵。八、小结Sensirion Core 库把两条差异巨大的物理链路基于 HDLC 的字节型串口协议、带逐 2 字节 CRC 的 I2C 协议统一收敛为TxFrame 构造 / Communication 收发 / RxFrame 解析三个对称的抽象层次并通过组合式错误码与公共 RxFrame 基类显著降低上层驱动开发成本。理解这份核心库的帧格式与 CRC/校验和机制是读懂 Tasmota 中 SCD4x、SEN5x 等传感器驱动实现、乃至自行移植新 Sensirion 传感器驱动的第一步。【免费下载链接】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),仅供参考