In diesem Projekt zeige ich dir, wie du einen einfachen 4-Achs-Roboterarm mit einem ESP32-Board und handelsüblichen Servos bauen und per Smartphone-App steuern kannst. Dank Bluetooth-Verbindung und der kostenlosen App „nRF Connect“ lassen sich die Bewegungen bequem vom Handy aus kontrollieren – ideal für erste Schritte mit Servoansteuerung, Bluetooth-Kommunikation und 3D-gedruckten Mechaniken.
Weiter unten eine Arduino Version, nur per Sketch steuerbar.

Hardware-Komponenten
- ESP32 Dev Board plus Erweiterungsplatine ESP3230P
- 2× SG90 Servos 180Grad Bewegungsraum
- 2× MG90S Servos 180Grad Bewegungsraum
- Externe 5V Stromversorgung für Servos
- Jumper-Kabel
- Servo-Arm-Modell (z. B. 3D-gedruckt oder Fischertechnik oder LEGO)
Verdrahtung:

Funktion | Servo-Name | GPIO | Beschreibung |
---|---|---|---|
„Basis unten“ | sg90=servo_b | D15 | Hebt/Senkt den Arm |
„Mittelarm“ | sg90=servo_m | D14 | Gelenk in der Mitte von 75 bis 140 |
„Greifer“ | mg90S=servo_g | D13 | Greift/öffnet die Zange mit 90 und schließt locker mit 5 |
„Drehen“ | mg90S=servo_d | D16 | Dreht den Sockel (Basis) |
⚠️ Hinweis zur Stromversorgung
Unabhängig vom Typ:
- Nicht direkt vom ESP32 speisen, wenn du mehrere Servos nutzt.
- Nutze ein externes 5V-Netzteil mit gemeinsamer Masse (GND) zum ESP32 oder Arduino.
Lösung bei mir, ist diese Erweiterungsplatine. Sie nimmt den ESP32 auf und hat für jeden nach außen geführten GPIO PIN zusätzlich VCC und GND, das mit externen USB Anschluss, die das ESP32 mit Strom versorgt und zugleich entlastet. Das macht das Experimentieren erheblich leichter.
Auch Umschaltbar von 5 Volt auf 3,3 Volt. Netzteilanschluss ist USB-C.

Library:
Sofern noch nicht in der Arduino IDE vorhanden, installiere die ESP32Servo Bibliothek

Test Modus
Bevor der Roboterarm im Zusammenspiel aller Achsen betrieben wird, empfiehlt es sich, jeden Servo einzeln zu testen. So lässt sich gezielt prüfen, ob die Verdrahtung korrekt ist, der Servo stabil läuft und keine ungewöhnlichen Geräusche oder Ruckler auftreten. Gleichzeitig kann man sich schrittweise an die maximalen Winkelbereiche herantasten, ohne die Mechanik zu überlasten.
Anfangs habe ich zum Beispiel bei mir, mit vier SG90 Servos experimentiert, jedoch dann zwei zum heben und drehen, gegen die robusteren und belastbareren MG90S getauscht.

