Zentrales Thema einer Modelleisenbahn ist natürlich die Steuerung der Lokomotiven. Ein An- oder Ausschalten digitaler Pins ist spätestens beim feinfühligen Rangieren nicht mehr ausreichend. Mit Pulsweitenmodulation oder kurz PWM können wir aus dem ESP32 die Drehzahl eines Motors über einen Leistungs MOSFET (Feldeffekt-Transistor) steuern.
Was Du für diesen Teil brauchst
- ESP32 Developmentboard (hier kaufen)
- Breadboard und Jumper Kabel (hier kaufen)
- IRLZ34N oder IRF520N N-Channel MOSFET (hier kaufen)
- 5V DC Motor (z.B. (hier kaufen)
- Diode (hier kaufen)
- USB-Kabel mit Mirco-USB Anschluss (hier kaufen)
- PC (ich zeige auf Windows, Mac oder Linux gehen aber auch)
Du kannst die Schaltung aus diesem Teil auch mit einem Arduino (z.B. Arduino Nano) statt des ESP32 durchführen.
Ein bisschen PWM Theorie
Wie eingangs geschrieben ist eine Lösung für die Geschwindigkeitsregelung die Pulsweitenmodulation (PWM). Der Name kommt durch die Stromzufuhr in kurzen Schüben, den Pulsen. Durch sehr schnelles An- und Ausschalten können durchschnittlich betrachtet Zwischenschritte zwischen aus und an realisiert werden. Dabei wird die Trägheit des sich drehenden Motors genutzt, der trotz der Pausen in der Stromzufuhr weiter eine flüssige Bewegung vollzieht. Ähnlich funktioniert das übrigens auch mit dem Dimmen von LEDs, wobei die Trägheit des menschlichen Auges ausgenutzt wird. Ist die Schaltfrequenz hoch genug sieht man statt einer flackernden LED eine weniger helle aber scheinbar konstant leuchtende LED.
Die scheinbare Helligkeit einer LED und genauso die Drehzahl eines Motors hängt von der durchschnittlich über einen Zeitraum zugeführten Energie ab. Da die Versorgungsspannung aber immer gleich bleibt, muss die Dauer der Strompulse, also die Pulsweite, verlängert oder verkürzt werden, um die Helligkeit oder die Drehzahl zu regeln.
Die Steuerung der Motordrehzahl erfolgt demnach über die Anpassung der eingeschalteten Zeit je Gesamtdauer einer Periode bis zum Beginn der nächsten eingeschalteten Zeit. Dieses Verhältnis kann in Prozent ausgedrückt werden und wird in der Regel mit dem englischen Begriff „duty cycle“ bezeichnet, auf Deutsch „Tastgrad“. Diese Anpassung des duty cycle kann in verschiedenen Abstufungen erfolgen, wie wir weiter unten sehen werden.

Die Dauer der Periode wird durch die PWM Frequenz definiert. Eine Frequenz von 1Hz bedeutet eine eingeschaltete Phase und eine ausgeschaltete Phase in einer Sekunde – unabhängig welche Phase wie viel Zeit einnimmt. Eine typische Frequenz von 20kHz bedeutet folglich 20000 Perioden in einer Sekunde oder eine Periodendauer von 0,05 Millisekunden.
Ein Duty-Cycle von 50% bedeutet, dass in der ersten Hälfte der Periode der Ausgang eingeschaltet und in der zweiten Hälfte ausgeschaltet ist. Auf das Beispiel mit 20kHz bezogen dauert der Duty-Cycle dann 0,025ms.
Wahl der PWM Frequenz
Beim Betrieb von Motoren sind zwei Kriterien für die Wahl der PWM Frequenz besonders wichtig:
- Pfeiffgeräusche durch Frequenzen im hörbaren Bereich
- Maximale Schaltgeschwindigkeit und Schaltverluste der Ansteuerung
Frequenzen zwischen etwa 100Hz und 18kHz liegen im hörbaren Bereich des menschlichen Ohres. Kleine Vibrationen der Motor-Teile durch die gepulste Ansteuerung erzeugen ein mitunter unangenehmes Pfeiffgeräusch. Unproblematisch, wenn der Motor außer Hörreichweite betrieben werden soll – umso problematischer aber bei einer Modelleisenbahn.
Auf der anderen Seite laufen Motoren bei weniger als 100Hz nicht mehr rund. Bleibt also nur der Weg, die Frequenz knapp oberhalb von 18kHz zu wählen. Knapp deshalb, weil die Schaltverluste mit höherer Frequenz zunehmen ohne dass ein Vorteil für die Steuerung erzielt wird. Dies schließt dennoch relativ langsame Motortreiber wie den günstigen L293D für die Ansteuerung aus, da diese nur bis 5kHz betrieben werden können. Besser eignen sich L298N oder TB6612FNG. Auf Motortreiber und deren Vor- und Nachteile gehe ich dann im Teil 3: Vollständige Motorsteuerung mit PWM und H-Brücke ein. Hier konzentrieren wir uns zunächst weiter auf das Thema PWM.
PWM Implementierung auf dem ESP32
Im Gegensatz zum Arduino, wo die PWM Frequenz als Teiler des Quartz-Taktgebers, definiert wird, erlaubt der ESP32 eine freiere Definition durch spezialisierte integrierte Hardware (LEDC) bei Frequenzen bis zu 40MHz oder bis zu 16bit Auflösung des Duty-Cycle. Wichtig ist dabei zu wissen, dass höhere Frequenzen die Auflösung verringern und umgekehrt. Insgesamt sind 16 LEDC-Kanäle verfügbar, denen Pins zugeordnet werden können. Dem Namen nach ist LEDC zwar zum Dimmen von LEDs gedacht, aber natürlich kann diese Hardware auch andere PWM Aufgaben erledigen. Bei 40MHz sind diese bei 1bit und daher fest bei 50%. Für die von uns interessanten Bereiche von lediglich einigen kHz sind ausreichend Abstufungen möglich. Ausgehend von 40MHz Frequenz und 1bit Auflösung wird für jedes weitere bit Auflösung die maximal mögliche Frequenz halbiert:
Maximale LEDC Frequenz | Auflösung | Abstufungen |
---|---|---|
40000000 Hz | 1 bit | 2 |
20000000 Hz | 2 bit | 4 |
10000000 Hz | 3 bit | 8 |
5000000 Hz | 4 bit | 16 |
2500000 Hz | 5 bit | 32 |
1250000 Hz | 6 bit | 64 |
625000 Hz | 7 bit | 128 |
312500 Hz | 8 bit | 256 |
156250 Hz | 9 bit | 512 |
78125 Hz | 10 bit | 1024 |
39062 Hz | 11 bit | 2048 |
19531 Hz | 12 bit | 4096 |
Ich habe mich in diesem Projekt für die maximale Frequenz von 12bit entschieden, da diese in dem von mir gewünschten Bereich oberhalb von 18kHz und damit außerhalb des hörbaren Bereichs liegt. Die Auflösung ist dennoch bei nur 8bit belassen, da das für eine saubere Ansteuerung völlig ausreichend ist und vom Code her kompatibel mit einer früheren Implementierung auf Arduino Nano war ;-).
Fangen wir mit einem neuen Sketch an:
Aktualisiert auf Version 3. Siehe dazu unter ESP32 Version 3.0 für Arduinio IDE
const int enPin = 27;
const int freq = 19531;
const int resolution = 8;
void setup() {
// Start Serial
Serial.begin(115200);
ledcAttach(enPin, freq, resolution);
ledcWrite(enPin, 150);
}
void loop() {
//empty
}
Hier wird zunächst der LEDC-Kanal 0 mit einer Frequenz von 19531Hz und einer Auflösung von 8bit eingerichtet. Danach wird Pin 27 diesem Kanal hinzugefügt.
Die eigentliche PWM Ausgabe wird dann über die Funktion ledcWrite() mit Angabe des Pins und des duty cycles (seit Version 3 auch alternativ mit analogWrite() wie auf dem Arduino) ausgelöst:
ledcWrite(0, 150);
Die Funktion nimmt als ersten Parameter den Ausgabe-Pin entgegen und als zweiten Parameter den duty cycle als Wert zwischen 0 und 255, wobei 255 einem duty cycle von 100% entspricht. Hier, mit 150, ein mittlerer Wert, der durch den einmaligen Aufruf permanent anliegen würde.
Messen kann man das mit einem Voltmeter, das zwischen GND und GPIO27 etwa 1,9V als mittlere Spannung anzeigt.
Für noch mehr Infos zur LEDC Peripherie empfehle ich die Dokumentation des ESP32: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#
Verstärkung
Ein Mikrocontroller hat bei Weitem nicht genug Leistung an den GPIO Pins für einen Motor einer H0 Modelleisenbahn-Lok. Wir müssen das Signal also verstärken. Dies erreichen wir, indem der Motor nicht direkt durch den Pin des Mikrocontrollers mit Strom versorgt wird, sondern durch eine stärkere Spannungsquelle und einen Transistor. Der ESP32 steuert lediglich den Schaltzustand des Transistors, wozu nur eine sehr geringe Leistung benötigt wird. Wir nutzen dazu einen MOSFET (Metal Oxyde Semiconductor Field Effekt Transistor).
Wie funktioniert ein MOSFET?
Bei MOSFETs wird zwischen n-Kanal und p-Kanal, sowie zwischen Anreicherungs- und Verarmungstyp unterschieden. Der Einfachheit halber, will ich hier nur auf den n-Kanal Anreicherungstyp eingehen, z.B. den IRLZ34N.


