Extensión C para MicroPython en Raspberry Pi Pico

Esta es una guía sobre cómo escribir y ejecutar una extensión básica de C para MicroPython en el Raspberry Pi Pico. Cubriré las razones para extender MicroPython, cómo escribir el módulo de extensión, cómo compilarlo y cómo instalarlo en el Pico. Si quieres leer más sobre este tema, por favor consulta https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Introducción

¿Por qué ampliar MicroPython?

Puede haber múltiples razones por las que quiera extender MicroPython. Dado que es una versión recortada de Python, MicroPython no se desempeña muy bien cuando se trata de tareas computacionalmente pesadas (similar a Python). Por otro lado, C es realmente adecuado para tales tareas. Una extensión C para Micropython le permite realizar esos cálculos a través del lenguaje C y utilizar los resultados en MicroPython. Esto puede acelerar algunos procesos en un orden de magnitudes.

Una segunda razón podría ser, que usted quiere interconectar algún hardware que utiliza una API C desde dentro de MicroPython. 

Tipos de extensiones de MicroPython C

Hay dos formas diferentes de extender MicroPython: módulos C externos y código máquina nativo en archivos .mpy.

Para ejecutar un módulo C externo es necesario recompilarlo en el firmware de MicroPython. Este es un proceso bastante complicado, por lo que esta guía se centrará en la segunda alternativa. Sin embargo, si está interesado en este enfoque, puede leer más sobre él en estas páginas web:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

El .mpy es un archivo que almacena el código máquina nativo compilado y puede ser enlazado dinámicamente. Esto lo hace mucho más flexible ya que no es necesario recompilar todo el firmware de MicroPython.

Escribir el módulo de ampliación

Escribir el módulo de extensión es la parte más fácil de extender MicroPython. Le mostraré cómo hacerlo viendo un ejemplo sencillo. Para empezar, crearemos un nuevo directorio llamado factfib. Abre una nueva ventana de terminal, entra en el nuevo directorio y crea un nuevo archivo llamado factfib.c. Si has creado el directorio en una Raspberry Pi bajo Dekstop, el comando tiene el siguiente aspecto.

cd ~/Desktop/factfib

touch factfib.c

Abrir factfib.c en su editor de texto favorito e introduzca el siguiente código:

#include "py/dynruntime.h

STATIC mp_int_t factorial_helper(mp_int_t x) {
   if (x < 1) {
      return 1;
   }
   return x*factorial_helper(x-1);
}

STATIC mp_obj_t factorial(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = factorial_helper(x);
   return rslt;
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);

STATIC mp_int_t fibonacci_helper(mp_int_t x) {
   if (x <= 0) {
      return 0;
   }
   else if (x == 1) {
      return 1;
   }
   else {
      return (fibonacci_helper(x-1) + fibonacci_helper(x-2));
   }
}

STATIC mp_obj_t fibonacci(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = fibonacci_helper(x);
   return mp_obj_new_int(rslt);
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci);

mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
   MP_DYNRUNTIME_INIT_ENTRY

   mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
   mp_store_global(MP_QSTR_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj));

   MP_DYNRUNTIME_INIT_EXIT
}

Permítame explicar el código anterior: En MicroPython y Python, cada variable es un objeto. Por lo tanto en C las representamos como mp_obj_t. Pero un programa en C sólo puede trabajar con variables de tipo C, por lo que necesitamos transformar el objeto en un tipo C. Esto se llama Marshalling y MicroPython proporciona funciones para extraer cualquier tipo C de un objeto.

Esta página web de Oliver Robson es realmente útil para Marshalling https://mpy-c-gen.oliverrobson.tech/. Además, puede encontrar ejemplos de Marshalling en la documentación de MicroPython https://docs.micropython.org/en/latest/develop/natmod.html. Le aconsejo que lea este artículo si necesita más explicaciones.

Construir la extensión

Construimos la extensión en un archivo .mpy utilizando un Makefile. Pero primero tenemos que configurar algunas cosas. Asegúrate de que tu terminal sigue abierta en el factfib directorio. A continuación, clone el repositorio GitHub de MicroPython.

sudo apt-get install build-essential libreadline-dev libffi-dev git pkg-config gcc-arm-none-eabi libnewlib-arm-none-eabi

git clone --recurse-submodules https://github.com/micropython/micropython.git

pip3 install 'pyelftools>=0.25'

Estas instalaciones llevarán un tiempo, si quieres ahorrar tiempo y sabes lo que estás haciendo, sólo tienes que clonar el directorio /py y /tools del repositorio de micropython y ponerlos dentro de un micropython dentro de la carpeta factfib (omita el segundo comando).

Mientras se ejecuta la instalación podemos aprovechar el tiempo para escribir el Makefile. Simplemente ejecuta

touch Makefile

y abra el archivo recién creado en un editor de texto.

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

Habrás notado que hemos puesto el ARCO variable a armv7m aunque el pico es armv6m. Tenemos que hacer esto porque el armv6m no es (en el momento en que estoy escribiendo este artículo) compatible con la arquitectura dynruntime.mk herramienta.