Der folgende Sketch bewegt alle Servos nacheinander zwischen Start- und Zielposition – ideal zum Beobachten, Justieren und Optimieren.
Quellcode:
#include <ESP32Servo.h> // Nutze die ServoESP32-kompatible Bibliothek
Servo servo_g; // Greifer
Servo servo_m; // Mittelgelenk
Servo servo_b; // Basis unten
Servo servo_d; // Drehbewegung
int su = 0; // Greifer Startposition
int tu = 90; // Greifer Zielposition
int sm = 70; // Mittelgelenk Startposition
int tm = 150; // Mittelgelenk Zielposition
int sb = 60; // Basis Startposition
int tb = 100; // Basis Zielposition
int sd = 70; // Drehservo Startposition
int td = 110; // Drehservo Zielposition
void setup() {
servo_g.attach(13); // Greifer an GPIO 13
servo_m.attach(14); // Mittelgelenk an GPIO 14
servo_b.attach(15); // Basis unten an GPIO 15
servo_d.attach(16); // Drehservo an GPIO 16
servo_g.write(su);
servo_m.write(sm);
servo_b.write(sb);
servo_d.write(sd);
delay(1000);
}
void loop() {
// Greifer bewegen
if (su != tu) {
su += (su < tu) ? 1 : -1;
servo_g.write(su);
}
// Mittelgelenk bewegen
if (sm != tm) {
sm += (sm < tm) ? 1 : -1;
servo_m.write(sm);
}
// Basis unten bewegen
if (sb != tb) {
sb += (sb < tb) ? 1 : -1;
servo_b.write(sb);
}
// Drehservo bewegen
if (sd != td) {
sd += (sd < td) ? 1 : -1;
servo_d.write(sd);
}
delay(50); // Bewegungsgeschwindigkeit 50ms pro Schritt
// Bewegungstest - Wenn alle Ziele erreicht sind → Richtungswechsel zur Startposition
if (su == tu && sm == tm && sb == tb && sd == td) {
delay(1000);
tu = (tu == 90) ? 0 : 90;
tm = (tm == 150) ? 70 : 150;
tb = (tb == 100) ? 60 : 100;
td = (td == 110) ? 70 : 110;
}
}
Wenn dann der Arm wie gewünscht im Test-Mode läuft, ist es nicht mehr weit, um kleine Funktionen zu erstellen, wie zB. etwas von A nach B übergeben.
Späteres Ziel –> Aufräumen, Abwaschen usw. 😉
App nun auf Smartphone installieren
nRF Connect für Mobile – iOS gibt es hier:
- Download: App Store Link
Und nRF Connect für Mobile – Android hier:
- Download: Google Play Link
Quellcode für Smartphonesteuerung:
Dieser Sketch erzeugt einen auffindbaren ESP32 der per Bluetooth Kommandos empfangen kann.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <ESP32Servo.h>
// === Servo-Objekte ===
Servo servo1, servo2, servo3, servo4;
// === Aktuelle Positionen der Servos ===
int pos1 = 90, pos2 = 90, pos3 = 90, pos4 = 90;
// === BLE UUIDs für Service und Charakteristik ===
#define SERVICE_UUID "12345678-1234-1234-1234-1234567890ab"
#define CHARACTERISTIC_UUID "abcd1234-abcd-1234-abcd-1234567890ab"
// === Funktion für sanfte Bewegung eines Servos ===
void smoothMove(Servo &servo, int ¤tAngle, int targetAngle) {
int step = (targetAngle > currentAngle) ? 1 : -1;
for (int pos = currentAngle; pos != targetAngle; pos += step) {
servo.write(pos);
delay(10); // Geschwindigkeit: 10 ms pro Schritt
}
servo.write(targetAngle); // Endposition setzen
currentAngle = targetAngle; // Neue Position merken
}
// === Einzelnen Befehl auswerten und Servo bewegen ===
void processCommand(String cmd) {
if (cmd.startsWith("servo1:")) {
int angle = cmd.substring(7).toInt();
smoothMove(servo1, pos1, angle);
} else if (cmd.startsWith("servo2:")) {
int angle = cmd.substring(7).toInt();
smoothMove(servo2, pos2, angle);
} else if (cmd.startsWith("servo3:")) {
int angle = cmd.substring(7).toInt();
smoothMove(servo3, pos3, angle);
} else if (cmd.startsWith("servo4:")) {
int angle = cmd.substring(7).toInt();
smoothMove(servo4, pos4, angle);
}
}
// === BLE Callback: empfängt Daten von App ===
class MyCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String value = String(pCharacteristic->getValue().c_str());
Serial.println("Empfangen: " + value);
// Nachricht in einzelne Befehle aufteilen (Trennung per Leerzeichen)
int start = 0;
while (start < value.length()) {
int end = value.indexOf(' ', start);
if (end == -1) end = value.length();
String cmd = value.substring(start, end);
processCommand(cmd); // Einzelbefehl verarbeiten
start = end + 1;
}
}
};
void setup() {
Serial.begin(115200);
// === Servos initialisieren ===
servo1.setPeriodHertz(100);
servo2.setPeriodHertz(100);
servo3.setPeriodHertz(100);
servo4.setPeriodHertz(100);
servo1.attach(13); // GPIO 13
servo2.attach(14); // GPIO 14
servo3.attach(15); // GPIO 15
servo4.attach(16); // GPIO 16
// === BLE initialisieren ===
BLEDevice::init("ESP32-4Servo");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();
Serial.println("Bluetooth gestartet – bereit für Befehle über nRF Connect");
}
void loop() {
// Hauptloop bleibt leer – alles läuft über BLE-Callback
}
Smartphone-App nRF Connect:
So sendest du Befehle
Du kannst nun in einer Zeile an alle 4 Servos einen Winkel Kommando zwischen 0 und 180 senden
Zum Beispiel wie „servo1:0
, servo2:180
servo3:50
, servo4:110
„
- App öffnen → Nach Geräte scannen → ESP32-4Servo auswählen → „Connect“

