17 de maio de 2021

Do bit Ao Byte

Embarcados, Linux e programação

Boot manager na RPi Pico

boot manager na RPi Pico

Usei o termo boot manager na RPi Pico porque através desse artigo poderemos escolher qual programa iniciar ajustando apenas o arquivo de parâmetros (params.ini), lembrando que os scripts Python também são arquivos e portanto, podemos ter uma série de programas no sistema de arquivos e fazer com que a placa execute o programa desejado ao iniciar. E como fazer isso? Simples; através de um arquivo “.ini”. Vejamos os conceitos e estruturas para fazer uma implementação que sirva como exemplo.

Como criar arquivo na RPi Pico

Com MicroPython é fácil. E esse é o primeiro passo, pois se não temos o conceito de manipulação de arquivos, não tem como fazer um projeto mais elaborado.

Escrita

Para escrever um arquivo precisamos de uma variável que será o objeto do arquivo, então usamos a chamada builtin open() com os parâmetros filename e mode. A sequência é simples:

  • Abrir
  • Escrever
  • Fechar

Se for necessário escrever continuamente antes de fechar, é necessário que a cada escrita seja chamado o método flush() no objeto do arquivo. Vamos por partes. Para criar um arquivo na RPi Pico:

file = open("params.ini","w")
file.write("{'exec':'temp','method':'temperature','args':'none'}")
file.close()

Esse foi o formato escolhido por mim para fazer o boot manager na RPi Pico. Se desejar, modifique, use lista invés de dicionário etc.

Se não conhece dicionário em Python, ele é semelhante ao formato json. Basicamente temos o par chave:valor usando vírgula como separador. Mas se estou escrevendo entre aspas, não virou uma string? – Resposta: Sim, mas tem um recurso no Python para transformarmos essa string em dicionário. Mas não pense nisso agora. O foco desse tópico é criar o arquivo com conteúdo, seja lá qual for o conteúdo. Cumprida a tarefa, sigamos.

Leitura

Vimos como criar um arquivo na RPi Pico; para ler também é uma baba. Abrimos o arquivo sem flag e usamos algum método para ler, seja read, readline, readlines. Para saber que métodos o objeto oferece, use o comando help(file) ao abrir o arquivo.

A sequência é a mesma:

  • Abrir arquivo
  • Ler
  • Fechar

Supondo o arquivo params.ini:

file       = open("params.ini")
dicionario = file.readline()
file.close()

Desse modo, lemos 1 linha do arquivo para a variável. Como nosso arquivo só tem uma linha mesmo, é tudo o que precisamos fazer. A parte interessante começa a seguir, lembrando que o arquivo foi criado no formato necessário para fazer o boot manager na RPi Pico.

Conversão de string para dicionário

Não é exatamente uma “conversão”. O que faremos é pedir ao Python que avalie o tipo do conteúdo. Como já lemos o arquivo para a variável, podemos agora reatribuir seu valor, utilizando a função builtin eval(). Aqui está o exemplo completo, feito na Thonny IDE:

eval

Na linha dicionario = eval(dicionario) estamos fazendo uma reatribuição, pois inicialmente tínhamos uma string e com eval transformamos a variável string em um dicionário. Para comprovar, imprimi suas chaves.

Como iniciar um programa ao ligar a RPi Pico

Lá no primeiro artigo eu já havia citado o arquivo main.py. Mas invés de escrever as rotinas diretamente nele, é mais adequado utilizá-lo para executar o programa. Há duas razões para isso:

  • Podemos executar o programa na board sem que esteja iniciando com ela. Isso permite corrigir erros facilmente sem perder o boot.
  • Podemos fazer diferentes boots para diferentes aplicações através do arquivo “.ini”. Veremos ambos.

Executar scripts na RPi Pico

Para executar o programa que estiver no editor Thonny, basta clicar no botão Run. Mas se tiver organizado o código em classes e métodos, complica fazer os testes assim. Salvando o script na board, fica fácil executar posteriormente, Voltando ao artigo anterior, criei o script para ler temperatura do sensor de temperatura da RP2040. O código usado foi esse:

from machine import ADC
from time import sleep_ms as delay

def temperature():
    adc_temp          = ADC(4)
    readings          = 0
    conversion_factor = 0
    
    for _ in range(4):
        readings += adc_temp.read_u16()
    
    conversion_factor = 3.3 / (65535)
    voltage = (readings/4) * conversion_factor
    
    temperature_now = 21 - (voltage - 0.706) / 0.001721
    
    print("Temperatura: ",temperature_now)
    
if __name__ == '__main__':
    temperature()

Salvei na RPi Pico como “temp.py”. Do jeito que está dá pra executar direto do editor sem problemas, mas vou utilizar a segunda forma para exemplificar:

boot manager na RPi Pico - script de temperatura pra iniciar no teste de boot

