Watchdog baseado em Nano do Arduino

Watchdog é um dispositivo projetado para detectar e solucionar problemas de hardware. Geralmente, um timer é usado para isso, uma reinicialização periódica que impede que um sinal seja enviado para a reinicialização.



O servidor principal no Gentoo é usado principalmente por experimentos, no entanto, ele executa vários serviços que, se possível, devem estar disponíveis sem interrupção. Infelizmente, as consequências de alguns experimentos levam ao pânico do kernel, 100% da carga da CPU e outros problemas no momento mais inoportuno. Portanto, a ideia de adicionar cão de guarda há muito tempo exige atenção e finalmente se materializa neste dispositivo.

Após uma inspeção minuciosa do que estava disponível e uma avaliação do tempo disponível, o cão de guarda montado com base no Arduino Nano foi a melhor opção. Uma lista de requisitos apareceu em torno do mesmo:

  1. Iniciando e parando o daemon para trabalhar com um timer, uma ferramenta comum do SO (OpenRC).
  2. Cão de guarda próprio no dispositivo, no ATmega, você precisa usá-lo.
  3. O log de eventos no dispositivo para corrigir a reinicialização e o cronômetro.
  4. Sincronize a hora do dispositivo com o host para registrar a hora correta no log.
  5. Recebendo e exibindo o status do dispositivo e suas entradas de log.
  6. Limpando o log e redefinindo o dispositivo para seu estado original.

Assim, o "microscópio" foi encontrado, a "unha" está marcada ... você pode martelar.

Hardware


A base do dispositivo foi o clone chinês Arduino Nano, fabricado com base no chip CH340. Os kernels recentes do Linux (testados desde a versão 3.16) têm um driver adequado, portanto o dispositivo é facilmente detectado como uma porta serial USB.

Reinicialização indesejada do Arduino


Cada vez que o terminal é conectado, o Arduino é reiniciado. O motivo é que o terminal envia um sinal DTR (Data Terminal Ready), o que faz com que o dispositivo seja reinicializado. Assim, o Arduino IDE coloca o dispositivo em um modo para carregar esboços.

Existem várias opções para solucionar o problema, mas apenas uma funcionou - é necessário instalar um eletrólito de 10µF (C1 no diagrama abaixo) entre os contatos RST e GND. Infelizmente, isso também bloqueia o download de esboços para o dispositivo.

Como resultado - o esquema é o seguinte:


Desenhado usando o KiCad

Explicações para o esquema
  • R1 — , PC817: (5V — 1.2V / 0.02A) = 190Ω, 180Ω.
  • U2 — Arduino PC. , ( USB ), .
  • JP1 — , . .
  • 1 — , DTR.
  • MB_RST, MB_GND — RESET , RST (GND). , .
  • BTN_RST, BTN_GND — , , , , .


Loop de inicialização (reinicialização cíclica) ao trabalhar com o WDT


Os microcontroladores ATmega possuem um mecanismo de redefinição WDT (WatchDog Timer) embutido. No entanto, todas as tentativas de usar essa função levaram a um loop de inicialização, que poderia ser encerrado apenas desligando a energia.

Pesquisas demoradas revelaram que os gerenciadores de inicialização da maioria dos clones do Arduino não oferecem suporte ao WDT. Felizmente, esse problema foi resolvido no gerenciador de inicialização alternativo Optiboot .

Para fazer o flash do carregador de inicialização, você precisa de um programador que possa trabalhar no protocolo SPI, também é desejável que o IDE do Arduino conheça esse dispositivo "pessoalmente". Nesse caso, outro Arduino é ideal.

Se usarmos o Arduino UNO, como programador, e a versão mais recente do Arduino IDE v1.6.5, o algoritmo será o seguinte:

  1. boards-1.6.txt optiboot hardware/arduino/avr/boards.txt Arduino IDE.
  2. Arduino Uno, File → Examples → ArduinoISP.
  3. Arduino Nano :
    Arduino Uno ()Arduino Nano (ICSP )
    5V → Vcc
    GND → GND
    D11 → MOSI
    D12 → MISO
    D13 → SCK
    D10 → Reset

    Pin1 (MISO) ← D12Pin2 (Vcc) ← 5V
    Pin3 (SCK) ← D13Pin4 (MOSI) ← D11
    Pin5 (Reset) ← D10Pin6 (GND) ← GND



  4. No IDE do Arduino, no menu Ferramentas, defina as configurações como na captura de tela:
  5. Selecione o item de menu Ferramentas → Gravar gerenciador de inicialização e verifique se o processo foi concluído sem erros.


