Grundlagen der Programmierung 2

Das Kapitel Grundlagen der Programmierung 1 dient als Einführung in das Thema Arduino-Programmierung. Komplexere und essentiellere Code-Sprach-Konstrukte bezüglich der Arduino-Programmentwicklung werden von den folgenden Textkapiteln vorgestellt und behandelt.

  1. Funktionen
  2. Wiederholungen 2 (for)
  3. Serielle Kommunikation
  4. Arrays
  5. PWM
  6. Bibliotheken

Funktionen

Eine Funktion ist ein wichtiges Programmierkonzept, dass das strukturierte Programmieren ermöglicht. Dabei wird Bearbeitungscode gebündelt, der anschließend vom Hauptprogramm aus durch Aufruf des Funktionsnamens ausführt werden kann.

Die Funktion kann einen Wert zurückliefern, der anschließend durch den Quellcode weiterverarbeitet werden kann. Die Parameterübergabe wird dabei durch ein Klammerpaar eingeleitet. Diese Wertübergabe ermöglich die Modifizierung des Funktionsquellcodes.

Bei der Funktionsinitialisierung wird dessen Rückgabedatentyp spezifiziert, dessen Funktionsname festgelegt, und dessen Übergabeparameter definiert, die allerdings in Klammern aufgeführt werden müssen. Der Funktionsquellcode folgt anschließend eingeschlossen durch geschweifte Klammern und bildet dadurch den Funktionsblock. Soll die Funktion dabei keinen Wert zurückliefern, so wird der Datentyp des Rückgabeparameters auf „void“ gesetzt. Soll keine Parameterübergabe erfolgen, so bleibt die Funktionsparameterklammer leer.

Im Kapitel Grundlagen der Programmierung 1 wurden die Funktionen setup und loop vorgestellt.

Die Befehlsaufrufe „digitalRead„, „digitalWrite“ und „analogRead“ gehören zur Gruppe der Funktionen, dessen Funktionalität durch die Arduino IDE zur Verfügung gestellt werden. Folgende Abbildung visualisiert die Struktur und den Aufbau der „digitalRead„-Quellcodefunktion.

Funktionsaufruf

Funktionsaufrufe können durch deren Namensaufruf eingeleitet werden. Parameter können dabei in Klammern stehen. Folgende Abbildungen sollen den Sachverhalt nochmals verdeutlichen. Die Funktion testet die Zahlengröße ihres Parameters und initialisiert Pin6 bezüglich eines logischen Ausdrucks.

Der eigentliche Funktionsaufruf passiert im Codeblockbereich der loop – Funktion. Dabei wird ihr Parameterwert auf die Zahl 40 gesetzt. Folgende Abbildung visualisiert das Codefragment.

Der Funktionsaufruf kann dabei an jeder Stelle des Quellcodes erfolgen. Dieser Sachverhalt gilt auch für den Quellcodebereich anderer Funktionen. Funktionen können Quellcode bündeln und begünstigen so die Lesbarkeit und die Übersichtlichkeit des Programmierprojekts. Mehrmalige Quellcodedefinition werden dabei vermieden. Dabei können komplexe Berechnungen, die oft durchgeführt werden müssen, als Funktion abgebildet werden. Der Berechnungsvorgang kann dann im entsprechenden Codeblockbereich durch den Funktionsnamen eingeleitet werden. Bei Anpassung und Modifizierung des Berechnungsvorgangs muss dabei nur der Funktionsquellcode entsprechend abgeändert werden (vgl. Konstanten).

Funktionsparameter

Funktion können mit Parametern arbeiten. Diese werden entsprechenden ihres Datentyps in der richtigen Reihenfolge übernommen. Die Funktionsparameter besitzen dabei ein Zugriffsverhalten, dass den der lokalen Variabel ähnelt. Folgende Abbildung visualisiert Quellcode von Funktionen mit und ohne Parameter.

Beide Integer-Varablen sind lokal zugreifbar. Mit Hilfe der zusätzlichen Parameterdefinition könnte aber die Integer-Variabel eine Quellcode – externe Wertzuweisung erhalten. Die folgende Abbildung möchte diesen Sachverhalt nochmals verdeutlichen.

Die Parameterwerte können dabei durch ihre Zahlenwerte festgelegt werden. Dabei wird lediglich der Variablenwert in den Parameter kopiert.

Der Parameter „zahl“ hat in der Funktion „testfunktion“ nach dem Aufruf mit der Variable „original_zahl“ als Parameterübergabe den Wert 40.

Der Funktion können mehrerer Parameterwerte übergeben werden. Die Parametertrennung erfolgt durch ein einfaches Komma. Die folgende Abbildung visualisiert den Quellcode einer Funktion mit mehreren Parametern.

