Tinkercad Übung 5 - LED Blinken


Dein erstes Arduino-Programm

In dieser Übung schließt du eine LED an einen Arduino an und lässt sie blinken.
  • Empfohlenes Vorwissen: Bedienung Tinkercad Circuits, LED, LED-Vorwiderstand
  • Neue Inhalte: Arduino als Spannungsversorgung, Arduino-Befehle, Fehlersuche bei Kompilier-Fehlern, LED ein-/ausschalten/blinken lassen, Verzögerung

Schritt 1 - Kompilier-Fehler

Arduino programmiert man mit einem Dialekt der Programmiersprache C++. Die Anweisungen, was also bei EV3 die Blöcke und deren Verbindungen waren, werden als Text geschrieben. Hierfür gibt es feste Regeln, damit das Programm auch in Maschinencode übersetzt werden kann, das Datenformat, dass der Mikrocontroller versteht. Diese Regeln sind wie die Grammatik bei echter Sprache und bilden die Syntax. Das Übersetzung von Programmcode in Maschinencode nennt man Kompilieren, das Programm, welches die Übersetzung durchführt, den Compiler. Der "versteht" dein Programm aber nur, wenn du dich auch an die Syntax-Regeln hältst. Sonst gibt es einen Fehler und dein Programm kann nicht übersetzt werden.

Das werden wir jetzt einmal testen:

Erstelle einen neuen Entwurf. In der Dem Auswahlfenster oben rechts wähle "Starter Arduino" und ziehe den Starter "Auf-/Abblenden" in den Arbeitsbereich. Starte die Simulation, um diese Demo zu testen. Das Programm ist bereits geschrieben. Öffne den Programmcode. Momentan ist alles graphisch programmiert. Wir wollen aber textbariert in C++ programmieren. Stelle in dem Auswahlfenster unter "Code" auf "Text" um und bestätige mit "Weiter". Nun siehst du den C++ Code.
Abbildung 1 - Texteditor öffnen

Bisher sollte alles funktioniert haben und die Simulation läuft. Das Programm konnte erfolgreich in Maschinencode übersetzt werden. Schreibe vorn in die erste Zeile ein "ä" und starte die Simulation.
Abbildung 2 - Kompilier-Fehler: komisches "ä"

FEHLER! "ä" ist kein gültiger Programmierbefehl und kann nicht übersetzt werden.
Die Fehlermeldung ist allerdings recht kryptisch und auch noch auf englisch:

1:1: error: stray '\303' in program 1:1: error: stray '\244' in program

Oft wirst du die genaue Fehlermeldung nicht verstehen. Es ist also wichtig zu wissen, wo  man suchen kann. Das "1:1" am Anfang der Fehlermeldung sagt uns, dass der Fehler vermutlich in Zeile 1, Spalte 1 passiert ist. Das wird im Texteditor auch rot markiert. Du schaust dir also diese Zeile genau an und siehst, dass sich dort ein "ä" hin verirrt hat, was da nicht hingehört.

Entferne das "ä" wieder und schreibe es an eine andere Stelle im Programmcode. Starte die Simulation und untersuche wie sich die Position (Zeile, Spalte) in der Fehlermeldung verändert.

Schreibe mehrere "ä" in verschiedene Zeilen des Programms und schaue dir die Fehlermeldung an.

Repariere das Programm wieder (entferne alle "ä"). Starte die Simulation, um zu testen, ob wieder alles funktioniert wie am Anfang.

Schritt 2 - Jede Befehlszeile endet mit einem Semikolon:

Bei EV3 bestimmt die Reihenfolge der Ausführung, in welcher Reihenfolge die Blöcke miteinander
verbunden sind:
Abbildung 3 - EV3: Zwei Töne abspielen
C++ Programme werden hingegen von oben nach unten, von links nach rechts gelesen. Jeder Befehl muss mit einem Semikolon abgeschlossen werden:

tonAbspielen(A5, 1, 100, 0);
tonAbspielen(A5, 1, 100, 0);

