Sequência de Reset e Inicialização

Um dispositivo executando MicroPython segue uma sequência de inicialização específica para ligar e se inicializar após um reset.

Nota

A sequência _boot.pyboot.pymain.py → REPL descrita abaixo é o que o firmware executa a cada reset, independentemente de como você se conecta — portanto, ela sempre se aplica. Quando você executa um script a partir do OpenMV IDE, a IDE interrompe o main.py em execução e roda o script aberto no editor em seu lugar, usando seu próprio protocolo de depuração. Ela não usa o REPL no dispositivo, então as referências específicas do REPL nesta página (o prompt interativo, Ctrl-D / Ctrl-C em um terminal serial, etc.) se aplicam à operação autônoma e a sessões diretas de terminal serial — mas a sequência de inicialização em si se aplica em todos os casos.

Hard reset

Inicializar a partir de um hard reset é o que acontece quando uma placa é ligada pela primeira vez, uma partida a frio. Este é um reset completo do hardware do MCU.

O código de porte do MicroPython inicializa todo o hardware essencial (incluindo clocks embarcados e reguladores de energia, UART serial interna, etc.) e, em seguida, inicia o ambiente MicroPython. A configuração existente do RTC pode ser mantida após um hard reset, mas todo o restante do estado do hardware é apagado.

A mesma sequência de inicialização por hard reset pode ser disparada por uma série de eventos, tais como:

  • Código Python executando machine.reset().

  • O usuário pressiona um botão físico de Reset na placa (quando aplicável).

  • Acordar do sono profundo (na maioria dos portes).

  • Reset do watchdog de hardware do MCU.

  • Detector de queda de tensão (brown out) do hardware do MCU.

Os detalhes dos disparadores de reset específicos do hardware dependem do porte e do hardware associado. A função machine.reset_cause() pode ser usada para determinar com mais precisão a causa de um reset.

Soft Reset

Quando o MicroPython já está em execução, é possível disparar um soft reset digitando Ctrl-D no REPL ou executando machine.soft_reset().

Um soft reset limpa o interpretador Python, libera toda a memória do Python e inicia o ambiente MicroPython novamente.

O estado que é apagado por um soft reset inclui:

  • Todas as variáveis, objetos, módulos importados Python, etc.

  • A maioria dos periféricos configurados usando o módulo machine. Há exceções muito limitadas; por exemplo, os modos do machine.Pin (isto é, se um pino é entrada ou saída, alto ou baixo) não são resetados na maioria dos portes. Configurações mais avançadas, como Pin.irq(), são sempre resetadas.

  • Bluetooth.

  • Sockets de rede. Sockets TCP abertos são fechados de forma limpa em relação à outra parte.

  • Arquivos abertos. O sistema de arquivos é deixado em um estado válido.

Parte do estado do sistema permanece a mesma após um soft reset, incluindo:

  • Quaisquer conexões de rede existentes (Ethernet, Wi-Fi, etc.) permanecem ativas na camada de rede IP. Consultar a interface de rede a partir do código pode indicar que a interface de rede ainda está ativa com um endereço IP configurado, etc.

  • Um REPL ativo parece contínuo antes e depois do soft reset, exceto em alguns casos incomuns:

    • Um REPL via UART serial restaurará sua configuração de hardware padrão (taxa de transmissão (baud rate), etc.).

  • A velocidade do clock da CPU normalmente não é alterada por um soft reset.

  • A configuração do RTC (isto é, o ajuste da hora atual) não é alterada pelo soft reset.

Sequência de Inicialização

Quando o MicroPython inicializa após um hard reset ou soft reset, ele segue esta sequência de inicialização em ordem:

_boot.py

Este é um script interno congelado no firmware do MicroPython. Ele é fornecido pelo MicroPython em muitos portes para realizar a inicialização essencial.

Por exemplo, na maioria dos portes o _boot.py detectará a primeira inicialização de um novo dispositivo e formatará o sistema de arquivos da flash interna, deixando-o pronto para uso.

A menos que você esteja criando um build personalizado do MicroPython ou adicionando um novo porte, provavelmente não precisa se preocupar com o _boot.py. É melhor não alterar seu conteúdo, a não ser que você realmente saiba o que está fazendo.

boot.py

Um arquivo chamado boot.py pode ser copiado para o sistema de arquivos interno da placa usando o mpremote.

Se o boot.py for encontrado, ele é executado. Você pode adicionar código no boot.py para realizar uma inicialização personalizada única (por exemplo, para configurar o hardware da placa).

Uma prática comum é configurar a conexão de rede da placa no boot.py para que ela esteja sempre disponível após o reset, para uso com o REPL, o mpremote, etc.

Aviso

o boot.py deve sempre encerrar e não rodar indefinidamente.

Dependendo da placa, parte da inicialização do hardware é adiada até depois que o boot.py encerra. Isso inclui a inicialização do USB nas OpenMV Cams baseadas em STM32. Nessas placas, a saída impressa pelo boot.py pode não ficar visível na porta serial USB embutida até que o boot.py termine de rodar.

