Stelle vor dem Lesen sicher, dass du die Grundlagen kennst, die in den vorherigen Teilen unseres kostenlosen Arduino Kurses beschrieben wurden!
Darstellung von Zahlen im Terminal
Die im Terminal sichtbaren Informationen sind für Menschen lesbar, da der Arduino sie in ASCII-Codes umwandelt. Sogar Zahlen werden als Text gesendet.
Wir haben alle Werte, die wir in den von uns durchgeführten Aufgaben gesendet haben, in dezimaler Form angezeigt. Was wäre, wenn wir Zahlen z. B. binär darstellen möchten? Müssen wir unsere eigenen Funktionen erstellen, um die Darstellung der Werte in andere Systeme umzuwandeln? Nein!
An dieser Stelle sei daran erinnert, dass Computer hauptsächlich das Binärsystem verwenden, so dass jedes andere System (einschließlich unseres, das Dezimalsystem) künstlich erzwungen wird.
Glücklicherweise enthalten die Arduino-Bibliotheken eine Reihe von Hilfsmitteln für Endbenutzer. Dazu gehört die Unterstützung für verschiedene Zahlendarstellungssysteme. Wenn wir einen Wert über println senden, können wir entscheiden, wie er auf dem Computer angezeigt werden soll:
[...]
int Zahl = 2345;
Serial.println(Zahl); //Anzeigen in Dezimalzahlen
Serial.println(Zahl, DEC); //Anzeigen in Dezimalzahlen
Serial.println(Zahl, HEX); //Anzeigen in hexadezimaler Form
Serial.println(Zahl, OCT); //Anzeigen im Oktalsystem
Serial.println(Zahl, BIN); //Anzeigen im Binärsystem
[...]
Wie die Experimente in den vorangegangenen Abschnitten gezeigt haben, werden Zahlen standardmäßig in dezimaler Form angezeigt, aber man kann auch zwischen hexadezimaler, oktaler und binärer Darstellung wählen. In der Praxis wird man wahrscheinlich am häufigsten die standardmäßige dezimale Darstellung und die binäre Darstellung verwenden.
Fertige Sets für Forbot-Kurse
Sie können jetzt ein Set von mehr als 70 Elementen, die für die Kursübungen notwendig sind, bei unseren Händlern kaufen!
Beliebtes Paket: Arduino Meister – Robotik Meister
Genauigkeit von Gleitkommazahlen
Solche Operationen sind für Computer relativ schwierig und benötigen mehr Zeit.
Nehmen wir aber an, dass wir unbedingt eine solche Zahl anzeigen wollen. Hierfür könnte das folgende, sehr kurze Programm verwendet werden:
void setup() {
float ZahlPI = 3.1415; //Deklaration der Variablen
Serial.begin(9600); //UART-Initialisierung
Serial.println(ZahlPI, 4); //4 Nachkommastellen
Serial.println(ZahlPI, 0); //0 Nachkommastellen
Serial.println(PI); //Rätsel
}
void loop() {
}
Die Ausführung des oben genannten Programms sollte folgende Auswirkungen haben:
Wie man sieht, kann bei der Übermittlung einer Zahl ein zusätzlicher Parameter eine Ziffer sein, die angibt, mit welcher Genauigkeit die Werte dargestellt werden sollen. Dabei ist es wichtig, daran zu denken, dass Float-Variablen mit bis zu 7 Stellen dargestellt werden – unabhängig davon, wie viele Stellen nach oder vor dem Komma stehen. Zum Beispiel:
- float Zahl1 = 0,123456 – richtig
- float Zahl2 = 12345.6 – richtig
- float Zahl3 = 123.456 – richtig
- float Zahl4 = 1234567.8 – falsch
Für eine höhere Genauigkeit (15 Stellen), verwende doppelte Variablen!
Kehren wir noch einmal kurz zu dem oben genannten Programm zurück, insbesondere zu der rätselhaften Zeile:
Serial.println(PI); //Rätsel
Warum ergab dieser Befehl einen Wert von 3,14? Wir haben nirgendwo eine solche Variable deklariert. Nun, der Wert der Zahl Pi wird so häufig verwendet, dass es in vielen Sprachen fertige Konstanten gibt, die ebenfalls annähernd die Zahl Pi darstellen. In diesem Fall ist es PI, das irgendwo im Programm in den entsprechenden Wert umgewandelt wird.
Aber VORSICHT: Du kannst dich hier sehr ernsten Risiken aussetzen!
Wenn wir zum Beispiel eine hohe Präzision in unserem Programm anstreben, könnten wir diese Funktion aufrufen:
Serial.print(PI, 25);
Zu unserer Freude wird dann ein sehr genauer Wert auf dem Bildschirm angezeigt:
3.1415927410125732421875
Aber gehen wir im Gedächtnis zurück zur maximalen Präzision, die Mikrocontroller bieten, so stellt sich heraus, dass die Konstante PI die Eigenschaften einer Float-Variablen hat, d.h. sie benötigt nur 7 Stellen! Mehr als das ist falsch, denn die genaueste Darstellung von Gleitkomma-Werten im Binärsystem erlaubt es nicht, das entsprechende „wahre Pi“ zu erhalten. Mehr darüber erfährst du in diesem speziellen Rechner.
Die folgende Zusammenstellung ist unsere angezeigte Zahl und das wahre Pi:
3.14159274101257324218750
3.14159265358979323846264
Aus den obigen Beispielen geht hervor, dass man, wann immer es möglich ist, Gleitkommazahlen vermeiden sollte (in 99 % der Fälle kann man auf ihre Hilfe verzichten). Wenn wir sie dennoch verwenden, dann mit gesundem Menschenverstand und in Kenntnis der begrenzten Genauigkeit!
Alles in einer neuen Zeile?
Sie hat genau die gleichen Werte wie die bisher verwendete println-Funktion. Nur dass jeder gesendete Wert in einer neuen Zeile erscheint. Beispiel:
void setup() {
Serial.begin(9600); //UART-Initialisierung
}
void loop() {
Serial.print("Willkommen zum Kurs auf Forbot.com! "); //Anzeige des Textes
delay(1000); //Verzögerung für mehr Komfort
}
Das Programm beeindruckt nicht durch seine Fähigkeiten, sondern demonstriert das Wesentliche (keine neue Zeile):
Wie wechselt man zu einer neuen Zeile an einer bestimmten Stelle? Auf drei Arten:
Serial.print("Erste Zeile");
Serial.println();
Serial.print("Zweite Zeile");
ODER
Serial.println("Erste Zeile");
Serial.print("Zweite Zeile");
ODER
Serial.print("Erste Zeile \n Zweite Zeile");
Gibt es noch andere nützliche Symbole dieser Art? Ja! Die Verwendung von Tabulatoren (Einzüge, großer Abstände) kann für die Formatierung des angezeigten Textes immer noch nützlich sein. Wenn wir den Text nach rechts verschieben wollen, verwenden wir das Symbol \t – Tabulator statt der umständlichen Eingabe mehrerer Leerzeichen.
Neue Informationen über den UART in der Praxis
Zu Beginn muss ein einfacher Stromkreis angeschlossen werden. Ich habe ein Potentiometer verwendet, um die Spannung zu ändern. Du könntest aber auch einen Spannungsteiler mit einem Fotowiderstand einbauen. Wenn du nicht mehr weißt, wie das geht, sieh dir Teil 4 des Kurses an.
Ich hoffe, dass die vorherigen Informationen dir nicht zu viele Probleme bereitet haben, deshalb möchte ich gleich das fertige Programm zeigen. Natürlich solltest du analysieren, wie es funktioniert, und selbst ein ähnliches Programm schreiben!
void setup() {
Serial.begin(9600); //UART-Initialisierung
}
void loop() {
int Potentiometer = analogRead(A5); //ADC-Wert ablesen
Serial.print("Ablesung: ");
Serial.print(Potentiometer, DEC);
Serial.print("[DEC]\t");
Serial.print(Potentiometer, HEX);
Serial.print("[HEX]\t");
Serial.print(Potentiometer, OCT);
Serial.print("[OCT]\t");
Serial.print(Potentiometer, BIN);
Serial.print("[BIN]\n");
delay(1000); //Verzögerung für Komfort
}
Nach dem Start des Programms sollte man im Terminal die schön formatierten ADC-Werte in verschiedenen Zahlendarstellungen sehen:
Ich möchte euch ermutigen, selbst zu experimentieren. Es ist auch eine ideale Gelegenheit, um die manuelle Umwandlung von Zahlen in verschiedene Systeme zu üben. Damit ist der Teil über den UART abgeschlossen, es ist Zeit, weiterzumachen.
Hausaufgabe 6.1
Schreibe ein Programm, das die Informationen von zwei Fotowiderständen und einem Potentiometer ausliest. Wenn dann der an den Arduino angeschlossene Knopf gedrückt wird, sende einmalig eine Zeile mit den Informationen:
„Fotowiderstand 1: XXX, Fotowiderstand: XXX, Potentiometer: XXX, Taste XX mal gedrückt“.
Wobei anstelle von X natürlich die richtigen Werte erscheinen werden.
Anweisung zur Steuerung des Switches
Um die Switch-Anweisung zu verstehen, werde ich ein Beispiel verwenden, das dann auf zwei Arten gelöst wird – traditionell und mit einer neuen Methode. Nehmen wir also an, dass wir ein Programm schreiben wollen, das den ADC-Wert liest und ihn dann als Dezimal-, Hexadezimal-, Oktal- oder Binärzahl an uns zurückschickt. Das hängt alles von unserer Wahl ab.
Mit unserem derzeitigen Wissen könnten wir ein Programm schreiben, da Bedingungen verwendet:
String empfangeneDaten = ""; //Leere Zeichenfolge der empfangenen Daten
void setup() {
Serial.begin(9600); //UART-Initialisierung
}
void loop() {
int Potentiometer = analogRead(A5); //Lesen des ADC-Wertes
if(Serial.available() > 0) { //Hat der Arduino die Daten empfangen
empfangeneDaten = Serial.readStringUntil('\n'); //Wenn ja, lese sie bis zum Zeilenendezeichen
}
if (empfangeneDaten == "d") {
Serial.println(Potentiometer, DEC);
} else if (empfangeneDaten == "h") {
Serial.println(Potentiometer, HEX);
} else if (empfangeneDaten == "o") {
Serial.println(Potentiometer, OCT);
} else if (empfangeneDaten == "b") {
Serial.println(Potentiometer, BIN);
}
delay(1000); //Verzögerung für Komfort
}
Durchführbar? Ja. Bequem? Mittelmäßig, vor allem, wenn es viel mehr Bedingungen gab oder die Bedingungen plötzlich geändert werden mussten. Hilfreich ist die neue Steuerungsanweisung switch-case. Sie sieht wie folgt aus:
switch (WertZurPrüfung) {
case Wert_1:
//Code, wird ausgeführt, wenn die Bedingung erfüllt ist
break;
case Wert_2:
//Code, wird ausgeführt, wenn die Bedingung erfüllt ist
break;
[...]
default:
//Code wird ausgeführt, wenn keine der Bedingungen erfüllt ist
break;
}
Am Anfang schreiben wir das Schlüsselwort switch, dann geben wir in runden Klammern die Variable an, die wir überprüfen wollen. In einem ähnlichen Beispiel wie dem vorangegangenen mit if’s würde dies lauten:
switch (empfangeneDaten) {
Wenn die Bedingung erfüllt ist, wird der Code von der Bedingung bis zum nächsten Wort break ausgeführt, wodurch der gesamte Switch beendet wird. Wenn die Bedingung nicht erfüllt ist, wird ein Teil des Codes ignoriert, und der Mikrocontroller fährt mit der Prüfung der nächsten Bedingung (case) fort.
Zur Erinnerung!
Die Anweisung „switch-case“ ist sehr nützlich, wenn du prüfen willst, ob die Werte gleich sind!
Schließlich können wir optional den Code zwischen default und break platzieren. Dieser wird ausgeführt, wenn keine der vorherigen Bedingungen erfüllt ist. Ich weiß, dass sich das kompliziert anhört, also werden wir jetzt zu einem praktischen Beispiel übergehen und das frühere Programm überarbeiten.
int empfangeneDaten = 0; //Leere Zeichenfolge der empfangenen Daten
void setup() {
Serial.begin(9600); //UART-Initialisierung
}
void loop() {
int Potentiometer = analogRead(A5); //Lesen des ADC-Wertes
if(Serial.available() > 0) { //Hat der Arduino Daten empfangen
empfangeneDaten = Serial.read(); //Wenn ja, 1 Zeichen lesen
}
switch (empfangeneDaten) {
case 'd':
Serial.println(Potentiometer, DEC);
break;
case 'h':
Serial.println(Potentiometer, HEX);
break;
case 'o':
Serial.println(Potentiometer, OCT);
break;
case 'b':
Serial.println(Potentiometer, BIN);
break;
}
delay(1000); //Verzögerung für Komfort
}
Außerdem wird anstelle der bisher verwendeten Funktion, werden die Daten gelesen:
empfangeneDaten = Serial.readStringUntil('\n');
Es wird eine einfachere Version der Funktion verwendet, die nur das erste Byte (Zeichen) der Daten liest:
empfangeneDaten = Serial.read(); //Wenn ja, 1 Zeichen lesen
So konnten wir die gesendeten Befehle vergleichen und die entsprechenden Operationen durchführen. Ich hoffe, dass euch die Switch-Anweisung noch klarer wird, wenn wir weitere praktische Beispiele durchführen.
Hausaufgabe 6.2
Servomechanismus in der Praxis - Lichtanzeige
Einige Werte wie Temperatur, Lichtintensität usw. lassen sich jedoch besser auf traditionellen Analoganzeigen darstellen. Das heißt, solche mit einem Zeiger:
Der Montageplan des fertigen Geräts sieht wie folgt aus:
Er ist ein wenig kompliziert, besteht aber eigentlich aus zwei einfachen Schemas. Das erste ist der Anschluss des Servos zusammen mit der Spannungsversorgung vom Stabilisator. Außerdem erscheinen zwei Kondensatoren direkt neben diesem Spannungsregler. Ein ähnliches Schema findet sich im vorherigen Teil des Arduino-Kurses.
Sei vorsichtig beim Anschließen des Servos und der Batterie, um nichts zu beschädigen!
Mechanischer Teil des Projekts
Es lohnt sich, gleich über ein „professionelles“ Zifferblatt und einen Zeiger nachzudenken. Das Zifferblatt habe ich aus ein paar aufgeklebten Visitenkarten und einer ausgedruckten Skala hergestellt. Der Zeiger wurde auf ähnliche Weise hergestellt. Zum Zusammenfügen der Teile empfehle ich Heißkleber (aus der Pistole) oder doppelseitiges Klebeband.
Bei der Verwendung von Sekundenkleber besteht die Gefahr, dass die beweglichen Teile des Servos verklebt werden, das dann nur noch zum Wegwerfen geeignet ist!
Die Verarbeitungsqualität ist nicht die beste, aber das sind nur Experimente, und es ist der Effekt, der zählt:
Das Programm ist relativ einfach. Seine Aufgabe ist es, zyklisch das auf den Fotorwiderstand fallende Licht zu messen und die Ausrichtung des Servos zu steuern. Hierfür wurden hauptsächlich die bereits erlernten Funktionen verwendet:
#include //Bibliothek zuständig für Servos
Servo servomechanismus; //Wir erstellen ein Objekt, damit wir auf das Servo verweisen können
byte Position = 0; //Aktuelle Servo-Position 0-180
int VorherigePosition = 0;
void setup() {
servomechanism.attach(11); //Servo angeschlossen an Pin 11
Serial.begin(9600);
}
void loop()
{
int SensorLesen = analogRead(A5); //Wir lesen den Wert aus dem Sensor
Position = map(SensorLesen, 0, 900, 0, 180); //Wir wandeln ihn in die Servoposition um
if (abs(Position-VorherigePosition) > 5) { //Wir prüfen, ob die Position um mehr als 5 Grad voneinander abweichen
servomechanism.write(Position); //Ausführen einer Bewegung
VorherigePosition = Position; //Speichern der aktuellen Position als vorherige Position
}
Serial.println(SensorLesen); //Sende den Wert an das Terminal
delay(300); //Verzögerung für einen besseren Effekt
}
In diesem Programm speichern wir außerdem die aktuelle Position des Servos in der globalen Variablen VorherigePosition. Dadurch wird sichergestellt, dass wir im nächsten Schleifenzyklus nur dann eine Bewegung durchführen, wenn es einen großen Wechsel der Lichtintensität gibt. Andernfalls könnte unser Zeiger oszillieren. Ich empfehle, mit dem Wert zu experimentieren, ab dem wir die Bewegung ausführen.
Jeder sollte das System für seine eigenen Bedingungen kalibrieren!
(Beschreibung unten)
Das Programm ist sehr einfach, so dass es keine automatischen Kalibrierungsmechanismen gibt. Daher sendet es den vom Lichtsensor abgelesenen aktuellen Wert über den UART an den Computer. Die schnellste Kalibrierung könnte darin bestehen, den niedrigsten und den höchsten Wert zu vermuten, der beobachtet wird, wenn der Sensor abgedeckt und beleuchtet ist. Diese sollten dann in dieser Zeile berücksichtigt werden:
Ich rate euch, Einstellungen zu ändern und neue Programme zu testen. Allerdings empfehle ich, das Servo nicht zu schnell zu bewegen. Dies kann zu Problemen führen oder den relativ empfindlichen Motor beschädigen. Ich würde vorschlagen, die in dieser Zeile eingestellten 100 ms nicht zu unterschreiten:
delay(300); //Verzögerung für besseren Effekt
Hausaufgabe 6.3
Verfeinere die Schaltung mit einer analogen Anzeige. Versuche, einen Kalibrierungsmechanismus hinzuzufügen. Finde eine weitere praktische Anwendung für eine solche Schaltung!
Zusammenfassung
Denke daran, dass der komplette Satz an Komponenten, der für alle Übungen benötigt wird, bei Botland erhältlich ist. Mit dem Kauf der Sets werden weitere Veröffentlichungen von Forbot unterstützt!
Die nächsten Teile des Kurses sind eine Demonstration der Verwendung des Textdisplays, das dem Set beiliegt.