Tasmota 集成指南:Grove/Xadow 多通道气体传感器(MutichannelGasSensor 库)完全实战手册
【免费下载链接】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
导读
本指南围绕 Seeed 出品的 Grove / Xadow 多通道气体传感器在 Arduino 生态(含 Tasmota 固件环境)中的完整使用展开,覆盖库的初始化、传感器加热供电、8 种气体的浓度测量、I2C 地址修改、校准与出厂恢复等全部核心 API,并结合 MutichannelGasSensor.cpp 源码剖析浓度换算的底层原理。读完本文,你将掌握该传感器从接线、上电、首次读数到校准投产的完整闭环流程,并理解它在 Tasmota 中如何被 xsns_19_mgs.ino 驱动并上报到 WebUI 与 MQTT。
一、传感器与库的定位
MutichannelGasSensor是一个 Arduino 库,专门用于驱动两款 I2C 接口的多通道气体传感器模块:
- Xadow – Mutichannel Gas Sensor(Xadow 系列叠装模块)
- Grove – Multichannel Gas Sensor(Grove 标准四线接口模块)
该库托管在仓库的 lib/lib_i2c/Mutichannel_Gas_Sensor 目录下,采用 MIT 许可(见 License.txt),由 Seeed 工程师 Jacky Zhang 编写、Loovee 在 2016 年升级至 V2 版本(参见 MutichannelGasSensor.h 头文件注释)。
1.1 模块硬件架构
从示例程序 ReadSensorValue_Grove.ino 的注释可以明确模块内部结构:
There is a ATmega168PA on Xadow-gas, it get sensors output and feed back to master. The data is raw ADC value, algorithm should be realized on master.
也就是说,传感器板载一颗ATmega168PA 从机 MCU:
- 从机 MCU 负责采集三路气体传感器的原始 ADC 值,并通过 I2C 总线把数据回传给主机(Arduino / ESP8266 / ESP32);
- 浓度换算算法运行在主机侧,即由本库在主机端完成"ADC 值 → 电阻比 → ppm 浓度"的计算;
- 模块还提供固件升级能力,仓库 examples/UpdateFrimware 中带有
bootloader_atmega168.h,支持通过主机对板载 MCU 刷写新固件。
1.2 版本特性
库在begin()时通过读取从机 EEPROM 自动识别固件版本(__version为 1 或 2,读取失败时为 -1),两种版本在 API 内部实现上有差异(详见下文"浓度换算原理")。示例 GetVersion.ino 演示了如何读取固件版本:
#include <Wire.h> #include "MutichannelGasSensor.h" #define SENSOR_ADDR 0X04 // 默认地址 0x04 void setup() { Serial.begin(115200); gas.begin(SENSOR_ADDR); unsigned char version = gas.getVersion(); Serial.print("Version = "); Serial.println(version); } void loop() { /* nothing to do */ }二、快速上手:三步读取气体浓度
README 给出的核心用法只有三步,这也是所有项目的最小可用骨架。
2.1 第一步:初始化 I2C
gas.begin(0x04);begin(int address)内部依次执行(见 MutichannelGasSensor.cpp 第 42-51 行):
Wire.begin()启动主机 I2C 总线;- 记录从机地址到
i2cAddress; - 调用
getVersion()探测从机固件版本(V1 或 V2)。
也可以调用无参版本gas.begin(),它会使用头文件中定义的默认地址宏DEFAULT_I2C_ADDR 0x04。
2.2 第二步:给传感器加热供电
gas.powerOn();多通道气体传感器内部采用电化学/半导体加热式检测元件,必须先上电加热才能获得稳定读数。powerOn()与powerOff()会根据固件版本走不同的协议分支(源码第 525-551 行):
- V1 固件:发送单字节命令
0x21(开)/0x20(关); - V2 固件:发送两字节命令
{0x0B, 0x01}(开)/{0x0B, 0x00}(关),其中0x0B即头文件中的CMD_CONTROL_PWR。
2.3 第三步:测量目标气体浓度
float nh3 = gas.measure_NH3(); // 氨气,单位 ppm float co = gas.measure_CO(); // 一氧化碳 float no2 = gas.measure_NO2(); // 二氧化氮完整可运行的循环读数示例(源自 ReadSensorValue_Grove.ino,增加了非法值处理):
#include <Wire.h> #include "MutichannelGasSensor.h" void setup() { Serial.begin(115200); gas.begin(0x04); // 默认 I2C 地址 gas.powerOn(); Serial.print("Firmware Version = "); Serial.println(gas.getVersion()); } void loop() { float c; c = gas.measure_NH3(); Serial.print("The concentration of NH3 is "); if (c >= 0) Serial.print(c); else Serial.print("invalid"); Serial.println(" ppm"); delay(1000); }三、完整 API 参考:全部测量函数与配套方法
3.1 八种气体浓度测量函数
所有测量函数均返回float 类型的 ppm 浓度值,它们都是对calcGas(int gas)的轻量封装(头文件第 116-123 行):
| 测量函数 | 对应气体 | 内部枚举 | 换算所用通道 |
|---|---|---|---|
measure_NH3() | 氨气 | NH3 | CH0(R0 通道) |
measure_CO() | 一氧化碳 | CO | CH1 |
measure_NO2() | 二氧化氮 | NO2 | CH2 |
measure_C3H8() | 丙烷 | C3H8 | CH0 |
measure_C4H10() | 丁烷 | C4H10 | CH0 |
measure_CH4() | 甲烷 | GAS_CH4 | CH1 |
measure_H2() | 氢气 | H2 | CH1 |
measure_C2H5OH() | 乙醇 | C2H5OH | CH1 |
其中NH3 / CO / NO2是模块的三路独立传感通道,而C3H8 / C4H10 / CH4 / H2 / C2H5OH是根据各传感器对多种气体的交叉灵敏度,复用前三路通道的电阻比、以不同经验公式换算得到的(详见第四节)。
错误返回值约定(源码calcGas()第 324-411 行):
-1.0f:V1 固件下读取 R0 失败;-2.0f:读取当前电阻 R 失败;-3:计算结果为NaN(isnan(c)判定);- 另见
readData()返回的-3(响应字节数不是 4)与-4(校验和不匹配)。
因此业务代码中务必用if (c >= 0)判断读数是否有效,示例 ReadSensorValue_Grove.ino 正是这样处理的。
3.2 辅助与配置方法
| 方法 | 作用 | 说明 |
|---|---|---|
begin(int address)/begin() | 初始化 I2C 并探测固件版本 | 默认地址 0x04 |
getVersion() | 返回固件版本(1 / 2 / -1) | 通过读 EEPROM 标志位 1126 判断 V2 |
isError() | 返回最近一次 I2C 传输是否出错 | 配合begin()检测设备是否在线 |
powerOn()/powerOff() | 传感器加热器供电开/关 | V1 单字节命令,V2 双字节命令 |
doCalibrate() | 执行标定 | V1 约需 8 秒;V2 自动采样稳定后写入用户 ADC 值 |
change_i2c_address(addr)/changeI2cAddr(addr) | 修改从机 I2C 地址并写入其 EEPROM | 新地址立即生效 |
getR0(ch)/getRs(ch) | 读取校准基准电阻/当前电阻(Ω) | 仅 V2 固件支持,ch: 0=NH3, 1=CO, 2=NO2 |
display_eeprom() | 打印从机 EEPROM 中全部标定数据 | 仅 V2 固件支持 |
factory_setting() | 恢复出厂设置 | 扫描总线将地址重置为 0x04 并恢复出厂 ADC 值 |
ledOn()/ledOff() | 控制模块板载 LED | V2 固件计算浓度时会临时点亮 |
readData(cmd) | 发送命令并读取 4 字节带校验数据 | 返回 16 位数据,失败返回负值 |
四、源码级剖析:浓度换算的底层原理
4.1 命令集与数据帧协议
头文件 MutichannelGasSensor.h 定义了完整的从机命令字:
CMD_ADC_RES0 = 1 // 读取 CH0 (NH3) 当前 ADC CMD_ADC_RES1 = 2 // 读取 CH1 (CO) 当前 ADC CMD_ADC_RES2 = 3 // 读取 CH2 (NO2) 当前 ADC CMD_ADC_RESALL = 4 // 读取全部通道 CMD_CHANGE_I2C = 5 // 修改 I2C 地址 CMD_READ_EEPROM = 6 // 读 EEPROM(返回无符号整型) CMD_SET_R0_ADC = 7 // 写入 R0 标定 ADC 值 CMD_GET_R0_ADC = 8 // 读取 R0 标定 ADC 值 CMD_GET_R0_ADC_FACTORY = 9 // 读取出厂 R0 ADC 值 CMD_CONTROL_LED = 10 // 控制板载 LED CMD_CONTROL_PWR = 11 // 控制加热电源V2 固件下,readData()采用4 字节带校验数据帧(源码第 230-255 行):buffer[3] = buffer[0] + buffer[1] + buffer[2](字节求和校验),有效数据为(buffer[1] << 8) + buffer[2],即大端序 16 位无符号值。校验失败返回-4,响应长度不足 4 字节返回-3,发送失败返回-5。
4.2 V1 固件换算流程
V1 固件(calcGas 第 328-342 行):
- 首次测量时通过命令
0x11/0x12/0x13读取三个通道的 R0 基准值并缓存(r0_inited置位,之后不再重复读取); - 通过命令
0x01/0x02/0x03读取当前电阻 R; - 计算三通道电阻比
ratio = res / res0; - 代入对应气体的经验公式。
4.3 V2 固件换算流程
V2 固件(源码第 343-359 行)直接读取 EEPROM 中的用户标定 ADC 值(A0)与当前 ADC 值(An),按分压电阻公式计算电阻比:
ratio = An / A0 * (1023 - A0) / (1023 - An)其中A0对应 EEPROM 偏移地址ADDR_USER_ADC_HN3/CO/NO2(8/10/12),An对应CH_VALUE_NH3/CO/NO2命令的实时读数。计算过程中会临时点亮板载 LED,完成后关闭(第 346、409 行)。
4.4 八种气体的经验换算公式
源码第 361-407 行给出了每种气体的幂函数模型c = a * ratio^b,其中pow()在 Tasmota 环境中被替换为 support_float.ino 提供的FastPrecisePowf()(见头文件第 71-77 行的 extern 声明与内联包装),以保证在资源受限 MCU 上的浮点运算效率:
| 气体 | 公式(c 单位 ppm) | 通道 |
|---|---|---|
| CO | c = 4.385 * ratio1^(-1.179) | CH1 |
| NO2 | c = ratio2^(1.007) / 6.855 | CH2 |
| NH3 | c = ratio0^(-1.67) / 1.47 | CH0 |
| C3H8 | c = 570.164 * ratio0^(-2.518) | CH0 |
| C4H10 | c = 398.107 * ratio0^(-2.138) | CH0 |
| CH4 | c = 630.957 * ratio1^(-4.363) | CH1 |
| H2 | c = 0.73 * ratio1^(-1.8) | CH1 |
| C2H5OH | c = 1.622 * ratio1^(-1.552) | CH1 |
这些系数来自 Seeed 对各传感器响应曲线的拟合,同一通道可换算多种气体正源于此。
五、校准与恢复出厂:保证读数准确
5.1 标定流程(calibration 示例)
校准是气体传感器投产前不可省略的步骤。仓库 calibration.ino 给出了标准流程:
#define SENSOR_ADDR 0X04 // 默认地址 #define PRE_HEAT_TIME 0 // 预热时间,官方建议 10~30 分钟 void setup() { Serial.begin(115200); gas.begin(SENSOR_ADDR); Serial.println("power on, and pre-heat"); for (int i = 60 * PRE_HEAT_TIME; i >= 0; i--) { // 倒计时预热 Serial.print(i / 60); Serial.print(":"); Serial.println(i % 60); delay(1000); } Serial.println("Begin to calibrate..."); gas.doCalibrate(); // 开始标定 Serial.println("Calibration ok"); gas.display_eeprom(); // 打印标定结果 }关键注意事项:
- 校准前必须预热 10~30 分钟,让传感器充分稳定(示例代码中
PRE_HEAT_TIME以分钟计); - 校准应在洁净空气环境中进行,此时各通道读数即 R0 基准;
doCalibrate()的 V1 实现(源码第 437-462 行)发送命令0x22并轮询等待新 R0 值就绪,约需 8 秒,期间从串口打印进度;- V2 实现(第 463-519 行)会反复采样三通道 ADC,直到连续 20 次采样内波动计数 ≤5(即读数趋于稳定),再把稳定值写入从机 EEPROM(命令
CMD_SET_R0_ADC = 7,数据格式为 7 字节{cmd, a0_hi, a0_lo, a1_hi, a1_lo, a2_hi, a2_lo}); - 标定完成后可用
getR0(ch)/getRs(ch)读取各通道的 R0 与 Rs 阻值(Ω),其内部公式为r = 56.0 * a / (1023.0 - a),即基于 56Ω 采样电阻的 10 位 ADC 分压换算; display_eeprom()会打印ADDR_IS_SET / ADDR_FACTORY_ADC_* / ADDR_USER_ADC_* / ADDR_I2C_ADDRESS全部 EEPROM 偏移内容,便于核对(源码第 553-569 行)。
5.2 恢复出厂设置
当标定数据异常或需要重新开始(例如换环境、换气体标定目标)时,使用 factory_setting.ino:
void setup() { Serial.begin(115200); Serial.println("Please input something to continue"); while (!Serial.available()); // 等待串口输入确认 gas.begin(SENSOR_ADDR); Serial.println("FACTORY SETTING OK"); float R0_NH3 = gas.getR0(0); // 恢复后读取 R0 校验 float R0_CO = gas.getR0(1); float R0_NO2 = gas.getR0(2); Serial.print("R0_NH3 = "); Serial.println(R0_NH3); Serial.print("R0_CO = "); Serial.println(R0_CO); Serial.print("R0_NO2 = "); Serial.println(R0_NO2); }factory_setting()(源码第 640-689 行)内部做了两件事:
- 扫描 1~126 全部 I2C 地址,找到应答设备后把地址改回
0x04; - 读取出厂 ADC 值(
ADDR_FACTORY_ADC_NH3/CO/NO2,偏移 2/4/6)并写回用户标定区,恢复出厂状态。
六、多设备共存:修改 I2C 地址
默认地址0x04在总线上同时挂多个模块时会冲突。仓库提供两种改地址方式:
change_i2c_address(addr):直接向当前地址设备发送{CMD_CHANGE_I2C, newAddr},成功后本地缓存地址同步更新(源码第 691-704 行);changeI2cAddr(addr):同样发送{0x23, newAddr}双字节命令,仅在 I2C 应答成功后才更新本地地址(源码第 417-427 行)。
官方示例 I2C_Address.ino:
#define SENSOR_ADDR_OLD 0x04 // 原地址 #define SENSOR_ADDR_NEW 0x19 // 新地址 void setup() { Serial.begin(115200); gas.begin(SENSOR_ADDR_OLD); gas.change_i2c_address(SENSOR_ADDR_NEW); // 修改并写入从机 EEPROM Serial.print("I2C ADDRESS SET TO 0X"); Serial.println(SENSOR_ADDR_NEW, HEX); }修改后地址会持久保存在从机 EEPROM 中(偏移ADDR_I2C_ADDRESS = 20),断电不丢失;下次begin()需使用新地址。
七、在 Tasmota 固件中的集成实战
本仓库作为 Tasmota 的 I2C 传感器库,已被官方驱动 xsns_19_mgs.ino 集成(对应XSNS_19,I2C 设备号XI2C_17),这意味着无需编写任何业务代码即可在 ESP8266/ESP32 上使用该传感器。
7.1 启用方式
- 在编译配置中启用宏
USE_I2C与USE_MGS(驱动文件第 20-21 行#ifdef USE_I2C/#ifdef USE_MGS); - 可通过编译宏覆盖传感器地址:
MGS_SENSOR_ADDR,默认0x04(第 32-34 行); - 烧录后驱动在
FUNC_INIT阶段调用MGSPrepare():先I2cSetDevice(MGS_SENSOR_ADDR)注册设备,再gas.begin()+gas.isError()探测在线状态,成功后标记为"MultiGas"(第 40-49 行)。
7.2 数据上报
- MQTT/JSON:在
FUNC_JSON_APPEND阶段,MGSShow(1)将 8 种气体浓度附加为"MGS":{"NH3":..,"CO":..,"NO2":..,"C3H8":..,"C4H10":..,"CH4":..,"H2":..,"C2H5OH":..}(第 62-73 行),数值经dtostrfd(f, 2, buffer)格式化为两位小数; - WebUI:在
FUNC_WEB_SENSOR阶段,MGSShow(0)以表格行MGS NH3: x.xx ppm形式渲染到网页(HTTP_MGS_GAS模板,单位D_UNIT_PARTS_PER_MILLION,即 ppm,第 59 行)。
也就是说,Tasmota 用户可以直接在控制台Status 10、MQTT 主题tele/.../SENSOR以及 Web 主页面看到 8 通道气体读数,测量间隔由TelePeriod等常规设置控制。
八、常见问题排查
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
getVersion()返回 -1 | I2C 无应答,接线错误或地址不符 | 检查 4 线接线(VCC/GND/SDA/SCL)、上拉电阻;运行示例 I2C_Address.ino 扫描总线 |
所有读数均为invalid | 未调用powerOn()或加热不足 | 确保begin()后调用powerOn(),预热 10~30 分钟 |
| V1 首次读数返回 -1 | R0 读取失败 | 重试;检查电源稳定性 |
| 返回 -3 / -4 | 数据帧长度或校验和错误 | 缩短 I2C 线缆、降低总线速率、检查干扰 |
| 读数漂移或偏差大 | 未标定或环境变化 | 在洁净空气中执行 calibration.ino 重新标定 |
| 多块模块地址冲突 | 默认地址均为 0x04 | 逐块执行地址修改示例,分配不同地址 |
九、总结
从 README 的三行最小用法出发,本文扩展到了完整的库 API 面:8 种气体测量、V1/V2 双版本协议差异、经验公式换算、EEPROM 标定与恢复、I2C 地址管理,以及 Tasmota 固件的零代码集成路径。核心源码与示例均可直接在仓库中查阅:库源码目录、全部示例、Tasmota 驱动、库元数据。在实际项目中,请务必遵守"预热 → 标定 → 读数"的正确流程,以获得可信的气体浓度数据。
【免费下载链接】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),仅供参考