Arduino Nano-basierter Watchdog

Watchdog ist ein GerĂ€t zur Erkennung und Behebung von Hardwareproblemen. In der Regel wird hierfĂŒr ein Timer verwendet, dessen periodischer Neustart verhindert, dass ein Signal zum Neustart gesendet wird.



Der Hauptserver auf Gentoo wird von mir hauptsĂ€chlich fĂŒr Experimente verwendet, fĂŒhrt jedoch eine Reihe von Diensten aus, die nach Möglichkeit ohne Unterbrechung verfĂŒgbar sein sollten. Leider fĂŒhren die Konsequenzen einiger Experimente zu Kernel-Panik, 100% CPU-Auslastung und anderen Problemen im ungĂŒnstigsten Moment. Die Idee, einen Watchdog hinzuzufĂŒgen, hat also lange Aufmerksamkeit erfordert und sich schließlich in diesem GerĂ€t verwirklicht.

Nach einer genauen PrĂŒfung der verfĂŒgbaren Daten und einer Bewertung der verfĂŒgbaren Zeit war der auf der Grundlage des Arduino Nano zusammengestellte Wachhund die beste Option. Eine Liste von Anforderungen wurde ungefĂ€hr gleich angezeigt:

  1. Starten und Stoppen des Daemons, um mit einem Timer, einem regulÀren Betriebssystem-Tool (OpenRC), zu arbeiten.
  2. Eigener Watchdog auf dem GerĂ€t, in ATmega ist es, mĂŒssen Sie es verwenden.
  3. Das Ereignisprotokoll auf dem GerÀt zum Beheben des Neustarts und des Timers.
  4. Synchronisieren Sie die Uhrzeit des GerÀts mit dem Host, um die korrekte Uhrzeit im Protokoll aufzuzeichnen.
  5. Empfangen und Anzeigen des Status des GerÀts und seiner ProtokolleintrÀge.
  6. Löschen des Protokolls und ZurĂŒcksetzen des GerĂ€ts in den ursprĂŒnglichen Zustand.

So wurde das "Mikroskop" gefunden, der "Nagel" ist markiert ... man kann hÀmmern.

Hardware


Basis des GerĂ€tes war der chinesische Klon Arduino Nano, der auf Basis des CH340-Chips hergestellt wurde. Frische Linux-Kernel (getestet seit 3.16) verfĂŒgen ĂŒber einen geeigneten Treiber, sodass das GerĂ€t leicht als serielle USB-Schnittstelle erkannt werden kann.

Arduino unerwĂŒnschter Neustart


Jedes Mal, wenn das Terminal verbunden ist, wird das Arduino neu gestartet. Der Grund dafĂŒr ist, dass das Terminal ein DTR-Signal (Data Terminal Ready) sendet, wodurch das GerĂ€t neu gestartet wird. Somit versetzt die Arduino IDE das GerĂ€t in einen Modus zum Laden von Skizzen.

Es gibt verschiedene Möglichkeiten, um das Problem zu lösen, aber nur eine hat funktioniert. Es ist erforderlich, einen 10-”F-Elektrolyten (C1 in der folgenden Abbildung) zwischen den RST- und GND-Kontakten zu installieren. Leider blockiert dies auch das Herunterladen von Skizzen auf das GerÀt.

Infolgedessen lautet das Schema wie folgt:


Mit KiCad gezeichnet

ErlÀuterungen zum Schema
  • 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 — , , , , .


Boot-Loop (zyklischer Neustart) bei der Arbeit mit WDT


ATmega-Mikrocontroller verfĂŒgen ĂŒber einen integrierten WDT-RĂŒcksetzmechanismus (WatchDog Timer). Alle Versuche, diese Funktion zu verwenden, fĂŒhrten jedoch zu einer Boot-Schleife, die nur durch Ausschalten der Stromversorgung beendet werden konnte.

Nicht lange Suchen ergaben, dass die Bootloader der meisten Arduino-Klone WDT nicht unterstĂŒtzen. GlĂŒcklicherweise wurde dieses Problem im alternativen Optiboot- Bootloader behoben .

Um den Bootloader zu flashen, benötigen Sie einen Programmierer, der mit dem SPI-Protokoll arbeiten kann. Es ist auch wĂŒnschenswert, dass die Arduino IDE dieses GerĂ€t „persönlich“ kennt. In diesem Fall ist ein anderes Arduino ideal.

Wenn wir Arduino UNO als Programmierer und die neueste Version von Arduino IDE v1.6.5 verwenden, lautet der Algorithmus wie folgt:

  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. Arduino IDE Tools :
  5. Tools → Burn Bootloader , .


, Arduino Nano - — Board: Optiboot on 32 pin cpus, Processor: ATmega328p, CPU Speed: 16MHz.


Als nĂ€chstes mĂŒssen Sie alles löten, damit es wie ein StĂŒck aussieht.



Hier brauchte ich einen USB-Stecker, da ich ein Mini-ITX-Motherboard mit nur einem Anschluss fĂŒr ein Paar USB 2.0 habe, die auf der Vorderseite benötigt werden, und es gab nichts, was an das USB 3.0-Pad angeschlossen werden konnte. Wenn möglich, sollten solche GerĂ€te direkt an das Motherboard angeschlossen werden, damit die DrĂ€hte nicht herausragen.

