Wiederholen statt abschreiben
Bisher kannst du Wiederholungen nur erzeugen, indem du Quelltext kopierst (unschön!) oder alles in die Funktion loop() schreibst. Es gibt eine elegantere Möglichkeit: Schleifen.
Mit ihnen wiederholst du einen Quelltextabschnitt, solange eine Bedingung erfüllt ist oder bis eine bestimmte Anzahl von Durchläufen erreicht ist.
In C++/Arduino gibt es unterschiedliche Arten von Schleifen. Zwei davon lernst du hier kennen: die „for“-Schleife und die „while“-Schleife. Die for-Schleife verwendest du für eine bestimmte Anzahl von Durchläufen, die while-Schleife, um etwas so lange auszuführen, wie eine Bedingung erfüllt ist.
for
Die for-Schleife wird meist verwendet, wenn von Anfang an klar ist, wie viele Wiederholungen benötigt werden. Ihr Kopf besteht aus drei Segmenten:
for (A; B; C)
Im Einzelnen haben sie folgende Aufgaben:
- A: wird vor der ersten Ausführung der Schleife einmal ausgeführt. Normalerweise wird hier eine sogenannte Schleifenvariable deklariert und initialisiert.
- B: die Schleifenbedingung, unter der die Schleife weiterhin ausgeführt wird. Sie wird vor jedem Durchlauf geprüft.
- C: wird nach jedem einzelnen Schleifendurchlauf ausgeführt. Oft wird dort die Schleifenvariable erhöht.
Ein Schleifenkopf könnte also so aussehen:
for (int i = 0; i < 100; i = i + 10) {
// Code
}
Hier wird der Variablen i anfangs der Wert 0 zugewiesen und nach jedem Durchlauf um 10 erhöht. Die Schleife wird ausgeführt, solange i kleiner als 100 ist. Sie wird also zehnmal durchlaufen, und i nimmt dabei die Werte 0, 10, 20, …, 90 an.
Ein Fächer aus Linien: for
#include <U8g2lib.h>
#include <SPI.h>
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 8, 9);
void setup(void) {
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
for (int i = 0; i < 16; i++) { // Der Inhalt läuft 16-mal.
// i nimmt die Werte von 0 bis 15 an.
u8g2.drawLine(0, 0, i * 8, 63); // Linie von (0,0) nach (i*8,63) zeichnen
delay(50); // 50 Millisekunden warten
u8g2.sendBuffer(); // Grafik anzeigen
}
}
void loop() {}
Anmerkungen
i++ist die Kurzschreibweise füri = i + 1. Beides bedeutet dasselbe, du wirst aber meisti++sehen.- Der Name „i“ ist üblich für Schleifenvariablen, aber nicht vorgeschrieben. Bei geschachtelten Schleifen folgen oft „j“ und „k“.
- Da
sendBuffer()innerhalb der Schleife steht, siehst du jede Linie einzeln entstehen. Stünde es dahinter, erschiene das fertige Bild auf einmal. - Achte auf den Vergleich im Schleifenkopf:
i < 16ergibt die Werte 0 bis 15, also 16 Durchläufe. Miti <= 16wären es 17. Dieser Fehler um eins gehört zu den häufigsten überhaupt. - Eine Variable, die im Schleifenkopf deklariert wird, gibt es nur innerhalb der Schleife. Dahinter ist
iunbekannt. - Hinter die Klammer des Schleifenkopfs gehört kein Semikolon. Mit
for (…);wiederholt die Schleife nichts, und der Abschnitt in den geschweiften Klammern läuft nur einmal.
while
Die while-Schleife wird meist verwendet, wenn vorher nicht bekannt ist, wie viele Durchläufe es geben soll. Zu Beginn jedes Durchlaufs – auch des ersten – wird geprüft, ob die Bedingung erfüllt ist. Ist sie erfüllt, wird der Programmteil innerhalb der Schleife ausgeführt, und danach wird erneut geprüft. Ist sie nicht erfüllt, geht es hinter der Schleife weiter.
Laufen, bis der Rand erreicht ist: while
Das folgende Programm zeichnet senkrechte Linien von links nach rechts über das Display. Die Schleife läuft, solange zwei Dinge gleichzeitig gelten: Der rechte Rand ist noch nicht erreicht und der Joystick wurde nicht mittig gedrückt. Du kannst den Aufbau also jederzeit abbrechen – wie viele Durchläufe es dann waren, steht vorher nicht fest.
#include <U8g2lib.h>
#include <SPI.h>
#define KEY_CENTER 0
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 8, 9);
int xPos;
void setup(void) {
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
pinMode(KEY_CENTER, INPUT_PULLUP);
}
void loop() {
xPos = 0; // links anfangen
u8g2.clearBuffer(); // Display leeren
// solange noch Platz ist und die Mitte nicht gedrückt wurde:
while (xPos < 128 && digitalRead(KEY_CENTER) == HIGH) {
u8g2.drawLine(xPos, 0, xPos, 63); // senkrechte Linie bei xPos zeichnen
u8g2.sendBuffer(); // Grafik anzeigen
xPos = xPos + 1; // einen Pixel weiter nach rechts
}
delay(1000); // warten, dann beginnt loop() von vorn
}
Anmerkungen
- Bei einer while-Schleife musst du selbst dafür sorgen, dass die Bedingung irgendwann nicht mehr erfüllt ist. Würde hier
xPos = xPos + 1;fehlen, liefe die Schleife endlos und das Programm käme nie weiter. - Die Zählung in die Bedingung einzubauen, nimmt dir die for-Schleife ab – deshalb ist sie bei bekannter Anzahl die bessere Wahl.

