Arduino Kurs – #6 – UART-Fortsetzung, Servos

In diesem Teil werden wir mehrere Themen behandeln. Zunächst kehren wir für eine Weile zum UART zurück, wir werden auch die neuen Steueranweisungen besprechen. Zum Schluss noch eine praktische Anwendung für den Servo.

Stelle vor dem Lesen sicher, dass du die Grundlagen kennst, die in den vorherigen Teilen unseres kostenlosen Arduino Kurses beschrieben wurden!
Bestellen Sie ein Set mit Elementen und beginnen Sie mit dem Lernen in der Praxis! Hier gehts zum Shop >>

Darstellung von Zahlen im Terminal

Im dritten Teil des Kurses haben wir die UART-Schnittstelle zur Kommunikation mit dem Computer verwendet. Dort haben wir Funktionen verwendet, um Informationen zu senden und zu empfangen. Damals habe ich jedoch nicht alle Möglichkeiten besprochen, die uns die scheinbar einfache println-Funktion bietet. In den bisherigen Beispielen wurde sie in ihrer einfachsten Form verwendet, d. h. mit einem einzigen Argument – einer Zahl oder einer zu sendenden Zeichenfolge.

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
 Satz von Elementen   Garantierte Unterstützung   Versand in 24 Stunden

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 MeisterRobotik Meister

Genauigkeit von Gleitkommazahlen

Wie in früheren Teilen des Kurses besprochen, ist es auch möglich, eine Variable zu deklarieren, um eine Gleitkommazahl zu speichern, z.B. 3.141592: 3.141592. Wir haben uns bisher nicht mit solchen Beispielen beschäftigt, weil es sich bewährt hat, auf Mikrocontrollern Zahlen mit Nachkommastellen zu vermeiden.

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:

Genauigkeit der angezeigten Zahlen.

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?

Bisher erschien jeder Wert, der im Terminal angezeigt wurde, in einer neuen Zeile. Das war zwar lesbar, aber nicht immer sinnvoll. Was aber, wenn man mehrere Variablen und Texte nebeneinander anzeigen möchte? Die Funktion println (ohne das Suffix ln von line) schafft hier Abhilfe.

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):

Print Funktion in der Praxis.

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");
				
			
Die bei weitem interessanteste Art ist die letzte. Dort erscheint ein neues Symbol \n. Es kommt nicht nur beim Arduino vor und zeigt einen Übergang zu einer neuen Zeile an. Wie man sieht, ist das sehr praktisch, weil man damit jederzeit die Zeile unterbrechen kann.

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

Es ist nun an der Zeit, die oben aufgeführten Informationen in die Praxis umzusetzen. Das Ziel unseres Programms ist es, den Spannungswert an Pin A5 zu messen und ihn dann im Terminal anzuzeigen. Diesmal reicht es jedoch nicht aus, die Zahl in Dezimalzahlen anzuzeigen. Zusätzlich sollen – in einer Zeile – Werte in HEX, OCT und BIN angezeigt werden. Das Ganze soll natürlich schön formatiert werden!

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.
Potentiometer angeschlossen an A5.

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:

Arduino - unterschiedliche Darstellung von Zahlen.

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

Es ist an der Zeit, die sehr häufig verwendete Switch-Steueranweisung zu besprechen. Sie wird in Situationen verwendet, in denen wir ausgehend von einer Variablen mehrere verschiedene Aktionen durchführen, je nach dem Wert, den wir überprüft haben.

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) {
				
			
Als nächstes öffnen wir die geschweiften Klammern. In diese Klammern können wir eine beliebige Anzahl von Bedingungen schreiben, die nacheinander überprüft werden sollen. Dazu schreiben wir das Wort case, und nach dem Leerzeichen fügen wir den Wert ein, den die zu prüfende Variable haben muss. Das Ganze endet mit einem Doppelpunkt „:“.

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
}
				
			
Ein kleiner Hinweis: Die Anweisung zur Steuerung von Switches funktioniert nur auf der Grundlage des Vergleichs von Zahlen. Daher müssen in diesem Beispiel die Buchstaben, die wir kontrollieren: d, h, o, b nicht als Buchstaben, sondern als ASCII-Codes behandelt werden. Schreibt man die Buchstaben in einzelne Apostrophe neben dem Großbuchstaben, so werden sie genau wie ASCII-Codes behandelt.

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

Gehe zurück zur Hausaufgabe 2.4, die im dritten Teil des Arduino-Kurses zu finden ist, und führe sie diesmal mit der Switch-Anweisung aus.

Servomechanismus in der Praxis - Lichtanzeige

Es ist Zeit für den versprochenen Einsatz des Servos in der Praxis. Derzeit werden immer mehr Informationen digital, d.h. auf einem Display, dargestellt.

Einige Werte wie Temperatur, Lichtintensität usw. lassen sich jedoch besser auf traditionellen Analoganzeigen darstellen. Das heißt, solche mit einem Zeiger:
Analoge Anzeigen.
Deshalb werden wir jetzt einen analogen Lichtmesser mit einem Mikroservo bauen. Der Zeiger auf einem Arm wird die Lichtmenge anzeigen, die auf den Sensor fällt. Dazu benötigen wir einen Arduino mit einem Fotowiderstand, der in einer Spannungsteilerschaltung angeschlossen ist, und einen Servo.

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!

Montageplan
Der zweite Teil des Schemas besteht darin, den Widerstand und den Fotowiderstand zu einem Spannungsteiler zu verbinden. Genauere Informationen dazu findet man im Teil über den ADC im Arduino.

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!

Analoges Zifferblatt ohne Zeiger.
Rückansicht.

Die Verarbeitungsqualität ist nicht die beste, aber das sind nur Experimente, und es ist der Effekt, der zählt:

Fertige analoge Anzeige.

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 <Servo.h> //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
}
				
			
Die neue Funktion abs() bedarf vielleicht einer Erklärung. Sie ist in Situationen wie der obigen sehr nützlich, weil sie einen absoluten Wert zurückgibt. Das heißt, ob wir eine kleinere Zahl von einer größeren subtrahieren oder umgekehrt, wir erhalten ein positives Ergebnis.

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:

Analoge Anzeige unter Verwendung des Arduino.

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

Der zusätzliche, ergänzende Teil ist ziemlich lang geworden. Ich hoffe jedoch, dass er hilfreich sein wird. Leider muss ich wieder einmal feststellen, dass ich zu viel geschrieben habe. Aus diesem Grund wurde ein Teil des Materials in den nächsten Teil verschoben. Konkret geht es um die Steuerung von Gleichstrommotoren. Dieses Thema ist zu wichtig, um es kurz zu behandeln.

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!
Bestellen Sie ein Set mit Elementen und beginnen Sie mit dem Lernen in der Praxis! Hier gehts zum Shop >>

Die nächsten Teile des Kurses sind eine Demonstration der Verwendung des Textdisplays, das dem Set beiliegt.

Nach oben scrollen