NEU Untergeordnete Seiten (4):http://sites.schaltungen.at/arduino-uno-r3/arduino-ide/codereferenzWels, am 2015-01-06BITTE nützen Sie doch rechts OBEN das Suchfeld [ ] [ Diese Site durchsuchen]DIN A3 oder DIN A4 quer ausdrucken ********************************************************************************** DIN A4 ausdrucken
*********************************************************
INO-Datei Alle schreiben von anderen ab (ich auch, aber ich teste vieles) aber die meisten Autoren testet Ihre Sketche selbst nicht aus. Ich besitze über 16 ARDUINO Bücher, of ist ein Sketch in allen Büchern falsch und ein Korrekturverzeichnis im Internet gibt es auch nicht. https://de.wikipedia.org/wiki/Errata_(Korrekturverzeichnis) An dieser Stelle soll eine kurze Erläuterung der Standardanweisungen erfolgen, die von der Arduino-Sprache unterstützt werden. Eine detaillierte Referenz steht unter arduino.cc/en/Reference/Home-Page zur Verfügung. *****************************************
Leserzuschrift
Sehr geehrter Herr Prenninger!
Sie beklagen die vielen Fehler, die besonders Anfänger irritieren und vielleicht den Spass am Arduino nehmen könnten.
Ich meine zwar, dass die Einstiegshürde beim Arduino erstaunlich niedrig ist (ich habe schnell sehr schöne Funktionen realisieren können), aber es gibt nichts, was nicht weiter verbessert werden könnte. Hier meine Fundstücke. Übrigends bin ich auf Ihre Seite gelandet, da ich den Sinn von "long WakeUp = (UpSRV < UpLED) ? UpSRV : UpLED;" nicht verstanden habe. Ein IF ohne die Buchstaben IF? Das konnte ich mit Ihren Informationen jedoch auch nicht klären.
Es geht um die Seite
Sie haben die Inhalte teilweise von Massimo Banzi übernommen, also nicht unbedingt ihre Schuld.
Schöne Grüße aus dem Land zwischen Harz und Heide in das schöne Österreich
Wolfgang Eue ********************************************************************************** ARDUINO für Einsteiger von Massimo Banzi
01 STRUKTUR
;(Semikolon)
{}(Geschweifte Klammern)
// Kommentare
03 KONSTANTEN
04 VARIABLEN Hinweis: Auf Computern stehen zwei größere Zeichensätze zur Verfügung: ASCII und UNICODE. ASCII umfasst 127 Zeichen, die unter anderem dazu verwendet werden, um Text zwischen seriellen Endgeräten und zeitlich verzahnten Computersystemen wie Großrechnern oder Minicomputern zu übertragen. UNICODE umfasst einen viel größeren Satz an Werten und wird von modernen Computerbetriebssystemen verwendet, um Zeichen in einer Vielzahl von Sprachen darzustellen. Dennoch ist auch ASCII nützlich beim Austauch von kurzen Informationen in Sprachen wie Italienisch oder Englisch, bei denen das lateinische Alphabet, arabische Zahlen und häufig verwendete Schreibmaschinenzeichen wie solche zur Interpunktion und dergleichen verwendet werden.
Dieser Datentyp enthält eine Zahl zwischen 0 und 255. Wie bei char wird auch bei byte nur ein Byte an Speicher verwendet. int Hier werden 2 Byte Speicher genutzt, um eine Zahl zwischen -32.168 und +32.167 darzustellen. Es ist der bei Arduino am häufigsten genutzte Datentyp. unsigned_int Wie bei int werden auch hier 2 Bytes verwendet, unsigned (vorzeichenlos) bedeutet aber, dass keine negativen Zahlen gespeichert werden können, daher reicht der Wertebereich von 0 bis 65.535. long Diese Variable ist doppelt so groß wie int und enthält Werte von -2.147.483.648 bis +2.147.483.647. unsigned_long Dies ist die vorzeichenlose Version von long mit einem Wertebereich von 0 bis 4.294.967.295. float Hierbei handelt es sich um eine recht große Variable, die Fließkommawerte (ein lustiger Ausdruck, um zu beschreiben, dass Zahlen mit einem Dezimalkomma gespeichert werden können) enthalten kann. Solche Variablen nutzen 4 Byte Ihres wertvollen RAM, und die Funktionen, die mit ihnen arbeiten können, verbrauchen ebenfalls viel Codespeicher. Sie sollten floats daher sparsam verwenden. double Diese Variable speichert eine Fließkommazahl mit doppelter Genauigkeit, mit einem maximalen Wert von 1.7976931348623157 x 10308. Wow, das ist ein wirklich großer Wert! string Hierin wird ein Satz von ASCII-Zeichen beherbergt, mit denen Textinformationen gespeichert werden (wenn beispielsweise mittels einer Zeichenfolge eine Nachricht über den seriellen Anschluss gesendet oder auf einem LCD-Display angezeigt werden soll). Zum Speichern wird dabei ein Byte für jedes Zeichen in der Zeichenfolge verwendet, plus ein Nullzeichen, um Arduino mitzuteilen, dass es sich um das Ende des Strings handelt. Folgende Schreibweisen sind gleichbedeutend:. char stringl [] = "Arduino"; // 7 chars + 1 Null-char char string2[8] "Arduino"; // wie oben array Hierbei handelt es sich um eine Liste, auf die über einen Index zugegriffen werden kann. Diese Variablen werden verwendet, um Wertetabellen zu erstellen, auf die schnell zugegriffen werden kann. Wenn Sie beispielsweise verschiedene Helligkeitsstufen speichern möchten, die zum Dimmen einer LED verwendet werden sollen, könnten Sie sechs Variablen mit den Namen light01, light02 usw. erzeugen. Besser ist aber die Verwendung einer einzigen Variablen wie der folgenden: int light[6] - {0, 20, 50, 75, 100}; Das Wort array wird bei der Variablendeklaration nicht verwendet. Stattdessen kommen die Zeichen [] und {} zum Einsatz. 05 KONTROLLSTRUKTUREN Arduino enthält Schlüsselwörter für das Steuern des Logikflusses Ihres Sketch. if...else Mit dieser Struktur werden in Ihrem Programm Entscheidungen gefällt. An if muss eine Frage anschließen, die als Ausdruck, der in Klammern eingeschlossen ist, angegeben wird. Wenn der Ausdruck wahr ist, wird alles Nachfolgende ausgeführt. Wenn er falsch ist, wird mit dem nächsten Codeblock fortgefahren. Es ist auch möglich, nur if ohne Angabe einer Bedingung zu verwenden. else clause. Beispiel: if (val == 1) { digitalWrite(LED,HIGH); } for Bei dieser Struktur wird der Codeblock mit einer angegebenen Häufigkeit wiederholt. Beispiel: for (int i = 0; i < 10; i++) Serial.print("ciao"); } switch case Die if-Anweisung ist mit einer Weggabelung vergleichbar. Die Kontrollstruktur switch case hingegen ähnelt eher einem massiven Kreisel. Sie ermöglicht dem Programm, eine Vielzahl von Richtungen einzuschlagen, in Abhängigkeit vom Wert einer Variablen. Diese Kontrollstruktur ist sehr hilfreich, wenn es darum geht, Ihren Code übersichtlich zu halten, da sie lange Listen von if-Anweisungen ersetzt. Beispiel: switch (sensorValue) { case 23: digitalWrite(13,HIGH); break; case 46: digitalWrite(12,HIGH); break; default: // wenn nichts zutrifft, wird dies ausgeführt digitalWrite(12,L0W); digitalWrite(13,L0W); } while Sie ähnelt der if-Anweisung. Es wird ein Codeblock ausgeführt, solange eine Bedingung wahr ist. Beispiel: // LED blinkt solange der Sensor-Wert kleiner 512 sensorValue = analogRead(1); while (sensorValue < 512) { digitalWrite(13,HIGH); delay(100); digitalWrite(13,HIGH); delay(100); sensorValue = analogRead(1); } do... while Diese Struktur ähnelt der whi/e-Anweisung, mit der Ausnahme, dass der Code ausgeführt wird, bevor die Bedingung ausgewertet wird. Diese Struktur wird verwendet, wenn der enthaltene Code mindestens einmal ausgeführt werden soll, bevor die Bedingung geprüft wird. Beispiel: do { digitalWrite(13,HIGH); delay(100); digitalWrite(13,HIGH); delay(100); sensorValue = analogRead(1); } while (sensorValue < 512); break Bei dieser Anweisung wird eine Schleife verlassen und mit der Ausführung des Codes fortgefahren, der nach der Schleife folgt. Sie wird auch verwendet, um die verschiedenen Abschnitte einer switch case-Anweisung zu separieren. Beispiel: // LED blinkt solange der Sensor-Wert kleiner 512 do { // Schleife wird verlassen, wenn ein Taster gedrückt wurde (digitalRead(7) == HIGH) break; digitalWrite(13,HIGH); delay(100); digitalWrite(13,LOW); delay(100); sensorValue = analogRead(1); } while (sensorValue < 512); continue Wenn diese Anweisung innerhalb einer Schleife verwendet wird, veranlasst continue, dass der restliche enthaltene Code übersprungen und die Bedingung erneut getestet wird. Beispiel: for (light = 0; light < 255; light++) { // überspringt Intensitäten zwischen 140 und 200 if ((x > 140) && (x < 200)) continue; analogWrite(PWMpin, light); delay(10); } return Bei dieser Anweisung wird die Ausführung einer Funktion gestoppt und ein entsprechender Wert zurückgegeben. Sie können diese Struktur auch verwenden, um einen Wert, der aus einer Funktion stammt, zurückzuliefern. Wenn Sie beispielsweise bei einer Funktion mit dem Namen computeTem-perature() das Ergebnis an den Teil Ihres Codes ausgeben möchten, über den die Funktion aufgerufen wurde, würden Sie etwa Folgendes schreiben: Beispiel: int computeTemperature() {int temperature = 0; temperature = (analogRead(0) + 45) / 100; return temperature; } 06 ARITHMETIK UND FORMELN Sie können Arduino für komplexe Berechnungen verwenden, indem Sie eine spezielle Syntax verwenden. Die Zeichen + und — funktionieren so, wie Sie es in der Schule gelernt haben, Multiplikation wird mit einem * und Division mit einem / dargestellt. Es gibt noch einen zusätzlichen Operator, der als Modulo ( %) bezeichnet wird und den Rest aus einer Division von Ganzzahlen zurückliefert. Sie können beliebig viele Klammern nutzen, um Ausdrücke zusammenzufassen und zu schachteln. Im Gegensatz zu dem, was Sie möglicherweise in der Schule gelernt haben, sind eckige und geschweifte Klammern für andere Zwecke reserviert (z.B. für Array-Indizes und Blöcke). Beispiele: a = 2 + 2; light = ((12 * sensorValue) - 5 ) / 2; remainder = 3 % 2; // liefert 1 zurück 07 VERGLEICHSOPERATOREN Zur Angabe von Bedingungen oder Prüfungen für if-, while- und for-Anwei-sungen stehen folgende Operatoren zur Verfügung: == gleich != ungleich < kleiner als > größer als <= kleiner oder gleich >= größer oder gleich 08 BOOLESCHE OPERATOREN Dieser Operator wird verwendet, wenn Sie mehrere Bedingungen verknüpfen möchten. Wenn Sie beispielsweise überprüfen möchten, ob der von einem Sensor zurückgelieferte Wert zwsichen 5 und 10 liegt, würden Sie Folgendes schreiben: if ((sensor => 5) && (sensor <=10)) Es gibt drei Operatoren: „und", durch && dargestellt, „oder", repräsentiert durch II, „nicht", dargestellt durch !. 09 KOMBINIERTE OPERATOREN Hierbei handelt es sich um spezielle Operatoren, die verwendet werden, um den Code bei häufig durchgeführten Operationen, wie z.B. das Hochzählen eines Wertes, möglichst kurz zu halten. Um beispielsweise value um 1 zu inkrementieren, würden Sie Folgendes schreiben: value = value +1; Unter Verwendung eines kombinierten Operators wird daraus diese vereinfachte Version: value++; Inkrementieren und Dekrementieren (-- und ++) Hiermit wird um den Wert 1 hoch- oder heruntergezählt. Seien Sie aber vorsichtig: Wenn Sie i++ schreiben, wird i um 1 inkrementiert und in Bezug auf das Äquivalent von 1+1ausgewertet. Bei ++iwird in Bezug auf den Wert von /ausgewertet und anschließend i inkrementiert. Dasselbe gilt für --. +=, *= und /= Hierdurch lässt sich die Schreibweise für bestimmte Ausdrücke verkürzen. Die beiden folgenden Ausdrücke sind gleichbedeutend: a= a + 5; a += 5;
Inkrementieren und Dekrementieren (-- und ++)
---------------------
Ist leider völlig falsch beschrieben. Es ist genau anders herum.
++Zahl: Erst erhöhen, dann die erhöhte Zahl für einen Vergleich oder eine Berechnung verwenden.
Zahl++: Die nicht erhöhte Zahl für einen Vergleich oder eine Berechnung verwenden und danach erhöhen.
Können Sie leicht mit meinem "Statistik-Beispiel" überprüfen. Ich wollte Übetragungsfehler oder andere Ereignisse zählen und habe mich über den Zahlenüberlauf geärgert. Ich fand es besser, wenn die Zahl auf Maximum hängen bleibt. Die Zahl muss mit Vorzeichen sein, damit es funktioniert.
if (++Zahl < 0) --Zahl; //Der Zahlenueberlauf fuert zu negativen Zahlen und wird prompt zurueckgestellt
Wenn diese Anweisung oft genug ausgeführt wird, bleibt Zahl auf Maximum hängen. Egal ob Sie int oder long nehmen. Wolfgang Eue 10 INPUT- und OUTPUT-FUNKTIONEN Arduino umfasst Funktionen für die Handhabung von Input und Output. Sie haben in diesem Buch bereits einige entsprechende Beispielprogramme gesehen. pinMode(pin, mode) Hiermit wird ein digitaler Pin neu definiert, sodass er dann als Eingangsoder Ausgangspin dient. Beispiel: pinMode(7,INPUT); // definiert Pin 7 als Input digitalWrite(pin, value) Hiermit wird ein digitaler Pin ein- oder ausgeschaltet. Pins müssen mittels pinMode explizit als Output definiert werden, bevor mit digita/Write irgendein Effekt erzielt werden kann. Beispiel: digitalWrite(8,HIGH); // schaltet den digitalen Pin 8 ein int digitalRead(pin) Hiermit wird der Zustand eines Eingangspins ausgelesen. Dabei wird HIGH zurückgeliefert, wenn vom Pin eine Spannung festgestellt wurde, und LOW, wenn keine Spannung anliegt. Beispiel: val = digitalRead(7); // liest Pin 7 in val ein int analogRead(pin) Diese Funktion liest die Spannung an einem analogen Pin aus und liefert einen Wert zwischen 0 und 255 zurück, der eine Spannung zwischen 0V und 5V repräsentiert. Beispiel: val = analogRead(0 ); // liest analogen Input 0 in val ein analogWrite(pin, value) Hiermit wird die PWM-Frequenz für einen der Pins, die als PWM definiert wurden, geändert. Dabei kann pin-11,pin-10, pin-9, pin-6,5 oder pin-3 sein. Die Variable value kann Werte zwischen 0 und 255 enthalten, die eine Skala von 0V und 5V für die Spannung am Output-Pin repräsentieren. Beispiel: analogWrite(9,128); // Dimmt eine LED an Pin 9 auf 50% shiftOut(dataPin, clockPin, bitOrder, value) Diese Funktion sendet Daten an ein Schieberegister, ein logisches Schaltewerk, das verwendet wird, um die Anzahl der digitalen Outputs zu erweitern. Dieses Protokoll nutzt einen Pin für Daten und einen als Taktgeber. Mit bitOrder wird die Reihenfolge der Abarbeitung (least significant bit oder most significant bit) bestimmt und in value ist das zu sendende Byte gespeichert. Beispiel: shiftOut(dataPin, clockPin, LSBFIRST, 255); unsigned long pulseln(pin, value) Hiermit wird die von einem der digitalen Pins eingehende Pulsdauer gemessen. Dies ist zum Beispiel dann nützlich, wenn ein Infrarotsensor oder ein Beschleunigungsmesser ausgelesen werden soll, bei dem die Werte in Form von Impulsen bezogen auf die Änderungsdauern ausgegeben werden. Beispiel: time = pulseln(7,HIGH); // misst die Zeitdauer, die der nächste // Impuls HIGH bleibt 11 ZEITFUNKTIONEN Arduino umfasst Funktionen für das Messen von abgelaufener Zeit und für Pausenzeiten von Sketches. unsigned long millis() Diese Funktion gibt die Anzahl an Millisekunden zurück, die seit dem Start des Sketch vergangen sind. Beispiel: duration = millis()-lastTime; // berechnet die vergangene Zeitdauer seit // "lastTime" delay(ms) Hiermit wird eine Pause des Programms für die angegebene Zeit in Millisekunden veranlasst. Beispiel: delay(500 ); // das Programm wird für eine halbe Sekunde gestoppt. delayMicroseconds(us) Das Programm wird veranlasst, für eine gegebene Anzahl an Millisekunden zu pausieren. Beispiel: delayMicroseconds(1000 ); // wartet eine 1 Millisekunde 12 MATHEMATISCHE FUNKTIONEN In Arduino sind viele häufig genutzte mathematische und trigonometrische Funktionen enthalten: min(x, y) Es wird ein Wert kleiner als xund y zurückgeliefert. Beispiel: val = min(10,20); // val ist nun 10 max(x, y) Es wird ein Wert größer als xund y ausgegeben. Beispiel: val = max(10,20); // val ist nun 20 abs(x) Es wird der absolute Wert von x zurückgeliefert, der negative Zahlen in positive umwandelt. Wenn also x5 ist, wird 5 zurückgeliefert, und wenn x — 5 ist, lautet der Rückgabewert ebenfalls 5. Beispiel: val = abs(-5); // val ist nun 5 constrain(x, a, b) Gibt den Wert von x zurück, der aber auf einen Bereich zwischen a und b beschränkt ist. Wenn x kleiner als a ist, wird einfach a zurückgeliefert, und wenn x größer als bist, wird b ausgegeben. Beispiel: val = constrain(analogRead(0), 0, 255); // weist Werte größer als 255 // zurück map(value, fromLow, fromHigh, toLow, toHigh) Hiermit wird ein Wert aus dem Bereich fromLow und maxLow dem Bereich toLow und toHigh zugewiesen. Dies ist sehr nützlich bei der Verarbeitung von Werten, die von analogen Sensoren stammen. Beispiel: val = map(analogRead(0 ),0,1023,100, 200 ); // weist den Wert von // analog 0 einem Wert // zwischen 100 und 200 zu double pow(base, exponent) Es wird das Ergebnis einer Zahl (Basis) im Hinblick auf eine Potenz (Exponent) zurückgeliefert. Beispiel: double x = pow(y, 32); // setzt x auf den um die Potenz 32 erhöhten Wert von y double sqrt(x) Es wird die Quadratwurzel einer Zahl zurückgeliefert. Beispiel: double a = sqrt(1138); // etwa 33.73425674438 double sin(rad) Es wird der Sinus eines Winkels als Bogenmaß zurückgeliefert. Beispiel: double sine = sin(2); // etwa 0.90929737091 double cos(rad) Es wird der Kosinus eines Winkels als Bogenmaß zurückgeliefert. Beispiel: double cosine = cos(2); // etwa -0.41614685058 double tan(rad) Es wird die Tangente eines Winkels als Bogenmaß zurückgeliefert. Beispiel: double tangent = tan(2); // etwa -2.18503975868 13 ZUFALLSZAHLENFUNKTIONEN Zum Erzeugen von Zufallszahlen können Sie den Pseudozufallszahlen-Ge-nerator von Arduino verwenden. randomSeed(seed) Hiermit wird der Pseudozufallszahlen-Generator von Arduino zurückgesetzt. Die Verteilung der von randorn° zurückgelieferten Zahlen ist zwar grundsätzlich zufällig, aber die Abfolge ist vorhersehbar. Daher sollten Sie den Generator auf einen Zufallswert zurücksetzen. Wenn ein nicht verbundener analoger Pin vorhanden ist, wird er einige zufällige Geräusche aus der Umgbebung auffangen (Radiowellen, kosmische Strahlung, elektromagnetische Interferenzen von Mobiltelefonen und fluoreszierendem Licht usw.). Beispiel: randomSeed(analogRead(5 )); // erzeugt Zufallszahlen mithilfe von Geräuschen an Pin 5 long random(max) long random(min, max) Es wird ein ganzzahliger Zufallswert vom Typ /ongzwischen min. und max. — 1 zurückgeliefert. Wenn kein Minimum angegeben wurde, ist die untere Grenze 0. Beispiel: Jong randnum = random(0, 100); // eine Zahl zwischen 0 und 99 long randnum = random(11); // eine Zahl zwischen 0 und 10 14 SERIELLE KOMMUNIKATION Wie Sie in Kapitel 5 gesehen haben, können Sie über den USB-Port mit anderen Geräten kommunizieren, wobei ein serielles Kommunikationsprotokoll zum Einsatz kommt. Im Folgenden sind die seriellen Funktionen aufgelistet. Serial.begin(speed) Mit dieser Funktion wird Arduino darauf vorbereitet, serielle Daten zu versenden und zu empfangen. Normalerweise arbeitet der serielle Monitor der Arduino-IDE mit einer Geschwindigkeit von 9600bit pro Sekunde. (bps), es stehen aber auch andere Werte zur Verfügung, üblicherweise aber nicht mehr als 115.200bps. Beispiel: Serial.begin(9600); Serial.print(data) Serial.print(data, encoding) Diese Funktion schickt Daten an den seriellen Anschluss. Die Zeichencodierung ist dabei optional; wenn keine Angaben getroffen werden, werden die Daten so weit wie möglich als Klartext behandelt. Beispiele: Serial.print(75); // druckt "75" Serial.print(75, DEC); // wie oben Serial.print(75, HEX); // "4B" (75 als Hexadezimalzahl) Serial.print(75, OCT); // "113" (75 als Oktalzahl) Serial.print(75, BIN); // "1001011" (75 als Binärzahl) Serial.print(75, BYTE); // "K" (das Byte // das zufällig 75 im ASCII-Zeichensatz ist) Serial.printl n(data) Serial.println(data, encoding) Diese Funktion arbeitet wie Serial.print() mit der Ausnahme, dass ein Wagenrücklauf und ein Zeilenvorschub (\r\n) angefügt wird, als ob nach der Dateneingabe die Return- oder Enter-Taste gedrückt worden wäre. Beispiele: Serial.println(75); // druckt "75\r\n" Serial.println(75, DEC); // wie oben Serial.println(75, HEX); // "4B\r\n" Serial.println(75, OCT); // "113\r\n" Serial.println(75, BIN); // "1001011\r\n" Serial.println(75, BYTE); // "K\r\ int Serial.available() Diese Funktion liefert zurück, wie viele Daten am seriellen Anschluss für das Auslesen mittels der read() Funtion bereitstehen. Nachdem mit read() alle verfügbaten Daten ausgelesen wurden, liefert Serial.available() so lange 0 zurück, bis neue Daten am seriellen Anschluss vorliegen. Beispiel: int count = Serial.available(); int Serial.read() Es wird ein Byte der eingehenden seriellen Daten abgerufen. Beispiel: int data = Serial.read(); Serial.flush() Da die Daten über den seriellen Anschluss möglicherweise schneller eintreffen, als dein Programm sie verarbeiten kann, speichert Arduino alle eingehenden Daten in einem Puffer. Wenn der Puffer gelöscht und Platz für neue Daten geschaffen werden soll, wird hierzu die flush()-Funktion verwendet. Beispiel: Serial.flush();
Serial.flush()
------------------------
ist leider wie an so vielen Quellen falsch beschrieben.
Dieser Befehl ist für die Senderichtung bestimmt. Dieser Befehl kommt einem bedingten delay() gleich. Die Befehle nach Serial.flush() werden erst ausgeführt, wenn der Sendepuffer geleert wurde. D.h. je nach Baudrate und Anzahl der Zeichen wird verschieden lange gewartet, bis alle Zeichen aus dem Sendepuffer übertragen wurden. Äußerst wichtiger Befehl, wenn man bei einer Halbduplex-Übertragung den Treiber von Senden auf Empfangen umschalten muss.
Für das beschriebene Leeren des Empfangspuffers wurde kein besonderer Befehl "spendiert", da es die Zeile
while(Serial.available() > 0) Serial.read();
ganz einfach bringt. Wolfgang Eue 705_d_O'REILLY-x_ARDUINO für Einsteiger -10- Kurzreferenz_1b.doc ********************************************************************************** Codereferenz für ARDUINO-IDE V1.0.5-r2 01 Programmstruktur 02 Aufbau einer Funktion 03 Konventionen 04 Datentypen 05 Datentypkonvertierung 06 Variablen & Konstanten 6.1 Variablen 6.2 Konstanten 07 Kontrollstrukturen 08 Mathematische Funktionen 09 Zufallszahlen 10 Arithmetik und Vergleichsfunktionen 11 Funktionen 11.1 Digitale Eingänge/Ausgänge 11.2 Analoge Eingänge/Ausgänge 11.3 Tonausgabe 11.4 Interrupts 12 Zeitfunktionen 13 Serielle Kommunikation ********************************************************* 1 Programmstruktur Die Grundstruktur eines Arduino-Programms besitzt immer die beiden Funktionen setup() und loop(). Somit sieht der minimale Code für ein Programm (Sketch) so aus: void setup() // Programmstart { // Anweisungen } void loop() // Hauptschleife { // Anweisungen } Listing 1.1: Arduino-Sketch: Grundstruktur Die Setup-Funktion wird einmalig beim Start des Arduino-Boards oder nach einem Reset ausgeführt. In dieser Funktion werden Grundeinstellungen wie Variablendeklaration oder Konfiguration der seriellen Schnittstelle gemacht. Zusätzlich werden in der Setup-Funktion die Ein- und Ausgänge gesetzt. int ledPin = 13; // LED an pin-13 int buttonPin = 2; // Button an pin-2 void setup() { pinMode(ledPin, OUTPUT); // pin-13 als Ausgang pinMode(buttonPin, INPUT); // pin-2 als Eingang Serial .begin(9600); // Initialisierung der seriellen Schnittstelle } Listing 1.2: Setup-Funktion: Definition von Ein- und Ausgängen und Konfiguration der seriellen Schnittstelle Die Setup-Funktion ist zwingend notwendig und muss immer vorhanden sein, auch wenn keine Deklarationen gemacht werden müssen. In diesem Fall bleibt die Funktion ohne Anweisungen. void setup() // Setup ohne Deklaration oder pinMode-Konfiguration { } Listing 1.3: Arduino-Sketch: Setup-Funktion ohne Deklaration Die Loop-Funktion ist der zweite Teil der Grundstruktur eines Arduino-Programms und hat die Aufgabe eines Hauptprogramms. Nach dem einmaligen Durchlaufen der Setup-Funktion wird die Loop-Funktion durchlaufen — wie der Name schon sagt als endlose Schleife. Im Loop werden alle weiteren Anweisungen und Funktionsaufrufe untergebracht, die im Normalbetrieb für die gewünschte Lösung benötigt werden. void loop() // Schleife durch das Hauptprogramm { digital Write(ledPin, HIGH); // LED einschalten delay(1000) ; // 1 Sekunde warten digital Write (ledPin, LOW); // 1 LED ausschalten delay(500); // 0,5 Sekunden warten // und weiter geht's am Start des Loops } Listing 1.4: Arduino-Sketch: Hauptschleife loop() ********************************************************* 2 Aufbau einer Funktion Eine Funktion ist ein in sich geschlossener Satz von Programmzeilen. Funktionen haben einen eigenen Namen und werden mittels dieses Namens aus anderen Programmteilen aufgerufen. Funktionen werden verwendet, um den Programmcode zu strukturieren und um wiederkehrende Anweisungen nicht mehrfach zu programmieren. Beim Aufruf einer Funktion können ein oder mehrere Parameter übergeben werden. Das Resultat eines Funktionsaufrufs ist die Ausführung verschiedener Anweisungen oder ein Rückgabewert. Der Typ des Rückgabewertes entspricht dem Typ, der bei der Funktionsdefinition angegeben wurde. Wird beispielsweise eine Funktion mit dem Typ int definiert, so entspricht der Rückgabewert dieser Funktion dem Typ int, also Integer. Wird kein Typ angegeben, so wird der Typ void verwendet. Der Grundaufbau einer Funktion sieht also so aus: Typ NameDerFunktion (Parameter) { // Anweisungen } Im folgenden Beispiel wird eine Funktion aufgerufen, die einen Analogwert von einem externen Sensor einliest, umrechnet und zurückgibt. Als Übergabeparameter wird die jeweilige Portnummer mitgegeben. float ReadSensor(int tempPinIn) // Abfrage von Analogport { float tempC = analogRead(tempPinIn); // Anlogwert einlesen tempC = (5.0 * tempC * 100.0)/1024.0; // Wert umrechnen Serial.println(tempC); // Ausgabe Wert an ser. Schnittstelle return tempC; // Rückgabe Wert } Listing 2.1: Arduino-Sketch: Aufruf Funktion Durch die Verwendung einer Funktion können ein oder mehrere analoge Eingänge abgefragt werden. Bei jedem Aufruf der Funktion wird einfach die entsprechende Pinnummer mitgegeben. int tempPin=1; // Pinnummer von Analogport ReadSensor(tempPin); // Aufruf Funktion ********************************************************* 3 Konventionen Bei der Programmierung der Sketches müssen einige Regeln eingehalten werden, damit am Schluss auch ein lauffähiges Programm auf das Arduino-Board geladen werden kann. Klammern In den Arduino-Sketches werden 2 verschiedene Arten von Klammern unterschieden: runde Klammern 0 geschweifte Klammern {}. Runde Klammern werden beim Aufruf von Funktionen, bei mathematischen Umrechnungen oder auch bei der Ausgabe über den seriellen Port verwendet. Beispiele: ReadSensor(tempPin); tempC = (5.0 * tempC * 100.0)/1024.0; Serial.println("Temperatur in Grad"); Geschweifte Klammern werden in vielen Programmiersprachen verwendet und sind oft für Programmiereinsteiger etwas gewöhnungsbedürftig. Diese Klammern definieren Beginn und Ende von Anweisungen, Funktionen und Codebereichen. Im Arduino-Code werden die geschweiften Klammern auch bei den Anweisungen if und for verwendet. Beispiele: ........... // Funktion float ReadSensor(int tempPinIn) { // Beginn Funktion // Anweisungen } // Ende Funktion // for-Anweisung for (i = 0; i < 100; i++){ Anweisungen } // if-Anweisung if (millis() - previousMillis > interval) { Anweisungen } Die Arduino-Entwicklungsumgebung unterstützt den Programmierer bei der Verwendung der geschweiften Klammern, indem jeweils beim Anklicken einer öffnenden Klammer die dazugehörige schließende Klammer dargestellt wird. ........... if (millis() - previousMillis > interval){ // save the last time you blinked the LED previousMillis = millis(); // if the LED is off turn lt on and vice-versa if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the varieable digitalWrite(ledPin, ledState); } } Abb. A.1: Codierung: Darstellung von öffnenden und schließenden Klammern in der Entwicklungsumgebung Semikolon Eine Anweisung wird jeweils mit einem Semikolon abgeschlossen. Das Semikolon ist zwingend notwendig. Ein fehlendes Semikolon erzeugt beim Kompilieren in der Entwicklungsumgebung eine Fehlermeldung. Leider sind die Fehlermeldungen in der IDE nicht immer sehr aussagekräftig. Darum sollte im Fehlerfall jeweils überprüft werden, ob jede Anweisung mit einem Semikolon abgeschlossen ist. int tempGrad = 12; const int ledPin = 13; Serial println(tempC); Ohne Semikolon verwendet wird die Definitionsanweisung #define. #define DCF77PIN 2 // pin-2 als DCF-Eingang #define BLINKPIN 13 // pin-13 als LED-Ausgang #define TEMPERATURE 2 // Analogeingang pin-A2 für Temperatursensor Kommentare Kommentare und Bemerkungen im Programmcode unterstützen den Programmierer bei der sauberen und verständlichen Darstellung der Codezeilen. Kommentare werden vom Arduino-Programm nicht interpretiert und benötigen keinen Speicherplatz. Die Darstellung von Kommentaren kann als Kommentarblock oder als einzelner einzeiliger Kommentar auf einer Zeile angewendet werden. Ein Kommentarblock beginnt mit /* und wird mit */ abgeschlossen. Der gesamte Text dazwischen wird vom Programm als Kommentar betrachtet. Ein einzeiliger Kommentar beginnt mit // und wird mit dem Zeilenende ohne weitere Anweisung abgeschlossen. /* Das ist ein Kommentarblock und beinhaltet Beschreibung und Informationen zu einem Arduino-Sketch. Der Kommentar kann über mehrere Zeilen verfasst werden */ // Das ist ein einzeiliger Kommentar // Ein Kommentar kann auch hinter einer Anweisung zur näheren Erklärung // gemacht werden tempC = (5.0 * tempC * 100.0)/1024.0; // Umrechnung auf Grad Celsius Bei der Programmierung lohnt es sich, genügend Kommentare zu verfassen und so die Wartbarkeit für andere Personen zu erreichen. Mittels des Kommentarblocks kann ein ganzer Codebereich für das Debugging aktiviert oder deaktiviert werden. ********************************************************* 4 Datentypen In den meisten Arduino-Projekten werden Daten von externen Sensoren gelesen und verarbeitet. Die Verarbeitung und Zwischenspeicherung dieser Daten, beispielsweise ein Abstandswert von einem Infrarotsensor oder ein Temperaturwert, erfolgt mittels Variablen. Neben dem Namen der Variablen und dem Wert besitzt die Variable einen Datentyp. Ein Datentyp beschreibt den erlaubten Wertebereich und die möglichen Operationen dieser Variablen. Nachfolgend werden die meistbenutzten Datentypen in Arduino-Programmen aufgelistet. Int int (Integer) ist der am häufigsten verwendete Datentyp. Integerwerte sind ganzzahlig ohne Kommastellen. Integerzahlen haben eine Länge von 16bit. Wertebereich: -32.768 bis 32.767 Beispiel: int SensorAbstand = 3478; // Variable SensorAbstand als Integer Zu beachten ist, dass bei einem »Überlauf« des Wertes, also bei 32.767 + i, der Variablenwert auf -32.768 gesetzt wird. Unsigned Int Dieser Datentyp entspricht dem Datentyp int, außer dass unsi gned int keine negativen Werte speichert. Wertebereich: 0 bis 65.535 Byte Beim Datentyp byte werden die Variablenwerte als ganzzahlige Dezimalzahl gespeichert. Die Länge des Wertes beträgt 8bit. Wertebereich: o bis 255 Beispiel: byte Helligkeit = 197; // Variable Helligkeit als Datentyp byte Long Integerzahlen mit einem erweiterten Wertebereich werden als Datentyp Long gespeichert. Die Long-Zahlen werden als ganzzahlige 32bit-Zahl im Speicher abgelegt. Wertebereich: -2.147.483.648 bis 2.147.483.647 Beispiel: long Anzahl = 324645; // Variable Anzahl als Datentyp long Unsigned Long Bei diesem long-Datentyp können nur positive Werte als 32bit-Zahl gespeichert werden. Wertebereich: 0 bis 4.294.967.295 Float Zahlen vom Datentyp float werden als 32bit-Fließkommazahl mit Nachkommastellen gespeichert. Berechnungen mit Fließkommazahlen sind langsamer als bei Integerberechnungen. Für schnelle Berechnungen sollten darum Integerzahlen verwendet werden. Wertebereich: -3,4028235E+38 bis 3,4028235E+38 Beispiele: float SensorKorrektur = 2.134; // Variable Sensorkorrektur als Datentyp float float PiWert = 3.141; // Wert von Pi Double Der Datentyp double entspricht in der Arduino-Programmierung dem Datentyp float. Char Mit dem Datentyp char werden Werte von Buchstaben und Zeichen als 8bit-Wert gespeichert. Wertebereich: -128 bis 127 Beispiele: char myCharacter = 'T'; // Wert von Buchstabe T als char gespeichert char myCharacter = '84'; // Wert als Dezimalzahl von Buchstabe T Eine Tabelle mit den ASCII-Werten der einzelnen Buchstaben und Zeichen ist unter http://arduino.cc/en/Reference/ASCIIchart zu finden. Wie aus dem Wertebereich für char zu erkennen ist, gehört dieser Datentyp zu den signed-Datentypen. Der Wertebereich liegt im negativen und positiven Bereich. Der entsprechende 8bit-Datentyp ohne Vorzeichen ist der Datentyp byte. Boolean Der Datentyp boolean besitzt nur 2 mögliche Werte: TRUE oder FALSE. Mit diesem Datentyp werden die Binärwerte 1 oder 0 gespeichert. Wertebereich: TRUE, FALSE Beispiel: int pinMotor = 8; // Motoransteuerung an pin-8 int EndschalterVornPin = 13; // Eingang von Endschalter (High = gedrückt) boolean RunStatus = false; void setup() { pinMode(pinMotor, OUTPUT); pinMode(EndschalterVornPin, INPUT); digitalWrite(pinMotor, LOW); // Motor aus } void loop() { digitalWrite(pinMotor, HIGH); // Motor an RunStatus = true; if (digitalRead(EndschalterVornPin) == HIGH) // Endschalter gedrückt { RunStatus = false; digitalWrite(pinMotor, LOW); // Motor aus // weiter mit Ausweichaktion } } Wie das Beispiel zeigt, wird boolean meist für die Speicherung eines Betriebszustands wie »Motor läuft« oder ähnlich verwendet. String Mit einem String bezeichnet man eine Zeichenkette von Zeichen des Datentyps char. Beispiele: // leerer String mit fixer Länge char myStrl[15]; // Zeichenkette char myStr2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'}; // Zeichenkette mit 0 zum Anzeigen des Endes char myStr3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'}; // Zeichenkette mit Anführungszeichen char myStr4[ ] = "arduino"; // Zeichenkette mit Anführungszeichen und fixer Länge char myStr5[8] = "arduino"; // Zeichenkette mit fixer Länge char myStr6[15] = "arduino"; Bei einem String muss jeweils die Länge angegeben werden. Der Abschluss eines Strings wird mit einer 0 angegeben. Die angehängte 0 kennzeichnet bei der String-Verarbeitung durch den Arduino das eindeutige String-Ende. Eine fehlende 0 kann unerwartete Resultate bei der Sketch-Ausführung hervorrufen. Wie man im Beispiel-String myStr2 erkennen kann, benötigt das Wort ARDUINO nur sieben Zeichen. Das letzte reservierte Zeichen wird für die angehängte 0 benötigt. Um die Initialisierung der String-Variablen zu vereinfachen, kann man mit der Verwendung von doppelten Anführungszeichen eine Zeichenkette wie im Beispiel-String myStr4 definieren. Eine explizite Angabe der String-Größe und eine angehängte 0 als Abschluss ist nicht zwingend erforderlich. Bei der Nutzung von Zeichenketten empfiehlt es sich daher, immer ein Auge auf den nötigen Speicherbedarf zu werfen und nur so viel Speicherplatz, sprich String-Größe, wie nötig zu initialisieren. Um größere Zeichenketten zu speichern, beispielsweise Daten zur Anzeige auf LC-Displays oder Zeichenketten von einem GPS-Modul, wird eine komplexere Zeichenkettenverarbeitung genutzt. In diesem Fall werden die Zeichenketten nicht direkt in der Tabelle (String-Array) abgelegt, sondern nur ein Zeiger (Verweis oder Pointer) auf eine weitere Tabelle. Dieses Konstrukt einer Zeigertabelle ist ein Konstrukt aus der fortgeschrittenen C-Programmierung und wird mit einem * (Asterisk) nach der Typendefinition char angezeigt. Ein Beispiel soll zeigen, wie dies im Arduino-Sketch verwendet wird. char* myStrings[]={ "String Zeile 1", "String Zeile 2", "String Zeile 3", "String Zeile 4", "String Zeile 5" }; void setup(){ Serial.begin(9600); // Ausgabe einzelne Zeilen Serial.println(myStrings[2]); } void loop(){ // Schleife durch die einzelnen Einträge im Array for (int i = 0; i < 6; i++){ Serial .println(myStrings[i]); delay(500); } } Array Ein Array ist, wie im vorherigen Beispiel erwähnt, eine Art Tabelle und dient zur Speicherung von Daten während des Programmablaufs. Ein Array wird eingesetzt, wenn man im Programm nicht nur einen einzelnen Wert speichern und verarbeiten möchte. Bevor man ein Array nutzen kann, muss dieses definiert werden. // Definition eines Arrays // Array mit 5 Positionen, keine Werte definiert int myArray[4] // Liste mit Portnummern, keine Definition der Array-Größe int myPorts[] = {8, 9, 11, 10}; // Liste mit Werten int myWerte[5] = {34, 12, 64, 5, 18}; // Array mit Text char myText [8] = "Arduino";
Array
// Array mit 5 Positionen, keine Werte definiert
int myArray[4]
---------------------------
Da gehört int myArray[5] hin.
Da haben die Designer der Programmiersprache die Leute etwas in die Falle gelockt. Bei der Definition ist myArray[5] die einzig korrekte Stelle für eine 5 in eckigen Klammern. Später darf nur 0 bis 4 vorkommen. Sobald Sie jedoch einen Namen für die Größe nehmen, wird es wieder verständlich, was sich die Designer der Programmiersprache dabei gedacht haben:
const AnzahlArrayPlaetze = 5;
int myArray[AnzahlArrayPlaetze];
Wolfgang Eue
Ein Array beinhaltet also immer einen Namen, eine Angabe der Größe und eine Werteliste. Das erste Beispiel myArray[4] wird initialisiert, hat aber noch keine Werte gespeichert. Im zweiten Beispiel myPorts [] wird das Array initialisiert und mit Werten gefüllt. Es ist keine Größe des Arrays definiert. In diesem Fall wird die benötigte Größe anhand der übergebenen Werte intern ermittelt. Das dritte Beispiel myText [8] zeigt ein Array vom Typ char. In diesem Fall ist zu beachten, dass bei der Größe des Arrays jeweils die Anzahl der Zeichen plus 0 als Abschluss des Strings angegeben werden muss. Die Größe eines Arrays ist also ein wichtiger Wert und muss bei der Verwendung dieses Datentyps beachtet werden. Eine falsche Angabe kann einen Fehler im Programmablauf erzeugen, da der Prozessor einen fehlerhaften Wert liest und weiterverarbeitet. Da die Array-Werte im internen Speicher des Prozessors gespeichert sind, kann bei einer zu großzügigen Definition schnell ein Speicherproblem entstehen. Dies ist meist in merkwürdigem, unstabilem Ausführen des Programms zu erkennen und muss nicht zwingend eine Fehlermeldung oder einen Programmabbruch hervorrufen. Die Abfrage eines Wertes aus dem Array erfolgt jeweils über die Angabe des Indexwertes für den gewünschten Array-Wert. // Abfrage eines Array-Wertes wert = myWerte[2]; // Abfrage des Wertes 64 aus dem Array wert2 = myPorts[3]; // Abfrage ergibt Wert 10 Da der erste Wert im Array den Index o besitzt, wird der dritte Wert innerhalb des Arrays mit dem Index 2 aufgerufen. Diese Tatsache ist speziell zu beachten, wenn man mittels Schleife ein ganzes Array durchsucht. Ein Array mit der Größe von 5 hat also Indexwerte von 0 bis 4. Der Index des letzten Wertes innerhalb des Arrays ist somit immer die Größe des Arrays minus i. Die Speicherung eines einzelnen Wertes in einem Array erfolgt nach der Initialisierung: // Wert in Array speichern myArray[0] = 23; // Wert 23 speichern an erster Position myArray[3] = 12; // Wert 12 speichern an letzter Position Wie bereits erwähnt, werden Arrays oftmals mittels Schleifen abgefragt oder mit Werten gefüllt. Das Beispiel speichert Zufallswerte in einem Array und gibt diese über die serielle Schnittstelle aus. Beispiel: // Zufallszahl in Array speichern int ArrayRandom[4]; // Array mit Zufallszahlen int i; int zufallszahl; void setup() { Serial .begin(9600); // Initialisierung serielle Schnittstelle } void loop() { for (i = 0; i < 5; i = i + 1) { zufallszahl= random(1, 99); // Zufallszahl generieren ArrayRandom[i] = zufallszahl; // Zufallszahl in Array Serial.println(ArrayRandom[i]); } delay(1000); } Für umfangreichere Datenmengen, beispielsweise beim Einsatz einer LED-Matrix, kann eine Struktur eines Arrays um eine zusätzliche Ebene erweitert werden. Das Resultat ist eine mehrdimensionale Tabelle, die jeweils zwei Indexwerte verwendet. Der generelle Aufbau eines mehrdimensionalen Arrays ist jeweils: Typ ArrayName [AnzahlEbenen] [AnzahlWerte] Beispiel: // Array mit 3 Ebenen und jeweils 3 Werten int 2EbenenArray[3][3]; // Initialisierung Array // Werte speichern in Array int 2EbenenArray[3][3] = { { 23, 34, 11}, // erste Ebene { 54, 0, 21}, // zweite Ebene { 128, 76, 9} // dritte Ebene }; ********************************************************* 5 Datentypkonvertierung Die Datentypkonvertierung benötigt man oftmals in der Praxis, um beispielsweise seriell empfangene Daten in einen Integerwert umzuwandeln. Die folgende Tabelle A.i zeigt verschiedene Varianten der Typenkonvertierung: Ausgangstyp Zieltyp Codebeispiel char a=char(x); byte a=byte(x); integer a=int(x); long a=long(x); float a=float(x) String integer char* MeinString="A"; a=atoi(MeinString); Tabelle 5.1 Typenkonvertierung ********************************************************* 6 Variablen & Konstanten 6.1 Variablen In einem Programm werden Werte, die für die Weiterbearbeitung gespeichert sind, mit einem Textbegriff benannt. Der Begriff, also der Variablenname, sollte so gewählt werden, dass er innerhalb des Programms gut lesbar und verständlich ist. Der Wert einer Variablen kann sich laufend ändern oder durch das Programm verändert werden. Eine Variable besitzt neben dem Variablennamen auch einen Datentyp, der den Wertebereich definiert. Bei der Variablendeklaration, die am Anfang des Programms erfolgt, wird der Datentyp, der Variablenname und der Wert der Variablen gesetzt. Wird kein Wert angegeben, so wird der Variablenwert auf 0 gesetzt. Datentyp Variablenname = Wert; Beispiel: int IRAbstand = 453; // Variable IRAbstand als Integer (ganzzahlig) Verständliche Variablennamen wie AbstandSensor oder Ei ngabePi n verbessern die Lesbarkeit eines Programms. // Ideale Variablennamen int AbstandSensor = 100; int EingabePin = 2; float TempWertAussen = 32.45; // Schlecht gewählte Variablennamen int x = 123; float valy = 34.45; 6.2 Konstanten Konstanten sind spezielle Variablen, die ihren Wert während des gesamten Programmablaufs behalten. Sie werden verwendet, um fixe Werte einmalig zu deklarieren. Diese Konstanten können dann innerhalb des Arduino-Programms aufgerufen und verwendet werden. Konstanten werden mit der Anweisung const definiert und können im Programmablauf nicht überschrieben werden. // Deklaration einer Konstanten const int GPSLED = 5; // LED für Anzeige von GPS-Empfang an Pin 5 Die Konstantendeklaration wird üblicherweise für die Definition von Portnummern, Werten von Konfigurationsparametern oder fixen Werten für Kommandos oder Zeiten verwendet. Beispiele: // Konstanten für Portnummer const int GPSLED = 5; // LED für Anzeige von GPS-Empfang an Pin 5 digitalWrite(GPSLED, LOW); // LED ist aus // Konstante für Zeitverzögerung const int zeitverzoegerung = 1000 // 1000ms Verzögerung delay(zeitverzoegerung); Neben den eigenen Definitionen von Konstanten kennt die Arduino-Syntax noch eine Anzahl von vordefinierten Konstanten. true/false Diese beiden booleschen Konstanten definieren logische Pegel. Die Konstante false hat immer einen Wert 0 oder null. Die Konstante true dagegen hat einen Wert von »alles außer 0«, meist wird jedoch i verwendet. Aber auch Werte von 2, 10 oder 100 werden als true angenommen. Beispiel: // true/false if (EndSchalter1 == true) { digitalWrite(MotorPin, LOW) ; // Motor aus } High/Low Mit diesen beiden Konstantenwerten werden die Zustände von Ein- und Ausgängen gelesen oder geschrieben. HIGH ist jeweils der Logiklevel 1,5 V oder EIN. LOW entspricht 0 (GND) oder AUS. Beispiel: // Konstanten HIGH/LOW // digitale Ausgänge digitalWrite (9, LOW) ; // Ausgangsport 9 aus digitalWrite(8, HIGH); // Ausgangsport 8 ein // digitale Eingänge EingangSchalter = digitalRead(2) // Wert von digitalem Eingang 2 lesen Zu beachten ist, dass diese beiden Konstantenwerte immer in GROSSBUCHSTABEN geschrieben werden müssen. Input/Output Mit diesen beiden Konstanten wird die Portart (Eingang oder Ausgang) des Arduino-Prozessors definiert, die in der Funktion pinMode () verwendet wird. Beispiele: // Portart setzen // Port 12 als Eingang pinMode(12, INPUT); // Port 13 als Ausgang pinMode (13, OUTPUT); ********************************************************* 7 Kontrollstrukturen Kontrollstrukturen steuern den Programmfluss und werden bei Entscheidungen eingesetzt. if Eine if-Kontrollstruktur wird für Entscheidungen verwendet und prüft, ob eine Bedingung erfüllt ist. if (Status == 1) { digitalWrite(8, HIGH); } Man beachte die Schreibweise mit den doppelten Gleichheitszeichen. Ein einfaches Gleichheitszeichen bedeutet nämlich eine Zuordnung eines Wertes. Status=1 // Variable Status bekommt Wert 1 if...else Mit der zusätzlichen else-Erweiterung ergibt sich eine ENTWEDER/ODER-Entscheidung. if (statusPinl == HIGH) { digitalWrite(8, HIGH); } else { digitalWrite(8, LOW) } for Die for-Schleife erlaubt das definierte Wiederholen einer Liste von Anweisungen. for (int i=0; i <= 255; i++) { analogWrite(PWMOut, i); delay(100); } Das Beispiel zählt die Variable i hoch bis zum Wert 255 und gibt jeweils den Wert als PWM-Signal (PWM = Pulsweitenmodulation) aus . while Unbegrenztes Ausführen einer Schleife, bis ein Ergebnis erreicht ist. wert = 0; while(wert < 200) { // Anweisung wert++; } Schleife ausführen, bis Wert = 200 ist, wobei der Wert bei jeder Schleife um erhöht wird. Do...while Ähnlich der while-Schleife. Bei der do . . . while-Schleife erfolgt die Prüfung der Bedingung am Ende. do { temp = anal ogRead(tempPi n) ; } while (temp < 40); Ausführen der Schleife, solange der Wert von temp kleiner als 40 ist. switch/case Diese Kontrollstruktur vergleicht einen Wert mit einer Reihe von Werten. Entspricht der Wert einem Wert aus der Reihe, so wird dieser Fall (case) ausgeführt. switch (temp) { case 20: // Temp ist genau 20 digitalWrite(LEDgelb, HIGH); break; case 25: // Temp ist genau 25 digitalWrite(LEDgelb, HIGH); break; default: // Falls kein anderer Fall ausgeführt wird digitalWrite(LEDgruen, HIGH); } break Mit der Anweisung break kann aus einer Schleife oder einem switch-Fall ausgestiegen werden. for (int i = 0; i < 255; i ++) { digitalWrite(PWMPin, i); // Stop-Eingang abfragen stopInp = digitalRead(stopPin); // falls Stop = HIGH, aussteigen if (stopInp == HIGH) { digitalWrite(PWMPin, 0); break; } delay(20); } Schleife wird unterbrochen, falls Stop-El ngang HIGH ist. continue Unterbricht eine Schleife und springt wieder an den Start zur Prüfung der Bedingung. for (int i = 0; i < 255; i++) { // falls i zwischen 50 und 100, keine Ausgabe von Wert if (i > 50 && i < 100) { continue; } digitalWrite(PWMPin, i); delay(20); } return Beendet eine Funktion und gibt einen Wert zurück, um im übergeordneten Programm weiterzuverarbeiten. int getTemperatur(inPin) { int valTemp=0; valTemp=analogRead(inPin); return valTemp; } ********************************************************* 8 Mathematische Funktionen min (x,y) Ermittelt das Minimum von zwei Werten und gibt den kleineren Wert zurück. min(a, b) wert = min(wert, 88); Setzt die Variable wert so, dass wert nie größer als 88 werden kann. max(y,z) Ermittelt das Maximum von zwei Werten und gibt den höheren zurück. max(a, b) wert = max(wert, 200) ; Setzt die Variable wert so, dass wert nie kleiner als 200 werden kann. Sowohl min() als auch max() werden in der Praxis oftmals zur Begrenzung des Maximalwertes (min) oder Minimalwertes (max) verwendet. Für das Begrenzen eines Wertes oder eines Bereichs kann auch die Funktion constrain () verwendet werden. abs(z) Gibt den Absolutwert einer Zahl zurück. abs (Zahl) int z =100; int x = abs(z); // x = 100 int a = -100 int b = abs(a); // b = 100 constrain(x,a,b) Mit dieser Funktion kann ein Wert so gesetzt werden, dass er immer in einem definierten Bereich liegt. constrain(Zahl, Minimal, Maximal) Die Funktion gibt folgende Werte zurück: Zahl , wenn Zahl zwischen Minimal und Maximal liegt. Minimal , wenn Zahl kleiner als Minimal ist. Maximal , wenn Zahl größer als Maximal ist. // Bereichsbegrenzung int xVal = 80; xVal = constrain(xVal, 100, 200); // xVal ist immer im Bereich von 100 bis 200 map() Diese Funktion kann einen Wertebereich (fromLow, fromHigh) in einen anderen Wertebereich (toLow, toHigh) konvertieren. Ein Temperaturwert von 20 bis 80 Grad kann auf diese Weise in einen Bereich von 0 bis 100 % umgesetzt werden. Dabei wird eine Temperatur von 20 zu 0 und der Endwert von 80 zu 100. Eine weitere Möglichkeit ist die Invertierung des Bereichs, indem ein Wertebereich von 0 bis 100 zu 100 bis 0 konvertiert wird. map(Wert, fromLow, fromHigh, toLow, toHigh) In der Praxis wird map() oft eingesetzt, um einen eingelesenen Analogwert (10bit) in einen 8bit Wert für die Ausgabe als PWM zu konvertieren. // Temperatursensor einlesen, Wert 0-1023 int tempVal = analogRead(5); // Konvertierung 0-1023 zu 0-255 int tempValOut = map(tempVal, 0, 1023, 0, 255); // Ausgabe des aktuellen Wertes als PWM-Signal analogWrite(9, tempValOut); pow(base, exponent) Funktion zur Ausgabe der Potenz einer übergebenen Zahl. pow(Zahl, Exponent) float x = (analogRead(5)); y = pow(x,2.0); sq(x) Gibt das Quadrat einer Zahl zurück. sqrt(x) Berechnet die Wurzel einer übergebenen Zahl. sin(rad) Berechnung des Sinus eines Winkels in Radian. Der Rückgabewert liegt somit zwischen und +i. cos(rad) Berechnung des Cosinus eines Winkels in Radian. Der Rückgabewert liegt somit zwischen -i und +i. tan (rad) Berechnung des Tangens eines Winkels in Radian. ********************************************************* 9 Zufallszahlen Die Generierung einer Zufallszahl erfolgt mittels des Pseudorandom Number Generators (PRNG). Dieser Generator ermittelt die Zufallszahl algorithmisch. Eine echte Zufallszahl wird generiert, indem der PRNG mit einer Zahl initialisiert wird und dieser daraus eine Zufallszahl erstellt. Die echte Zufallszahl kann beispielsweise ein Analogwert sein. randomSeed(Wert) Initialisieren des PRNG mit einer Zahl. random(max), random(min,max) Erstellen einer Pseudo-Zufallszahl. // Initialisieren des PRNG randomSeed(analogRead(0)); // Zufallszahl zwischen 0 und 100 randZahl = random(100); // Zufallszahl 1 und 49 randZahl = random(1, 49); ********************************************************* 10 Arithmetik und Vergleichsfunktionen Arithmetik Durchführen von Addition, Subtraktion, Multiplikation und Division. y = y + 3; // Addition x = x - 7; // Subtraktion i = j * 6; // Multiplikation r = r / 5; // Division a = 6 % 4; // Modulo (wobei b = 2) Vergleichsoperatoren Bei Vergleichsoperationen werden zwei Variablen oder Konstanten miteinander verglichen. a == b // a ist gleich b a != b // a ist nicht gleich b a < y // a ist kleiner als b a > b // a ist großer als b a <= b // a ist kleiner oder gleich b a >= b // a ist größer oder gleich b Hinweis Achtung: a = b ist keine Vergleichsoperation wie das erste Beispiel, sondern eine Wertezuweisung: Wert a bekommt den Wert von b. Gemischte Zuweisungen Gemische Zuweisungen sind eine Art Kurzschreibweise für arithmetische Operationen und Variablenzuweisungen. Increment/Decrement x++; // erhöht x um 1, entspricht x = x + 1 x--; // vermindert x um 1, entspricht x x - 1 Zusammengesetzte Zuweisungen x += y; // entspricht x = x + y x y; // entspricht x = x - y x *= y; // entspricht x = x * y x /= y; // entspricht x = x / y Logische Operatoren Mit den logischen Operatoren werden meist zwei Werte oder Ausdrücke miteinander verglichen. Das Resultat ist entweder TRUE oder FALSE. Die drei logischen Operatoren AND, OR und NOT werden normalerweise in if-Anweisungen verwendet. Logisches AND (&&) Das Resultat ist TRUE, wenn beide Werte TRUE sind. // Werte prüfen, true, wenn temp zwischen 15 und 25 ist if (temp >= 15 && temp <= 25) { // } // Eingänge prüfen if (InpPin4 = HIGH && InpPin5 == HIGH) { // } Logisches OR Das Resultat ist TRUE, wenn einer der Werte TRUE ist. if (a > 10 || b > 10) { // } Logisches NOT (!) Das Resultat wird TRUE, wenn der Ausdruck FALSE ist. if (!x > 5) { // } ********************************************************* 11 Funktionen 11.1 Digitale Eingänge/Ausgänge pinMode() Konfiguriert einen digitalen Port als Eingang oder Ausgang. pinMode(PortNummer, Modus) pinMode(8, OUTPUT); // pin-8 als Ausgang pinMode(9, INPUT); // pin-9 als Eingang Eingang einlesen Liest den Wert des digitalen Eingangs ein. digitalRead(PortNummer) InpPin = digitalRead(4); // Einlesen von pin-4, Wert wird in Variable InpPin abgelegt Ausgang setzen Setzt den Ausgang auf r oder o, entspricht HIGH oder LOW. digitalWrite(PortNummer, Wert) digitalWrite(8, HIGH); // Ausgang pin-8 EIN digitalWrite(9, LOW); // Ausgang pin-9 AUS Eingangspuls messen Diese Funktion misst die Zeit eines am Eingang anliegenden Eingangspulses. Dabei kann die Pulszeit zwischen 10 Mikrosekunden und 3 Minuten liegen. pulseln(PortNummer, Wert) pulseln(PortNummer, Wert, Timeout) Der Wert HIGH oder LOW gibt an, ob man einen HIGH-Puls oder einen LOW-Puls messen möchte. Bei HIGH wird am Port auf ein Signal gewartet, das auf HIGH geht, dann beginnt die Zeitmessung, bis der Puls wieder LOW ist. Die gemessene Zeit wird in Mikrosekunden angegeben. Die Angabe der Timeout-Zeit, wie viele Mikrosekunden man auf den Puls warten muss, ist optional. Als Standardwert ist 1 Sekunde definiert. int PulsPin = 2; unsigned long zeitdauer; zeitdauer = pulseIn(PulsPin, HIGH); 11.2 Analoge Eingänge/Ausgänge Analoge Eingänge Analoge Eingänge lesen mit einer Auflösung von 10bit, das entspricht einem Bereich von 0 bis 1023. Das Eingangssignal liegt dabei im Bereich von 0V bis 5 Volt. analogRead(PortNummer) tempIn = analogRead(0); // Analogport 0 einlesen, Wert in Variable tempIn ablegen Für die analogen Eingänge können folgende Ports verwendet werden: (pin-A0 .. pin-A5): Arduino-Standardboards (bis pin-A7): Arduino Mini und Nano (bis pin-A15): Arduino Mega Analoge Ausgänge Das Ausgeben einer Spannung an den analogen Ausgängen wird mittels Pulsweitenmodulation (PWM) realisiert. Die Grundfrequenz liegt dabei bei ungefähr 490 Hertz. Der Wert liegt im Bereich von 0 bis 255, das entspricht 8bit. analogWrite(PortNummer, Wert) analogWrite(10, 123); // Ausgeben eines analogen Wertes an pin-10 Für die analoge Ausgabe sind folgende Ports möglich: (pin-A3, 5, 6, 9, 10, 11): Arduino-Standardboards (pin-A2 .. 13): Arduino Mega Hinweis Anwendungsbeispiele und Konfigurationsmöglichkeiten der analogen Ausgabe als PWM sind in Kapitel 4 ausführlicher beschrieben. 11.3 Tonausgabe tone() Ausgabe eines Rechtecksignals mit einstellbarer Frequenz und einer Pulsdauer von 50 %. Das Ausgangssignal kann direkt an einem Piezo-Alarmgeber oder einem Lautsprecher angeschlossen werden. tone(PortNummer, Frequenz) tone(PortNummer, Frequenz, Dauer) Durch die Eingabe der Nummer des Ports, an dem ein Speaker angeschlossen ist, und der Frequenz in Hertz kann ein Ton erzeugt werden. Wird zusätzlich noch die optionale Dauer in Millisekunden (ms) angegeben, stoppt die Tonausgabe nach der definierten Zeit. Eine praktische Anwendung des Befehls und Ausgangspunkt für weitere Experimente ist das Beispiel aus dem Arduino Playground. http://arduino.cc/en/Tutorial /Tone3 notone() Stoppt die Ausgabe des Rechtecksignals, das mit tone 0 gestartet wurde. 11.4 Interrupts Bei einem Interrupt wird durch ein Ereignis, beispielsweise ein Signal von außen, das Hauptprogramm gestoppt und ein anderes Programm ausgeführt. Das externe Signal kann zum Beispiel ein kurzer Puls von einem sich drehenden Magneten sein. Damit jedes Signal des sich drehenden Magneten auch gezählt wird, kann damit ein Interrupt ausgelöst werden, der einen Zähler um i erhöht. attachInterrupt() Diese Funktion löst mittels Signal an einem definierten digitalen Port einen Inter-rupt aus, der eine wählbare Programmfunktion aufruft. attachInterrupt(InterruptNummer, Funktion, Mode) Parameter I nterruptN um mer Die Arduino-Standardboards können zwei Interrupts erfassen, die die Nummer 0 (angeschlossen an pin-2) und die Nummer 1 (angeschlossen an pin-3) besitzen. Parameter Funktion Durch das Erkennen des Eingangssignals an den erwähnten Ports kann eine auszuführende Funktion angegeben werden. Parameter Mode Dieser Parameter gibt an, bei welcher Signaländerung ein Interrupt ausgelöst werden soll. LOW: Löst den Interrupt aus, wenn Pin auf LOW geht. CHANGE: Löst den Interrupt aus, wenn sich das Signal am Pin ändert. RISING: Löst den Interrupt aus, wenn sich das Signal von LOW auf HIGH ändert. FALLING: Löst den Interrupt aus, wenn sich das Signal von HIGH auf LOW ändert. Beispiel: Funktion wird bei ansteigendem Signal an Pin 2 ausgeführt. attachInterrupt(0, alarm, RISING); void alarm() { // Anweisungen } detachlnterrupt() Schaltet den Interrupt aus. detachInterrupt(InterruptNummer) InterruptNummer: 0 oder 1 (bei Arduino-Standardboards) ********************************************************* 12 Zeitfunktionen Bei den Zeitfunktionen unterscheidet man zwischen Funktionen, um Zeiten zu messen und Funktionen, um definierte Pausen (Verzögerungen) zu realisieren. delay() Pausiert ein Programm für eine angegebene Zeit in ms. Dabei ergibt die Eingabe von 1000 eine Verzögerung von einer Sekunde. Dies wird oft in Blinkroutinen für die einzelnen Ein- und Aus-Phasen verwendet. void loop() { // LED Ein digitalWrite(8, HIGH); // 1 Sekunde warten delay(1000); // LED Aus digitalWrite(8, LOW); // 1 Sekunde warten delay(1000); } delayMicroseconds() Pausiert ein Programm für eine angegebene Zeit in Mikrosekunden (us). Die Eingabe von 1000 Mikrosekunden ergibt eine Pause von einer Millisekunde. Der maximale Wert für die Pause kann dabei 16.383 Mikrosekunden betragen, was einer Verzögerung von 16,383 Millisekunden entspricht. millis() Gibt die Zeit in Millisekunden (ms) seit dem Start des aktuellen Programms zurück. Der Rückgabewert ist eine Zahl vom Datentyp unsigned long. Nach 48,6 Tage erfolgt ein Überlauf (Overflow) und die Zeitmessung beginnt wieder bei 0. unsigned long time; // Wert seit Start des Programms time = millis(); micros() Gibt die Zeit in Mikrosekunden (us) seit dem Start des aktuellen Programms zurück. Der Rückgabewert ist eine Zahl vom Datentyp unsigned long. Nach 70 Minuten erfolgt ein Überlauf (Overflow) und die Zeitmessung beginnt wieder bei 0. ********************************************************* 13 Serielle Kommunikation Auf dem Arduino Duemilanove wird die serielle Schnittstelle einerseits für die Kommunikation mit dem angeschlossenen Rechner über den USB-Port und andererseits auf den pin-0 (Rx) und pin-1 (Tx) für die externe Kommunikation verwendet. begin() Initialisieren der seriellen Schnittstelle und Definition der Übertragungsgeschwindigkeit. Über den seriellen Monitor in der Entwicklungsumgebung (IDE) können die übertragenen Daten sichtbar gemacht werden. Dabei muss im seriellen Monitor die entsprechende Übertragungsgeschwindigkeit eingestellt werden. Serial.begin(Übertragungsrate) Übertragungsrate Wertebereich: 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200 void setup() { // Konfiguration serielle Schnittstelle Serial.begin(9600) ; // Übertragungsgeschwindigkeit } ACHTUNG: Wird die serielle Schnittstelle verwendet, so können die digitalen Ports D0 und D1 nicht für andere Aufgaben verwendet werden. Der Arduino Mega hat drei zusätzliche serielle Schnittstellen, die an den folgenden Ports betrieben werden: Serial1: Port 19 (Rx), Port 18 (Tx) Serial2: Port 17 (Rx), Port 16 (Tx) Serial3: Port 15 (Rx), Port 14 (Tx) Somit muss jede der vier seriellen Schnittstellen auf dem Arduino Mega einzeln initialisiert werden. // Arduino Mega void setup() { // Konfiguration der seriellen Schnittstellen Serial.begin (9600); Serial1.begin(38400); Serial2.begin(115200); Serial3.begin(9600); } Die Übertragungsgeschwindigkeiten der vier seriellen Schnittstellen können dabei unterschiedlich sein. end() Beendet die serielle Funktion auf den pin-0 und pin-1. Die beiden digitalen Ports können anschließend wieder für andere Anwendungen genutzt werden. Serial.end() available() Diese Funktion prüft, ob im Empfangsbuffer der seriellen Schnittstelle Daten vorhanden sind. Der Rückgabewert gibt die Anzahl der vorhandenen Bytes zurück. Falls Daten vorhanden sind, können diese nun mit read () aus dem Buffer gelesen werden. Zu beachten ist, dass der Empfangsbuffer eine maximale Größe von 128 Bytes hat. Darum muss im Programm sichergestellt sein, dass die Daten regelmäßig aus dem Empfangsbuffer gelesen werden. // prüfen, ob Daten empfangen wurden if (Serial.available() > 0) { // Daten aus Eingangsbuffer einlesen empfangeneDaten = Serial.read (); // Ausgabe von Infomeldung Serial print ("Du hast Daten empfangen"); } read() Diese Funktion liest das nächste Zeichen aus dem Empfangsbuffer der seriellen Schnittstelle. Falls keine Daten empfangen wurden, wird der Wert - i von der Funktion zurückgegeben. Serial.read() empfangeneDaten = Serial read () ; print() Sendet Daten als ASCII-Zeichen zur Ausgabe an die serielle Schnittstelle. Dabei können verschiedene Ausgabeformate und -typen ausgegeben werden. Serial.print(Wert) Serial.print(Wert, Format) Beispiel von Ausgabemöglichkeiten: Ausgabeanweisung Ausgabeformat Serial.print(45) "45" Serial.print(t.23456) "1.23" (Standard sind zwei Nachkommastellen) Serial.print('A') "A" Serial.print("Hallo Arduino.") "Hallo Arduino." Mit einem optionalen zweiten Parameter für das Format kann das Ausgabeformat gesteuert werden. Ausgabeanweisung Ausgabeformat Serial.print(65, BIN) "r000001" Serial.print(65, OCT) "for" Serial.print(65, DEC) "65" Serial.print(78, HEX) "0" Serial.println(1.23456,0) "I" Serial.println(1.23456,2) "1.23" Serial.println(1.23456,4) "1.2346" printIn() Sendet Daten mit einem anschließenden Zeilenumbruch (Carriage Return und Linefeed) an die serielle Schnittstelle. Der Carriage Return entspricht dem ASCII-Zeichen 13 oder »\r«, ein Linefeed entspricht dem ASCII-Zeichen 10 oder »\n«. Serial.println(Wert) Serial.println(Wert, Format) Die möglichen Formatausgaben entsprechen den Beispielen von print () write() Schreibt binäre Daten auf die serielle Schnittstelle. Dabei werden die Daten als ein oder mehrere Bytes versendet. Serial.write(Wert) Serial.write(String) Seriel.write(Buffer,Länge) Wert: Wert als Byte String: String in Form von mehreren Bytes Buffer: Array in Form von mehreren Bytes Länge: Größe des Arrays // Versenden von Byte mit Wert 45 Serial write(45) ; //Versenden von String "Hallo", Rückgabewert ist die Länge des Strings int bytesSent = Serial .write("Hallo") ; flush() Diese Funktion leert den Empfangsbuffer der seriellen Schnittstelle. Serial.flush()
Quelle:
arduino.cc/en/Reference/Home-Page
ident mit BUCH: ARDUINO Praxiseinstieg
DIN A4 ausdrucken ********************************************************* Impressum: Fritz Prenninger, Haidestr. 11A, A-4600 Wels, Ober-Österreich, mailto:[email protected] ENDE |
ARDUINO-IDE >