Was in EV3 2 Blöcke waren, sind jetzt 2 Befehlszeilen. In der Regel schreibt man das auch so, weil es für alle einfacher ist. Man kann denselben Code aber auch so schreiben:

tonAbspielen(A5,1,100,0);tonAbspielen(A5,1,100,0);

oder so:

tonAbspielen(      A5,
      1,  
100     ,     0
)
;
tonAbspielen  (
  A5,
  1,
  100,
  0
);

In gewissen Grenzen kann man also sogenannten Whitespace (Leerzeichen, neue Zeilen, Tabs) hinzufügen wo und wie einem das passt. Das Beispiel oben ist natürlich nicht mehr sonderlich lesbar… Damit der Compiler weiß, wann eine Befehls-„Zeile“ zu Ende ist, setzt man an das Ende jeder Befehlszeile eine Semikolon, weil wie oben gezeigt, muss eine Befehlszeile nicht zwingend in eine Zeile geschrieben werden.

Probiere das mal aus und füge in dem Demo-Programm ganz viel Whitespace hinzu. Starte die Simulation, um zu testen, ob es noch funktioniert.

Entferne eines der Semikolons. Das sollte zu einem Fehler führen:
Abbildung 4 - Compilerfehler: Fehlendes Semikolon

In function 'void setup()':
18:1: error: expected ';' before '}' token

Die Fehlermeldung sagt uns, dass vor dem Zeichen "}" (Zeile 18) ein Semikolon erwartet wäre. Ein bisschen verwirrend ist, dass wir den Fehler ja in Zeile 17 erzeugt haben, der Compiler den aber in Zeile 18 erst findet.

Repariere das Programm, indem du in Zeile 18 direkt vor "}" ein Semikolon schreibst:

17  pinMode(9, OUTPUT)
18  ;}

Das Repariert den Fehler, ist aber nicht sehr schön, weil das Semikolon ja eigentlich den Befehl "pinMode(9, OUTPUT)" abschließt und deswegen auch lieber wieder am Ende von Zeile 17 hinzugefügt werden sollte.

Schritt 3 - Schaltung aufbauen

Genug der Fehler! Wir wollten eigentlich eine LED blinken lassen.

Erstelle einen neuen Entwurf, baue folgende Schaltung auf und starte die Simulation:
Abbildung 5 - LED versorgt vom Arduino

Findige Beobachter werden feststellen, dass es sich hierbei wieder um einen Stromkreis mit einer LED-Leuchte und einem strombegrenzenden Vorwiderstand handelt, nur ist dieser diesmal nicht direkt an eine Batterie angeschlossen, sondern an den Arduino. Der Arduino wird ja bereits vom USB-Kabel mit 5V Spannung versorgt. Es wird also keine Batterie benötigt. Die 5V Spannung können wir an dem Arduino an verschiedenen Stellen abgreifen:
  • Pin "5V" ist Plus-Pol und über das Arduino-Board mit dem Plus-Pol des USB verbunden.
  • Pin "GND" ist Minus-Pol und mit dem Minus-Pol des USB verbunden.
Manche Pins kommen am Arduino mehrere Male vor. "GND" z.B. gibt es direkt daneben nochmal. Sind die Pins mit dem gleichen Namen beschriftet, sind sie auch miteinander verbunden. Es ist also egal, welchen der "GND" Pins man als Minus-Pol hernimmt. Probiere doch mal den anderen GND-Pin aus.

Der dazugehörige Schaltplan sieht so aus:
Abbildung 6 - Stromkreis LED-Leuchte versorgt vom Arduino

Das ist jetzt aber wieder eine Schaltung wie aus Übung 3 und wir können die LED nicht programmieren, weil sie direkt an der Spannungsversorgung dran hängt.

