リセットとブートシーケンス

MicroPythonを実行しているデバイスは、リセット後に起動して初期化するために、特定のブートシーケンスに従います。

注釈

以下で説明する _boot.pyboot.pymain.py → REPL のシーケンスは、接続方法に関係なく、ファームウェアが 毎回のリセット時 に実行するものであり、常に適用されます。OpenMV IDE からスクリプトを実行する場合、IDEは現在実行中の main.py を中断し、エディタで開いているスクリプトを独自のデバッグプロトコルを介してその代わりに実行します。これはデバイス上の REPL を使用しないため、このページの REPL固有 の記述(対話プロンプト、シリアル端末での Ctrl-D / Ctrl-C など)はスタンドアロン動作および直接のシリアル端末セッションに適用されます。ただし、ブートシーケンス自体はすべての場合に適用されます。

ハードリセット

ハードリセットからのブートは、ボードに初めて電源が投入されたとき、つまりコールドブート時に発生します。これはMCUハードウェアの完全なリセットです。

MicroPythonのポートコードは、すべての必須ハードウェア(組み込みクロックや電源レギュレータ、内部シリアルUARTなどを含む)を初期化し、その後MicroPython環境を起動します。既存の RTC 設定はハードリセット後も保持される場合がありますが、その他のすべてのハードウェア状態はクリアされます。

同じハードリセットのブートシーケンスは、次のようなさまざまなイベントによってトリガーされます:

  • Pythonコードが machine.reset() を実行する。

  • ユーザーがボード上の物理的なリセットボタンを押す(該当する場合)。

  • ディープスリープからの復帰(ほとんどのポートで)。

  • MCUハードウェアウォッチドッグリセット。

  • MCUハードウェアのブラウンアウト検出器。

ハードウェア固有のリセットトリガーの詳細は、ポートおよび関連するハードウェアによって異なります。machine.reset_cause() 関数を使用すると、リセットの原因をさらに特定できます。

ソフトリセット

MicroPythonが既に実行されている場合、REPLでCtrl-Dを入力する か、machine.soft_reset() を実行することで、ソフトリセットをトリガーできます。

ソフトリセットはPythonインタプリタをクリアし、すべてのPythonメモリを解放して、MicroPython環境を再び起動します。

ソフトリセットによってクリアされる状態には、次のものが含まれます:

  • すべてのPython変数、オブジェクト、インポート済みモジュールなど。

  • machine module を使用して設定されたほとんどのペリフェラル。ごくわずかな例外があり、たとえば machine.Pin のモード(ピンが入力か出力か、ハイかローかなど)は、ほとんどのポートでリセットされません。Pin.irq() のようなより高度な設定は常にリセットされます。

  • Bluetooth。

  • ネットワークソケット。開いているTCPソケットは、相手側に対して正常にクローズされます。

  • 開いているファイル。ファイルシステムは有効な状態に保たれます。

ソフトリセット後も変わらない一部のシステム状態には、次のものが含まれます:

  • 既存のネットワーク接続(Ethernet、Wi-Fiなど)はIPネットワーク層でアクティブなまま維持されます。コードからネットワークインターフェース を照会すると、ネットワークインターフェースが設定済みのIPアドレスなどでまだアクティブであることが示される場合があります。

  • アクティブな REPL は、いくつかの特殊なケースを除いて、ソフトリセットの前後で連続しているように見えます:

    • シリアルUART REPLは、デフォルトのハードウェア設定(ボーレートなど)を復元します。

  • CPUクロック速度は、通常ソフトリセットによって変更されません。

  • RTC の設定(つまり現在時刻の設定)は、ソフトリセットによって変更されません。

ブートシーケンス

MicroPythonがハードリセットまたはソフトリセットのいずれかの後にブートするとき、次のブートシーケンスを順番に実行します:

_boot.py

これは MicroPythonファームウェアにフリーズされた 内部スクリプトです。多くのポートでMicroPythonによって提供され、必須の初期化を行います。

たとえば、ほとんどのポートでは _boot.py が新しいデバイスの初回ブートを検出し、内部フラッシュファイルシステム をフォーマットして使用可能な状態にします。

カスタムのMicroPythonビルドを作成したり新しいポートを追加したりするのでなければ、_boot.py について心配する必要はおそらくありません。何をしているのか本当に理解している場合を除き、内容を変更しないのが最善です。

boot.py

boot.py という名前のファイルは、mpremote を使用してボードの内部 ファイルシステム にコピーできます。

boot.py が見つかると、それが実行されます。boot.py にコードを追加して、カスタムの一回限りの初期化(たとえばボードのハードウェアの設定)を実行できます。

一般的な手法は、boot.py でボードのネットワーク接続を設定し、REPLmpremote などで使用するためにリセット後も常に利用できるようにすることです。

警告

boot.py は常に終了し、無期限に実行し続けないようにすべきです。

