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 ProgrammstrukturDie 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: GrundstrukturDie 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 SchnittstelleDie 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 DeklarationDie 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 FunktionEine 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 FunktionDurch 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 KonventionenBei 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.
KlammernIn 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
SemikolonEine 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 TemperatursensorKommentareKommentare 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 DatentypenIn 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.
Intint (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 IntegerZu beachten ist, dass bei einem »Überlauf« des Wertes, also bei 32.767 + i, der Variablenwert auf -32.768 gesetzt wird.
Unsigned IntDieser Datentyp entspricht dem Datentyp int, außer dass unsi gned int keine negativen Werte speichert.
Wertebereich: 0 bis 65.535
ByteBeim 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 LongIntegerzahlen 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 longUnsigned LongBei diesem long-Datentyp können nur positive Werte als 32bit-Zahl gespeichert werden.
Wertebereich: 0 bis 4.294.967.295
FloatZahlen 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 PiDoubleDer Datentyp double entspricht in der Arduino-Programmierung dem Datentyp float.
CharMit 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 TEine 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.
BooleanDer Datentyp boolean besitzt nur 2 mögliche Werte:
TRUE oder FALSE.
Mit diesem Datentyp werden die Binärwerte 1 oder 0 gespeichert.
Wertebereich: TRUE, FALSEBeispiel: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.
StringMit 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);
}
}ArrayEin 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 10Da 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 PositionWie 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 DatentypkonvertierungDie 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 & Konstanten6.1 VariablenIn 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 KonstantenKonstanten 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 5Die 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/falseDiese 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/LowMit 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 lesenZu beachten ist, dass diese beiden Konstantenwerte immer in GROSSBUCHSTABEN geschrieben werden müssen.
Input/OutputMit 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 KontrollstrukturenKontrollstrukturen steuern den Programmfluss und werden bei Entscheidungen eingesetzt.
ifEine 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)
}forDie 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
.
whileUnbegrenztes 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/caseDiese 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);
}breakMit 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.
continueUnterbricht 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);
}returnBeendet 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 Funktionenmin (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 ZufallszahlenDie 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 VergleichsfunktionenArithmetikDurchfü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)VergleichsoperatorenBei 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 bHinweisAchtung:
a = b ist keine Vergleichsoperation wie das erste Beispiel, sondern eine Wertezuweisung:
Wert a bekommt den Wert von b.
Gemischte ZuweisungenGemische 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 - 1Zusammengesetzte Zuweisungen
x += y; // entspricht x = x + y
x y; // entspricht x = x - y
x *= y; // entspricht x = x * y
x /= y; // entspricht x = x / yLogische OperatorenMit 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 ORDas 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 Funktionen11.1 Digitale Eingänge/AusgängepinMode()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 einlesenLiest den Wert des digitalen Eingangs ein.
digitalRead(PortNummer)
InpPin = digitalRead(4); // Einlesen von pin-4, Wert wird in Variable InpPin abgelegt
Ausgang setzenSetzt 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 AUSEingangspuls messenDiese 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ängeAnaloge EingängeAnaloge 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ängeDas 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
HinweisAnwendungsbeispiele und Konfigurationsmöglichkeiten der analogen Ausgabe als PWM sind in Kapitel 4 ausführlicher beschrieben.
11.3 Tonausgabetone()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 /Tone3notone()Stoppt die Ausgabe des Rechtecksignals, das mit tone 0 gestartet wurde.
11.4 InterruptsBei 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 merDie 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 FunktionDurch das Erkennen des Eingangssignals an den erwähnten Ports kann eine auszuführende Funktion angegeben werden.
Parameter ModeDieser 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 ZeitfunktionenBei 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 KommunikationAuf 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()