Baue die Schaltung so um wie in Abbildung 8 gezeigt:
Abbildung 7 - LED mit Vorwiderstand an einen Arduino Uno angeschlossen
Jetzt hängt die LED nicht mehr an "5V", sondern an Pin "13". Dieser Pin ist programmierbar, das heißt man kann ihn per Programm-Befehl ein- und ausschalten.
  • Eingeschaltet = 5V
  • Ausgeschaltet = 0V = GND
Der dazugehörige Schaltplan sieht so aus:
Abbildung 8 - LED mit Vorwiderstand an Pin 13 angeschlossen

Schritt 4 - Struktur eines Arduino-Programms

Öffne den "Code"-Editor und füge folgenden Programm-Code hinzu (kannst du von hier kopieren):

void setup()
{

}

void loop()
{

}

Das ist die Grundstruktur eines jeden Arduino-Programms. Es gibt 2 Bereiche: "setup" und "loop". Diese Bereiche heißen "Funktionen". Was das genau bedeutet, lernst du später kennen. Du kannst sie dir wie "Eigene Blöcke" in der EV3-Programmiersprache vorstelle, d.h. man kann dort Programm-Befehle reinschreiben und sie werden gewissermaßen gruppiert.
  • setup() wird einmal bei Programmstart ausgeführt. Man nutzt sie typischerweise für Initialisierungen (z.B. definieren, welcher Motor an welchem Pin angeschlossen ist).
  • loop() wird nach setup() immer wiederholt, ist also die Hauptprogrammschleife.
So sähe das in EV3 aus:
Abbildung 9 - setup() und loop() als EV3-Code

Schritt 4 - Einen Ausgang ansteuern

Wir wollen die LED nun aufleuchten lassen. Füge dazu folgende Befehle in setup() hinzu:

pinMode(13, OUTPUT);
digitalWrite(13, HIGH);

Starte die Simulation. Die LED sollte jetzt aufleuchten.

Die interner LED auf dem Arduino-Board leuchtet übrigens auch mit auf. Das liegt daran, dass auch sie an Pin 13 angeschlossen ist.
Abbildung 10 - LED aufleuchten lassen
pinMode(13, OUTPUT) konfiguriert Pin 13 als Ausgang, d.h. es geht eine Information aus dem Arduino heraus. Es gibt auch INPUT. Da würde eine Information in den Arduino hineingehen, z.B. bei einem Sensor. Wenn wir den Pin nicht als Ausgang konfigurieren, können wir ihn nicht einschalten.

digitalWrite(13, HIGH) schaltet Pin 13 an. Es gibt auch LOW. Damit würde man einen Pin ausschalten.

Um den Umstieg von der graphischen LEGO EV3-Programmiersprache auf C++ zu erleichtern, kannst du das "Cheat Sheet" zu Hilfe nehmen, welches EV3-Beispielprogramme deren C++ Entsprechung gegenüberstellt. Solltest du einmal vergessen wie genau man zum Beispiel eine Verzweigung oder eine Schleife in C++ programmiert, kannst das dort wieder schnell nachschlagen. Wir werden ab jetzt häufig das Cheat Sheet zu Hilfe nehmen.

Öffne das Cheat Sheet: https://www.dropbox.com/s/k0hw31v07y1ykk6/Cheat%20Sheet.pdf?dl=0
Lade es dir am besten auch herunter.

Cheat Sheet:
Abbildung 11 - Cheat Sheet: Befehlssequenz
Das Beispiel in Cheat Sheet zeigt dir wie solche Arduino Befehle funktionieren: Das EV3-Programm links beinhaltet 2 Motorblöcke. Diese haben jeweils 1 Einstellungsmöglichkeit: Die Geschwindigkeit, auf die der Motor eingestellt werden soll (75%).

Die Befehle setzeMotorAGeschwindigkeit und setzeMotorBGeschwindigkeit gibt es bei Arduino nicht, aber gäbe es sie, würde man die Einstellung Motorgeschwindigkeit=75 in die Klammern schreiben wie in Abbildung 11 gezeigt.

