Use lambda function as microcontroller interrupts handler - c++

For my microcontroller project I need custom timer with possibility to change interruption handler function. I created a Timer class for this.
I need to initialize ESP32 timer with function.
That is how I am trying to do this:
class Timer
{
private:
hw_timer_t* timer = nullptr;
std::function<void(void)>& onTimer;
public:
Timer(uint16_t intervalMs, std::function<void(void)>& newOnTimer): onTimer(newOnTimer)
{
timer = timerBegin(0, 40, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, intervalMs * 1000, true);
}
void start()
{
timerAlarmEnable(timer);
}
};
And timer initialization:
Timer t = Timer(250, []IRAM_ATTR(){
Serial.print("Tick ");
Serial.println(millis());
if(point)
{
point = false;
d.clearPixel(4, 4);
return;
}
point = true;
d.drawPixel(4,4);
});
But when I am launching it, I get:
sketch.ino: In constructor 'Timer::Timer(uint16_t, std::function<void()>&)':
sketch.ino:1161:35: error: cannot convert 'std::function<void()>*' to 'void (*)()'
timerAttachInterrupt(timer, &onTimer, true);
^~~~~~~~
In file included from /esp32/hardware/esp32/2.0.4/cores/esp32/esp32-hal.h:88,
from /esp32/hardware/esp32/2.0.4/cores/esp32/Arduino.h:36,
from sketch.ino.cpp:1:
/esp32/hardware/esp32/2.0.4/cores/esp32/esp32-hal-timer.h:39:53: note: initializing argument 2 of 'void timerAttachInterrupt(hw_timer_t*, void (*)(), bool)'
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
~~~~~~~^~~~~~~~~
sketch.ino: At global scope:
sketch.ino:1341:16: error: expected primary-expression before '(' token
Timer t = Timer(250, []IRAM_ATTR(){
^
In file included from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/hal/esp32/include/hal/cpu_ll.h:18,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/hal/include/hal/cpu_hal.h:16,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/esp_hw_support/include/esp_cpu.h:14,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/esp_hw_support/include/soc/cpu.h:14,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/esp_hw_support/include/soc/spinlock.h:11,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/freertos/port/xtensa/include/freertos/portmacro.h:42,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/freertos/include/freertos/portable.h:51,
from /esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/freertos/include/freertos/FreeRTOS.h:63,
from /esp32/hardware/esp32/2.0.4/cores/esp32/Arduino.h:33,
from sketch.ino.cpp:1:
sketch.ino: In lambda function:
/esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/esp_common/include/esp_attr.h:150:46: error: expected '{' before '__attribute__'
#define _SECTION_ATTR_IMPL(SECTION, COUNTER) __attribute__((section(SECTION "." _COUNTER_STRINGIFY(COUNTER))))
^~~~~~~~~~~~~
/esp32/hardware/esp32/2.0.4/tools/sdk/esp32/include/esp_common/include/esp_attr.h:23:19: note: in expansion of macro '_SECTION_ATTR_IMPL'
#define IRAM_ATTR _SECTION_ATTR_IMPL(".iram1", __COUNTER__)
^~~~~~~~~~~~~~~~~~
sketch.ino:1341:24: note: in expansion of macro 'IRAM_ATTR'
Timer t = Timer(250, []IRAM_ATTR(){
^~~~~~~~~
sketch.ino: At global scope:
sketch.ino:1341:24: error: expected ')' before '__attribute__'
Timer t = Timer(250, []IRAM_ATTR(){
~ ^
)
Error during build: exit status 1
I am not expert in C++, so I totally don't understand what I need to do there to fix it.
Could anyone who knows what to do tell me about this, please?
Thank you in advance.

The signature of timerAttachInterrupt is
void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
Like the error says, you cannot convert a std::function<void()>* to a void(*)(). While std::function<...> is a class that can wrap pretty much any callable object and store the data needed (eg. the variables captured in a lambda), function pointers are just simple addresses in memory.
If your lambdas are simple and don't capture anything, like the one in your example, std::function is overkill. You can just use void(*)() instead and the issue is solved.
Otherwise, if your lambdas do capture stuff, then you need std::function and perhaps some static member variables.
Since you seem to be using only one timer, you could do something like this:
class Timer
{
private:
hw_timer_t* timer = nullptr;
static std::function<void(void)> onTimer;
static void onTimerCaller()
{
onTimer();
}
public:
Timer(uint16_t intervalMs, std::function<void(void)>&& newOnTimer)
{
if (onTimer)
{
// another Timer already exists. Show an error, somehow
}
onTimer = std::move(newOnTimer);
timer = timerBegin(0, 40, true);
timerAttachInterrupt(timer, &onTimerCaller, true);
timerAlarmWrite(timer, intervalMs * 1000, true);
}
void start()
{
timerAlarmEnable(timer);
}
};
The most important changes are1:
Add a onTimerCaller static function (that can be converted to void(*)() and therefore used with timerAttachInterrupt) which calls onTimer;
onTimer is now static, so it can be accessed from onTimerCaller.
Also note that I tried to keep the class as simple as possible. It should also have copy/move constructors/operators and a destructor. Additionally, there's not really much reason to even use a class in this case. A namespace with some functions would be simpler and cleaner.
It is also possible to support multiple timers, but I believe this may be enough for your use-case. Let me know if you'd like to see a version for multiple timers.
1 I also did some minor changes, like moving the std::function into the class to avoid a dangling reference.

Related

Problem with creation of freeRTOS timer in ESP-IDF

I use ESP-IDF 4.2 and want to create a freeRTOS timer. My code is similar to what I have used successfully before but now I get a compilation error that I can't resolve. My code is based on the example in the ESP-IDF documentation on the xTimerCreate function. In the documentation an array of timers is created, whereas I want to create only one.
Here is my code:
void myTimerCallback(void){
std::cout << "test timer callback" << std::endl;
};
TimerHandle_t create_freeRTOSTimer(){
TimerHandle_t timerHandle;
int32_t Id = 0;
int durationTicks = 100;
timerHandle = xTimerCreate( "Timer", // Just a text name, not used by the kernel.
durationTicks, // The timer period in ticks.
pdTRUE, // The timer will auto-reload itself when it expires.
( void * ) Id, // unique id.
myTimerCallback // Callback when timer expires.
);
return timerHandle;
};
This is the compiler response:
error: invalid conversion from 'void ()()' to
'TimerCallbackFunction_t' {aka 'void ()(void*)'} [-fpermissive]
myTimerCallback // Each timer calls the same callback when it expires.
^~~~~~~~~~~~~~~ In file included from ../components/NiCMidi/src/../include/timer.h:46,
from ../components/NiCMidi/src/timer.cpp:26: C:/Users/Fred/esp-idf/components/freertos/include/freertos/timers.h:267:33:
note: initializing argument 5 of 'void* xTimerCreate(const char*,
TickType_t, UBaseType_t, void*, TimerCallbackFunction_t)'
TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed
for strings and single characters only. */
For testing I replaced the myTimerCallback in the call of xTimerCreate by NULL. Then the compiler does not complain.
Oops, I forgot to use a parameter in the definition of the callback function!
When I use void myTimerCallback(TimerHandle_t pxTimer) for the definition, everything is OK!

How to use std::async with void method with no parameters?

I'm assuming there's something very simple I'm missing about std::async. I'm trying to run 2 void methods asynchronously, with no return values.
#include <future>
class AsyncTestClass {
public:
void Initialize()
{
std::async(&AsyncTestClass::AsyncMethod1);
std::async(&AsyncTestClass::AsyncMethod2);
}
void AsyncMethod1()
{
//time consuming operation
}
void AsyncMethod2()
{
//time consuming operation
}
};
But get an error when calling my AsyncMethod1 or AsyncMethod2 within std:async:
Substitution failed: type 'typename std:conditional<sizeof....(ArgTypes) == 0, std::_Invoke_traits_Zero<void, typename std::decay.....is ill formed with _Fty = void (AsyncTestClass::*)(), _ArgTypes =
What is the proper usage of std:async with void, parameterless methods? The examples I see seem similar to how I'm using it, but it's not working for me.
AsyncTestClass::AsyncMethod1, being a non-static member function, can only be called if an instance of AsyncTestClass is supplied. You probably meant this:
std::async(&AsyncTestClass::AsyncMethod1, this)
This creates a std::future object whose value will be obtained by evaluating this->AsyncMethod1().
By the way, the return value of std::async should be assigned to a variable, otherwise the call will block. See std::async won't spawn a new thread when return value is not stored. If you have C++20, the compiler will catch this for you thanks to [[nodiscard]].

Arduino lambdas 'no suitable conversion function from' error

I have small Arduino project that using lambas:
typedef void(*keyboardKeyDelegate)(int);
void KeyboardKeyChange(int button, keyboardKeyDelegate onKeyChange);
void KeyboardKeyChange(int button, keyboardKeyDelegate onKeyChange)
{
// code...
}
void KeyboardKeyClick(int button, keyboardKeyDelegate onClick);
void KeyboardKeyClick(int button, keyboardKeyDelegate onClick)
{
// code...
}
.. next
bool r = true;
KeyboardKeyClick(KB_TOP, [&r](int state)
{
r = !r;
Serial.println("> KB_TOP CLICKED");
});
I have an error while compiling source:
no suitable conversion function from "lambda []void (int state)->void" to "keyboardKeyDelegate" exists
Maybe someone knows more about this error and help me to figured it out.
Your lambda has state and is thus not convertible to a functionpointer like the function argument.
You need to either remove the state from your lambda and recover it someway, or change the function to accept any callable by turning it into a template.

C++11 Lambda Functions inside member methods inherit scope

I've written a function foreach that accepts a lambda function ala:
void foreach(void (*p)(pNode))
{ /* ... */ }
Which works as intended if I pass a lambda function from the main loop:
int a = 5;
env.N().foreach
(
[&](pNode n)->void
{
n->tps(a);
}
);
However, if I try to call the same function from within a member method, the lambda function "inherits" the scope of the member function and generates a compiler error. For example, if I try to include it inside the member method of class Object named method(), I get the following error:
error: no matching function for call to ‘IDSet<Node>::foreach(Object::method()::<lambda(pNode)>)’
note: candidate is: void IDSet<T>::foreach(void (*)(IDSet<T>::pT)) [with T = Node, IDSet<T>::pT = pNode]
I realize this is the compiler being safe, since I could include instance-specific variables inside the lambda function, in which case the lambda would need to be scoped, however I'm wondering if it's possible to make this lambda "static".
I've tried a reinterpret_cast, however that gives me this error:
error: invalid cast from type ‘Object::method()::<lambda(pNode)>’ to type ‘void (*)(pNode)’
Specifying static before [&](pNode ... doesn't seem like valid syntax either.
Desperately, I also tried changing [&] to [=], [], [a], none of which worked.
Does anyone know if there is a way to do accomplish my goal of creating a "static" lambda function, or at any sort of lambda function that will be accepted for that matter?
Thanks!
Answer:
With help from Cat Plus Plus, I was able to turn my incorrect code:
void foreach(void (*p)(pT))
{
for(pTiter i = _map.begin(); i != _map.end(); i++)
{
(*p)(i->second);
}
}
into fully functional code:
void foreach(std::function<void(pT)>(p))
{
for(pTiter i = _map.begin(); i != _map.end(); i++)
{
p(i->second);
}
}
that does what I was looking for perfectly.
Well, you can not use pointers.
void foreach(std::function<void(pNode)>);

I am new to threads, What does this compile error mean?

Using C++.
pthread_t threads[STORAGE]; // 0-99
...
void run()
Error>>> int status = pthread_create(&threads[0], NULL, updateMessages, (void *) NULL);
if (status != 0)
{
printf("pthread_create returned error code %d\n", status);
exit(-1);
}
...
void ClientHandler::updateMessages(void *)
{
string reqUpdate = "91"; // Request for update
string recvMSG;
while (true)
{
sleep(5);
sending(sock,reqUpdate); // send
recvMSG = receiving(sock); // receive
QString output(recvMSG);
emit signal_chat(output, 0); // Print message to text box
}
}
...
Compile Error:
TCPClient.cpp:109: error: argument of type ‘void (ClientHandler::)(void*)’ does not match ‘void* (*)(void*)’
I can't figure out whats wrong.
Thanks in advance.
A pointer to a member function is different from a global function with the same signature since the member function needs an additional object on which it operates. Therefore pointers to these two types of functions are not compatible.
In this case this means that you cannot pass a member function pointer to pthread_create but only a pointer to a non-member (or static) function. A work around for this problem is to use the forth parameter of pthread_create to pass a pointer to a object to a global function which then calls the method of the passed object:
class ClientHandler {
public:
void updateMessages();
void run();
};
// Global function that will be the threads main function.
// It expects a pointer to a ClientHandler object.
extern "C"
void *CH_updateMessages(void *ch) {
// Call "real" main function
reinterpret_cast<ClientHandler*>(ch)->updateMessages();
return 0;
}
void ClientHandler::run() {
// Start thread and pass pointer to the current object
int status = pthread_create(&threads[0], NULL, CH_updateMessages, (void*)this);
...
}
It's nothing to do with threads, it's a normal C++ error, you're just passing an incompatible type of function pointer.
A function pointer is not the same as a member instance function pointer, even if their signature is the same; this is because there is an implicit reference to *this passed. You can't avoid this.
As pthread_create takes a free function, create a static function(is a free function) inside ClientHandler
static void Callback(void * this_pointer,int other_arg) {
ClientHandler* self = static_cast< ClientHandler*>(this_pointer);
self-> updateMessages(other_arg);
}
and call pthread_create as follows
pthread_create(&threads[0], NULL, &ClientHandler::Callback, (void *) pointer_to_ClientHandler,int other_arg);
That works because Callback is free function
YoLinux has a nice pthread tutorial that my help you in learning about threads.
As others have already said, the problem is that the signatures between the functions are different. Class member functions always have a "secret" extra parameter, the this pointer. So you can never pass a member function where a global function is expected. You can hack around this either with libraries such as Boost.Bind, or by making the function a static member of the class.
But the simplest, and most elegant solution is to use a different threading API.
Boost.Thread is a very nice threading library for C++ (pthreads is designed for C, and that's why it doesnt play well with C++ features such as class methods).
I'd recommend using that.
Your code could be rewritten as something like this:
class ClientHandler {
public:
ClientHandler(/* All the parameters you want to pass to the thread. Unlike pthreads you have complete type safety and can pass as many parameters to this constructor as you like */){...}
void operator()() // boost.thread calls operator() to run the thread, with no parameters. (Since all parameters were passed in the constructor and saved as member variables
{
string reqUpdate = "91"; // Request for update
string recvMSG;
while (true)
{
sleep(5);
sending(sock,reqUpdate); // send
recvMSG = receiving(sock); // receive
QString output(recvMSG);
emit signal_chat(output, 0); // Print message to text box
}
}
// whatever arguments you want to pass to the thread can be stored here as member variables
};
boost::threead_group gr; // can store all your threads here, rather than being limited to your fixed-size array
gr.create_thread(ClientHandler(/* construct a ClientHandler object with the parameters you like*/));
You're passing a member function instead of a global, normal, one.
Just define:
void updateMessages(void *) {
static ClientHandler c;
// use c..
}