Using a class's member function to handle EventQueue events?

I’m trying to write a class that will handle interrupts from button pushes using an EventQueue. I’ll need to send data over a BufferedSerial connection in response to the event so I can’t do it in the interrupt handler.

I’m completely stuck, likely because of my rusty C++ skills, on how I can construct a call to queue.event that actually compiles and results in a call to my class’s method. All of the examples I can find online are basic C examples where the event handler function is just a function, not a class member.

Keeping it simple if I have a class definition like this:

class ButtonManager
{
public:
    InterruptIn *_pin;
    void Init();
    void OnPress();
}

and the Init() method is:

void ButtonManager::Init()
{
    _pin = new InterruptIn(Button1);
   pin->fall(queue.event(???));
}

What replaces ??? to get the equivalent of this.OnPress() called? Thanks in advance!

What I currently have is this:

  _pin->fall(queue.event(this, &ButtonManager::OnPress));

In order to get this to compile I had to specify "platform.callback-nontrivial": true. In OnPress() I am setting the onboard LED to 1 and in OnRelease() I’m setting the onboard LED to 0.

When I run this and press the button the LED starts to flash on and off randomly :frowning:

Hello,

That mean MbedOS crash, so check the serial output for the report message.

I think, the examples fom EventQueue - API references and tutorials | Mbed OS 6 Documentation can help.

BR, Jan

Thanks for the reply. The EventQueue API documentation only has examples for basic C functions that aren’t part of a class. I’m specifically trying to learn how to use a class method as the event handler.

Thanks for the tip on the blinking LED, it is indeed crashing with a Fault exception :frowning:

Take a look to a simple test code bellow

Example code


#include "mbed.h" // MbedOS 6.10 (platform.callback-nontrivial": true), NucleoF429ZI

#define DEBUG 1

EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;
DigitalOut led2 (LED2);
DigitalOut led3 (LED3);

class ButtonManager
{
public:
    InterruptIn *_pin;
    void Init();
    void OnPress();
};

void ButtonManager::OnPress()
{
    led3 = !led3;
}

void ButtonManager::Init()
{
    _pin = new InterruptIn(BUTTON1);
   _pin->fall(queue.event(callback(this, &ButtonManager::OnPress)));
}

int main()
{
    debug_if(DEBUG, "Mbed OS version %d.%d.%d\n\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);

    t.start(callback(&queue, &EventQueue::dispatch_forever));

    ButtonManager test;
    test.Init();

    while (1) {
        ThisThread::sleep_for(500ms);
         led2 = !led2;
    }
}

BR, Jan

1 Like

Thanks for the simple example! I see a few differences from what I have currently, I will give it a try and report back. Much appreciated!

It works! Thanks @JohnnyK the code sample was just what I needed. Once I solved some other dumb pointer issues the event handler gets called correctly and the LED flashes.

I am glad it helped.

I also read your post about printf before update.
Just try to add \n at end of string.

Gl with your project.

BR, Jan

I had lots of other errors in my code relating to pointers/memory/etc :slight_smile: Once I got that resolved I was good!

hi @Neil_Enns,

i am attempting to do something similar and am getting the following error

Turn on Mbed configuration option ‘platform.callback-nontrivial’ to use more complex function objects

how do you turn on this configuration option?

thanks
Loay

never mind, i just worked it out haha

“projectFolder\mbed-os\platform\mbed_lib.json” changed the value for callback-nontrivial from false to true

lol
thanks
Loay

Hello,

It can be, but it is not correct solution because you will lose it every time you change MbedOS version.
Correct is to place content below to mbed_app.json file

{
    "target_overrides": {
        "*": {
            "platform.callback-nontrivial": true
        }
    }
}

If you do not have this file in your project, then just create it. That will override the original settings from mbed_lib.json file.

BR, Jan

where can i find this .json file?

It may looks like in this example (in the root of your project) - ARMmbed/mbed-os-example-blinky-baremetal: Bare metal blinky Mbed OS example (github.com)

For more info - The configuration system - Program setup | Mbed OS 6 Documentation

BR, Jan

ok so that compiled but didnt work as i expected.

so in my application i have a machine with 3 axes that are controlled by stepper motors so i built a class to control these axes. a “move” command will come in over the USB and my application will call the appropriate axis. when then move has been completed by the axis class i want an event to be raised ion the main where a handler will determine what to do next.

below is some of my code

main.cpp

EventQueue queue(32 * EVENTS_EVENT_SIZE);
Thread t;

void handleAxis(void)

{

serial.write("X Axis complete\n", 17);

}

int main()

{

t.start(callback(&queue, &EventQueue::dispatch_forever));

xAxis->emitComplete(queue.event(handleAxis));
xAxis->move(steps,speed,accel,dir);
}

axis.cpp

void axis::emitComplete(Callback<void()> func)

{

axisParams.status = "complete";

}

basically i was thinking of posting an event to the thread t in main using the “emitComplete()” function in the axis class.

your emitComplete() does not call func, it needs something like

  if (func) {
     func();
  }

I’ve written something very similar and I’m using EventFlags to signal the action complete. EventFlags can be set also from an ISR:

#define FLAG_POS_REACHED (1UL << 0)
#define FLAG_LIMITSWITCH (1UL << 1)

// timer ISR
void fnStepperPulse()
{
    if (--moveSteps) {
        // set next timer int if more steps to go
        hwTimer.start(chrono::microseconds {(int)roundf(abs(_cn - 3.75f))});
    } else {
        eventFlags.set(FLAG_POS_REACHED);
    }

    if (_motorDirection) {
        if (homePos) {          // check home switch
            moveSteps = 0;
            stepperPos = 0;      
            isHomed = true;      
            eventFlags.set(FLAG_LIMITSWITCH);
            return;
        }

// process steps
}


// for testing in controller task:
    fnStepperPulse();                           // start move

    uint32_t flags_read = 0;
    flags_read = eventFlags.wait_any(FLAG_POS_REACHED | FLAG_LIMITSWITCH);
    // move completed

PS:
please use 3x ` (backtick) for formatting source code

hi Johannes,

so is this part of your code in main()

It is where I finished the calculation of movement and after starting the timer ISR. It is re-triggering itself until stepping is finished.

1 Like

i tried the following but my handler is never called

void axis::emitComplete(Callback<void()> func)
{
    axisParams.status = "complete";
    if(func)
    {
        func();
    }
}

so i tried this and got an error

void axis::emitComplete(Callback<void()> func)
{
    axisParams.status = "complete";
    func();
}

this is the error i got



++ MbedOS Error Info ++
Error Status: 0x80FF0144 Code: 324 Module: 255
Error Message: Assertion failed: bool(*this)
Location: 0x800AE83
File: ./mbed-os/platform/include/platform\Callback.h+506
Error Value: 0x0
Current Thread: main Id: 0x20005F34 Entry: 0x800BC19 StackSize: 0x1000 StackMem: 0x200047C8 SP: 0x2004FE78
For more info, visit: https://mbed.com/s/error?error=0x80FF0144&tgt=NUCLEO_F746ZG
-- MbedOS Error Info --

= System will be rebooted due to a fatal error =
= Reboot count(=1) reached maximum, system will halt after rebooting =

any ideas