Diese Anleitung hilft dir, deinen Funbot aufzubauen und zu programmiern.
Inhaltsverzeichnis
Einleitung und didaktische Grundlagen
Kleine flitzende Roboter sind seit Jahren beliebte Projekte, wenn es um das Thema Arduino in der Schule oder ganz allgemein im Bildungsbereich geht. Mit Ihnen lassen sich einfache Zusammenhänge zwischen Programmfunktionen und den daraus resultierenden Bewegungsabläufen erkennen. Mit nur wenigen weiteren Modulen und Sensoren lassen sich die Roboter anschaulich automatisieren. Mit einem Ultraschall-Entfernungssensor kann der kleine Funbot Hindernisse erkennen und darauf reagieren, indem er sie umfährt. Ein Line-Tracking-Sensor macht es möglich, den Roboter auf einer Linie fahren zu lassen.
Roboter und Chassis bzw. Fahrgestelle gibt es schon sehr viele. Was ist beim Funbot anders?
Der Funbot ist als vollständiges Roboter-Kit verfügbar. Man muss nichts Weiteres beschaffen, um den Roboter arbeiten zu lassen. Die Komponenten sind bewusst so ausgewählt, dass die Programmierung mit der originalen Arduino-Software, aber auch auf Blockbasis visuell erfolgen kann. Zum Beispiel mit OpenRoberta, MakeCode oder S4A (Scratch for Arduino). Zudem handelt es sich um sehr typische Bauteile im Bereich der Mikroelektronik. Diese sind in vielen Anfänger-Lernsets enthalten und können leicht beschafft werden. So lässt sich der Roboter auch im Bildungsbereich nachhaltig einsetzen. Selbst wenn mal etwas kaputtgeht, lassen sich die einzelnen Komponenten günstig beschaffen.
Verwendungsmöglichkeiten
Auswahl des Mikrocontrollers
Es gibt ein breites Spektrum an kompatiblen Mikrocontrollern, die mit dem Funbot verwendet werden können.
Arduino UNO (und weitere Controller in kompatibler Bauform)
Arduino NANO (und weitere Controller in kompatibler Bauform)
ESP32 (und weitere Controller in kompatibler Bauform)
Raspberry Pi Pico (und weitere Controller in kompatibler Bauform)
Es muss nichts verkabelt oder gelötet werden. Nach dem Auspacken und Zusammenbauen kann man direkt mit der Programmierung beginnen.
Das Programmieren ist mit verschiedenen Programmiersprachen und Programmen möglich. Dazu zählen visuelle (blockbasierte) und schriftliche Programmierungen, wie Arduino, OpenRoberta oder Micropython
Differenzierungsmöglichkeiten
Der Funbot kann in der einfachsten Form ohne jegliche Anbauteile programmiert werden.
Grundbausteine
Die Bauteile in der Grundform sind folgende:
– Zwei Antriebsmotoren mit Rädern
– Zwei Taster
– Ein Lautsprecher
– Zwei Frontscheinwerfer LEDs
– Unterbodenbeleuchtung mit sechs WS2812 LEDs
Enthaltene Erweiterungen
– Linienverfolgung (Linetracking) – Das Linetrackingmodul wird an der Vorderseite des Funbot eingesteckt und kann dunkle Linien auf dem Fahrweg erkennen. Mit einer passenden Programmierung ist es möglich, den Roboter an einer Linie entlangfahren zu lassen.
– Gyroskop (Lagesensor) – Mit dem Gyroskopmodul MP6050 ist es möglich, die Lage des Roboter zu erkennen. Das ermöglicht es, den Roboter winkelgenau fahren zu lassen. Dazu gehört, dass er im 90° Winkel abbiegen kann, oder dass er gerade durch einen Raum fährt, ohne durch produktionsbedingte Leistungsunterschiede der beiden Motoren eine Kurve fährt.
– Bluetooth (Funkmodul) – Mit dem Bluetoothmodul ist der Funbot in der Lage, Daten an andere Geräte wie Tablets oder Smartphones zu senden und zu empfangen. So kann die App der Funduino GmbH als Fernbedienung verwendet werden. Noch besser ist es natürlich, wenn die Schüler selber eine App für den Funbot schreiben, beispielsweise mit Appinventor.
Individuelle Erweiterungen und KI Anwendungen
Der Funbot kann mit eigenen 3D-Anbauteilen, Antennen, Spoiler, Schneeschieber etc. erweitert werden. Auf der Internetseite der Funduino GmbH können die Basisdaten bereits als 3D-Datei heruntergeladen werden (STL-Datei der Grundplatte mit den Bohrungen). Zusammen mit ein paar Abstandshaltern lassen sich so Aufbauten und Träger für weitere Module herstellen. Auch die Erweiterung des Roboters mit KI-gestützten Kameras, wie der Pixycam oder der Huskylens sind so möglich.
Energieversorgung
Der Funbot kann über das USB-Kabel mit Strom versorgt werden. Alle Module lassen sich ohne externe Spannungsversorgung programmieren und in ihrer Funktion testen. Allerdings reicht die Spannung nur bedingt aus, um die beiden Antriebsräder mit ausreichender Leistung zu versorgen. Wenn der Funbot autark fahren soll, bietet es sich daher an, eine externe Spannungsquelle zu verwenden. Dazu ist auf dem Funbot eine DC-Buchse vorhanden, über den der Roboter mit einer Spannung zwischen 6V und 9V Gleichspannung versorgt werden kann.
Praxistipp: Es gibt aufladbare 9V Batterien, die mit Hilfe eines USB-Kabels wieder aufgeladen werden können. Das schont die Umwelt und man kann die Batterie sehr häufig wiederverwenden.
Auf dem Funbot befindet sich ein Spannungswandler, der aus der angeschlossenen externen Spannung grundsätzlich eine 5V Spannung erzeugt. Das ist sehr praktisch, denn dadurch können wir an dem Modul auch zeitgleich eine Spannungsquelle und ein USB-Kabel zum Computer anschließen.
Wenn der Roboter durch eine Batterie mit Strom versorgt wird, ist keine separate Spannungsquelle für den Mikrocontroller notwendig. Der im Funbot integrierte Spannungswandler stellt über die Schnittstelle zum Mikrocontroller auch die Spannung bereit.
Komponenten
Der Funbot ist die Weiterentwicklung eines fahrenden Roboters, der aus einzelnen Komponenten zusammengebaut, verkabelt und gelötet werden musste. Für jüngere Schüler ist das immer eine besondere Herausforderung. Im Rahmen von schulischem Unterricht ist der didaktische Baustein der Montage und Konstruktion in der Regel nur im MINT- oder Technikunterricht möglich. Daher ist der Funbot, insbesondere im Informatikunterricht ideal einsetzbar. Denn hier liegt der Schwerpunkt auf der Programmierung. Mit dem Funbot macht die ganze Sache dann auch noch eine Menge Spaß.
Folgende Module sind Bestandteil des Funbots und können über den Mikrocontroller programmiert werden.
Frontscheinwerfer - LED
Im vorderen Bereich des Funbot befinden sich zwei Leuchtdioden als Frontscheinwerfer. Diese LEDs werden zusammen über einen einzigen Kontakt am Mikrocontroller angesteuert. Daher werden die beiden LEDs immer gemeinsam ein- und ausgeschaltet.
Die beiden LEDs sind mit einem 200Ohm Vorwiderstand verkabelt, damit die LEDs auch im 5V-Betrieb nicht kaputtgehen.
Taster Links und Rechts
In der Vorderseite befinden sich außen zwei Taster. Der linke Taster ist mit dem Pin A2 und der rechte Taster mit dem Pin A3 des Mikrocontrollers verbunden. Beide verfügen über einen Pulldown Widerstand und sind mit 5V gekoppelt. Sobald die Taster gedrückt werden, wird ein 5V (HIGH) Signal an den Mikrocontroller gesendet. Durch den 10K Pulldown-Widerstand wird das Signal nach dem Loslassen des Tasters schnell wieder auf einen neutralen Zustand gebracht.
Lautsprecher
Auf der Platine des Funbot befindet sich mittig ein kleiner schwarzer Zylinder. Er ist mit dem Pin A0 des Mikrocontrollers verbunden. Es handelt sich um ein passives Bauteil, der Ton muss daher über den Mikrocontroller generiert werden. Bei der Verwendung von Arduino z.B. über den „Tone“ Befehl. Eine reine Ansteuerung mit 5V führt nicht zu einer Schallerzeugung.
Antriebsmotoren
Im hinteren Bereich des Roboters befinden sich an der Unterseite zwei N20 Motoren, die mit jeweils einem Rad verbunden sind. Die Motoren werden über einen L293D Motortreiber angesteuert und mit Energie versorgt.
Liniensensor / Line-Tracking-Sensor
Der Liniensensor wird vorne am Funbot Waagerecht in die dafür vorgesehenen Kontakte eingesteckt. Grundlegend ist die Funktion des Liniensensors einfach erklärt. Der Liniensensor gibt je nachdem, ob er eine dunkle Linie oder einen hellen Untergrund erkennt, eine Spannung aus, oder eben nicht. Die Spannung am Ausgangspin des Sensors wird vom Mikrocontroller des Funbot erkannt. Wenn der Mikrocontroller vom Abstandssensor das digitale Signal „1“ erfasst, entspricht das einem weißen Untergrund, und wenn das digitale Signal „0“ erfasst wird, handelt es sich um einen dunklen Untergrund. Der dunkle Untergrund ist dann die schwarze Line, die vorab in Form von schwarzem Klebeband auf den Tisch geklebt wurde.
Ultraschallsensor
Vorne befinden sich in der Mitte die „Augen“ des Roboters. Es handelt sich um einen RCW-0001 Ultraschallsensor. Der Sensor funktioniert so, wie der bekanntere HC-SR04 Sensor. Der Unterschied liegt jedoch darin, dass dieser Sensor mit 5V und mit 3,3V Spannung betrieben werden kann. Das macht den Funbot kompatibel mit so vielen unterschiedlichen Mikrocontrollern.
Unterbodenbeleuchtung mit WS2812B Neopixel LEDs
Auf der Unterseite des Roboters befinden sich sechs WS2812B LEDs. Diese sind der Reihe nach von LED 0 bis LED 6 mit dem Pin 13 des Mikrocontrollers verbunden und können jeweils individuell in tausenden von Farben angesteuert werden.
Lagesensor
Der Funbot ist mit dem MPU6050 ausgestattet. Dieser Sensor beinhaltet einen Beschleunigungs- und Lagesensor (Gyroskop). Der Sensor ist über die I2C Schnittstelle mit dem Mikrocontroller verbunden und kann daher große Datenmengen bzgl. der Position des Roboters liefern. Dadurch kann der Roboter winkelgenau bewegt werden.
Praxisaufgaben
Ab jetzt geht es erst so richtig los. In den folgenden Lektionen werden alle Module des Funbots beispielhaft mithilfe der Arduino IDE programmiert.
Frontscheinwerfer
Aufgabe 1: Die blinkende LED
Gemeinsam möchten wir uns gleich zu Beginn unserer Reise eines der wichtigsten Programme der Mikroelektronik ansehen: die blinkende Leuchtdiode.
Leuchtdioden kennst du aus deinem Alltag. Sie geben häufig Informationen über den Status von elektronischen Geräten an. Ein typisches Beispiel für eine Leuchtdiode in einem elektronischen Gerät ist die Stand-by-Leuchte von einem Bildschirm. Wenn wir die Stand-by-Leuchte sehen können, die sich häufig als roter Punkt darstellt, wissen wir, dass das Gerät z.B. über eine Fernbedienung wieder eingeschaltet werden kann.
Aufgabenstellung
Schalte die vorderen Leuchtdioden auf deinem Funbot ein. Pausiere eine Sekunde und schalte sie wieder aus. Nach einer weiteren Sekunde sollen die Leuchtdioden wieder aufleuchten.
Programmcode
Schritt 1
Im ersten Schritt erstellen wir zunächst eine Variable für die Leuchtdioden. Hierzu schreiben wir zuerst das Wort „int“ in den Programmcode.
„Int“ ist dabei die Kurzform für „integer“. Bei einem Integer handelt es sich um einen Datentyp. In diesem Fall um eine Ganzzahl (z.B. 1, 2, 5 oder auch 1337). Es erscheint dir vielleicht ungewöhnlich, aber beim Programmieren ist die Angabe des Datentyps für das Programm notwendig.
Nach der Festlegung des Datentyps folgt der Name für die Variable. Wir nennen unsere LED in dem Sketch einfach nur „modul_LED“. Wenn du möchtest, kannst du anstelle von „modul_LED“ auch „x“ oder „Lampe“ oder „Jessica“ schreiben. Wichtig ist nur, dass die Bezeichnung für dieses Modul im weiteren Programm immer exakt gleich bleibt!
Da unsere beiden LEDs auf dem Funbot an dem Pin A1 (Analog 1) mit dem Mikrocontroller verbunden sind, hinterlegen wir in der Variable den Wert „A1“.
int modul_LED = A1;
Schritt 2
Im zweiten Schritt ergänzen wir unseren Code um das Setup.
Im Setup legen wir fest, an welchem Pin die LEDs mit unserem Mikrocontroller verbunden sind.
Der erste Teil der Programmbefehls „PINBELEGUNG“ wird mit dem Namen unserer Variable „modul_LED“ aus Schritt 1 ersetzt. In dieser Variable ist der Wert „A1“ gespeichert.
pinMode(PINBELEGUNG, INPUT oder OUTPUT);
Schritt 4
Das Codebeispiel aus Schritt 3 ergänzen wir jetzt in unserem Setup.
Wenn du alles richtig gemacht hast, sieht dein Code jetzt so aus wie in der nachfolgenden Abbildung.
int modul_led= A1;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
Schritt 5
An dieser Stelle erinnern wir uns an unsere Aufgabenstellung zurück: Wir möchten die LEDs blinken lassen, also nacheinander ein- und wieder ausschalten. Bisher haben wir jedoch nur Vorbereitungen getroffen. Jetzt schreiben wir das eigentliche Programm. Der Bereich, in dem das geschieht, wird als „Loop“ bezeichnet.
int modul_led = A1;
void setup()
{
pinMode(modul_led_gruen, OUTPUT);
}
void loop()
{
(Hier zwischen den Klammern wird der Programmcode eingetragen)
}
Schritt 6
Der Befehl, den wir für das Ein- und Ausschalten benötigen, nennt sich „digitalWrite“. Mit diesem Befehl können wir Module an einem digitalen Pin ansteuern.
Die LEDs auf unserem Funbot befinden sich an Pin A1. Das „A“ steht dabei für „analog“, also „Analog 1“.
Es klingt natürlich etwas seltsam, dass wir mit einem digitalen Befehl, einen analogen Pin ansteuern. Dazu muss man wissen, dass die analogen Pins auch mit digitalen befehlen ansteuern kann. Die analogen Pins haben lediglich zusätzlich die Möglichkeit, auch analoge Werte zu erfassen, was bei digitalen Pins nicht möglich ist.
Auch dieser Befehl setzt sich aus zwei Informationen zusammen, die durch ein Komma voneinander getrennt sind.
Die erste Information legt fest, an welchem Pin das gewünschte Modul angeschlossen ist. Wir tragen hier also wieder unsere Variable „modul_LED“ ein.
digitalWrite(modul_lLED, An/Aus)
Schritt 7
Die zweite Information legt fest, welches Signal wir an den digitalen Pin senden möchten.
Wenn wir ein HIGH Signal senden, wird am Pin A1 des Mikrocontrollers eine Spannung von 5V ausgegeben. Damit schalten wir die LEDs ein.
Wenn wir ein LOW Signal senden, wird der Pin A1 auf eine Spannung von 0V geschaltet. Damit schalten wir die LEDs aus.
Da wir die LEDs zunächst einschalten möchten, hinterlegen wir hier zunächst den Wert „HIGH“. Anschließend fügen wir die Codezeile in den Loop-Teil unseres Codes ein.
int modul_LED = A1;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
void loop()
{
digitalWrite(modul_LED, HIGH);
}
Schritt 8
Nachdem wir die Leuchtdioden eingeschaltet haben, möchten wir die Leuchtdioden auch wieder ausschalten. Dies erzielen wir, indem wir die zweite Information aus dem „digitalWrite“ Befehl von HIGH auf LOW umschreiben.
Auch diese Codezeile fügen wir jetzt in unseren Loop-Teil ein.
digitalWrite(modul_LED, LOW)
Schritt 9
Wenn du die beiden Codezeilen richtig in deinen Programmcode eingefügt hast, sollte dieser jetzt so wie in der Abbildung aussehen.
Was passiert, wenn du den Code jetzt auf deinen Funbot überspielst?
int modul_LED = A1;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
void loop()
{
digitalWrite(modul_LED, HIGH);
digitalWrite(modul_LED, LOW);
}
Hmm… die LED leuchtet dauerhaft. Aber warum?
Der Mikrocontroller auf deinem Funbot verarbeitet den Code in einer rasanten Geschwindigkeit. Die Geschwindigkeit, in welcher der Code verarbeitet wird, nennt man auch Taktfrequenz.
Die Frequenz ist dabei so hoch, dass wir Menschen das Ein- und Ausschalten der Leuchtdioden mit dem Auge nicht wahrnehmen können. Für uns macht es den Anschein, als würden die Leuchtdioden durchgehend leuchten. Was müssen wir in unserem Code ergänzen, damit das Ein- und Ausschalten für uns Menschen sichtbar wird?
Schritt 11
Genau, wir müssen eine Pause zwischen dem Einschalten und dem Ausschalten ergänzen.
Die Pause gibt eine Zeit vor, in der das Programm stoppt. Diese Angabe erfolgt in Millisekunden (ms). Eine Sekunde entspricht dabei 1000 Millisekunden.
In einem Programmcode wird diese Pause auch „delay“ (engl. für Verzögerung) genannt. Die Codezeile sieht im Programm so aus:
delay(1000);
Schritt 12
Zwischen dem Einschalten und dem Ausschalten der Leuchtdiode fügen wir jetzt diese Pause ein.
Anschließend laden wir das Programm auf wieder auf unseren Funbot hoch.
Und, was kannst du beobachten?
int modul_LED = A1;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
void loop()
{
digitalWrite(modul_LED, HIGH);
delay(1000);
digitalWrite(modul_LED, LOW);
}
Schritt 13
Mist, die Leuchtdioden leuchten immer noch dauerhaft?
Aber woran liegt das?
In Schritt 9 haben wir gelernt, dass der Mikrocontroller den Programmcode in einer sehr hohen Frequenz ausliest. Und genau hier liegt wieder einmal das Problem. Der Mikrocontroller startet sofort nach dem Verarbeiten des Loop-Teils wieder von vorne. Demnach wird direkt nach dem Ausschalten der Leuchtdioden die Leuchtdioden wieder eingeschaltet. Und das in einer Frequenz, die für das menschliche Auge nicht sichtbar ist.
Schritt 14
Wir ergänzen unseren Programmcode also um eine weitere Pause, direkt nach dem Ausschalten der Leuchtdioden. Anschließend laden wir das Programm wieder auf unseren Funbot. Mit diesem letzten Schritt ist unser Sketch endlich fertig!
int modul_LED = A1;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
void loop()
{
digitalWrite(modul_LED, HIGH);
delay(1000);
digitalWrite(modul_LED, LOW);
delay(1000);
}
Gratulation, du hast deinen ersten Sketch geschrieben!
Nur eine Frage hätten wir da noch…
… was passiert eigentlich, wenn du die zweite Pause direkt an den Start des Loop-Teils setzt?
Taster Links und Rechts
Der Funbot verfügt über zwei Taster. Diese Taster können so programmiert werden, dass sie beliebige Funktionen annehmen können. Man kann zum Beispiel programmieren, dass beim Tastendruck die LEDs vorn aktiviert werden oder dass eine Melodie abgespielt wird. Man kann aber auch komplexe Bewegungsabläufe damit programmieren, wenn man das möchte.
Aufgabe 1: Tasteneingaben auswerten
Nachdem wir in der ersten Lektion bereits gelernt haben, wie man LEDs zum Blinken bringt, schauen wir uns nun ein weiteres wichtiges Bauteil in der Mikroelektronik an: den Taster.
Ähnlich wie Leuchtdioden findest du Taster an ganz vielen Stellen in deinem alltäglichen Leben wieder. Zum Beispiel auf deinem Weg zur Schule, wo du einen Taster dafür verwendest, die Fußgängerampel zu betätigen.
Aufgabenstellung
Wenn wir den linken Taster auf unserem Funbot drücken, sollen die Leuchtdioden für 2 Sekunden aufleuchten.
Programmcode
Schritt 1
Du erinnerst dich bestimmt noch an den Begriff „Variable“, den wir in der ersten Lektion kennengelernt haben. Eine Variable ist vergleichbar mit einem Informationsspeicher, in welchem wir Informationen zwischenspeichern und bei Bedarf abrufen können. Im ersten Schritt definieren wir zuerst unsere Variablen als Ganzzahl, auch als „Integer“ bezeichnet.
Diese Variablen dienen dazu, die Pin-Nummern unserer Module zu speichern.
Die LEDs sind mit dem Pin D10 des Mikrocontrollers auf unserem Funbot verbunden.
Der Linke Taster ist an Pin A2 und der rechte Taster ist an Pin A3 angeschlossen.
int modul_tasterL = A2;
int modul_LED = A1;
Schritt 2
Um den Status unseres linken Tasters zu überwachen, erstellen wir eine Variable namens „TasterL_status“. Dieser Variable weisen wir den Startwert 0 zu.
Die Variable ermöglicht es uns, den aktuellen Taster-Status während der Programmausführung abzufragen.
int modul_taster = A2;
int modul_led = A1;
int TasterL_status = 0;
Schritt 3
Jetzt, da alle Variablen einsatzbereit sind, ist es an der Zeit, sich um die Ein- und Ausgänge zu kümmern. Um LEDs verwenden zu können, müssen die digitalen Ports des Mikrocontrollers, an denen die LEDs angeschlossen sind, natürlich als Ausgang definiert werden. Denn an diesen Ports soll der Mikrocontroller später eine elektrische Spannung an die LEDs senden.
Daher ergänzen wir im Setup die LEDs.
pinMode(modul_LED, OUTPUT);
Hast du schon eine Idee, wie wir den Taster integrieren?
int modul_TasterL = A2;
int modul_LED = A1;
int TasterL_status = 0;
void setup()
{
pinMode(modul_LED, OUTPUT);
}
Schritt 4
Um einen Taster zu verwenden, muss der entsprechende Pin als Eingang (Input) deklariert werden. Durch das Auswerten des eingehenden Spannungssignals kann der Mikrocontroller auf dem Funbot erkennen, wann der Taster gedrückt wird. Denn der Taster leitet eine Spannung weiter, wenn er gedrückt wurde.
Wir schreiben dafür den Ausdruck „INPUT“ statt „OUTPUT“ im „pinMode“ Befehl.
int modul_TasterL = A2;
int modul_LED = A1;
int TasterL_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
Schritt 5
Um auszuwerten, ob der Taster gedrückt worden ist, verwenden wir jetzt den Befehl „digitalRead“.
Die Nutzung des „digitalRead“-Befehls unterscheidet sich etwas von der Verwendung des „digitalWrite“ Befehls.
Mit dem „digitalWrite“ Befehl gibt der Mikrocontroller an dem entsprechenden digitalen Pin eine Spannung von 5V aus. Dadurch können wir z.B. eine Leuchtdiode ein- oder ausschalten. Beim „digitalRead“ läuft es genau umgekehrt. Der Mikrocontroller fragt den im „digitalRead“ Befehl hinterlegten digitalen Pin ab. Wenn etwa eine Spannung anliegt (oder auch keine Spannung anliegt), können wir diese Information in einer Variable, unserem Informationsspeicher, zwischenspeichern. Dabei steht 1 für HIGH und 0 für LOW.
digitalRead(Pin);
Schritt 6
Lasst uns jetzt den digitalRead-Befehl in unserem Programmcode einbetten.
Dazu müssen zwei Dinge geschehen.
Erstens: Der Pin mit dem linken Taster muss ausgelesen werden. Liegt eine Spannung an dem Pin an, dann ist der Taster gedrückt und der Mikrocontroller erkennt den Wert „HIGH“. Ist der Taster nicht gedrückt, dann kommt keine Spannung an dem Pin an und der ausgelesene Wert ist „LOW“.
Zweitens: Der ausgelesene Wert muss in der Variablen „TasterL_status“ abgespeichert werden.
Diese beiden Aktionen werden im Programmcode in nur einem einzigen Befehl zusammengefasst:
TasterL_status = digitalRead(modul_TasterL);
Diese Zeile kann man interpretieren als:
Die Variable „TasterL_Status“ | ist ab jetzt | das Ergebnis aus dem Ausgelesenen Pin mit dem Taster |
TasterL_status | = | digitalRead(modul_TasterL) |
int modul_TasterL = A2;
int modul_LED = A1;
int TasterL_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterL_status = digitalRead(modul_TasterL);
}
Schritt 7
Um die rote Leuchtdiode immer dann einzuschalten, wenn der Taster gedrückt ist, verwenden wir jetzt erstmalig eine besondere Methode namens „If-Abfrage“.
Aber was genau ist eine If-Abfrage? Den Code dazu sehen wir uns nun genauer an.
wenn (Bedingung)
{
was soll passieren, wenn unsere Bedingung erfüllt wird?
}
Schritt 8
Eine IF-Abfrage ermöglicht es uns, einen Codeabschnitt nur dann auszuführen, wenn eine bestimmte Bedingung erfüllt ist. Wenn die Bedingung nicht erfüllt ist, wird der Codeabschnitt vom Mikrocontroller einfach übersprungen, als wäre er nicht vorhanden.
Wir verwenden hierfür das Schlüsselwort „if“, gefolgt von einer Klammer. Innerhalb dieser Klammer geben wir die Bedingung an, die erfüllt sein muss, damit der Codeabschnitt ausgeführt wird.
Danach öffnen wir eine geschweifte Klammer, in der wir unsere Anweisungen platzieren. Der Abschnitt wird mit einer schließenden geschweiften Klammer beendet.
if (Bedingung)
{
was soll passieren?
}
Schritt 9
Mit einer If-Abfrage können wir komplexe Problemstellungen lösen. Es handelt sich hierbei um eine einfache, logische Verknüpfung.
Auch in deinem täglichen Leben verwendest du If-Abfragen – und zwar ganz automatisch, häufig ohne es zu merken! Vor dem Gang zur Schule stellst du bei einem Blick aus dem Fenster fest, dass graue Wolken aufziehen. Du bist unsicher, ob du einen Regenschirm mitnehmen sollst oder nicht.
Deshalb blickst du in den Wetterbericht. Falls Regen vorausgesagt wird, nimmst du einen Regenschirm mit. Falls nicht, lässt du den Regenschirm zu Hause.
Hast du eine Idee, wie wir unsere Aufgabe aus dem Alltag mit einer If-Abfrage lösen können?
if (Hohe Regenwahrscheinlichkeit)
{
Regenschirm mitnehmen!
}
Schritt 10
Wir müssen zuerst eine geeignete Bedingung formulieren.
Da die LEDs nur leuchten sollen, wenn der linke Taster gedrückt ist, setzen wir dies als Bedingung fest.
Anschließend geben wir innerhalb der geschweiften Klammern die Anweisungen, dass unsere LEDs eingeschaltet werden sollen.
if (Taster gedrückt?)
{
schalte die LEDs ein
}
Schritt 11
Zuerst passen wir die Bedingung an. Da die LEDs nur leuchten sollen, wenn der linke Taster gedrückt wurde, müssen wir eine entsprechende Überprüfung vornehmen.
Wenn der Taster gedrückt wird, speichert der Mikrocontroller den Wert 1 (HIGH) in der Variable „TasterL_status“ ab. Andernfalls speichert er den Wert 0.
Wir prüfen in der Bedingung, ob der Wert der Variable gleich 1 ist. Wir verwenden dazu den Variablennamen, gefolgt von zwei Gleichheitszeichen und dem Wert, den wir überprüfen möchten – in unserem Fall den Wert 1.
if (TasterL_status == 1)
{
}
Würde man diese Programmzeile in unsere menschliche Sprache übersetzen, würde sie lauten: „Wenn der Tasterstatus genau 1 ist“.
Innerhalb der geschweiften Klammern geben wir dann die Anweisungen an. Die LEDs sollen für 2 Sekunden leuchten. Dazu schalten wir sie mit dem „digitalWrite“-Befehl ein, warten zwei Sekunden mithilfe des „delay“-Befehls und schalten die LEDs dann wieder mit dem „digitalWrite“-Befehl aus.
if (taster_status == 1)
{
digitalWrite(modul_led_rot, HIGH);
delay(2000);
digitalWrite(modul_led_rot, LOW);
}
Vielleicht habt ihr euch gefragt, warum wir bei der If-Abfrage zwei Gleichheitszeichen verwenden. In der Programmierung dient ein einzelnes Gleichheitszeichen dazu, Werte zuzuweisen. Dies kennst du zum Beispiel von der Definition der Variablen gleich zu Beginn des Programms.
Zwei Gleichheitszeichen werden immer dann verwendet, wenn Werte miteinander verglichen werden sollen.
Schritt 12
Höchste Zeit, unseren Programmcode auf den Funbot zu überspielen!
int modul_TasterL = A2;
int modul_LED = A1;
int TasterL_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterL_status = digitalRead(modul_TasterL);
if (TasterL_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
}
Aufgabe 2
Und was ist mit dem rechten Taster? Auch den rechten Taster möchten wir Programmieren. Am einfachsten geht das, indem wir unseren vorhandenen Sketch ein wenig verändern. Der rechte Taster ist am Mikrocontroller mit dem Pin A3 verbunden.
Im Sketch würde es schon genügen, gleich in der ersten Zeile die Zuordnung des Pins von A2 auf A3 zu ändern. Teste es und lade das Programm auf den Funbot.
int modul_TasterL = A2;
int modul_LED = A1;
int TasterL_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterL_status = digitalRead(modul_TasterL);
if (TasterL_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
}
Du siehst, es funktioniert. Aber schön ist es nicht! Denn die Bezeichnungen TasterL und status_TasterL passen überhaupt nicht zum rechten Taster. In diesem kleinen Sketch ist das nicht wirklich schlimm, jeder weiß ja, was gemeint ist. Aber wenn du deinen Funbot später mit allen vorhandenen Modulen programmiert hast, kann es ein echtes Problem werden. Denn je größer und komplexer ein Sketch wird, desto schwieriger wird es, den Überblick zu behalten.
Daher solltest du dir von vornherein angewöhnen, in deinem Sketch Ordnung zu behalten. Alle Bezeichnungen und Variablen müssen eindeutig zuzuordnen sein.
Der Sketch könnte daher nun so aussehen.
int modul_TasterR = A3;
int modul_LED = A1;
int TasterR_status = 0;
void setup()
{
pinMode(modul_TasterR, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterR_status = digitalRead(modul_TasterR);
if (TasterR_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
}
Mit dieser Ordnung ist es auch ganz einfach möglich, beide Taster separat voneinander zu programmieren.
Aufgabe 3
Mit der neu gewonnenen Ordnung sollen nun beide Taster in einem Sketch programmiert werden. Wenn der linke Taster Gedrückt wurde, sollen die vorderen LEDs für 2 Sekunden leuchten. Wenn der rechte Taster gedrückt wurde, sollen die LEDs für 4 Sekunden aufleuchten.
Dazu legen wir für beide Taster eine eigene Bezeichnung fest. Diese heißen nun:
int modul_TasterL = A2;
int modul_TasterR = A3;
Und wie legen für beide Taster eine eigene Variable fest.
int TasterL_status = 0;
int TasterR_status = 0;
Nun erstellen wir den passenden Sketch. Zunächst legen wir die Variablen für die beiden Taster, für die LED und für den Status der beiden Taster an.
int modul_TasterL = A2;
int modul_TasterR = A3;
int modul_LED = A1;
int TasterL_status = 0;
int TasterR_status = 0;
Danach legen wir im Setup fest, dass beide Taster ein Eingang sind, und dass der Pin für die LEDs ein Ausgang ist.
int modul_TasterL = A2;
int modul_TasterR = A3;
int modul_LED = A1;
int TasterL_status = 0;
int TasterR_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_TasterR, INPUT);
pinMode(modul_LED, OUTPUT);
}
Im Loop schreiben wir zunächst den vollständigen Sketch aus Aufgabe 1. Im Anschluss ergänzen wir die If-Abfrage für den rechten Taster.
int modul_TasterL = A2;
int modul_TasterR = A3;
int modul_LED = A1;
int TasterL_status = 0;
int TasterR_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterL_status = digitalRead(modul_TasterL);
if (TasterL_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
An dieser Stelle ergänzen wir die IF-Afrage für den rechten Taster
}
Nachdem der Sketch für die Abfrage des rechten Tasters ergänzt wurde, ist unser Sketch vollständig.
int modul_TasterL = A2;
int modul_TasterR = A3;
int modul_LED = A1;
int TasterL_status = 0;
int TasterR_status = 0;
void setup()
{
pinMode(modul_TasterL, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
TasterL_status = digitalRead(modul_TasterL);
if (TasterL_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
TasterR_status = digitalRead(modul_TasterR);
if (TasterR_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(5000);
digitalWrite(modul_LED, LOW);
}
}
Lautsprecher
Aufgabe 1: Töne und Melodien erzeugen
Lautsprecher, oder auch „Speaker“, werden in nahezu allen Maschinen verwendet, um akustische Signale zu erzeugen. Der „Piep-Ton“ hilft uns sicherzustellen, dass z.B. eine Taste auch wirklich gedrückt worden ist oder ein Vorgang abgeschlossen wurde.
Beim Frühstück ertönt immer ein lautes Piepen, wenn die Kaffeemaschine ihre Arbeit beendet hat. Fallen dir eigene Beispiele ein, an welchen Stellen dir der „Piep-Ton“ in deinem Alltag begegnet?
Aufgabenstellung
In dieser Anleitung sehen wir uns gemeinsam an, wie wir selbst einen „Piep-Ton“ mithilfe eines Lautsprechers erzeugen können. Dabei sollen zusätzlich immer die vorderen LEDs aufleuchten, wenn der Lautsprecher einen Ton abgibt. Danach soll eine kurze Pause folgen und das Geräusch anschließend wieder ertönen.
Schritt 1
In dieser Lektion werden wir mit LEDs und Lautsprechern arbeiten. Die vorderen LEDs sind mit Pin A1 des Arduino Mikrocontrollers verbunden und der Lautsprecher ist mit Pin A0 verbunden.
Schritt 2
Wenn ihr alles korrekt gemacht habt, sollten eure Variablen aussehen wie auf der nachfolgenden Abbildung.
int modul_speaker = A0;
int modul_led = A1;
Schritt 3
Im Setup-Bereich müssen wir uns erneut überlegen, ob wir die LED und den Lautsprecher als Eingang oder Ausgang definieren möchten. Da Leuchtdioden mit einem Spannungssignal eingeschaltet werden, handelt es sich bei einer LED immer um einen Ausgang. Wir legen deshalb im „pinMode Befehl“ den Wert „OUTPUT“ fest.
Nun die Frage an euch: Handelt es sich bei dem Lautsprecher um einen Eingang oder Ausgang?
int modul_speaker = A0;
int modul_led = A1;
void setup()
{
pinMode(modul_led, OUTPUT);
}
Schritt 4
Wenn ihr vermutet habt, dass der Lautsprecher ein Ausgang ist, dann liegt ihr damit völlig richtig.
Da die Töne durch eine ausgehende Spannung vom Mikrocontroller erzeugt werden, handelt es sich um einen Ausgang. Doch in diesem Fall gibt es eine Besonderheit.
Für den Lautsprecher benötigen wir im Setup keine Angabe darüber, ob es ein Ausgang oder ein Eingang ist.
Das liegt daran, dass die Erzeugung von Tönen in der Arduino-Software einen eigenen Befehl, mit dem Namen „Tone“, besitzt. Dieser Befehl beinhaltet bereits die Information, dass der Pin für die Tonausgabe ein Ausgangspin ist.
Schritt 5
Nachdem wir das Setup abgeschlossen haben, wenden wir uns jetzt dem Loop zu. Im Loop möchten wir zunächst einen Ton mit unserem Lautsprecher erzeugen und anschließend die Leuchtdiode zum Leuchten bringen.
Im Zusammenhang mit dem „Tone“ Befehl gibt es zwei Befehle, die in der Arduino-Software verwendet werden können.
tone(„Pin des Lautsprechers“, „Tonhöhe“);
und
noTone(„Pin des Lautsprechers“);
Der erste Befehl aktiviert den Lautsprecher an dem angegebenen Pin und der zweite Befehl schaltet den Lautsprecher stumm.
Den Lautsprecher aktivieren wir nun mit der Tonhöhe „200“ und die LED aktivieren wir wie gehabt mit dem digitalWrite-Befehl. LED und Lautsprecher sollen für 1000ms, also für eine Sekunde, aktiviert bleiben. Das erledigen wir mit einem delay.
int modul_speaker = A0;
int modul_led = A1;
void setup()
{
pinMode(modul_led, OUTPUT);
}
void loop()
{
tone(modul_speaker, 100);
digitalWrite(modul_led, HIGH);
delay(1000);
}
Anschließend möchten wir sowohl die LED, als auch den Lautsprecher wieder ausschalten. Wichtig ist nun, dass wir zum Ausschalten des Lautsprechers den „notone“ Befehl verwenden. Damit LED und Lautsprecher eine Sekunde aus bleiben, wird nach der Deaktivierung wieder ein delay verwendet.
int modul_speaker = A0;
int modul_led = A1;
void setup()
{
pinMode(modul_led, OUTPUT);
}
void loop()
{
tone(modul_speaker, 100);
digitalWrite(modul_led, HIGH);
delay(1000);
noTone(modul_speaker);
digitalWrite(modul_led_rot, LOW);
delay(1000);
}
Nun seid ihr an der Reihe. Wie würde das Programm aussehen, wenn die LED und der Lautsprecher abwechselnd blinken oder piepen sollen?
Antriebsmotoren
Der Antrieb erfolgt über zwei Elektromotoren, die unter der Bezeichnung N20 bekannt sind. Ein zusätzliches Rollenlager sorgt dafür, dass der Funbot bei Fahren gerade steht.
Die beiden Motoren werden von einem Motortreiber, einer sogenannten H-Brücke, angesteuert und mit Spannung versorgt. Diese H-Brücke hat die Bezeichnung L293D. Der Strom für die Motoren kommt als nicht direkt aus dem Mikrocontroller, da die elektrische Leistung der Pins nicht ausreichen würde, um den Roboter in Bewegung zu versetzen. Der Mikrocontroller sendet lediglich Spannungssignale an den Motortreiber, damit dieser wiederum genügend Energie an die beiden Motoren weiterleitet.
Beide Elektromotoren haben am Motortreiber eine vollständig unabhängige Verkabelung. Am Motortreiber L293D sind die beiden Motoren auch örtlich voneinander getrennt. Die linke Hälfte des L293D steuert den linken Motor und die rechte Hälfte steuert den rechten Motor.
Drehbewegung des Motors
Der Programmcode zur Ansteuerung von Gleichstrommotoren ist vergleichsweise einfach. Dennoch erklären wir vorab das Grundprinzip.
Der Motortreiber hat pro Motor zwei Signaleingänge. Wir nennen diese beiden Eingänge „in1“ und „in2“. Je nachdem, wie diese beiden Eingänge vom Mikrocontroller mit Spannung versorgt werden, lassen sich unterschiedliche Drehrichtungen einstellen.
Folgende Kombinationen sind möglich.
1. Beide Eingänge werden vom Mikrocontroller mit Spannung versorgt
digitalWrite(in1, HIGH);
digitalWrite(in2, HIGH);
Ergebnis: Der Motor dreht sich nicht.
2. Der erste Eingang wird vom Mikrocontroller mit Spannung versorgt, der zweite Eingang bekommt keine Spannung (LOW).
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
Ergebnis: Der Motor dreht sich linksherum.
3. Der erste Eingang bekommt vom Mikrocontroller keine Spannung und der zweite Eingang bekommt 5V (HIGH).
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
Ergebnis: Der Motor dreht sich rechtsherum.
4. Beide Eingänge werden vom Mikrocontroller nicht mit Spannung versorgt.
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
Ergebnis: Der Motor dreht sich nicht.
Drehgeschwindigkeit des Motors
Mit den soeben kennengelernten Befehlen können wir die Drehrichtung des Motors vorgeben. Allerdings können wir damit noch nicht die Geschwindigkeit des Motors einstellen. Dies geht mit dem dritten (und letzten) Befehl im Zusammenhang mit der Ansteuerung eines Motors. Mit Hilfe des Befehls senden wir eine Spannung an den sogenannten „Enable“ bzw. „En“ Pin des Motortreibers. Je höher diese Spannung am „EN“ Pin ist, desto schneller dreht sich der Motor. Da wir über den bekannten DigitalWrite Befehl jedoch nur HIGH und LOW senden können, was einer Spannung von 5Volt oder 0Volt entspricht, benötigen wir eine neue Art der Ansteuerung. Dieser Befehl heißt „analogWrite“. Damit sendet der Mikrocontroller nicht nur 5V und 0V an den Motortreiber, sondern der Befehl kann auch Zwischenstufen der Spannung an den Empfänger ausgeben. Dazu wird ein besonderer technischer Kniff verwendet, der sich „Pulsweitenmodulation“ (PWM) nennt. Was das genau ist, werden wir an dieser Stelle nicht weiter erläutern. Wichtig ist, dass dieser Befehl nicht an allen Pins des Mikrocontrollers funktioniert, sondern nur an den Pins, die über die PWM-Funktion verfügen. Welche das sind, kann man im sogenannten „Pinout“ eines Mikrocontrollers ablesen. Aber keine Sorge, auf dem Funbot wurden für die Funktion natürlich passende Pins ausgewählt.
Der Befehl lautet:
analogWrite(Name-Motor, Wert);
Erklärung
Anstelle von „Name-Motor“ tragen wir entweder die Pin-Nummer ein, über den der Mikrocontroller mit dem En-Pin des Motortreibers verbunden ist, oder die zugeordnete Variable.
Anstelle von „Wert“ tragen wir eine Zahl zwischen 0 und 255 ein. Dabei entspräche der Wert 0 einer Spannung von 0 Volt und 255 einer Spannung von 5V. Bei einem Wert von 125 würden ca. 2,5V vom Mikrocontroller an den Motortreiber ausgegeben werden. Der Motortreiber würde damit ca. 50% der maximalen Leistung an den Motor weiterleiten.
Das folgende Bild zeigt die Verkabelung der Motoren mit dem Motortreiber. Über die Leitungen mit den Zahlen eins bis sechs erhält der Motortreiber für beide Seiten die Informationen zu Drehrichtung und Geschwindigkeit. Zusätzlich ist jeder Motor mit Plus und Minus mit dem Treiber verbunden. Über die weiteren Kontakte des Motortreibers, die auf der Skizze nicht verbunden sind, bezieht der Treiber direkt auf der Platine seine elektrische Energie.
Aufgebe 1: Drehbewegung
Mit den gelernten Befehlen werden wir nun den linken Motor in eine Drehbewegung versetzen. Dazu beginnen wir den Sketch mit ein paar Festlegungen.
Wir nennen die beiden Pins zur Richtungsbestimmung MotorLa und MotorLb und ordnen ihnen die Pins des Mikrocontrollers zu, an denen sie angeschlossen sind.
Außerdem bezeichnen wir den Pin, mit dem die Geschwindigkeit des linken Motors bestimmt wird als „MSL“. Das ist unsere Kurzform für „Motor Speed Links“.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
Im Setup müssen wir Eingänge und Ausgänge festlegen. Um den Motortreiber mit Hilfe des Mikrocontrollers anzusteuern werden drei Ausgänge benötigt. Zwei für die Richtungsbestimmung und einen für die Geschwindigkeit.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
void setup()
{
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
}
Damit haben wir bereits alle Vorbereitungen erledigt und können uns um das Hauptprogramm, den Loop kümmern.
Im ersten schritt lassen wir den Motor nun mit voller Leistung in eine Richtung drehen.
Dafür setzten wir einen der Richtungskontakte auf HIGH und den anderen auf LOW. Zusätzlich geben wir bei der Geschwindigkeit den höchsten Wert, 255, an.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
void setup()
{
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
}
void loop()
{
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
}
Aufgabe 2: Wechselnde Drehbewegung
Im zweiten Schritt soll sich das gleiche Rad jeweils für zwei Sekunden vor und zurück drehen. Zwischendurch soll das Rad für eine Sekunde still stehen.
Dafür müssen wir folgende Änderungen am Sketch vornehmen.
1. Eine Pause eingeben, damit das Rad nach dem Start der Drehbewegung für zwei Sekunden weiterdreht.
2. Das Rad stoppen
3. Eine Pause von einer Sekunde hinzufügen
4. Die Drehrichtung im Sketch zu ändern.
5. Eine Pause eingeben, damit das Rad nach dem Start der Drehbewegung für zwei Sekunden weiterdreht.
Fahr, kleiner Roboter!
Der letzte Schritt, um einen Roboter fahren zu lassen, liegt natürlich darin, dass sich beide Räder drehen. Was müssen wir nun am vorhandenen Sketch verändern, damit sich beide Motoren drehen?
Wir beginnen mit den Variablen. Wir ergänzen die notwendigen Angaben für den zweiten Motor. Den Pin für die Geschwindigkeit des zweiten Motors nennen wir MSR (Motor Speed Rechts) und die beiden Kontakte für die Drehrichtung des rechten Motors nennen wir „MotorRa“ und „MotorRb“.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
void setup()
{
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
}
void loop()
{
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
delay(2000);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, LOW);
delay(1000);
digitalWrite(MotorLa, HIGH);
digitalWrite(MotorLb, LOW);
delay(2000);
}
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
Danach ergänzen wir im Setup die drei neuen Pins und definieren sie als Ausgang.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
Im Hauptteil, dem „Loop“, ergänzen wir nun einen Code, der beide Motoren drehen lässt.
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
void loop()
{
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 255);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
}
Jetzt weißt du, wie du die Motoren kontrollieren kannst. Teste den Code und verändere Drehrichtung und Geschwindigkeit. Schaffst du es, den Roboter im Kreis fahren zu lassen?
Liniensensor
Das erste Modul, das zusätzlich am Funbot angebracht werden kann, ist der Liniensensor.
Das Prinzip der Linienverfolgung lässt sich leicht erklären. Das Ziel ist, dass der Roboter automatisch, ohne weitere Hilfe von außen, an einer schwarzen Linie entlang fährt.
Dazu muss der Roboter grundsätzlich in der Lage sein, kurven zu fahren, bzw. sich nach links und rechts zu bewegen.
Kurven fahren
Wenn sich der linke Motor allein oder stärker dreht als der rechte Motor, wird der Roboter eine Rechtskurve fahren.
Wenn sich der rechts Motor allein oder stärker dreht als der linke Motor, wird der Roboter eine Linkskurve fahren.
Die Kombination dieser beiden Bewegungen muss nun in Abhängigkeit der schwarzen Linie erfolgen.
Sensorwerte auslesen
Damit die Bewegungen der beiden Motoren präzise gesteuert werden können, ist es entscheidend, dass der Roboter die Linie kontinuierlich erkennt. Daher beginnen wir damit, den Liniensensor zu programmieren und die Sensorwerte auszulesen.
Was die Datenerfassung angeht, können wir den gleichen Code verwenden, als würden wir einen Taster auslesen. Denn genau wie beim Taster, gibt der Sensor ein digitales Signal, also 5V Spannung, an den Mikrocontroller weiter.
Da es sich um einen ähnlichen Code handelt, wird dieser hier nicht detailliert erläutert. Die einzige Änderung ist, dass wir anstelle des Tasters den Linetrackingsensor mit dem Pin definieren und eine Variable umbenennen. Diese Zeilen lauten:
int modul_Tracking = 12;
int Tracking_status = 0;
Die neuen Codezeilen setzen wir in den Sketch aus der Anleitung zum Taster ein.
int modul_Tracking = 12;
int Tracking_status = 0;
void setup()
{
pinMode(modul_Tracking, INPUT);
}
void loop()
{
Tracking_status = digitalRead(modul_Tracking);
if (Tracking_status == 1)
{
In diese Klammern schreibt man als nächstes, was passieren soll, wenn die schwarze Linie erkannt wurde.
}
}
Um zu prüfen, ob die Linienerkennung auch tatsächlich funktioniert, können wir einen kleinen Zwischenschritt einbauen.
In den vorherigen Code schreiben wir, dass die Frontscheinwerfer leuchten sollen, wenn die dunkle Linie erkannt wurde. Auch dieser Code ist sehr ähnlich zu der Aufgabe mit dem Taster, da auch hier die LEDs leuchten sollten, wenn der Taster gedrückt wurde.
Erkennst du die Gemeinsamkeiten?
int modul_Tracking = 12;
int Tracking_status= 0;
int modul_LED = A1;
void setup()
{
pinMode(modul_Tracking, INPUT);
pinMode(modul_LED, OUTPUT);
}
void loop()
{
Tracking_status = digitalRead(modul_Tracking);
if (Tracking_status == 1)
{
digitalWrite(modul_LED, HIGH);
delay(1000);
digitalWrite(modul_LED, LOW);
}
}
Praxistipp: Wenn der Sensor nicht gut eingestellt ist, könnte es sein, dass der Sensor nur schlecht zwischen hellem und dunklem Untergrund unterscheiden kann. Hierfür gibt es am Sensor eine blaue Box, die es erlaubt, den Sensor mit einem Schraubendreher anzupassen. Dazu stellen wir den Funbot mit dem Sensor auf eine dunkle Oberfläche, z.B. die Linie, und kalibrieren die Sensoren, während diese über der dunklen Linie sind. Auf der Linie muss die linke Status-LED am Sensor aus gehen. Ist das nicht der Fall, drehen wir mit einem Schraubendreher so lange an der Stellschraube in dem blauen Block, bis die LED aus geht. Wenn die LED ausgeht, ist er richtig kalibriert. Die Schraube im blauen Block darf aber nicht zu weit gedreht werden, denn sonst geht die Status-LED auch auf einem hellen Untergrund nicht wieder an.
Nun geht es weiter.
Bisher gehen die Frontscheinwerfer an, wenn die Linie erkannt wurde. Das werden wir jetzt wieder ändern, mit folgender Absicht. Wenn die schwarze Linie erkannt wurde, soll sich der Roboter nach rechts auf die helle Fläche bewegen. Sobald die helle Fläche erreicht wurde, soll sich der Roboter wieder nach Links zur schwarzen Linie bewegen.
Auf diesem Weg wird der Funbot auf einem leichten ZickZack Kurs entlang der Außenkante der schwarzen Linie fahren.
Wir entfernen aus dem Code nun zunächst die Frontscheinwerfer und ergänzen alles, was zur Ansteuerung der Motoren benötigt wird.
Erst danach können wir die Bewegungen „rechtsherum“ und „linksherum“ in Abhängigkeit des Liniensensor programmieren.
int modul_Tracking = 12;
int Tracking_status= 0;
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(modul_Tracking, INPUT);
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
void loop()
{
Tracking_status = digitalRead(modul_Tracking);
if (Tracking_status == 1)
{
In diese Klammer muss der Code für die Rechtskurve
}
else
{
In diese Klammer muss der Code für die Linkskurve
}
}
IF-ELSE
Zum ersten Mal verwenden wir hier eine Erweiterung der If-Abfrage. Bei der If-Abfrage wird eine Bedingung geprüft und nur in dem Fall, dass die Bedingung erfüllt ist, wird ein Code ausgeführt.
Ist die Bedingung nicht erfüllt, läuft das vorherige Hauptprogramm einfach weiter.
Die If-Abfrage kann um den Else-Befehl erweitert werden. Es handelt sich dabei um einen Code, der alternativ ausgeführt wird, wenn die Bedingung aus der vorherigen If-Abfrage nicht erfüllt ist.
Die Grundstruktur kannst du im vorherigen Code schon erkennen.
In diesem Fall werden wir nun mit diesem Code die Linienverfolgung vervollständigen.
Dazu werden wird die IF-ELSE Abfrage nach folgendem Schema gestalten.
„Wenn die schwarze Linie erkannt wurde, dann fahr nach rechts. Ansonsten (und dann ist der Sensor im hellen Bereich neben der Linie) fahr nach links.“
Diese Abfrage reicht aus, um den Roboter an der Linie entlang fahren zu lassen. Welchen Code benötigen wir für die Linkskurve und Rechtskurve? In der Anleitung zu den Motoren hast du kennengelernt, mit welchem Code man die Geschwindigkeit der Motoren beeinflusst. Um eine Kurve zu Fahren, werden wir jeweils einen Motor langsamer drehen lassen.
Rechtskurve
Linker Motor schnell
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
Rechter Motor langsam
analogWrite(MSR, 40);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
Linkskurve
Linker Motor langsam
analogWrite(MSL, 40);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
Rechter Motor schnell
analogWrite(MSR, 255);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
Die Codes für Linkskurve und Rechtskurve setzen wir nun in die If-Else Abfrage ein.
int modul_Tracking = 12;
int Tracking_status= 0;
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(modul_Tracking, INPUT);
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
void loop()
{
Tracking_status = digitalRead(modul_Tracking);
if (Tracking_status == 1)
{
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 40);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
}
else
{
analogWrite(MSL, 40);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 255);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
}
}
Ultraschallsensor
Entfernung messen und auswerten
Der Ultraschallsensor HC-SR04 zählt in der Mikroelektronik definitiv zu den bekanntesten Bauteilen. Mit Hilfe von Ultraschallsensoren können wir den Abstand von Objekten messen. Du bist in deinem Alltag bestimmt auch schon Ultraschallsensoren begegnet: Die kleinen Sensoren werden zum Beispiel in Autos verbaut. Dort unterstützen Sie uns beim Einparken und warnen uns, wenn wir zu dicht an ein Hindernis (zum Beispiel an eine Wand) heranfahren.
Auch der Funbot soll den Sensor nutzen, um Gegenstände zu erkennen.
Aufgabe 1: Miss mit dem Ultraschallsensor den Abstand zu einem Objekt. Das Objekt kann deine Hand oder eine Wand sein. Wenn der Sensor in weniger als 30 Zentimetern Abstand ein Hindernis erkennt, sollen die LEDs am Funbot leuchten.
Programmcode
Schritt 1
Ein Vergleich aus der Natur: Eine Fledermaus sendet Ultraschallwellen, um herauszufinden, wie weit ein Objekt entfernt ist. Diese Ultraschallwellen werden von Objekten reflektiert und landen wie bei einem Echo nach einer kurzen Zeit wieder bei der Fledermaus. Die Fledermaus erkennt anhand der Wartezeit, wie weit Objekte von ihr entfernt sind.
Der Ultraschallsensor verhält sich zusammen mit dem Mikrocontroller ähnlich, wie die Fledermaus in der Natur. Der Sensor verschickt Ultraschallwellen und wartet, bis diese von Objekten reflektiert werden. Sobald die Ultraschallwellen zum Modul zurückgekommen sind, sendet der Ultraschallsensor ein Spannungssignal (5V) an den Mikrocontroller. Aus der Zeit, in der die Ultraschallwelle unterwegs war, kann der Mikrocontroller die Entfernung zum Objekt berechnen.
Aber wie funktioniert die Technik dahinter?
Schritt 2
Der Ultraschallsensor verfügt über zwei Signalpins: „Trigger“ und „Echo“. Der Trigger-Pin gibt dem Ultraschallsensor das Startsignal, um mit der Abstandsmessung zu beginnen. Dazu muss er mindestens 10ms (10 Millisekunden) lang eine Spannung vom Mikrocontroller erhalten.
Die Schallwelle ist nun unterwegs zum Objekt und wird dort reflektiert.
Sobald das Modul die reflektierten Ultraschallwellen empfängt, sendet es über den „Echo-Pin“ eine Spannung an den Mikrocontroller.
Sehr kleine Zahlen. Wir benötigen einen neuen Datentyp!
Die Ultraschallwellen bewegen sich mit Schallgeschwindigkeit. Wie schnell das ist, wird mit dem nächsten Beispiel deutlich.
Wir nehmen an, zwei Personen sitzen sich gegenüber. Der Abstand sollte dabei etwa einen Meter betragen. Die Ultraschallwelle benötigt für diese Entfernung nur den Bruchteil einer Sekunde, nämlich ungefähr 0,03 Sekunden.
Bis zum jetzigen Schritt konnten wir immer mit ganzen Zahlen arbeiten und haben im Setup als Datentyp den „Integer“, abgekürzt „int“ verwendet. Da die Ultraschallwellen zu schnell sind, benötigen wir einen anderen Datentyp: Die Fließkommazahl. Der Datentyp dafür heißt „Float“ und steht für „Floating-Point-Number“, was auf Deutsch „Fließkommazahl“ bedeutet. Mit diesem Datentyp können wir typische Komma-zahlen, wie zum Beispiel 0,03 verarbeiten.
Wichtig ist hierbei, das wir als Trennzeichen einen Punkt verwenden. Wir schreiben also „0.03“ anstelle von „0,03“.
Beispiel
float fliesskommazahl = 0.03;
Schritt 4
Jetzt erstellen wir die Variablen für diese Lektion. Die vorderen LEDs sind mit dem Pin A1 des Mikrocontrollers verbunden. Der Trigger des Ultraschallsensors ist mit dem Pin 10 des Mikrocontrollers verbunden. Der Echo-Pin vom Ultraschallsensor ist mit dem Pin 11 des Mikrocontrollers verbunden.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
Schritt 5
Um die Entfernung zu ermitteln, brauchen wir folgende Formel:
Entfernung = (Gemessene Zeit / 2) x Schallgeschwindigkeit
Wie wir mit dieser Formel rechnen können, erklären wir dir später. Wir teilen diese Formel jetzt in Variablen auf und kommen auf folgende vier Variablen:
„entfernung“
„gemessene_zeit“
„zeit_pro_strecke“
„schallgeschwindigkeit“
Den Variablen „entfernung“, „gemessene_Zeit“ und „zeit_pro_strecke“ weisen wir den Startwert 0 zu. Die Variable „schallgeschwindigkeit“ bekommt den Wert 0.0034 zugewiesen. Dieser Zahlenwert steht für die Schallgeschwindigkeit in Mikrosekunden pro Zentimeter
Nachfolgend findet Ihr eine Übersicht aller verwendeten Variablen.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0.034;
Schritt 6
Im Setup definieren wir die Ein- und Ausgänge.
Da der Trigger-Pin des Ultraschallsensors Spannung vom Mikrocontroller benötigt, um Ultraschallwellen zu auszusenden, legen wir die Trigger-Variable als Ausgang fest.
Da über den Echo-Pin des Ultraschallsensors eine Spannung an den Mikrocontroller zurückgegeben wird, wird der Pin als Eingang definiert.
Die LED wird wie üblich als Ausgang deklariert, da sie vom Mikrocontroller mit Spannung versorgt werden muss.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0.034;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
}
Schritt 7
Im Loop starten wir mit der Entfernungsmessung. Um genaue Messwerte zu erhalten, muss der Ultraschallsensor zuerst spannungsfrei sein. Erst dann ist gewährleistet, dass keine Störsignale an den Trigger-Pin des Ultraschallsensors anliegen. Der Pin muss dafür mindestens 5ms lang ausgeschaltet werden.
Um danach die Entfernungsmessung zu starten, muss der Trigger-Pin mindestens 10ms lang ein Spannungssignal vom Mikrocontroller erhalten.
Nach den 10ms schalten wir den Trigger wieder auf LOW.
Jetzt wartet der Ultraschallsensor auf die Reflexion der Schallwelle. Wie das funktioniert, schauen wir uns im nächsten Schritt genauer an.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0.034;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
}
void loop()
{
digitalWrite(modul_ultraschallsensor_trigger, LOW);
delay(5);
digitalWrite(modul_ultraschallsensor_trigger, HIGH);
delay(10);
digitalWrite(modul_ultraschallsensor_trigger, LOW);
}
Schritt 8
Mit dem Setzen des „modul_trigger“ auf „HIGH“ im vorherigen Schritt, wurden Ultraschallwellen ausgesendet.
Zur Erinnerung: Um die Entfernung messen zu können, müssen wir wissen, wie lange die Ultraschallwellen benötigen, um zum Objekt und wieder zurück zu gelangen.
Dafür verwenden wir den Befehl pulseIn(Pin, HIGH/LOW). Achtung der vorletzte Buchstabe von „pulseIn“ ist ein großes „i“.
Mit diesem Befehl wird die Zeit gemessen, die vergeht, bis das 5V Signal vom Echo-Pin des Ultraschallsensors am Mikrocontroller detektiert wird. Denn das 5V-Signal des Echo-Pins wird vom Ultraschallsensor aktiviert, wenn die Schallwelle zum Sensor zurück gekommen ist.
Mit dem Befehl „pulseln“ prüft der Mikrocontroller permanent den Status des Echo-Pins. Diese Überprüfung soll ermitteln, ob sich der „Status“ des Pins verändert hat. Der Mikrocontroller möchte also erfahren, ob eine Spannung anliegt, die als Wechsel von einem LOW auf ein HIGH Signal interpretiert werden kann.
Wenn ein solcher Signalwechsel erfolgt, bedeutet das, dass die zuvor ausgesendeten Ultraschallwellen wieder am Ultraschallsensor eingetroffen sind und wir unsere Zeitmessung beenden können.
Die Zeit bis zum Eintreffen des Echos wird mithilfe des Befehls „gemessene_zeit = pulseIn(modul_echo, HIGH);“ in der Variablen „gemessene_zeit“ gespeichert.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0.034;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_ultraschallsensor_trigger, OUTPUT);
pinMode(modul_ultraschallsensor_echo, INPUT);
}
void loop()
{
digitalWrite(modul_ultraschallsensor_trigger, LOW);
delay(5);
digitalWrite(modul_ultraschallsensor_trigger, HIGH);
delay(10);
digitalWrite(modul_ultraschallsensor_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
}
Schritt 9
In der Variablen „gemessene_zeit“ haben wir die Zeit gespeichert, die die Ultraschallwelle vom Aussenden am Ultraschallsensor, bis hin zum erneuten Eintreffen am Ultraschallsensor benötigt hat.
Diese Zeitdauer misst also den Hinweg und den Rückweg zu dem Objekt (Hand / Wand).
Zur Berechnung des Abstandes eines Objektes benötigen wir allerdings nur die Zeitdauer einer Strecke, also dem Hin- oder Rückweg zum Objekt. Deshalb teilen wir die gemessene Zeit durch zwei und speichern das Ergebnis in der Variablen „zeit_pro_strecke“.
int modul_led = A1;
int modul_trigger = 10;
int modul_echo = 11;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0.034;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
}
void loop()
{
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
}
Schritt 10
Jetzt können wir auch die Entfernung bestimmen. Wir multiplizieren hierfür die Variable „zeit_pro_strecke“ mit der Variablen „schallgeschwindigkeit“. Das Ergebnis speichern wir in der Variable „entfernung“.
int modul_led = 12;
int modul_ultraschallsensor_trigger = 6;
int modul_ultraschallsensor_echo = 5;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_ultraschallsensor_trigger, OUTPUT);
pinMode(modul_ultraschallsensor_echo, INPUT);
}
void loop()
{
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
entfernung = zeit_pro_strecke * schallgeschwindigkeit;
}
Schritt 11
Die Entfernung zwischen Ultraschallsensor und Objekt ist nun in der Variable „entfernung“ gespeichert. Zur Lösung unserer Aufgabenstellung müssen wir jetzt noch überprüfen, ob die gemessene Entfernung größer als 30 cm ist. Hierfür nutzen wir eine If-Abfrage. In der Bedingung geben wir an, dass der Mikrocontroller überprüfen soll, ob der Abstand kleiner als 30 cm ist.
Wenn diese Bedingung erfüllt ist, werden innerhalb der If-Abfrage die LEDs eingeschaltet. Die LEDs leuchten für eine Sekunde und werden danach wieder ausgeschaltet.
int modul_led = 12;
int modul_ultraschallsensor_trigger = 6;
int modul_ultraschallsensor_echo = 5;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
}
void loop()
{
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
entfernung = zeit_pro_strecke * schallgeschwindigkeit;
if (entfernung > 30)
{
digitalWrite(modul_led, HIGH);
delay(1000);
digitalWrite(modul_led, LOW);
}
}
Automatisierung - Antrieb und Ultraschallsensor kombiniert
Jetzt bekommt unser Roboter zum ersten Mal eine Sensorik. Wir lassen den Roboter geradeaus fahren und programmieren zusätzlich den Ultraschallsensor.
Aufgabe: Wenn ein Gegenstand 30cm vor dem Roboter auftaucht, soll der Roboter stehenbleiben.
Um es uns ein wenig einfacher zu machen, über nehmen wir das letzte Programm aus der Anleitung mit dem Ultraschallsensor und löschen dort den Teil mit den Leuchtdioden.
int modul_led = 12;
int modul_ultraschallsensor_trigger = 6;
int modul_ultraschallsensor_echo = 5;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
}
void loop()
{
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
entfernung = zeit_pro_strecke * schallgeschwindigkeit;
if (entfernung > 30)
{
}
}
Nun ergänzen wir den Code, den wir benötigen, um den Roboter geradeaus fahren zu lassen. Diesen färben wir zunächst ein, und fügen dann beide Sketche zusammen.
int modul_led = 12;
int modul_ultraschallsensor_trigger = 6;
int modul_ultraschallsensor_echo = 5;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0;
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
void loop()
{
analogWrite(MSR, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
entfernung = zeit_pro_strecke * schallgeschwindigkeit;
if (entfernung > 30)
{
}
}
Nun wurden beide Sketche zusammengeführt. Zu Beginn wurden alle Module definiert. Im Setup wurden alle Eingänge und Ausgänge festgelegt und im Loop wurden die Motoren aktiviert. Nachdem die Motoren mit der Drehbewegung beginnen, findet hier noch eine Entfernungsmessung statt.
Jedoch ist die If-Abfrage noch nicht mit Programmcode gefüllt. Wenn ein Abstand unter 30cm festgestellt wurde, passiert jetzt also: NICHTS.
Daher füllen wir nun die IF-Abfrage mit einem Code, der die Drehbewegung der Motoren stoppt. Den Code haben wir in der Anleitung zum Motor kennengelernt.
analogWrite(MSR, 0);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, LOW);
analogWrite(MSR, 0);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, LOW);
Diesen Code fügen wir nun in die IF-Abfrage ein.
int modul_led = 12;
int modul_ultraschallsensor_trigger = 6;
int modul_ultraschallsensor_echo = 5;
float entfernung = 0;
float gemessene_zeit = 0;
float zeit_eine_strecke = 0;
float schallgeschwindigkeit = 0;
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup()
{
pinMode(modul_led, OUTPUT);
pinMode(modul_trigger, OUTPUT);
pinMode(modul_echo, INPUT);
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
}
void loop()
{
analogWrite(MSR, 255);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
digitalWrite(modul_trigger, LOW);
delay(5);
digitalWrite(modul_trigger, HIGH);
delay(10);
digitalWrite(modul_trigger, LOW);
gemessene_zeit = pulseIn(modul_echo, HIGH);
zeit_pro_strecke = gemessene_zeit / 2;
entfernung = zeit_pro_strecke * schallgeschwindigkeit;
if (entfernung > 30)
{
analogWrite(MSR, 0);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, LOW);
analogWrite(MSR, 0);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, LOW);
}
}
Damit ist der Sketch schon echt umfangreich geworden. Aber das ist noch nicht alles. Der Roboter soll schließlich noch Töne abspielen, leuchten, auf einer Linie fahren und auf ankommende Bluetooth-Daten reagieren.
Unterbodenbeleuchtung mit WS2812B Neopixel LEDs
Bunte Farben und Effekte erzeugen
Der Funbot verfügt auf der Unterseite über besondere Leuchtdioden. Diese besonderen Leuchtdioden werden auch als „WS2812B“, „Pixel“ oder „Neopixel“ bezeichnet.
Die Besonderheit der WS2812B Pixel besteht darin, dass sie einzeln in tausenden Farben angesteuert werden können, obwohl alle sechs Pixel mit nur einer Datenleitung vom Mikrocontroller angesteuert werden. WS2812B-LEDs sind vielseitig Einsetzbar und eignen sich hervorragend als Stimmungslicht im Raum, für kreative Kunstprojekte oder eben zur Unterbodenbeleuchtung von Robotern. Dort sind Sie geeignet, um Programmabschnitte zu visualisieren.
Anwendungsbeispiele
– Man könnte die LEDs etwa gleichzeitig rot leuchten lassen, wenn der Roboter über den Lautsprecher einen Ton abgibt. Wenn dann im Betrieb die LEDs rot leuchten, aber kein Ton zu hören ist, weiß man, dass man offenbar bei der Programmierung des Lautsprechers einen Fehler gemacht hat.
– Man könnte die LEDs grün leuchten lassen, wenn der Funbot geradeaus fahren soll. Wenn der Roboter im Betrieb grün leuchtet, aber nicht fährt, erkennt man, dass die Programmierung der Motoren überarbeitet werden muss. Vielleicht hat man zu wenig Leistung eingestellt.
– Man kann auch die drei linken und die drei rechten LEDs orange blinken lassen, um so anzudeuten, dass der Roboter laut Programm abbiegen wird. Die tatsächliche Bewegung des Roboters muss dann analysiert werden.
Aufgabe
In dieser Lektion möchten wir unsere WS2812B LEDs in drei Farben leuchten lassen: Rot, Grün und Blau. Die Pixel 0 und 1 sollen dabei rot, die Pixel 2 und 3 grün und die Pixel 4 und 5 blau leuchten.
Programmcode
Schritt 1
Die WS2812-LEDs faszinieren durch ihre Fähigkeit, individuell steuerbares farbiges Licht zu erzeugen. Doch wie kann man die kleinen Bauteile so präzise ansteuern? Hier kommt zum ersten Mal eine sogenannte „Programmbibliothek“ ins Spiel. Diese werden häufig auch einfach nur „Bibliothek“ oder „Library“ genannt.
Um eines gleich vorwegzusagen: Es ist nicht die Rede von Bibliotheken zum Ausleihen von Büchern.
In der Welt der Elektronik und Programmierung sind Bibliotheken wichtige Werkzeuge, die dabei helfen, komplexe Aufgaben einfacher zu bewältigen. In einer Bibliothek befindet sich bereits fertiger Code. Auf diesen Code können wir in unserem Sketch zugreifen, wenn wir eine Bibliothek zu unserem Programm hinzufügen und danach im Sketch aufrufen.
Der Code der Bibliothek ist also im „Hintergrund“ gespeichert und wird bei Bedarf abgerufen.
Zwei Dinge muss man sprachlich unterscheiden, wenn man vom hinzufügen einer Bibliothek spricht, da der Ausdruck zwei Bedeutungen haben kann.
Erstens: Man spricht vom Hinzufügen einer Bibliothek, wenn man diese in die Arduino-Software integriert. Dies erfolgt über den Bibliotheksverwalter in der Arduino-Software. Das Hinzufügen zur Arduino-Software ist notwendig, da immer wieder neue elektronische Komponenten erfunden und verkauft werden. Viele dieser Komponenten werden mithilfe von Bibliotheken programmiert. Daher wäre es nicht gut, wenn man keine weiteren Bibliotheken zur Arduino-Software hinzufügen könnte.
Zweitens: Man spricht ebenfalls vom Hinzufügen einer Bibliothek, wenn man diese bei der Programmierung in einem Sketch integriert, obwohl man hier eigentlich sagen müsste, dass die Bibliothek im Sketch aufgerufen wird.
Der Aufruf einer Bibliothek erfolgt über den „include“ – Befehl. In diesem Beispiel mit den WS2812B LEDs sieht das so aus:
#include <Adafruit_NeoPixel.h>
Schritt 2
Für die WS2812B LEDs müssen wir nun eine passende Bibliothek zur Arduino-Software hinzufügen, damit wir diese im weiteren Verlauf verwenden können. Die Bibliothek können wir direkt über unsere Arduino-Software installieren. Erst durch diese Installation können wir die Bibliothek in unserem Sketch einbinden.
- Navigiere in der Arduino IDE auf den Reiter „Sketch“.
- Wähle den Unterpunkt „Bibliothek einbinden“ aus.
- Klicke auf „Bibliotheken verwalten…“.
- Tippe in der Suchleiste den Ausdruck „Adafruit_NeoPixel.h“ ein.
- Suche die Bibliothek „Adafruit Neopixel von Adafruit“ und klicke auf „Installieren“.
Schritt 3
Jetzt können wir unsere Bibliothek in den Programmcode einbinden.
Dafür verwenden wir den „#include“-Befehl. Das englische Wort „include“ bedeutet so etwas wie „einbetten“. Hinter diesen Ausdruck fügen wir jetzt noch in spitze Klammern den Namen der Bibliothek ein. In unserem Beispiel also <Adafruit_NeoPixel.h>.
#include <Adafruit_NeoPixel.h>
Schritt 4
Im nächsten Schritt legen wir fest, an welchem Pin des Mikrocontrollers die sechs LEDs hintereinander angeschlossen sind. Es ist der Pin 13. Der Pin wird mit dem Ausdruck „#define PIN“ festgelegt.
Anschließend legen wir fest, wie viele einzelne Pixel sich auf unserem Funbot an dem Pin 13 befinden. In unserem Fall sind es sechs Stück. Der entsprechende Code zur Festlegung der Anzahl lautet #define NUMPIXELS 6.
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Schritt 5
Jetzt wird es etwas komplizierter.
Damit wir die WS2812-LEDs im weiteren Programmverlauf bequem ansteuern können, fassen wir die sechs LEDs zu einem „Objekt“ zusammen. Diesem Objekt geben wir einen Namen. Das funktioniert grundlegend so, wie du es bereits von den Variablen aus den früheren Lektionen kennst. Wir greifen jetzt allerdings nicht auf einen Datentypen (z.B. Int für Integer) zurück, sondern verwenden den Ausdruck „Adafruit_NeoPixel“ aus der zuvor eingefügten Bibliothek.
Unsere WS2812B-LEDs bekommen als gemeinsames Objekt den Namen „BodenLED“. Diesem Namen weisen wir jetzt Informationen zu. Für die Zuweisung greifen wir ebenfalls auf den Ausdruck „Adafruit_NeoPixel“ zurück. In die Klammer hinter dem Ausdruck legen wir jetzt vier wichtige Informationen fest:
- Über wie viele Pixel verfügt das Objekt? (NUMPIXELS)
- An welchem Pin ist das Objekt mit dem Mikrocontroller verbunden? (PIN)
- In welchem Farbraum sollen die Pixel aufleuchten? (NEO_GRB)
- In welcher Frequenz sollen die Pixel angesteuert werden (NEO_KHZ800)
Du fragst dich vielleicht, was das alles für verrückte Angaben sind, und woher man so etwas wissen könnte, wenn es nicht gerade in einer Anleitung wie dieser auftaucht. Aber mach dir keine Sorgen, es handelt sich um technische Angaben über die verwendeten Leuchtdioden. Wenn man diese Module kauft, bekommt man vom Hersteller oder dem Verkäufer die passenden Informationen mitgeliefert.
Die vollständige Programmzeile sieht nun so aus.
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Adafruit_NeoPixel BodenLED = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
Schritt 6
In Schritt 6 können wir endlich mit dem Hauptprogramm beginnen. Wir starten im Setup-Teil unseres Programms mit Initialisierung, also dem Starten des WS2812B-Objekts.
Erst durch diese Initialisierung bereiten wir den Mikrocontroller für den Datenaustausch mit dem WS2812B-Objekt vor. Dieser Befehl „BodenLED.begin();“ ist dabei vergleichbar mit dem „pinMode“-Befehl, den du bereits aus den früheren Lektionen kennst.
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Adafruit_NeoPixel BodenLED = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
BodenLED.begin();
}
Schritt 7
Nachdem wir den Mikrocontroller auf unserem Funbot für den Datenaustausch mit den WS2812B-LEDs vorbereitet haben, können wir jetzt in den Loop-Teil übergehen.
In unserem Loop-Teil möchten wir die Pixel 0 und 1 in der Farbe Rot aufleuchten lassen.
Hierfür verwenden wir die Programmzeile „BodenLED.setPixelColor“. Den ersten Teil dieses Ausdrucks kennen wir bereits aus Schritt 5. Es handelt sich hierbei um den Namen unseres WS2812B-Objekts.
Der zweite Teil des Ausdrucks lässt sich zu Deutsch mit „lege Pixelfarbwert fest“ übersetzen.
Dir fällt mit einem Blick in den nachfolgenden Programmcode sicherlich auf, dass hinter diesem Ausdruck in der Klammer noch weitere Informationen folgen. Die erste Information legt fest, welchen Pixel wir von unserem Objekt ansteuern möchten. Wir tragen hier die jeweilige Nummer ein, die wir neben dem Pixel auf unserem Funbot ablesen können. Da wir zunächst nur die erste LED aufleuchten lassen möchten, tragen wir hier den Wert „0“ ein.
Mit der zweiten Information „BodenLED.Color()“ legen wir die Leuchtfarbe fest. Die Leuchtfarbe wird dabei als sogenannter RGB Wert angegeben. RGB steht dabei als Abkürzung für die Anfangsbuchstaben der Worte Red, Green und Blue – also Rot, Grün und Blau.
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Adafruit_NeoPixel BodenLED = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
BodenLED.begin();
}
void loop()
{
BodenLED.setPixelColor(0, BodenLED.Color(255, 0, 0));
}
Schritt 8
Die Aufgabenstellung dieser Lektion sieht vor, dass die Pixel 0 und 1 in der Farbe Rot aufleuchten.
Der RGB-Farbwert für die Farbe Rot lautet
R = 255
G = 0
B = 0
Diesen Farbwert tragen wir jetzt in Klammern hinter den Ausdruck „BodenLED.Color“ ein. Die vollständige Programmzeile lautet somit
„BodenLED.setPixelColor(0, BodenLED.Color(255,0,0));.
In dieser Übersicht wird der Befehl noch einmal im Detail erklärt:
BodenLED | .setPixelColor | 0 | BodenLED.Color(255,0,0) |
Name des Objekts | Farbbefehl | Nummer der LED | Farbcode |
Anschließend müssen wir den Pixel noch einschalten. Dafür verwenden wir die Programmzeile „BodenLED.show();“. Dieser Code muss nur dann ausgeführt werden, wenn die vorher programmierten Farbbefehle – es können auch mehrere sein – aktiviert werden sollen.
Lade das gesamte Programm jetzt auf deinen Funbot hoch und beobachte, was passiert!
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Adafruit_NeoPixel BodenLED = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
BodenLED.begin();
}
void loop()
{
BodenLED.setPixelColor(0, BodenLED.Color(255, 0, 0));
BodenLED.show();
}
Schritt 9
Toll gemacht! Das war wirklich nicht einfach!
Wir erinnern und jetzt an unsere Aufgabenstellung zurück. Diese sieht vor, dass die Pixel 0-1 in der Farbe Rot aufleuchten. Die Pixel 2 und 3 sollen in der Farbe Grün und die Pixel 4 und 5 in der Farbe Blau aufleuchten. Die Farbwerte für die Farben Grün und Blau kannst du der nachfolgenden Übersicht entnehmen. Schaffst du es, das Programm eigenständig zu erweitern?
R | G | B | |
ROT | 255 | 0 | 0 |
GRÜN | 0 | 255 | 0 |
BLAU | 0 | 0 | 255 |
Schritt 10
Wenn du alles richtig gemacht hast, dürfte dein Programmcode jetzt so aussehen, wie in der nachfolgenden Abbildung dargestellt.
#include <Adafruit_NeoPixel.h>
#define PIN 13
#define NUMPIXELS 6
Adafruit_NeoPixel BodenLED = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
BodenLED.begin();
}
void loop()
{
BodenLED.setPixelColor(0, BodenLED.Color(255, 0, 0));
BodenLED.setPixelColor(1, BodenLED.Color(255, 0, 0));
BodenLED.setPixelColor(2, BodenLED.Color(0, 255, 0));
BodenLED.setPixelColor(3, BodenLED.Color(0, 255, 0));
BodenLED.setPixelColor(4, BodenLED.Color(0, 0, 255));
BodenLED.setPixelColor(5, BodenLED.Color(0, 0, 255));
BodenLED.show();
}
Versuche jetzt, die einzelnen Pixel in unterschiedlichen Farben leuchten zu lassen. Welche Farbe wird zum Beispiel angezeigt, wenn du die Farbwerte (71, 138, 132) auswählst?
Lagesensor
Aufgabe 1: Position des Funbot im Raum bestimmen
Der MPU6050 Gyroskop- und Beschleunigungssensor ist ein weitverbreiteter und beliebter Sensor, der in der Lage ist, Bewegungsdaten, Beschleunigungsdaten und sogar die Temperatur zu erfassen.
Der Sensor kombiniert ein 3-Achsen-Gyroskop und einen 3-Achsen-Beschleunigungssensor in einem einzigen Chip. Die Sensordaten werden dabei unter anderem über die I2C-Schnittstelle gesendet.
Bei einem Gyroskop handelt es sich um einen Sensor, der zur Messung der Lageposition eines Objektes verwendet wird. Der Sensor kann Änderungen in der X-, Y- und Z-Achsenposition erfassen und diese Messwerte mit der ursprünglichen Position zu vergleichen. Dadurch kann zum Beispiel der Neigungswinkel oder der Rotationswinkel ermittelt werden.
Mit einem Beschleunigungssensor messen wir die Beschleunigung in Bezug auf die X-, Y- und Z-Achse. Wir können dadurch den Grad der Beschleunigung ermitteln, mit dem sich der Funbot bewegt. Die Kombination dieser beiden Sensoren macht den MPU6050-Sensor so besonders: Es ermöglicht die Orientierung in einem dreidimensionalen Raum. Dieser Sensortyp wird genau aus diesem Grund häufig zur Steuerung von Robotern, in VR-Brillen oder zur Flugzeugstabilisierungen in Flugmodellen eingesetzt.
Aufgabe
In dieser Lektion möchten wir das Gyroskop des MPU6050 Sensors verwenden, um die Lageposition um die Z-Achse zu ermitteln. Die erfasste Winkelposition der Z-Achse möchten wir uns im Seriellen Monitor anzeigen lassen.
Programmcode
Schritt 1
Damit wir die Funktionen des MPU6050-Sensors verwenden können, greifen wir auch in dieser Lektion auf eine Bibliothek zurück.
Die Bibliothek „TinyMPU6050.h“ von Gabrial Milan können wir direkt über die Bibliotheksverwaltung zu unserer Arduino IDE hinzufügen. Wir binden die Bibliothek über den bereits bekannten Befehl „#include <TinyMPU6050.h> in unser Programm ein.
In der Lektion mit den WS2812 LEDs haben wir bereits gelernt, was ein Objekt ist und wofür wir Objekte in einem Programm verwenden.
Auch in dieser Lektion verwenden wir ein Objekt, dass wir über die Programmzeile „MPU6050 MPU (Wire);“ erstellen.
Der Ausdruck „MPU6050“ ruft dabei eine Funktion aus der Bibliothek auf, mit welcher wir den Namen für unser Objekt definieren können. Wir wählen in unserem Beispiel den Namen „MPU“. Über den Ausdruck (Wire) wird definiert, dass die Kommunikation über die I2C-Schnittstelle (also die Pins A4 und A5) erfolgen soll.
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
Schritt 2
Anschließend bereiten wir unseren Mikrocontroller auf den Empfang von Messdaten über den I2C-Bus des MPU-6050 vor. Da wir die Messwerte auf dem Seriellen Monitor ausgeben möchten, starten wir über den Befehl „Serial.begin(9600)“ die serielle Kommunikation und legen mit 9600 auch die sogenannte Baudrate (Übertragungsgeschwindigkeit) fest. Bei der seriellen Kommunikation werden die erfassten Werte über das USB-Kabel an den Computer gesendet und können in der Arduino-Software angezeigt werden. Dafür gibt es die Funktion „Serieller Monitor“
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
void setup()
{
MPU.Initialize();
Serial.begin(9600);
}
Schritt 3
Im Eingangstext dieser Lektion haben wir beschrieben, dass das Gyroskop auf unserem MPU6050 die Winkelposition des Sensors im Verhältnis zur ursprünglichen Position erfassen kann.
Damit wir also diese Veränderung der Winkelposition auch messen können, müssen wir den MPU6050 zuerst kalibrieren. Durch diese Kalibrierung wird die ursprüngliche Position gespeichert und wir können den Grad der Drehung um die Z-Achse unseres Funbots messen.
Die Kalibrierung erfolgt ebenfalls durch einen Befehl aus unserer Bibliothek. Der Befehl lautet „MPU.Calibrate();“.
Da die Kalibrierung etwas Zeit in Anspruch nimmt und wir den Funbot in dieser Zeit nicht bewegen dürfen, lassen wir uns mit der Programmzeile „Serial.println();“ im seriellen Monitor anzeigen, wann die Kalibrierung beginnt und wann sie abgeschlossen ist.
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
void setup()
{
MPU.Initialize();
Serial.begin(9600);
Serial.println("Kalibrierung beginnt, bitte warten!");
MPU.Calibrate();
Serial.println("Kalibrierung abgeschlossen");
}
Schritt 4
Wir starten mit der Programmzeile „MPU.Execute();“ . Mit diesem Befehl werden die aktuellen Messdaten des MPU6050-Sensors über die I2C Schnittstelle abgerufen. Dieser Schritt ist notwendig, um die Messdaten im weiteren Programmverlauf verarbeiten zu können.
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
void setup()
{
MPU.Initialize();
Serial.begin(9600);
Serial.println("Kalibrierung beginnt, bitte warten!");
MPU.Calibrate();
Serial.println("Kalibrierung abgeschlossen");
}
void loop()
{
MPU.Execute();
}
Schritt 5
Wir erweitern unseren Loop-Teil um zwei weitere Programmzeilen.
Bei der ersten Programmzeile „Serial.print(„Winkel Z-Achse = „);“ handelt es sich um eine einfache Textausgabe im seriellen Monitor. In die Klammer dieser Programmzeile können wir den Text hinterlegen, der im seriellen Monitor ausgegeben werden soll.
Die zweite Programmzeile „Serial.print(MPU.GetAngZ());“ gibt ebenfalls einen Text im seriellen Monitor aus. Anders als in der ersten Programmzeile, geben wir jetzt allerdings keinen festen Text aus, sondern rufen mit dem Ausdruck „MPU.GetAngZ()“ die aktuelle Winkelposition der Z-Achse des MPU6050 ab.
Mit diesem fünften Schritt können wir die Winkelveränderung der Z-Achse erstmalig einsehen. Höchste Zeit also, den Programmcode einmal auszuprobieren.
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
void setup()
{
MPU.Initialize();
Serial.begin(9600);
Serial.println("Kalibrierung beginnt, bitte warten!");
MPU.Calibrate();
Serial.println("Kalibrierung abgeschlossen");
}
void loop()
{
MPU.Execute();
Serial.print("Winkel Z-Achse = ");
Serial.println(MPU.GetAngZ());
}
Hinweis zur Kalibrierung des MPU6050
Stelle den Funbot während des Uploads deines Programmcodes gerade auf den Tisch. Erst wenn du im seriellen Monitor der Arduino IDE den Ausdruck „Kalibrierung abgeschlossen“ lesen kannst, erhältst du im Anschluss unverfälschte Messwerte.
Schritt 6
Toll gemacht! Wenn du deinen Funbot jetzt nach links und rechts drehst, sollten sich die im seriellen Monitor angezeigten Messwerte verändern.
Funbot winkelgenau fahren lassen
Wie können wir die Messwerte des Lagesensors nutzen, um den Funbot gerade durch den Raum fahren zu lassen?
Für diese Aufgabe müssen die Sketche für den Lagesensor und für die Motoren kombiniert werden, ähnlich wie es bei der Linienverfolgung der Fall war.
Um die Herleitung des Codes etwas abzukürzen, zeigen wir an dieser Stelle ein kommentiertes Beispielprogramm. Die vollständigen Erklärungen befinden sich in den einzelnen Anleitungen zu den Motoren und zum Lagesensor.
// zuerst werden Bibliotheken eingebunden und Variablen erstellt.
// 1. Für den Lagesensor
#include <TinyMPU6050.h>
MPU6050 MPU(Wire);
float Grundstellung = 0;
float Position = 0;
// 2. für den Motor
int MSL = 9;
int MotorLa = 7;
int MotorLb = 8;
int MSR = 6;
int MotorRa = 5;
int MotorRb = 4;
void setup() {
// Im Setup werden die Ein- und Ausgänge definiert
Serial.begin(9600);
pinMode(MSL, OUTPUT);
pinMode(MotorLa, OUTPUT);
pinMode(MotorLb, OUTPUT);
pinMode(MSR, OUTPUT);
pinMode(MotorRa, OUTPUT);
pinMode(MotorRb, OUTPUT);
// Der MPU Sensor wird ebenfalls im Setup initialisiert
MPU.Initialize();
// Wir starten den Seriellen Monitor. Darüber lassen wir uns später die Messwerte des Lagesensors anzeigen.
Serial.begin(9600);
Serial.println("Kalibrierung beginnt, bitte warten!");
MPU.Calibrate();
Serial.println("Kalibrierung abgeschlossen");
// Am Ende des Setup werden einmalig die Sensordaten erfasst.
MPU.Execute();
// Die Ausgangspositiion der Z-Achse wird in der Variablen "Grundeinstellung" gespeichert.
Grundstellung = MPU.GetAngZ();
}
void loop() {
//Im Loop werden zunächst die aktuellen Messwerte des Lagesensors erfasst.
MPU.Execute();
//Die von der Grundstellung abweichende Position berechnet sich aus Grundstellung+Position
Position = Grundstellung + MPU.GetAngZ();
// Der folgende Befehl sendet den Wert der aktuellen Position an den Seriellen Monitor.
Serial.println(Position);
// Nun folgt die IF-ELSE Abfrage. Wenn die Position größer ist als die Grundstellung erfolgt eine Rechtsdrehung
// Info: Gegen den Uhrzeigersinn nimmt der Wert zu, im Uhrzeigersinn wird der Wert kleiner und nimmt auch negative Werte an
if (Position >= Grundstellung) {
//Rechtsdrehung
analogWrite(MSL, 255);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 40);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
}
// Ansonsten
else {
// Erfolgt eine Linksdrehung
analogWrite(MSL, 40);
digitalWrite(MotorLa, LOW);
digitalWrite(MotorLb, HIGH);
analogWrite(MSR, 255);
digitalWrite(MotorRa, LOW);
digitalWrite(MotorRb, HIGH);
}
}