ESP32 und Arduino

ESP32 Servo per MQTT: Servo fernsteuern mit Positionsrückmeldung

Bewegen Sie einen Servo mit einem ESP32 und der ESP32Servo-Bibliothek per MQTT-Befehl, fahren Sie den Zielwinkel sanft an und veröffentlichen Sie eine Positionsrückmeldung, die Dashboards anzeigen können.

SG90-Servo an einem ESP32-Board, das in einem ESP32-Servo-MQTT-Aufbau Winkelbefehle empfängt
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

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:

  1. Entfernen Sie jede Mechanik, damit sich das Servohorn frei drehen kann.
  2. Setzen Sie MIN_ANGLE vorübergehend auf 0 und MAX_ANGLE auf 180, senden Sie 0 und horchen Sie. Brummt der Servo oder drückt er am Ende weiter, erhöhen Sie MIN_US um 50 und versuchen es erneut, bis er ruhig stillsteht.
  3. Wiederholen Sie das bei 180 und senken Sie MAX_US, bis die Endposition ruhig ist.
  4. Senden Sie 90 und 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.
  5. 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 Sie MIN_ANGLE und MAX_ANGLE einige 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

  1. 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.
  2. ESP32 startet neu, wenn sich der Servo bewegt. Brownout. Verwenden Sie ein separates Netzteil für den Servo mit gemeinsamer Masse.
  3. Der Stellbereich beträgt nur etwa 90 Grad. Der Servo erwartet einen breiteren Pulsbereich. Passen Sie MIN_US und MAX_US in kleinen Schritten an; viele Servos arbeiten mit etwa 500 bis 2500 Mikrosekunden, doch jenseits der echten Endanschläge brummt und verspannt der Servo.
  4. 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 blockierendes delay() irgendwo in loop() stört sowohl das Anfahren als auch MQTT.
  5. Befehle werden ignoriert. Das JSON muss einen ganzzahligen angle enthalten. Strings wie "angle":"90" werden absichtlich abgelehnt.
  6. Zwei Servos stören sich gegenseitig. Jeder braucht einen eigenen Pin; ESP32Servo vergibt PWM-Kanäle automatisch, rufen Sie aber allocateTimer fü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.

Verwandte Artikel

ESP32 NTP MQTT Zeitstempel: genaue Uhrzeit in jeder Nachricht

Synchronisieren Sie die ESP32-Uhr per NTP, formatieren Sie Zeitstempel in ISO 8601 und Epoch-Millisekunden und hängen Sie sie an MQTT-Payloads, damit Messwerte ihren echten Erfassungszeitpunkt behalten, auch wenn sie verspätet veröffentlicht werden.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.