Otto DIY Roboter in nur einer Stunde selbst bauen
Komponenten und Zubehör
| Bauteil | Menge | Hinweis |
|---|---|---|
| Otto DIY Builder Kit | 1 | |
| Otto DIY Arduino NANO Shield I/O | 1 | |
| Arduino Nano Every | 1 | |
| USB-A auf Mini-USB Kabel | 1 | |
| SG90 Mikro-Servomotor | 4 | |
| Summer (Buzzer) | 1 | |
| Female/Female Jumper-Kabel | 6 | |
| Otto DIY 8x8mm Mikroschalter Selbstverriegelung Ein/Aus | 1 | |
| 4xAA Batteriehalter | 1 | 2x 4AA Batterien stapelbar |
| AA Batterien | 1 |
Benötigte Werkzeuge und Maschinen
- 3D-Drucker (generisch)
- Kreuzschlitz-Schraubendreher (Phillips)
Apps und Online-Dienste
- Arduino IDE
- Arduino Web Editor
- Microsoft Visual Studio Code Extension for Arduino
Über dieses Projekt
Wer ist Otto?
Otto ist ein interaktiver Roboter, den jeder bauen kann!

Was kann Otto?
Otto läuft, tanzt, macht Geräusche und weicht Hindernissen aus.

Warum ist Otto besonders?
Otto ist vollständig Open Source, Arduino-kompatibel, 3D-druckbar und verfolgt eine soziale Mission: eine inklusive Umgebung für Kinder zu schaffen.

Otto wurde vom Roboter „BoB the BiPed“ inspiriert und nutzt Code des Open-Source-Zweibeiners „Zowi“.

Lizenz: CC-BY-SA
Otto unterscheidet sich durch seine kompakte Größe (11 cm × 7 cm × 12 cm), saubere Komponentenintegration und ausdrucksstarke Gesichtszüge. Mit Standardbauteilen und 3D-gedruckten Teilen, einfachen elektronischen Verbindungen (fast kein Löten nötig) und grundlegenden Programmierkenntnissen baust du deinen eigenen niedlichen Otto-Freund in nur einer Stunde – oder sogar schneller!
Otto wurde mit Autodesk 123D Design (heute Tinkercad) entworfen, sodass du ihn einfach anpassen und weiterentwickeln kannst.

Diese Anleitung konzentriert sich auf den Bau der einfachen Otto DIY Version; es gibt auch fortgeschrittenere Modelle wie Otto DIY+ oder Tito.
Schritt 1: Alle Teile und Werkzeuge zusammenstellen

Sammle alle Hardware-Komponenten, die du für den Zusammenbau benötigst. Ein Mini-Breadboard kann als Alternative zum Shield dienen, erfordert aber mehr Verkabelung und Zeit.
Anschließend müssen nur 6 Teile 3D-gedruckt werden:
- 3D-gedruckter Kopf
- 3D-gedruckter Körper
- 3D-gedrucktes Bein ×2
- 3D-gedruckter Fuß ×2
Optional: Seitenschneider zum Nachbearbeiten der 3D-Teile (bei guter Druckqualität nicht nötig) und Lötkolben (falls Batteriebetrieb gewünscht, sonst reicht USB-Stromversorgung).
Lade alle .stl-Dateien herunter. Falls du keinen 3D-Drucker hast, kannst du einen lokalen Makerspace aufsuchen.
Falls Teile schwer zu beschaffen sind, kannst du komplette Kits kaufen: www.ottodiy.com
3D-Druck-Einstellungen