Após esse procedimento, você precisará fazer o upload de esboços para o Arduino Nano, escolhendo as mesmas configurações - Placa : Optiboot em cpus de 32 pinos, Processador : ATmega328p, Velocidade da CPU : 16MHz.

De solda


Em seguida, você precisa soldar tudo, para que pareça uma peça.



Aqui, eu precisava de um plugue USB devido ao fato de ter uma placa-mãe mini-ITX com apenas um conector para um par de USB2.0, necessários no painel frontal, e não havia nada para conectar ao bloco USB3.0. Se possível, esses dispositivos devem ser conectados diretamente à placa-mãe para que os fios não fiquem presos.

A soldagem, por via de regra, não causa problemas, mas, neste caso, é utilizada uma placa de ensaio, que possui suas próprias especificidades.

Como soldar faixas em uma tábua de pão
( , ). .

:



Resultado:





pode parecer que alguns contatos estão mal soldados, mas isso é apenas um fluxo. O consumo de solda na tábua de pão é bastante grande, então tudo o que é possível é manchado com um fluxo aqui. De fato, este é um bom exemplo de como você não precisa deixar o produto após a solda. O fluxo deve ser lavado, caso contrário, pode haver problemas com a corrosão dos compostos. Vou acrescentar e vou lavar ... é melhor:



 

Parte do software


Objetivamente falando, o código deste projeto não é de interesse especial. Os introdutórios estão longe de serem extremos, e a arquitetura é descrita em uma frase: envie um comando - aguarde uma resposta. Por uma questão de ordem, descreverei aqui a principal funcionalidade e brevemente estarei nos pontos mais interessantes, do meu ponto de vista.

Todo o código é publicado no GitHub, portanto, se você estiver familiarizado com o Bash e o C / C ++ (no contexto dos esboços do Arduino), a leitura neste momento poderá ser concluída. Se você estiver interessado, o resultado final pode ser encontrado aqui .

Conexão Watchdog


Quando você conecta o watchdog, um arquivo de dispositivo é criado contendo o número de série. Se o sistema tiver outros dispositivos ttyUSB (no meu caso, um modem), há um problema com a numeração. Para identificar exclusivamente o dispositivo, você precisa criar um link simbólico com um nome exclusivo. Para isso, o udev foi projetado, o que provavelmente já existe no sistema.

Primeiro, você precisa encontrar visualmente o cão de guarda conectado, por exemplo, observando o arquivo de log do sistema. Em seguida, substituindo / dev / ttyUSB0 pelo dispositivo desejado, escreva no terminal:

udevadm info -a -p "$(udevadm info -q path -n /dev/ttyUSB0)"

Exemplo de saída
  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="ch341-uart"
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4/1-1.4:1.0':
    ...
    
  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1.4':
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{idVendor}=="1a86"
    ATTRS{idProduct}=="7523"
    ATTRS{product}=="USB2.0-Serial"
    ...


Nesse caso, a regra ficará assim:
ACTION=="add", KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyrst-watchdog"


Você precisa colocá-lo em um arquivo separado no diretório /etc/udev/rules.d , por exemplo 51-ttyrst-watchdog.rules e dizer ao udev para recarregar as regras:
udevadm control --reload-rules


A partir deste momento, ao conectar o watchdog, um link / dev / ttyrst- watchdog será criado no dispositivo desejado, que será usado posteriormente.

Script Bash (ttyrst-watchdog.sh)


A comunicação com o watchdog é realizada a uma velocidade de 9600 baud. O Arduino funciona sem problemas com terminais em alta velocidade, mas os comandos para trabalhar com texto (gato, eco, etc.) recebem e enviam apenas lixo. É possível que esse seja um recurso apenas da minha cópia do Arduino Nano.

Para o ciclo de reinicialização do timer principal e para as funções de linha de comando, um script é usado. O motivo é que ambos os componentes usam um recurso comum - o arquivo do dispositivo, e é necessário fornecer acesso síncrono a ele.

