0

I have this code:

#include <Arduino.h>

#include "esp_timer.h"

//volatile int iCounter; <-- Uncommenting this line makes iCounter update in myTestFn()

class Test {
    public:       

        Test(void) {
            esp_timer_handle_t testTimer;
            esp_timer_create_args_t timerConfig;
            timerConfig.arg = this;
            timerConfig.callback = reinterpret_cast<esp_timer_cb_t>( testFn );
            timerConfig.dispatch_method = ESP_TIMER_TASK;
            timerConfig.name = "Test_Timer";
            esp_timer_create( &timerConfig, &testTimer );
            esp_timer_start_periodic( testTimer, 1000000 );
        }
        
        static void testFn(void *arg) {
          Test *obj = (Test *)arg;
          obj->myTestFn();
        }
          
        void myTestFn(void) {
            iCounter = iCounter +1;
            Serial.printf("Test succeeded! Counter: %d Time elapsed: %lu.%03lu sec\n", iCounter, millis() / 1000, millis() % 1000);
        }
    private:

        volatile int iCounter; // <-- This does not work, iCounter does not update in myTestFn()
};

void setup() {

    Serial.begin(115200);
    while(!Serial);
    Test justATestObject;
}

void loop() {
}

I expected the iCounter variable to be indexed with each call to myTestFn(), but the value remains 1.

When I move the declaration of iCounter outside the class Test{} it does update (which seems odd to me). How do I get "Test" class member variables to update inside myTestFn()?

1 Answer 1

0

You almost had the problem in hand and tried to program around it. This is one of those painful impedance mismatches between the C-oriented SDK and C++ dev.

Your problem is that you don't have a class instance from the interrupt context. The interrupt callback is a C function. Your interrupt callback is C++ and because you're using class data, you need a 'this' pointer that you don't have because the interrupt doesn't have a class instance. It works when you move the counter outside the class because it's then a global and not this->iCounter (remember that the 'this' is optional in even vaguely modern C++.)

There are two ways around this that I can think of. I'm typing from memory and the syntax is finicky, so this is more of a "launchpad" answer than a "paste this into your code" answer.

  1. Register a lambda for the interrupt handler and capture [this]. The syntax is something vaguely like: timerConfig.callback = []{Test:myTestFn};
  2. Use bind() to get a pointer bundle that includes the function pointer and the [this] reference. timerConfig.callback = std::bind(&Test::myTestFn, this)

I'm pretty sure that the answer is in this approximate space, though I surely have the precise C++ spellings wrong. Key words like PMF (pointer to [static] member function), capture this, and, generally, interrupt handling in C++ should help further google-fu.

Remember, too, that data that can be touched by an interrupt handler on ESP32 needs to be marked IRAM_ATTR. If you get a (LoadProhibited). Exception was unhandled. when trying to access your instance data, you'll know you've run afoul of this. Running printf in an interrupt handler is a pretty brave thing to do.

Sign up to request clarification or add additional context in comments.

1 Comment

I tried searching for "lambda interrupt..." and "PFM..." and it seems like this is the solution to my problem, but the examples in those search results are beyond my current C++ programming comprehension level. I have reverted back to my original approach of watching the value of millis() in the main loop() function. I do appreciate the help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.