Introduction
Dieser Kurs bietet eine Einführung in Micropython und den ESP32.
Er besteht aus mehreren Modulen welche teilweise aufeinander aufbauen. Jedes Modul ist wiederum in mehrere LAB-Einheiten gegliedert.
Die Struktur finden Sie auf der linken Seite.
Vorraussetzung
Der Kurs bietet keinen Einstieg in die Programmiersprache Python an sich. Grundsätzliche Kenntnisse von Programmierkonzepten und zur Syntax von Python werden vorrausgesetzt. Kennen Sie bereits eine andere Programmiersprache sollte es aber Möglich sein die Syntax von Python nebenher selbstständig mit zu erlernen.
Folgende Konzepte sollten Sie können.
- Variablen
- Bedingungen
- Schleifen
- Funktionen
- Klassen
Als Microcontroller kommt ein ESP32 zum Einsatz. Mehr dazu unter Hardware
Feedback
Bei Fragen, Vorschlägen oder Kritik wenden Sie sich bitte an ...
Resources
Hier finden Sie verschiedene Links zum Thema Mircopython, welche Sie als zusätzliche Quellen nutzen können.
Micropython
Micropython ist eine Python Implementierung die für die Verwendung auf Microcontrollern optimiert ist.
Die Dokumentation ist unter https://docs.micropython.org zu finden. Dort ist auch eine Liste mit allen python modulen zu finden, welche in micropython vorhanden sind.
Mitlerweile gibt es eine große Anzahl an Microcontrollern, auf denen Micropython genutzt werden kann. Hier nur einige Beispiele:
- offizielles pyboard
- ESP8266
- ESP32
- RP PI PICO
- ...
Um die Verwendung von Micropython zu vereinfachen nutzen wir in diesem Kurs die IDE Thonny.
Thonny IDE
Thonny ist eine Python IDE für Beginner, welche es sehr einfach macht einen Microcontroller mit Micropython zu programmieren.
Installieren Sie die Thonny IDE, indem Sie auf die Webseite gehen und die Version für ihr Betriebssystem herunterladen.
Falls Sie schon Erfahrung mit einer anderen IDE und Micropython haben können Sie auch diese verwenden. Jedoch werden alle Beispiele in diesem Kurs mit Thonny gezeigt.
Wokwi – MicroPython im Browser ausprobieren
Wokwi ist eine webbasierte Simulationsplattform für Mikrocontroller-Projekte. Damit kannst du ganz ohne zusätzliche Hardware MicroPython auf virtuellen Boards wie dem ESP32 oder Raspberry Pi Pico testen. Wokwi bietet dir die Möglichkeit, Schaltungen zu erstellen und deine MicroPython-Skripte direkt im Browser auszuführen. Einfach Board auswählen, Schaltkreis designen und deinen Code testen – ideal für schnelle Experimente oder das Lernen ohne physische Hardware.
- Schauen Sie sich das Einführungsvideo an.
- Gehe auf die wokwi webseite und schaue dir einige Beispielprojekte an.
ESP32
Der ESP32 ist ein günstiger (~10€) Microcontroller mit integriertem Wifi und BLE. Das hat ihn zu einem im Hobby und IoT Bereich weit verbreiteten Microcontroller gemacht.
Den ESP32 gibt es in verschiedenen Ausführungen. Für diesen Kurs verwenden wir ein ESP32 DevBoard Kit oder alternativ ESP32 NodeMCU.
Treiber
Damit ihr PC mit dem ESP32 kommunizieren kann müssen Sie unter umständen noch die nötigen Treiber herunterladen und installieren.
Espressif - establish serial connection CP2101 Treiber
Solution
Solution
Nachdem Sie den Treiber installiert haben, sollten Sie im Gerätemanager kein gelbes Dreieck (⚠️) mehr sehen.
Breadboard
Damit Sie die verschiedenen Schaltungen auch aufbauen können, benötigen Sie ein Breadboard und einige Kabel. Falls Sie keines besitzen gibt es diese im Set für wenige Euro.
Falls Sie nicht wissen wie ein Breadboard funktioniert, informieren Sie sich bitte auf dieser Webseite.
Bauteile
Führen Sie diesen Kurs im Rahmen des Unterrichts an der RDF durch, werden Ihnen teile der Hardware gestellt.
Hier finden Sie eine Liste der Bauteile, welche Sie selbst bestellen müssen.
- ESP32 NodeMCU Development Board ~ 6,80€
- Micro USB Kabel ~ 1,70€
- Breadoard mit 830 Kontakten ~ 1,90€
- Jumper Kabel Kit ~ 1,40€
Tutorial
Alle Module und Labs sind so aufgebaut, dass Sie diese selbstständig durcharbeiten können.
Dabei sollen Sie sich selbstständig mit den Präsentierten Inhalten beschäftigen. Diese Seite gibt einen kurzen Überblick über den Aufbau eines Labs.
Informationsphase
Jedes Lab stellt verschiedene Materialien als Informationsquelle bereit. Dies können neben Text auch 🎥 Videos oder andere Formate sein.
Grundsätzlich sollen Sie die dort präsentierten Informationen nachvollzogen und selbst ausprobiert haben. Dies bedeutet:
- Wird ein Hardware Aufbau gezeigt, sollen Sie diesen auch aufbauen.
- Werden Code Beispiele gezeigt, sollen Sie diese auch ausführen.
- ...
Code Beispiele
Viele Labs enthalten Code Beispiele wie zum Beispiel den folgenden.
# Importieren des Moduls time
import time
# Eine Sekunde die Augen zu machen ;)
time.sleep(1)
Fahren Sie mit der Maus über den Code erscheint im rechten Eck ein oder zwei Symbole.
- Das Auge zeigt teilweise versteckten Code an.
- Der kopieren Button kopiert den Code in ihre Zwischenablage. Dabei wird immer der Code kopiert den Sie zu diesem Zeitpunkt auch sehen.
Nicht jedes Code Beispiel ist für sich selber ausführbar. Manche Beispiele enthalten nur Auszüge aus einem größeren Program.
Handlungsphase
Aufgaben
Dies ist eine Aufgabe, welche Sie umsetzen sollen.
👉 Klicken Sie auf Solution um die Lösung anzuzeigen.
Solution
Solution
Zu den meisten Aufgaben finden Sie direkt im Anschluss eine mögliche Lösung.
-
Dies muss nicht immer die einzige richtige Lösung sein.
-
Haben Sie eine andere Lösung und sind sich unsicher fragen Sie gerne nach.
-
Probieren Sie die Aufgabe vorher selber zu lösen bevor Sie die Lösung anschauen.
Abgaben
Teilweise beinhalten die Labs Abgaben, welche Sie einreichen müssen. Dafür sind keine Lösungen vorhanden sondern Sie bekommen eine Rückmeldung vom Kursleiter/Lehrer.Evaluationsphase
Kontrollfragen
Manche Labs beinhalten Kontrollfragen welche, zur Kontrolle des eigenen Lernerfolgs genutzt werden können.
Übungen
Hier finden Sie weitere Übungen zum vertiefen des Gelernten und zur weiteren Vorbereitung. Tauschen Sie sich mit ihren Kollegen aus wenn Sie bei einer Übung nicht weiter kommen. Oder arbeiten Sie gleich zusammen.
- Pairprogramming: Einer denkt der andere tippt. (Abwechseln nicht vergessen)
Feedback
Haben Sie Wünsche, Anregungen oder Kritik, teilen Sie uns diese bitte zeitnah mit.
Einrichtung und Installation von Micropython
Vorkenntnisse:
Ziel:
- Flashen der Micropython Firmware auf den ESP32.
- Benutzen der Micropython REPL.
Micropython vs klassische Microcontroller Programmierung
Programmiert man Microcontroller in C/C++ wird die Firmware wie auf der linken Seite dargestellt auf dem PC geschrieben und mit dem Compiler in Machine Code übersetzt. Diese wird dann in den Flash Speicher des Microcontrollers übertragen (Flashen).
Mit Micropython wird zuerst die Micropython Firmware auf den Microcontroller übertragen. Anschließend läuft auf dem Microcontroller ein Python Interpreter welche die Source Dateien, welche auch auf dem Microcontroller gespeichert werden bei der Ausführung interpretieren kann.
Bevor wir loslegen können, müssen wir also erst die Micropython Firmware auf dem ESP32 installieren.
Installation
Mit Thonny
Mit dem esptool
Dies ist eine ALTERNATIVE zur Installation mit Thonny falls diese nicht funktioniert oder sie nicht mit Thonny arbeiten wollen.
Download der Firmware: Micropython for ESP32
Haben Sie eine andere Version des ESP23, als hier angegeben, benötigen sie auch eine andere Firmware!
ESP Tool installieren
pip install esptool
Verbinden Sie den ESP32 mit dem PC und überprüfen Sie an welchem seriellen Anschluss dieser erreichbar ist.
- Windows: z.B
COM3
- Unix z.B.
/dev/ttyUSB0
oder/dev/ttyACM0
Flash speicher leeren.
esptool.py -p <serial_port> erase_flash
Firmware flashen
python -m esptool --chip esp32 --port <serial_port> write_flash -z 0x1000 <esp32-X.bin>
Sie müssen die folgenden Werte entsprechend ihrer Vorgaben ersetzen.
- <serial_port> ist der Serielle Anschluss über welchen der ESP verbunden ist.
- <esp32-X.bin> ist die Firmware welche Sie heruntergeladen haben.
Getting started
Vorkenntnisse
Ziel:
- Erstes Micropython Program ausführen.
-
Unterschied zwischen
main.py
undboot.py
verstehen.
Vorbereitung
Ein erstes Programm ausführen
REPL
Wie im letzten Video bereits kurz gezeigt, können Sie die REPL nutzen um micropython code auf ihrem esp32 auszuführen. Dies eignet sich vorallem wenn Sie z.B. eine Funktion testen wollen.
Scripts
Wenn Sie Micropython installiert haben, sehen Sie unter Dateien bereits eine Datei boot.py
.
Diese Datei wird nach dem Booten des ESP32 als erstes ausgeführt.
Das kann z.B dazu genutzt werden, um sich mit einem WLAN-Netz zu verbinden.
Die Datei main.py
wird, falls vorhanden, direkt nach der boot.py
aufgerufen und sollte das Hautprogramm beinhalten.
Dies ermöglicht, dass auch nach einem Reset ihr Python Programm wieder zu laufen beginnt.
Sie können natürlich weiterhin nach belieben andere Module einbinden.
In die boot.py
sollten Sie nicht ihr Hauptprogramm einfügen.
Dafür ist die Datei main.py
gedacht.
Editiern Sie die beiden Dateien main.py
und boot.py
folgendermaßen.
main.py
:
print("Hello from main.py")
boot.py
:
print("Hello from boot.py")
Take a break
import time
print("Hello")
time.sleep(1)
print("Bye")
Kopieren Sie diesen Code in die main.py
und führen Sie ihn aus.
- Welche Zeitspanne liegt zwischen der ersten und zweiten Ausgabe?
- Ändern Sie das Beispiel so ab, dass die Zeitspanne halbiert/verdoppelt wird.
Solution
Solution
Die Funktion time.sleep(1)
lässt den Microcontroller für 1 Sekunde pausieren.
time.sleep(0.5)
-> 500mstime.sleep(2)
-> 2s
Ressources
Offiziele Dokumentation zu time
GPIOs
Vorkenntnisse
Ziel:
- Ein- und Ausgänge benutzen.
Jeder Microcontroller hat mehrere GPIO1 Pins. Über diese Pins können exteren Komponenten angeschlossen werden. Je nach Bedarf können diese als Eingang oder Ausgang festgelegt werden.
Pinout
Damit man weiß welcher Pin für was verantwortlich ist, benötigt man das sogenannte Pinout. Hier sehen Sie das Pinout des ESP32 DevKitC V4.2
Achtung! Manche Pins sollten Sie nicht verwenden!
Achtung! Manche Pins sollten Sie nicht verwenden!
Die folgende Tabelle zeigt, welche Pins sicher verwendet werden können und welche mit Vorsicht zu verwenden sind.
GPIO (Label) | Safe? | Reason |
---|---|---|
0 | must be HIGH during boot and LOW for programming (BOOT) | |
1 (TX) | Tx pin, used for flashing and debugging | |
2 | must be LOW during boot and also connected to the on-board LED | |
3 (RX) | Rx pin, used for flashing and debugging | |
4 | ||
5 | must be HIGH during boot (SDIO) | |
6 (CLK) | Connected to Flash memory | |
7 (D0) | Connected to Flash memory | |
8 (D1) | Connected to Flash memory | |
9 (D2) | Connected to Flash memory | |
10 (D3) | Connected to Flash memory | |
11 (CMD) | Connected to Flash memory | |
12 | must be LOW during boot (VDD_FLASH) | |
13 | ||
14 | ||
15 | must be HIGH during boot, prevents startup log if pulled LOW (LOG) | |
16 | ||
17 | ||
18 | ||
19 | ||
21 | ||
22 | ||
23 | ||
25 | ||
26 | ||
27 | ||
32 | ||
33 | ||
34 | Input only GPIO, cannot be configured as output | |
35 | Input only GPIO, cannot be configured as output | |
36 (VP) | Input only GPIO, cannot be configured as output | |
39 (VN) | Input only GPIO, cannot be configured as output |
Quelle: lastminuteengineers
Pins nutzen
Damit wir in Micropython Pins als Eingang oder Ausgang definieren können müssen wir zunächst die Klasse Pin
aus dem Module machine
einbinden.
from machine import Pin
Der Konstruktor der Klasse Pin
, muss die Nummer des GPIO Pins aus dem Pinout übergeben werden.
from machine import Pin
pin = Pin(19)
In diesem Beispiel wird ein Object für den Pin mit der Nummer 19
angelegt.
Ausgang / Output
Um einen Pin als Ausgang festzulegen können wir dem Konstruktor Pin.OUT
als zweites Argument übergeben.
from machine import Pin
output_pin = Pin(19, Pin.OUT)
Zustand des Pins ändern
Um den gewünschten Pegel am Ausgang zu setzen bietet Micropython zwei Möglichkeiten.
Möglichkeit 1:
from machine import Pin
output_pin = Pin(19, Pin.OUT)
# Setzen des Pegels auf HIGH.
output_pin.on()
# Setzen des Pegels auf LOW
output_pin.off()
- Die Methode
on()
setzt den Zustand des Pins aufHIGH
. - Die Methode
off()
setzt den Zustand des Pins aufLOW
.
Möglichkeit 2:
from machine import Pin
output_pin = Pin(19, Pin.OUT)
# Setzen des Pegels auf HIGH.
output_pin.value(1)
# Setzen des Pegels auf LOW
output_pin.value(0)
Sie können auch die Methode value(x)
verwenden um den Zustand zu setzen.
Eingang / Input
Um einen Pin als Eingang festzulegen können wir dem Konstruktor Pin.IN
als zweites Argument übergeben.
from machine import Pin
input_pin = Pin(18, Pin.IN)
Zustand des Pins abfragen
Ist ein Pin als Eingang definiert, können wir die Methode value()
nutzen um den aktuellen Zustand am Pin abzufragen.
from machine import Pin
input_pin = Pin(18, Pin.IN)
current_value = input_pin.value()
Im gegebenen Beispiel wird der Zustand in der Variable current_value
gespeichert.
Führen Sie folgendes Program aus.
from machine import Pin
from time import sleep
input_pin = Pin(17, Pin.IN)
while True:
current_value = input_pin.value()
print(current_value)
sleep(0.5)
Verbinden Sie GPIO17 mit einer Drahtbrücke zu
- GND (0V) und anschließend
- VCC (3.3V).
Welche Werte lesen Sie jeweils?
WARNUNG: Verbinden Sie den Pin niemals mit einer Eingangsspannung von 5V. Dies könnte den Pin oder den ganzen Chip zerstören.
Besondere Pins
Achtung manche Pins auf dem ESP32 DevKit werden auch für andere Funktionalitäten genutzt.
- Pins 1 and 3 are REPL UART TX and RX respectively
- Pins 6, 7, 8, 9, 10 and 11 are used for connecting the embedded flash, and are not recommended for other uses.
- Pins 34-39 are input only, and also do not have internal pull-up resistors
- See Deep-sleep mode for a discussion of pin behaviour during sleep
Übungen
Schreiben Sie eine Funktion welche den Zustand eines Pins toggelt3.
Kontrollfragen
- Wofür steht der Begriff GPIO?
- Welche Informationen sind im Pinout zu finden?
- Wie legen Sie einen Pin als Ausgang fest?
- Wie können Sie den Zustand eines Pins ändern(HIGH | LOW)?
- Wie legen Sie einen Pin als Eingang fest?
- Wie können Sie den Zustand eines Pins abfragen?
- Ist es möglich den Zustand eines als Ausgang festgelegten Pins zu lesen?
- Ist es möglich den Zustand eines als Eingang festgelegten Pins auf HIGH | LOW zu setzten?
Ressources
GPIO steht für General Purpose Input Output.
Quelle: Espressif
Toggeln bedeutet umschalten, dh. wenn vorher HIGH setzte auf LOW und wenn vorher LOW setze auf HIGH.
Blink a LED
Dieses Lab zeigt Ihnen wie sie eine LED mit dem Microcontroller blinken lassen können.
Vorkenntnisse:
Ziel:
- LED mit MCU verbinden.
- Blockierendes Blinken einer LED
Die LED
weitere Informationen zur LED finden Sie hier.
Aufbau
LED an und aus schalten.
Um die LED mit Micropython anzusteuern müssen Sie den Pin, welcher mit der LED verbunden ist als Ausgang definieren. Dies haben Sie bereits im Lab GPIO Pins gelernt.
Beispiel:
from machine import Pin
# LED als Ausgang festlegen.
led_pin = Pin(16, Pin.OUT)
# LED anschalten
led_pin.on()
Time to blink
Schreiben Sie ein Programm welches die LED mit Hilfe der Sleep Funktion im Takt von 500ms blinken lässt. Das heißt die LED ist 500ms an und danach 500ms aus.
Solution
Solution
Variante 1:
import time
from machine import Pin
led_pin = Pin(16, Pin.OUT)
while True:
led_pin.on()
time.sleep(0.5)
led_pin.off()
time.sleep(0.5)
Variante 2:
import time
from machine import Pin
led_pin = Pin(16, Pin.OUT)
while True:
led_pin.value(not led_pin.value())
time.sleep(0.5)
Kontrollfragen
- Welcher Pol der LED muss an GND angeschlossen werden?
- Warum benötigen Sie einen Vorwiderstand für die LED?
- Wie groß sollten Sie den Vorwiderstand mindestens wählen?
- Wie können Sie eine LED mit Micropython anschalten?
- Wie können Sie eine LED mit Micropython blinken lassen?
Übungsaufgaben
- Lassen Sie die LED abwechselnd 3x blinken und anschließend für 3s leuchten.
- Erstellen Sie eine Ampelschaltung, welche die 3 LEDs in der richtigen Abfolge leuchten lässt.
- Erstellen Sie eine Klasse LED, mit den Methoden
on
,off
undtoggle
.
Buttons
Dieses Lab zeigt Ihnen wie sie einen Taster als User Input nutzen können.
Vorkenntnisse:
Ziel:
- Button mit MCU verbinden (Pullup/Pulldown).
- Zustand eines Tasters einlesen
Taster (Push Button)
Ein Taster, nicht zu verwechseln mit einem Schalter ist ein mechanisches Bauteil, welches als Eingang für einen Microcontroller genutzt werden kann.
Aufbau
Zustand einlesen
from machine import Pin
btn = Pin(27, Pin.IN, Pin.PULL_UP)
while True:
current_btn_state = btn.value()
print(current_btn_state)
Der Ausdruck Pin.PULL_UP
aktiviert den internen Pullup-Widerstand des ESP32.
Dadurch liegt ein HIGH-Pegel am Pin an, wenn der Taster nicht gedrückt ist und ein LOW-Pegel wenn der Taster gedrückt wird.
Ein Taster sollte immer mit einem Pullup oder Pulldown Widerstand verwendet werden, da der Spannungszustand des Taster-Pins sonst nicht stabil ist und der Zustand des Eingangspings sich zufällig ändern kann.
Schreiben Sie ein Programm welches eine LED einschaltet solange der Taster gedrückt ist. Und die LED wieder ausschaltet falls der Taster nicht mehr gedrückt ist.
Solution
Solution
from machine import Pin
led = Pin(14, Pin.OUT)
btn = Pin(27, Pin.IN, Pin.PULL_UP)
while True:
if 0 == btn.value():
led.on()
else:
led.off()
⭐ Wokwi
Tastendruck erkennen
Nun wollen wir nicht immer den Taster gedrückt halten nur damit unsere LED leuchtet. Wir brauchen also eine Möglichkeit zu erkennen ob der Taster gedrückt wurde.
flowchart TB setup["`**Setup** Pins and init *last* state to HIGH `"] check["`Check *current* button state`"] if_edge{" "} do["`set *last* state = *current* state`"] if_falling{"is current state LOW"} toggle["`*toggle LED*`"] setup --> check check --> if_edge if_edge -- last != current --> check if_edge -- last == current --> do do --> if_falling if_falling -- true --> toggle if_falling -- false --> check
Betrachten Sie folgendes Diagram1 welches den Ablauf darstellt. Versuchen Sie dies in Code umzusetzen.
Solution
Solution
from machine import Pin
led = Pin(14, Pin.OUT)
btn = Pin(27, Pin.IN, Pin.PULL_UP)
# Set btn state to HIGH as btn is low active.
last_btn_state = 1
while True:
current_btn_state = btn.value()
if current_btn_state != last_btn_state:
last_btn_state = current_btn_state
if current_btn_state == 0:
led.value(not led.value())
Kontrollfragen
-
Was passiert, wenn ein Taster direkt (also ohne Pullup-Widerstand) mit dem Microkontroller verbunden wird.
-
Ein Taster ist mit einem Pullup-Widerstand verbunden. Welches Level (HIGH oder LOW) liegt am Pin an, wenn der Taster offen bzw. geschlossen ist? Erläutern Sie Ihre Antwort.
-
Ein Taster ist mit einen Pull-Down-Widerstand angeschlossen. Begründen Sie, welchen Zustand (Spannung) der angeschlossene Pin aufweist, wenn der Taster a) geschlossen und b) geöffnet ist.
Diese Art von Diagram wird auch als Ablaufdiagramm oder Flowchart Diagramm bezeichnet.
Poti
Dieses Lab zeigt Ihnen wie sie die wechselnde Spannung von einem Potentiometer als User Input nutzen können.
Vorkenntnisse:
Ziel:
- Poti verbinden
- ADC nutzen um Spannung zu messen.
- Wertebereich umrechnen.
Das Potentiometer
Ein Potentiometer wirkt wie ein verstellbarer Spannungsteiler, der die Spannung zwischen GND und VCC in Abhängigkeit von der Position des Schleifkontakts teilt. Durch Drehen des Potentiometers kann die Ausgangsspannung stufenlos verändert und an verschiedene Anforderungen angepasst werden
Aufbau
Bauen Sie die abgebildete Schaltung auf. (Wokwi)
Analog Digital Wandler
Zuerst müssen wir die Klasse ADC
aus dem Modul machine
importieren.
from machine import ADC
Der Konstruktor benötigt nur die Pin nummer.
from machine import ADC
adc = ADC(32)
Nicht jeder beliebige Pin kann als ADC verwendet werden. Welche Pins als ADC genutzt werden können steht im Pinout oder im Datenblatt des entprechenden Microcontrollers.
ADC-Block 2 wird auch von WiFi verwendet. Der Versuch, Analogwerte von den Pins des Blocks 2 zu lesen, wenn WiFi aktiv ist, führt daher zu einer Exception.
Der ADC bietet uns zwei verschieden Methoden um den aktuellen Wert am Pin zu lesen.
from machine import ADC
adc = ADC(32)
value = adc.read_u16() # Wert zwischen 0 und 2^16
microvolts = adc.read_uv() # Wert in microvolts
Die interne ADC-Referenzspannung beträgt typischerweise 1,1 V und hat eine minimale Messspannung um 100mV, Spannungen <= diesem Wert werden als 0 gelesen.
Schreiben Sie ein Program welches den Wert vom Poti einliest und jede Sekunde den aktuellen Wert über die serielle Schnittstelle wieder ausgibt.
Solution
Solution
from machine import ADC
from time import sleep
poti = ADC(32)
while True:
value = poti.read_u16()
print(f"Aktueller Wert: {value}")
sleep(1)
Schreiben Sie ein Program welches es ermöglicht eine LED über den Poti ein und aus zu schalten. Die Grenze soll genau in der Mitte des Wertebereichs liegen.
Solution
Solution
from machine import ADC, Pin
poti = ADC(32)
led = Pin(27, Pin.OUT)
while True:
value = poti.read_u16()
# Hälfte von 2¹⁶ = 32768
if value > 32768:
led.on()
else:
led.off()
Messbereich auf Steuerungsbereich mappen
Ein häufiges Problem im Zusammenhang mit dem Einlesen analoger Werte ist das Umrechnen dieser Werte in einen anderen Wertebereich. Als Beispiel soll mit einem Potentiometer eine LED gedimmt werden. Wir haben nun gesehen, dass wir Werte zwischen 0 und 2^16 einlesen können. Zum Dimmen der LED benötigen wir nun einen Wertebereich zwischen 0 und 255.
Der gewünschte Wertebereich hat also 256 Schritte. Wir müssen nun also den ursprünglichen Bereich in 256 Schritte unterteilen.
Ursprungsbereich | Zielbereich |
---|---|
0 - 255 | 0 |
256 - 512 | 1 |
513 - 768 | 2 |
... | ... |
65024 - 65279 | 254 |
65280 - 65535 | 255 |
Foglende Formel kann für die Umrechnung genutzt werden: Dabei ist der Ursprungsbereich und der Zielwertebereich.
Schritt für Schritt Erklärung
Schritt für Schritt Erklärung
- Normalisierung auf den Bereich Hier wird der Eingabewert relativ zu seinem Ursprungsbereich skaliert.
- Skalierung auf den Zielbereich: Das Ergebnis der Normalisierung wird mit der Größe des Zielbereichs multipliziert.
- Verschiebung in den Zielbereich: Schließlich wird das Ergebnis an den Startpunkt (min_out) des Zielbereichs angepasst.
Beispiel
Beispiel
Sie wollen den Wert 30 aus dem Wertebereich in den Zielbereich umrechnen.
-
Normalisierung auf den Bereich
-
Skalierung auf den Zielbereich: Hinweis: Beginnt der Zielbereich bei wären wir schon fertig.
-
Verschiebung in den Zielbereich:
Das Ergebnis ist ✅
👉 Schreiben Sie eine Funktion map(value, min_in, max_in, min_out, max_out)
welche diese in Code umsetzt.
Hinweis: Achten Sie darauf, dass sie nicht durch NULL teilen dürfen.
Tip
Tip
Testen Sie ihre Funktion z.B mit folgenden Werten:
Value | Expected Output |
---|---|
65279 | 254,99609375 |
65280 | 255 |
Solution
Solution
def map(value, min_in, max_in, min_out, max_out):
if min_in == max_in:
raise ValueError("Der Eingabebereich darf nicht null sein.")
return (value - min_in)/(max_in - min_in) * (max_out - min_out) + min_out
Diese Funktion gibt einen float
zurück. Oft brauchen wir jedoch nur den ganzzahligen Anteil (int
) zum weiterrechnen. Dies können Sie zum Beispiel durch eine Typumwandlung int(output)
oder eine Integer Division output//1
erreichen.
Übungsaufgaben
-
Schreiben Sie ein Program, dass den Poti nutzt um 3 Zustände (AUS, AN, BLINKEN) einer LED auszuwählen.
-
Schreiben Sie ein Program, dass die Blinkfrequenz einer LED über einen Poti einstellbar macht.
-
Bauen Sie einen Spannungsteiler aus 220 Ω und 1kΩ auf. Berechnen Sie den Wert welcher vom ADC eingelesen wird und überprüfen Sie dies mit einem kleinen Program nach. Analysieren Sie mögliche Abweichungen des Messwertes von dem theoretischen Ergebnis.
-
Der Ladestand eines Akkus soll über 5 LEDs optisch dargestellt werden. Nutzen Sie einen Poti um die Spannung des Akkus zu simulieren. Programmieren Sie Ihre Schaltung so, dass proportional zur Stellung des Potentiometers die Anzahl der leuchtenden LEDs variiert. Nutzen Sie die eine
for
-Schleife. -
Begründen Sie warum Sie einen 9V Akku nicht einfach an den ADC Pin anschließen dürfen. Skizzieren Sie anschließend eine mögliche Lösung um den Akkustand trotzdem zu messen.
-
Werte, die über einen analogen Eingang eingelesen wurden, sollen auf dem seriellen Monitor mit einer Art Balkenanzeige dargestellt werden. Je größer der eingelesen Wert, desto mehr > werden dargestellt. Die Balkenanzeige soll 25 Balken haben und das Maximum wird durch | angezeigt. Mit einem angeschlossenen Potentiometer soll die Balkenanzeige gesteuert werden.2
Resources
PWM
Dieses Lab erklärt Ihnen was ein PWM Signal ist und wie sie dieses Verwenden können um eine LED zu dimmen.
Vorkenntnisse:
Ziel:
- PWM Signale verstehen.
- Anwendung des PWM signals.
PWM Signale
Um LEDs nicht nur an- oder auszuschalten, sondern ihre Helligkeit stufenlos anzupassen, kann eine Pulsweitenmodulation (PWM) verwendet werden. Mit dieser Methode lässt sich ein digitales Signal so erzeugen, dass es wie ein analoges wirkt. Dies geschieht durch ein schnelles Umschalten des Ausgangsports zwischen den Zuständen LOW und HIGH. Dabei sind die Frequenz des Umschaltvorgangs und das Tastverhältnis (Duty Cycle) wichtige Faktoren.
Weitere Informationen zu PWM Signalen finden sie z.B im Elektronik Kompendium.
LEDs dimmen
Um PWM Signale mit Micropython zu nutzen müssen wir die Klasse Pin
und PWM
importieren.
from machine import Pin, PWM
Als nächstes legen wir unseren led Pin an und erzeugen anschließend ein Objekt der Klasse PWM
.
Der Konstruktor erwartet mindestens ein Argument vom Typ Pin
, z.B. unser led_pin
.
PWM kann an allen ausgangsfähigen Pins aktiviert werden.
from machine import Pin, PWM
led_pin = Pin(25) # create Pin object from a pin_number
pwm = PWM(led_pin) # create PWM object from a pin
Um die Frequenz und das Tastverhältnis festzulegen gibt es die zwei Methoden freq(x)
und duty(x)
Im Beispiel wird eine Frequenz von 5000 Herz eingestellt, was für das Dimmen einer LED ausreichend ist.
Am ESP32 kann die Frequenz von 1Hz bis 40MHz eingestellt werden, aber wenn die Frequenz steigt, sinkt die Auflösung des Tastverhältnisses.
from machine import Pin, PWM
led_pin = Pin(25) # create Pin object from a pin_number
pwm = PWM(led_pin) # create PWM object from a pin
pwm.freq(5000)
pwm.duty(512)
Die PWM hat eine Auflösung von 10 Bit. Damit sind 2^10 = 1024 Werte möglich. Wir können also Werte zwischen 0 und 1023 als Argument übergeben.
- 0 entspricht einer Impulsdauer von 0%, also LED aus.
- 1023 entspricht einer Impulsdauer von 100%, also LED an.
Der Wert 512 entspricht also einer Impulsdauer von 50%, also LED auf halbe Helligkeit.
Effekte (Fading)
Wir wollen nun unsere LED langsam ein und wieder ausschalten.
from machine import Pin, PWM
from time import sleep_ms
led_pin = Pin(25) # create Pin object from a pin_number
pwm = PWM(led_pin, freq=5000) # create PWM object from a pin
TIME_DELAY = 5
while True:
for duty_cycle in range(0,1024):
pwm.duty(duty_cycle)
sleep_ms(TIME_DELAY)
Folgendes Program nutzt eine for
Schleife um den Dutycyle von 0% auf 100% zu erhöhen.
Damit dies nicht zu schnell geschieht und wir den Prozess gut erkennen können fügen wir einen kleine delay in jedem Schleifendurchlauf ein.
Probieren Sie gerne andere Zeitwerte aus und sehen Sie die Veränderung.
Übungsaufgaben
-
Mit dem folgenden Befehl wird ein PWM-Signal erzeugt:
pwm = PWM(pin, freq=9, duty=213)
. Bestimmen Sie das Tastverhältnis des PWM-Signals in Prozent. Berechnen Sie die Zeit des Impulses. -
Welchen Wert muss der Parameter für das Tastverhältnis in der Funktion
duty()
annehmen, damit sich ein Tastverhältnis von 76% ergibt. -
Sie wollen ein PWM Signal mit einer Periodendauer von 20ms und einem Impuls von 1.5ms erzeugen. Geben Sie die den entsprechenden Code an.
-
Schreiben Sie ein Programm, welches über einen Poti die Helligkeit einer LED ändert.
-
Erklären Sie wie sie die gleiche Funktionalität auch ohne Microcontroller erreichen.
-
Schreiben Sie ein Programm für ein futuristisches Lauflicht mit 6 LEDs. Dabei sollen zu jedem Zeitpunkt immer zwei bis drei LEDs mit verschiedenen Helligkeitswerten leuchten. Die erste LED soll möglichst hell und die nachfolgenden LEDs etwas dunkler leuchten. Hierdurch soll der Effekt entstehen, dass der ersten LED eine Art Leuchtspur folgt. Das Lauflicht soll kontinuierlich hin- und herwandern.
-
Zeigen Sie rechnerisch anhand von zwei Beispielen was mit diesem Satz gemeint ist: "Wenn die Frequenz steigt, sinkt die Auflösung des Tastverhältnisses."
Ressources
Controlling a motor
Vorkenntnisse
Um einen Motor anzusteuern könnten Sie auf die Idee kommen diesen, wie eine LED direkt an einen GPIO-Pin anzuschließen. Dies ist jedoch eine sehr schlechte Idee. Hier wollen wir uns anschauen warum und welche Lösung es dafür gibt.
Leistungsaufnahme eines Motors.
Sie können diese Motoren mit 3V bis 6V betreiben, wobei sie bei höheren Spannungen natürlich etwas schneller laufen. Wenn Sie ins Datenblatt schauen oder selbst Messungen durchführen können folgende Werte ermittelt werden:
- Bei 3 V wurden 150 mA bei 120 U/min im Leerlauf und 1,1 A im Stillstand (Motor blockiert) gemessen.
- Bei 4,5 V wurden 155 mA bei 185 Umdrehungen pro Minute ohne Last und 1,2 Ampere im Stillstand gemessen.
- Bei 6 V wurden 160 mA bei 250 Umdrehungen pro Minute ohne Last und 1,5 Ampere im Stillstand gemessen.
Probleme
Wenn Sie die Werte genauer ansehen, ist Ihnen eventuell aufgefallen, dass unser ESP32 nicht mit 6V sondern nur 3.3V arbeitet. Wir könnten den Motor also garnicht mit voller Geschwindigkeit laufen lassen.
Das zweite, und viel größere Problem ist jedoch, dass ein GPIO-Pin des Microcontrollers max 40mA liefern kann.
Zieht unser Motor einen Strom von 150mA kann das dazu führen, dass der GPIO-Pin dauerhaft Beschädigt wird.
Lösung
Je nach Anwendungsfall gibt es verschiedene Lösungsmöglichkeiten für dieses Problem.
- Nutzen eines Transistors zur Ansteuerung
- Nutzen einer H-Brücke zur Ansteuerung
- Nutzen eines Motor-Treiber-ICs
Transistorschaltung
Die meisten Microcontroller können maximal 20 - 40mA bei 3.3V/5V pro Ausgangspin liefern. Dies reicht für DC-Motoren in der Regel nicht aus. Man benötigt mindestens eine einfache Transitorschaltung um den Motor anzusteuern. Transistoren sind kleine elektronische Halbleiter-Bauteile, um Schaltkreise zu steuern und Signale zu verstärken.
Transitoren gibt es in verschiedenen Typen (FET und BJT). Welcher Transitor am besten für einen konkreten Anwendungsfall geeignet ist, ist nicht Teil dieses Tutorials. Für die hier gezeigte Beispielschaltung wird ein NPN-Bipolartransitor verwendet.
Hier finden Sie ein kurzes Erklärvideo als Übersicht zum Transitor.
Schaltplan
Ansteuerung
Sie können den Motor nun über den Ausgangspin steuern.
LOW
schaltet den Motor aus.HIGH
schalten den Motor ein (Fullspeed).- Mit einem
PWM
Signal kann die Geschwindigkeit gesteuert werden.
Wenn Sie genug Zeit haben bauen Sie die Schaltung auf und programmieren eine einfache Steuerung.
Kontrollfragen
- Für welche Anwendungsfälle ist diese Schaltung geeignet?
H-Brücke
Mit der Transitorschaltung und einem PWM Signal können wir den Motor zwar steuern aber immer nur in eine Richtung drehen. Damit ein Fahrzeug auch rückwärts fahren kann, muss eine Möglichkeit geschaffen werden, die Drehrichtung gezielt zu steuern.
Dies lässt sich mit einer H-Brücke umsetzen.
Schaltplan
Bauen Sie die Schaltung auf und programmieren eine einfache Steuerung, welche die Drehrichtung des Motors ändern kann. Zum Beispiel über einen Taster.
Motor Driver ICs
Controlling a motor
Vorkenntnisse
Ziel:
- Motorsteuerungen nutzen
Neben den beiden zuvor vorgestellten Lösungen gibt es auf dem Markt auch eine Reihe von fertigen ICs für die Motoransteuerung.
Bekannte Motortreiber sind z.B der L298N
, L293D
, DRV8833
, TB6612FNG
, ...
LN293D
vs DRV8833
Strom und Spannung:
L293D
: Bis 36V, max 600mA pro Kanal (kurzfristig: 1.2 A)DRV8833
: 2.7V - 10.8V, max 1.5A pro Kanal (kurzfristig: 2A)
Wirkungsgrad:
LN293D
: Bipolartransistoren (hohe Wärmeentwicklung, Spannungsverlust ~1.5V)DRV8833
: MOSFET-Technologie (weniger Hitzeentwicklung, niedriger Spannungsabfall)
Fazit: Der DRV8833 ist erheblich effizienter und bleibt kühler.
Preis:
L293D
: Tendenziell günstiger, aber mit dem Nachteil der geringeren EffizienzDRV8833
: Etwas teurer, aber modernes Design und bessere Leistung
Solution
Solution
L293D
: Wenn du höhere Spannungen (z. B. 24 V) brauchst und Effizienz nicht so wichtig ist.DRV8833
: Für batteriebetriebene Anwendungen, kleine Motoren (bis 10 V) besser geeignet.
L293D nutzen
Der L293D ist ein Dual H-Brücke Motortreiber, der zwei Motoren in beide Richtungen steuern kann. Das Pinout liefert uns die nötigen Informationen zu den Anschlüssen des ICs.
Power Pins:
VSS
: Versorgungsspannung für die Logik (5V)VS
: Versorgungsspannung für die Motoren (4.5V - 36V)
Richtung festlegen:
Die Richtung des Motors wird durch die Pins IN1
und IN2
festgelegt.
IN1 | IN2 | Motor |
---|---|---|
LOW | LOW | Stop |
HIGH | LOW | Vorwärts |
LOW | HIGH | Rückwärts |
HIGH | HIGH | Stop |
Geschwindigkeit festlegen:
Über den Pin EN A
kann die Geschwindigkeit des Motors eingestellt werden.
- Ist der Pin
EN A
LOW, stoppt der Motor. - Ist der Pin
EN A
HIGH, läuft der Motor mit voller Geschwindigkeit. - Für eine Variable Geschwindigkeit kann der Pin
EN A
auch mit einem PWM-Signal angesteuert werden.
Alternative
Alternative
Sie können auch den EN1
Pin dauerhaft auf HIGH setzen und die Geschwindigkeit über die Pins IN1
und IN2
steuern.
Ausgangssignale:
An den beiden Ausgangssignalen OUT1
und OUT2
liegt die Spannung an, die den Motor antreibt.
DRV8833 nutzen
Dieser Motortreiber hat ebenfalls zwei H-Brücken integriert. Dies erkennt man auch wieder am Pinout.
Neben dem reinen Chip, welcher nicht als Breadboard kompatibles Design verfügbar ist, gibt es auch Module, auf denen der Chip bereits verbaut ist. Der Anschluss solcher Module ist dann relativ einfach.
Die Pins 5 und 6 können auch durch andere Pins am ESP32 ersetzt werden.1
Wie sie sehen gibt es hier keinen enable Pin. Sie steuern die Richtung und Geschwindigkeit des Motors über die Pins IN1
, IN2
indem Sie ein PWM
Signal verwenden.
Bildquelle: rule the bot
Distanz Sensoren
Wenn Sie die Distanz zu einem Hinderniss messen wollen gibt es verschiedene Optionen:
Ultraschall
Vorkenntnisse
Ziel:
- Funktionsweise eines Ultraschall Sensors kennenlernen.
- Vorgefertigte Bibliothek nutzen.
- HC-SR04 Ultraschall Sensor auslesen.
Funktionsweise
Der Ultraschallsensor HC-SR04 verwendet Sonar zur Bestimmung der Entfernung zu einem Objekt. Der Sensor funktioniert in einem Bereich von 2cm - 4m, mit einer Genauigkeit von 0,3cm.
- Der Ultraschallsender (Triggerstift) sendet einen hochfrequenten Ton (40 kHz) aus.
- Der Schall wandert durch die Luft. Wenn er auf ein Objekt trifft, wird er zum Modul zurückgeworfen.
- Der Ultraschallempfänger (Echo-Pin) empfängt den reflektierten Schall (Echo).
Unter Berücksichtigung der Geschwindigkeit des Schalls in der Luft und der Laufzeit (Zeit, die seit der Übertragung und dem Empfang des Signals verstrichen ist) können wir die Entfernung zu einem Objekt berechnen. Hier ist die Formel:
Bei 20° ist die Schallgeschwindigkeit in der Luft 343
Verwendung
Ok wir wissen nun wie der Sensor in der Theorie funktioniert, schauen wir uns nun die Praxis an.
Aufbau:
Um das Modul zu verwenden müssen wir es zunächst an den ESP32 anschließen. Im folgenden sehen Sie eine mögliche Verdrahtung.
Sie können den TRIGGER
und ECHO
Pin auch an zwei andere Pins (z.B GPIO 5
und GPIO 18
) anschließen.
Dies müssen Sie dementsprechend später im Programm berücksichtigen.
Hier finden Sie den Aufbau auf ⭐ Wokwi
Programmcode
Ok, verkabelt ist. Nun müssen wir also ein Signal über den TRIGGER
Pin senden und am ECHO
Pin messen wie lange es dauert bis das Signal zurück kommt. Anschließend noch mit der Formel die Zeit in cm umrechnen und schon haben wir unseren Abstand.
Wir könnten uns nun die Mühe machen, das alles selbst zu programmieren, aber zum Glück hat schon jemand anderes sich für uns die Arbeit gemacht. Im folgenden finden Sie eine Bibliothek (python Datei) welche wir in unser Projekt einbinden können.
HC-SR04 Bibliothek (hcrs04.py)
HC-SR04 Bibliothek (hcrs04.py)
import machine, time
from machine import Pin
__version__ = '0.2.0'
__author__ = 'Roberto Sánchez'
__license__ = "Apache License 2.0. https://www.apache.org/licenses/LICENSE-2.0"
class HCSR04:
"""
Driver to use the untrasonic sensor HC-SR04.
The sensor range is between 2cm and 4m.
The timeouts received listening to echo pin are converted to OSError('Out of range')
"""
# echo_timeout_us is based in chip range limit (400cm)
def __init__(self, trigger_pin, echo_pin, echo_timeout_us=500*2*30):
"""
trigger_pin: Output pin to send pulses
echo_pin: Readonly pin to measure the distance. The pin should be protected with 1k resistor
echo_timeout_us: Timeout in microseconds to listen to echo pin.
By default is based in sensor limit range (4m)
"""
self.echo_timeout_us = echo_timeout_us
# Init trigger pin (out)
self.trigger = Pin(trigger_pin, mode=Pin.OUT, pull=None)
self.trigger.value(0)
# Init echo pin (in)
self.echo = Pin(echo_pin, mode=Pin.IN, pull=None)
def _send_pulse_and_wait(self):
"""
Send the pulse to trigger and listen on echo pin.
We use the method `machine.time_pulse_us()` to get the microseconds until the echo is received.
"""
self.trigger.value(0) # Stabilize the sensor
time.sleep_us(5)
self.trigger.value(1)
# Send a 10us pulse.
time.sleep_us(10)
self.trigger.value(0)
try:
pulse_time = machine.time_pulse_us(self.echo, 1, self.echo_timeout_us)
return pulse_time
except OSError as ex:
if ex.args[0] == 110: # 110 = ETIMEDOUT
raise OSError('Out of range')
raise ex
def distance_mm(self):
"""
Get the distance in milimeters without floating point operations.
"""
pulse_time = self._send_pulse_and_wait()
# To calculate the distance we get the pulse_time and divide it by 2
# (the pulse walk the distance twice) and by 29.1 becasue
# the sound speed on air (343.2 m/s), that It's equivalent to
# 0.34320 mm/us that is 1mm each 2.91us
# pulse_time // 2 // 2.91 -> pulse_time // 5.82 -> pulse_time * 100 // 582
mm = pulse_time * 100 // 582
return mm
def distance_cm(self):
"""
Get the distance in centimeters with floating point operations.
It returns a float
"""
pulse_time = self._send_pulse_and_wait()
# To calculate the distance we get the pulse_time and divide it by 2
# (the pulse walk the distance twice) and by 29.1 becasue
# the sound speed on air (343.2 m/s), that It's equivalent to
# 0.034320 cm/us that is 1cm each 29.1us
cms = (pulse_time / 2) / 29.1
return cms
Sie können sich gerne einmal den Quellcode der Bibliothek anschauen und versuchen nachzuvollziehen. Aber das wichtige für uns ist wie man die Bibliothek nutzen kann und nicht sie in allen Einzelheiten zu verstehen.
Bibliothek einbinden
Damit wir die Bibliothek nutzen können müssen wir sie zuerst in unser Projekt einbinden. Gehen Sie dazu wie folgt vor.
- Öffnen Sie Thonny und stecken Sie den ESP32 an.
- Erstellen Sie eine neue Datei und kopieren Sie den kompletten Inhalt der Bibliothek in diese Datei.
- Speichern Sie diese Datei unter dem Namen
hcrs04.py
auf dem Micropython Device.
Danach sollten Sie die neue Datei auf dem Gerät sehen können.
Bibliothek nutzen
Als nächsten Schritt müssen wir die Bibliothek in unsere main.py
einbinden.
from hcsr04 import HCSR04
Anschließend können wir ein Objekt der Klasse HCSR04
erzeugen.
sensor = HCSR04(trigger_pin=26, echo_pin=25, echo_timeout_us=20000)
Das Objekt speichern wir in der Variablen sensor
. Der Konstruktor benötigt 3 Argumente.
Die Pin-Nummer, welche mit dem TRIGGER
verbunden ist. Die Pin-Nummer, welche mit dem ECHO
verbunden ist. Sowie eine Zeit nachdem die Messung abgebrochen wird.
Dann können wir in unserer Endlosschleife die Methode distance_cm()
aufrufen, welche uns die gemessene Distanz in cm zurück liefert.
distance = sensor.distance_cm()
Ein Blick in die Bibliothek verrät uns das es auch eine Methode distance_mm()
gibt, welche die Distanz in mm zurück gibt.
Hier sehen Sie nochmal die komplette main.py
auf einen Blick.
from hcsr04 import HCSR04
from time import sleep
sensor = HCSR04(trigger_pin=26, echo_pin=25, echo_timeout_us=20000)
while True:
distance = sensor.distance_cm()
print('Distance:', distance, 'cm')
sleep(1)
Liefert die Methode distance_cm
einen negativen Wert zurück, ist die Entfernung zum Hinderniss zu groß und der Timeout hat die Messung abgebrochen.
- Ist der maximale gemessene Abstand kleiner als 4m können Sie den Timeout Wert erhöhen.
- Benötigen Sie nur die Info ob ein Abstand kleiner oder größer als ein Schwellwert ist, z.B 22cm, können Sie den Timeout-Wert auch verkleinern um die Messzeit zu reduzieren.
Übungen
- Probieren Sie verschiedene Messungen indem Sie den Abstand mit ihrer Hand verändern.
- Überprüfen Sie mit einem Lineal ob die Messungen (abzüglich Messfehler und Genauigkeit) stimmen.
- Lassen Sie je nach Abstand zum Sensor eine LED leuchten. Bsp: < 8cm LED aus. > 8cm LED an.
- Erweitern Sie ihren Aufbau mit einem zweiten Abstandssensor und lassen Sie zwei LEDs bei einem Schwellwert leuchten oder eben nicht.
Bildquelle: random nerd tutorials
IR
Vorkenntnisse
Ziel:
- Funktionsweise eines IR Sensors kennenlernen.
- Verwendung des Sharp IR-Sensor Moduls.
Funktionsweise
Infrarotsensoren (IR-Sensoren) arbeiten nach dem Prinzip der Lichtreflexion. Eine eingebaute Infrarot-LED sendet Licht aus, das von einer Oberfläche reflektiert wird. Ein Fotodetektor misst anschließend die Intensität des zurückgeworfenen Lichts.
Einflussfaktoren auf das Messsignal
Die Stärke des reflektierten Lichts hängt von mehreren Faktoren ab:
- Abstand zwischen Sensor und Objekt
- Oberflächenbeschaffenheit (rau, glatt, glänzend, matt)
- Farbe der Oberfläche (helle Flächen reflektieren mehr Licht)
- Ausrichtung der Oberfläche zur Lichtquelle
Deshalb eignen sich einfache IR-Sensoren gut zur Objekt- oder Linienerkennung. Für sehr präzise Abstandsmessungen sind sie nur bedingt geeignet, für eine grobe Entfernungsabschätzung jedoch ausreichend gut.
Störquellen aus der Umgebung
In realen Umgebungen gibt es viele zusätzliche Lichtquellen – z. B. Sonnenlicht oder Lampen. Diese senden ebenfalls Licht aus, das vom IR-Sensor erfasst werden kann. Solche Störungen können zu ungenauen Messergebnissen führen. Dies sollte beim Testen der Sensoren bedacht werden.
Warum Infrarotlicht verwendet wird
Um den Einfluss von Umgebungslicht zu verringern, nutzen IR-Sensoren meist Infrarotlicht mit einer bestimmten Wellenlänge. Dieses Licht liegt mit etwa 950 Nanometern außerhalb des für Menschen sichtbaren Spektrums (sichtbares Licht endet bei ca. 780 nm).
Durch den gezielten Einsatz dieser Wellenlänge kann der Sensor das eigene Lichtsignal von anderen Lichtquellen besser unterscheiden – das erhöht die Messsicherheit und Zuverlässigkeit.
Bildquelle: rule the bot
Sensorschaltung mit IR-LED und Phototransistor
Mit einer IR-LED und einem Phototransitor kann man eine geeignete Schaltung aufbauen. Eine Beispielhafte Schaltung ist bei rule the bot zu finden. Diese basiert jedoch auf der Ansteuerung mit einem Arduino Uno.
Entfernungsmessung mit dem Sharp GP2Y0A51SK0F Modul
Das GP2Y0A51SK0F von Sharp ist ein analoger Infrarot-Abstandssensor, der sich ideal zur kurzreichweitigen Entfernungsmessung eignet (2–15cm). Dabei wird die analoge Spannung des Sensors über einen ADC (Analog-Digital-Wandler) eingelesen und in eine Entfernung umgerechnet. Dank seiner geringen Größe und schnellen Reaktionszeit ist der Sensor besonders gut für Robotik- oder Hindernisvermeidungsanwendungen geeignet.
- Nutzen Sie den ADC um die erzeugte Spannung des Moduls einzulesen.
- Recherchieren Sie im Datenblatt wie die eingelesenen Werte in eine Entfernung umgerechnet werden können.
Solution
Solution
from machine import ADC, Pin
import time
# ADC initialisieren (z.B. GPIO 34 beim ESP32)
adc = ADC(Pin(34))
def calc_distance(uv):
# convert microvolts to distance in cm
if uv < 400_000 or uv > 2_800_000:
return None # außerhalb des Messbereichs
elif uv < 500_000:
return 15
elif uv < 700_000:
return 10
elif uv < 1_400_000:
return 5
elif uv < 2_800_000:
return 2
while True:
raw = adc.read_uv()
dist = calc_distance(raw)
print(f"Distance: {dist}")
time.sleep(0.5)
Die Umrechung ist eine vereinfachte Näherung basierend auf typischen Werten aus dem Datenblatt. Für genauere Messungen empfiehlt sich eine individuelle Kalibrierung.
Laser
Blink a LED without sleep
Dieses Lab zeigt Ihnen wie sie eine LED mit dem Microcontroller blinken lassen können, ohne sleep zu verwenden.
Vorkenntnisse:
Ziel:
- Nicht blockierendes Blinken einer LED
Rückblick
Im Lab 6.1 habenm wir gesehen wie wir eine LED blinken lassen können.
Wir wollen nun mehrere LEDs in unterschiedlicher Geschwindigkeit blinken lassen, dazu wollen wir die Funktionalität zunächst in eine eigene Funktion namens blink
kapseln.
Das könnte zum Beispiel so aussehen.
import time
from machine import Pin
led1_pin = Pin(18, Pin.OUT)
def blink(led, delay_time):
led.value(not led.value())
time.sleep(delay_time)
while True:
blink(led1_pin, 0.5)
Fügen Sie eine weitere LED hinzu die im Takt von 700ms blinken soll.
👉 blink(led2_pin, 0.7)
Welche Beobachtung können Sie machen?
Solution
Solution
import time
from machine import Pin
def blink(led, delay_time):
led.value(not led.value())
time.sleep(delay_time)
led1_pin = Pin(18, Pin.OUT)
led2_pin = Pin(19, Pin.OUT)
while True:
blink(led1_pin, 0.5)
blink(led2_pin, 0.7)
Beobachtung
Beobachtung
Wie sie vlt festgestellt haben blinken die beiden LEDs hintereinander anstatt parallel, dies liegt daran dass die Funktion sleep
die MCU daran hindert in dieser Zeit weiter zu arbeiten. Man spricht hier auch von einer blockierenden Funktion.
Nicht blockierendes Blinken einer LED
Probieren Sie anstatt dessen folgendes Programm
from machine import Pin
import time
led1_pin = Pin(18, Pin.OUT)
led2_pin = Pin(19, Pin.OUT)
last_ticks_led1 = 0
last_ticks_led2 = 0
BLINK_TIME_LED1 = 500
BLINK_TIME_LED2 = 700
def blink(led, last_ticks, blink_time):
current_ticks = time.ticks_ms()
if time.ticks_diff(current_ticks, last_ticks) > blink_time:
led.value(not led.value())
last_ticks = current_ticks
return last_ticks # needs to be saved in last_ticks for the led
while True:
last_ticks_led1 = blink(led1_pin, last_ticks_led1, BLINK_TIME_LED1)
last_ticks_led2 = blink(led2_pin, last_ticks_led2, BLINK_TIME_LED2)
# still time to do something else.
Alternative ohne Funktion
Alternative ohne Funktion
from machine import Pin
import time
led1_pin = Pin(18, Pin.OUT)
led2_pin = Pin(19, Pin.OUT)
last_ticks_led1 = 0
last_ticks_led2 = 0
BLINK_TIME_LED1 = 500
BLINK_TIME_LED2 = 700
while True:
current_ticks = time.ticks_ms()
# blink led 1
if time.ticks_diff(current_ticks, last_ticks_led1) > BLINK_TIME_LED1:
led1_pin.value(not led1_pin.value())
last_ticks_led1 = current_ticks
# blink led 2
if time.ticks_diff(current_ticks, last_ticks_led2) > BLINK_TIME_LED2:
led2_pin.value(not led2_pin.value())
last_ticks_led2 = current_ticks
# still time to do something else.
Beschreiben Sie in eigenen Worten wie das Programm funktioniert und warum dies nicht blockierend ist.
Hinweis: eine weitere Möglichkeit für eine nicht blockierende Lösung sehen Sie im Lab Timers.
Kontrollfragen
- Welches Problem kann sich durch die Verwendung von
sleep
zum blinken ergeben? - Was versteht man unter dem Begriff blockierende Funktion?
- Welchen Wert gibt die Funktion
time.tick_ms()
zurück. - Mit welcher Funktion können Sie berechnen wie viel Zeit seit einem gemessenen Zeitpunkt vergangen ist?
Übungsaufgabe
- Schreiben Sie ein Programm welches eine LED im Intervall von 250ms blinken lässt ohne sleep zu verwenden.
Taster entprellen
Dieses Lab zeigt Ihnen wie sie einen Taster für eine solidere Eingabe entprellen können.
Vorkenntnisse:
Ziel:
- Taster entprellen.
What is it with bouncing?
Beim testen des vorherigen Beispiels ist ihnen vlt aufgefallen, dass diese Methode nicht immer sicher einen Tastendruck erkannt hat. Um genau zu sein, hat das Programm zu viele Tastendruücke erkannt. Dies liegt an der mechanischen Bauweise solcher Taster.
Das Signal ist nicht wie zu erwarten ein sauberer übergang von HIGH zu LOW, sondern sieht eher so aus.
Es gibt nun verschiedene Lösungen für dieses Problem:
- eine Hardware Schaltung zum entprellen bauen.
- eine einfache blockierende Entprelllogik in Software.
- eine nicht blockierende Entrelllogik in Software.
Wir wollen uns hier nur die letzten beiden Lösungen anschauen.
Das Ergebnis sollte eine saubere Erkennung des Tasters sein.
Blocking Debounce
Hier sehen Sie eine Mögliche Lösung:
from machine import Pin
import time
led = Pin(14, Pin.OUT)
btn = Pin(27, Pin.IN, Pin.PULL_UP)
def toggle(led):
led.value(not led.value())
# Set btn state to HIGH as btn is low active.
last_btn_state = 1
while True:
current_btn_state = btn.value()
if current_btn_state != last_btn_state:
time.sleep(0.03)
if current_btn_state == 0:
toggle(led)
last_btn_state = current_btn_state
Nonblocking Debounce
Wie beim blinken der LED haben wir nun auch das Problem das die sleep
funktion die CPU blockiert.
Das Prinzip für einen nicht blockierendes Entprellen des Tasters, ist sehr ähnlich zum nicht blockierenden blinken einer LED.
from machine import Pin
import time
led = Pin(14, Pin.OUT)
btn = Pin(27, Pin.IN, Pin.PULL_UP)
def toggle(led):
led.value(not led.value())
# Set btn state to HIGH as btn is low active.
last_btn_state = 1
last_debounce_ticks = 0
BTN_DEBOUNCE_MS = 30
while True:
current_btn_state = btn.value()
if current_btn_state != last_btn_state:
current_ticks = time.ticks_ms()
if time.ticks_diff(current_ticks, last_debounce_ticks) > 30:
if current_btn_state == 0:
toggle(led)
last_debounce_ticks = current_ticks
last_btn_state = current_btn_state
Kontrollfragen
- todo
- todo
- todo
Weitere Ressourcen
Es gibt noch einige weitere Möglichkeiten und Algorithmen zum Entprellen eines Tasters.