In diesem Teil des Arduino-Kurses werden wir den HC-SR04 Entfernungssensor verwenden, der unter Heimwerkern sehr beliebt ist.
Es ist auch an der Zeit, mit dem Schreiben unserer eigenen Funktionen zu beginnen. Schließlich werden wir uns auch mit einem sehr einfachen akustischen Element beschäftigen – dem Summer.
Bestellen Sie ein Set mit Elementen und beginnen Sie mit dem Lernen in der Praxis! Hier gehts zum Shop >>
Ich hoffe, dass dieser Abschnitt für Roboterbauer und alle, die einen Abstandssensor wie den unten abgebildeten in ihren Projekten verwenden möchten, von Interesse sein wird.
Zuvor werden wir jedoch neue Programmiertricks erlernen, die unsere Programme noch besser machen – sowohl für den Arduino als auch für die Person, die für die Erstellung des Programms verantwortlich ist, d.h. für dich!
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
Eigene Funktionen ohne Argumente
Bis jetzt haben wir unseren Code in die Funktionen setup() {} oder loop() {} eingefügt. Die erste Funktion diente der Einrichtung und die zweite war eine unendliche Hauptschleife, die ständig ausgeführt wurde. Um zum Beispiel die an Pin 13 angeschlossene LED zum Blinken zu bringen, mussten wir ein Programm wie dieses schreiben:
Denke daran, dass im Arduino eine Diode fest eingebaut ist, die an Pin 13 angeschlossen ist!
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
}
void loop() {
digitalWrite(13, HIGH); //Diode einschalten
delay(1000); //1 Sekunde warten
digitalWrite(13, LOW); //Diode ausschalten
delay(1000); //1 Sekunde warten
}
Stellt euch eine Situation vor, in der unser Programm sehr komplex ist und wir das Blinken als Bestätigung ausgewählter Operationen verwenden wollen. Es wird schnell klar, dass das wiederholte Duplizieren des Abschnitts, der für das Ein- und Ausschalten der LED verantwortlich ist, für den Programmierer zeitraubend ist. Schlimmer noch, es erschwert auch die Analyse des gesamten Programms.
Wenn wir eine Variable deklarieren, können wir auf einfache Weise mehrfach auf sie verweisen. Wenn wir ganze Zeichenketten von Operationen unter einem einfachen Namen speichern könnten, wären die Programme viel lesbarer. Auch mögliche Änderungen wären einfacher. An dieser Stelle können Funktionen helfen.
Bei der Programmierung des Arduino verwenden wir viele vorgefertigte Funktionen, z.B.: digitalWrite(13, HIGH);. Wir wissen, dass dieser Befehl einen High-Zustand (logisch 1) an Pin 13 setzt, aber wir müssen nicht wissen, wie er umgesetzt wird. In Analogie dazu können wir unsere eigenen Funktionen erstellen. Betrachten wir zum Beispiel das bereits erwähnte Blinken.
Der folgende Codeschnipsel ist für diesen Vorgang zuständig:
digitalWrite(13, HIGH); //Diode einschalten
delay(1000); //1 Sekunde warten
digitalWrite(13, LOW); //Diode ausschalten
delay(1000); //1 Sekunde warten
Wir können ihn aus loop() {} herausziehen und zu einer eigenen Funktion machen. Wie machen wir das?
Zunächst müssen wir den Namen der Funktion und ihren Typ deklarieren. Wir tun dies zum Beispiel: unter der Funktion loop() {}:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
}
void loop() {
}
void blinkenLED() {
//Inhalt der Funktion
}
Wie man sieht, steht vor dem Funktionsnamen ein Funktionstyp, der in diesem Fall blinkenLED ist. Dies ist nichts anderes als ein Hinweis darauf, ob die Funktion nach Abschluss einen Wert zurückgibt. Wenn dies der Fall wäre, müsste dort der entsprechende Typ eingetragen werden (entsprechend den Variablentypen). Das kann z.B. sein: Ganzzahl (int), Zeichen (char), Nicht-Ganzzahl (float), usw.
In diesem Fall steht dort das Wort void. (engl. leere) Dieser Typ bedeutet, dass die Funktion keinen Wert zurückgibt. Warum habe ich diesen Typ gewählt? Die Funktion hat die Aufgabe, eine Diode blinken zu lassen; dieser Vorgang führt zu keinem Ergebnis (außer dem, das für unser Auge sichtbar ist).
Wenn die Funktion kein Ergebnis liefert,
dann stellen Sie ihr das Wort void voran!
(Andere Fälle werden später in diesem Artikel behandelt)
Hinter dem Funktionsnamen erscheint eine runde Klammer. Für den Moment nehmen wir an, dass sich innerhalb dieser Klammer nichts befindet. Im weiteren Verlauf öffnen wir die geschweifte Klammer und platzieren darin den Code, der ausgeführt werden soll, wenn die Funktion aufgerufen wird. In der Praxis:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
}
void loop() {
blinkenLED();
}
void blinkenLED() {
digitalWrite(13, HIGH); //Diode einschalten
delay(500); //0,5 Sekunden warten
digitalWrite(13, LOW); //Diode ausschalten
delay(500); //0,5 Sekunden warten
}
Beachte, dass wir in der Schleife loop() {} den Namen unserer Variablen eingegeben haben (bereits ohne das Präfix void). Eine solche Aktion wird als Funktionsaufruf bezeichnet. Lade das Programm hoch und überprüfe, ob es wie in der Animation unten funktioniert:
Merke:
Funktionsnamen sollten erklären, was die Funktion tut und müssen eindeutig sein! Sie dürfen sich z.B. nicht mit Variablennamen überschneiden.
Kehren wir zu der zuvor angenommenen Situation zurück, in der du das Blinken an zahlreichen, sehr unterschiedlichen Stellen im Programm verwendest (um einen Vorgang zu bestätigen). Was wäre, wenn man plötzlich die Reihenfolge der Lichter oder z.B. die Anzahl der Blinksignale ändern wollte? Dann müsste man denselben Code an Dutzenden von Stellen ändern. Wenn du deine eigene Funktion verwendest, ist es ganz einfach, du musst nur an dieser einen Stelle etwas ändern:
//Schnelleres Blinken
void blinkenLED() {
digitalWrite(13, HIGH); //Diode einschalten
delay(200); //0,2 Sekunden warten
digitalWrite(13, LOW); //Diode ausschalten
delay(200); //0,2 Sekunden warten
}
Hausaufgabe 9.1
Schreibe eine Funktion, die die Diode pulsieren lässt (allmähliches Dimmen und Aufhellen). Verwende dabei natürlich die Kenntnisse, die du in der Lektion über PWM-Signale erworben hast.
Arduino - eigene Funktionen mit Argumenten
Es ist an der Zeit, weitere Funktionsgeheimnisse zu entdecken. Erinnerst du dich an eine der am häufigsten aufgerufenen Arduino-Funktionen? Wahrscheinlich geht es darum, den Zustand eines bestimmten Pins zu ändern, zum Beispiel:
digitalWrite(13, HIGH);
Im Gegensatz zu unserer blinkenLED(); Funktion geben wir hier einige Daten in runden Klammern ein. Das kann die Pin-Nummer sein, der gewünschte Zustand oder die Füllung des PWM-Signals. Diese Informationen werden später innerhalb der Funktion verwendet, um bestimmte Operationen durchzuführen.
Dies sind die Argumente, die an die Funktion übergeben werden.
Achte auf das verwendete Vokabular, sowohl Argumente als auch die Übergabe sind nicht zufällig verwendet worden. Diese Begriffe werden von allen Programmierern verwendet! Nun ist es an der Zeit, unsere eigene Funktion zu schreiben, die mit einem einzigen Argument beginnt.
Nehmen wir an, wir wollen unsere blinkenLED-Funktion so bearbeiten, dass es möglich ist, die Blinkgeschwindigkeit zu ändern, wenn sie aufgerufen wird. In unserer Funktionsdeklaration müssen wir die Information hinzufügen, dass sie ein Argument annehmen kann. Das tun wir im Funktions-Header:
//Ursprüngliche Version
void blinkenLED(){
//Neue Version
void blinkenLED(int Zeit){
Von nun an weiß der Compiler, dass er eine Zahl (typ int) erwartet, wenn er auf einen Funktionsaufruf in runden Klammern trifft. Diese Zahl wird an die Funktion übergeben und erscheint dort unter dem Namen Zeit.
Dies wird eine Variable sein, die nur innerhalb unserer Funktion sichtbar ist, d.h. es wird eine sogenannte lokale Variable sein.
In der Praxis wird das Innere der Funktion wie folgt aussehen:
void blinkenLED(int Zeit){
digitalWrite(13, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(13, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
Es ist Zeit für das letzte Element, d. h. den neuen Funktionsaufruf. Hier wird es wahrscheinlich nichts Überraschendes geben. Wir geben einfach eine Zahl in Klammern ein, die die Zeit angibt, in der die LED ein- und ausgeschaltet sein soll. Der gesamte Code sieht wie folgt aus:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
}
void loop() {
blinkenLED(50);
}
void blinkenLED(int Zeit){
digitalWrite(13, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(13, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
Prüfe, ob du durch Ersetzen des Wertes in den Klammern die Blinkfrequenz beeinflussen kannst. Zeit für ein weiteres Beispiel.
Funktionen mit einem Argument - Beispiel 2
Bis jetzt war das ständige Blinken der LED sichtbar, weil wir unsere Funktion in der Loop-Funktion aufgerufen haben, die eine Schleife ist. Wenn wir den Funktionsaufruf in den Setup-Abschnitt verschieben, werden wir nur ein Blinken sehen, wenn das System startet. Prüfe:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
blinkenLED(50);
}
void loop() {
}
void blinkenLED(int Zeit){
digitalWrite(13, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(13, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
Genau so würde es funktionieren, wenn wir die Funktion z. B. zur Bestätigung eines Tastendrucks verwenden wollten. Ein Aufruf von blinkenLED() würde ein einmaliges Blinken verursachen.
Es ist an der Zeit, die Funktion so zu überarbeiten, dass man nicht nur die Blinkzeit, sondern auch die Anzahl der Blinksignale beeinflussen kann. Dazu müssen wir zunächst die Funktionsdeklaration ändern:
//Ursprüngliche Version
void blinkenLED(int Zeit){
//Überarbeitete Version
void blinkenLED(int Zeit, int wieviel){
Wir fügten das Argument wieviel hinzu, es wird für die Anzahl der Blinksignale verantwortlich sein. Wie man sieht, wurde es nach dem Komma eingegeben, natürlich mit vorangestelltem Typ („Ganzzahl“, int). Dieser Parameter könnte auch von einem anderen Typ sein, das macht keinen Unterschied. Wichtig ist nur, dass die Deklaration richtig ist.
Eine Funktion kann mehrere Argumente unterschiedlichen Typs annehmen!
Wie du dir wahrscheinlich denken kannst, ist die Verwendung des zweiten Arguments sehr einfach. Wir haben jetzt einfach eine zweite lokale Variable wieviel zur Verfügung.
Hoffentlich weißt du schon, mit welcher Schleife du die Anzahl der Blinksignale am einfachsten steuern kannst? Falls nicht, verweise ich auf den vorherigen Teil, in dem ich for-Schleifen besprochen habe. Ich gehe jedoch davon aus, dass du den Stoff beherrschst (oder dabei bist, ihn nachzuholen) und stelle dir die neue Funktion blinkenLED vor:
void blinkenLED(int wieviel, int Zeit){
for (int i=0; i < wieviel; i++) {
digitalWrite(13, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(13, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
}
Überprüfen wir den gesamten Code:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 8 als Ausgang
blinkenLED(100, 5);
}
void loop() {
}
void blinkenLED(int Zeit, int wieviel){
for (int i=0; i < wieviel; i++) {
digitalWrite(13, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(13, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
}
Alles sollte funktionieren. Jetzt, nach dem Start, muss die Diode genau 5 Mal blinken. Aber woher weiß der Arduino, wie er zwischen den Angaben in Klammern, der Zeit und der Anzahl der Wiederholungen unterscheiden kann?
Nun, das Prinzip ist einfach: Aufeinanderfolgende Werte, die durch Kommas getrennt sind, werden aufeinanderfolgenden Argumenten zugeordnet, die in der Funktionsdeklaration beschrieben sind. Im obigen Fall wird die erste Zahl immer als die Zeit und die zweite als die Anzahl der Wiederholungen behandelt.
Hausaufgabe 9.2
Schreibe eine Funktion, mit der du die Anzahl der Blinksignale, die Zeit, in der die LED aus ist, und die Zeit, in der die LED an ist, separat einstellen kannst.
Kann unsere Funktion noch weiter ausgebaut werden? Ja, natürlich! Wäre es nicht praktisch, wenn man außer der Zeit und der Anzahl der Wiederholungen auch den Pin angeben könnte, an dem die Diode angeschlossen ist?
Funktionen mit Argumenten - Beispiel Nr. 3
Der Pin, an den die Diode angeschlossen ist, ist in unserem Programm eine Nummer. Wir verwenden sie an zwei Stellen, wenn wir einen bestimmten Pin als Ausgang deklarieren und wenn wir einen hohen oder niedrigen Zustand einstellen. Das heißt, die Zahl kann ein weiteres Argument für unsere Funktion sein!
Zuerst schließen wir die zweite Diode an einen anderen Pin an, z.B.: Nr. 8:
Ich werde hier nicht weiter darauf eingehen, sondern gleich das ganze Beispiel vorstellen:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
blinkenLED(100, 5, 13);
blinkenLED(150, 4, 8);
}
void loop() {
}
void blinkenLED(int Zeit, int wieviel, int pin){
for (int i=0; i < wieviel; i++) {
digitalWrite(pin, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(pin, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
}
Mal sehen, ob es funktioniert? Nicht sehr, oder? Nur die Diode, die an Pin 13 angeschlossen ist, blinkt, und die Diode unter Pin 8 will kein Lebenszeichen von sich geben. Und warum? Nun, wir haben einen ganz einfachen Fehler gemacht, den man beim Schreiben solcher Funktionen leicht machen kann. Wir haben nirgendwo vorgesehen, dass die anderen Pins (außer 13) Ausgänge sein könnten. Siehe dazu den Abschnitt setup:
void setup() {
pinMode(13, OUTPUT); //Konfiguration von Pin 13 als Ausgang
blinkenLED(100, 5, 13);
blinkenLED(150, 4, 8);
}
Wir können dies relativ einfach beheben, indem wir die Pin-Konfiguration in unsere Funktion verschieben. Dies würde wie folgt aussehen:
void setup() {
blinkenLED(100, 5, 13);
blinkenLED(150, 4, 8);
}
void loop() {
}
void blinkenLED(int Zeit, int wieviel, int pin){
pinMode(pin, OUTPUT); //Konfiguration als Ausgang
for (int i=0; i < wieviel; i++) {
digitalWrite(pin, HIGH); //Diode einschalten
delay(Zeit); //Einige Zeit warten
digitalWrite(pin, LOW); //Diode ausschalten
delay(Zeit); //Einige Zeit warten
}
}
Jetzt funktioniert alles, aber ich muss zugeben, dass es keine elegante Lösung ist. Daher würde ich davon abraten, sie in professionelleren Projekten zu verwenden. Im Hobbybereich sollte jedoch nichts Schlimmes passieren und wir können uns mit einer solch einfachen Funktion das Leben erleichtern.
Bevor wir uns dem Entfernungssensor zuwenden, ist es Zeit für eine letzte Information zu den Funktionen.
Funktionen, die ein Ergebnis liefern
Bisher haben wir nur Funktionen verwendet, die bestimmte Operationen ausführen, aber kein Ergebnis zurückgeben mussten. Die Aufgabe der neuen Funktion wird es sein… die Fläche eines Quadrats mit einer bestimmten Seite zu berechnen und das Ergebnis (die berechnete Fläche) zurückzugeben. Zunächst muss die Funktion deklariert werden, diesmal sieht die Kopfzeile wie folgt aus:
int QuadratFläche(int a) {
Wie man sieht, steht zusätzlich zum Argument (die Seite des Quadrats) ein int vor der Funktion, was bedeutet, dass sie ein Ergebnis in Form einer Zahl zurückgibt. In diesem Fall ist es natürlich die berechnete Fläche.
Das Innere der Funktion sollte dagegen wie folgt aussehen:
int QuadratFläche(int a) {
int Ergebnis = 0;
Ergebnis = a * a;
return Ergebnis;
}
Ich habe hier die Zeile mit dem Wort return hervorgehoben. Dahinter setzen wir den Wert (Variable), der als Ergebnis dieser Funktion zurückgegeben wird. Das heißt, für das Argument 4 sollten wir als Ergebnis der Funktion 16 erhalten, denn 4*4 = 16. Du fragst dich wahrscheinlich, was es bedeutet, dass die Funktion ein Ergebnis zurückgibt. Wohin geht dieser Wert?
Schauen wir uns das folgende Beispiel an:
void setup() {
Serial.begin(9600);
}
void loop() {
int Ergebnis = QuadratFläche(4);
Serial.println(Ergebnis); //Meldung anzeigen
delay(500);
}
int QuadratFläche(int a) {
int Ergebnis = 0;
Ergebnis = a * a;
return Ergebnis;
}
Führe das Beispiel aus. Der Monitor der seriellen Schnittstelle sollte das Ergebnis der Operation anzeigen, d.h. 16. Die Tatsache, dass die Funktion ein Ergebnis zurückgibt, bedeutet, dass das Ergebnis nach der Ausführung an der Stelle des Aufrufs „ersetzt“ wird. In diesem Fall wird anstelle von QuadratFläche(4); 16 ersetzt, was in die Ergebnisvariable geschrieben wird.
Die Deklaration einer separaten Variablen ist jedoch unnötig. Die folgende Notation funktioniert genauso gut (ist aber etwas weniger gut lesbar):
//Alte Version
int Ergebnis = QuadratFläche(4);
Serial.println(Ergebnis); //Meldung anzeigen
//Neue Version
Serial.println(QuadratFläche(4)); //Meldung anzeigen
Das Einzige, was noch fehlt, um es vollständig zu machen, ist, dass die Seite des Quadrats vom Computer an den Arduino gesendet werden kann. Hier werden wir die Meldungen aus dem Abschnitt des Kurses über die Kommunikation via UART verwenden. Das Programm wird wie folgt aussehen:
String empfangeneDaten = ""; //Leere Zeichenfolge der empfangenen Daten
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available() > 0) { //Hatt der Arduino Daten empfangen
empfangeneDaten = Serial.readStringUntil('\n'); //Wenn ja, bis zum Zeilenende lesen und in der Variablen empfangeneDaten speichern empfangeneDaten
int Ergebnis = QuadratFläche(empfangeneDaten.toInt());
Serial.println(Ergebnis); //Meldung anzeigen
}
}
int QuadratFläche(int a) {
int Ergebnis = 0;
Ergebnis = a * a;
return Ergebnis;
}
Neu ist jedoch die Notation empfangeneDaten.toInt(). Wie ihr euch vielleicht erinnert, werden bei der Kommunikation über den UART alle Zeichen als ASCII-Codes gesendet. Daher konnten wir während unserer Tests nicht einfach Zahlen an den Arduino senden.
Dies kann jedoch sehr leicht geändert werden. Es genügt, .toInt() an den gelesenen String anzuhängen. Damit wird die als Text gesendete Zahl in eine Zahl vom Typ int umgewandelt.
Die Änderung des Typs einer Variablen in eine andere wird als Abwandlung bezeichnet.
Hausaufgabe 9.1
Schreibe Funktionen, die die Flächen eines Rechtecks, eines Kreises und eines Dreiecks berechnen. Sende die Ergebnisse über den UART an den PC!
Ultraschall-Abstandssensor HC-SR04
Es ist an der Zeit, den Abstandssensor zu beschreiben, auf den viele Menschen gewartet haben. Der HC-SR04-Sensor besteht aus einem Ultraschall-Sender und -Empfänger und mehreren integrierten Schaltkreisen. Dank dieser Schaltkreise können wir durch Abhören des (für den Menschen unhörbaren) akustischen Signals den Abstand des Sensors zu einem Hindernis ziemlich genau bestimmen. Genau wie … Fledermäuse:
Aber kommen wir zurück zum Arduino und dem Abstandssensor. Konzentrieren wir uns zunächst auf seine vier Signalpins. Zwei davon sind für die Stromversorgung des Chips (Vcc, GND) und die anderen beiden (Trigger und Echo) sind für die Messungen.
Trigger ist der Auslöser-Eingang. Wenn an ihm ein hoher Zustand anliegt (für mindestens 10 Mikrosekunden), beginnt die Abstandsmessung. Der Echo-Ausgang hingegen gibt die gemessene Entfernung aus. Die maximale Reichweite dieser unauffälligen Schaltung wird vom Hersteller mit 4 m angegeben.
Es ist Zeit für ein erstes, einfaches Beispiel, wie der Sensor in der Praxis eingesetzt werden kann.
Entfernungsmesser
Schließe die von dir erstellte Schaltung an, die in regelmäßigen Abständen Entfernungsmessungen vornimmt und diese auf dem Computerbildschirm anzeigt. Dazu müssen wir die Schaltung gemäß dem untenstehenden Schema zusammenbauen:
Die Beschreibung der Leitungen befindet sich zwar auf dem Sensor, aber der Form halber gebe ich sie hier wieder. Von oben (wie im Diagramm oben) und von links betrachtet:
- Vcc – 5V
- Trig
- Echo
- Gnd
Nachstehend findet sich ein Foto der zusammengebauten Schaltung.
In unserem Fall verbinden wir den Trig-eingang mit Pin 12 und das Echo mit Pin 11. Damit wir nicht durcheinander kommen, schlage ich vor, gleich die entsprechenden Definitionen vorzunehmen:
#define trigPin 12
#define echoPin 11
Du weißt mehr oder weniger, wie man eine Distanzmessung auslöst. Es ist an der Zeit, es als Programm aufzuschreiben:
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Pin, an den wir Trig als Ausgang anschließen
pinMode(echoPin, INPUT); //und das Echo als Eingang
}
void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
//Messung durchgeführt
}
Man beachte die neue Funktion delayMicroseconds();, die nichts anderes ist als das Äquivalent der bekannten Funktion delay();. Der Unterschied ist wahrscheinlich offensichtlich. Die neue Funktion zählt die Zeit in Mikrosekunden herunter. Ich möchte nur daran erinnern, dass die bisher verwendete delay-Funktion Millisekunden misst.
Die Sequenz, mit der die Messung beginnt, ist sehr einfach. Zuerst setzen wir ein Low-Signal an den Pin, der mit dem Trigger-Eingang des Sensors verbunden ist. 2 Mikrosekunden sind ausreichend, dann setzen wir den High-Zustand auf 10 Mikrosekunden. Der Sensor führt eine Messung durch und gibt die Ergebnisse an den Echo-Pin zurück.
Die Frage ist, wie man es auslesen kann? Ist dafür eine UART- oder eine andere Kommunikationsschnittstelle erforderlich? Nein, glücklicherweise ist dieser Sensor sehr einfach und die gemessene Entfernung wird durch einen Impuls (High-Zustand) am Echo-Pin dargestellt. Seine Länge ist proportional zur Entfernung. Je länger er ist, desto größer ist die gemessene Entfernung. Hier kommt eine neue Funktion des Arduino zum Einsatz.
Messung der Impulsdauer im Arduino
Glücklicherweise wurde im Arduino eine sehr einfache Funktion implementiert, die die Dauer eines Impulses an einem beliebigen Eingang messen kann. Seine Länge muss zwischen 10 Mikrosekunden und 3 Minuten liegen. Die Messung beginnt, wenn eine Zustandsänderung an einem Pin erkannt wird.
Der Aufbau ist sehr einfach:
int Ergebnis = 0;
Ergebnis = pulseIn(11, HIGH);
Die Funktion benötigt in ihrer einfachsten Form nur zwei Argumente. Die zu prüfende Pin-Nummer und der zu messende Logikpegel (low/high).
Das heißt, um den Abstand zu messen, müssen wir die folgende Operation durchführen:
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Pin, an den wir Trig als Ausgang anschließen
pinMode(echoPin, INPUT); //und das Echo als Eingang
}
void loop() {
long Zeit;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Zeit = pulseIn(echoPin, HIGH);
Serial.print(Zeit);
delay(500);
}
Wenn ein solches Programm ausgeführt wird, erscheinen Zahlen auf dem Bildschirm. Je näher wir das Hindernis am Sensor platzieren, desto kleiner werden sie. Sie sind jedoch weit entfernt von den Einheiten, die wir verwenden. Um die Messung für Menschen lesbar zu machen, müssen wir das Ergebnis durch die „magische Zahl“ dividieren.
Schau dir an, wie das Programm unten funktioniert – jetzt sollte das Ergebnis in Zentimetern angezeigt werden:
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Pin, an den wir Trig als Ausgang anschließen
pinMode(echoPin, INPUT); //und das Echo als Eingang
}
void loop() {
long Zeit, Entfernung;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Zeit = pulseIn(echoPin, HIGH);
Entfernung = Zeit / 58;
Serial.print(Entfernung);
Serial.println(" cm");
delay(500);
}
Der Wert, durch den wir teilen (58), ist natürlich nicht „magisch“. Er ergibt sich aus der Zeit, die der Schall benötigt, um eine Strecke von 1 cm zurückzulegen, und der vom Hersteller angegebenen Entfernungsformel.
In diesem Moment ist es nicht notwendig, darauf einzugehen.
Wenn sich die Notwendigkeit ergibt, werde ich das Thema im Detail beschreiben.
Funktion, die den Sensorabstand in cm zurückgibt
Wie man sieht, ist der Teil des Programms, mit dem man die Messung durchführen kann, relativ lang. Es wäre sehr praktisch, wenn wir eine Funktion hätten, die das alles erledigt. Schreiben wir sie. Du brauchst nicht zu warten, ich präsentiere dir das fertige Fragment, denn es ist ja nur eine Kopie des obigen Codes:
int EntfernungMessen() {
long Zeit, Abstand;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Zeit = pulseIn(echoPin, HIGH);
Abstand = Zeit / 58;
return Abstand;
}
Nun genügt es, die Funktion an der entsprechenden Stelle im Programm aufzurufen:
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Pin, an den wir Trig als Ausgang anschließen
pinMode(echoPin, INPUT); //und das Echo als Eingang
}
void loop() {
Serial.print(EntfernungMessen());
Serial.println(" cm");
delay(500);
}
int EntfernungMessen() {
long Zeit, Abstand ;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Zeit = pulseIn(echoPin, HIGH);
Abstand = Zeit / 58;
return Abstand ;
}
Das ist doch viel klarer, oder?
Überprüfung des Entfernungsbereichs
Es ist Zeit für die letzte Übung in diesem Teil des Arduino-Kurses. Diesmal wollen wir einen Summer, ein Bauteil, das einen Ton erzeugt, in unsere Schaltung einbauen. Er sieht so aus:
Dieses Bauteil hat zwei Eingänge. Wenn Vcc an einen Eingang angelegt wird und der andere mit Masse verbunden ist,… Der Summer wird einen lauten Ton erzeugen. Er ist nicht sehr angenehm, deshalb schlage ich vor, den Aufkleber darauf zu lassen (dadurch wird der erzeugte Ton leiser). Wie man an der Aufschrift unschwer erkennen kann, schützt dieser Aufkleber das Innere des Summers beim Waschen der Platinen. Dies geschieht hauptsächlich, um Lötstellen zu entfernen.
Der Summer ist ein polares Bauteil!
Beachte, dass eines der Beine länger ist, und auf dem Gehäuse,
findet sich daneben das Plus-Symbol.
Um den Summer zu verwenden, schließen wir ihn an den Arduino an, wie in der folgenden Abbildung dargestellt.
Das Symbol, mit dem ich das Aufbauschema gezeichnet habe, weicht ein wenig vom Aussehen des Summers ab, so dass man sich zusätzlich mit einem Foto meines Layouts vergewissern kann:
Der Summer, der den Forbot-Bausätzen beiliegt, ist so dimensioniert, dass er ohne Widerstand in den Arduino gesteckt werden kann. Wenn du deinen Summer verwendest, prüfe, wie viel Strom er zieht!
Diesmal werden wir eine Funktion schreiben, die prüft, ob sich ein Objekt in einer bestimmten Entfernung vom Sensor befindet. Ist dies der Fall, ertönt ein Alarm („Summer“).
Die Funktion, die den Abstand prüft, verwendet die vorherige Funktion EntfernungMessen(),
void Bereich(int a, int b) {
int wieWeit = EntfernungMessen();
if ((wieWeit > a) && (wieWeit < b)) {
digitalWrite(2, HIGH); //Summer einschalten
} else {
digitalWrite(2, LOW); //Summer ausschalten, wenn Objekt außerhalb des Bereichs
}
}
Wir geben zwei ganze Zahlen als Argumente für die Funktion an. Wir prüfen dann, ob der gemessene Abstand größer als der Anfangswert unseres Intervalls (a) und kleiner als der Maximalwert (b) ist.
Du kannst übrigens sehen, dass wir mit den &&-Symbolen mehrere Bedingungen zu einer einzigen zusammenfassen.
Das gesamte Programm sieht wie folgt aus:
#define trigPin 12
#define echoPin 11
void setup() {
Serial.begin (9600);
pinMode(trigPin, OUTPUT); //Pin, an den wir Trig als Ausgang anschließen
pinMode(echoPin, INPUT); //und das Echo als Eingang
pinMode(2, OUTPUT); //Ausgang für Summer
}
void loop() {
Bereich(10, 25); //Der Alarm ertönt, wenn sich ein Hindernis zwischen 10 und 25 cm vom Sensor befindet.
delay(100);
}
int EntfernungMessen() {
long Zeit, Entfernung;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
Zeit = pulseIn(echoPin, HIGH);
Entfernung = Zeit / 58;
return Entfernung;
}
void Bereich(int a, int b) {
int wieWeit = EntfernungMessen();
if ((wieWeit > a) && (wieWeit < b)) {
digitalWrite(2, HIGH); //Summer einschalten
} else {
digitalWrite(2, LOW); //Summer ausschalten, wenn Objekt außerhalb der Reichweite ist
}
}
Schau dir an, wie das Programm in der Praxis funktioniert. Meiner Meinung nach ist dies die perfekte Grundlage für den Aufbau einer einfachen Alarmzentrale, um Ihre kostbaren Schätze zu bewachen!
Hausaufgabe 9.3
Schreibe ein Programm, das die Entfernung des Hindernisses vom Sensor auf einem Lineal aus LEDs darstellt. Je näher das Hindernis am Sensor ist, desto mehr LEDs sollten aufleuchten. Ein Beispiellineal könnte wie folgt aufgebaut sein:
Hausaufgabe 9.4
Überprüfe, wie sich die Ablesung des Sensors in Abhängigkeit vom Material des Hindernisses verändert. Siehst du Unterschiede in der Entfernungsanzeige, wenn der Sensor z. B. auf harte oder weiche Objekte gerichtet ist? Vergleiche Hindernisse wie eine Wand, eine Decke oder ein Stück Papier.
Zusammenfassung des Arduino-Kurses!
Ich weiß nicht, wie ich das passiert ist, aber du hast gerade alles gelernt, was im Kurs „Arduino für Anfänger“ behandelt wird! Ich hoffe, du hattest eine fruchtbare Lernerfahrung. Wenn du Interesse hast, verspreche ich, einen weiteren Teil hinzuzufügen und die Arbeit am Kurs für Fortgeschrittene fortzusetzen (weitere 9 neue Episoden).
Bestellen Sie ein Set mit Elementen und beginnen Sie mit dem Lernen in der Praxis! Hier gehts zum Shop >>