Der Arduino als flotter Taschenrechner
Mathematische Aufgaben auf einem Kleincomputer lösen
Der Arduino ist bestens für Automationsprojekte geeignet. Hier ist es immer mal wieder nötig, mathematische Formeln abzuarbeiten. Doch obwohl der Arduino durchaus mit einem umfangreichen Fundus an mathematischen Funktionen glänzen kann, müssen sich Entwickler stets vor Augen halten, dass dieser Kleincomputer Grenzen hat.
Kleincomputer werden für verschiedenste Dinge verwendet. Darunter sind viele Anwendungen, bei denen es darum geht, mathematische Aufgaben zu lösen, um beispielsweise abhängig vom Ergebnis unterschiedliche Aktionen auszulösen.
Die vorhandenen mathematischen Funktionen des Arduino sind überschaubar, doch für viele Zwecke völlig ausreichend, ganz abgesehen davon, dass sich bei entsprechenden Mathematikkenntnissen aus dem vorhandenen Fundus nahezu jede andere Rechenfunktion erstellen lässt.
Zur mathematischen Bibliothek des Arduino zählen unter anderem:
Grundrechenarten: Addition, Subtraktion, Multiplikation, Division
Fortgeschrittene Funktionen: abs, constrain, map, max, min, pow, Potenz, Wurzel
Trigonometrie: sin, cos, tan
Der Arduino bringt von Haus aus einen emulierten Monitor mit, auf dem sich beispielsweise Texte und Rechenergebnisse darstellen lassen. Dadurch kann auf den Einsatz einer externen LCD-Anzeige verzichtet werden. Dieser Monitor wird über das Menü ›Werkzeuge‹ unter dem Menüpunkt ›Serieller Monitor‹ oder über die Tastenkombination STRG+SHIFT+M aufgerufen.
Beim Programmieren eines Computers muss diesem zunächst mitgeteilt werden, welche Variablen im Programm vorkommen und mit welchen Werten diese gefüllt sind. Diese Variablen müssen einem Datentyp entsprechen, der ihrem Verwendungszweck entspricht.
Folgende Datentypen sind beim Arduino im Zusammenhang mit mathematischen Aufgaben besonders wichtig:
bool (true oder false)
byte (speichert eine vorzeichenlose 8-bit-Zahl von 0 bis 255)
char (zum Speichern von ASCII-Zeichen)
const (unveränderliche Konstante)
double (Fließkommazahl mit doppelter Genauigkeit)
float (Fließkommazahl)
int (Ganzzahl von -32.768 bis +32.767)
long (Ganzzahl von -2.147.483.648 bis 2.147.483.647)
word (speichert eine vorzeichenlose 16-bit-Zahl von 0 bis 65535)
Um ein erstes Testprogramm zu schreiben, das eine Integerzahl (ganze Zahl ohne Dezimalstellen) mit den Grundrechenarten manipuliert, sowie daraus die Potenz beziehungsweise die Wurzel berechnet, muss diese Zahl im Sketch zunächst bestimmt und ihr ein Datentyp zugweisen werden.
Soll beispielsweise die Zahl 16 als Intergerzahl für die vorgesehenen Berechnungen genutzt werden, so ist eine Variable mit der Bezeichnung ›zahl‹ anzulegen, der der Datentyp ›Integer‹ zugewiesen wird. Dies geschieht wie folgt:
Hinweis:
Es ist nicht nötig, direkt der Variablen bereits bei der Deklaration einen Wert zuzuweisen, kann jedoch gemacht werden, wie am obigen Beispiel zu sehen ist. Alternativ ist es möglich, der Variable dann einen Wert zuzuweisen, wenn ein solcher benötigt wird.
Das Ergebnis der Berechnung muss wiederum in einer Variablen abgelegt werden, deren Datentyp ebenfalls vom Typ ›Integer‹ sein soll. Die Anlage ist wie folgt zu tätigen:
In der Variablen ›zahl‹ befindet sich nun die Zahl 16, die als Integerzahl definiert wurde. Mit dieser Zahl lassen sich die im Arduino implementierten mathematischen Funktionen testen. Damit die Ergebnisse auf dem Monitor mitgelesen werden könne, muss dieser zunächst initialisiert werden, was wie folgt geschieht:
Ein Sketch für den mathematischen Testlauf könnte auszugsweise so aussehen:
zahl = 16;
Serial.print("Ausgangswert = ");
Serial.println(zahl); //Taste an Monitor ausgeben
ergebnis = zahl + zahl;
Serial.print(zahl); Serial.print(" + "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = zahl - zahl;
Serial.print(zahl); Serial.print(" - "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = 5 * zahl;
Serial.print("5 x "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = 5 / zahl;
Serial.print("5 / "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis =sq(zahl);
Serial.print("Potenz aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis =sqrt(zahl);
Serial.print("Wurzel aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
Es wird deutlich, dass der Befehl ›Serial.print‹ sich bestens eignet, fortlaufend Zeichen auf den Monitor zu drucken, ohne dass der Cursor in die nächste Zeile springt. Dies passiert erst durch den Befehl ›Serial.println‹.
Wie sich jedoch zeigt, sind die Ergebnisse nur solange korrekt, solange keine Flieskommaoperationen nötig werden, um das Ergebnis mathematisch richtig auszugeben. So ist die Berechnung 5/16 selbstverständlich nicht Null, sondern 0,3125.
Damit dieser Mangel beseitigt wird, müssen die Variablen ›zahl‹ und ›ergebnis‹ einen Datentyp bekommen, der Fließkommaoperationen erlaubt, was beispielsweise mit ›float‹ möglich wird.
Die beiden Variablen werden demnach wie folgt umgeschrieben:
Das Ergebnis des erneuten Rechendurchlaufs wird nebenstehend dargestellt. Es zeigt sich, dass die Berechnung nun passt.
Möchte man mehr Nachkommastellen in den Berechnungen haben, so muss im Befehl Serial.println oder Serial.print lediglich die gewünschte Nachkommastelle angegeben werden. Ein allgemeinses Beispiel wäre: Serial.println(Flieskommazahl, Nachkommastelle).
Im Sketch würde der Befehl mit fünf Nachkommastellen beispielsweise wie folgt aussehen:
Serial.println(ergebnis,5);
Selbstverständlich besitzt der Arduino-Befehlssatz auch trigonometrische Funktionen, sodass es mühelos möglich ist, beispielsweise die Länge einer Ankathete eines Dreiecks zu berechnen, wenn ein Winkel und beispielsweise die Gegenkathete gegeben sind.
Die Deklarationen der trigonometrischen Funktionen lauten:
Sin();
cos();
tan();
Zwischen den Klammern ist jeweils der Winkelwert einzutragen, der allerdings in Bogenmaß erwartet wird. Aus diesem Grund muss im Sketch eine weitere Variable definiert werden, um den umgerechneten Wert aufzunehmen. Nachfolgend ein Beispiel:
Um einen Winkel, der in Dezimalgrad vorliegt, in Bogenmaß umzurechnen, ist folgende Formel zu nutzen:
Soll beispielsweise der Winkel von 28 Grad in Bogenmaß umgerechnet werden, so ergibt sich folgendes Ergebnis:
Da im vorliegenden Beispiel die Variable ›zahl‹ mit dem gewünschten Rechenwert belegt ist, genügt es, diese Variable zur Berechnung im Zusammenhang mit den trigonometrischen Funktion heranzuziehen. So wird beispielsweise der in der Variable ›zahl‹ stehende Winkelwert von Dezimalgrad in Bogenmaß wie folgt berechnet:
Wie zu sehen ist, wurde die Ergebnisausgabe auf 12 Nachkommastellen ausgeweitet, wodurch eine Prüfung auf die Rechengenauigkeit des Datentyps ›float‹ möglich wird. Es zeigt sich, dass dieser Datentyp für sehr genaue Berechnungen ungeeignet ist. So wird beispielsweise als Ergebnis für den Sinus aus 28 der Wert 0,469471549987 ausgegeben. Ein Taschenrechner gibt hingegen das Ergebnis 0,469471562785890 aus.
Somit ergibt sich ab der siebten Stelle eine Abweichung, die durchaus geeignet ist, bei hochgenauen Berechnungen einen nicht hinnehmbaren Fehler zu produzieren. Aus diesem Grund einet sich der Arduino nur eingeschränkt für mathematisch exakte Berechnungen. Es fehlt im einfach an einem mathematischen Co-Prozessor. Da hilft auch der Datentyp ›double‹ nichts, der eine doppelte Genauigkeit der Fließkommaberechnungen verspricht.
Nichtsdestotrotz bleiben noch viele Projekte, bei denen die Rechengenauigkeit des Arduino völlig ausreichend ist.
Nachfolgend noch ein Auszug des Programms, um das Ergebnis der Ausführungen nachvollziehen zu können:
zahl = 28;
winkel = zahl*PI/180;
Serial.print("Ausgangswert = ");
Serial.println(zahl);
ergebnis = zahl + zahl;
Serial.print(zahl); Serial.print(" + "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = zahl - zahl;
Serial.print(zahl); Serial.print(" - "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = 5 * zahl;
Serial.print("5 x "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis = 5 / zahl;
Serial.print("5 / "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis,5);
ergebnis =sq(zahl);
Serial.print("Potenz aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis =sqrt(zahl);
Serial.print("Wurzel aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis);
ergebnis =sin(winkel);
Serial.print("Sinus aus "); Serial.print(zahl); Serial.print(" = "); Serial.println(ergebnis,12);
ergebnis =cos(winkel);
Serial.print("Cosinus aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis,12);
ergebnis =tan(winkel);
Serial.print("Tangens aus "); Serial.print(zahl); Serial.print(" = ");
Serial.println(ergebnis,12);