Ablauf

Die folgende Abbildung visualisiert einen möglichen Funktionsprogrammablauf.

Bei 1. Markierung wird Pin 6 durch die Funktion „pinMode“ als Ausgabeeinheit konfiguriert.
Bei der 2. Markierung erfolgt der Funktionsaufruf der Funktion „testfunktion„. Ihr wird der Parameterwert 40 übergeben.
Bei der 3. Markierung beginnt der Funktionsquellcode. Die Verarbeitung der aktuellen Funktion stoppt und die Verarbeitung der Unterfunktion beginnt. Im Quellcode der „testfunktion“ – Funktion wird ein Größenvergleich zwischen übergebenen Wert (40) und der Zahl 50 durchgeführt. Der Vergleichsoperator liefert den logischen Wert false zurück, weil der Zahlenwert 40 kleiner als 50 ist. Das Vergleichsergebnis bewirkt bei der 4. Markierung, dass das Spannungspotential des Pins 6 auf 0V gesetzt wird.
Die 5. Markierung visualisiert das Quellcodeende der Funktion und die Ausführungskette wird gestoppt und der Rücksprung aus der Funktion wird eingeleitet.
Bei der 6. Markierung ist ebenfalls die Befehlskette beendet und die Verarbeitung beginnt erneut beim Start der „loop„-Funktion.

Rückgabe

Funktionen sind in der Lage Daten zurückzuliefern. Diese Daten können jeden beliebigen Datentyp annehmen und können zum Beispiel das Ergebnis komplexer Berechnungen sein.

Die folgende Abbildung visualisiert den Quellcode der Berechnung des sogenannten Body Maß Index. Dieser errechnet sich aus der Körpergröße und dem Gewicht eines Menschen. Die Funktion gibt dabei den errechneten BMI zurück.

Der folgende Quellcode errechnet die BMI-Faktoren zweier Personen. Die Zuweisungsoperatoren kopieren die Funktionsrückgabewerte in die entsprechenden Variablen.

Beim ersten Aufruf der BMI-Funktion werden für die erste Person ein Gewicht von 77.2 kg und eine Größe von 1.81 m angenommen. Die BMI-Funktion errechnet mit Hilfe dieser Parameter den BMI-Index der Person durch die entsprechende Formelanwendung aus (Körpergewicht/Größe²). Das Funktionsrückgabeergebnis wird dabei in die lokalen Variabel „ergebnis“ der BMI-Funktion zwischengespeichert und durch das Schlüsselwort Return zurückgeliefert.
Die BMI-Funktion liefert mit Parametersatz (77.2, 1.81) den BMi-Index von 23,56. Der entsprechende Wert wird anschließend durch den Zuweisungsoperator in die Variable „bmiFrank“ kopiert.

Eine Funktion mit definierten Rückgabewert eines konkreten Typs, außer „void“, muss dabei einen Wert zurückliefern, ansonsten wird der Compiler einen Fehler markieren und ausgeben. Der „return“-Befehl kann dabei mit Variablenwerten und konkreten Zahlenwerten umgehen.

Der „return-Befehl leitet die Beendigung der aktiven Funktion ein. Er ist der letzte bearbeitete Befehl der Bearbeitungskette einer Funktion. Er muss aber deshalb nicht zwangsläufig am Funktionsquellcodeende stehen. Die folgende Abbildung visualisiert den Quellcode und möchte diesen Sachverhalt nochmal verdeutlichen. Die Funktion könnte schon bei der Angabe eines nicht zulässigen Körpergewichts vorzeitig beendet werden. Die If-Funktion liefert dann den logischen Wert „false“ und beendet dadurch vorzeitig die Funktion.

Die Funktion liefert demnach bei unzulässigen Körperpropotionen, wie dem Gewicht und der Größe einen entsprechenden Fehlercode (-1) zurück. Dieser Fehlercode könnte jetzt geschickt ausgewertet werden, um den Quellcode zu optimieren.

Der „return„-Befehl könnte ebenfalls mit dem Datentyp „void“ umgehen. Hier würde die Funktion nichts zurückliefern. Die folgende Abbildung visualisiert den Quellcode einer Funktion dessen „return„-Befehl nichts zurückliefert. Der „return„-Befehl wird ohne Rückgabetyp markiert.

Die Anfrage der Funktion innerhalb des „setup„-Codeblocks mit dem Parameterwert 25 würde hier keine Bearbeitungsketten auslösen. Der Arduino besitzt keinen Pin25.

Wiederholungen 2 (for)