ボードによっては、一部のハードウェア初期化が boot.py の終了後まで遅延されます。これには、STM32ベースのOpenMV CamでのUSBの初期化が含まれます。これらのボードでは、boot.py から出力された内容は、boot.py の実行が終了するまで内蔵USBシリアルポートに表示されない場合があります。

この遅延初期化の目的は、boot.py で特定のハードウェアを事前設定し、その正しい設定で起動できるようにするためです。

注釈

boot.py ファイルを置かず、初期化コードを代わりに main.py の先頭に配置する方が簡単な場合もあります。

main.py

boot.py と同様に、main.py という名前のファイルをボードの内部 ファイルシステム にコピーできます。見つかった場合、起動プロセスで次に実行されます。

main.py は、デバイスが起動するたびに実行したい任意のPythonコードのためのものです。

main.py の使用に関するいくつかのヒント:

  • main.py は終了する必要はなく、無限の while True ループをそこに入れても構いません。

  • 複雑なPythonアプリケーションの場合、すべてのコードを main.py に入れる必要はありません。main.py は、アプリケーションをインポートして実行を開始する単純なエントリポイントにできます:

    import my_app
    my_app.main()
    

    これはアプリケーションの構造を明確に保つのに役立ちます。また、ボードに複数のアプリケーションをインストールして、それらを切り替えることも容易になります。

  • 堅牢なアプリを書くときの良い手法は、main.py のコードを例外ハンドラでラップして、コードがクラッシュした場合に適切な対応をとることです。たとえば:

    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()
    

    そうしないと、MicroPythonはクラッシュ後やmainが終了したとき(下記参照)にREPLに移行します。

  • boot.py で設定されたグローバル変数は、main.py のグローバルコンテキストでも引き続き設定されています。

  • フラッシュ使用量とメモリ消費を完全に最適化するには、プリコンパイル済みmain.mpyboot.mpy ファイルをファイルシステムにコピーするか、さらにはそれらをファームウェアビルドに フリーズ させることもできます。

  • main.py の実行は、raw REPLモード からソフトリセットが開始された場合(たとえば mpremote や別のプログラムがMicroPythonと直接やり取りしている場合)はスキップされます。

対話型インタプリタ(REPL)

main.py が見つからない場合、または main.py が終了した場合、MicroPython 対話型インタープリタモード(別名 REPL) がただちに起動します。

注釈

main.py に無限ループが含まれている場合でも、REPLのシリアルポートで Ctrl-C を入力すると KeyboardInterrupt が注入されます。例外ハンドラがそれをキャッチしなければ、main.py は終了してREPLが起動します。

boot.pymain.py で設定されたグローバル変数は、REPLのグローバルコンテキストでも引き続き設定されています。

REPLは、Pythonコードがハードリセットまたはソフトリセットをトリガーするまで実行を続けます。

ソフトブリック化(ブート失敗)

まれですが、MicroPythonが起動中に応答しなくなることがあり、これは「ソフトブリック化」と呼ばれる状態です。たとえば:

  • boot.py の実行が動かなくなり、ネイティブUSBシリアルポートが一度も初期化されない場合。

  • PythonコードがREPLインターフェースを再設定し、アクセスできなくなった場合。

ご安心ください、復旧は可能です!

OpenMV IDEを使用している場合、単に接続するだけで多くの場合十分です。IDEは実行中の main.py を停止して制御を引き継ぎます。カメラがまったく接続できない場合は、以下の ファクトリーリセット を使用してください。次に説明する Ctrl-C の方法は直接のシリアル端末セッション向けです。これはデバイス上のREPLに依存しており、OpenMV IDEはそれを使用しません。

KeyboardInterrupt

多くの場合、REPLのシリアルポートを開いて Ctrl-C を入力すると KeyboardInterrupt が注入され、実行中のスクリプトが終了してREPLが起動することがあります。REPLから、os.remove() を使用して問題のあるPythonファイルを削除できます:

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

内部ファイルシステムにまだどのファイルが存在するかを確認するには:

import os
os.listdir()

ファクトリーリセット

上記の方法でREPLにアクセスできない場合、残された選択肢はファクトリーリセット、つまり内部フラッシュファイルシステムの内容全体を消去することです。これは内部ファイルシステムが破損した場合の修正方法でもあります。

OpenMV IDEには、これを行ういくつかの組み込みの方法があります。まずカメラをリカバリ/ブートローダーモードにします。方法はボードごとに異なるため、入り方についてはお使いのボードの クイックリファレンスRecovery and debug pins セクションを参照してください。次にOpenMV IDEで接続ボタンをクリックし、プロンプトに従ってファイルシステムを消去し、ファームウェアを再書き込みします。

警告

ファイルシステムを消去 せずに ファームウェアを再書き込みしても、通常はソフトブリック化から復旧できません。通常のファームウェア更新はファイルシステムの内容を保持するためです。OpenMV IDEが確認を求めたら、必ず消去オプションを選択してください。

行き詰まった場合は、OpenMVフォーラム で質問してください。