Das Löten verursacht in der Regel keine Probleme, aber in diesem Fall wird ein Steckbrett verwendet, das seine eigenen Besonderheiten hat.

So löten Sie Spuren auf einem Steckbrett
( , ). .

:



Ergebnis:





Es scheint, dass einige Kontakte schlecht verlötet sind, aber dies ist nur ein Flussmittel. Der Lötmittelverbrauch auf dem Steckbrett ist ziemlich hoch, so dass alles, was möglich ist, hier mit einem Flussmittel verschmiert wird. In der Tat ist dies ein gutes Beispiel dafĂŒr, wie Sie das Produkt nach dem Löten nicht verlassen mĂŒssen. Das Flussmittel muss abgewaschen werden, da sonst Probleme mit der Korrosion der Verbindungen auftreten können. Ich werde hinzufĂŒgen und waschen gehen ... Das ist besser:



 

Software-Teil


Objektiv gesehen ist der Code dieses Projekts nicht von besonderem Interesse. Die einleitenden sind alles andere als extrem, und die Architektur wird in einem Satz beschrieben: Senden Sie einen Befehl - warten Sie auf eine Antwort. Der Ordnung halber werde ich hier die HauptfunktionalitÀt beschreiben und kurz auf die aus meiner Sicht interessantesten Punkte eingehen.

Der gesamte Code wird auf GitHub veröffentlicht. Wenn Sie also mit Bash und C / C ++ (im Kontext von Arduino-Skizzen) vertraut sind, kann das Lesen an dieser Stelle abgeschlossen werden. Wenn Sie interessiert sind, finden Sie das fertige Ergebnis hier .

Watchdog-Verbindung


Wenn Sie Watchdog verbinden, wird eine GerĂ€tedatei mit der Seriennummer erstellt. Wenn das System ĂŒber andere ttyUSB-GerĂ€te verfĂŒgt (in meinem Fall ein Modem), liegt ein Problem mit der Nummerierung vor. Um das GerĂ€t eindeutig zu identifizieren, mĂŒssen Sie einen Symlink mit einem eindeutigen Namen erstellen. HierfĂŒr ist udev konzipiert, das wahrscheinlich bereits im System vorhanden ist.

Zuerst mĂŒssen Sie den verbundenen Watchdog visuell finden, indem Sie beispielsweise die Systemprotokolldatei anzeigen. Wenn Sie dann / dev / ttyUSB0 durch das gewĂŒnschte GerĂ€t ersetzen, schreiben Sie in das Terminal:

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

Ausgabebeispiel
  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"
    ...


In diesem Fall sieht die Regel folgendermaßen aus:
ACTION=="add", KERNEL=="ttyUSB[0-9]*", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="ttyrst-watchdog"


Sie mĂŒssen es in einer separaten Datei im Verzeichnis /etc/udev/rules.d ablegen , z. B. 51-ttyrst-watchdog.rules, und udev anweisen , die Regeln neu zu laden:
udevadm control --reload-rules


Ab diesem Moment wird beim Verbinden des Watchdogs ein Link / dev / ttyrst- Watchdog auf dem gewĂŒnschten GerĂ€t erstellt, der spĂ€ter verwendet wird.

Bash-Skript (ttyrst-watchdog.sh)


Die Kommunikation mit dem Watchdog erfolgt mit einer Geschwindigkeit von 9600 Baud. Arduino arbeitet problemlos mit Terminals mit hoher Geschwindigkeit, aber Befehle zum Arbeiten mit Text (Katze, Echo usw.) empfangen und senden nur MĂŒll. Es ist möglich, dass dies nur ein Merkmal meiner Kopie des Arduino Nano ist.

FĂŒr den Neustartzyklus des Hauptzeitgebers und fĂŒr Befehlszeilenfunktionen wird ein Skript verwendet. Der Grund dafĂŒr ist, dass beide Komponenten eine gemeinsame Ressource verwenden - die GerĂ€tedatei - und dass ein synchroner Zugriff darauf erforderlich ist.

Die Synchronisation besteht im Wesentlichen aus einer Warteschleife:
while fuser ${DEVICE} >/dev/null 2>&1; do true; done
und erfassen Sie das GerĂ€t fĂŒr die erforderliche Zeit:
cat <${DEVICE}


Offensichtlich unterliegt ein solches Schema einer Rennbedingung. Sie können dies auf erwachsene Weise behandeln (z. B. um eine Nachrichtenwarteschlange zu organisieren). In diesem Fall reicht es jedoch aus, die ZeitĂŒberschreitungen korrekt festzulegen, um sicherzustellen, dass Sie das Ergebnis in einer akzeptablen Zeit erhalten. TatsĂ€chlich arbeitet das gesamte Skript mit ZeitĂŒberschreitungen.

Die DĂ€monisierung (lĂ€uft im Hintergrund) wird mit dem OpenRC-Paket durchgefĂŒhrt. Es wird davon ausgegangen, dass sich dieses Skript in der Datei /usr/local/bin/ttyrst-watchdog.sh befindet und das OpenRC-Skript in /etc/init.d/ttyrst-watchdog .

