Anhang A: Codereferenz

A.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 A.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 Variablendeklarationen oder Konfiguration der seriellen Schnittstelle vorgenommen. 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 A.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 A.3:  Arduino-Sketch: Setup-Funktion ohne Deklarationen

Die Loop-Funktion ist der zweite Teil der Grundstruktur eines Arduino-Programms und übernimmt die Aufgabe eines Hauptprogramms . Nach dem einmaligen Durchlaufen der Setup-Funktion wird die Loop-Funktion durchlaufen – wie der Name schon sagt als endlose Schleife. In dieser Schleife 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
{
  digitalWrite(ledPin, HIGH);   // LED einschalten
  delay(1000);                  // 1 Sekunde warten
  digitalWrite(ledPin, LOW);    // LED ausschalten
  delay(500);                   // 0,5 Sekunden warten
  // und weiter geht’s am Start det der Schleife
}

Listing A.4:  Arduino-Sketch: Hauptschleife loop()

A.2  Aufbau einer Funktion

Eine Funktion ist ein in sich geschlossener Satz von Programmzeilen. Funktionen besitzen 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 und gegebenenfalls ein Rückgabewert . Der Typ des Rückgabewert es 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 eines externen Sensors einliest, umrechnet und zurückgibt. Als Parameter wird die jeweilige Portnummer übergeben.

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 A.5:  Arduino-Sketch: Aufruf einer 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 Pin-Nummer übergeben.

int tempPin=1;         // Pinnummer von Analogport
ReadSensor(tempPin);   // Aufruf Funktion

A.3  Konventionen

Bei der Programmierung der Sketche 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-Sketchen werden drei verschiedene Arten von Klammern unterschieden: runde Klammern (), geschweifte Klammern {} und eckige 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 hervorgehoben wird (und umgekehrt).

Abb. A.1:  Codierung: Darstellung von öffnenden und schließenden Klammern in der Entwicklungsumgebung

Eckige Klammern werden bei Arrays verwendet.

Beispiel:

// Liste mit Werten
int myWerte[5] = {34, 12, 64, 5, 18};

Arrays werden in einem späteren Abschnitt genauer beschrieben.

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 abschließt.

int tempGrad = 12;
const int ledPin =  13;
Serial.println(tempC);

Ohne Semikolon verwendet wird die Definitionsanweisung #define .

#define DCF77PIN 2     // Port 2 als DCF-Eingang
#define BLINKPIN 13    // Port 13 als LED-Ausgang
#define TEMPERATURE 2  // Analogeingang 2 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. Er beinhaltet Beschreibungen und Informationen zu einem Arduino-Sketch.
Der Kommentar kann sich über mehrere Zeilen erstrecken.
*/
 
// 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, weil es dadurch anderen Personen leichter fällt, den Code zu warten.

Mittels des Kommentarblocks kann ein ganzer Codebereich für das Debugging aktiviert oder deaktiviert werden.

A.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 Ultraschallsensor oder ein Temperaturwert, erfolgt mittels Variablen. Eine Variable besitzt einen Namen und einen Wert eines bestimmten Datentyps. 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 besitzen eine Länge von 16 Bit.

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 + 1, der Variablenwert auf -32.768 gesetzt wird.

Unsigned Int

Dieser Datentyp entspricht dem Datentyp int , außer dass unsigned int keine negativen Werte speichert.

Wertebereich: 0 bis 65.535

Byte

Beim Datentyp byte sind die Variablenwerte ebenfalls ganzzahlig. Die Länge des Wertes beträgt 8 Bit.

Wertebereich: 0 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 32-Bit-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 32-Bit-Zahl gespeichert werden.

Wertebereich: 0 bis 4.294.967.295

Float

Zahlen vom Datentyp float werden als 32-Bit-Fließkommazahl mit Nachkommastellen gespeichert. Berechnungen mit Fließkommazahlen sind langsamer als Integerberechnungen. Zur schnelleren Ausführung von Rechenoperationen 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 bei der Arduino-Programmierung dem Datentyp float .

Char

Mit dem Datentyp char werden Werte von Buchstaben und Zeichen als 8-Bit-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 8-Bit-Datentyp ohne Vorzeichen ist der Datentyp byte .

Boolean

Der Datentyp boolean besitzt nur zwei 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

Eine Aneinanderreihung von Zeichen des Datentyps char wird als String (Zeichenkette) bezeichnet.

Beispiele:

// leerer String mit fixer Länge
char myStr1[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 entstammt der fortgeschrittenen C-Programmierung und wird mit einem * (Asterisk ) nach der Typdefinition 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 über 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";

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 vierte Beispiel myText[8] zeigt ein Array des Typs char . In diesem Fall ist zu beachten, dass bei der Größe des Arrays die Anzahl der Zeichen zuzüglich 1 angegeben werden muss, weil die 0 als Abschluss des Strings ebenfalls Speicherplatz belegt.

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 Speicherplatzproblem entstehen. Dies ist meist an 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 Indexes des gewünschten Werts.

// Abfrage eines Array-Wertes
wert = myWerte[2];    // Abfrage des Wertes 64 aus dem Array
wert2 = myPorts[3];   // Abfrage ergibt den Wert 10

Da der erste Wert im Array den Index 0 besitzt, wird der dritte Wert innerhalb des Arrays mit dem Index 2 aufgerufen. Diese Tatsache ist speziell zu beachten, wenn man mittels einer Schleife ein ganzes Array durchsucht.

Ein Array der Länge 5 besitzt also Indexwerte von 0 bis 4. Der Index des letzten Wertes innerhalb des Arrays ist somit immer die Größe des Arrays minus 1.

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 die 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
};

A.5  Datentypkonvertierung

Die Datentypkonvertierung benötigt man oftmals in der Praxis, um beispielsweise seriell empfangene Daten in einen Integerwert umzuwandeln.

Die folgende Tabelle A.1 zeigt verschiedene Varianten der Typkonvertierung:

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 A.1:   Typenkonvertierung

A.6  Variablen & Konstanten

A.6.1  Variablen

In einem Programm werden Werte, die zur Weiterbearbeitung gespeichert sind, mit einem Bezeichner benannt. Der Bezeichner, 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 EingabePin 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;

A.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 zur 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  // 1000 ms 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 besitzt immer den Wert 0. Der Wert der Konstanten true dagegen beträgt »alles außer 0«, meist wird jedoch 1 verwendet. Aber auch Werte von 2, 10 oder 100 werden als true bewertet.

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 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 des digitalen Eingangs 2 lesen

Zu beachten ist, dass diese beiden Konstanten 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);

A.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 (statusPin1 == 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 bis zum Wert 255 hoch und gibt jeweils den Wert als PWM-Signal (PWM = Pulsweitenmodulation) aus.

while

Wiederholtes Ausführen einer Schleife, bis eine Bedingung erfüllt ist.

wert = 0;
while(wert < 200)
{
 // Anweisung
 wert++;
}

Die Schleife wird ausgeführt, bis Wert = 200 ist, wobei der Wert bei jedem Schleifendurchgang um 1 erhöht wird.

Do ... while

Ähnlich der while -Schleife, aber bei der do ... while -Schleife erfolgt die Prüfung der Bedingung erst am Ende.

do
{
 temp = analogRead(tempPin);
} while (temp < 40);

Das Ausführen der Schleife findet statt, 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);
}

Die Schleife wird unterbrochen, falls stopInp high ist.

continue

Unterbricht eine Schleife und springt wieder zur Prüfung der Bedingung.

for (int i = 0; i < 255; i++)
{
  // falls i zwischen 50 und 100, i nicht ausgeben
  if (i > 50 && i < 100)
  {
    continue;
  }
  digitalWrite(PWMPin, i);
  delay(20);
}

return

Beendet eine Funktion und gibt einen Wert zur Weiterverarbeitung im übergeordneten Programm zurück.

int getTemperatur(inPin)
{
  int valTemp=0;
  valTemp=analogRead(inPin);
  return valTemp;
}

A.8  Mathematische Funktionen

min (x,y)

Ermittelt das Minimum zweier Werte und gibt den kleineren Wert zurück.

wert = min(wert, 88);

Setzt die Variable wert so, dass wert nie größer als 88 werden kann.

max (y,z)

Ermittelt das Maximum zweier Werte und gibt den höheren zurück.

wert = max(wert, 200);

Setzt die Variable wert so, dass wert nie kleiner als 200 werden kann.

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.

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.

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 (Wert, fromLow, fromHigh, toLow, toHigh)

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 inso beispielsweise auf einen Bereich von 0 bis 100 Prozent abgebildet 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.

In der Praxis wird map() oft eingesetzt, um einen eingelesenen Analogwert (10 Bit) in einen 8-Bit-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.

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 im Bogenmaß. Der Rückgabewert liegt zwischen -1 und +1.