Se houvesse erro, sem problemas. Bastaria reparar o código, importar e executar a função desejada. Usaremos esse script com esse mesmo nome e extensão para fazer o boot manager na RPi Pico.

Boot manager na RPi Pico

Se tivéssemos um ambiente onde um RPi Pico fizesse medição de temperatura, outro controlasse relés, outro contando pulsos de interrupção etc. Cada um receberia um código, certo? São programas distintos. Mas se pensarmos também que essas MCUs não poderiam parar, seria necessário um backup para substituição em caso de falhas. Nesse formato, seriam N placas de backup para N RPi Pico. Se fizermos um boot seletivo, bastaria 1 backup para N placas. Essa é uma das vantagens em optar por um arquivo de inicialização.

Mais no começo desse artigo exemplifiquei a criação de um arquivo “.ini” contendo um dicionário, para evitar parsing em uma série de strings. Agora precisamos criar o programa que lerá o arquivo de configuração para determinar qual programa executar.

Criar o main.py na RPi Pico

Antes de qualquer coisa: Esse script foi escrito de forma a ser genérico. Pode copiar como está para sua RPi Pico, depois faça seus programas e o arquivo “.ini” que corresponda ao programa e função que devem ser executados no boot manager na RPi Pico.

Quando fazemos import, devemos passar um nome válido de biblioteca. Não podemos fazer:

biblioteca = 'teste'
import biblioteca

Para fazer um import a partir de uma string temos a biblioteca importlib, que não está implementada no MicroPython. Mas sem problemas, tem um outro caminho:

biblioteca = __import__('teste')

Isso significa que podemos importar uma biblioteca através de variável “sim”. No código fará mais sentido, esse exemplo acima é a prova de conceito.

O script main.py é executado na inicialização da RPi Pico. A primeira coisa que precisamos fazer é verificar se existe um arquivo de configuração a ser lido. Isso fazemos com a biblioteca os, como será visto no código a seguir. Além disso, devemos tratar todas as exceções possíveis relacionadas à leitura e escrita. O código do main.py fica assim:

#para listar arquivos
from os import listdir as ls
from sys import exit

#verifica se existe o arquivo params.ini
if not 'params.ini' in ls():
    #se nao existe, nao tem nada a iniciar
    print("Nenhum programa a inicializar")
    exit(0)

#se nao entrou na condicao acima, podemos tentar ler o arquivo
file   = open("params.ini")
params = file.readline()
file.close()

#supondo que o 'minimo' esperado sao 10 bytes:
if not len(params) > 9:
    #nao passou, sai aqui
    print("Tamanho curto: ", len(params))
    exit(0)

#se passou pela condicao acima, tentamos gerar o dicionario
try:
    params = eval(params)
except:
    pass

if not type(params) is dict:
    #se nao for dicionario, algo de errado no conteudo
    print("erro no formato? confira abaixo:")
    print(params)
    exit(0)
    
#se passou pela condicao acima, podemos verificar se as chaves estao corretas
correct_keys = ['exec','method','args']
for _key in correct_keys:
    if not _key in params.keys():
        print("Chave faltando: ",_key)
        exit(0)
        
#Se passou pelas chaves:
#1 - fazer o import do script referenciado pela chave exec. ver se existe o arquivo
script = params['exec'] + ".py"
if not script in ls():
    #nao tem o script referenciado
    print("Arquivo nao encontrado: ",script)
    exit(0)
    
#se encontrou, import
program  = __import__(params['exec'])

#agora importa a funcao, se existir
if not params['method'] in dir(program):
    print("Nome errado da funcao?")
    exit(0)

f_val    = "program."+ params['method']
function = eval(f_val)

#por fim, executa a funcao
function()

Não implementei os parâmetros, mas está fácil. A “coisa” é que me bate uma preguiça lascada.

Criado o arquivo params.ini como lá no começo do artigo, crie também o script temp.py para medir a temperatura, usando o código mais acima. No MicroPython deverão existir 3 arquivos:

  • main.py
  • temp.py
  • params.py

Para escrever e fazer upload, use a Thonny IDE. TODO o processo necessário está nesse vídeo de 3 minutos. Mas se já fez uso da IDE Thonny, pode dispensar o vídeo.

Tendo todos os arquivos com os respectivos códigos, use agora a combinação Ctrl+D na IDE Thonny. Em meio às mensagens do boot você verá algo como a segunda linha azul desse print:

boot manager na RPi Pico - resultado

No próximo artigo relacionado, veremos como fazer a seleção por RF. Acompanhe!

Não está fácil achar RPi Pico no Brasil, mas na Robocore ainda tem algumas unidades (até renovação do estoque, claro). Dê uma conferida, caso esteja em seus planos essa diversão. Aumente sua coleção de embarcados!

Dessa vez é certeza que o vídeo não sai no mesmo dia do artigo, mas teremos vídeo sobre o tema em nosso canal Dobitaobytebrasil no Youtube. Se não é inscrito ainda, inscreva-se e clique no sininho para receber notificações!