Tinkercad Übung 8 - LED Lauflicht (2)
Pausiere die LED Animation
In dieser Übung pausierst du die LED Animation mit Hilfe des Tasters.
- Empfohlenes Vorwissen: Bedienung Tinkercad Circuits, Übung 7
- Neue Inhalte: Verzweigungen (if, else if), Variablen, Zustandsmaschine
Schritt 1 - Animationsprogramm kopieren
https://www.tinkercad.com/things/f6sTWwhVT0R-kkg-robotik-ubung-8-lauflicht-2-starter
![]() |
Abbildung 1 - LED Animation aus Übung 7 |
Wir wollen nun den Taster integrieren. Mir fallen 2 Möglichkeiten ein:
- Die Animation ist pausiert und jedes Mal und wenn man den Taster gedrückt hält, läuft die Animation.
- Die Animation läuft und solange man den Taster gedrückt hält, ist die Animation pausiert.
Schritt 2 - Taster auslesen und die gesamte Animation pausieren
Momentan läuft die Animation im loop()-Bereich und wird somit die ganze Zeit wiederholt. Innerhalb des loop()-Bereiches sind die einzelnen Animationsschritte definitiert, z.B.
// Animationsschritt 1:
digitalWrite(9, LOW); // Pin 9 ausschalten
digitalWrite(4, HIGH); // Pin 4 einschalten
digitalWrite(8, LOW);
digitalWrite(13, HIGH);
delay(ANIMATIONSGESCHWINDIGKEIT);
usw...
Im diesem Schritt wollen wir programmieren, dass die Animation nur läuft, wenn der Taster gedrückt (gehalten) wird.
Dafür benötigen wir eine Verzweigung, denn wir müssen ja auf den Zustand des Tasters irgendwie reagieren:
- wenn Taster gedrückt: LED Animation
- sonst: nichts
In EV3 würde das so aussehen:
Abbildung 2 - Verzweigung in EV3 |
Am Anfang der Hauptprogrammschleife loop() wird der Zustand des Tasters ausgelesen. Nur, wenn er gedrückt ist, wird die Animation ausgeführt, sonst passiert nichts.
Es hilft, sich erstmal als Pseudocode aufzuschreiben, was man machen will:
// wenn Bedingung wahr:
// Programm-Befehle, die ausgeführt werden, wenn Bedingung wahr ist
// sonst:
// Programm-Befehle, die ausgeführt werden, wenn Bedingung unwahr ist
In C++ sieht das dann so aus:
// wird immer ausgeführt
if (Bedingung)
{
// Befehle, die ausgeführt werden, wenn Bedingung wahr ist
}
else
{
// Befehle, die ausgeführt werden, wenn Bedingung unwahr ist
}
// wird immer ausgeführt
Das englische "if/else" übersetzt sich ins Deutsche als "wenn/sonst". Die Bedingung steht in Klammern. Die Befehls-Bereiche stehen in geschweiften Klammern. Ist die Bedingung wahr, wird der Bereich nach if ausgeführt, ist sie unwahr der Bereich nach dem else.
Unsere Bedingung ist, ob der Taster-Zustand gedrückt ist. Eine Bedingung ist immer ein Vergleich. Einfach Vergleiche:
- Ist 12 gleich 12? → 12 == 12 → wahr
- Ist 12 gleich 13? → 12 == 13 → unwahr
Schau mal ins Cheat Sheet (siehe Übung 5), was dir die Programmiersprache C++ da für Möglichkeiten bietet. Das Cheat Sheet kannst du hier herunterladen:
Ausschnitt aus dem Cheat Sheet:
Abbildung 2 - Vergleichsoperatoren im Cheat Sheet |
Du siehst, man kann auch vergleichen, ob eine Zahl größer, kleiner, größer gleich, kleiner gleich oder ungleich einer anderen Zahl ist.
Schau mal in der Arduino-Referenz. Auch dort sind die Vergleichsoperatoren beschrieben:
Wir wollen also die Frage stellen:
Ist Taster Zustand GEDRÜCKT? → Taster Zustand == GEDRÜCKT
Den Taster liest du mit digitalRead() aus. Schau mal in die Arduino-Referenz, was dieser Befehl zurückgibt:
- HIGH: an, enspricht wahr, auf englisch true, logische 1
- LOW: aus, entspricht unwahr, auf englisch false, logische 0
Wir wollen also die Frage stellen
Taster Zustand == HIGH oder
Taster Zustand == true oder
Taster Zustand == 1
Wie du den Zustand eines Tasters ausliest, hast du in Übung Übung 6 gelernt. Fertig sieht unsere Bedingung für einen Taster an Pin 2 also so aus:
digitalRead(2) == HIGH oder
digitalRead(2) == true oder
digitalRead(2) == 1
Eingesetzt in die Verzweigung:
if (digitalRead(2) == HIGH) {
// Befehle, die ausgeführt werden, wenn Bedingung wahr ist
} else {
// Befehle, die ausgeführt werden, wenn Bedingung unwahr ist
}
Auch if und else gibt es in der Arduino-Referenz. Schaue dort mal nach:
Baue dein Animationsprogramm so um, das die Animation nur ausgeführt wird, wenn der Taster an Pin 2 den Zustand HIGH oder true oder 1 hat so wie im folgenden Pseudocode beschrieben:
// wiederhole (loop()):
// wenn Taster an Pin 2 gedrückt:
// Animationschritt 1
// Animationschritt 2
usw...
// sonst
// nichts
Geschweifte Klammer auf { und zu } befinden sich mit auf den Tasten 7 und 0.
Tipp: Den else-Zweig darf man übrigens auch weglassen. Das sieht dann so aus:
if (Bedingung) {
// Befehle, die ausgeführt werden, wenn Bedingung wahr ist
}
Starte die Simulation und betätige den Taster. Was passiert? Probiere alle Möglichkeiten des Vergleichens aus: HIGH, true und 1. Was passiert, wenn du stattdessen mit LOW, false und 0 vergleichst?
Tipp: Momentan musst du den Taster noch ein wenig gedrückt halten bis das Programm reagiert. Das werden wir gleich verbessern.
Lösungsvorschlag: https://www.tinkercad.com/things/ikUixjTeszV-kkg-robotik-ubung-8-lauflicht-2-ganze-animation-pausieren
![]() |
Abbildung 3 - Ganze Animation pausieren |
Schritt 3 - Einzelne Animationsschritte pausieren
Wir versuchen das mal zu beheben, indem wir einfach jeden Animationsschritt einzeln in eine Verzweigung verpacken:
Pseudocode:
// wiederhole (loop()):
// wenn Taster an Pin 2 gedrückt:
// Animationschritt 1
// wenn Taster an Pin 2 gedrückt:
// Animationschritt 2
usw...
Echter C++ Code:
// solange der Taster gedrückt ist:
if (digitalRead(2) == HIGH) {
// Animationsschritt 1:
digitalWrite(9, LOW); // Pin 9 ausschalten
digitalWrite(4, HIGH); // Pin 4 einschalten
digitalWrite(8, LOW);
digitalWrite(13, HIGH);
delay(ANIMATIONSGESCHWINDIGKEIT);
}
if (digitalRead(2) == HIGH) {
// Animationsschritt 2:
digitalWrite(4, LOW);
digitalWrite(5, HIGH);
digitalWrite(13, LOW);
digitalWrite(12, HIGH);
delay(ANIMATIONSGESCHWINDIGKEIT);
}
usw...
Baue dein Programm so um wie oben gezeigt. Starte die Simulation und halte den Taster gedrückt.
Lösungsvorschlag: https://www.tinkercad.com/things/lKLt09jrbXz-kkg-robotik-ubung-8-lauflicht-2-einzeln-pausieren
![]() |
Abbildung 4 - einzelne Schritte der Animation pausieren |
Schritt 4 - Zustandsmaschine
Du hast bestimmt gerade gemerkt: Solange man den Taster gedrückt hält, ist alles in Ordung, aber denn man ihn nur kurz drückt, wird alles recht zufällig.
Grund: Wenn der Taster nicht gedrückt ist, werden alle Verzweigungsblöcke übersprungen, da die Bedingung digitalRead(2) == HIGH immer unwahr ist, egal wie oft man danach fragt. Das ganze befindet sich in loop(), der Hauptrogrammschleife, wird also immer wiederholt. Dadurch, dass aber alles übersprungen wird in der Schleife, gehen die einzelnen Wiederholungen sehr schnell.
Drückt man nun den Taster, wird der Animationsschritt ausgeführt, bei dem gerade zufällig die Bedingung digitalRead(2) == HIGH abgefragt wird. Lässt man den Taster sofort wieder los, wird der darauf folgende Animationsschritt wieder übersprungen und alle anderen auch.
Irgendwie muss die Programm-Struktur aus Schritt 2 mit der aus Schritt 3 verheiratet werden.
Die Lösung ist eine Zustandsmaschine. Eine Zustandsmaschine besteht aus mehreren Zuständen (in unserem Fall Animationsschritte). Aus einem Zustand kann man in den nächsten übertreten (in unserem Fall ganz einfach von Animationsschritt 1 nach 2 nach 3 usw.). Dafür bauen wir das Programm folgendermaßen um:
Pseudocode:
// wiederhole (loop()):
// wenn Taster an Pin 2 gedrückt:
// wenn Animationschritt 1
// Animationschritt 1
// weiter mit Animationsschritt 2
// oder wenn Animationschritt 2
// Animationschritt 2
// weiter mit Animationsschritt 3
// oder wenn Animationschritt 3
// Animationschritt 3
// weiter mit Animationsschritt 4
usw...
Das soll so funktionieren:
Ist der Taster nicht gedrückt, wird alles übersprungen so wie bei dem Programm aus Schritt 2.
Ist der Taster gedrückt:
- Taster ist gedrückt.
- Wir befinden wir uns gerade bei Animationsschritt 1: Er wird ausgeführt und wir merken uns, dass als nächstes Animationsschritt 2 ausgeführt werden soll.
- Ende von loop(), wieder zum Anfang.
- Wir befinden wir uns gerade bei Animationsschritt 2: Er wird ausgeführt und wir merken uns, dass als nächstes Animationsschritt 3 ausgeführt werden soll.
- Ende von loop(), wieder zum Anfang.
- Taster nicht mehr gedrückt.
- Alles wird übersprungen, d.h. die LEDs zeigen den zuletzt eingestellten Zustand: Animationsschritt 2.
- Ende von loop(), wieder zum Anfang.
- Taster ist wieder gedrückt.
- Wir befinden wir uns gerade bei Animationsschritt 3: Er wird ausgeführt und wir merken uns, dass als nächstes Animationsschritt 4 ausgeführt werden soll.
Jetzt wird bei losgelassenem Taster wirklich bei einem Animationsschritt pausiert!
In C++:
loop() {
if (digitalRead(2) == HIGH) {
if ( Ist Animationsschritt 1? ) {
// Animationsschritt 1
// weiter mit Animationsschritt 2
} else if ( Ist Animationsschritt 2? ) {
// Animationsschritt 2
// weiter mit Animationsschritt 3
} else if ( Ist Animationsschritt 3? ) {
// Animationsschritt 3
// weiter mit Animationsschritt 4
usw...
}
}
if ( Ist Animationsschritt 1? ) {
// Animationsschritt 1
// weiter mit Animationsschritt 2
} else if ( Ist Animationsschritt 2? ) {
// Animationsschritt 2
// weiter mit Animationsschritt 3
} else if ( Ist Animationsschritt 3? ) {
// Animationsschritt 3
// weiter mit Animationsschritt 4
usw...
}
}
}
Variablen
Irgendwie muss sich das Programm merken, welcher Animationsschritt gerade ausgeführt werden soll. Dafür kann eine Variable verwendet werden. Variablen leben im RAM des Mikrocontrollers. Man kann dort Werte zwischenspeichern und wieder auslesen.
Variablen definieren:
Eine Variable wird definiert wie eine Konstante, nur ohne const:
int animationsschritt = 1;
- int ist der Datentyp. Den kennst du Schon aus Übung 7. Dieser sagt dem Compiler, dass wir eine Zahl abspeichern wollen.
- animationsschritt ist der Name der Variable.
- = 1 ist der Startwert der Variable. Man kann diesen auch weglassen. Ich empfehle aber, immer einen Startwert zu setzen.
Der Variablen einen Typ und einen Namen zuzuweisen, nennt man Deklaration. Ab jetzt darf man sie benutzen. Eine Variable muss immer weiter oben im Programm deklariert werden als sie benutzt wird. In der Variablen das erste Mal einen Wert abzuspeichern, nennt man Initialisierung.
Deklaration und Initialisierung können auch als 2 Befehlszeilen geschrieben werden:
int animationsschritt;
animationsschritt = 1;
Wert in eine Variable speichern:
Um einen Wert in einer Variable abzuspeichern, wird der Zuweisungs-Operator verwendet. Dieser speichert den Wert auf der rechten Seite in die Variable auf der linken Seite.
int animationsschritt = 1; // animationsschritt hat den Wert 1
animationsschritt = 12; // animationsschritt hat den Wert 12
Das obige Beispiel nutzt den Zuweisungsoperator, um die Variable animationsschritt mit 1 zu initialisieren und ein weiteres Mal, um den Wert der Variable auf 12 zu ändern.
In EV3 sähe das Schreiben einer Variable so aus:
Abbildung 6 - Variable schreiben in EV3 |
Wert aus einer Variablen lesen:
Jetzt nutzt es uns wenig, einen Wert irgendwo abzuspeichern, ihn aber nie zu benutzen. Den Wert wieder auszulesen, würde in EV3 so aussehen:
Abbildung 7 - Variable lesen in EV3 |
In C++ schreibt man einfach den Namen der Variable hin:
animationsschritt
Variable vergleichen:
Um in einer Verzweigung auf einen bestimmten Variablen-Wert zu prüfen, schreibt man also einfach den Namen der Variable:
animationsschritt == 12 → wahr
animationsschritt == 13 → unwahr
Der Variablen-Name wird dann durch den Inhalt (Wert) ersetzt:
12 == 12 --> wahr
12 == 13 --> unwahr
Eingebaut in die Zustandsmaschine:
// Animationsschritt (wir starten mit 1)
int animationsschritt = 1;
loop() {
if (digitalRead(2) == HIGH) {
if (animationsschritt == 1) {
// Animationsschritt 1
animationsschritt = 2; // nächster Animationsschritt
} else if (animationsschritt == 2) {
// Animationsschritt 2
animationsschritt = 3; // nächster Animationsschritt
} else if (animationsschritt == 1) {
// Animationsschritt 3
animationsschritt = 4; // nächster Animationsschritt
usw...
}
}
if (animationsschritt == 1) {
// Animationsschritt 1
animationsschritt = 2; // nächster Animationsschritt
} else if (animationsschritt == 2) {
// Animationsschritt 2
animationsschritt = 3; // nächster Animationsschritt
} else if (animationsschritt == 1) {
// Animationsschritt 3
animationsschritt = 4; // nächster Animationsschritt
}
}
}
Baue das Programm so um, dass die einzelnen Animationsschritte Zustände einer Zustandsmaschine sind. Diese Zustandsmaschine wird übersprungen, wenn der Taster nicht gedrückt ist. Ist der Taster gedrückt, geht die Zustandsmaschine in den zuletzt gespeicherten Zustand (Animationsschritt) und führt diesen aus. Ist der Zustand fertig ausgeführt, wird der nächste Zustand abgespeichert. Ist der letzte Zustand fertig ausgeführt, wird der erste Zustand abgespeichert.
Starte die Simulation und halte den Taster gedrückt.
Was passiert, wenn du den Zustand des Taster mit LOW vergleichst anstatt mit HIGH?
Lösungsvorschlag: https://www.tinkercad.com/things/e07zX31HYYN-kkg-robotik-ubung-8-lauflicht-2-pausieren-mit-taster
![]() |
Abbildung 8 - Animation pausieren mit Zustandsmaschine |
Zusammenfassung
Du kannst nun Verzweigungen in C++ programmieren, weißt, dass eine Bedingung immer ein Vergleich ist und wie man diesen schreibt, kannst Zahlen in einer Variablen abspeichern, ändern und wieder auslesen und hast das Konzept einer Zustandsmaschine kennen gelernt.
Kommentare
Kommentar veröffentlichen