Estensione C per MicroPython su Raspberry Pi Pico

Questa è una guida su come scrivere ed eseguire un'estensione C di base per MicroPython sul Raspberry Pi Pico. Tratterò le ragioni per estendere MicroPython, come scrivere il modulo di estensione, come compilarlo e come installarlo sul Pico. Se volete leggere di più su questo argomento, fate riferimento a https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Introduzione

Perché estendere MicroPython?

Ci possono essere molte ragioni per cui potreste voler estendere MicroPython. Poiché è una versione ridotta di Python, MicroPython non funziona molto bene quando si tratta di compiti computazionalmente pesanti (simili a Python). Il C, d'altra parte, è davvero adatto per questi compiti. Un'estensione C per Micropython permette di eseguire questi calcoli attraverso il linguaggio C e utilizzare i risultati in MicroPython. Questo può velocizzare alcuni processi di un ordine di grandezza.

Una seconda ragione potrebbe essere che volete interfacciare dell'hardware che usa un'API C dall'interno di MicroPython. 

Tipi di estensioni C di MicroPython

Ci sono due modi diversi per estendere MicroPython: moduli C esterni e codice macchina nativo in file .mpy.

Per eseguire un modulo C esterno è necessario ricompilarlo nel firmware MicroPython. Questo è un processo piuttosto complicato, ecco perché questa guida si concentrerà sulla seconda alternativa. Se siete comunque interessati a questo approccio, potete leggerne di più su queste pagine web:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

Il .mpy è un file che memorizza il codice macchina nativo compilato e può essere collegato dinamicamente. Questo lo rende molto più flessibile poiché non è necessario ricompilare l'intero firmware MicroPython.

Scrivere il modulo di estensione

Scrivere il modulo di estensione è la parte più semplice dell'estensione di MicroPython. Vi mostrerò come farlo guardando un semplice esempio. Per iniziare, creeremo una nuova directory chiamata factfib. Aprite una nuova finestra di terminale, fate un cd nella nuova directory e create un nuovo file chiamato fattofib.c. Se avete creato la directory su un Raspberry Pi sotto Dekstop, il comando si presenta così.

cd ~/Desktop/factfib

touch factfib.c

Aprire fattofib.c nell'editor di testo preferito e inserire il seguente codice:

#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
}

Lasciatemi spiegare il codice qui sopra: In MicroPython e Python, ogni variabile è un oggetto. Quindi in C le rappresentiamo come mp_obj_t. Ma un programma C può lavorare solo con variabili di tipo C, ecco perché abbiamo bisogno di trasformare l'oggetto in un tipo C. Questo si chiama Marshalling e MicroPython fornisce funzioni per estrarre qualsiasi tipo C da un oggetto.

Questa pagina web di Oliver Robson è davvero utile per Marshalling https://mpy-c-gen.oliverrobson.tech/. Inoltre potete trovare esempi di Marshalling nella documentazione di MicroPython https://docs.micropython.org/en/latest/develop/natmod.html. Vi consiglio di leggere questo articolo se avete bisogno di ulteriori spiegazioni.

Costruire l'estensione

Costruiamo l'estensione in un file .mpy usando un Makefile. Ma abbiamo bisogno di impostare alcune cose prima. Assicuratevi che il vostro terminale sia ancora aperto nella cartella factfib directory. Poi clonate il repository GitHub di 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'

Queste installazioni richiederanno un po' di tempo, se volete risparmiare tempo e sapete cosa state facendo, avete solo bisogno di clonare le directory /py e /tools dal repository micropython e metterle dentro un micropython all'interno della cartella factfib (saltare il secondo comando).

Mentre l'installazione è in esecuzione possiamo usare il tempo per scrivere il Makefile. Eseguite semplicemente

touch Makefile

e aprire il file appena creato in un editor di testo.

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

Avrete notato che abbiamo impostato il parametro ARCH variabile a armv7m anche se il pico è armv6m. Dobbiamo fare questo perché il armv6m non è (al momento in cui sto scrivendo questo articolo) supportata dall'architettura dynruntime.mk strumento.

Ma fortunatamente possiamo ancora compilare un'estensione funzionante per la Pico. Abbiamo solo bisogno di modificare il file dynruntime.mk che dovrebbe trovarsi sotto micropython/py/dynruntime.mk. Apritelo e sotto Architecture configuration in armv7m trovate la variabile CFLAGS (dovrebbe essere la linea 64). Cambiate -mcpu=cortex-m3-mcpu=cortex-m0 e salvare il file.

