3.27. ウォッチドッグタイマー

ウォッチドッグタイマー は、実行中のスクリプトが定期的に小突くのをやめると、マイクロコントローラーをリセットするハードウェアです。スクリプトは、正常なコードが動いているとわかっている場所からウォッチドッグに「餌を与え」ます。バグやハング、予期しない例外によって、設定したタイムアウト内にカメラがウォッチドッグへ餌を与えられなくなると、チップは自身をリセットし、スクリプトは最初からやり直します。

電源を入れ直してくれる人が近くにいない配備済みデバイスでは、これが数秒で復旧する一時的なバグと、サービスコールが必要になる文鎮との違いになります。

時間経過に対するウォッチドッグカウンターのグラフ。カウンターは タイムアウト値から始まり、ゼロに向かって直線的に低下し、 スクリプトがfeed()を呼び出すたびにタイムアウト値へ 再読み込みされる。3回のfeed()呼び出しが成功した後、 4番目の区間ではfeed()がなく、カウンターがゼロに達して MCUリセットがトリガーされる。

ウォッチドッグカウンターは、そのタイムアウトからカウントダウンします。feed() のたびに再読み込みされ、ゼロに達するとチップがリセットされます。

3.27.1. machine.WDTクラス

machine.WDT はウォッチドッグを有効にし、単一のメソッド feed() を提供します。いったん開始すると、ウォッチドッグは停止できません。抜け道は、スケジュール通りに餌を与えるか、チップをリセットさせるかのどちらかだけです。

from machine import WDT

wdt = WDT(timeout=2000)    # reset if not fed within 2 seconds

while True:
    do_work()
    wdt.feed()

timeout はミリ秒単位です。適切な値は、メインループの最も長い正当な反復にどれだけ時間がかかるかに、十分な余裕を加えて決まります。100ミリ秒のループに2秒のタイムアウトを設定すれば、遅い反復があっても、迷惑なリセットを起こさずに余裕があります。

3.27.2. feed()をどこで呼ぶか

feed() をどこに置くかは決定的な設計判断です。ウォッチドッグは、餌を与える間に 動作しない コード部分のバグしか捕捉できません。

  • メインループの先頭か末尾から呼ぶ。 最も一般的なパターンです。ウォッチドッグはメインループをハングさせるもの(デッドロック、無限の while、決して戻らないペリフェラル)を捕捉し、チップをリセットしてループに戻します。

  • 割り込みハンドラから呼ばない。 ウォッチドッグの目的は、通常の コードパスでのハングを捕捉することです。メインループが詰まっているかどうかに関わらず発火するISRは、本来 トリガーされるべき ウォッチドッグに餌を与え続けてしまいます。

  • 長いブロッキング操作の内側から呼ばない。 10秒かかるネットワークリクエストやセンサー読み取りは、まさにウォッチドッグが捕捉すべき種類のハングです。その内側に feed() を置くと、保護が無効になります。

ほとんどのプログラムでうまくいくガイドライン: メインループの反復ごとに1回餌を与え、タイムアウトは予想されるループ時間の数倍に設定します。単一の反復が正当にタイムアウトより長く必要な場合(たとえば意図的なキャリブレーション段階など)は、その段階を、間に feed() を挟んだ一連の小さなチャンクとして構成するか、それに入る前に(サポートされている場合は)timeout_ms() でタイムアウトを変更します。

3.27.3. 利用可否

ウォッチドッグはほとんどのOpenMVカメラで提供されていますが、すべてではありません。ハードウェアはすべての部品に存在しますが、Python APIがまだすべてに配線されているわけではありません。OpenMV ボード を確認するか、WDT の構築を試み、サポートされていない場合は AttributeError を捕捉してください。

WDT が提供されていないカメラでも、フィールドに配備されたデバイスはソフトウェアによる等価物を使えます。別のタスクやメインループのステップが進捗を監視し、何かが詰まっているように見えたら machine.reset() をトリガーするものです。ハードウェアウォッチドッグほど堅牢ではありません(詰まった割り込みハンドラはソフトモニターも巻き込んで止めてしまう可能性があります)が、アプリケーションレベルで同じケースをカバーします。