Das Programmierkonzept der Wiederholung von Bearbeitungsvorgängen wird ebenfalls von der „for“-Schleife verfolgt. Das Umsetzungsprinzip der „for„-Schleife ähnelt der while-Schleife. Die Anzahl der Schleifendurchgänge kann aber hier durch einen Parameter gezielt festgelegt werden. Dies ermöglicht gezielt den Ablauf von Bearbeitungsschritten vornehmen zu können durch Quellcodebeschreibung.

Der Einsatz der while-Schleife bietet sich an, wenn Bearbeitungsschritte unbestimmt wiederholt werden sollen. Die Sensordatenerfassung und dessen Auswertung kann hier stellvertretend als Beispiel herangezogen werden.

Die folgende Abbildung visualisiert den Quellcode einer Arduino-Pin-Initialisierung. Die digitalen Pins von 1 bis 13 sollen dabei als Ausgabeeinheit konfiguriert werden. Hier bietet es sich an eine „for„-Schleife einzusetzen.

Das „for„-Keyword leitet dabei die Bearbeitungsschleife ein, gefolgt von runden Klammern. Der erste Parameter spezifiziert dabei die Schleifenzählvariabel. Sie wird initialisiert und mit dem Schleifenstartwert belegt. Die Schleifenabbruchbedingung und die Zählvariabel-Berechnungsänderungsvorschrift folgen anschließend durch Semikola getrennt. Der folgende visualisierte Codeblockbereich würde, vergleichbar zur while-Schleife, jetzt so lange ausgeführt werden bis die Abbruchbedingung erfüllt wäre.

Die Zählvariabel „digitalPin“ wird hier mit dem Datentyp char initialisiert und erhält den Wert 1 zugewiesen. Die Schleifenschrittzahl wird bei ihrem Start auf 1 gesetzt. Die Bearbeitungsschritte im Schleifencodeblock würden also beim ersten Schleifenstart auf den ersten digitalen Arduino-Pin angewendet werden.
Im zweiten Bearbeitungsschritt wird die Abbruchbedingung überprüft. Diese wäre natürlich noch nicht erfüllt, weil die Zählvariabel kleiner oder gleich 13 wäre. Die Zählvariabel würde anschließend um den Faktor 1 erhöht. Die Bearbeitungskette würde sich anschließend wiederholen, aber mit einem veränderten Zählvariabel-Wert. Der Codeblock würde jetzt auf den zweiten digitalen Arduino Pin angewendet werden und die beschriebene Bearbeitungskette würde sich wiederholen bis die Zählvariabel den Wert größer 13 erreichen würde. Die Abbruchbedingung wäre jetzt erfüllt und der Compiler würde aus der Schleifenbearbeitungskette springen. Die digitalen Arduinio-Pins würden jetzt als Ausgabeeinheit initialisiert werden.

Der folgende visualisierte Quellcode modifiziert den Schleifenparameterbereich geschickt, um den Schleifencodetext zu reduzieren. Die Schleifenzähl-Variabel wird dafür außerhalb des Schleifenkonstrukts initialisiert und definiert. Das Aufsummieren der Zählvariabel findet dabei im Schleifencodeblockbereich statt.

Hochzählen der Laufvariable, sowie deren Definition erfolgen außerhalb des Schleifen-Kopfes.

Jede „for„-Schleife kann durch eine while-Schleife und jede while-Schleife durch eine „for„-Schleife beschrieben werden.

Die dargestellten Schleifen sind komplett identisch. In diesem Fall würde jedoch eher eine for-Schleife verwendet werden, da die Anzahl der Durchläufe bekannt ist.
Auch diese beiden Schleifen sind komplett identisch. Hier muss beachtet werden, dass im for-Kopf die leeren Einträge für die Laufvariable, sowie das Hochzählen dieser nicht fehlen dürfen. In diesem Fall würde eher eine while-Schleife verwendet werden, da die Durchlaufanzahl unbekannt ist.

Die mathematische Berechnungsvorschrift der Zählvariabel ist dabei flexibel anpassbar. Der folgende visualisierte Schleifenquellcode subtrahiert pro Schleifendurchlauf die Zahl 2 von der Zählvariabel ab. Dabei würde von Pin 13 aus jeder zweite Pin und deren grade Vielfache als Ausgabeeinheit konfigurieren.

Serielle Kommunikation

Der Arduino besitzt eine serielle Schnittstelle, die die Kommunikation mit dem Computer ermöglicht. Bei der seriellen Kommunikation werden Bytes zwischen Arduino und Computer ausgetauscht. Die Schnittstelle wird automatisch eingerichtet und aktiviert, wenn der Arduino über das USB Kabel mit dem Computer verbunden wird.

Die Übertagungsgeschwindigkeit liegt zwischen 300 Bd und 2000000 Bd. Dabei wird die Einheit der Übertragungsgeschwindigkeit in Baud angegeben. 1 Bd entspricht die Übertragungsgeschwindigkeit eines Bits pro Sekunde.

