Im vorhergegangenen Artikel hatte ich beschrieben, wie wir uns eine Programmierumgebung für den ATtiny 13A Mikrocontroller einrichten. Nachdem wir erfolgreich unseren ersten Sketch auf den µC geflasht haben, sind wir nun bereit für die eigentliche Herzschrittmacher-Software. In diesem Artikel geht es um den Softwareteil, der auf dem Mikrocontroller läuft. Der ATtiny soll über einen Anschlusspin auf einen regelmäßigen Signalwechsel des Raspberry Pi hören. Wenn dieser Herzschlag für eine vorgegebene Zeit ausbleibt, müssen wir annehmen, dass der Raspberry Pi nicht mehr aktiv ist. Er hat sich aufgehängt oder befindet sich in einer Endlosschleife, aus der er sich nicht mehr befreien kann – jedenfalls erfüllt er nicht mehr seine Aufgabe. An dieser Stelle greift der Mikrocontroller ein und dreht dem Raspberry Pi kurzzeitig den Strom ab. Danach kann der RasPi neu booten, seine Funktion wieder aufnehmen und der Mikrocontroller seinerseits wird erneut mit der Überwachung beginnen.
Funktion der Herzschrittmacher Software
In der Einleitung habe ich bereits grob beschrieben, was die Aufgabe des Herzschrittmachers ist. Nebenstehend auch zur Erinnerung nochmal das Blockschaltbild. Der Mikrocontroller hat zwei Hauptaufgaben:
- Überwachung auf Timeout und
- Stromabschaltung im Falle eines Timeouts
Genau das macht der Hauptteil des Programm, wie wir gleich sehen werden.
Herzschrittmacher Software
Für einen Mikrocontroller schreibt man Software üblicherweise nicht in Python wie beim Raspberry Pi sondern in der Programmiersprache C. Die Arduino IDE hat bereits alle Bestandteile an Board, um das C-Programm zu compilieren und auf den Mikrocontroller zu flashen.
Hier mein Herzschrittmacher Programm in C:
#define BOOTZEIT 30000
#define AUSZEIT 5000
#define AUSGANG 3
#define EINGANG 1
unsigned long warteZeit=60000;
volatile unsigned long letzterHerzschlag;
void setup() {
pinMode(AUSGANG, OUTPUT);
digitalWrite(AUSGANG, LOW);
pinMode(EINGANG, INPUT);
letzterHerzschlag = millis(); // aktuelle Zeit merken
attachInterrupt(digitalPinToInterrupt(EINGANG), isr, CHANGE);
stromAn(); // Strom einschalten
}
void loop() {
if((millis() - letzterHerzschlag) > warteZeit) { // Timeout eingetreten
stromAus();
stromAn();
}
delay(100);
}
void isr() {
letzterHerzschlag = millis(); // letzten Herzschlag merken
}
void stromAus() {
digitalWrite(AUSGANG, HIGH); // Strom ausschalten
delay(AUSZEIT); // für ein paar Sekunden
}
void stromAn() {
digitalWrite(AUSGANG, LOW); // Strom einschalten
delay(BOOTZEIT); // und Bootzeit abwarten
letzterHerzschlag = millis(); // aktuelle Zeit merken
}
Wie man sieht ist das Programm ziemlich kompakt. Gehen wir es der Reihe nach durch. Zu Beginn werden einige Parameter definiert:
BOOTZEIT
steht für die Zeit im Millisekunden, die dem Raspberry Pi zugestanden wird um zu booten. Hier 30s.AUSZEIT
ist Dauer der Stromunterbrechung um einen Reboot auszulösen. Hier 5s.AUSGANG
ist die Pinbezeichnung für den Anschluss zur Stromabschaltung. Hier PB3 = Pin Nummer 2.EINGANG
ist die Pinbezeichnung für den Herzschlageingang. Hier PB1 = Pin Nummer 6.warteZeit
ist die Zeit, die maximal ohne Herzschlag verstreichen darf, bevor ein Reboot ausgelöst wird. Hier großzügig 60s
Die Zeiten können natürlich angepasst werden, wenn beispielsweise ein Reboot länger als 30 Sekunden dauert.
Zusätzlich wird am Programmanfang eine Variable letzterHerzschlag
definiert, die jeweils die Zeit des letzten erkannten Signalwechsels speichert.
In der Funktion setup()
werden die Ein- und Ausgänge festgelegt und beschaltet. Dabei wird der AUSGANG
auf LOW
gelegt. Daran müssen wir denken, dass wir es hier mit einer Reverse Logic zu tun haben, denn der MOSFET invertiert das Signal. Es gilt also:
AUSGANG = LOW
: Strom anAUSGANG = HIGH
: Strom aus
Dann wird die Variable letzterHerzschlag
mit der aktuellen Zeit vorbelegt und eine Interruptbehandlung eingerichtet mit der Zeile:
attachInterrupt(digitalPinToInterrupt(EINGANG), isr, CHANGE);
Dadurch wird laufend der Anschluss EINGANG
überwacht und im Falle eines Polaritätswechsels (CHANGE
) die Funktion isr
aufgerufen. Diese Funktion, wie man weiter unten im Programm sehen kann, macht nichts anderes als die Variable letzterHerzschlag
erneut mit der aktuellen Zeit zu versorgen. Das passiert jetzt automatisch bei jedem Wechsel von HIGH
auf LOW
und von LOW
auf HIGH
am Pin EINGANG
.
Die letzte Aktion der Funktion setup()
ist das Einschalten des Stroms über die Funktion stromAn()
. Die finden wir am Ende des Programms. stromAn()
legt zuerst den AUSGANG
auf LOW
um den Strom für den Raspberry Pi einzuschalten und wartet dann die BOOTZEIT
ab. Dann wird wiederum die Variable letzterHerzschlag
mit der aktuellen Zeit belegt.
Analog zu stromAn()
gibt es auch die Funktion stromAus()
. Sie macht nichts anderes als den AUSGANG
auf HIGH
zu legen um dem Raspberry Pi den Strom abzuschalten und die vorgegebene AUSZEIT
abzuwarten.
Das Hauptprogramm loop()
verbindet nun alle Funktionen und ist trotzdem recht simpel. Es überprüft, ob der TimeOut abgelaufen ist. Das ist dann der Fall, wenn die Differenz aus aktueller Zeit und der Zeit des letzten erkannten Herzschlags größer ist als die vorgegebene warteZeit
. Nur dann werden der Reihe nach stromAus()
und stromAn()
aufgerufen um den Raspberry Pi zwangsweise durchzubooten. Andernfalls wird nur 100ms gewartet um dann von vorne zu beginnen.
Herzschrittmacher Software flashen
Um das Programm auf einen ATtiny 13A zu bekommen, kopieren wir den Quelltext auf dieser Seite und fügen ihn in eine neue Seite der Arduino IDE ein. Abspeichern nicht vergessen! Wenn dann die Programmier-Hardware (siehe Schaltung) bereit ist, lässt sich der Sketch leicht auf den ATtiny flashen durch drücken der Shift-Taste und gleichzeitigem Klick auf das Icon mit dem Pfeil nach rechts. Am Programmer (Nano) werden die Lämpchen kurz blinken, dann ist der Flash-Vorgang beendet. Wenn die Arduino IDE keine Fehler auswirft, kann der ATtiny vom Steckbrett entnommen und in die eigentliche Herzschrittmacher-Schaltung eingesetzt werden.