Chien de garde basé sur Arduino Nano

Watchdog est un appareil conçu pour dĂ©tecter et rĂ©soudre les problĂšmes matĂ©riels. Habituellement, un temporisateur est utilisĂ© pour cela, dont un redĂ©marrage pĂ©riodique empĂȘche l'envoi d'un signal au redĂ©marrage.



Le serveur principal sur Gentoo est utilisĂ© par moi principalement pour des expĂ©riences, cependant, il exĂ©cute un certain nombre de services qui, si possible, devraient ĂȘtre disponibles sans interruption. Malheureusement, les consĂ©quences de certaines expĂ©riences conduisent Ă  la panique du noyau, Ă  100% de charge CPU et Ă  d'autres problĂšmes au moment le plus inopportun. L'idĂ©e d'ajouter un chien de garde a donc longtemps retenu l'attention et s'est finalement concrĂ©tisĂ©e dans cet appareil.

AprĂšs une inspection minutieuse de ce qui Ă©tait disponible et une Ă©valuation du temps disponible, le chien de garde assemblĂ© sur la base de l'Arduino Nano Ă©tait la meilleure option. Une liste d'exigences est apparue autour de la mĂȘme:

  1. DĂ©marrage et arrĂȘt du dĂ©mon pour travailler avec une minuterie, un outil de systĂšme d'exploitation normal (OpenRC).
  2. Propre chien de garde sur l'appareil, dans ATmega, vous devez l'utiliser.
  3. Le journal des événements sur l'appareil pour fixer le redémarrage et la minuterie.
  4. Synchronisez l'heure de l'appareil avec l'hĂŽte pour enregistrer l'heure correcte dans le journal.
  5. Recevoir et afficher l'état de l'appareil et ses entrées de journal.
  6. Effacer le journal et réinitialiser l'appareil à son état d'origine.

Ainsi, le «microscope» a Ă©tĂ© retrouvĂ©, le «clou» est marqué  on peut marteler.

Matériel


La base de l'appareil était le clone chinois Arduino Nano, fabriqué sur la base de la puce CH340. Les nouveaux noyaux Linux (testés depuis la 3.16) ont un pilote approprié, donc le périphérique est facilement détecté comme un port série USB.

Redémarrage indésirable Arduino


Chaque fois que le terminal est connecté, l'Arduino redémarre. La raison en est que le terminal envoie un signal DTR (Data Terminal Ready), ce qui provoque le redémarrage de l'appareil. Ainsi, l'IDE Arduino met l'appareil en mode de chargement des croquis.

Il existe plusieurs options pour résoudre le problÚme, mais une seule s'est avérée fonctionner - il est nécessaire d'installer un électrolyte de 10”F (C1 dans le diagramme ci-dessous) entre les contacts RST et GND. Malheureusement, cela bloque également le téléchargement de croquis sur l'appareil.

En conséquence - le schéma est le suivant:


Dessiné à l'aide de KiCad

Explications du schéma
  • 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 — , , , , .


Boucle de démarrage (redémarrage cyclique) lors de l'utilisation de WDT


Les microcontrĂŽleurs ATmega ont un mĂ©canisme de rĂ©initialisation WDT (WatchDog Timer) intĂ©grĂ©. Cependant, toutes les tentatives d'utilisation de cette fonction ont conduit Ă  une boucle de dĂ©marrage, qui ne pouvait ĂȘtre interrompue qu'en coupant l'alimentation.

Peu de recherches ont révélé que les chargeurs de démarrage de la plupart des clones Arduino ne prennent pas en charge WDT. Heureusement, ce problÚme a été résolu dans le chargeur de démarrage alternatif Optiboot .

Afin de flasher le chargeur de démarrage, vous avez besoin d'un programmeur qui peut fonctionner en utilisant le protocole SPI, il est également souhaitable que l'IDE Arduino connaisse cet appareil «en personne». Dans ce cas, un autre Arduino est idéal.

Si nous prenons Arduino UNO, en tant que programmeur, et la derniĂšre version d'Arduino IDE v1.6.5, alors l'algorithme sera le suivant:

  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. Dans l'IDE Arduino, dans le menu Outils, définissez les paramÚtres comme dans la capture d'écran:
  5. SĂ©lectionnez l'Ă©lĂ©ment de menu Outils → Graver Bootloader et assurez-vous que le processus s'est terminĂ© sans erreur.


AprĂšs cette procĂ©dure, vous devez tĂ©lĂ©charger des croquis Ă  Arduino Nano en choisissant les mĂȘmes paramĂštres - Conseil : Optiboot sur 32 broches CPUs, Processeur : ATMEGA328P, CPU Speed : 16MHz.

Soudure