cos (rad)

Berechnung des Cosinus eines Winkels im Bogenmaß. Der Rückgabewert liegt zwischen -1 und +1.

tan (rad)

Berechnung des Tangens eines Winkels im Bogenmaß.

A.9  Zufallszahlen

Die Generierung einer Zufallszahl erfolgt mittels des Pseudo-random-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 PRNGs 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);

A.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 (Rest bei ganzzahliger Division)

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 größer als b
a <= b    // a ist kleiner oder gleich b
a >= b    // a ist größer oder gleich b

Gemischte Zuweisungen

Gemischte Zuweisungen sind eine Art Kurzschreibweise für arithmetische Operationen und Variablenzuweisungen.

Inkrement/Dekrement

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 liegt
if (temp >= 15 && temp <= 25)
{
  // Weitere Anweisungen
}
 
// Eingänge prüfen
if (InpPin4 == HIGH  && InpPin5 == HIGH)
{
  // Weitere Anweisungen
}

Logisches or (||)

Das Resultat ist true , wenn einer der Werte true ist.

if (a > 10 || b > 10)
{
  // Weitere Anweisungen
}

Logisches not (!)

Das Resultat wird true , wenn der Ausdruck false ist.

if (!x > 5)
{
  // Weitere Anweisungen
}

A.11  Funktionen

A.11.1  Digitale Ein- und Ausgänge

pinMode ()

Konfiguriert einen digitalen Port als Ein- oder Ausgang.

pinMode
(8, OUTPUT);  // Port 8 als Ausgang
pinMode
(9, INPUT);   // Port 9 als Eingang

Eingang einlesen

Liest den Wert des digitalen Eingangs ein.

InpPin = digitalRead(4);   // Einlesen von Port 4, Wert wird in der Variablen 
                                // InpPin abgelegt

Ausgang setzen

Setzt den Ausgang auf 1 oder 0, entspricht high oder low .

digitalWrite(8, HIGH);  // Ausgang 8 EIN
digitalWrite(9, LOW);   // Ausgang 9 AUS

Eingangspuls messen

Diese Funktion misst die Dauer eines am Eingang anliegenden Eingangspulses. Dabei kann die Pulszeit zwischen 10 Mikrosekunden und 3 Minuten liegen.

Der Wert high oder low gibt an, ob man einen high -Puls oder einen low -Puls messen möchte. Bei high wird darauf gewartet, dass das am Port anliegende Signal auf high wechselt, dann beginnt die Zeitmessung, die endet, wenn das Signal wieder low ist. Die gemessene Zeit wird in Mikrosekunden angegeben.

Die Angabe der Zeitüberschreitung (Timeout), also wie viele Mikrosekunden auf das Signal gewartet werden soll, ist optional. Als Standardwert ist 1 Sekunde definiert.

int PulsPin = 2;
unsigned long zeitdauer;
zeitdauer = pulseIn(PulsPin, HIGH);

A.11.2  Analoge Ein- und Ausgänge

Analoge Eingänge

Analoge Eingänge arbeiten mit einer Auflösung von 10 Bit, das entspricht einem Bereich von 0 bis 1023. Das Eingangssignal liegt dabei im Bereich von 0 bis 5 Volt.

tempIn = analogRead(0);    // Analogport 0 einlesen, Wert
                           // in Variable tempIn ablegen

Als analoge Eingänge können folgende Ports verwendet werden:

(0–5): Arduino-Standardboards

(0–7): Arduino Mini und Nano

(0–15): 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 8 Bit.

analogWrite(10, 123);  // Ausgeben eines analogen Wertes an Port 10

Zur analogen Ausgabe sind folgende Ports verwendbar:

(3, 5, 6, 9, 10, 11): Arduino-Standardboards

(2–13): Arduino Mega

A.11.3  Tonausgabe

tone()

Ausgabe eines Rechtecksignals mit einstellbarer Frequenz und einer Pulsweite von 50%. Das Ausgangssignal kann direkt an einem Piezo-Alarmgeber oder einem Lautsprecher angeschlossen werden.

Durch die Eingabe der Nummer des Ports, an dem ein Lautsprecher 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() gestartet wurde.

A.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 erfasst wird, kann damit ein Interrupt ausgelöst werden, der einen Zähler um 1 erhöht.

attachInterrupt ()