Verbindung (de)aktivieren

Die Serielle Arduino-Schnittstelle ist standartmäßig deaktiviert. Das Aufrufen der Funktion „Serial.begin“ würde diese aktivieren. Ihr wird als Parameter die gewünschte Datenrate übergeben. Der folgende Quellcode würde die serielle Arduino-Schnittstelle aktivieren und dessen Übertragungsgeschwindigkeit auf 9600 Bd setzen.

Anmerkung! Eine aktive serielle Arduino-Kommunikationsschnittstelle deaktiviert die digitalen Anschlüsse 0 und 1. Die Anschlüsse werden von der seriellen Schnittstelle belegt und können Sende – und Empfangsschnittelle sein. Markierungen an den entsprechenden Pins unterstreichen die Feststellung. Die Markierung TX steht stellvertretend für eine Sendeschnittstelle, die Markierung RX für eine Empfangsschnittstelle.

Die Abbildung visualisiert nochmals die entsprechenden Pins.

https://store-cdn.arduino.cc/uni/catalog/product/cache/1/image/500×375/f8876a31b63532bbba4e781c30024a0a/a/0/a000066_front_2_2.jpg

Eine aktive serielle Schnittstelle sollte wieder deaktiviert werden, wenn die Pins 0 und 1 wieder aktiviert werden sollen. Die Deaktivierung geschieht dabei durch den Funktionsaufruf „Serial.end„, der keinen Parameter erwartet. Der folgende visualisierte Quellecode soll diesen Sachverhalt nochmals verdeutlichen.

Serieller Monitor (Schnittstelle Computer)

Die Visualisierung gesendeter Daten kann über den seriellen Monitor erfolgen, der in der IDE integriert wurde. Seine Aktivierung erfolgt durch Klicken auf dem Lupen-Button, der sich in der oberen rechten Ecke der Benutzeroberfläche befindet.

https://cdn-learn.adafruit.com/assets/assets/000/002/179/medium800/learn_arduino_ide_serial_moniotor_button.jpg?1396780247

Die obere Abbildung visualisiert den aktivierten Serial-Monitor der IDE. Daten, die seriell übertragen werden sollen, können über die Eingabemaske eingetragen werden. Der rechts davon angeordnete „Senden“-Button leitet bei dessen Betätigung die serielle Kommunikation ein. Die eingegebenen Daten würden jetzt zum Arduino übertragen werden.
Empfangender serieller Arduino-Datenstrom würde dabei im unteren Bildbereich aufgelistet werden.
Das Aktivieren des Kontrollkästchens „Autoscroll“ erzwingt die Visualisierung aktueller Empfangsdaten, dadurch werden empfangende Daten ständig aktualisiert.
Das folgende Kombinationsfeld bietet die Möglichkeit das Datenstromübertragungsverhalten bezüglich seiner Zeilenenden einzustellen. Die Standarteinstellung „Kein Zeilenende“ ist hier anzuwählen.
Mit dem nächsten Kombinationsfeld kann die Datenrate ausgewählt werden. Die Datenrate muss mit dem Funktionsparameter der Serial.begin -Funktion dabei übereinstimmen, damit die serielle Kommunikation richtig funktioniert.
Der Mausklick auf dem Button „Ausgabe löschen“ bereinigt den Bildbereich des Empfangseditors.

Daten senden

Der Datentransfer vom Arduino zum Computer erfolgt über die Funktion „Serial.println„. Der Funktionsparameter beinhaltet dabei die Übertragungsdaten. Der folgende Quellcode soll diesen Sachverhalt verdeutlichen. Die Funktion übernimmt Textbausteine und überträgt diese an dem Computer.

Hier werden die Daten vom Typ String an den Computer gesendet.

Auf der Empfängerseite würden folgende Daten visualisiert werden.

Daten empfangen

Der Arduino könnte auch Daten empfangen. Der kombinierte Einsatz der Funktionen „Serial.available“ und „Serial.read“ könnte diesen Vorgang realisieren.

Serial.available

Der Arduino-Empfangsspeicherbereich kann bis zu 64 gesendete Zeichen aufnehmen. Weitere eingehende Zeichen würden danach ignoriert und gelöscht werden. Die Funktion „Serial.available“ erwartet dabei keine Parameter. Sie gibt die aktuelle Anzahl der im Speicher befindlichen Zeichen zurück. Wurden dabei keine Daten vom Arduino empfangen, so würde die Funktion den Wert 0 zurückliefern. Wäre der Empfangsspeicher ausgeschöpft, so würde sie den Wert 64 liefern.

