Call python function from c code in a .mpy file

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
wolfc01
Posts: 4
Joined: Mon Nov 10, 2014 7:51 pm

Call python function from c code in a .mpy file

Post by wolfc01 » Sat Feb 20, 2021 4:19 pm

Hi

Can somebody provide me with an example of an runtime importable .mpy file containing c code which calls a micropython function?

I see examples in the codebase: e.g. in pin.c but no examples which are coded in an .mpy file.

Best regards
Carl

wolfc01
Posts: 4
Joined: Mon Nov 10, 2014 7:51 pm

Re: Call python function from c code in a .mpy file

Post by wolfc01 » Sun Feb 21, 2021 2:23 pm

Hi,

as a follow up, I created the following code. This code issues a callback, previously defined, into the micropython VM.

It "seems to work", but I have the suspicion that i'm overlooking and missing things, e.g. lock the VM to begin with...
Can somebody elaborate on this?

The micropython manual states: (quote) "It is thus not possible to simply call some arbitrary HAL/OS/RTOS/system function, for example."

If I want to call a HAL function, can this be done by extending the nativeglue.h, mpy_ld.py and dynruntime.h?

Br
Carl.


Code: Select all

// Include the header file to get access to the MicroPython API
#include "py/dynruntime.h"

mp_obj_t g_callback;

// This is the function which will be called from Python, which callbacks into VM using the g_callback
// which has to be set prior to call this function.
STATIC mp_obj_t test() {
    if (g_callback != mp_const_none)
    {
        mp_call_function_n_kw(g_callback, 0, 0, NULL);
    }
    else{
        mp_printf(&mp_plat_print, "no valid callback set!");
    }
    return mp_const_none;
}

STATIC mp_obj_t set_callback(mp_obj_t callback) {
    g_callback = callback;
    mp_printf(&mp_plat_print, "set callback=%p\n", callback);
    return mp_const_none;
}

// Define a Python reference to the function above
STATIC MP_DEFINE_CONST_FUN_OBJ_0(test_obj, test);

//Define a Python reference to a function to set a callback
STATIC MP_DEFINE_CONST_FUN_OBJ_1(set_callback_obj, set_callback);

// This is the entry point and is called when the module is imported
mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
    // This must be first, it sets up the globals dict and other things
    MP_DYNRUNTIME_INIT_ENTRY
    g_callback = mp_const_none;
    // Make the function available in the module's namespace
    mp_store_global(MP_QSTR_test, MP_OBJ_FROM_PTR(&test_obj));
    mp_store_global(MP_QSTR_set_callback, MP_OBJ_FROM_PTR(&set_callback_obj));

    // This must be last, it restores the globals dict
    MP_DYNRUNTIME_INIT_EXIT
}

User avatar
jimmo
Posts: 2244
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Call python function from c code in a .mpy file

Post by jimmo » Mon Feb 22, 2021 3:41 am

wolfc01 wrote:
Sun Feb 21, 2021 2:23 pm
It "seems to work", but I have the suspicion that i'm overlooking and missing things, e.g. lock the VM to begin with...
What you have will work, except for that it will have issues with garbage collection. The problem is that the GC doesn't know about the g_callback global (as it's not in the heap), and so the callback object that it points to (which will be on the heap) might get garbage collected. In general (outside of dynamic native modules), the way this is solved is via root pointers.

For a dynamic native module, I think the closest thing you can do is to stash g_callback onto the module itself (i.e. using setattr). Or if it was a member of a class, and you could be sure that the class instance's lifetime was long enough.

When you say "lock the VM" I'm guessing you're referring to invoking the callback asynchronously (for example, if you're invoking it because of a hardware IRQ or something).

Have a look at utils/mpirq.c and the mp_irq_handler function. For a "hard" IRQ (i.e. one that is called asynchronously) then you need to do a few things. If you want to avoid this, then you can use the scheduler (to implement a "soft" callback) instead via mp_sched_schedule. (I'm not sure off the top of my head though if this is available from dynruntime.h...).
wolfc01 wrote:
Sun Feb 21, 2021 2:23 pm
If I want to call a HAL function, can this be done by extending the nativeglue.h, mpy_ld.py and dynruntime.h?
Yes. Probably the best reference for this would be https://github.com/micropython/micropython/pull/5711 which is exploring how to provide this for the ESP32 IDF functions.

In either case, this the dynamic native module feature was really designed for offloading self-contained computation into C code... It would be really great to support writing low-level HAL drivers and things like callbacks, but that hasn't really been designed into this feature yet.

Post Reply