3.27. Temporizador watchdog

Um temporizador watchdog é um componente de hardware que reinicia o microcontrolador se o script em execução parar de o verificar periodicamente. O script «alimenta» o watchdog a partir de algum ponto onde sabe que está a executar código saudável; se um bug, um bloqueio ou uma exceção inesperada impedirem a câmara de alimentar o watchdog dentro de um timeout configurado, o chip reinicia-se e o script começa do início.

Num dispositivo em produção sem ninguém por perto para fazer um ciclo de energia, esta é a diferença entre um bug transitório que se recupera em segundos e um tijolo que precisa de uma chamada de serviço.

A graph of a watchdog counter over time. The counter starts at the timeout value, drops linearly toward zero, and is reloaded to the timeout each time the script calls feed(). After three successful feed() calls, a fourth interval has no feed() and the counter reaches zero, triggering an MCU reset.

O contador watchdog diminui a partir do seu timeout. Cada feed() recarrega-o; se chegar a zero, o chip reinicia.

3.27.1. A classe machine.WDT

machine.WDT ativa o watchdog e expõe um único método, feed(). Uma vez iniciado, o watchdog não pode ser parado – as únicas saídas são alimentá-lo no prazo ou deixá-lo reiniciar o chip:

from machine import WDT

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

while True:
    do_work()
    wdt.feed()

O timeout está em milissegundos. O valor correto depende de quanto tempo demora a iteração legítima mais longa do ciclo principal, com margem confortável – um ciclo de 100 ms com um timeout de 2 s tem margem suficiente para uma iteração lenta sem resets desnecessários.

3.27.2. Onde chamar feed()

O local onde feed() reside é a decisão de conceção crítica; o watchdog apenas apanha bugs nas partes do código que não são executadas entre as alimentações.

  • Chamar a partir do ciclo principal, no início ou no fim. O padrão mais comum. O watchdog apanha tudo o que bloqueie o ciclo principal – um deadlock, um while infinito, um periférico que nunca responde – e reinicia o chip de volta ao ciclo.

  • Não chamar a partir de um handler de interrupção. O objetivo do watchdog é apanhar bloqueios no percurso de código normal. Uma ISR que dispara independentemente de o ciclo principal estar bloqueado continuaria a alimentar um watchdog que deveria estar a disparar.

  • Não chamar a partir de uma operação de bloqueio longa. Um pedido de rede ou uma leitura de sensor que demora dez segundos é exatamente o tipo de bloqueio que o watchdog deve apanhar. Colocar feed() dentro dele anula a proteção.

Uma diretriz que funciona para a maioria dos programas: alimentar uma vez por iteração do ciclo principal, com o timeout definido para várias vezes a duração esperada do ciclo. Se uma única iteração legitimamente precisar de mais tempo do que o timeout – uma fase de calibração deliberada, por exemplo – estruture essa fase como uma série de partes menores com feed() entre elas, ou altere o timeout com timeout_ms() (onde suportado) antes de entrar nela.

3.27.3. Disponibilidade

O watchdog está disponível na maioria das câmaras OpenMV, mas não em todas – o hardware está presente em todos os componentes, mas a API Python ainda não está ligada em toda a parte. Consulte o Placas OpenMV ou tente construir uma WDT e apanhe o AttributeError se não for suportado.

Mesmo nas câmaras onde WDT não está disponível, um dispositivo em produção pode usar um equivalente por software – uma tarefa separada ou um passo do ciclo principal que monitoriza o progresso e aciona machine.reset() se algo parecer bloqueado. É menos robusto do que um watchdog de hardware (um handler de interrupção bloqueado também pode derrubar o monitor por software), mas cobre os mesmos casos ao nível da aplicação.