Der folgende Quellcode soll diesen Sachverhalt nochmals verdeutlichen. Mit Hilfe der entsprechenden Funktion wird überprüft, ob mindestens ein Zeichen empfangen wurde. Die Funktion „Serial.available()“ würde in diesem Fall einen Wert größer 0 zurückliefern. Der Codeblockbereich der if-Funktion würde dann abgearbeitet werden, weil die If-Bedingung erfüllt wäre. Siehe dazu ebenfalls im Kapitel Datentyp „bool“.

Serial.read

Die Funktion „Serial.read“ kann genutzt werden, um auf die empfangenden Daten zuzugreifen. Zeichen werden dabei einzeln gelesen und anschließend aus dem Speicher gelöscht. Dieses Prinzip erzeugt Speicherplatz für neue ankommende Datenzeichen. Die Funktion gibt dabei das Zeichen zurück, das zuerst empfangen wurde. Dieses würde dann, wie schon beschrieben, wieder aus dem Speicher entfernt werden.

Befindet sich die Zahl 47 zum Beispiel im Speicher, so würde der erste Funktionsaufruf die Zahl 4 und der zweite die Zahl 7 zurückliefern.

Sind keine Daten im Empfangsspeicherbereich vorhanden, würde die Funktion den Wert -1 zurückgeben. Dies würde signalisieren, dass bei der Datenabfrage ein Fehler aufgetreten wäre.

Die zurückgegebenen Daten sind in ASCII-Code beschrieben. Genaue Informationen über diese Kodierung liefert eine entsprechende Tabelle ASCII Tabelle. Die Ziffer 0 wird auf die 48 abgebildet, also existiert ein sogenannter Offset bei der Zahlendarstellung, der bei der Dekodierung berücksichtigt werden sollte. Bei der Zahlendekodierung muss eine Subtraktion zwischen empfangendem Zeichen und der Zahl 48 erfolgen, um den tatsächlichen übertragenden Zahlenwert zu erhalten. Folgender Quellecode visualisiert nochmals diesen Sachverhalt. Im Codeblockbereich der if-Funktion wird diese Subtraktion zwischen empfangendem Zeichen und Offset durchgeführt.

Liefert die Funktion „Serial.read“ den Zahlenwert 55 zurück, so hat die Sendeeinheit die Zahl 7 verschickt. Es wäre sinnvoll den zu erwartenden Zahlenbereich zu kennen und entsprechenden auf diesen reagieren zu können. Sollten Daten zwischen 0 und 9 empfangen werden, so wird die Zahl 48 subtrahiert und überprüft, ob das Ergebnis im entsprechendem Zahlenbereich liegen würde. Der entsprechenden Blick auf die ASCII Tabelle verhilft hier zu mehr Transparenz.

Serial.readString

Das Lesen von Zeichenketten aus dem Arduino-Empfangsspeicherbereich kann durch die Funktion „Serial.readString“ erfolgen. Sie erwartet ebenfalls keinen Parameter und liefert ganze Zeichenketten, die den Datentyp String besitzen, zurück.

Beispiel LED-Steuerung

Die folgende visualisierte elektrische Schaltung soll hier als Beispiel für eine serielle Kommunikationsstruktur zwischen Computer und dem Arduino dienen. Die einzelnen LEDs sollen dabei vom Computer aus beliebig schaltbar sein.

Der folgende Quellcode befindet sich dabei im arduino-Arbeitsspeicher.

Bei aktiver Verbindung zwischen dem Arduino mit dem PC wird folgende Nachricht ausgegeben.

Der entsprechende Quellecode, der diesen Arbeitsschritt einleitet, wird durch folgende Abbildung dargestellt. Dabei wird die Übertragungsrate auf 9600 Bd gesetzt.

Aktivieren der seriellen Verbindung und senden der Nachricht.

Mit Hilfe der loop-Funktion wartet der Arduino auf Daten, die ihm über die serielle Schnittstelle zugesendet werden würde.

Sobald Daten empfangen wurden (Serial.available() == true), wird die eingegebene Nummer aus den übertragenen Daten (Serial.read()) berechnet und als Variable gespeichert. Diese Nummer wird an die Funktion „umschalten“ übergeben.

Der Arduino schaltet die LEDs entsprechend der seriellen Anfrage um.

Wenn eine nicht vorhandene led gewählt wurde, wird die Funktion sofort verlassen.
Die übergebene Nummer wird geprüft und die entsprechende LED wird in ihrem Zustand umgeschaltet. Hierbei wird sich die Tatsache zu nutze gemacht, dass false = LOW und true = HIGH ist.

Die empfangende Zeichenkette 23 würde den Vorgang einleiten, der die grüne und blaue LED aufleuchten ließe. Das erneute empfangen der Zeichenkette würde den Leuchtvorgang wieder beenden.