Ensuite, vous devez tout souder pour qu'il ressemble Ă  une seule piĂšce.



Ici, une prise USB Ă©tait nĂ©cessaire en raison du fait que j'ai une carte mĂšre mini-ITX avec un seul connecteur pour une paire d'USB2.0, qui sont nĂ©cessaires sur le panneau avant, et il n'y avait rien Ă  connecter au pad USB3.0. Si possible, ces appareils doivent ĂȘtre connectĂ©s directement Ă  la carte mĂšre afin que les fils ne dĂ©passent pas.

La soudure, en rÚgle générale, ne pose pas de problÚme, mais dans ce cas, une planche à pain est utilisée, et cela a ses propres spécificités.

Comment souder des pistes sur une planche Ă  pain
( , ). .

:



Résultat:





il peut sembler que certains contacts sont mal soudĂ©s, mais ce n'est qu'un flux. La consommation de soudure sur la planche Ă  pain est assez importante, donc tout ce qui est possible est enduit d'un flux ici. En fait, c'est un bon exemple de la façon dont vous n'avez pas besoin de quitter le produit aprĂšs le soudage. Le flux doit ĂȘtre lavĂ©, sinon il peut y avoir des problĂšmes de corrosion des composĂ©s. Je vais ajouter et aller me laver ... C'est mieux:



 

Partie logiciel


Objectivement parlant, le code de ce projet n'a pas un intĂ©rĂȘt particulier. Les introductions sont loin d'ĂȘtre extrĂȘmes, et l'architecture est dĂ©crite en une phrase: envoyer une commande - attendre une rĂ©ponse. Par souci d'ordre, je dĂ©crirai ici les fonctionnalitĂ©s principales et m'attarderai briĂšvement sur les points les plus intĂ©ressants, de mon point de vue.

Tout le code est publiĂ© sur GitHub, donc si vous ĂȘtes familier avec Bash et C / C ++ (dans le contexte des croquis Arduino), la lecture Ă  ce stade peut ĂȘtre terminĂ©e. Si vous ĂȘtes intĂ©ressĂ©, le rĂ©sultat final peut ĂȘtre trouvĂ© ici .

Connexion chien de garde


Lorsque vous connectez le chien de garde, un fichier de périphérique est créé contenant le numéro de série. Si le systÚme possÚde d'autres périphériques ttyUSB (dans mon cas, un modem), il y a un problÚme de numérotation. Pour identifier de maniÚre unique le périphérique, vous devez créer un lien symbolique avec un nom unique. Pour cela, udev est conçu, qui existe probablement déjà dans le systÚme.

Vous devez d'abord trouver visuellement le chien de garde connecté, par exemple, en consultant le fichier journal du systÚme. Ensuite, en remplaçant / dev / ttyUSB0 par le périphérique souhaité, écrivez dans le terminal:

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

Exemple de sortie
  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"
    ...


Dans ce cas, la rĂšgle ressemblera Ă  ceci:
ACTION=="add", KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyrst-watchdog"


Vous devez le placer dans un fichier séparé dans le répertoire /etc/udev/rules.d , par exemple, 51-ttyrst-watchdog.rules et dire à udev de recharger les rÚgles:
udevadm control --reload-rules


À partir de ce moment, lors de la connexion du chien de garde, un lien / dev / ttyrst- watchdog sera créé sur le pĂ©riphĂ©rique souhaitĂ©, qui sera utilisĂ© plus tard.

Script bash (ttyrst-watchdog.sh)


La communication avec le chien de garde s'effectue à une vitesse de 9600 bauds. Arduino fonctionne sans problÚme avec les terminaux à haute vitesse, mais les commandes pour travailler avec du texte (chat, écho, etc.) ne reçoivent et n'envoient que des ordures. Il est possible que ce soit une caractéristique de ma seule copie de l'Arduino Nano.

Pour le cycle de redémarrage du minuteur principal et pour les fonctions de ligne de commande, un script est utilisé. La raison en est que les deux composants utilisent une ressource commune - le fichier de périphérique, et il est nécessaire de lui fournir un accÚs synchrone.

La synchronisation consiste essentiellement en une boucle d'attente:
while fuser ${DEVICE} >/dev/null 2>&1; do true; done
et capturer l'appareil pendant le temps requis:
cat <${DEVICE}