Diese Funktion löst mittels eines Signals an einem definierten digitalen Port einen Interrupt aus, der eine wählbare Programmfunktion aufruft.

Parameter InterruptNummer

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

Diese Funktion wird ausgeführt, wenn das Eingangssignal an den erwähnten Ports erkannt wird.

Parameter Modus

Dieser Parameter gibt an, bei welcher Signaländerung ein Interrupt ausgelöst werden soll.

  • low : Löst den Interrupt aus, wenn der 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
}

detachInterrupt ()

Schaltet den Interrupt aus.

InterruptNummer: 0 oder 1 (bei Arduino-Standardboards)

A.12  Zeitfunktionen

Bei den Zeitfunktionen unterscheidet man zwischen Funktionen, um Zeiten zu messen und Funktionen, um definierte Pausen (Verzögerungen) einzulegen.

delay ()

Pausiert ein Programm für die in ms angegebene Zeit. 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 des Datentyps unsigned long .

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.

A.13  Serielle Kommunikation

Auf dem Arduino Duemilanove wird die serielle Schnittstelle einerseits zur Kommunikation mit dem angeschlossenen Rechner über den USB-Port und andererseits auf den Ports 0 (rx ) und 1 (tx ) zur externen 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.

Übertragungsrate Wertebereich:

300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200

void setup()
{
  // Konfiguration serielle Schnittstelle
  Serial.begin(9600);  // Übertragungsgeschwindigkeit
}

Wird die serielle Schnittstelle verwendet, so können die digitalen Ports D0 und D1 nicht für andere Aufgaben genutzt 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 Ports 0 und 1. Die beiden digitalen Ports können anschließend wieder für andere Anwendungen genutzt werden.

available ()

Diese Funktion prüft, ob im Empfangspuffer der seriellen Schnittstelle Daten vorhanden sind. Der Rückgabewert ist die Anzahl der vorhandenen Bytes. Falls Daten vorhanden sind, können diese nun mit read() aus dem Puffer gelesen werden. Zu beachten ist, dass der Empfangspuffer eine maximale Größe von 128 Bytes besitzt. Darum muss im Programm sichergestellt sein, dass die Daten des Empfangspuffers regelmäßig ausgelesen werden.

// prüfen, ob Daten empfangen wurden
if (Serial.available() > 0)
{
  // Daten aus Eingangspuffer einlesen
  empfangeneDaten = Serial.read();
  // Ausgabe von Infomeldung
  Serial.print("Du hast Daten empfangen"); 
}

read ()

Diese Funktion liest das nächste Zeichen aus dem Empfangspuffer der seriellen Schnittstelle. Falls keine Daten empfangen wurden, gibt die Funktion den Wert -1 zurück.

empfangeneDaten = Serial.read();

print ()

Sendet Daten als ASCII-Zeichen zur Ausgabe an die serielle Schnittstelle. Dabei können verschiedene Ausgabeformate und -typen ausgegeben werden.

Beispiel von Ausgabemöglichkeiten :

Ausgabeanweisung

Ausgabeformat

Serial.print(45)

"45"

Serial.print(1.23456)

"1.23"
(Standard sind zwei Nachkommastellen)

Serial.print('A')

"A"

Serial.print("Hallo Arduino.")

"Hallo Arduino."

Mit einem optionalen zweiten Parameter kann das Ausgabeformat gesteuert werden.

Ausgabeanweisung

Ausgabeformat

Serial.print(65, BIN)

"1000001"

Serial.print(65, OCT)

"101"

Serial.print(65, DEC)

"65"

Serial.print(78, HEX)

"41"

Serial.println(1.23456, 0)

"1"

Serial.println(1.23456, 2)

"1.23"

Serial.println(1.23456, 4)

"1.2346"

println ()

Sendet Daten mit einem anschließenden Zeilenumbruch (Carriage Return und Linefeed) an die serielle Schnittstelle. Das Carriage Return entspricht dem ASCII-Zeichen 13 oder »\r«, ein Linefeed entspricht dem ASCII-Zeichen 10 oder »\n«.

Formatangaben sind entsprechend den bei der print() -Funktion aufgeführten Beispielen möglich.

write()

Schreibt binäre Daten auf die serielle Schnittstelle. Dabei werden die Daten als einzelne Bytes oder in Form mehrerer Bytes in einem Puffer versendet.

Wert: Wert als Byte

String: String in Form von mehreren Bytes

Puffer: 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 Empfangspuffer der seriellen Schnittstelle.