Otto ist optimal für den 3D-Druck konstruiert. Die heruntergeladenen Dateien sind bereits richtig orientiert und zentriert. Empfohlene Parameter:
- FDM-3D-Drucker mit PLA-Material
- Keine Stützstrukturen oder Rafts nötig
- Schichthöhe: 0,15 mm
- Fülldichte: 20 %
Zum Slicen eignet sich kostenlose Software wie Cura oder FlashPrint (bei FlashForge-Druckern). Bei externem Druckservice entfällt dieser Schritt.
Nach dem Druck müssen die Bereiche an Beinen und Füßen, an denen die Motoren befestigt werden, leicht nachbearbeitet werden.
Teile prüfen
Wie in Schritt 2 erwähnt, werden die Mikro-Servomotoren mit 3 Schrauben geliefert. Die Teile sind nun nummeriert für bessere Übersicht.
Denke daran, deinen Mini-Schraubendreher zu magnetisieren.
Lade die Bauanleitung herunter oder drucke sie aus, oder folge einfach diesem Video:
Fuß-Servos montieren
Setze den Mikro-Servo in den Fuß ein und drücke ihn fest hinein. Falls er zu fest sitzt, bearbeite den Bereich mit einem Seitenschneider nach.
Wichtig: Prüfe, ob der Servo sich mindestens 90 Grad in jede Richtung drehen kann.
Nach dem Funktionstest fixiere ihn nur mit der kleinen Schraube. Wiederhole den Vorgang für den anderen Fuß.
Servos am Körper befestigen
Setze die anderen 2 Mikro-Servos in die vorgesehenen Positionen im 3D-gedruckten Körper ein und befestige sie nur mit den Spitzschrauben.
Beine am Körper befestigen
Verbinde die Beine mit den Servo-Hörnern. Wie bei den Fuß-Servos muss sichergestellt sein, dass die Beine sich 90 Grad in jede Richtung relativ zum Körper drehen können.
Nach der Ausrichtung fixiere sie mit den kleinen Schrauben durch das Loch im Bein.
Füße an den Beinen befestigen
Achte auf die Kabel wie in der Abbildung gezeigt: Führe die Kabel durch die Schlitze im Körper und durch die Löcher in den Beinen.
In korrekter Position fixiere sie von hinten mit den Spitzschrauben.
Kopf montieren
Beginne mit dem Ultraschallsensor: Ziehe die „Augen“ vollständig heraus.
Nachdem der Arduino Nano auf dem Shield sitzt, kannst du optional das Plus-Kabel des Batteriehalters an Vin und das Minus-Kabel an einen GND-Pin löten.
Setze beide Platinen diagonal zusammen, sodass der USB-Anschluss zur Öffnung im 3D-gedruckten Kopf zeigt, und befestige sie mit den letzten 2 Spitzschrauben.
Schritt 9: Elektrische Verbindungen

