Extensão C para MicroPython em Raspberry Pi Pico

Este é um guia sobre como escrever e executar uma extensão C básica para MicroPython no Raspberry Pi Pico. Vou cobrir as razões para a extensão do MicroPython, como escrever o módulo de extensão, como compilá-lo e como instalá-lo no Pico. Se você quiser ler mais sobre este tópico, por favor, consulte https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Introdução

Porquê estender o MicroPython?

Pode haver razões de multpiple por que você pode querer estender o MicroPython. Uma vez que é uma versão aparada do Python, o MicroPython não está funcionando muito bem quando se trata de tarefas computacionalmente pesadas (semelhante ao Python). O C, por outro lado, é realmente bem adequado para tais tarefas. Uma extensão C para Micropython permite que você execute esses cálculos através da linguagem C e use os resultados em MicroPython. Isto pode acelerar alguns processos por uma ordem de grandeza.

Uma segunda razão pode ser, que você queira fazer interface com algum hardware que usa uma API C de dentro do MicroPython. 

Tipos de extensões MicroPython C

Existem diferentes maneiras de estender o MicroPython : módulos C externos e código de máquina nativo em arquivos .mpy.

Para executar um módulo C externo você precisa recompilá-lo no firmware do MicroPython. Este é um processo bastante complicado, e é por isso que este guia irá focar a segunda alternativa. Se você estiver interessado nesta abordagem, você pode ler mais sobre ela nestas páginas web:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

O .mpy é um arquivo que armazena código de máquina nativo compilado e pode ser ligado dinamicamente. Isso o torna muito mais flexível, já que você não precisa recompilar todo o firmware do MicroPython.

Escreva o módulo de extensão

Escrever o módulo de extensão é a parte mais fácil de estender o MicroPython. Vou mostrar-lhe como fazê-lo, olhando para um exemplo simples. Para começar, vamos criar um novo diretório chamado factfib. Abrir uma nova janela de terminal, entrar no novo diretório e criar um novo ficheiro chamado factfib.c. Se você criou o diretório em um Raspberry Pi sob Dekstop, o comando é parecido com este.

cd ~/Desktop/factfib

touch factfib.c

Aberto factfib.c no seu editor de texto favorito e introduza o seguinte 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
}

Deixe-me explicar o código acima: Em MicroPython e Python, cada variável é um objecto. Portanto, em C nós as representamos como mp_obj_t. Mas um programa em C só pode trabalhar com variáveis do tipo C, e é por isso que precisamos transformar o objeto em um tipo C. Isto é chamado Marshalling e MicroPython fornece funções para extrair qualquer tipo de C de um objeto.

Esta página de Oliver Robson é realmente útil para a Marshalling https://mpy-c-gen.oliverrobson.tech/. Além disso, você pode encontrar exemplos de Marshalling na documentação do MicroPython. https://docs.micropython.org/en/latest/develop/natmod.html. Aconselho-o a ler este artigo se precisar de mais explicações.

Construir a extensão

Nós construímos a extensão em um arquivo .mpy usando um Makefile. Mas precisamos de configurar algumas coisas primeiro. Certifique-se de que o seu terminal ainda está aberto no arquivo factfib directório. Depois clone o repositório MicroPython GitHub.

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 instalações vão demorar algum tempo, se você quiser economizar tempo e sabe o que está fazendo, você só precisa clonar o diretório /py e /tools do repositório micropython e colocá-los dentro de um micropython pasta dentro do factfib (saltar o segundo comando).

Enquanto a instalação está a decorrer podemos usar o tempo para escrever o Makefile. Basta executar o

touch Makefile

e abrir o arquivo recém-criado em um editor de texto.

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

Você deve ter notado que nós definimos o ARCH variável para armv7m embora o pico seja armv6m. Temos de fazer isto porque o armv6m arquitetura não é (no momento em que estou escrevendo este artigo) suportada pelo dynruntime.mk ferramenta.

Mas, felizmente, ainda podemos compilar uma extensão funcional para o Pico. Só precisamos de modificar a dynruntime.mk que deve ser localizado em micropython/py/dynruntime.mk. Abra-a e sob a configuração de Arquitectura no armv7m encontre a variável CFLAGS (deve ser a linha 64). Alterar -mcpu=cortex-m3 para -mcpu=cortex-m0 e salvar o arquivo.