Pero por suerte aún podemos compilar una extensión que funcione para el Pico. Sólo tenemos que modificar el archivo dynruntime.mk que debe estar ubicado en micropython/py/dynruntime.mk. Ábrelo y bajo Configuración de la arquitectura en armv7m encuentra la variable CFLAGS (debería ser la línea 64). Cambie -mcpu=cortex-m3-mcpu=cortex-m0 y guardar el archivo.

cambiar -mcpu=cortex-m3 a -mcpu=cortex-m0

Luego, cuando todo esté instalado, ejecute

make

y el factfib.mpy se creará un archivo.

Instalar y probar la extensión

El último paso es instalar y probar. Para ello utilizamos el sistema de archivos que Micropython crea en la Raspberry Pi Pico. En primer lugar tenemos que flashear el Pico con el firmware MicroPython. la forma más fácil de hacer esto es utilizar el IDE Thonny (preinstalado en Raspbian / Raspberry Pi OS).

Si no está utilizando una Raspberry Pi para programar su Pico o si nunca ha utilizado MicroPython en el Pico, consulte la guía de iniciación a MicroPython de Raspberry Pi https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

Si usted está usando Windows debe revisar este blog Post sobre la Raspberry Pi Pico y MicroPython en Windows https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

Cierre Thonny después de haber flasheado su Pico con el firmware de MicroPython e instale rshell. Usamos rshell para acceder al sistema de archivos desde la línea de comandos.

sudo pip3 install rshell
rshell --version

Esto imprime la versión rhsell si todo está configurado correctamente. Luego desenchufa el pico y conéctalo de nuevo (sin mantener el botón BOOTSEL) para asegurarte de que nada está intentando acceder al sistema de archivos. Ahora tenemos que ejecutar

rshell -p /dev/ttyACM0 --buffer-size 512

cp factfib.mpy /pyboard/factfib.mpy

Para probar la instalación puede utilizar rshell o el IDE de Thonny para guardar su .py en el Pico, pero voy a demostrar cómo utilizar el intérprete MicroPython sobre el minicom.

Así que primero pulse Ctrl+c para salir de rshell y luego instalar minicom a través de

sudo apt install minicom

ahora podemos acceder al REPL de Picos (bucle de lectura-evaluación-impresión)

minicom -o -D /dev/ttyACM0

Los tres corchetes angulares de cierre >>> indican que el intérprete de MicroPython se está ejecutando. Ahora podemos importar nuestra extensión y trabajar con ella como de costumbre.

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

¡Enhorabuena! ¡Ha creado su primera extensión MicroPython C para el Pico! El siguiente paso es aprender a escribir y compilar módulos más complejos. Una buena manera de empezar es probando cómo funciona el Marshalling de listas y dicts (ver la página web de Marshalling de arriba).

Si tiene alguna pregunta sobre este post, no dude en escribir un comentario.

7 Comentarios

  1. PeterB el noviembre 26, 2021 a las 8:29 am

    Aparte del error en el programa c ha funcionado de maravilla. Gracias.

    STATIC mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj);
    mp_int_t rslt = factorial_helper(x);
    return mp_obj_new_int(rslt);
    }

  2. XMC.pl el junio 1, 2022 a las 3:31 am

    ¿Cómo configurar joomla para que pueda recuperar los datos de mysql?

  3. Lynggaard el enero 8, 2023 a las 8:09 pm

    He intentado hacer lo anterior, pero varias cosas no funcionan como se describe:

    - Seguí las instrucciones anteriores, conseguí micropython, modifiqué el makefile como se describe
    - Hago make (pero obtengo un archivo factfib.native.mpy en lugar de factfib.mpy)
    - Copio el archivo factfib.native.mpy al pico como factfib.mpy
    (Intenté con rshell pero obtuve un "timed out or error in transfer to remote: b"", pero pude copiarlo usando Thonny)
    - Puedo importar el módulo (en thonny o rshell - lo mismo) sin errores
    - Trato de ejecutar el "ff.fibonacci(5)" pero entonces me sale:

    "Traceback" (última llamada más reciente):
    Archivo "", línea 1, en
    AttributeError: 'module' object has no attribute 'fibonacci'"

    ¿Qué hago mal?

    • paopao69 el marzo 22, 2023 a las 5:59 pm

      ¿Has conseguido que funcione?

  4. Paulo el febrero 9, 2024 a las 1:44 am

    Ahora se puede compilar a armv6 por lo que sólo tiene que cambiar esta línea en el Makefile y no hay necesidad de cambiar el código micropython
    ARCH = armv6m

    Tuve algunos errores al compilar el factfib.c, ¿dónde están las correcciones:

    // falta la " al final.
    #include "py/dynruntime.h"

    // convertir el valor de retorno a mp_obj_t
    STATIC mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj);
    mp_int_t rslt = factorial_helper(x);
    return mp_obj_new_int(rslt);
    }

    • Adam el febrero 9, 2024 a las 9:22 am

      Gracias por la información, Paulo.

Deja un comentario