bluerange-io / bluerange-mesh

BlueRange Mesh (formerly FruityMesh) - The first completely connection-based open source mesh on top of Bluetooth Low Energy (4.1/5.0 or higher)
https://bluerange.io/
Other
288 stars 109 forks source link

Intercept GPIO pin state change (interrupt) and execute code from main thread #165

Closed giowild closed 3 years ago

giowild commented 3 years ago

Hello, I implemented my own custom module which needs to execute code when a GPIO pin state changes (interrupt from external SPI device).

In old versions of Fruitymesh, I configured my GPIO pin as input, defined the interrupt handler to be as fast as possible, and performed code execution in the CheckAndProcess() function which I was calling from the main thread, from within the FruityHal::EventLooper() method, in this way:

static void interrupt_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
    sig_received = true;
}

void MyModule::initGpioCallback (int32_t pinNo) { 
    nrf_drv_gpiote_in_config_t pinConfig;
    pinConfig.sense = NRF_GPIOTE_POLARITY_HITOLO;
    pinConfig.pull = NRF_GPIO_PIN_PULLUP;
    pinConfig.hi_accuracy = true;
    u32 err =  nrf_drv_gpiote_in_init(pinNo, &pinConfig, interrupt_handler);

    //Enable the events
    nrf_drv_gpiote_in_event_enable(pinNo, true);
}

void MyModule::CheckAndProcess() {  // called from main loop
    if (sig_received ) {
        sig_received = false;

                // commands to execute each time signal is received
    }
}

However in new versions of Fruitymesh (>1.0), it seems that CheckAndProcess functions have been removed from FruityHal::EventLooper() .

Here are my questions:

1) Is "nrf_drv_gpiote_in_init" the best way to react to gpio interrupts routines in Fruitymesh? 2) What is the correct way of calling MyModule->CheckAndProcess(), as fast as possible (no timer handlers), from the main thread?

I found RegisterMainContextHandler and RegisterApplicationInterruptHandler methods in GlobalState.ccp, but I can't figure out exactly if they can be useful for my case and how to use them.

Thanks a lot! Giovanni

giowild commented 3 years ago

I found that the only way to make it work is to use Globalstate::RegisterMainContextHandler with a static function, while it doesn't work with non-static class methods:

void MyModule::ResetToDefaultConfiguration() {
    //  ...

    GS->RegisterMainContextHandler( mystaticfunc);

   //  ...
}

static void mystaticfunc() {  // to be called from main looper, flags are set true
    if (sig_received ) {
        sig_received = false;

               // call to the non-static method, (supposing MyModule is the last module registered) 
               // alternatively we can create a reference to the module upon initialization and then use it here
               ((MyModule*) (GS->activeModules[GS->amountOfModules-1]))->CheckAndProcess();
    }
}

If there is a better way to do this in Fruitymesh, I'd be happy to know

mariusheil commented 3 years ago

Hello, sorry for not responding on that. This is how it can be done. You can also use a static method, e.g. "static void MainContextHandler();" and then give it as "MyModule::MainContextHandler". Then, you should use "static_cast<MyModule*>(GS->node.GetModuleById(YOUR_MODULE_ID);" to get the module instance.