Arrays

Ein Array ist eine Variabel-Tabelle eines konkreten Datentyps. Der Zugriff auf die entsprechenden Tabellenelemente erfolgt durch deren Indizes. Diese bestimmen ebenfalls die Position der Variabel innerhalb der Tabelle.

Die folgende Abbildung visualisiert ein Array mit den Namen „zahlenArray„. Das Array besteht aus 12 Variabel mit dem Datentypunsigned long int„. Jedem gelb markierten Wert wird ein rot markierten Index zugewiesen. Die Indexzählung beginnt bei 0. Der Arraynamen und der Index bestimmt eindeutig die Variabel bzw. dessen Wert.
Das 7. Arrayelement des Arrays „zahlenArray“ mit Index 6 besitzt z.B. den Wert 26.

Der folgende Quellcode beschreibt das entsprechende Array.

Zuerst wird der Datentyp des Arrays angegeben. Anschließend folgt ein sinnvoller Name und ein Klammerpaar, das letztendlich das Array spezifiziert. Die Arraydaten können kommasepariert in geschweiften Klammern erfolgen. Die Anzahl der Daten bestimmt hierbei die Arraygröße.

Der Zugriff auf Arrayelemente ähnelt dem der Standartvariablen. Dabei wird der Array-Name angegeben mit Indexnummer, die jedoch in Klammern gesetzt werden sollte. Der folgende Quellcode möchte diesen Sachverhalt nochmals verdeutlichen. Im Bedingungsbereich der if-Funktion wird das 7. Arrayelement mit dem Zahlenwert 26 auf Gleichheit überprüft.

In diesem Beispiel würde die Bedingung im if-Konstrukt true sein, da der siebte Eintrag, also Index = 6 den Wert 26 hat.

Eine Wertzuweisung konkreter Arrayelemente wäre durch folgenden Quellcode möglich. Dabei wird dem 2. Arrayelement der Wert 0 zugewiesen.

Die Arrayerzeugung ohne Wertzuweisung wird durch folgenden Quellcode beschrieben. Allerdings muss dabei die Arraygröße in Klammern angegeben werden, damit entsprechender Speicherplatz reserviert werden kann.

Länge des Array wird in eckigen Klammern angegeben, wenn nicht direkt mit Werten gefüllt.

Die Arraygröße entspricht hier der tatsächlichen Anzahl der Array-Variabel.

Anzahl der Arrayelemente ist nicht gleich dem Index des letzten Arrayelements!

Das Initialisieren der Arrayelemente mit konkreten Werten sollte erfolgen, um mögliche Programmfehler entgegenzuwirken. Andernfalls werden den Arrayelementen Zufallswerte zugewiesen, die im späteren Verlauf des Programms zu Fehlfunktionen führen könnten.

„Initialisierung“ des Arrays mit einer for-Schleife. Hierbei muss beachtet werden, dass das letzte Arrayelement kleiner als die Anzahl der Arrayelemente ist!

Mit Hilfe einer for-Schleife wird ein eleganter Zugriff auf die Arrayelemente möglich, dabei muss die Arraygröße beachtet werden, weil es sonst zu Abbruchbedingungen bei der Programmausführung kommen könnte. Der Arrayzugriff mit ungültigen Index würde einen Programmabstützt verursachen.

Die Arraygröße könnte in einer Konstanten gespeichert werden und vor dem eigentlichen Arrayzugriff auf ihre Gültigkeit bezüglich der Arraygröße überprüft werden.

Die Verwaltung großer Datenmengen gleichen Typs ist mit einem Array möglich. Die Arraygröße wäre bekannt und der Zugriff auf die Arrayelemente könnte mit einer for-Schleife erfolgen. Daten könnten dabei gelesen und beschrieben werden.

Arrays können auch an Funktionen übergeben werden. Bei der Arrayübergabe wäre es sinnvoll die Arraygröße als Parameter gleich mit zu übergeben. Der folgende Quellcode möchte diesen Sachverhalt nochmals verdeutlichen. Die Funktion „arrayFunktion“ besitzt zwei Parameter. Das eigentliche Array und dessen Größe.

Die übergebenen Arrayparameter werden im Gegensatz zu Variablen nicht durch eine lokale Kopie dupliziert. Diese Änderung führen zu Änderungen am übergebenen Array.

Ein Array könnte ein anderes Array beinhalten. Diese mehrdimensionalen Arrays ähneln Tabellenstrukturen. Dabei erzeugt jedes zugefügte Array eine weitere Tabellenspalte in der Struktur.

Das Indizieren dieser mehrdimensionalen Arrays macht es nötig, dass die entsprechende Spalte und Zeile selektiert werden können.