A sincronização consiste essencialmente em um loop de espera:
while fuser ${DEVICE} >/dev/null 2>&1; do true; done
e capture o dispositivo pelo tempo necessário:
cat <${DEVICE}


Obviamente, esse esquema está sujeito a uma condição de corrida. Você pode lidar com isso de maneira adulta (por exemplo, para organizar uma fila de mensagens), mas, neste caso, é suficiente definir corretamente os tempos limite para garantir que você obtenha o resultado em um tempo aceitável. De fato, o script inteiro está trabalhando com tempos limite.

A demonização (executando em segundo plano) é realizada usando o pacote OpenRC. Supõe-se que esse script esteja no arquivo /usr/local/bin/ttyrst-watchdog.sh , e o script OpenRC esteja em /etc/init.d/ttyrst-watchdog .

Quando o daemon para, a desativação correta do cão de guarda é necessária. Para fazer isso, o script define o manipulador de sinal que requer conclusão:
trap deactivate SIGINT SIGTERM
E aqui surge um problema - o OpenRC não pode parar o daemon, ou melhor, mas não com frequência.

O fato é que o comando kill envia um sinal para o script e o programa sleep, que é usado para pausar o script, é executado em outro processo e não recebe o sinal. Como resultado, a função de desativação é ativada somente após a suspensão do sono, que é muito longa.

A solução é iniciar o sono em segundo plano e aguardar a conclusão do processo no script:
sleep ${SLEEP_TIME} & wait $!  #  $!  ID   


Constantes básicas:

WATCHDOG_ACTIVE - YES ou NO, respectivamente, enviam um sinal para reiniciar quando o timer é acionado ou não.
WATCHDOG_TIMER - tempo em segundos durante o qual o timer está definido.
SLEEP_TIME - tempo em segundos após o qual o timer deve ser reiniciado. Ele deve ser muito menor que WATCHDOG_TIMER, mas não muito pequeno, para não criar carga excessiva no sistema e no dispositivo. No tempo limite atual, um mínimo razoável é de aproximadamente 5 segundos.
DEFAULT_LOG_LINES - o número de entradas recentes do log do dispositivo retornadas pelo comando de log padrão.

Comandos de script:

start- início do ciclo de reinicialização do timer principal. Você pode adicionar código de verificação adicional à função is_alive, por exemplo, para verificar a possibilidade de conexão via ssh.
status - exibe o status do dispositivo.
reset - redefine a EEPROM (dados de log) e reinicia o dispositivo para restaurar o watchdog ao seu estado original.
log <número de entradas> - exibe o número especificado de entradas de log recentes.

Esboço do Arduino (ttyrst-watchdog.ino)


Para compilar com êxito o esboço, você precisa de uma biblioteca de horário de terceiros , necessária para a sincronização de horário.

Um esboço consiste em dois arquivos. Isso ocorre porque o IDE do Arduino não aceita as estruturas (struct) declaradas no arquivo principal, elas devem ser movidas para um arquivo de cabeçalho externo. Além disso, para declarar uma estrutura, a palavra-chave typedef não é necessária, provavelmente é até prejudicial ... depois de verificar as opções padrão, não consegui encontrar a sintaxe apropriada. O resto é mais ou menos C ++ padrão.

As funções wdt_enable e wdt_reset funcionam com o watchdog integrado no microcontrolador. Após inicializar o WDT, o principal a lembrar é redefini-lo no loop principal e dentro dos loops de todas as operações demoradas.

As entradas do log são gravadas na memória EEPROM não volátil, seu tamanho disponível pode ser especificado em logrecord.h, neste caso, é 1024. O log é feito na forma de um anel, o separador é uma estrutura com valores zero. O número máximo de entradas para 1 KiB EEPROM é 203.

Um registro sobre o carregamento do dispositivo chega ao log somente após a sincronização de tempo. A sincronização é realizada ao mesmo tempo que o timer é reiniciado e antes que qualquer comando seja executado durante a inicialização do dispositivo. De outra maneira, não será possível comparar a hora correta com esse evento, e as informações sobre reinicializações do dispositivo, isoladamente do daemon em funcionamento, não são muito interessantes.



Isso é tudo, obrigado por assistir!

Os arquivos de origem do projeto estão localizados no GitHub

Source: https://habr.com/ru/post/pt384467/


All Articles