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
本篇技术指南以 Tasmota 固件的开发者文档(
.doc_for_ai/FOR_DEVELOPERS.md)为骨架,系统梳理其事件驱动的模块化架构、编译期配置覆盖机制、命令系统、GPIO 管理,以及从零编写驱动/传感器模块的完整套路(回调分发、XdrvMailbox命令上下文、TSettings持久化存储、I2C 辅助 API)。读完本文,你将能直接在本仓库源码中定位关键实现,并具备为 Tasmota 新增一个传感器或驱动模块的完整工程能力。
一、Tasmota 整体架构:事件驱动 + 模块化
1. 核心设计原则
Tasmota 是运行在ESP8266/ESP8285 与 ESP32 系列微控制器上的模块化替代固件,其架构遵循三条基本原则:
- 事件驱动:主循环通过"回调 ID"把事件分发给驱动(driver)、传感器(sensor)、能量监测(energy)与灯光(light)模块。这些回调 ID 定义在
enum XsnsFunctions中,见 tasmota/include/tasmota.h。 - 模块化:Wi-Fi、MQTT、Web 界面等核心功能常驻固件;可选功能通过编译期
#define USE_xxx指令条件编译;驱动、传感器、显示、灯光、能量模块按编号组织。 - 非阻塞:所有回调都必须是非阻塞操作,否则会拖垮主循环的响应性。周期性回调按50 ms、100 ms、200 ms、250 ms、1 s的固定间隔调度。
2. 通信通道
| 通道 | 用途 |
|---|---|
| MQTT | 自动化系统的主要协议 |
| HTTP | Web 界面 + REST 风格命令端点/cm |
| 串口 Serial | 调试与配置的 console 访问 |
| WebSocket | 标准 UI 不使用;Web 界面基于 chunked HTTP + 周期轮询 |
3. 固件源码布局
tasmota/ ├── tasmota.ino # 主固件入口 ├── my_user_config.h # 主编译期配置(勿直接编辑) ├── user_config_override.h # 用户覆盖配置 ├── user_config_override_sample.h # 覆盖配置模板 ├── tasmota_xdrv_driver/ # 驱动模块 (xdrv_##_*.ino) ├── tasmota_xsns_sensor/ # 传感器模块 (xsns_##_*.ino) ├── tasmota_xdsp_display/ # 显示模块 (xdsp_##_*.ino) ├── tasmota_xlgt_light/ # 灯光控制器模块 (xlgt_##_*.ino) ├── tasmota_xnrg_energy/ # 能量监测模块 (xnrg_##_*.ino) ├── tasmota_xx2c_global/ # 跨模块共享代码(各 *_interface.ino 调度器) ├── tasmota_support/ # 核心支撑代码(settings、命令解析、I2C 等) ├── include/ # 公共头文件 (tasmota.h, tasmota_types.h, i18n.h ...) ├── berry/ # Berry VM 与随固件分发的 Berry 脚本 ├── language/ # 多语言本地化头文件 └── html_uncompressed/, html_compressed/ # Web UI HTML/JS 源码每个目录下的文件数量会随新驱动不断变化,不要依赖硬编码的文件数量。
4. 调度器的实现证据
所谓"主循环分发事件",在源码层面的落点就是各*_interface.ino中的调度函数。例如传感器侧,tasmota/tasmota_xx2c_global/xsns_interface.ino 用一个函数指针数组把XSNS_01~XSNS_xx映射到Xsns01()~Xsnsxx(),XsnsCall(uint32_t function)(见 xsns_interface.ino)再遍历已启用的传感器逐一分发事件,并且支持运行时禁用某个传感器(XsnsEnabled检查)。驱动、显示、灯光、能量模块的xdrv_interface.ino、xdsp_interface.ino、xlgt_interface.ino、xnrg_interface.ino结构完全一致。
二、编译期配置覆盖:user_config_override.h
Tasmota 的构建定制遵循永不直接编辑my_user_config.h的原则——它是随每个 release 分发的"主配置"。正确做法是:
- 把 tasmota/user_config_override_sample.h 复制为
tasmota/user_config_override.h; - 在其中加入自己的
#define/#undef指令。
#ifndef _USER_CONFIG_OVERRIDE_H_ #define _USER_CONFIG_OVERRIDE_H_ // 启用可选功能 #define USE_BERRY_DEBUG // 关闭无用功能以节省 flash 空间 #undef USE_DOMOTICZ #undef USE_KNX #endif // _USER_CONFIG_OVERRIDE_H_该文件对所有#ifdef USE_xxx条件编译的模块生效:每个驱动/传感器模块的.ino文件整体被#ifdef USE_<feature>包裹,未定义对应宏时整个模块不会参与编译,这正是"可选功能裁剪"机制的根基。此外平台级配置(如 flash 分区、编译环境)可参考 platformio_tasmota_env.ini 与 platformio_tasmota_env32.ini。
三、统一命令系统
Tasmota 的全部功能都收敛到一个统一的命令系统:
- 命令可通过MQTT、HTTP(
/cm?cmnd=...)、串口或 Web Console发送; - 格式为
Command [parameter],命令名大小写不敏感; - 响应通常是 JSON;
- 多条命令可用
Backlog cmd1; cmd2; cmd3链式执行。
命令解析最终会落到各模块的FUNC_COMMAND/FUNC_COMMAND_SENSOR/FUNC_COMMAND_DRIVER回调中,参数与上下文通过全局结构XdrvMailbox传递(详见下文第五节)。
四、GPIO 运行时管理
Tasmota 在运行时把逻辑功能("组件")映射到物理 GPIO,因此更换引脚映射无需重新编译:
- 模块(Module):预置的硬件基座配置,用
Module命令切换; - 模板(Template):JSON 描述符,可覆盖模块默认的 GPIO 分配,用
Template命令应用; - GPIO 命令:
GPIO<x> <component>把某个组件单独指派到指定引脚。
模板数据与模块定义集中在 tasmota/include/tasmota_template.h 与 tasmota/include/tasmota_template_legacy.h;官方模板仓库索引见仓库根目录 TEMPLATES.md。
五、驱动与传感器开发规范
1. 命名约定与回调入口
每个模块拥有唯一编号,并导出一个统一签名的回调函数:
| 模块类型 | 存放目录 | 回调函数 | 编译开关示例 |
|---|---|---|---|
| 驱动 | tasmota_xdrv_driver/xdrv_XX_<name>.ino | bool XdrvXX(uint32_t function) | #define USE_<feature> |
| 传感器 | tasmota_xsns_sensor/xsns_XX_<name>.ino | bool XsnsXX(uint32_t function) | #define XSNS_XX XX |
| 能量 | tasmota_xnrg_energy/xnrg_XX_<name>.ino | bool XnrgXX(uint32_t function) | #define XNRG_XX XX |
| 灯光 | tasmota_xlgt_light/xlgt_XX_<name>.ino | bool XlgtXX(uint32_t function) | #define XLGT_XX XX |
| 显示 | tasmota_xdsp_display/xdsp_XX_<name>.ino | bool XdspXX(uint32_t function) | #define XDSP_XX XX |
模块在编译期通过#define USE_<feature>启用,并用#define XSNS_XX XX(或对应前缀)声明自己的 ID。以传感器为例,tasmota/tasmota_xsns_sensor/xsns_05_ds18x20.ino 声明XSNS_05,xdrv_01_9_webserver.ino、xdrv_02_9_mqtt.ino等驱动则对应XDRV_前缀。
2. 传感器驱动骨架(可直接套用)
#ifdef USE_MY_SENSOR #define XSNS_99 99 // 唯一传感器 ID #define XI2C_99 99 // I2C 驱动索引(仅当使用 I2C 并注册时) bool MySensorDetected = false; void MySensorInit(void) { // ... 探测设备,成功后置 MySensorDetected = true } void MySensorEverySecond(void) { // ... 读取传感器数据 } void MySensorShow(bool json) { if (json) { ResponseAppend_P(PSTR(",\"MySensor\":{\"Temperature\":%d}"), temperature); #ifdef USE_WEBSERVER } else { WSContentSend_PD(HTTP_SNS_F_TEMP, "MySensor", Settings->flag2.temperature_resolution, // flag2 为 SysMBitfield1 类型 &temperature, TempUnit()); #endif // USE_WEBSERVER } } bool Xsns99(uint32_t function) { if (!I2cEnabled(XI2C_99)) { return false; } // 以 I2C 注册状态作为门控 bool result = false; if (FUNC_INIT == function) { MySensorInit(); } else if (MySensorDetected) { switch (function) { case FUNC_EVERY_SECOND: MySensorEverySecond(); break; case FUNC_JSON_APPEND: MySensorShow(true); break; #ifdef USE_WEBSERVER case FUNC_WEB_SENSOR: MySensorShow(false); break; #endif } } return result; } #endif // USE_MY_SENSOR三个关键注意点:
- 旧的
i2c_flg全局变量已不存在。应改用I2cEnabled(XI2C_xx)与I2cSetActiveFound()与 I2C 驱动注册表集成,实现见 tasmota/tasmota_support/support_a_i2c.ino; - Web 传感器显示字符串(如
HTTP_SNS_F_TEMP、HTTP_SNS_HUM、HTTP_SNS_F_DISTANCE_CM)声明在 tasmota/include/i18n.h,能复用就复用,不要自造新字符串; Settings->flag2类型为SysMBitfield1(定义在 tasmota/include/tasmota_types.h),内含temperature_resolution、humidity_resolution等分辨率位域。
六、回调 ID 全参考(enum XsnsFunctions)
完整回调 ID 定义在 tasmota/include/tasmota.h。enum XsnsFunctions被哨兵值FUNC_return_result = 200分为两段:
FUNC_return_result之前:fire and forget,分发器忽略返回值;FUNC_return_result之后:回调须返回bool表示是否"消费"了该事件(true在适用场景下会终止后续分发)。
1. 无返回值回调(节选)
| 函数 | 用途 |
|---|---|
FUNC_SETTINGS_OVERRIDE | 在设置加载前调整默认值 |
FUNC_SETUP_RING1/RING2 | 早期 setup 阶段 |
FUNC_PRE_INIT | GPIO 建立完成后 |
FUNC_INIT | 初始化结束 |
FUNC_ACTIVE | 驱动存在性查询 |
FUNC_ABOUT_TO_RESTART | 触发重启前 |
FUNC_LOOP/FUNC_SLEEP_LOOP | 主循环 tick |
FUNC_EVERY_50_MSECOND | 每 50 ms |
FUNC_EVERY_100_MSECOND | 每 100 ms |
FUNC_EVERY_200_MSECOND | 每 200 ms(仅能量槽位) |
FUNC_EVERY_250_MSECOND | 每 250 ms |
FUNC_EVERY_SECOND | 每秒 |
FUNC_RESET_SETTINGS/FUNC_RESTORE_SETTINGS/FUNC_SAVE_SETTINGS | 设置重置 / 恢复 / 保存 |
FUNC_SAVE_AT_MIDNIGHT | 每日 00:00 例行维护 |
FUNC_SAVE_BEFORE_RESTART | 计划重启前持久化数据 |
FUNC_INTERRUPT_STOP/START | 临界区前后禁用/启用中断 |
FUNC_AFTER_TELEPERIOD | teleperiod 发布之后 |
FUNC_JSON_APPEND | 向 teleperiod 载荷追加 JSON |
FUNC_WEB_SENSOR/FUNC_WEB_COL_SENSOR | 向主页面传感器区块追加 HTML 行 / 列格式 HTML |
FUNC_MQTT_SUBSCRIBE | 连接时追加 MQTT 订阅 |
FUNC_MQTT_INIT | MQTT(重)连接结束时执行一次 |
FUNC_SET_POWER | 继电器变化前通知 |
FUNC_SHOW_SENSOR | FUNC_JSON_APPEND完成后 |
FUNC_ANY_KEY | 任意按键/开关交互 |
FUNC_LED_LINK | 链路/状态 LED 控制 |
FUNC_ENERGY_EVERY_SECOND/FUNC_ENERGY_RESET | 能量驱动 tick / 计数器复位 |
FUNC_TELEPERIOD_RULES_PROCESS | 与 teleperiod 绑定的规则处理 |
FUNC_FREE_MEM | 诊断内存转储请求 |
FUNC_WEB_ADD_BUTTON/FUNC_WEB_ADD_CONSOLE_BUTTON/FUNC_WEB_ADD_MANAGEMENT_BUTTON/FUNC_WEB_ADD_MAIN_BUTTON | 向配置页 / 控制台页 / 管理页 / 主页添加按钮 |
FUNC_WEB_GET_ARG | 自定义 HTTP 参数处理 |
FUNC_WEB_ADD_HANDLER | 注册额外 URL handler |
FUNC_SET_SCHEME | 灯光方案变更(灯光槽位) |
FUNC_HOTPLUG_SCAN | 周期性热插拔设备扫描 |
FUNC_TIME_SYNCED | NTP 同步完成 |
FUNC_DEVICE_GROUP_ITEM | 设备组条目处理 |
FUNC_NETWORK_UP/DOWN | 网络连通性变化 |
FUNC_WEB_STATUS_LEFT/RIGHT | 状态页左右列 |
2. 带返回值回调(ID ≥ 200)
| 函数 | 用途 |
|---|---|
FUNC_PIN_STATE | 配置期覆盖 GPIO 状态 |
FUNC_MODULE_INIT | 模块专属初始化 |
FUNC_ADD_BUTTON/FUNC_ADD_SWITCH | 注册自定义按键 / 开关 handler |
FUNC_BUTTON_PRESSED/FUNC_BUTTON_MULTI_PRESSED | 处理单次按压 / 多次按压手势 |
FUNC_SET_DEVICE_POWER | 驱动设备专属继电器 |
FUNC_MQTT_DATA | 检查/处理原始 MQTT 载荷 |
FUNC_SERIAL | 处理主串口收到的字节 |
FUNC_COMMAND | 通用命令分发(驱动自定义命令) |
FUNC_COMMAND_SENSOR | 处理Sensor<id>命令 |
FUNC_COMMAND_DRIVER | 处理Driver<id>命令 |
FUNC_RULES_PROCESS | 自定义规则展开 |
FUNC_SET_CHANNELS | 灯光通道更新 |
显示驱动还有一套独立的
FUNC_DISPLAY_*回调;哪个驱动/传感器/显示/能量/灯光槽位收到哪个回调、按什么顺序分发的权威表见 docs/API.md(仓库根目录 API.md 亦可供参考)。
七、命令上下文:XdrvMailbox
自定义命令 handler(FUNC_COMMAND、FUNC_COMMAND_SENSOR、FUNC_COMMAND_DRIVER)从全局结构XdrvMailbox读取输入,其定义位于 tasmota/tasmota.ino:
struct XDRVMAILBOX { bool grpflg; // 命令是否来自 group topic bool usridx; // 命令名是否带用户提供的数字后缀 uint16_t command_code; // 命令表索引 uint32_t index; // 命令的数字后缀(如 Power2 的 2),默认 1 uint32_t data_len; // data 的长度 int32_t payload; // 第一个参数的数值,非数值时为 -99 char *topic; // 命令来源的 MQTT topic(或 nullptr) char *data; // 原始参数字符串(可写) char *command; // 解析出的命令名 } XdrvMailbox;注意:
docs/Sensor-API.md展示的是该结构的旧版本,请以tasmota.ino中的当前定义为准。
用于构建 JSON 响应的辅助函数声明在 tasmota/tasmota_support/support_command.ino(实现见 support_command.ino):
void ResponseCmnd(void)— 开始{"<Command>":...响应;void ResponseCmndDone(void)— 输出{"<Command>":"Done"};void ResponseCmndNumber(int value)— 输出{"<Command>":<value>};void ResponseCmndIdxNumber(int value)— 输出{"<Command><index>":<value>};void ResponseCmndFloat(float value, uint32_t decimals)与ResponseCmndIdxFloat(...)— 浮点版本;int Response_P(const char* format, ...)— 替换响应缓冲区(位于 tasmota/tasmota_support/support.ino);int ResponseAppend_P(const char* format, ...)— 追加到响应缓冲区。
若要把缓冲区作为传感器遥测载荷发布:
void MqttPublishTeleSensor(void); // 发布 tele/<topic>/SENSOR八、持久化设置存储:TSettings
持久化设置保存在 flash 中,结构体为TSettings,定义于 tasmota/include/tasmota_types.h,全局实例通过指针Settings访问(声明于 tasmota/tasmota.ino):
extern TSettings* Settings; // 见 tasmota.ino几个关键设计点:
- 大多数字符串字段(Wi-Fi SSID/密码、MQTT host/user/password/topic、hostname、friendly name、NTP 服务器等)不是结构体直接成员,而是存于单一
text_pool[]中,通过索引经SettingsText()/SettingsUpdateText()访问(实现见 tasmota/tasmota_support/settings.ino); - 合法索引集合为
enum SettingsTextIndex(见 tasmota/include/tasmota.h),例如SET_OTAURL、SET_MQTT_HOST、SET_HOSTNAME、SET_DEVICENAME等; SetOption32..SetOption49存储在Settings->param[PARAM8_SIZE];- 大量标志位分布在
Settings->flag、Settings->flag2(类型SysMBitfield1)等位域中(typedef 见tasmota_types.h)。
读写访问模式:
const char* host = SettingsText(SET_MQTT_HOST); SettingsUpdateText(SET_MQTT_HOST, "broker.example.com"); uint16_t period = Settings->tele_period; Settings->tele_period = 300;不要凭旧版本固件的记忆臆造字段名——始终以
tasmota_types.h与SettingsTextIndex枚举为准。
九、日志系统
日志级别定义在 tasmota/include/tasmota.h 的enum LoggingLevels:
enum LoggingLevels { LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE };主日志函数(位于 tasmota/tasmota_support/support.ino):
void AddLog(uint32_t loglevel, PGM_P formatP, ...);格式串要求来自 PROGMEM,通常用PSTR(...)包裹:
AddLog(LOG_LEVEL_INFO, PSTR("MyDriver: value=%d"), value);不存在AddLog_P/AddLog_P2宏——直接调用AddLog即可。
条件调试宏
定义于 tasmota/include/tasmota_globals.h,由编译期#define门控:
#ifdef DEBUG_TASMOTA_CORE #define DEBUG_CORE_LOG(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__) #else #define DEBUG_CORE_LOG(...) #endif #ifdef DEBUG_TASMOTA_DRIVER #define DEBUG_DRIVER_LOG(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__) #else #define DEBUG_DRIVER_LOG(...) #endif #ifdef DEBUG_TASMOTA_SENSOR #define DEBUG_SENSOR_LOG(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__) #else #define DEBUG_SENSOR_LOG(...) #endif #ifdef DEBUG_TASMOTA_TRACE #define DEBUG_TRACE_LOG(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__) #else #define DEBUG_TRACE_LOG(...) #endif #ifdef USE_DEBUG_DRIVER #define SHOW_FREE_MEM(WHERE) ShowFreeMem(WHERE); #else #define SHOW_FREE_MEM(WHERE) #endif在user_config_override.h中定义对应的DEBUG_TASMOTA_*符号即可启用。没有CHECK_OOM宏。
堆内存诊断
自由堆可从标准 ESP 封装获取:
uint32_t free = ESP.getFreeHeap(); AddLog(LOG_LEVEL_DEBUG, PSTR("Free heap: %u"), free);Tasmota 另在 tasmota/tasmota_support/support_esp32.ino(ESP8266 侧为 support_esp8266.ino)提供自有辅助函数(如ESP_getFreeHeap()、ESP_getMaxAllocHeap()),具体集合请以所在分支的该文件为准。
十、I2C 辅助 API
定义于 tasmota/tasmota_support/support_a_i2c.ino。所有读写辅助函数都带可选bus参数(默认0);双 I2C 总线设备上,次总线传1。
1. 总线管理
bool I2cBegin(int sda, int scl, uint32_t bus = 0, uint32_t frequency = 100000); bool I2cSetClock(uint32_t frequency = 0, uint32_t bus = 0); bool I2cReset(uint32_t bus = 0); void I2cScan(uint8_t bus = 0); // 把发现的地址打印到响应缓冲区2. 读操作(成功返回true)
bool I2cValidRead (uint8_t addr, uint8_t reg, uint8_t size, uint8_t bus = 0, bool sendStop = false); bool I2cValidRead8 (uint8_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cValidRead16 (uint16_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cValidRead16LE (uint16_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cValidRead24 (int32_t *data, uint8_t addr, uint8_t reg, uint8_t bus = 0);3. 读操作(直接返回值,无错误指示)
uint8_t I2cRead8 (uint8_t addr, uint8_t reg, uint8_t bus = 0); uint16_t I2cRead16 (uint8_t addr, uint8_t reg, uint8_t bus = 0); int16_t I2cReadS16 (uint8_t addr, uint8_t reg, uint8_t bus = 0); uint16_t I2cRead16LE (uint8_t addr, uint8_t reg, uint8_t bus = 0); int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg, uint8_t bus = 0); int32_t I2cRead24 (uint8_t addr, uint8_t reg, uint8_t bus = 0);不存在
I2cReadS32/I2cReadS32_LE/I2cValidReadS32/I2cValidReadS32_LE——需要 32 位读取时请用I2cReadBuffer自行拼装。
4. 写操作(成功返回true)
bool I2cWrite0 (uint8_t addr, uint8_t reg, uint8_t bus = 0); bool I2cWrite8 (uint8_t addr, uint8_t reg, uint32_t val, uint8_t bus = 0); bool I2cWrite16(uint8_t addr, uint8_t reg, uint32_t val, uint8_t bus = 0); bool I2cWrite (uint8_t addr, uint8_t reg, uint32_t val, uint8_t size, uint8_t bus = 0);没有I2cWrite16LE辅助函数。
5. 缓冲传输(注意:成功返回false、出错返回true,约定相反)
bool I2cReadBuffer (uint8_t addr, int reg, uint8_t *reg_data, uint16_t len, uint8_t bus = 0); bool I2cReadBuffer0(uint8_t addr, uint8_t *reg_data, uint16_t len, uint8_t bus = 0); bool I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len, uint8_t bus = 0);6. 驱动注册表 / 探测
bool I2cActive (uint32_t addr, uint8_t bus = 0); void I2cSetActive (uint32_t addr, uint8_t bus = 0); void I2cResetActive (uint32_t addr, uint8_t bus = 0); void I2cSetActiveFound (uint32_t addr, const char *types, uint8_t bus = 0); bool I2cSetDevice (uint32_t addr, uint8_t bus = 0); bool I2cEnabled (uint32_t i2c_index);I2cEnabled(XI2C_<n>)是 I2C 传感器回调函数开头的首选门控(替代已废弃的i2c_flg全局变量),其实现位于 tasmota/tasmota_xx2c_global/xx2c_interface.ino。底层地址占用表由I2cActive()/I2cSetDevice()维护(见 support_a_i2c.ino):I2cSetDevice先检查地址是否已被其他驱动认领,未认领才发起总线探测,而I2cSetActiveFound会在成功后登记地址并打印I2C: <types> found at 0x.. on bus<n>日志。
7. 标准探测模式
void MySensorDetect(void) { for (uint32_t bus = 0; bus < MAX_I2C; bus++) { for (uint32_t i = 0; i < MY_SENSOR_ADDR_NUM; i++) { uint8_t addr = MY_SENSOR_BASE_ADDR + i; if (!I2cSetDevice(addr, bus)) { continue; } // 已被其他驱动认领则跳过 uint8_t id; if (I2cValidRead8(&id, addr, MY_SENSOR_ID_REG, bus) && id == MY_SENSOR_ID_VAL) { I2cSetActiveFound(addr, "MySensor", bus); // ... 保存 addr/bus,标记已检测 ... return; } } } }该模式先在多总线上遍历候选地址,用I2cSetDevice抢占式认领,再读设备 ID 寄存器校验,最后用I2cSetActiveFound登记并记日志——这也是仓库中xsns_118_ags02ma.ino、xsns_112_ens210.ino等 I2C 传感器普遍遵循的探测套路。
十一、开发速查:把新传感器接入固件的完整路径
- 起名与编号:在
tasmota_xsns_sensor/下新建xsns_XX_<name>.ino,取一个尚未占用的编号(仓库现有传感器已用到xsns_127,如 xsns_127_esp32_sensors.ino,新模块请避开已有编号); - 实现回调:按上文骨架导出
bool XsnsXX(uint32_t function),在FUNC_INIT中探测、FUNC_EVERY_SECOND中读数、FUNC_JSON_APPEND/FUNC_WEB_SENSOR中上报; - 声明 ID 宏:
#define XSNS_XX XX,并把&XsnsXX按编号顺序追加进 xsns_interface.ino 的函数指针表; - 配置编译开关:模块整体用
#ifdef USE_<feature>包裹,并在 tasmota/user_config_override_sample.h 参考模板中添加对应#define; - 本地化字符串:Web 端显示串尽量复用 tasmota/include/i18n.h 中已有的
HTTP_SNS_*常量; - 命令与设置:需要自定义命令时在
FUNC_COMMAND_SENSOR/FUNC_COMMAND_DRIVER中读XdrvMailbox,用ResponseCmnd*系列函数回包;需要持久化数据时通过Settings/SettingsText读写。
十二、进一步阅读
- API.md — 驱动/传感器回调 ID 权威参考(
docs/API.md的同内容版本亦在仓库中) - tasmota/include/tasmota.h — 主要枚举与常量(
XsnsFunctions、LoggingLevels、SettingsTextIndex) - tasmota/include/tasmota_types.h —
TSettings结构与其他类型 - tasmota/include/i18n.h — Web 传感器显示字符串常量
- tasmota/user_config_override_sample.h — 编译期配置覆盖模板
- platformio_tasmota_env.ini 与 platformio_tasmota_env32.ini — ESP8266 / ESP32 平台编译环境
- TEMPLATES.md — GPIO 模板总表
【免费下载链接】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),仅供参考