Arduino

Von Zahlen und Buchstaben – Variablen

Du speicherst Zahlen, rechnest damit und gibst Werte an den PC aus.

Werte aufbewahren

Variablen! – Du kennst sie aus dem Matheunterricht, und in der Programmierung haben sie eine ähnliche Bedeutung. Eine Variable in der Informatik ist wie eine Schublade, in der du Daten wie Text, Zahlen oder sogar Bilder aufbewahren kannst.

Ein Schrank mit zwei Schubladen: int xPos enthält 10, die herausgezogene Schublade double radius bekommt den Wert 5.0
Sie hat einen Typ, der die Art des Inhalts bestimmt, einen Namen, den du selbst wählst, und einen Wert, den du ihr zuweist. Den Namen solltest du so wählen, dass er den Inhalt beschreibt – du brauchst ihn später, um auf die Variable zuzugreifen.

Bevor du eine Variable verwenden kannst, musst du sie deklarieren. Dazu gibst du Datentyp und Name nacheinander an. Zusätzlich kannst du sie direkt mit einem Anfangswert initialisieren. Folge diesem Muster:

Datentyp Variablenname;

bzw.

Datentyp Variablenname = Initialwert;

Datentypen

Einige wichtige Datentypen sind in der Tabelle zusammengefasst. Es gibt noch weitere, über die du sicherlich stolpern wirst.

DatentypInhaltBeispiel
intganze Zahlen42
doubleKommazahlen3.1415
booleanWahrheitswertetrue
chareinzelnes Zeichen'A'
StringText"Hallo"

Einige Beispiele zur Deklaration und Initialisierung:

int punkte = 120;
int punkteMitBonus = punkte + 50;
double tempo = 1.5;
boolean spielLaeuft = true;

Wie du in der zweiten Zeile siehst, darf auf der rechten Seite auch eine Rechnung stehen – sogar eine, die andere Variablen verwendet.

Für den Namen gelten ein paar Regeln: Er besteht aus Buchstaben, Ziffern und Unterstrichen und darf nicht mit einer Ziffer beginnen. Leerzeichen sind nicht erlaubt, Umlaute und ß lässt du am besten weg. Groß- und Kleinschreibung zählt, punkte und Punkte sind also zwei verschiedene Variablen. Besteht ein Name aus mehreren Wörtern, schreibt man das nächste Wort einfach groß wie in punkteMitBonus.

Rechnen mit Variablen

Zum Rechnen gibt es +, -, * und /, dazu % für den Rest einer Division. Das Gleichheitszeichen bedeutet dabei etwas anderes als in der Mathematik: Es ist ein Befehl. Zuerst wird die rechte Seite ausgerechnet, dann landet das Ergebnis in der Variablen links. Deshalb ist punkte = punkte + 5; kein Widerspruch, sondern erhöht punkte um 5.

Aufpassen musst du beim Teilen: Sind beide Zahlen ganzzahlig, ist auch das Ergebnis ganzzahlig.

int a = 7 / 2;      // a ist 3, nicht 3.5: Die Nachkommastellen fallen weg
int rest = 7 % 2;   // rest ist 1: 7 geteilt durch 2 ist 3, Rest 1
double b = 7 / 2;   // b ist 3.0: 7 / 2 ist schon 3, bevor es in b landet
double c = 7.0 / 2; // c ist 3.5: Eine Kommazahl in der Rechnung genügt

Weil man Variablen so oft verändert, gibt es dafür Kurzformen. Sie begegnen dir in den nächsten Kapiteln immer wieder:

int punkte = 10;
punkte = punkte + 5;   // punkte ist jetzt 15
punkte += 5;           // Kurzform, punkte ist jetzt 20
punkte /= 4;           // Kurzform für punkte = punkte / 4, punkte ist jetzt 5
punkte++;              // Kurzform für punkte = punkte + 1, punkte ist jetzt 6

Beispiel: int und double

Das folgende Programm zeichnet immer größere Kreise, die immer weiter nach rechts rutschen. Beide Werte stecken in Variablen und werden nach jedem Kreis verändert.

