编写ESPHome自定义组件时使用Arduino定时器库遇错求助
问题描述
我正准备编写一个ESPHome自定义组件,但C++经验不足,在使用外部定时器库时遇到问题。为便于演示,我编写了一个简单的组件类:
class Test: public Component { public: auto t = timer_create_default(); void setup() override { ESP_LOGD("TEST", "Test setup called!"); t.every(1000, TestLog); } void loop() override { ESP_LOGD("TEST", "Test loop called!"); t.tick(); } bool TestLog(void *) { ESP_LOGD("TEST", "TestLOG!"); return true; } };
运行后出现第一个报错:
In file included from src\main.cpp:32:0: src\Test.h:7:35: error: non-static data member declared 'auto'
auto t = timer_create_default();
我参考了无类结构的示例,但不清楚如何在类中使用该库。若将返回类型改为Timer<>,会出现第二个报错:
src\Test.h: In member function 'virtual void Test::setup()':
src\Test.h:11:24: error: no matching function for call to 'Timer<>::every(int, )'
t.every(1000, TestLog);
我可以不用该定时器,仅在loop函数中处理逻辑,但我想了解错误原因。
错误原因及解决方法
第一个错误:非静态成员变量不能用auto声明
C++标准明确规定,非静态类成员变量不允许使用auto进行类型推导,必须显式指定变量类型。timer_create_default()返回的是Timer<>类型(带默认模板参数的定时器实例),因此需要将成员变量声明改为显式类型:
Timer<> t;
无需在声明时用t = timer_create_default()初始化,因为Timer<>的默认构造函数已经能创建可用实例,和timer_create_default()的效果完全一致。
第二个错误:非静态成员函数无法直接作为定时器回调
TestLog是类的非静态成员函数,它的实际签名是bool (Test::*)(void*),而Timer<>::every()要求的是普通函数指针或无额外隐含参数的可调用对象。非静态成员函数会隐含一个指向类实例的this指针,无法直接转换为普通函数指针,因此会出现匹配错误。
有两种常用解决方式:
- 将
TestLog改为静态成员函数
静态成员函数没有隐含的this指针,签名符合要求,但缺点是无法访问类的非静态成员:static bool TestLog(void *) { ESP_LOGD("TEST", "TestLOG!"); return true; } - 使用带捕获的lambda表达式
如果需要访问类的非静态成员,可以用捕获this的lambda作为回调,arduino-timer库支持直接接受这类可调用对象:void setup() override { ESP_LOGD("TEST", "Test setup called!"); t.every(1000, [this](void*) { ESP_LOGD("TEST", "TestLOG!"); // 此处可直接访问类的非静态成员 return true; }); }
修正后的完整代码
#include "esphome.h" #include <arduino-timer.h> class Test: public Component { public: Timer<> t; void setup() override { ESP_LOGD("TEST", "Test setup called!"); t.every(1000, [this](void*) { ESP_LOGD("TEST", "TestLOG!"); return true; }); } void loop() override { ESP_LOGD("TEST", "Test loop called!"); t.tick(); } };
内容的提问来源于stack exchange,提问作者Ondrej Havlicek