Mehrdimensionale Arrays können nicht (trivial) an Funktionen übergeben werden. Bei deren Initialisierung muss die Arraygröße der Unterarrays mit angegeben werden.

Bei der Selektion eines Arrayelements indiziert der erste Array-Operator die entsprechenden Tabellenspalten. Der zweite indiziert dann die entsprechenden Zeilen aus der gewählten Spalte.

PWM

Die Abkürzung PWM bedeutet „Pulse Width Modulation„, auf Deutsch Pulsweitenmodulation. PWM ist eine Technik, um die mittlere Leistung einer elektrischen Komponenten, wie z.B. der einer LED, zu generieren.
Durch die Technik der PWM könnte das LED-Licht also gedimmt werden.

Wie wird LED-Licht also gedimmt? Die LED-Lichtstärke ist abhängig von dessen mittleren Leistungsaufnahme. Die Signalleistung könnte durch folgenden Sachverhalt verringert werden. Das visualisierte Signal besitzt eine Periodendauer von einem Herz. Das Signal nimmt für 0,5 Sekunden eine Spannung von 5V und 0V an. Die mittlere Leistung des Signals könnte jetzt verdoppelt werden, wenn die Spannung von 5V jetzt 1 Sekunde andauern würde.

Die LED würde dann alle 0,5 Sekunden lang ihren Leuchtzustand ändern. Sie wäre also während einer Zeitperiode einmal an und einmal aus. Durch Verringerung der Zyklusdauer auf 0,001 Sekunden würde die LED jetzt 0,5 Millisekunden lang einmal an und aus sein. Die Trägheit des menschlichen Auges könnte diesen Lichtaktivitätswechsel jetzt nicht mehr wahrnehmen. Das LED-Licht würde jetzt weniger hell erscheinen.

In diesem Bild ist das Umschaltverhalten in einer Zeit kleiner als 0,1 Sekunden dargestellt.

Eine Videokamera könnte diesen Lichtwechsel aufnehmen und auf Film bannen. Durch die anschließende Betrachtung dieser Aufnahme könnte der Wechsel der Lichtintensität der LED für den Menschen sichtbar gemacht werden.

Das Zeitverhalten eines PWM-Signals kann also die Helligkeit eines LED-Lichts steuern. Dabei ist die mittlere Leistung abhängig vom Zeitschaltverhalten der Signalspannung. Je länger eine Spannung während einer Periode anliegt, desto kürzer liegt eine Spannung von 0V an. Die mittlere Signalleistung würde ebenfalls entsprechend ansteigen.
Das Schaltspannungsverhältnis kann über den Parameter „duty cycle“ gesteuert werden. Dieser gibt die Zeitdauer der 5V-Signalspannung bezüglich einer Signalperiode an.
Zum Beispiel würde bei einer Periodendauer von 1 Millisekunde und einem „duty cycle“ Parameter von 50% die 5V Signalspannung 0,5 Millisekunden lang erzeugt werden. Bei 75% würde die 5V-Spannung 0,75 Millisekunden lang vorherrschen.

Nicht alle Arduino-Anschlüsse unterstützen die PWM Funktionalität. Die digitalen Pins 3, 5, 6, 9, 10 und 11 können das entsprechende Spannungszeitverhalten erzeugen.

https://store-cdn.arduino.cc/uni/catalog/product/cache/1/image/500×375/f8876a31b63532bbba4e781c30024a0a/a/0/a000066_front_2_2.jpg

Die PWM-Frequenz beträgt 490Hz, an den Anschlüssen 5 und 6 allerdings 980Hz.

Die Funktion „analogWrite“ leitet das entsprechende PWM-Signal ein. Der Funktion werden dabei die Pin-Anschlussnummer und der entsprechende „duty cycle“ – Faktor übergeben. Dieser wird in einem Bereich zwischen 0 und 255 angegeben. Dabei entspricht ein „duty cycle„-Faktor von 255, dass die Spannungsausgabe während der kompletten Zeitspanne erfolgt. Am Signalanschluss würde eine permanente Spannung von 5V anliegen.
Ein „duty cycle„-Faktor von 0 entspricht dabei 0% Zeitausnutzung. Das Ausgangssignal würde jetzt zu keinem Zeitpunkt den 5V (HIGH) Wert erreichen. Die Übergabe der Parameterwerte 127 oder 128 würden den „duty cycle „-Faktor auf ca. 50% setzen. Dies würde Signalspitzen von 5V (HIGH) und 0V (LOW) während einer Periodendauer verursachen. Das Ausgangssignal würde jetzt bei der Hälfte der Zeitperiode jeweils HIGH und LOW sein.

https://infoskript.de/files/infoskript/arduino/crashkurs/pwm.gif