Bereite die DuPont-Kabel, den Mikroschalter und den Summer vor.
Folge dem Pin-Belegungsplan und achte auf die korrekte Positionierung.
Falls du kein Arduino Nano Shield hast, kannst du ein Breadboard und dieses Tinkercad-Schaltbild verwenden.
Programmierung mit Blöcken
Du kannst den Arduino-Quellcode von der Otto Wikifactory nutzen.
Hier sind ergänzende Tutorials für Einsteiger zu jedem Bauteil:
Besuche www.ottodiy.com
Die Otto DIY+ Version bietet weitere Möglichkeiten: Bluetooth, mehr Sensoren und Interaktionen.
Teile deine Remixes und Modifikationen in der Wikifactory.
Code
Hindernisvermeidung
Otto läuft endlos, bis er ein Hindernis im Bereich erkennt. Er erschrickt, geht zurück, dreht sich und läuft weiter.
//----------------------------------------------------------------
//-- Zowi basic firmware v2 adaptiert für Otto
//-- (c) BQ. Veröffentlicht unter GPL-Lizenz
//-- 04. Dezember 2015
//-- Autoren: Anita de Prado, Jose Alberca, Javier Isabel,
//-- Juan Gonzalez (obijuan), Irene Sanz
//-----------------------------------------------------------------
//-- Experimentiere mit allen Funktionen, die Otto dank Zowi hat!
//-----------------------------------------------------------------
#include <Servo.h>
#include <Oscillator.h>
#include <EEPROM.h>
#include <BatReader.h>
#include <US.h>
#include <LedMatrix.h>
#include <EnableInterrupt.h>
#include <OttoSerialCommand.h>
OttoSerialCommand SCmd; // SerialCommand Objekt
//-- Otto Bibliothek
#include <Otto.h>
Otto Otto; // Das ist Otto!
//---------------------------------------------------------
//-- Erster Schritt: Pins konfigurieren, an denen die Servos angeschlossen sind
/* --------------- | O O | |---------------| YR 3==> | | <== YL 2 --------------- || || || || RR 5==> ----- ------ <== RL 4 |----- ------| */
#define PIN_YL 2 // servo[0]
#define PIN_YR 3 // servo[1]
#define PIN_RL 4 // servo[2]
#define PIN_RR 5 // servo[3]
//--- Otto Buttons
#define PIN_SecondButton 6
#define PIN_ThirdButton 7
/////////////////////////////////////////////////////////////////
//-- Globale Variablen ------------------------------------------//
/////////////////////////////////////////////////////////////////
const char programID[] = "Otto_todo"; // Jedes Programm hat eine ID
const char name_fac = '$'; // Fabrikname
const char name_fir = '#'; // Vorname
//-- Bewegungsparameter
int T = 1000; // Initiale Bewegungsdauer
int moveId = 0; // Bewegungsnummer
int moveSize = 15; // Mit Höhe mancher Bewegungen assoziiert
//---------------------------------------------------------
//-- Otto hat 5 Modi:
//-- * MODE = 0: Otto wartet
//-- * MODE = 1: Tanzmodus!
//-- * MODE = 2: Hinderniserkennungsmodus
//-- * MODE = 3: Geräuscherkennungsmodus
//-- * MODE = 4: OttoPAD oder Teleoperationsmodus (SerialPort überwachen).
//---------------------------------------------------------
volatile int MODE = 0; // Zustand in der Hauptzustandsmaschine
volatile bool buttonPushed = false;
volatile bool buttonAPushed = false;
volatile bool buttonBPushed = false;
unsigned long previousMillis = 0;
int randomDance = 0;
int randomSteps = 0;
bool obstacleDetected = false;
/////////////////////////////////////////////////////////////////
//-- Setup ------------------------------------------------------//
/////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
pinMode(PIN_SecondButton, INPUT);
pinMode(PIN_ThirdButton, INPUT);
// Servo-Pins setzen
Otto.init(PIN_YL, PIN_YR, PIN_RL, PIN_RR, true);
// Servo-Trims manuell setzen und im EEPROM speichern (auskommentieren nach Kalibrierung)
// Otto.setTrims(TRIM_YL, TRIM_YR, TRIM_RL, TRIM_RR);
// Otto.saveTrimsOnEEPROM();
// Zufalls-Seed setzen
randomSeed(analogRead(A6));
// Interrupts aktivieren
enableInterrupt(PIN_SecondButton, secondButtonPushed, RISING);
enableInterrupt(PIN_ThirdButton, thirdButtonPushed, RISING);
// Callbacks für SerialCommand
SCmd.addCommand("S", receiveStop);
SCmd.addCommand("L", receiveLED);
SCmd.addCommand("T", recieveBuzzer);
SCmd.addCommand("M", receiveMovement);
SCmd.addCommand("H", receiveGesture);
SCmd.addCommand("K", receiveSing);
SCmd.addCommand("C", receiveTrims);
SCmd.addCommand("G", receiveServo);
SCmd.addCommand("D", requestDistance);
SCmd.addCommand("N", requestNoise);
SCmd.addCommand("B", requestBattery);
SCmd.addCommand("I", requestProgramId);
SCmd.addDefaultHandler(receiveStop);
// Otto erwacht!
Otto.sing(S_connection);
Otto.home();
delay(50);
// Animationssequenz: Uuuuuh - kurzer Überraschungsmoment
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 8; j++) {
if (buttonPushed) break;
Otto.putAnimationMouth(littleUuh, j);
delay(150);
}
}
// Lächeln für glücklichen Otto :)
if (!buttonPushed) {
Otto.putMouth(smile);
Otto.sing(S_happy);
delay(200);
}
// Wenn Ottos Name '#' ist, wurde er noch nicht getauft
if (EEPROM.read(5) == name_fir) {
if (!buttonPushed) { Otto.jump(1, 700); delay(200); }
if (!buttonPushed) { Otto.shakeLeg(1, T, 1); }
if (!buttonPushed) {
Otto.putMouth(smallSurprise);
Otto.swing(2, 800, 20);
Otto.home();
}
}
if (!buttonPushed) { Otto.putMouth(happyOpen); }
previousMillis = millis();
}
/////////////////////////////////////////////////////////////////
//-- Haupt-Schleife ---------------------------------------------//
/////////////////////////////////////////////////////////////////
void loop() {
//-- MODE 2 - Hinderniserkennungsmodus
if (obstacleDetected) {
Otto.sing(S_surprise);
Otto.playGesture(OttoFretful);
Otto.sing(S_fart3);
Otto.walk(2, 1300, -1);
Otto.turn(2, 1000, -1);
delay(50);
obstacleDetector();
} else {
Otto.walk(1, 1000, 1);
obstacleDetector();
}
}
/////////////////////////////////////////////////////////////////
//-- Funktionen -------------------------------------------------//
/////////////////////////////////////////////////////////////////
// Zweiter Button gedrückt
void secondButtonPushed() {
buttonAPushed = true;
if (!buttonPushed) {
buttonPushed = true;
Otto.putMouth(smallSurprise);
}
}
// Dritter Button gedrückt
void thirdButtonPushed() {
buttonBPushed = true;
if (!buttonPushed) {
buttonPushed = true;
Otto.putMouth(smallSurprise);
}
}
// Distanzsensor lesen & obstacleDetected aktualisieren
void obstacleDetector() {
int distance = Otto.getDistance();
if (distance < 15) obstacleDetected = true;
else obstacleDetected = false;
}
// Stop-Befehl empfangen
void receiveStop() {
sendAck();
Otto.home();
sendFinalAck();
}
// LED-Befehle empfangen
void receiveLED() {
sendAck();
Otto.home();
unsigned long int matrix;
char *arg, *endstr;
arg = SCmd.next();
if (arg != NULL) {
matrix = strtoul(arg, &endstr, 2);
Otto.putMouth(matrix, false);
} else {
Otto.putMouth(xMouth);
delay(2000);
Otto.clearMouth();
}
sendFinalAck();
}
// Summer-Befehle empfangen
void recieveBuzzer() {
sendAck();
Otto.home();
bool error = false;
int frec, duration;
char *arg;
arg = SCmd.next();
if (arg != NULL) frec = atoi(arg); else error = true;
arg = SCmd.next();
if (arg != NULL) duration = atoi(arg); else error = true;
if (error) {
Otto.putMouth(xMouth);
delay(2000);
Otto.clearMouth();
} else {
Otto._tone(frec, duration, 1);
}
sendFinalAck();
}
// Trim-Befehle empfangen
void receiveTrims() {
sendAck();
Otto.home();
int trim_YL, trim_YR, trim_RL, trim_RR;
bool error = false;
char *arg;
arg = SCmd.next(); if (arg != NULL) trim_YL = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) trim_YR = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) trim_RL = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) trim_RR = atoi(arg); else error = true;
if (error) {
Otto.putMouth(xMouth);
delay(2000);
Otto.clearMouth();
} else {
Otto.setTrims(trim_YL, trim_YR, trim_RL, trim_RR);
Otto.saveTrimsOnEEPROM();
}
sendFinalAck();
}
// Servo-Befehle empfangen
void receiveServo() {
sendAck();
moveId = 30;
bool error = false;
char *arg;
int servo_YL, servo_YR, servo_RL, servo_RR;
arg = SCmd.next(); if (arg != NULL) servo_YL = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) servo_YR = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) servo_RL = atoi(arg); else error = true;
arg = SCmd.next(); if (arg != NULL) servo_RR = atoi(arg); else error = true;
if (error) {
Otto.putMouth(xMouth);
delay(2000);
Otto.clearMouth();
} else {
int servoPos[4] = {servo_YL, servo_YR, servo_RL, servo_RR};
Otto._moveServos(200, servoPos);
}
sendFinalAck();
}
// Bewegungsbefehle empfangen
void receiveMovement() {
sendAck();
if (Otto.getRestState() == true) Otto.setRestState(false);
char *arg;
arg = SCmd.next(); if (arg != NULL) moveId = atoi(arg);
else { Otto.putMouth(xMouth); delay(2000); Otto.clearMouth(); moveId = 0; }
arg = SCmd.next(); if (arg != NULL) T = atoi(arg); else T = 1000;
arg = SCmd.next(); if (arg != NULL) moveSize = atoi(arg); else moveSize = 15;
}
// Rechte Bewegung ausführen
void move(int moveId) {
bool manualMode = false;
switch (moveId) {
case 0: Otto.home(); break;
case 1: Otto.walk(1, T, 1); break;
case 2: Otto.walk(1, T, -1); break;
case 3: Otto.turn(1, T, 1); break;
case 4: Otto.turn(1, T, -1); break;
case 5: Otto.updown(1, T, moveSize); break;
case 6: Otto.moonwalker(1, T, moveSize, 1); break;
case 7: Otto.moonwalker(1, T, moveSize, -1); break;
case 8: Otto.swing(1, T, moveSize); break;
case 9: Otto.crusaito(1, T, moveSize, 1); break;
case 10: Otto.crusaito(1, T, moveSize, -1); break;
case 11: Otto.jump(1, T); break;
case 12: Otto.flapping(1, T, moveSize, 1); break;
case 13: Otto.flapping(1, T, moveSize, -1); break;
case 14: Otto.tiptoeSwing(1, T, moveSize); break;
case 15: Otto.bend(1, T, 1); break;
case 16: Otto.bend(1, T, -1); break;
case 17: Otto.shakeLeg(1, T, 1); break;
case 18: Otto.shakeLeg(1, T, -1); break;
case 19: Otto.jitter(1, T, moveSize); break;
case 20: Otto.ascendingTurn(1, T, moveSize); break;
default: manualMode = true; break;
}
if (!manualMode) sendFinalAck();
}
// Gesten-Befehle empfangen
void receiveGesture() {
sendAck();
Otto.home();
int gesture = 0;
char *arg = SCmd.next();
if (arg != NULL) gesture = atoi(arg);
else { Otto.putMouth(xMouth); delay(2000); Otto.clearMouth(); }
switch (gesture) {
case 1: Otto.playGesture(OttoHappy); break;
case 2: Otto.playGesture(OttoSuperHappy); break;
case 3: Otto.playGesture(OttoSad); break;
case 4: Otto.playGesture(OttoSleeping); break;
case 5: Otto.playGesture(OttoFart); break;
case 6: Otto.playGesture(OttoConfused); break;
case 7: Otto.playGesture(OttoLove); break;
case 8: Otto.playGesture(OttoAngry); break;
case 9: Otto.playGesture(OttoFretful); break;
case 10: Otto.playGesture(OttoMagic); break;
case 11: Otto.playGesture(OttoWave); break;
case 12: Otto.playGesture(OttoVictory); break;
case 13: Otto.playGesture(OttoFail); break;
default: break;
}
sendFinalAck();
}
// Sound-Befehle empfangen
void receiveSing() {
sendAck();
Otto.home();
int sing = 0;
char *arg = SCmd.next();
if (arg != NULL) sing = atoi(arg);
else { Otto.putMouth(xMouth); delay(2000); Otto.clearMouth(); }
switch (sing) {
case 1: Otto.sing(S_connection); break;
case 2: Otto.sing(S_disconnection); break;
case 3: Otto.sing(S_surprise); break;
case 4: Otto.sing(S_OhOoh); break;
case 5: Otto.sing(S_OhOoh2); break;
case 6: Otto.sing(S_cuddly); break;
case 7: Otto.sing(S_sleeping); break;
case 8: Otto.sing(S_happy); break;
case 9: Otto.sing(S_superHappy); break;
case 10: Otto.sing(S_happy_short); break;
case 11: Otto.sing(S_sad); break;
case 12: Otto.sing(S_confused); break;
case 13: Otto.sing(S_fart1); break;
case 14: Otto.sing(S_fart2); break;
case 15: Otto.sing(S_fart3); break;
case 16: Otto.sing(S_mode1); break;
case 17: Otto.sing(S_mode2); break;
case 18: Otto.sing(S_mode3); break;
case 19: Otto.sing(S_buttonPushed); break;
default: break;
}
sendFinalAck();
}
// Ultraschall-Messung senden (Distanz in cm)
void requestDistance() {
Otto.home();
int distance = Otto.getDistance();
Serial.print(F("&&")); Serial.print(F("D ")); Serial.print(distance); Serial.println(F("%%")); Serial.flush();
}
// Geräuschsensor-Messung senden
void requestNoise() {
Otto.home();
int microphone = Otto.getNoise();
Serial.print(F("&&")); Serial.print(F("N ")); Serial.print(microphone); Serial.println(F("%%")); Serial.flush();
}
// Batteriespannung in Prozent senden
void requestBattery() {
Otto.home();
double batteryLevel = Otto.getBatteryLevel();
Serial.print(F("&&")); Serial.print(F("B ")); Serial.print(batteryLevel); Serial.println(F("%%")); Serial.flush();
}
// Programm-ID senden
void requestProgramId() {
Otto.home();
Serial.print(F("&&")); Serial.print(F("I ")); Serial.print(programID); Serial.println(F("%%")); Serial.flush();
}
// ACK senden
void sendAck() {
delay(30);
Serial.print(F("&&")); Serial.print(F("A")); Serial.println(F("%%")); Serial.flush();
}
// Finales ACK senden
void sendFinalAck() {
delay(30);
Serial.print(F("&&")); Serial.print(F("F")); Serial.println(F("%%")); Serial.flush();
}
// Animationsfunktionen
void OttoLowBatteryAlarm() {
double batteryLevel = Otto.getBatteryLevel();
if (batteryLevel < 45) {
while (!buttonPushed) {
Otto.putMouth(thunder);
Otto.bendTones(880, 2000, 1.04, 8, 3);
delay(30);
Otto.bendTones(2000, 880, 1.02, 8, 3);
Otto.clearMouth();
delay(500);
}
}
}
void OttoSleeping_withInterrupts() {
int bedPos_0[4] = {100, 80, 60, 120};
if (!buttonPushed) Otto._moveServos(700, bedPos_0);
for (int i = 0; i < 4; i++) {
if (buttonPushed) break;
Otto.putAnimationMouth(dreamMouth, 0);
Otto.bendTones(100, 200, 1.04, 10, 10);
if (buttonPushed) break;
Otto.putAnimationMouth(dreamMouth, 1);
Otto.bendTones(200, 300, 1.04, 10, 10);
if (buttonPushed) break;
Otto.putAnimationMouth(dreamMouth, 2);
Otto.bendTones(300, 500, 1.04, 10, 10);
delay(500);
if (buttonPushed) break;
Otto.putAnimationMouth(dreamMouth, 1);
Otto.bendTones(400, 250, 1.04, 10, 1);
if (buttonPushed) break;
Otto.putAnimationMouth(dreamMouth, 0);
Otto.bendTones(250, 100, 1.04, 10, 1);
delay(500);
}
if (!buttonPushed) { Otto.putMouth(lineMouth); Otto.sing(S_cuddly); }
Otto.home();
if (!buttonPushed) Otto.putMouth(happyOpen);
}
Arduino-Quellcode & Bibliotheken
Installiere zuerst diese Bibliotheken: https://github.com/OttoDIY/DIY
Individuelle Teile und Gehäuse
Thingiverse .STL 3D-druckbare Dateien
3D-Druckteile für den Bau von Otto. CAD-Datei auf thingiverse.com
Schaltpläne
Servos, Ultraschallsensor, Summer und Batterieanschlüsse – folge den Kabeln. Verwende DuPont-Kabel.

Herstellungsprozess
- DIY Thermocam: Vom Klassenraum-Idee zum globalen Produkt
- Benutzerdefinierte Objektsensoren für LEGO Mindstorms EV3 bauen: DIY Licht-basierte „Augen“
- DIY Google Voice HAT für Raspberry Pi selbst bauen
- Internet-gesteuerter Video-Streaming-Roboter mit Arduino & Raspberry Pi bauen
- Ballistisches Fallschirm-Rückführungssystem für UAVs designen
- Reinforcement Learning: Die Zukunft intelligenter Industrierobotik
- Sauron bauen: Der semi-humanoide Roboter – Komplettanleitung mit Arduino Mega 2560
- Roboterarm selbst bauen: Risiken und Chancen
- Bewährte Schritte zum Aufbau eines robusten IoT-Prototyps
- Warum jeder Heimwerker einen Druckluftkompressor braucht: Vorteile & Auswahl des richtigen Modells