chnge -mcpu=cortex-m3 a -mcpu=cortex-m0

Depois que tudo estiver instalado, execute

make

e o factfib.mpy será criado um arquivo.

Instalar e testar a extensão

O último passo é a instalação e o teste. Para isso utilizamos o sistema de ficheiros que o Micropython cria no Raspberry Pi Pico. A forma mais fácil de o fazer é usar o Thonny IDE (pré-instalado no Raspbian/Raspberry Pi OS).

Se você não está usando um Raspberry Pi para programar seu Pico ou se você nunca usou MicroPython no Pico antes disso, por favor consulte o guia de iniciação ao MicroPython do Raspberry Pi https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

Se você está usando Windows você deve conferir este blog Post sobre o Raspberry Pi Pico e MicroPython no Windows https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

Feche Thonny depois de ter flashado o seu Pico com o firmware MicroPython e instale o rshell. Nós usamos o rshell para acessar o sistema de arquivos a partir da linha de comando.

sudo pip3 install rshell
rshell --version

Isto imprime a versão rhsell se tudo estiver configurado corretamente. Depois desconecte o pico e conecte-o novamente (sem segurar o botão BOOTSEL) para ter certeza de que nada está tentando acessar o sistema de arquivos. Agora precisamos executar o

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

cp factfib.mpy /pyboard/factfib.mpy

Para testar a instalação você poderia usar o rshell ou o Thonny IDE para salvar seu .py no Pico, mas vou demonstrar como usar o intérprete MicroPython sobre o minicom.

Então, a primeira imprensa Ctrl+c para deixar o rshell e depois instalar o minicom via

sudo apt install minicom

agora podemos aceder à REPL (Read-eval-print loop) dos Picos

minicom -o -D /dev/ttyACM0

Os três parênteses angulares de fecho >>> indicam que o interpretador MicroPython está a ser executado. Agora podemos importar a nossa extensão e trabalhar com ela como habitualmente.

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

Parabéns! Criou a sua primeira extensão MicroPython C para o Pico! O próximo passo é aprender como escrever e compilar módulos mais complexos. Uma boa maneira de começar é testando como funciona o Marshalling de listas e ditados (veja a página Marshalling acima).

Se tiver alguma dúvida sobre este post, não hesite em escrever um comentário.

7 comentários

  1. PeterB em Novembro 26, 2021 às 8:29 am

    Além do erro no programa c, funcionou como um deleite. Obrigado.

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

  2. XMC.pl em Junho 1, 2022 às 3:31 am

    Como configurar o joomla para que possa recuperar os dados do mysql?

  3. Lynggaard em Janeiro 8, 2023 às 8:09 pm

    Tentei fazer o acima exposto, mas várias coisas não funcionam como descrito:

    - Segui as instruções acima, obtive micropitão, modifiquei o makefile como descrito
    - Eu faço (mas recebo um ficheiro factfib.native.mpy em vez de factfib.mpy)
    - Copio o ficheiro factfib.native.mpy para o pico como factfib.mpy
    (tentei rshell mas obtive um "timed out ou erro na transferência para o remoto: b", mas podia copiá-lo usando Thonny)
    - Posso importar o módulo (em thonny ou rshell - o mesmo) sem erros
    - Tento executar os "ff.fibonacci(5)" mas depois recebo:

    "Traceback" (última chamada mais recente):
    Ficheiro "", linha 1, em
    AttributeError: objecto 'módulo' não tem atributo 'fibonacci'".

    O que é que faço de errado?

    • paopao69 em Março 22, 2023 às 5:59 pm

      Acabou por conseguir pô-lo a funcionar?

  4. Paulo em Fevereiro 9, 2024 às 1:44 am

    Agora é possível compilar para armv6, basta alterar esta linha no Makefile e não é necessário alterar o código micropython
    ARCH = armv6m

    Tive alguns erros ao compilar o factfib.c. Onde estão as correcções?

    // falta o " no fim.
    #include "py/dynruntime.h"

    // converte o valor de retorno para 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);
    retornar mp_obj_new_int(rslt);
    }

    • Adam em Fevereiro 9, 2024 às 9:22 am

      Obrigado pela informação, Paulo!

Deixe um comentário