使用 Adafruit MCP9808 精密 I2C 温度传感器库:从 Arduino 入门到 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
MCP9808 是 Microchip 推出的一款高精度 I2C 数字温度传感器,实测精度达 ±0.25°C(典型值),以 16 位分辨率输出环境温度。本文以 Tasmota 仓库中随附的 Adafruit_MCP9808_Tasmota 库为骨架,系统讲解其安装方式、I2C 地址与分辨率配置、完整 API 用法,并结合 xsns_72_mcp9808.ino 揭示该库在 Tasmota 固件中如何被驱动为原生温度传感器,帮助读者在 Arduino 独立开发与 Tasmota 固件两条路径上都快速落地 MCP9808。
一、库的定位与核心特性
根据 README.md 的说明,这是 Adafruit 出品的MCP9808 Precision I2C Temperature sensor library(MCP9808 精密 I2C 温度传感器库),专门面向 Adafruit MCP9808 Breakout 开发板(产品编号 1782)设计并验证。该芯片通过 I2C 总线通信,硬件上仅需两根信号线(SDA、SCL)即可完成接口。
仓库内的这份库是 Tasmota 项目的定制分支,在 Adafruit_MCP9808.h 的文件头中明确标注了改动记录:
changes by Martin Wagner for tasmota project: the library supports variable I2C address(库支持可变 I2C 地址)
这一点正是 Tasmota 分支与上游版本的关键差异:原版库在创建对象后固定使用默认地址,而本仓库版本的所有读写 API 都接受一个addr参数,允许同一对象动态切换总线上的不同器件,为同一条 I2C 总线上挂载最多 8 颗 MCP9808 提供了底层支持。
按 library.properties 的元数据,本库版本号为1.1.2,作者为 Adafruit,类别为 Sensors,架构支持*(即不限架构),并声明依赖 Adafruit Unified Sensor。许可证为 BSD(详见 license.txt)。
二、硬件接线与 I2C 地址配置
2.1 最小接线
MCP9808 是纯 I2C 器件,共需 4 根连线:
| 引脚 | 连接目标 |
|---|---|
| VDD | 3.3V(芯片工作电压 2.7V–3.6V) |
| GND | 地 |
| SDA | 主控 I2C 数据线 |
| SCL | 主控 I2C 时钟线 |
在 Arduino 上通常接 A4/A5(UNO)或 SDA/SCL(Mega、ESP8266、ESP32 等开发板)即可。
2.2 地址选择:A2/A1/A0 引脚
MCP9808 提供 3 个地址选择引脚 A2、A1、A0,通过焊接跳线或拉高/拉低组合出8 个可用地址(0x18–0x1F),这也是"同一条总线上可挂 8 颗传感器"的硬件基础。示例程序 mcp9808test.ino 中给出了完整的地址对照表:
| A2 | A1 | A0 | I2C 地址 |
|---|---|---|---|
| 0 | 0 | 0 | 0x18(默认地址) |
| 0 | 0 | 1 | 0x19 |
| 0 | 1 | 0 | 0x1A |
| 0 | 1 | 1 | 0x1B |
| 1 | 0 | 0 | 0x1C |
| 1 | 0 | 1 | 0x1D |
| 1 | 1 | 0 | 0x1E |
| 1 | 1 | 1 | 0x1F |
代码中对应宏定义MCP9808_I2CADDR_DEFAULT 0x18位于 Adafruit_MCP9808.h。示例程序特别提醒:连接多个传感器时,只需为每颗芯片配置不同地址并定义多个库对象即可。
三、安装方式
README 给出的安装路径是Arduino Library Manager(Arduino 库管理器):在 Arduino IDE 中打开"工具 → 管理库",搜索Adafruit MCP9808并安装即可。本仓库中的这份库则属于 Tasmota 的 vendored 库,位于 lib/lib_i2c/Adafruit_MCP9808_Tasmota,由平台构建系统直接引用,无需手动安装。
四、核心 API 详解
本节基于 Adafruit_MCP9808.h 与 Adafruit_MCP9808.cpp 的完整源码展开,API 均支持传入addr参数(Tasmota 分支特性)。
4.1 初始化:begin / init
库提供 4 个重载的begin方法:
bool begin(); // 默认地址 0x18 + 默认 Wire bool begin(TwoWire *theWire); // 默认地址 + 指定 Wire 总线 bool begin(uint8_t addr); // 指定地址 + 默认 Wire bool begin(uint8_t addr, TwoWire *theWire); // 指定地址 + 指定 Wire 总线四个重载最终都会调用init(),其内部逻辑(Adafruit_MCP9808.cpp)依次执行:
_wire->begin()启动 I2C 总线;- 读取制造商 ID 寄存器(0x06),校验是否为
0x0054(Microchip); - 读取器件 ID 寄存器(0x07),校验是否为
0x0400; - 将配置寄存器(0x01)写 0,恢复默认工作状态。
只有 ID 校验全部通过才返回true,因此begin的返回值就是"传感器是否在线且正确"的可靠判据——示例代码正是利用这一点做启动自检(见第五节)。
4.2 温度读取:readTempC / readTempF
float readTempC(uint8_t addr); // 返回摄氏度 float readTempF(uint8_t addr); // 返回华氏度读取寄存器MCP9808_REG_AMBIENT_TEMP(0x05)中的 16 位环境温度值,换算逻辑(Adafruit_MCP9808.cpp)为:
temp = t & 0x0FFF; // 低 12 位为温度有效位(0.0625°C/LSB) temp /= 16.0; // 除以 16 还原温度 if (t & 0x1000) // bit12 为符号位,置 1 表示负温度 temp -= 256; // 负温度按补码减去 256即:温度 = 原始值 / 16,符号位(bit12)为 1 时再减 256,最小分辨率 0.0625°C。readTempF在摄氏度结果基础上执行temp * 9.0 / 5.0 + 32完成换算。当寄存器读到0xFFFF(总线无响应)时返回NAN,便于上层做异常判断。
4.3 分辨率控制:setResolution / getResolution
void setResolution(uint8_t addr, uint8_t value); // value 只取低 2 位(& 0x03) uint8_t getResolution(uint8_t addr);分辨率寄存器(0x08)的低 2 位决定 ADC 转换精度与采样时间,示例程序中给出了完整对照表:
| 模式 | 分辨率 | 采样时间 |
|---|---|---|
| 0 | 0.5°C | 30 ms |
| 1 | 0.25°C | 65 ms |
| 2 | 0.125°C | 130 ms |
| 3 | 0.0625°C | 250 ms |
模式 3 精度最高但采样最慢,需根据应用场景在功耗、速度与精度间权衡。
4.4 低功耗控制:shutdown / wake / shutdown_wake
void shutdown_wake(uint8_t addr, boolean sw); // true=关断,false=唤醒 void shutdown(uint8_t addr); void wake(uint8_t addr);这是本库的特色功能之一:通过读写配置寄存器(0x01)的 bit8(MCP9808_REG_CONFIG_SHUTDOWN 0x0100)实现关断与唤醒(Adafruit_MCP9808.cpp)。示例程序给出的功耗数据为:
- 唤醒/正常采样状态:约 200 µA;
- 关断状态:约 0.1 µA(停止温度采样)。
wake()内部在清除关断位后额外delay(250)等待传感器稳定,确保唤醒后立刻读取到有效数据。
4.5 底层寄存器读写
void write16(uint8_t reg, uint16_t val); uint16_t read16(uint8_t reg); void write8(uint8_t reg, uint8_t val); uint8_t read8(uint8_t reg);这四个方法是所有上层 API 的 I2C 原语(Adafruit_MCP9808.cpp):
read16:先写寄存器地址,若endTransmission()返回 0(ACK 正常)再requestFrom读 2 字节,按大端序拼装;失败时返回0xFFFF;write16:寄存器地址后先发高字节再发低字节(I2C 大端序要求);read8/write8:单字节版本的读写。
理解这些原语,有助于自行扩展报警阈值、临界温度等未封装的寄存器功能。
五、完整示例程序解读
示例 mcp9808test.ino 演示了完整的"探测 → 配置 → 唤醒读取 → 关断"工作循环,核心流程如下:
#include <Wire.h> #include "Adafruit_MCP9808.h" Adafruit_MCP9808 tempsensor = Adafruit_MCP9808(); // 创建传感器对象 void setup() { Serial.begin(9600); while (!Serial); // 等待串口就绪(较新 Arduino 板必需) // 传入地址做初始化自检,也可省略参数使用默认地址 0x18 if (!tempsensor.begin(0x18)) { Serial.println("Couldn't find MCP9808! Check your connections and verify the address is correct."); while (1); // 找不到传感器则停机 } Serial.println("Found MCP9808!"); tempsensor.setResolution(3); // 设为最高分辨率 0.0625°C / 250ms } void loop() { Serial.println("wake up MCP9808.... "); tempsensor.wake(); // 唤醒(若处于关断态) Serial.print("Resolution in mode: "); Serial.println(tempsensor.getResolution()); float c = tempsensor.readTempC(); float f = tempsensor.readTempF(); Serial.print("Temp: "); Serial.print(c, 4); Serial.print("*C\t and "); Serial.print(f, 4); Serial.println("*F."); delay(2000); Serial.println("Shutdown MCP9808.... "); tempsensor.shutdown_wake(1); // 关断,电流降至约 0.1 µA delay(200); }要点归纳:
- 启动自检:
begin()返回 false 即提示"检查接线与地址",这是排查 I2C 连接问题的第一道防线; - 分辨率演示:
setResolution(3)配合getResolution()展示当前分辨率模式; - 摄氏度/华氏度双输出:
readTempC()与readTempF()分别以 4 位小数打印; - 节能循环:每轮读取后调用
shutdown_wake(1)关断,下次循环再wake(),实现周期采样下的低功耗运行。
六、在 Tasmota 固件中的集成实现
6.1 驱动挂载与探测流程
Tasmota 通过 xsns_72_mcp9808.ino(传感器驱动编号 XSNS_72)接入该库。驱动的启用由编译宏控制,默认配置模板中声明为:
//#define USE_MCP9808 // [I2cDriver51] Enable MCP9808 temperature sensor (I2C addresses 0x18 - 0x1F) (+0k9 code)该宏位于 tasmota_configurations.h,同时需要USE_I2C启用 I2C 子系统,并在my_user_config.h中按需打开。宏注释中的[I2cDriver51]表明该驱动的 I2C 设备编号为 XI2C_51,对应 I2CDEVICES.md 中的驱动清单。
6.2 多传感器轮询机制
驱动源码充分利用了 Tasmota 分支"可变 I2C 地址"的特性:
#define MCP9808_MAX_SENSORS 8与#define MCP9808_START_ADDRESS 0x18(xsns_72_mcp9808.ino)对应芯片 8 个可配置地址;MCP9808Detect()在FUNC_INIT阶段从 0x18 到 0x1F 逐地址调用mcp9808.begin(addr)探测,命中即登记为在线传感器,并统一调用setResolution(..., 2)将分辨率设为 0.125°C/130ms(精度与采样速度的折中选择);MCP9808EverySecond()在FUNC_EVERY_SECOND每秒钟轮询一次所有已发现的传感器,温度经ConvertTemp()换算后存入结构体。
6.3 数据上报与展示
MCP9808Show()负责数据输出(xsns_72_mcp9808.ino):
- JSON 上报(FUNC_JSON_APPEND):通过
ResponseAppend_P(JSON_SNS_F_TEMP, ...)输出到 MQTT 的Tele/.../SENSOR主题;多传感器时按地址生成MCP9808-18、MCP9808-1A等命名(使用IndexSeparator()分隔); - Domoticz / KNX 联动:在遥测周期为 0 时,将第一个传感器温度同步到
DomoticzFloatSensor(DZ_TEMP, ...)与KnxSensor(KNX_TEMPERATURE, ...); - Web 界面(FUNC_WEB_SENSOR):通过
WSContentSend_Temp()在设备主页的传感器区域显示温度。
Xsns72()调度函数还在入口处调用I2cEnabled(XI2C_51)做可用性检查,未启用时直接返回,保证与其他 I2C 驱动并存时的行为正确。
七、从库到固件的完整链路小结
把前文串联起来,MCP9808 在 Tasmota 中的完整数据链路为:
MCP9808 芯片(0x18–0x1F) → Adafruit_MCP9808 库(readTempC / setResolution / shutdown_wake) → xsns_72_mcp9808.ino(I2cDriver51 探测、每秒轮询、ConvertTemp 换算) → Tasmota 框架(JSON/MQTT、Web 界面、Domoticz/KNX 转发)对独立 Arduino 开发而言,只需引入本库头文件、按地址表配置跳线、调用begin()自检并循环readTempC()/readTempF()即可获得 ±0.25°C 级精度的温度数据;对 Tasmota 用户而言,启用USE_MCP9808编译宏后,即可在 Web 界面与 MQTT 主题中直接读到传感器数据,无需编写任何应用代码。两条路径共用同一份驱动源码,这也是本仓库随附该库的核心价值所在。
【免费下载链接】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),仅供参考