Ein ESP32-Servo-MQTT-Projekt macht aus einem günstigen Modellbau-Servo einen ferngesteuerten Aktor für Jalousien, Ventile, Kameraschwenkköpfe oder Futterautomaten. In diesem Tutorial bewegen Sie einen Servo per MQTT-Winkelbefehl, fahren ihn sanft an, statt ihn springen zu lassen, setzen mechanische Grenzen in der Firmware durch und veröffentlichen eine Positionsrückmeldung, die jedes Dashboard lesen kann. Jeden Schritt können Sie ohne Konto gegen broker.connectmq.com ausprobieren.
Was Sie bauen
Der ESP32 hört auf einem Topic auf Befehle und meldet den Fortschritt auf einem anderen. Befehle können ein reiner Winkel oder ein kleines JSON-Objekt mit Geschwindigkeit sein:
| Topic | Richtung | Retained | Beispiel-Payload |
|---|---|---|---|
connectmq/demo/<id>/servo/set |
App zum ESP32 | Nein | 90 oder {"angle":45,"speed":60} |
connectmq/demo/<id>/servo/position |
ESP32 zu den Apps | Ja | {"angle":72,"target":45,"moving":true} |
connectmq/demo/<id>/status |
ESP32 zu den Apps | Ja | online / offline |
Eine ehrliche Einschränkung vorweg: Ein Standard-Modellbau-Servo meldet seine tatsächliche Wellenposition nicht. Die hier veröffentlichte Rückmeldung ist der Winkel, den die Firmware vorgegeben hat, aktualisiert, während der Servo auf das Ziel zufährt. Das genügt für Dashboards und zur Bestätigung, dass ein Befehl angenommen wurde. Brauchen Sie die tatsächlich gemessene Position, benötigen Sie einen Servo mit Rückmeldeleitung oder einen externen Encoder, und das ist ein anderes Projekt.
Hardware und Stromversorgung
- ESP32-Entwicklungsboard.
- Ein Modellbau-Servo wie der SG90 (Mikro, Kunststoffgetriebe) oder der MG996R (Standardgröße, Metallgetriebe).
- Ein separates 5-V-Netzteil für alles, was größer als ein Mikro-Servo ist, ausgelegt mindestens für den Blockierstrom des Servos.
- Ein Elektrolytkondensator mit 470 µF bis 1000 µF an der Servoversorgung, nah am Servo.
| Servo-Leitung | Anschließen an |
|---|---|
| Braun / schwarz (GND) | GND des Netzteils und GND des ESP32 |
| Rot (V+) | 5-V-Netzteil |
| Orange / gelb (Signal) | ESP32 GPIO 18 |
Die gemeinsame Masse ist nicht optional: Ohne sie hat das Signal keinen Bezug, und der Servo zuckt willkürlich. Einen MG996R aus dem 5-V-Pin des ESP32 zu versorgen ist die häufigste Ursache für Brownout-Resets in Servoprojekten, weil der Servo bei jedem Anlaufen eine große Stromspitze zieht. Das 3,3-V-Signal des ESP32 wird von fast allen Modellbau-Servos akzeptiert.
Bibliotheken
Installieren Sie das Board-Paket esp32 und folgende Bibliotheken aus dem Arduino-Bibliotheksverwalter:
- PubSubClient von Nick O'Leary für MQTT.
- ESP32Servo von Kevin Harrington und John K. Bennett, die Servos über die LEDC-PWM-Hardware des ESP32 ansteuert.
- ArduinoJson von Benoit Blanchon (Version 7), um das JSON-Befehlsformat zu parsen.
Die normale Arduino-Bibliothek Servo unterstützt den ESP32 nicht, daher ist ESP32Servo die richtige Wahl. Neu bei MQTT auf diesem Board? Beginnen Sie mit dem ESP32-IoT-Tutorial, das die Board-Einrichtung und einen ersten Publish behandelt.
Der ESP32-Servo-MQTT-Sketch
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* DEVICE_ID = "servo-4c81d2"; // pro Gerät eindeutig
const String BASE = String("connectmq/demo/") + DEVICE_ID;
const int SERVO_PIN = 18;
const int MIN_ANGLE = 10; // mechanische Grenzen IHRER Mechanik
const int MAX_ANGLE = 170;
const int MIN_US = 500; // Pulsbreite für 0 Grad
const int MAX_US = 2400; // Pulsbreite für 180 Grad
Servo servo;
WiFiClient net;
PubSubClient mqtt(net);
float currentAngle = 90;
int targetAngle = 90;
float speedDegPerSec = 90; // Standard-Anfahrgeschwindigkeit
unsigned long lastStep = 0, lastReport = 0, lastAttempt = 0;
bool wasMoving = false;
void publishPosition(bool force) {
bool moving = (int)round(currentAngle) != targetAngle;
if (!force && millis() - lastReport < 250) return;
lastReport = millis();
char buf[96];
snprintf(buf, sizeof(buf), "{\"angle\":%d,\"target\":%d,\"moving\":%s}",
(int)round(currentAngle), targetAngle, moving ? "true" : "false");
String topic = BASE + "/servo/position";
mqtt.publish(topic.c_str(), buf, true);
}
void onMessage(char* topic, byte* payload, unsigned int length) {
JsonDocument doc;
int angle;
if (deserializeJson(doc, payload, length) == DeserializationError::Ok && doc.is<JsonObject>()) {
if (!doc["angle"].is<int>()) return;
angle = doc["angle"];
if (doc["speed"].is<float>()) speedDegPerSec = constrain(doc["speed"].as<float>(), 5.0f, 600.0f);
} else if (doc.is<int>()) {
angle = doc.as<int>(); // reine Zahl wie "90"
} else {
return;
}
targetAngle = constrain(angle, MIN_ANGLE, MAX_ANGLE);
if (!servo.attached()) servo.attach(SERVO_PIN, MIN_US, MAX_US);
Serial.printf("Target %d deg at %.0f deg/s\n", targetAngle, speedDegPerSec);
publishPosition(true);
}
bool connectMqtt() {
String status = BASE + "/status";
if (!mqtt.connect(DEVICE_ID, nullptr, nullptr, status.c_str(), 1, true, "offline")) {
Serial.printf("MQTT failed, state=%d\n", mqtt.state());
return false;
}
mqtt.publish(status.c_str(), "online", true);
mqtt.subscribe((BASE + "/servo/set").c_str(), 1);
publishPosition(true);
return true;
}
void setup() {
Serial.begin(115200);
ESP32PWM::allocateTimer(0);
servo.setPeriodHertz(50); // Standard-Servoframe
servo.attach(SERVO_PIN, MIN_US, MAX_US);
servo.write((int)currentAngle);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setSocketTimeout(5);
}
void stepServo() {
unsigned long now = millis();
float dt = (now - lastStep) / 1000.0f;
lastStep = now;
float diff = targetAngle - currentAngle;
if (fabs(diff) < 0.5f) {
currentAngle = targetAngle;
if (wasMoving) {
servo.write(targetAngle);
publishPosition(true); // abschließende Positionsmeldung
wasMoving = false;
}
return;
}
float stepSize = speedDegPerSec * dt;
currentAngle += constrain(diff, -stepSize, stepSize);
servo.write((int)round(currentAngle));
wasMoving = true;
publishPosition(false); // gedrosselter Fortschritt
}
void loop() {
stepServo();
if (WiFi.status() != WL_CONNECTED) return;
if (!mqtt.connected()) {
if (millis() - lastAttempt > 5000) { lastAttempt = millis(); connectMqtt(); }
return;
}
mqtt.loop();
}
Warum sanft anfahren statt springen
Ein direkter Aufruf von servo.write(170) lässt den Servo mit voller Geschwindigkeit in den neuen Winkel schlagen. Bei einer Mechanik, die ein Ventil oder einen Deckel bewegt, belastet das die Kunststoffzahnräder und zieht eine große Stromspitze aus dem Netzteil. Die Funktion stepServo() verschiebt den Sollwinkel bei jedem Schleifendurchlauf ein wenig, abhängig von der verstrichenen Zeit, sodass die Geschwindigkeit konstant bleibt, egal wie schnell loop() läuft. 60 Grad pro Sekunde wirken bedächtig, 300 fühlen sich zackig an.
Warum die Grenzen in der Firmware liegen
MIN_ANGLE und MAX_ANGLE sollten dem physischen Verfahrweg dessen entsprechen, was der Servo antreibt. Sendet eine App 0 und Ihre Mechanik klemmt bei 8 Grad, blockiert der Servo, wird heiß und zieht seinen Blockierstrom, bis etwas nachgibt. Wird auf dem Gerät begrenzt, kann ein Tippfehler in einem Dashboard die Hardware nie beschädigen. Behandeln Sie jeden MQTT-Befehl als nicht vertrauenswürdige Eingabe.
Befehle senden und Rückmeldung beobachten
Abonnieren Sie das Geräte-Präfix:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-4c81d2 -v -t "connectmq/demo/servo-4c81d2/#"
Bewegen Sie dann den Servo:
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-4c81d2 -t "connectmq/demo/servo-4c81d2/servo/set" -m 150
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-4c81d2 -t "connectmq/demo/servo-4c81d2/servo/set" -m '{"angle":30,"speed":40}'
Während sich der Servo bewegt, sehen Sie den Positionsstrom, der mit "moving":false endet:
connectmq/demo/servo-4c81d2/servo/position {"angle":90,"target":150,"moving":true}
connectmq/demo/servo-4c81d2/servo/position {"angle":113,"target":150,"moving":true}
connectmq/demo/servo-4c81d2/servo/position {"angle":150,"target":150,"moving":false}
Die Fortschrittsmeldungen sind auf vier pro Sekunde gedrosselt. Damit bleiben Sie deutlich unter dem Limit des öffentlichen Brokers von 10 Nachrichten pro Sekunde und Client, und es reicht völlig für eine flüssige Dashboard-Anzeige. Denken Sie daran, dass der kostenlose öffentliche MQTT-Broker für alle lesbar ist: Er ist ein Testplatz, kein Ort für einen Servo, der Ihre Haustür öffnet.
Den Servo kalibrieren, bevor Sie ihm vertrauen
Servos verschiedener Hersteller und sogar verschiedener Chargen desselben Modells setzen Pulsbreite leicht unterschiedlich in Winkel um. Investieren Sie fünf Minuten in die Kalibrierung, bevor Sie eine Halterung darum bauen:
- Entfernen Sie jede Mechanik, damit sich das Servohorn frei drehen kann.
- Setzen Sie
MIN_ANGLEvorübergehend auf 0 undMAX_ANGLEauf 180, senden Sie0und horchen Sie. Brummt der Servo oder drückt er am Ende weiter, erhöhen SieMIN_USum 50 und versuchen es erneut, bis er ruhig stillsteht. - Wiederholen Sie das bei
180und senken SieMAX_US, bis die Endposition ruhig ist. - Senden Sie
90und prüfen Sie, ob das Horn dorthin zeigt, wo Sie die "Mitte" erwarten. Falls nicht, setzen Sie das Horn auf der Verzahnung neu auf, statt in der Software zu kompensieren. - Montieren Sie die Mechanik, fahren Sie mit niedriger Geschwindigkeit wie
{"angle":20,"speed":15}langsam auf jedes Ende zu und notieren Sie, wo es zu klemmen beginnt. Setzen SieMIN_ANGLEundMAX_ANGLEeinige Grad innerhalb dieser Punkte.
Notieren Sie die endgültigen Werte in einem Kommentar neben den Konstanten. Wenn Sie ein Jahr später einen defekten Servo ersetzen, wissen Sie, welche Zahlen Sie erneut prüfen müssen. Die Kalibrierung ist auch der Grund, warum das Rückmelde-Topic ehrlich den vorgegebenen Winkel meldet, statt eine Messung vorzutäuschen: Die Zahl bedeutet "wohin die Firmware den Servo geschickt hat", und erst die Kalibrierung sorgt dafür, dass diese Zahl der Realität entspricht.
Fehlerbehebung
- Servo zittert im Stillstand. Meist Störungen in der Stromversorgung. Ergänzen Sie den Pufferkondensator, kürzen Sie die Servoleitungen und führen Sie die Signalleitung fern von Motorleitungen. Hält der Servo keine Last, können Sie nach Erreichen des Ziels
servo.detach()aufrufen, um die Pulse ganz zu stoppen; der Sketch verbindet ihn beim nächsten Befehl wieder. - ESP32 startet neu, wenn sich der Servo bewegt. Brownout. Verwenden Sie ein separates Netzteil für den Servo mit gemeinsamer Masse.
- Der Stellbereich beträgt nur etwa 90 Grad. Der Servo erwartet einen breiteren Pulsbereich. Passen Sie
MIN_USundMAX_USin kleinen Schritten an; viele Servos arbeiten mit etwa 500 bis 2500 Mikrosekunden, doch jenseits der echten Endanschläge brummt und verspannt der Servo. - Servo bewegt sich, aber es kommt keine Rückmeldung. Prüfen Sie, ob die Publishes nicht größer als der Standardpuffer von PubSubClient sind und
mqtt.loop()oft genug läuft. Ein blockierendesdelay()irgendwo inloop()stört sowohl das Anfahren als auch MQTT. - Befehle werden ignoriert. Das JSON muss einen ganzzahligen
angleenthalten. Strings wie"angle":"90"werden absichtlich abgelehnt. - Zwei Servos stören sich gegenseitig. Jeder braucht einen eigenen Pin;
ESP32Servovergibt PWM-Kanäle automatisch, rufen Sie aberallocateTimerfür die Timer auf, die Sie nutzen wollen.
Von der Demo zum echten Einsatz
Sobald der Servo funktioniert, holen Sie ihn vom öffentlichen MQTT-Broker weg, der nur für Tests und Prototypen gedacht ist. Legen Sie im ConnectMQ-Portal einen Broker an, kopieren Sie die generierten Werte für Benutzername, Passwort und Client ID und verbinden Sie sich mit WiFiClientSecure über TLS auf Port 8883. Der Leitfaden zu ESP32 MQTT mit SSL zeigt die Zertifikatseinrichtung Schritt für Schritt.
Für Stellantriebe in Maschinen und Prozessanlagen gelten andere Maßstäbe: Dort übernehmen SPS und industrielle Antriebe die Regelung, und MQTT transportiert Soll- und Istwerte für Visualisierung, MES und Wartung. Für Prototypen, Laboraufbauten und Hilfsfunktionen ist ein Modellbau-Servo am ESP32 dagegen ideal.
Bei allem, was sich bewegt, zählt Verfügbarkeit. Ein verwalteter MQTT-Broker wie ConnectMQ gibt jedem Gerät eigene Zugangsdaten und lässt Sie servo/position im Live-Topic-Explorer des Portals beobachten, während Sie die Geschwindigkeiten abstimmen. Dasselbe Befehls- und Zustandsmuster lässt sich direkt auf die Relaissteuerung mit ESP32 und MQTT und auf per MQTT gesteuerte Schrittmotoren übertragen, der nächste Schritt, wenn Sie mehr Drehmoment oder kontinuierliche Rotation mit präziser Positionierung brauchen.
Starten Sie mit ConnectMQ
Registrieren Sie sich unter app.connectmq.com und erstellen Sie einen kostenlosen Serverless Broker, ohne Kreditkarte. Sie erhalten sofort TLS-Zugangsdaten, einen MQTT-Client im Browser zum Senden von Servobefehlen und ein Dashboard, um die Positionsrückmeldung live zu verfolgen.
Häufig gestellte Fragen
Meldet ein Modellbau-Servo seine echte Position per MQTT?
Nein. Ein Standard-Servo hat keinen Positionsausgang, daher meldet der ESP32 den vorgegebenen Winkel. Für eine gemessene Position verwenden Sie einen Servo mit Rückmeldung oder ergänzen einen Encoder oder ein Potentiometer, das der ADC des ESP32 ausliest.
Welche ESP32-Pins können einen Servo ansteuern?
Jeder ausgangsfähige GPIO funktioniert, denn die PWM stammt aus dem LEDC-Peripheriemodul. Meiden Sie die reinen Eingangspins 34 bis 39 und Strapping-Pins wie 0, 2, 12 und 15.
Wie viele Servos kann ein ESP32 per MQTT steuern?
Der ursprüngliche ESP32 hat 16 LEDC-Kanäle (neuere Varianten wie der S3 weniger), grundsätzlich sind also viele Servos möglich; die eigentliche Grenze sind Stromversorgung und Verkabelung. Verwenden Sie pro Servo ein Topic, zum Beispiel servo/1/set und servo/2/set.
Sollten Servobefehle QoS 1 verwenden?
Ja, QoS 1 ist ein sinnvoller Standard. Ein absoluter Winkelbefehl ist idempotent, eine doppelte Zustellung setzt also einfach dasselbe Ziel erneut.