Das ist natürlich sehr stark vereinfacht, aber für unseren Zweck hoffentlich ausreichend. Viele weitere Infos gibt es unter https://de.wikipedia.org/wiki/Metall-Oxid-Halbleiter-Feldeffekttransistor.
Breadboard Aufbau

Der Motor bekommt +5V Arbeitsspannung zusammen mit dem VIN am ESP32. In diesem Versuch reicht es aus, wenn als Spannungsquelle USB angeschlossen ist. Wegen der induktiven Last ist parallel zum Motor eine sogenannte Freilaufdiode in umgekehrter Richtung eingebaut. Sie dient dazu eine beim Abschalten des Stroms durch die Energie im Magnetfeld des Motors induzierte, kurzzeitig sehr hohe, Gegenspannung kurzzuschließen.
Der GND Anschluss des Motors ist zum Drain eines N-Channel MOSFET Transistors geführt, dessen Source schließlich mit GND verbunden ist (Low-side Konfiguration). Der Transistor dient als Schalter: Der Stromkreis des Motors ist nur geschlossen, wenn am Gate des Transistors eine ausreichende Spannung anliegt.
Dazu wird das Gate mit einem GPIO-Pin des ESP32 verbunden, den wir als OUTPUT digital ansteuern werden und der so zwischen 0V und 3,3V wechseln kann.
Sketch
Mit folgendem Sketch wird über ein PWM Signal der duty cycle des angeschlossenen MOSFET schrittweise auf 98% (250 von 255) erhöht und dann wieder verringert. Der angeschlossene Motor verändert seine Drehzahl entsprechend.
Aktualisiert auf Version 3. Siehe dazu unter ESP32 Version 3.0 für Arduinio IDE
const int enPin = 27;
const int freq = 19531;
const int resolution = 8;
void setup() {
// Start Serial
Serial.begin(115200);
ledcAttach(enPin, freq, resolution);
}
void loop() {
Serial.print("Drehzahl wird schrittweise erhöht...");
for (byte i=0; i <= 25; i++) {
Serial.print((String)"" + i + "...");
ledcWrite(enPin, i * 10);
delay(1000);
}
Serial.println("");
Serial.print("Drehzahl wird schrittweise verringert...");
for (byte i=25; i >= 0; i--) {
Serial.print((String)"" + i + "...");
ledcWrite(enPin, i * 10);
delay(1000);
}
Serial.println("");
delay(5000);
}
Die Geschwindigkeit des Motors lässt sich somit sehr gut steuern. Die Laufrichtung ist aber fest verdrahtet. Im nächsten Beitrag zeige ich, wie die Ansteuerung über den MOSFET zu einer H-Brücke erweitert wird, um auch die Laufrichtung umschalten zu können.