Sturzi's Arduino-Bastel-Projekt #1 - Es werde Licht

  • Das erste Arduino-Programm (LED blinken lassen), zuerst die dumme Variante


    Dies wird unser erstes Arduino-Programm sein. Was ich im Titel angedeutet habe, mit der dummen Variante, werde ich im nächsten Beitrag erklären. Diese dumme Variante hat aber einen grossen didaktischen Vorteil: Sie ist eklatant einfach und damit lässt sich der Ablauf eines Arduino-Programmes sehr gut erklären.


    Erst zur Schaltung: Wir übernehmen die bisherige Schaltung und ziehen Arduino-seitig den Stecker am roten Kabel aus und stecken ihn in Pin 7. Das ist hardware-mässig schon alles. Siehe nächstes Bild. Die LED wird jetzt nur noch leuchten, wenn der Pin 7 per Programm auf HIGH gesetzt worden ist.



    Und jetzt gehts an die Programm-Erstellung. Am Compi muss die Entwicklungs-Umgebung laufen und immer noch (oder wieder) über USB mit dem Uno verbunden sein.

    Wenn immer noch das Leer-Programm ersichtlich ist, ist das die richtige Voraussetzung. Andernfalls Menu File / New. Ich zeig's nochmals im nächsten Bild.



    Man sieht zwei Funktionen, setup und loop. Jede Funktion hat einen Deklarations-Teil, nämlich void setup(), bzw. void loop(), sowie einen Body. Der jeweilige Body ist alles, was zwischen den beiden geschweiften Klammern steht. Die Programmiersprache, die wir hier programmieren, heisst C++. Obwohl wir hier (für einfache Anwendungen) nur mit einem Minimum an C++ konfrontiert werden, müssen wir uns an die Regeln des C++ halten.

    Eine an dieser Stelle schon mal sehr wichtige Regel ist die strikte Gross- / Kleinschreibung. Also void darf nicht Void oder VOID heissen. setup darf nicht Setup heissen.


    Das zweite, was man hier an dieser Stelle gut erklären kann (und muss), sind die Kommentare. Man sieht hier zwei davon, in jeder Funktion einen. Alles was auf einer Zeile hinter // geschrieben ist, wird vom Compiler (Programm-Übersetzer) im Arduino-System ignoriert. Die Kommentare sind Texte, die der Programmierer einfügt, damit der (menschliche) Leser des Programmes dieses besser versteht. Das kann jemand anderes sein, oder der Programmierer selber zu einem späteren Zeitpunkt, wenn er selbst nicht mehr weiss, was er früher verbrochen hat :) .


    Nun werden Arduino-Befehle in die beiden Funktionen setup() und loop() eingefügt. Nach dem compilieren (übersetzen) und uploaden des Programms zum Arduino werden diese Befehle einer nach dem anderen ausgeführt.


    Der Unterschied zwischen setup() und loop(): In der Funktion setup() werden die Befehle sequentiell nach dem Programm-Start (das ist automatisch nach dem Upload) einmalig ausgeführt. In der Funktion loop() werden die Befehle unmittelbar nach dem Beenden des setup() sequentiell ausgeführt. Aber wenn der Programm-Ablauf das Ende der loop() Funktion erreicht hat, beginnt er wieder von oben. Das ist der Grund, warum die Funktion loop() heisst (Schleife). Hier ist es sogar eine Endlos-Schleife, die mit dem Loopen erst am Nimmerleinstag aufhört, oder wenn man dem Arduino den Strom wegnimmt oder wenn man die Reset-Taste betätigt.


    Wir fügen nun die Arduino-Befehle gemäss folgendem Bild in die beiden Funktionen ein.



    Ich habe die existierenden Kommentare gelöscht, dafür meine eigenen eingebaut. Diese Kommentare erklären eigentlich alles, was es bezüglich Programm hier zu erklären gibt.


    Nun müsstest du das Programm hochladen (wie schon erwähnt mit dem Rechts-Pfeil in der grünen Kopf-Zeile).


    Wenn nun deine LED ziemlich schnell blinkt (zweimal ein und aus pro Sekunde), hast du die kritische Hürde geschafft und dein erstes Arduino-Programm zum Laufen gebracht. Herzliche Gratulation! Du darfst deinen Erfolg hier gern posten, dann freue ich mich mit dir.


    Falls es nicht gelungen ist, besteht ein Problemchen, das es zu lösen gilt. Lies nochmals alle meine Anweisungen genau durch. Prüfe ob du alles genau so gemacht hast. Bedenke: Ein Compiler ist pingelig. Ein fehlender Strichpunkt am Ende eines Befehls kann die Ursache sein. Wenn du allein nicht weiterkommst, melde dich bei mir. Ich versuche gerne zu helfen.


    Ich habe oben geschrieben, dass diese Kommentare eigentlich alles erklären, was es bezüglich Programm zu erklären gilt. Nun, das stimmt nicht ganz. Ich habe doch noch einiges zu erklären. Das mache ich aber im nächsten Beitrag und dann erkläre ich auch gleich, warum das eine dumme Lösung ist.


    Halt, ich habe noch etwas vergessen: Bevor wir heute mit Arbeiten aufhören, müssen wir noch das Programm speichern. Das geht im File-Menu mit der "Save as"-Funktion. Speichere das Programm dort, wo die Entwicklungs-Umgebung es vorschlägt. Gib ihm einen coolen Namen, z.B. "Dummes Blink-Programm". Eine Datei-Endung ist nicht nötig, das macht die IDE automatisch. Mehr dazu erkläre ich später.

  • Röbi, auch wenn das eine ‚dumme‘ Variante sein soll. Langsam fange ich Feuer. So wie du das erklärst, würde ich am Kurs auch gern in natura teilnehmen. Ich bestelle glaub auch ein Starter-Set.

    Allerdings habe ich bereits eine Frage: woher kennst du die Befehle, die der Arduino versteht?

    ‚DigitalWrite‘ zum Beispiel? Wieviele verschiedene Befehle kennt denn der Apparat?

    Gruss Roger


    95 von 121 grünen Ae 6/6


    Die Katze schläft im Lärm; nur die Stille weckt sie, wenn die Mäuse rascheln.

  • So wie du das erklärst, würde ich am Kurs auch gern in natura teilnehmen.

    Roger, du kannst dich bei mir zu einem Praktikum anmelden. Mit einem Bier bist du dabei. Dann kannst du auch gleich noch die ChRB III inspizieren.


    Allerdings habe ich bereits eine Frage: woher kennst du die Befehle, die der Arduino versteht?

    ‚DigitalWrite‘ zum Beispiel? Wieviele verschiedene Befehle kennt denn der Apparat?

    Das ist eine sehr gute Frage und ich bin froh, dass du sie gestellt hast. Auf der Arduino-Website sind auf dieser Seite alle Arduino-Befehle hervorragend erklärt. Wenn du auf einen der Befehle klickst, erscheint Detail-Info zu dem Befehl.


    Ich habe die zur Auswahl stehenden Befehle jetzt nicht gezählt. Aber es dürften zwischen 60 und 70 sein. Lass' dich jetzt nicht durch die Anzahl abschrecken. Ich selber habe bis jetzt kaum mehr als 10 davon gebraucht.

  • Jedes Arduino-Programm hat die beiden Funktionen setup() und loop(). setup() wird beim Programm-Start genau einmal durchlaufen (ausgeführt). Nach Beendigung des setup() wird loop() endlos durchlaufen.


    Jede Funktion hat einen Body. Das ist der Teil innerhalb des zur Funktion gehörenden geschweiften Klammer-Paares. Im Body platziert man seine Anweisungen.


    Unsere Anweisungen sind pinMode(), digitalWrite() und delay(). Aus der Sicht des Arduino-Programmierers sind dies Arduino-Befehle. Aus der Sicht des C++ Programmierers sind das Funktions-Aufrufe. In der Tat wurden die Arduino-Befehle von den Entwicklern der IDE als C++ Funktionen implementiert, die wir (die Arduino-Programmierer) nach Bedarf aufrufen. Das ist das, was die Arduino-Programmierung so einfach macht. Die anspruchsvolleren Konstrukte des C++ sind in diesen Funktionen verborgen.


    Wenn wir nun eine solche vordefinierte Funktion aufrufen, wollen wir in der Regel der Funktion mitteilen, was wir genau von ihr wollen. Diese Mitteilungen fügen wir innerhalb der zur Funktion gehörenden runden Klammern bei. Diese Angaben nennt man Argumente (oder auch Parameter).


    Schauen wir uns das am Beispiel von digitalWrite() an. Mit digitalWrite() wollen wir den Arduino veranlassen, auf einem bestimmten Pin eine Ausgabe-Funktion auszuführen. Die Funktion ist so implementiert, dass beim Aufruf zwei Argumente erwartet werden. Mit dem ersten Argument teilt der Arduino-Programmierer mit, um welchen Pin es jetzt geht und im zweiten Argument um was für eine Operation es sich handelt (Ausgang auf HIGH oder auf LOW setzen). Im ersten Argument erwartet die Funktion eine ganze Zahl, die die Pin-Nummer angibt und im zweiten Argument wird LOW oder HIGH erwartet. Die beiden Argumente müssen durch ein Komma getrennt werden.


    Jeder Befehl muss durch ein Semikolon abgeschlossen werden. Den nächsten Befehl auf eine neue Zeile zu schreiben reicht nicht. Wir könnten das ganze Programm auch so schreiben:


    void setup(){pinMode(7,OUTPUT);}void loop(){digitalWrite(7,HIGH);delay(250);digitalWrite(7,LOW);delay(250);}


    Der Compiler würde das Programm immer noch verstehen, wir Menschen aber nicht mehr. Damit will ich vor Allem zeigen, dass die Befehle nicht durch die Zeilen-Enden, sondern durch die Semikolons getrennt werden.


    Wenn wir es schon von der Lesbarkeit durch Menschen haben: Es ist sinnvoll, das Programm so zu formatieren, wie es in der Programmierung üblich ist. Dazu gehört auch das Einrücken. Bei jedem Block innerhalb eines geschweiften Klammern-Paares wird um eine Tabulator-Position weiter eingerückt als ausserhalb.


    Und jetzt will ich noch erklären, warum unser Blink-Programm ein dummes Konzept ist. Der Arduino ist fähig, die Befehle innerhalb der loop() Funktion rasend schnell auszuführen (*). Das macht ihn sehr leistungsfähig. Wenn wir nun einen delay() einbauen, macht er während der angegebenen Zeitdauer nichts. In unserem dummen Programm schafft der Arduino nur 2 Schleifendurchgänge pro Sekunde. Damit ist der Arduino schon zu 100% ausgelastet, um nur eine einzige LED blinken zu lassen. Wenn wir nur zwei LED unabhängig voneinander blinken lassen wollten, bräuchten wir dazu bereits einen zweiten Arduino.


    Im nächsten Schritt werden wir noch beim dummen Programm bleiben und die LED in unregelmässigen (zufallsgesteuerten) Intervallen blinken lassen. Anschliessend werden wir dann ein intelligentes Blink-Programm entwickeln.



    (*) Ich habe gestern mal gemessen, wie schnell er durch die loop() Funktion rasen kann. Dazu habe ich im loop() ein Minimal-Programm erstellt, das die Schleifendurchgänge zählt und von Zeit zu Zeit eine Aktion unternimmt. Er hat pro Sekunde 26'000 Schleifendurchgänge geschafft. Das bedeutet, dass er für einen Durchgang nur 0.04 Millisekunden gebraucht hat. Für ein Gerät, das nur um die 30 Franken kostet, finde ich das schnell. Verwöhnten Benutzern von modernen Desktop- oder Schlepptop-Computern würde bei dieser Perspektive natürlich das Gesicht einschlafen.

  • Nur so aus Spass: Ich habe jetzt grad mal auf meinem neuen Mac Studio das gleiche Schleifen-Programm in Java implementiert. Das schafft pro Sekunde 1.2 Milliarden Schleifen-Durchgänge. Das sind 0.83 Nanosekunden pro Durchlauf. Und dabei wurde nur einer von 10 Prozessor-Kernen gebraucht.

  • Röbi, langsam beginne ich einige Dinge zu verstehen, aber noch lange nicht alles.

    Es wird Zeit, dass mein Starterset kommt und ich anfangen kann zu üben. :hmm:

    Gruess Martin

  • Unregelmässiges Blinken, gesteuert mit Zufallswerten


    Wir erweitern nun die dumme Variante so, dass die beiden Warte-Phasen mit delay() nicht mehr von fixer, sondern von zufälliger Dauer sind.



    Um einen ganzzahligen Zufallswert zu bestimmen, gibt es eine praktische Funktion random(). Dieser Funktion muss man innerhalb des runden Klammernpaares zwei ganzzahlige Werte übergeben. Der Aufruf heisst also z.B. random(100, 2000). Die Funktion bringt dann einen Wert vom Datentyp long zurück, wobei dieser Wert (in unserem Beispiel) eine Zahl im Bereich 100 ... 1999 ist. Der Datentyp long ist für ganzzahlige Werte wie 17, 39, 1234567, etc. bestimmt, aber nicht für gebrochene Zahlen wie z.B. 3.14 oder 0.0012. Dass die Funktion random() einen Wert vom Typ long zurückbringt steht in der Dokumentation (https://www.arduino.cc/referen…ns/random-numbers/random/). Der Bereich der auszuwählenden Zufallszahl gilt für den unteren Wert inklusive Grenze, für den oberen Wert aber exklusiv Grenze. Darum 100 ... 1999 und nicht 100 ... 2000. Auch das steht in der Dokumentation.


    Ich habe die Implementation für das zufällig dauernde Warten absichtlich (aus didaktischen Gründen natürlich) bei den beiden delay() Funktionen unterschiedlich vorgenommen.

    Beim ersten Vorkommnis (Zeilen 7 und 8 habe ich eine lokale Variable vom Typ long mit Namen millisToWait definiert und ordne den Rückgabewert der Funktion random() dieser Variablen zu (Zeile 7). Auf Zeile 8 rufe ich die delay() Funktion und übergebe ihr den Wert der Variablen millisToWait.

    Beim zweiten Vorkommnis (Zeile 10) mache ich es direkt (ohne Zwischen-Variable). Hier wird der Rückgabewert der Funktion random() direkt der Funktion delay() als Argument übergeben. Das ist eleganter, nicht wahr? Manchmal aber, besonders wenn die Ausdrücke etwas komplexer sind als hier, empfiehlt sich der besseren Lesbarkeit des Programmes wegen, die Variante mit Zwischen-Variablen.


    Wenn man nun das Programm auf den Uno hochlädt, müsste die LED in unregelmässigen Intervallen blinken. Das Programm hochladen kann man übrigens auch mit dem Tastenkürzel ctrl U (für Windows und Linux), bzw. cmd U (für Mac), statt mit dem Rechts-Pfeil in der grünen Kopf-Zeile der IDE. Nicht vergessen das Programm zu speichern!


    Wenn man sich vorstellt, dass unsere LED in ein Faller-Häuschen als zufällig ein- und ausschaltende Beleuchtung eingebaut wird, darf sie natürlich nicht so schnell blinken. Man ändert dann einfach die Intervalle auf wesentlich grössere Werte wie z.B. delay(random(600000, 1200000)), also zwischen 10 und 20 Minuten. Falls man das vorhat, wird man ja nicht nur die Beleuchtung eines einzigen Häuschens steuern wollen, sondern vielleicht eines ganzen Dörfchens. Man braucht dann also viele LEDs, die zufällig und unabhängig voneinander ein- und ausschalten, und nicht nur eine. Das ist dann der Moment, wo wir mit der dummen Variante nicht mehr zum Ziel kommen.

    Mit dem Arduino Uno, der 14 Ein- / Ausgänge hat, könnten wir bis zu 14 LEDs steuern. Sollten es mehr sein, drängt sich der Arduino Mega auf. Der funktioniert gleich (wird auch gleich programmiert wie der Uno), hat aber 54 Ein- / Ausgänge. Die Investition für den Mega ist moderat: Ein DFRduino Mega 1280 (DFRduinos sind voll kompatibel zu den original Arduinos) kostet z.B. bei Bastelgarage CHF 21.80.

  • Guten Abend Röbi

    Mit deiner Erklärung blinkt die LED unregelmässig. :thumbup:

    Die Sache mit Random ist mir klar aber zu der Variante mit millisToWait habe ich Fragen.


    1.

    ich finde den Befehl umständlicher da ein zusätzlicher Schritt gemacht wird.

    wann macht dieser Befehl Sinn?

    Damit die LED unregelmässig blinkt reicht der Befehl delay(random(100, 2000)); doch.


    2.

    Zum experimentieren wollte ich eine zweite LED mit denselben Befehlen erweitern und auf Pin 8 anschliessen. Doch leider gibt es beim beim verifyzieren einen Fehler aus. Kannst du mir sagen was ich falsch gemacht habe?

           


    Ohne die millisToWait Befehle habe ich es hinbekommen mit zwei LEDs die unabhängig, unregelmässig blinken


         


    Entschuldigung Herr Lehrer

    Ich wollte nicht von deiner Aufgabe abschweifen aber durch das experimentieren um es besser zu verstehen sind bei mir diese Fehler aufgetreten. :wacko: :facepalm:

  • Hallo Andy

    ich finde den Befehl umständlicher da ein zusätzlicher Schritt gemacht wird.

    wann macht dieser Befehl Sinn?

    Damit die LED unregelmässig blinkt reicht der Befehl delay(random(100, 2000)); doch.

    ... dieser Schritt wird verwendet, wenn "millisTowait" an vielen Stellen vorkommt - dann muss nur einmal die Zuweisung geändert werden. Wird bei allen Stellen der Wert "hardcoded" muss er überall einzeln geändert werden. Das ist der Vorteil von Variablen (oder auch Konstanten).


    Lg
    Giorgio

    Lg

    Giorgio

  • Hi Andy


    Keine Ahnung von Arduinos, aber ich nehme an, dass beim ersten „long…“ Befehl eine Variable deklariert wird und gleich mit einem Wert befüllt wird. Anschliessend darf die Variable kein zweites Mal deklariert werden, nur noch mit einem neuen Wert befüllt werden. Vielleicht mal das zweite long weglassen und nur millisToWait = …. schreiben?

    Wie gesagt, keine Ahnung von Arduino.


    Aber ich lese interessiert mit und werde mir ebenfalls ein Set zulegen.

    Grüsse

    Rolf

    ---------------------------------------------------------------------------------------------------------------------------------

    K-Gleis, CS3 + 3xBooster, Intel Xeon, Win 10-64, iTrain 5.x PRO, uCon S88 Master, Qdecoder und ESU Servodecoder

    Fahrzeuge von Märklin, Roco, HAG, Liliput uvm.


    Meine Anlage: Homepage

    Modellbahn Verwaltung: MobaVer

    Mein YouTube Kanal: rfnetch

  • Mit deiner Erklärung blinkt die LED unregelmässig.

    Gratuliere! Gut gemacht!

    ich finde den Befehl umständlicher da ein zusätzlicher Schritt gemacht wird.

    wann macht dieser Befehl Sinn?

    Damit die LED unregelmässig blinkt reicht der Befehl delay(random(100, 2000)); doch.

    Da hast du absolut recht. Ab dem nächsten Schritt (beim gescheiten Blinkprogramm) werden wir mit Variablen arbeiten müssen. Ich wollte euch ganz süüferli in den Gebrauch von Variablen einführen.

    Dieser Umweg kann aber durchaus Sinn machen, weil bei komplizierten Ausdrücken das Programm leserlicher wird. Hier aber nicht, da ist es wie du schreibst besser, wenn man es in einem Befehl kombiniert.

    Doch leider gibt es beim beim verifyzieren einen Fehler aus. Kannst du mir sagen was ich falsch gemacht habe?

    Ja, das kann ich. Du hast die Variable millisToWait in der gleichen Funktion ein zweites Mal deklariert. Das darf man nicht.

    Du hättest beim zweiten Mal einfach das long weglassen müssen, dann wäre es gegangen. Also:

    Beim 1. Mal: long millisToWait = random(100, 2000); // Hiermit wird die Variable deklariert und ihr gleich ein Wert zugeordnet

    Beim 2. Mal: millisToWait = random(100, 2000); // Hier wird der Wert der bereits bestehenden Variablen zugeordnet.

    Aber wie du schreibst, hast du mit der direkten Variante das Problem nicht.

    Ich wollte nicht von deiner Aufgabe abschweifen aber durch das experimentieren um es besser zu verstehen sind bei mir diese Fehler aufgetreten

    Das ist sehr gut, dass du abschweifst und experimentierst. So lernt man am Meisten.

  • Andy und @alle:


    Ich habe gestern Abend spät bei Andy's Lösung noch etwas Interessantes entdeckt:


    Ohne die millisToWait Befehle habe ich es hinbekommen mit zwei LEDs die unabhängig, unregelmässig blinken

    Das stimmt nur zum Teil. Die LEDs blinken zwar unregelmässig aber nicht unabhängig. Ich habe die Dioden zwar nicht blinken sehen, aber ich sehe es aus dem Programm. Es blinkt immer zuerst die eine (ein / aus), dann die andere (ein/aus), dann wieder die erste, .... Unabhängig wäre es, wenn sich die Intervalle auch mal überlappen würden. Zudem addiert sich die Wartezeit von allen LED auf und mit weiteren LEDs würde das Blinken immer langsamer. Die Blink-Intervalle entsprechen so nicht dem geforderten Intervall 100 ... 2000. Bei zwei Dioden fällt es noch nicht so gut auf, aber wenn du noch eine dritte dazu nimmst, wird es offensichtlich.

    Das zeigt eindrücklich auf, dass das keine geeignete Lösung ist.


    Die geeignete Lösung werde ich heute noch präsentieren.

  • Unregelmässiges Blinken - jetzt die gescheitere Variante


    Wir werden hier ein paar neue Konzepte kennenlernen, nämlich den Gebrauch von globalen Variablen, die Definition von eigenen Funktionen, sowie den if-Befehl. Es wird jetzt ein klein wenig anspruchsvoller, aber die genannten Konzepte, vor Allem die Verwendung von eigenen Funktionen und der if-Befehl sind das A und O der Programmierung. Da müssen wir jetzt einfach durch.



    Zeilen 1 und 2:

    Hier deklarieren wir zwei globale Variablen, beide vom Typ long. Global sind sie, weil sie ausserhalb (und vor) den Funktionen deklariert wurden. Damit sind sie für alle Funktionen sicht- und benutzbar. Die Namen sagen eigentlich genug aus über ihren Zweck.


    setup() Funktion:

    Diese wird ja bekanntlich zu Beginn genau einmal ausgeführt. Wir bestimmen, dass der Pin 7 als Ausgang verwendet werden soll und rufen dann eine neue Funktion changeStatus(), die wir ganz unten definieren, auf. Der Aufruf dieser Funktion ändert den Ein- / Aus-Zustand der LED auf das Gegenteil. Das wäre hier an sich nicht nötig, aber die Funktion übernimmt auch noch das Aktualisieren der beiden Variablen millisOnLastStatusChange und millisToWait und das ist hier zwingend und deshalb rufen wir die Funktion auf. Lies hier die Erklärungen für die changeStatus() Funktion, bevor du zurück gehst zur loop() Funktion.


    loop() Funktion:

    Diese wird endlos (immer wieder) ausgeführt).

    Bedenke, dass (egal ob wir uns im ersten oder in einen späteren Loop-Duchlauf befinden) die beiden Variablen millisOnLastStatusChange und millisToWait aktuelle Werte enthalten, weil mindestens einmal changeStatus() gerufen worden ist).

    Zeile 10: Hier prüfen wir, ob die vorgenommene Wartezeit millisToWait schon abgelaufen ist. Dazu bilden wir die Differenz zwischen den aktuellen Millisekunden (millis()) und den Millisekunden seit dem letzten Status-Wechsel (millisOnLastStatusChange). Wenn diese Differenz grösser oder gleich ist wie die vorgenommene Wartezeit (millisToWait), sind die Bedingungen für einen neuen Status-Wechsel erfüllt. Das machen wir mit einem if-Befehl, den ich auch weiter unten genauer erklären werde. Jedenfalls wenn die Bedingung erfüllt ist (und nur dann), wird die Funktion changeStatus() aufgerufen, die den Status-Wechsel vollzieht und die beiden globalen Variablen auch gleich wieder aktualisiert. In jedem Fall (erfüllt oder nicht) geht der Programm-Ablauf danach in den nächsten Loop-Durchlauf, um das gleiche zu wiederholen.

    Es kann durchaus sein, dass die loop() Funktion ein paar hundert mal durchlaufen wird, bis die aktuellen Millis wieder so weit vorgerückt sind, dass die Bedingung endlich wieder einmal erfüllt wird. Aber keine Angst, die loop() Funktion beschwert sich nicht, wenn sie immer und immer wieder das Gleiche machen muss.


    changeStatus() Funktion:

    Im Gegensatz zu den Funktionen setup() und loop(), die von Arduino-Entwicklern implementiert worden sind, ist das eine neue Funktion, die wir jetzt und hier implementieren. Das steht uns frei und wir können beliebig viele Funktionen implementieren und aus einer anderen Stelle in unserem Programm über ihren Namen aufrufen. In dieser Funktion führen wir einen Status-Wechsel der LED aus. D.h. wenn sie aus ist, schalten wir sie ein und umgekehrt.

    Die Anweisung in Zeile 16 vollzieht den eigentlichen Status-Wechsel. Die Zeile 16 bedarf noch weiterer Erklärung. Diese werde ich weiter unten anbringen.

    Zeile 17: Das Arduino-System hat einen internen Zähler, der die seit dem Programmstart verstrichenen Millisekunden zählt. Mit dem Aufruf von millis() holen wir uns den aktuellen Stand dieses Zählers und speichern ihn in der Variablen millisOnLastStatusChange.

    Zeile 18: Hier bestimmen wir die Dauer des nächsten Warte-Intervalls mit Hilfe von random() (kennen wir ja schon) und speichern diese in der Variablen millisToWait.

    Zeile 19: Das ist das Ende unserer Funktion changeStatus(). Der Programm-Ablauf geht dort weiter, wo die Funktion aufgerufen wurde, also entweder im setup() oder im loop().


    Der if-Befehl:

    Ohne if-Befehl keine Programmierung! Deshalb sehr wichtig.

    Wenn wir im Programm-Ablauf entscheiden wollen, ob wir etwas Bestimmtes ausführen wollen oder nicht, programmieren wir einen if-Befehl. Der sieht so aus:

    if ( Bedingung ) {

    Anweisungen die nur dann ausgeführt werden, wenn die Bedingung erfüllt ist

    }

    Auf das "if" folgt eine Bedingung immer in runden Klammern. Die Bedingung ist ein logischer Ausdruck, der entweder wahr oder falsch ist. Ist der Ausdruck wahr, werden die Befehle innerhalb des geschweiften Klammernpaares ausgeführt. In beiden Fällen (wahr oder falsch) geht der Programm-Ablauf anschliessend nach der schliessenden geschweiften Klammer weiter.

    In unserem Programm heisst die Bedingung ((millis() - millisOnLastStatusChange) >= millisToWait). Zuerst wird die Differenz gebildet und diese wird dann mit millisToWait verglichen.

    Beispiel 1: millis() ergibt 1774589, millisOnLastStatusChange enthält 1774466, millisToWait enthält 250, Bedingung resultiert in "falsch", Anweisungen zwischen geschweiften Klammern werden nicht ausgeführt.

    Beispiel 2: millis() ergibt 1774773, millisOnLastStatusChange enthält 1774466, millisToWait enthält 250, Bedingung resultiert in "wahr", Anweisungen zwischen geschweiften Klammern werden ausgeführt.


    Erklärung zur Zeile 16:

    Hier setzen wir den Pin 7 entweder auf HIGH oder auf LOW. Auf was wir ihn aber letztlich setzen, hängt von seinem aktuellen Status ab. Dazu ermitteln wir den aktuellen Status mit dem Aufruf der Funktion digitalRead(7). Diese bringt HIGH oder LOW zurück. Wir wollen ihn aber auf das Gegenteil setzen also HIGH auf LOW und LOW auf HIGH. Das Gegenteil erreichen wir mit dem Ausrufezeichen, das man den Not-Operator nennt.


    Ein Hinweis zu eigenen Funktionen:

    Man kann sich jetzt fragen, warum wir gewisse Befehle in eine Funktion auslagern und nicht direkt statt des Funktions-Aufrufs codieren. Das wird meistens gemacht bei Programm-Code der sich wiederholt. Wenn eine bestimmte Code-Sequenz nur an einem Ort geschrieben werden muss, wird es kürzer, übersichtlicher und weniger störanfällig.


    Zum Schluss noch ein paar Hinweise zu den Namenkonventionen im C++.

    Konstanten (wie zum Beispiel HIGH und LOW) werden durchwegs gross geschrieben. Wird das Wort zu lang, verwendet man Underscores (z.B. LED_BUILTIN oder INPUT_PULLUP).

    Variablen und Funktions-Namen beginnen mit einem Kleinbuchstaben und gehen dann in natürlicher Gross-/Kleinschreibung weiter (z.B. pinMode, digitalWrite, millisOnStatusChange, changeStatus, etc.).

    Diese Namens-Konventionen sind nicht zwingend (also dem Compiler ist es egal, ob wir sie einhalten oder nicht), aber es macht sehr viel Sinn, weil man so gegenseitig die Programme besser versteht). Deshalb empfehle ich deren Einhaltung dringend.

  • Hoi Röbi

    Deine Feststellung ist korrekt. Das immer nur eine LED blinkte und erst dann die zweite ist mir auch aufgefallen und ich wollte dazu auch die Frage stellen wie man es durcheinander machen könnte.

    Ich habe die Frage nicht gestellt da ich dir nicht ins Konzept dreinmischen wollte, und es ja nicht zur Aufgabe gehört hat.

    Ein Unruhestifter im Schulzimmer macht kein guten Eindruck. ;(


    Meine Idee (damit ich mir sicher gewesen wäre die Befehle verstanden und selbstständig erweitert zu haben), war es ja eigentlich zwei LEDs mit zwei verschiedenen, zufälligen Parametern zB:(millisToWait = (random(100,2000)) und (millisToWait = (random(1000,10000))zu programmieren die überschneidend sein könnten.


    @Giorgio, Rolf und Röbi

    Vielen Dank für euere Erklärungen. Ich bin eben sehr neugiergig und freue mich riesig über dieses Thema Arduino und darum kommen mir so extra Ideen in den Sinn, wo ich ja am Schluss doch nicht funktionsfähig hinbekomme aber es macht grossen Spass dabei zusein.

    Danke euch.