O objetivo dessa inicialização tardia é tornar possível pré-configurar determinado hardware no boot.py e, então, fazê-lo iniciar com a configuração correta.

Nota

Às vezes é mais simples não ter um arquivo boot.py e colocar qualquer código de inicialização no topo do main.py em vez disso.

main.py

Semelhante ao boot.py, um arquivo chamado main.py pode ser copiado para o sistema de arquivos interno da placa. Se encontrado, ele é executado em seguida no processo de inicialização.

O main.py serve para qualquer código Python que você queira executar toda vez que o dispositivo iniciar.

Algumas dicas para o uso do main.py:

  • O main.py não precisa encerrar; fique à vontade para colocar um laço infinito while True nele.

  • Para aplicações Python complexas, você não precisa colocar todo o seu código no main.py. O main.py pode ser um ponto de entrada simples que importa sua aplicação e inicia a execução:

    import my_app
    my_app.main()
    

    Isso pode ajudar a manter clara a estrutura da sua aplicação. Também facilita instalar várias aplicações em uma placa e alternar entre elas.

  • É uma boa prática, ao escrever aplicações robustas, envolver o código no main.py com um tratador de exceções para tomar a ação apropriada caso o código falhe. Por exemplo:

    import machine, sys
    import my_app
    try:
        my_app.main()
    except Exception as e:
        print("Fatal error in main:")
        sys.print_exception(e)
    
    # Following a normal Exception or main() exiting, reset the board.
    # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C),
    # this code will drop to a REPL. Place machine.reset() in a finally
    # block to always reset, instead.
    machine.reset()
    

    Caso contrário, o MicroPython cairá no REPL após qualquer falha ou se o main encerrar (veja abaixo).

  • Quaisquer variáveis globais que foram definidas no boot.py ainda estarão definidas no contexto global do main.py.

  • Para otimizar totalmente o uso da flash e o consumo de memória, você pode copiar arquivos main.mpy e/ou boot.mpy pré-compilados para o sistema de arquivos, ou até mesmo congelá-los no build do firmware.

  • A execução do main.py é ignorada quando um soft reset é iniciado a partir do modo raw REPL (por exemplo, quando o mpremote ou outro programa está interagindo diretamente com o MicroPython).

Interpretador Interativo (REPL)

Se o main.py não for encontrado, ou se o main.py encerrar, então o O Modo Interpretador Interativo do MicroPython (também conhecido como REPL) iniciará imediatamente.

Nota

Mesmo que o main.py contenha um laço infinito, digitar Ctrl-C na porta serial do REPL injetará um KeyboardInterrupt. Se nenhum tratador de exceções o capturar, então o main.py encerrará e o REPL iniciará.

Quaisquer variáveis globais que foram definidas no boot.py e no main.py ainda estarão definidas no contexto global do REPL.

O REPL continua executando até que o código Python dispare um hard ou soft reset.

Soft Bricking (falha ao inicializar)

É raro, mas é possível que o MicroPython fique sem resposta durante a inicialização, um estado às vezes chamado de “soft bricked”. Por exemplo:

  • Se a execução do boot.py travar e a porta serial USB nativa nunca for inicializada.

  • Se o código Python reconfigurar a interface do REPL, tornando-a inacessível.

Fique tranquilo, a recuperação é possível!

Se você usa o OpenMV IDE, simplesmente conectar costuma ser suficiente — a IDE interrompe o main.py em execução e assume o controle. Se a câmera não conectar de jeito nenhum, use o Factory Reset abaixo. O método com Ctrl-C descrito a seguir é para sessões diretas de terminal serial — ele depende do REPL no dispositivo, que o OpenMV IDE não utiliza.

KeyboardInterrupt

Em muitos casos, abrir a porta serial do REPL e digitar Ctrl-C injetará um KeyboardInterrupt e pode fazer com que o script em execução encerre e um REPL inicie. A partir do REPL, você pode usar os.remove() para remover o arquivo Python problemático:

import os
os.remove('main.py')

Para confirmar quais arquivos ainda estão presentes no sistema de arquivos interno:

import os
os.listdir()

Factory Reset

Se você não conseguir acessar um REPL usando o método acima, a opção restante é um factory reset: apagar todo o conteúdo do sistema de arquivos da flash interna. Esta também é a solução caso o sistema de arquivos interno tenha ficado corrompido.

O OpenMV IDE possui várias formas embutidas de fazer isso. Primeiro coloque a câmera em seu modo de recuperação/bootloader — o método varia conforme a placa, então consulte a seção Recovery and debug pins da referência rápida da sua placa para saber como entrar nele. Em seguida, clique no botão de conectar no OpenMV IDE e siga as instruções para apagar o sistema de arquivos e regravar o firmware.

Aviso

Regravar o firmware sem apagar o sistema de arquivos geralmente não recupera o dispositivo de um soft bricking, pois uma atualização normal de firmware preserva o conteúdo do sistema de arquivos. Certifique-se de escolher a opção de apagar quando o OpenMV IDE solicitar.

Se você ficar travado, pergunte nos fóruns da OpenMV.