Charakteristik finden → Auswahl: UUID abcd1234-abcd-1234-abcd-1234567890ab

„UTF8“ auswählen → z. B. servo1:90
eingeben

Mit Write Absenden → Servo bewegt sich auf 90° oder bis zu 4 gleichzeitig. Bewegungsgeschwindigkeit kann im Sketch angepasst werden.
Jetzt lassen sich die Bewegungen bequem vom Handy aus kontrollieren.
Arduino-Alternative:
Roboterarm mit Arduino ohne manuelle Steuerung
Wer nur den Roboterarm mit Arduino Uno erstellen möchte, um zum Beispiel automatisierte Abläufe im Sketch zu erstellen, sollte die GPIO 3, 5, 6 und 9 verwenden. Und eventuell noch die Servo Bibliothek installieren.

Beispiel-Sketch für Arduino:
#include <Servo.h>
Servo servo_g; // Greifer an Pin 6
Servo servo_m; // Mittelgelenk an Pin 5
Servo servo_b; // Basis unten an Pin 3
Servo servo_d; // Drehservo an Pin 9
int su = 0; // Greifer Startposition (zu)
int tu = 90; // Greifer Zielposition (auf)
int sm = 70; // Mittelgelenk Startposition (gestreckt)
int tm = 150; // Mittelgelenk Zielposition (90grad)
int sb = 30; // Basis Startposition (170grad)
int tb = 110; // Basis Zielposition (190grad)
int sd = 40; // Drehservo Startposition
int td = 140; // Drehservo Zielposition
void setup() {
servo_g.attach(6); // Greifer
servo_m.attach(5); // Mittelgelenk
servo_b.attach(3); // Basis unten
servo_d.attach(9); // Drehservo
servo_g.write(su);
servo_m.write(sm);
servo_b.write(sb);
servo_d.write(sd);
delay(1000);
}
void loop() {
// Greifer bewegen
if (su != tu) {
su += (su < tu) ? 1 : -1;
servo_g.write(su);
}
// Mittelgelenk bewegen
if (sm != tm) {
sm += (sm < tm) ? 1 : -1;
servo_m.write(sm);
}
// Basis unten bewegen
if (sb != tb) {
sb += (sb < tb) ? 1 : -1;
servo_b.write(sb);
}
// Drehservo bewegen
if (sd != td) {
sd += (sd < td) ? 1 : -1;
servo_d.write(sd);
}
delay(30); // Bewegungsgeschwindigkeit
// Wenn alle Ziele erreicht sind → Richtungswechsel
if (su == tu && sm == tm && sb == tb && sd == td) {
delay(1000);
tu = (tu == 90) ? 0 : 90; // Greifer umkehren
tm = (tm == 150) ? 70 : 150; // Mittelgelenk umkehren
tb = (tb == 110) ? 30 : 110; // Basis umkehren
td = (td == 140) ? 40 : 140; // Drehservo umkehren
}
}