Wenn der DĂ€mon stoppt, ist die korrekte Deaktivierung des Watchdogs erforderlich. Zu diesem Zweck legt das Skript den Signalhandler fest, der abgeschlossen werden muss:
trap deactivate SIGINT SIGTERM
Und hier taucht ein Problem auf - OpenRC kann den Daemon nicht stoppen, oder besser gesagt, aber nicht oft.

Tatsache ist, dass der Befehl kill ein Signal an das Skript sendet und das Schlafprogramm, mit dem das Skript angehalten wird, in einem anderen Prozess ausgefĂŒhrt wird und das Signal nicht empfĂ€ngt. Infolgedessen wird die Deaktivierungsfunktion erst nach Abschluss des zu langen Ruhezustands gestartet.

Die Lösung besteht darin, den Ruhezustand im Hintergrund zu starten und zu warten, bis der Vorgang im Skript abgeschlossen ist:
sleep ${SLEEP_TIME} & wait $!  #  $!  ID   


Grundkonstanten:

WATCHDOG_ACTIVE - YES bzw. NO senden ein Signal zum Neustart, wenn der Timer ausgelöst wird oder nicht.
WATCHDOG_TIMER - Zeit in Sekunden, fĂŒr die der Timer eingestellt ist.
SLEEP_TIME - Zeit in Sekunden, nach der der Timer neu gestartet werden muss. Es sollte viel kleiner als WATCHDOG_TIMER sein, aber nicht sehr klein, um das System und das GerĂ€t nicht ĂŒbermĂ€ĂŸig zu belasten. Bei aktuellen ZeitĂŒberschreitungen betrĂ€gt ein angemessenes Minimum ungefĂ€hr 5 Sekunden.
DEFAULT_LOG_LINES - Die Anzahl der zuletzt vom Standardprotokollbefehl zurĂŒckgegebenen GerĂ€teprotokolleintrĂ€ge.

Skriptbefehle:

Start- Start des Neustartzyklus des Haupttimers. Sie können der Funktion is_alive zusĂ€tzlichen BestĂ€tigungscode hinzufĂŒgen, um beispielsweise die Möglichkeit einer Verbindung ĂŒber ssh zu prĂŒfen.
status - Zeigt den Status des GerÀts an.
ZurĂŒcksetzen - ZurĂŒcksetzen des EEPROM (Protokolldaten) und Neustarten des GerĂ€ts, um den ursprĂŒnglichen Zustand des Watchdogs wiederherzustellen.
log <Anzahl der EintrÀge> - Zeigt die angegebene Anzahl der letzten ProtokolleintrÀge an.

Arduino-Skizze (ttyrst-watchdog.ino)


Um erfolgreich die Skizze kompilieren, mĂŒssen Sie eine Drittanbieter - Zeitbibliothek , die fĂŒr die Zeitsynchronisation erforderlich ist.

Eine Skizze besteht aus zwei Dateien. Dies liegt daran, dass die Arduino-IDE die in der Hauptdatei deklarierten Strukturen (struct) nicht akzeptiert, sondern in eine externe Header-Datei verschoben werden muss. Außerdem ist das SchlĂŒsselwort typedef nicht erforderlich, um eine Struktur zu deklarieren. Es ist wahrscheinlich sogar schĂ€dlich. Nachdem ich die Standardoptionen ĂŒberprĂŒft habe, konnte ich die entsprechende Syntax nicht finden. Der Rest ist mehr oder weniger Standard C ++.

Die Funktionen wdt_enable und wdt_reset arbeiten mit dem im Mikrocontroller integrierten Watchdog. Nach dem Initialisieren des WDT ist es wichtig, ihn in der Hauptschleife und in den Schleifen aller lĂ€ngeren VorgĂ€nge zurĂŒckzusetzen.

ProtokolleintrĂ€ge werden in den nichtflĂŒchtigen EEPROM-Speicher geschrieben. Die verfĂŒgbare GrĂ¶ĂŸe kann in logrecord.h angegeben werden. In diesem Fall ist dies 1024. Das Protokoll wird in Form eines Rings erstellt. Das Trennzeichen ist eine Struktur mit Nullwerten. Die maximale Anzahl von EintrĂ€gen fĂŒr 1 KiB-EEPROM betrĂ€gt 203.

Ein Datensatz ĂŒber das Laden des GerĂ€ts wird erst nach der Zeitsynchronisation in das Protokoll aufgenommen. Die Synchronisierung wird gleichzeitig mit dem Neustart des Timers und vor der AusfĂŒhrung eines Befehls wĂ€hrend der GerĂ€teinitialisierung durchgefĂŒhrt. Auf andere Weise ist es nicht möglich, die korrekte Zeit mit diesem Ereignis zu vergleichen, und die Informationen zum Neustart des GerĂ€ts, isoliert vom ArbeitsdĂ€mon, sind nicht sehr interessant.



Das ist alles, danke fĂŒrs Zuschauen!

Projektquelldateien befinden sich auf GitHub

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


All Articles