Mitten in der Schleife aussteigen: break
Manchmal zeigt sich erst mitten im Durchlauf, ob es weitergehen soll. Dann schreibt man while (true) und verlässt die Schleife an der passenden Stelle mit break. Dieses Stück zeichnet dieselben Linien wie das Programm oben:
int xPos = 0;
while (true) { // Diese Bedingung ist immer erfüllt
u8g2.drawLine(xPos, 0, xPos, 63);
u8g2.sendBuffer();
xPos = xPos + 1;
if (xPos == 128) { // Rechter Rand erreicht?
break; // Dann die Schleife sofort verlassen
}
}
Das Programm macht hinter der schließenden Klammer der Schleife weiter. Dieses Muster begegnet dir später im Snake-Spiel wieder.
Schachteln
Wie du es schon von den Verzweigungen kennst, lassen sich auch Schleifen schachteln: Eine Schleife steht dann vollständig im Inneren einer anderen.
Beispiel: Schleife in Schleife
Die äußere Schleife zählt die Zeilen, die innere die Spalten. Für jede der 8 Zeilen läuft die innere Schleife komplett durch – insgesamt also 8 × 16 = 128 Punkte.
#include <U8g2lib.h>
#include <SPI.h>
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 8, 9);
void setup(void) {
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
for (int i = 0; i < 8; i = i + 1) { // 8 Zeilen
for (int j = 0; j < 16; j = j + 1) { // 16 Spalten je Zeile
u8g2.drawPixel(j * 8, i * 8);
delay(20);
u8g2.sendBuffer();
}
}
}
void loop() {}
Beispiel: Verzweigung in Schleife
Hier steckt eine if-else-Verzweigung in einer while-Schleife. Die Variable farbe merkt sich, welche LED gerade leuchtet, und wird bei jedem Durchlauf umgeschaltet – die LEDs blinken also abwechselnd, bis du den Joystick mittig drückst.
#include <U8g2lib.h>
#include <SPI.h>
#define LEDR 5
#define LEDG 7
#define KEY_CENTER 0
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 8, 9);
void setup(void) {
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
pinMode(KEY_CENTER, INPUT_PULLUP);
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
int farbe = 0;
while (digitalRead(KEY_CENTER) == HIGH) { // bis die Mitte gedrückt wird
if (farbe == 0) {
farbe = 1;
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, LOW);
} else {
farbe = 0;
digitalWrite(LEDR, LOW);
digitalWrite(LEDG, HIGH);
}
delay(50);
}
}
void loop() {}
Beispiel: tiefer geschachtelt
Auch tiefere Verschachtelungen sind möglich – hier steckt eine Verzweigung in zwei Schleifen. Sie lässt genau die Punkte weg, bei denen i * 2 und j gleich sind, und reißt so eine Lücke in das Punktemuster.
#include <U8g2lib.h>
#include <SPI.h>
U8G2_SH1106_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, 10, 8, 9);
void setup(void) {
u8g2.begin();
u8g2.clearBuffer();
u8g2.sendBuffer();
for (int i = 0; i < 8; i = i + 1) {
for (int j = 0; j < 16; j = j + 1) {
if (i * 2 != j) { // Punkte mit i * 2 == j auslassen
u8g2.drawPixel(j * 8, i * 8);
delay(20);
u8g2.sendBuffer();
}
}
}
}
void loop() {}
Übung
Zeichne mit zwei geschachtelten Schleifen ein Schachbrettmuster aus gefüllten Rechtecken (drawBox()) der Größe 8 × 8 auf das Display. Tipp: Ob ein Feld schwarz sein soll, verrät dir eine Verzweigung über die beiden Schleifenvariablen. Bei einem Schachbrett ist die Summe aus Zeile und Spalte abwechselnd gerade und ungerade. Das prüfst du mit dem Rest-Operator aus dem Kapitel Variablen: (i + j) % 2 == 0 ist genau dann wahr, wenn i + j gerade ist.