cambia -mcpu=cortex-m3 in -mcpu=cortex-m0

Poi, dopo che tutto è stato installato, eseguite

make

e il fattofib.mpy sarà creato.

Installare e testare l'estensione

L'ultimo passo è l'installazione e il test. Per questo utilizziamo il filesystem che Micropython crea sul Raspberry Pi Pico. Per prima cosa dobbiamo flashare il Pico con il firmware MicroPython. Il modo più semplice per farlo è usare l'IDE Thonny (preinstallato su Raspbian/Raspberry Pi OS).

Se non state usando un Raspberry Pi per programmare il vostro Pico o se non avete mai usato MicroPython sul Pico prima d'ora, fate riferimento alla guida per iniziare con MicroPython di Raspberry Pi https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

Se stai usando Windows dovresti dare un'occhiata a questo post sul Raspberry Pi Pico e MicroPython su Windows https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

Chiudete Thonny dopo aver flashato la Pico con il firmware MicroPython e installate rshell. Usiamo rshell per accedere al filesystem dalla linea di comando.

sudo pip3 install rshell
rshell --version

Questo stampa la versione rhsell se tutto è impostato correttamente. Poi scollegate la pico e collegatela di nuovo (senza tenere premuto il pulsante BOOTSEL) per assicurarvi che nulla stia cercando di accedere al file system. Ora abbiamo bisogno di eseguire

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

cp factfib.mpy /pyboard/factfib.mpy

Per testare l'installazione potreste usare rshell o l'IDE di Thonny per salvare il vostro .py sulla Pico, ma dimostrerò come usare l'interprete MicroPython sul minicom.

Quindi prima premi Ctrl+c per lasciare rshell e poi installare minicom tramite

sudo apt install minicom

ora possiamo accedere al REPL di Picos (ciclo Read-eval-print)

minicom -o -D /dev/ttyACM0

Le tre parentesi angolari di chiusura >>> indicano che l'interprete MicroPython è in esecuzione. Ora possiamo importare la nostra estensione e lavorare con essa come al solito.

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

Congratulazioni! Avete creato la vostra prima estensione MicroPython C per Pico! Il passo successivo è imparare a scrivere e compilare moduli più complessi. Un buon modo per iniziare è quello di testare il funzionamento del Marshalling di liste e detti (vedere la pagina web Marshalling di cui sopra).

Se avete domande su questo post, non esitate a scrivere un commento.

7 commenti

  1. PeterB in Novembre 26, 2021 il 8:29 am

    A parte l'errore nel programma C, ha funzionato benissimo. Grazie.

    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);
    ritorna mp_obj_new_int(rslt);
    }

  2. XMC.pl in Giugno 1, 2022 il 3:31 am

    Come configurare joomla che può recuperare i dati da mysql?

  3. Lynggaard in Gennaio 8, 2023 il 8:09 pm

    Ho provato a fare quanto sopra, ma diverse cose non funzionano come descritto:

    - Ho seguito le istruzioni di cui sopra, ho preso micropython, ho modificato il makefile come descritto
    - Lo faccio (ma ottengo un file factfib.native.mpy invece di factfib.mpy)
    - Copio il file factfib.native.mpy nel pico come factfib.mpy
    (Ho provato con rshell ma ho ottenuto un "timed out o errore nel trasferimento a remote: b"", ma ho potuto copiarlo usando Thonny)
    - Posso importare il modulo (in thonny o in rshell - lo stesso) senza errori
    - Provo ad eseguire "ff.fibonacci(5)" ma ottengo:

    "Traceback (ultima chiamata):
    File "", riga 1, in
    AttributeError: l'oggetto 'modulo' non ha l'attributo 'fibonacci'".

    Cosa sbaglio?

    • paopao69 in Marzo 22, 2023 il 5:59 pm

      Alla fine sei riuscito a farlo funzionare?

  4. Paulo in Febbraio 9, 2024 il 1:44 am

    Ora è possibile compilare per armv6, quindi basta cambiare questa riga nel Makefile e non c'è bisogno di cambiare il codice micropython
    ARCH = armv6m

    Ho avuto alcuni errori nella compilazione di factfib.c, dove sono le correzioni:

    // manca il " alla fine.
    #include "py/dynruntime.h"

    // convertire il valore di ritorno in 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);
    ritorna mp_obj_new_int(rslt);
    }

    • Adam in Febbraio 9, 2024 il 9:22 am

      Grazie per le informazioni, Paulo!

Lascia un commento