De toute évidence, un tel régime est soumis à une condition de concurrence. Vous pouvez gérer cela de maniÚre adulte (par exemple, pour organiser une file d'attente de messages), mais dans ce cas, il suffit de définir correctement les délais afin de garantir un résultat dans un délai acceptable. En fait, l'ensemble du script fonctionne avec des délais d'attente.

La diabolisation (exécutée en arriÚre-plan) est effectuée à l'aide du package OpenRC. Il est supposé que ce script se trouve dans le fichier /usr/local/bin/ttyrst-watchdog.sh et que le script OpenRC se trouve dans /etc/init.d/ttyrst-watchdog .

Lorsque le dĂ©mon s'arrĂȘte, la dĂ©sactivation correcte du chien de garde est requise. Pour ce faire, le script dĂ©finit le gestionnaire de signal qui doit ĂȘtre terminĂ©:
trap deactivate SIGINT SIGTERM
Et ici, un problĂšme surgit - OpenRC ne peut pas arrĂȘter le dĂ©mon, ou plutĂŽt, mais pas souvent.

Le fait est que la commande kill envoie un signal au script et que le programme sleep, qui est utilisé pour suspendre le script, est exécuté dans un autre processus et ne reçoit pas le signal. Par conséquent, la fonction de désactivation n'est lancée qu'aprÚs la fin du sommeil, ce qui est trop long.

La solution consiste à démarrer le sommeil en arriÚre-plan et à attendre la fin du processus dans le script:
sleep ${SLEEP_TIME} & wait $!  #  $!  ID   


Constantes de base:

WATCHDOG_ACTIVE - YES ou NO, respectivement, envoient un signal pour redémarrer lorsque le temporisateur est déclenché ou non.
WATCHDOG_TIMER - temps en secondes pour lequel la minuterie est réglée.
SLEEP_TIME - temps en secondes aprĂšs lequel le temporisateur doit ĂȘtre redĂ©marrĂ©. Il doit ĂȘtre beaucoup plus petit que WATCHDOG_TIMER, mais pas trĂšs petit, afin de ne pas crĂ©er de charge excessive sur le systĂšme et l'appareil. Aux dĂ©lais d'attente actuels, un minimum raisonnable est d'environ 5 secondes.
DEFAULT_LOG_LINES - le nombre d'entrées de journal de périphérique récentes retournées par la commande de journal par défaut.

Commandes de script:

démarrer- début du cycle de redémarrage de la minuterie principale. Vous pouvez ajouter un code de vérification supplémentaire à la fonction is_alive, par exemple, pour vérifier la possibilité de se connecter via ssh.
état - affiche l'état de l'appareil.
reset - réinitialisation de l'EEPROM (données de journal) et redémarrage de l'appareil pour restaurer le chien de garde à son état d'origine.
log <nombre d'entrées> - affiche le nombre spécifié d'entrées de journal récentes.

Croquis Arduino (ttyrst-watchdog.ino)


Pour compiler avec succÚs l'esquisse, vous avez besoin d'une bibliothÚque de temps tierce , nécessaire à la synchronisation de l'heure.

Un croquis se compose de deux fichiers. Cela est dĂ» au fait que l'IDE Arduino n'accepte pas les structures (struct) dĂ©clarĂ©es dans le fichier principal, elles doivent ĂȘtre dĂ©placĂ©es vers un fichier d'en-tĂȘte externe. De plus, pour dĂ©clarer une structure, le mot-clĂ© typedef n'est pas nĂ©cessaire, il est probablement mĂȘme dangereux ... aprĂšs avoir vĂ©rifiĂ© les options standard, je n'ai pas trouvĂ© la syntaxe appropriĂ©e. Le reste est du C ++ plus ou moins standard.

Les fonctions wdt_enable et wdt_reset fonctionnent avec le chien de garde intégré dans le microcontrÎleur. AprÚs avoir initialisé le WDT, la principale chose à retenir est de le réinitialiser dans la boucle principale et à l'intérieur des boucles de toutes les opérations longues.

Les entrĂ©es de journal sont Ă©crites dans la mĂ©moire EEPROM non volatile, sa taille disponible peut ĂȘtre spĂ©cifiĂ©e dans logrecord.h, dans ce cas, il est 1024. Le journal est fait sous forme d'anneau, le sĂ©parateur est une structure avec des valeurs nulles. Le nombre maximum d'entrĂ©es pour 1 Kio EEPROM est de 203.

Un enregistrement sur le chargement de l'appareil ne parvient au journal qu'aprĂšs synchronisation de l'heure. La synchronisation est effectuĂ©e en mĂȘme temps que le temporisateur est redĂ©marrĂ© et avant l'exĂ©cution de toute commande lors de l'initialisation du pĂ©riphĂ©rique. Sinon, il ne sera pas possible de comparer l'heure correcte avec cet Ă©vĂ©nement, et les informations sur les redĂ©marrages des pĂ©riphĂ©riques, isolĂ©ment du dĂ©mon de travail, ne sont pas trĂšs intĂ©ressantes.



C'est tout, merci d'avoir regardé!

Les fichiers source du projet sont situés sur GitHub

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


All Articles