Bibliotheken

Bibliotheken sind vorkompilierte Programmfunktionalitäten, die in eigenen Projekten implementiert werden können. Die entsprechenden Funktionalitäten stehen dem Anwender anschließend zur Verfügung. Zum Beispiel existieren Bibliotheken, die den Zugriff auf spezifische Hardware erlauben, um so mit dieser zu interagieren.

Der Hersteller Adafruit stellt für die Interaktion mit seinem Produkt „NeoPixel“ Bibliotheken zur Verfügung, die das einfache Ansprechen seiner Hardware erlauben. Es existieren dabei unzählige Arduino-Bibliotheken, auf die hier nicht eingegangen werden können. Konkret soll die Bibliothek des Herstellers Adafruit näher betrachtet werden.

https://images-na.ssl-images-amazon.com/images/I/51xRFGyGrnL.AC_SX466.jpg

Die folgende Abbildung visualisiert einen schematischen Schaltungsaufbau mit der Hardware.

Oben ist ein 8-LED NeoPixel-Streifen dargestellt, der über einen Widerstand mit dem Aruino verbunden ist.

Die Bibliothek muss zunächst dem Projekt hinzugefügt werden, um dessen Funktionalität einsetzen zu können. Falls die Bibliothek nicht bereits auf dem Computer vorhanden ist, muss sie vorerst noch heruntergeladen werden. Die meisten Bibliotheken können über den Bibliotheksverwalter der Arduino IDE installiert werden. Ergänzende Informationen zur NeoPixel Bibliothek und zum manuellen Download werden unter der Onlineseite des Adafruit Herstellers zur Verfügung gestellt.

Der Bibliotheksverwalter könnte unter dem Menüpunkt „Werkzeuge“ aktiviert werden.

Die Suche nach Bibliotheken könnte hier erfolgen. Anschließend könnte eine Bibliothek durch Mausklick auf den Button „Installieren“ eingerichtet werden.

Der Befehl #include <Adafruit_NeoPixel.h> bindet die entsprechende Bibliotheksfunktionalität ein. Der Bibliotheksname ist hier anzugeben „Adafruit_NeoPixel.h„. Der „#include„-Befehl bindet vor der eigentlichen Kompilierung den Bibliothekscode ein. Für den Compiler gibt es keinen Unterschied zwischen Bibliothekscode und Quellcode. Eine Übersicht der Bibliotheksbefehle werden unter der Adafruit Website aufgeführt.

Bei erfolgreicher Bibliotheksimplementierung würden die entsprechenden Bibliotheksfunktionen zur Verfügung stehen. Der folgende Quellcode möchte diesen Sachverhalt verdeutlichen. Ein Programm, das die LED-Streifen-Steuerung ermöglichen würde.

Die NeoPixel-Bibliothek erfordert die Konfiguration des eingesetzten LED-Streifens. Bei der Initialisierung werden die Anzahl der Leds, der Arduino-Steuer-Pin und spezifische Werte über den eingesetzten Led-Streifen übergeben.
Diese konfigurierte Variable, die den Datentyp einer NeoPixel-Klassenbibliothek aufweist, ermöglicht jetzt den Zugriff auf die entsprechenden Leds der Leuchtkette.

Der folgende Quellcode leiten mit Hilfe der Konfigurationsvariabel die entsprechenden Steuerungen der LED-Leiste ein.

Die Funktion „LEDFarbeSetzen“ ermöglicht das Setzen der LED-Leuchtfarbe. Die NeoPixel-Bibliotheksfunktion „setPixelColor“ setzt dabei die entsprechende Leuchtfarbe einer einzelnen Leds. Ihr werden die entsprechenden Farbanteile übergeben, die den Rotanteil, Grünanteil und Blauanteil der Farbe bestimmen. Durch Anwendung des additiven Farbmodels auf diese Farbanteile wird eine Farbe kombiniert, die letztendlich die Led-Leuchtfarbe definieren soll.

Der folgende visualisierte Quellcode leitet jetzt das Aufleuchten des Led-Streifens ein.

Durch die Modifizierung der Laufvariabel der for-Schleife können gezielt einzelne Leds angesteuert werden. Der folgende Quellcode möchte diesen Sachverhalt nochmal verdeutlichen. Hier wird jede zweite Led der Kette angesteuert, um diese zum Aufleuchten zu bewegen.

Die Kopplung mehrerer LED-Streifen an den Arduino erfordert deren Quellcode-Konfiguration. Jeder Streifen wird dabei über einen individuellen Namen angesprochen. Der folgende Quellcode möchte diesen Sachverhalt verdeutlichen. Die einzelnen LED-Streifen können jetzt individuell und unabhängig voneinander gesteuert werden.