#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();

  double radius = 5;                  // Deklarieren und mit 5 initialisieren
  int xPos = 10;                      // Deklarieren und mit 10 initialisieren

  u8g2.drawCircle(xPos, 32, radius);  // Kreis mit Radius radius um (xPos,32)
  radius = radius * 1.2;              // Neuer Wert: alter radius mal 1.2
  xPos = xPos + 10;                   // Erhöht xPos um 10
  u8g2.drawCircle(xPos, 32, radius);  // Kreis mit Radius radius um (xPos,32)
  radius = radius * 1.2;              // Neuer Wert: alter radius mal 1.2
  xPos = xPos + 10;                   // Erhöht xPos um 10
  u8g2.drawCircle(xPos, 32, radius);  // Kreis mit Radius radius um (xPos,32)
  radius = radius * 1.2;              // ...
  xPos = xPos + 10;                   // ...
  u8g2.drawCircle(xPos, 32, radius);  // ...
  radius = radius * 1.2;              // ...
  xPos = xPos + 10;                   // ..
  u8g2.drawCircle(xPos, 32, radius);  // .
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);
  radius = radius * 1.2;
  xPos = xPos + 10;
  u8g2.drawCircle(xPos, 32, radius);

  u8g2.sendBuffer();                  // Displayspeicher anzeigen
}

void loop() {}

Dass sich hier dieselben drei Zeilen immer wiederholen, ist unschön – im Kapitel Schleifen siehst du, wie man das in drei Zeilen erledigt.

Anmerkungen

  • Wird eine Variable innerhalb einer Funktion deklariert – z. B. in setup() –, so ist sie nur dort und erst ab dieser Zeile nutzbar.
  • Wird eine Variable außerhalb aller Funktionen deklariert, also ganz oben im Programm, so können alle Funktionen sie nutzen. Man nennt sie dann global.
  • Der Radius ist hier ein double, weil beim Multiplizieren mit 1,2 Kommazahlen entstehen. Die Grafikbefehle rechnen intern mit ganzen Pixeln und schneiden die Nachkommastellen ab.

Ausgabe von Variablen

Oft ist es nützlich, den Inhalt von Variablen an den PC zu senden – sei es zur Fehlersuche oder um Messwerte des eingebauten Beschleunigungssensors am PC weiterzuverarbeiten.

void setup(void) {
  Serial.begin(9600);                      // Serielle Verbindung zum PC starten
}

void loop() {
  int zufall = random(1, 100);             // Zufallszahl von 1 bis 99
  Serial.print("Aktuelle Zufallszahl: ");  // Text senden, kein Zeilenumbruch
  Serial.println(zufall);                  // Mit Zeilenumbruch ("ln" = line)
  delay(500);                              // Warten, sonst rauscht alles durch
}

Nach dem Hochladen ist zunächst nichts zu sehen: Es erscheint nichts auf dem Display und die LEDs bleiben dunkel, denn der PicoBoy sendet seine Ausgabe über USB an deinen PC. Führe diese Schritte aus, um sie zu sehen:

  1. Wähle unter „Werkzeuge“ → „Port“ den (virtuellen) seriellen Port aus, an dem der PicoBoy hängt. Das ist der Eintrag, der beim Ein- und Ausstecken des PicoBoy erscheint und verschwindet. In Klammern stehen dort oft mehrere Boardnamen, das ist normal. Unten rechts in der Statusleiste steht danach „Raspberry Pi Pico“ mit dem gewählten Port.
    Den seriellen Port auswählen
  2. Öffne den seriellen Monitor über „Werkzeuge“ → „Serieller Monitor“ oder über das Lupen-Symbol rechts oben in der Werkzeugleiste.
    Den seriellen Monitor öffnen
  3. Stelle im seriellen Monitor die Geschwindigkeit auf denselben Wert ein, der im Programm bei Serial.begin() steht – hier also 9600.
    Die Geschwindigkeit im seriellen Monitor einstellen
  4. Die Zufallszahlen sollten nun zu sehen sein!

Anmerkung: Beim PicoBoy läuft die serielle Verbindung über USB, deshalb wird die Zahl in Serial.begin() ignoriert. Bei vielen anderen Arduino-Boards müssen die Geschwindigkeiten im Programm und im Monitor dagegen übereinstimmen, sonst erscheinen wirre Zeichen statt lesbarem Text. Stelle also am besten beide gleich ein.

Noch ein Stolperstein: Was der PicoBoy sendet, bevor der serielle Monitor verbunden ist, geht verloren. In loop() merkst du davon wenig, weil ständig neue Ausgaben folgen. Sendest du dagegen nur einmal etwas in setup(), lass das Programm vorher warten:

void setup(void) {
  Serial.begin(9600);
  while (!Serial) {}  // warten, bis der serielle Monitor verbunden ist
  Serial.println("Jetzt geht es los");
}

void loop() {}

Die Zeile mit while ist eine Schleife, die du bald kennenlernst. Läuft der PicoBoy ohne PC, wartet er an dieser Stelle für immer. Nimm sie also wieder heraus, wenn dein Programm auf Batterie laufen soll.