Diese Einstellungen heißen "Parameter". Sie stehen immer in Klammern und werden durch Kommata voneinander getrennt . Für unsere 2 verwendeten Befehle bedeuten sie folgendes:
  • pinMode(13, OUTPUT): Befehlt heißt "pinMode"
    • Parameter 1: Pin=13
    • Parameter 2: Richtung=Ausgang
  • digitalWrite(13, HIGH): Befehl heißt "digitalWrite"
    • Parameter 1: Pin=13
    • Parameter 2: Zustand=Eingeschaltet
Wenn du dich nicht mehr erinnern kannst wie ein Befehl genau heißt oder welche Parameter man angeben muss, kannst du es auf der offiziellen Arduino-Seite nachschauen:

Suche die beiden Befehle und schaue dir deren Beschreibung an.

Füge eine dritte Befehlszeile hinzu, die die LED wieder ausschaltet. Starte die Simulation. Was beobachtest du?

Schritt 5 - Künstlich verzögern

Vermutlich hast du gerade beobachtet und auch erwartet, dass die LED sehr kurz aufleuchtet und dann sofort wieder ausgeht. Auf einem echten Arduino würde das so schnell gehen, dass wir mit bloßem Auge das Aufleuchten nicht einmal sehen würden, sondern nur mit einer Zeitlupen-Kamera. Die Simulation läuft allerdings wesentlich langsamer als ein echter Arduino.

Wenn wir das Aufleuchten irgendwie sichtbar machen wollen, müssen wir zwischen dem Ein- und Ausschalten der LED irgendwie verzögern.

Dafür gibt es einen Befehl: delay()

Öffne die Arduino Referenz und lese dir die Beschreibung des Befehls "delay()" durch.

Schreibe ein Programm, dass
  1. Pin 13 als Ausgang konfiguriert
  2. Pin 13 einschaltet
  3. 1 Sekunde (1000 Millisekunden) verzögert
  4. Pin 13 wieder ausschaltet
Abbildung 12 - Verzögerung

Momentan musst du die Simulation immer wieder neu starten, um die LED wieder einzuschalten, da der Code sich im setup()-Bereich befindet, der ja nur einmal bei Programmstart ausgeführt wird.

Schreibe ein Programm, dass
  1. Einmalig Pin 13 als Ausgang konfiguriert und dann wiederholt:
    1. Pin 13 einschaltet
    2. 1 Sekunde (1000 Millisekunden) verzögert
    3. Pin 13 wieder ausschaltet
    4. 1 Sekunde (1000 Millisekunden) verzögert
Abbildung 13 - LED blinken
digitalWrite() funktioniert bei jedem durchnummerierten Pin des Arduino (also bei Pins 0 bis 13 und A0 bis A5). Schließe die LED an einen anderen Pin an und verändere dies auch im Programm-Code. Starte die Simulation, um zu testen, dass es noch funktioniert. Die interne LED auf dem Arduino-Board wird allerdings nicht mehr mit blinken, da sie ja immer noch an Pin 13 angeschlossen ist, den du jetzt nicht mehr verwendest.

Schritt 6 - Morsen

Schreibe ein Programm, dass das SOS-Notsignal als Morsecode blinkt.
Blink-Code Wiederholung: kurz-kurz-kurz-pause-lang-lang-lang-pause-kurz-kurz-kurz-pause-pause
Abbildung 14 - SOS Morse-Code

Zusammenfassung:

Du weißt jetzt, wie man eine LED an einen Arduino anschließt, ein- und ausschaltet. Du hast den Verzögerungs-Befehl kennen gelernt und damit Blink-Codes programmiert. Du weißt wie ein grundlegendes Arduino-Programm auszusehen hat, wo du Arduino-Befehle nachschlagen kannst und wie du Kompilier-Fehler behebst.




Kommentare

Kommentar veröffentlichen

Beliebte Posts aus diesem Blog

Tinkercad Übung 6 - LED mit Taster ansteuern

Tinkercad Übung 11 - LED dimmen

Tinkercad Übung 15 - Ultraschallsensor auslesen