Extend the C module for MicroPython!
Published: 2020-10-27 10:13:33

When developing with the native MicroPython, there are some limitations you may encounter, for example, the official didn't provide you the features that you want, or some features do not meet your project needs. At this point, adding your C module to MicroPython is a good choice, and you can design any Python functions by your own.

To help developers easily add C modules, RT-Thread provides a handy accessibility tool [C binding code auto-generator](https://summerlife.github.io/RT-MicroPython-Generator/). The tool can automatically generate the interface layer between C code and MicroPython, for which developers can call the functions directly by simply adding functional code written in the C language to a specified location.

## The implementation principle of calling the C function in Python

C and Python are two completely different languages, how to use MicroPython to call the functions implemented by the C language confused many developers. Simply put, the very key point is to clearly express the **input parameters** and **output parameters** in the MicroPython source code in the form of C language. Here's an example to help you understand this, observe the following Python function:

```python
def add(a, b):
    return a + b
```

This function has two input parameters and returns a parameter. At this point, if you can represent the input and output parameters of the Python function in C language, you can add a C function to MicroPython.

### Add user function to MicroPython

Assuming that the parameter types of the above functions are int, the following sample functions can be obtained through the auto-generator:

```c
STATIC mp_obj_t add(
    mp_obj_t arg_1_obj,
    mp_obj_t arg_2_obj) {
    mp_int_t arg_1 = mp_obj_get_int(arg_1_obj);  /* The first INT parameter arg_1 obtained by Python. */
    mp_int_t arg_2 = mp_obj_get_int(arg_2_obj);  /* The second INT parameter arg_e obtained by Python. */
    mp_int_t ret_val;

    /* Your code start! */

    ret_val = arg_1 + arg_2;  /* Handles the input parameters of arg_1 and arg_2 and assigns the result to the return value ret_val */

    /* Your code end! */

    return mp_obj_new_int(ret_val);               /* Return the INT parameter ret_val to python */
}
MP_DEFINE_CONST_FUN_OBJ_2(add_obj, add);
```

The generator handles the input and output parameters of functions that need to export to MicroPython, and the developer only needs to write the appropriate code to handle the input parameters and assign the return value to the output parameters. By containing the header file, you can call the previously written C function to process the input parameters, or perform the corresponding actions based on the input parameters, the same principle works for adding the driver to control hardware.

The effect of calling the C function with Python is as follows:

```python
>>> import userfunc
>>> userfunc.add(666,777)
1443
```

### Add user modules to MicroPython

It's not hard to add user modules to MicroPython. First, get familiar with the process of adding C functions, then you can refer to this [Pull Request](https://github.com/RT-Thread-packages/micropython/pull/144) to add your module. This PR implements the functionality of adding the `userfunc` module to MicroPython, and you can register your modules with MicroPython in the same way, paying attention to reviewing four modified files in this PR in case of missing the details of the modifications.

3 steps are taken to add a C function in MicroPython that correspond to three actions in Output:

- Copy the automatically generated C language function of the same name to the **port/modules/user/moduserfunc.c** file.
- Register the function to user modules list**(_globals_table[])**.

- Append the QSTR associated with the function to file **port/genhdr/qstrdefs.generated.h**.

**Output**

```c
Step 1: Copy the following functions into a file
STATIC mp_obj_t my_function() {
    /* Your code start! */

    /* Your code end! */

     return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(my_function_obj, my_function);

Step 2: Copy the following code <NoComment> into the module registration list.
// STATIC const mp_rom_map_elem_t my_module_globals_table[] = { <----- Find this list in module.
//    { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_builtins) },
//----------------------------------------------------------------------------------------------------
    { MP_ROM_QSTR(MP_QSTR_my_function), MP_ROM_PTR(&my_function_obj) },
//----------------------------------------------------------------------------------------------------
// };

Step 3: Please append the following QSTR code after the <port/genhdr/qstrdefs.generated.h> file
QDEF(MP_QSTR_my_function, (const byte*)"\x6c\x0b" "my_function")
```

**RT-Thread Contact Info:**

 [Website](https://www.rt-thread.io/)  |  [Github](https://github.com/RT-Thread)  | [Twitter](https://twitter.com/rt_thread) |  [Facebook](https://www.facebook.com/RT-Thread-IoT-OS-110395723808463)   |   [Youtube](https://www.youtube.com/channel/UCdDHtIfSYPq4002r27ffqPw
)