In diesem Artikel erfährst du, wie man einen automatischen Arduino Aufzug für ein Hausmodell baut und programmiert. Mich hat die folgende Frage erreicht:
Hallo, ich würde gern ein Modellhochhaus mit einem selbstständig fahrenden Aufzug ausstatten. Das Hochhaus hat 12 Stockwerke.
Das klingt doch spannend. Glücklicherweise müssen wir dafür keinen kompletten Aufzug mit Ruftasten, Etagenauswahl und schließenden Türen bauen. Konzentrieren wir uns also nur um automatisches Hoch- und runterfahren. Herzstück dieses Projektes ist ein Schrittmotor (auch Stepper-Motor). Man nutzt sie unteranderem in 3D-Druckern und CNC-Fräsen. Sie lassen sich schrittweise (um z. B. 1,5 Grad) drehen. Wir statten die Achse so eines Schrittmotors mit einer Trommel aus, auf der das Tragseil des Aufzugs auf- und abgewickelt werden kann. Nun müssen wir uns nur noch überlegen, wie viele Schritte der Motor ausführen soll, um ein Stockwerk anzufahren. Da Stockwerke unterschiedlich hoch sein können (oft ist das Erdgeschoss höher), speichern wir die Schritte vom einen zum anderen Stockwerk einfach in einem Array. Die nötigen Schritte kann man dann einfach ausprobieren. Sie sind vom Motor, dem Durchmesser der Trommel und der Höhe der Stockwerke abhängig.
Das unterste Geschoss (z. B. Erdgeschoss, Keller, Tiefgarage) dient uns als Referenzwert. Ab hier geht es nur nach oben. Legen wir noch für uns fest, das im Uhrzeigersinn auf, gegen den Uhrzeigersinn abgewickelt wird.
int stockwerkSchritte[] = {150,100,100,100,100,100,100,100 };
Um also vom Erdgeschoss in den ersten Stock zu gelangen, muss der Motor 150 Schritte im Uhrzeigersinn ausführen.
Achtung: Schrittmotoren sind sehr kraftvoll. Wenn der Motor weiterdreht, als der Aufzug fahren kann, kann das Seil reißen oder das ganze Modell Schaden nehmen. Auch kann auch Verletzungsgefahr bestehen. Ich schlage vor, sich vorsichtig an die oberste Etage heranzutasten. Im Notfall schnell die Stromversorgung trennen.
Bauteile
- L293D (falls nicht im Starterkit enthalten)
- Nema 17 Schrittmotor
- Externes Netzteil
- Kühlkörper
Schaltung

Die Schaltung besteht aus einem Arduino, das über ein L293D-Motortreiber einen Schrittmotor (Nema17) steuert. Versorgt wird das Arduino per USB-Strom, der Stepper über ein externes Netzteil. Den L293D sollte man auf jeden Fall mit einem Kühlkörper ausstatten.
Programm: Arduino Aufzug
Nun ist es Zeit, das Programm zu planen. Der Aufzug soll also im untersten Stockwerk starten, sich per Zufall ein Zielstockwerk suchen und dorthin fahren. Da soll er warten, sich wiederum ein Stockwerk suchen und dorthin fahren.
Der Einfachheit halber nehmen wir zwei Variablen, die wir später nur noch vergleichen müssen. Dabei handelt es sich um die aktuelle Position des Aufzugs (positionAktuell) und die Zielposition (positionZiel). Beide Variablen speichern die Schritte, des Schrittmotors. Wenn beide gleich sind, ist der Aufzug an der Zielposition, also in der Zieletage. Dann ist es Zeit für eine Pause und daran, ein neues Zielstockwerk auszuwählen. Mithilfe des Zielstockwerks lässt sich die neue Zielposition errechnen.
Nun benötigen wir nur noch zwei if-Abfragen zum Vergleich der beiden Variablen. Entweder ist die Zielposition größer als die aktuelle Position, dann soll der Aufzug nach oben fahren, oder sie ist kleiner, dann soll der Aufzug nach unten fahren.
Wenn du das folgende Programm auf das Arduino Board lädst, kannst du im seriellen Monitor (Arduino Menü>Werkzeuge>Serieller Monitor) sehen, was das Programm gerade tut. Wichtig ist, dass du die Baud-Rate (unten rechts im seriellen Monitor) auf 115200 setzt. Der Motor sollte sich ebenfalls schon drehen. Nun kannst du die Schritte pro Stockwerk kalibrieren.
#include <Stepper.h> // Einbinden der Stepper-Programmbibliothek #define STEPS 200 // Schritte pro Umdrehung - ist für dieses Projekt nicht wichtig Stepper stepper(STEPS, 8, 9, 10, 11); // erzeugt ein stepper-Objekt int motorSpeed = 1; // je größer, desto langsamer bewegt sich der Aufzug int stockwerkSchritte[] = {150, 100, 100, 100, 100, 100, 100, 100, 100}; // speichert die Steps von Etage zu Etage int anzahlStockwerke = 10; // Anzahl der Stockwerke int aktuellesStockwerk = 0; // 0 = unterstes Geschoss int zielStockwerk = 0; // speichert das Zielstockwerk int positionAktuell = 0; // Position in Schritten, an denen sich der Aufzug befindet int positionZiel = 0; // Zielposition in Schritten int wartezeitMin = 1000; // kürzeste Wartezeit im Stockwerk int wartezeitMax = 5000; // längste Wartezeit im Stockwerk void setup() { Serial.begin(115200); // Serielle Ausgabe stepper.setSpeed(30); // hier kann man die Drehgeschwindigkeit festlegen randomSeed(analogRead(5)); // startet den Zufalls-Nummerngenerator } void loop() { Serial.print("positionZiel= "); Serial.print(positionZiel); // Serielle Ausgabe Serial.print(" positionAktuell= "); Serial.print(positionAktuell); // Serielle Ausgabe if (positionAktuell == positionZiel) { // Aufzug ist am Ziel angekommen Serial.println(" Angekommen"); delay(wartezeitMin + random(wartezeitMax - wartezeitMin));// Warte eine zufällige Zeit zwischen wartezeitMin und Max zielStockwerk = random(anzahlStockwerke); // ermittelt ein neues Stockwerk while (zielStockwerk == aktuellesStockwerk) { // solange das neue Stockwerk aber das aktuelle Stockwerk ist zielStockwerk = random(anzahlStockwerke); // wird noch einmal ermittelt } positionZiel=0; // Zielposition zurücksetzen for (int i = 0; i < zielStockwerk; i++) { // Schritte bis zur Zielposition vom Nullpunkt berechnen positionZiel += stockwerkSchritte[i]; } } else if (positionAktuell < positionZiel) { // nach oben fahren Serial.println(" Nach oben fahren"); // Serielle Ausgabe stepper.step(1); // erhöhe um einen Step positionAktuell++; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } else if (positionAktuell > positionZiel) { // nach unten fahren Serial.println(" Nach unten fahren"); // Serielle Ausgabe stepper.step(-1); // erhöhe um einen Step positionAktuell--; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } }
Bei Stormunterbrechung wird leider die Position des Fahrstuhls vergessen. Um die Position zwischenzuspeichern, kann man den Eprom (Speicher) des Arduino-Boards verwenden:
#include <Stepper.h> // Einbinden der Stepper-Programmbibliothek #include <EEPROM.h> // Einbinden der EEPROM-Bibliothek #define STEPS 200 // Schritte pro Umdrehung Stepper stepper(STEPS, 8, 9, 10, 11); // erzeugt ein stepper-Objekt int motorSpeed = 1; // je größer, desto langsamer bewegt sich der Aufzug int stockwerkSchritte[] = {150, 100, 100, 100, 100, 100, 100, 100, 100}; // speichert die Steps von Etage zu Etage int anzahlStockwerke = 10; // Anzahl der Stockwerke int aktuellesStockwerk = 0; // 0 = unterstes Geschoss int zielStockwerk = 0; // speichert das Zielstockwerk int positionAktuell = 0; // Position in Schritten, an denen sich der Aufzug befindet int positionZiel = 0; // Zielposition in Schritten int wartezeitMin = 1000; // kürzeste Wartezeit im Stockwerk int wartezeitMax = 5000; // längste Wartezeit im Stockwerk void setup() { Serial.begin(115200); // Serielle Ausgabe stepper.setSpeed(30); // Drehgeschwindigkeit festlegen randomSeed(analogRead(5)); // Zufallszahlengenerator initialisieren // Position aus EEPROM laden positionAktuell = EEPROM.read(0) | (EEPROM.read(1) << 8); aktuellesStockwerk = berechneStockwerk(positionAktuell); Serial.print("EEPROM: PositionAktuell = "); Serial.println(positionAktuell); } void loop() { Serial.print("positionZiel= "); Serial.print(positionZiel); // Serielle Ausgabe Serial.print(" positionAktuell= "); Serial.print(positionAktuell); // Serielle Ausgabe if (positionAktuell == positionZiel) { // Aufzug ist am Ziel angekommen Serial.println(" Angekommen"); delay(wartezeitMin + random(wartezeitMax - wartezeitMin));// Warte eine zufällige Zeit zwischen wartezeitMin und Max zielStockwerk = random(anzahlStockwerke); // Ermittelt ein neues Stockwerk while (zielStockwerk == aktuellesStockwerk) { // Solange das neue Stockwerk das aktuelle ist zielStockwerk = random(anzahlStockwerke); // wird erneut ermittelt } positionZiel = berechnePosition(zielStockwerk); // Zielposition in Schritten berechnen } else if (positionAktuell < positionZiel) { // Nach oben fahren Serial.println(" Nach oben fahren"); stepper.step(1); // Schritt nach oben positionAktuell++; // Position erhöhen delay(motorSpeed); // Wartezeit für Geschwindigkeit } else if (positionAktuell > positionZiel) { // Nach unten fahren Serial.println(" Nach unten fahren"); stepper.step(-1); // Schritt nach unten positionAktuell--; // Position verringern delay(motorSpeed); // Wartezeit für Geschwindigkeit } // Position im EEPROM speichern EEPROM.write(0, positionAktuell & 0xFF); // Niedriges Byte EEPROM.write(1, (positionAktuell >> 8) & 0xFF); // Hohes Byte } // Hilfsfunktion: Stockwerk basierend auf der Position berechnen int berechneStockwerk(int position) { int summe = 0; for (int i = 0; i < anzahlStockwerke; i++) { summe += stockwerkSchritte[i]; if (position < summe) { return i; } } return anzahlStockwerke - 1; } // Hilfsfunktion: Zielposition basierend auf dem Stockwerk berechnen int berechnePosition(int stockwerk) { int position = 0; for (int i = 0; i < stockwerk; i++) { position += stockwerkSchritte[i]; } return position; }
Änderungen im Detail:
- EEPROM-Integration:
- Die aktuelle Position wird im
EEPROM
in zwei Bytes gespeichert. - Die Position wird beim Start aus dem EEPROM geladen.
- Die aktuelle Position wird im
- Helferfunktionen:
berechneStockwerk()
berechnet das aktuelle Stockwerk anhand der Position.berechnePosition()
berechnet die Zielposition basierend auf dem Zielstockwerk.
- Setup:
- Lädt die zuletzt gespeicherte Position aus dem EEPROM und berechnet das aktuelle Stockwerk.
- Loop:
- Nach jedem Schritt wird die aktuelle Position im EEPROM gespeichert.
Vorteile:
Das System kann beim Neustart den Aufzug korrekt positionieren.
Die Position bleibt auch nach einem Stromausfall erhalten.
Variante mit Stepper-Treiber DRV8825
Nun bleibt noch zu erwähnen, dass der L293D eine günstige Einsteiger-Variante ist, jedoch wäre die Verwendung eines Stepper-Treibers wie dem DRV8825* besser. Wie das geht, sehen wir uns hier an:

Die Schaltung besteht aus dem Arduino, dessen Pins 2 und 3 mit dem Dir- und Step-Pin des DRV8825 verbunden sind. Dessen Reset- und Sleep-Pin sind mit 5V+ verbunden. Das gilt auch für den M0-Pin, was ein 1/2 Microstepping ermöglicht. Der Motor dreht dadurch ruhiger.
Versorgt wird der Stepper durch eine externe Stromversorgung (12V), die hier am DRV8825 an den Pins VMOT und GND verbunden sind. Diese wird noch durch einen 100uF-Elektrolytkondensator (>12V) unterstützt.
Beim Stepper handelt es sich um einen NEMA17 (42BYGHW811).
Achtung! Es ist sehr wichtig, dass der maximale Strom des Stepper-Drivers eingestellt wird. Das kann man mit dem kleinen Potentiometer auf dem DRV8825 erledigen. Wie das genau geht, ist in diesem fantastischen Tutorial erklärt. (Leider nur auf Englisch, aber das lässt sich ja übersetzen. Ich nutze dafür gerne deepl.com.)
Code
Der neue Code verwendet die BasicStepperDriver-Bibliothek, die sich über Bibliotheken verwalten, im Arduino Sketch Menü einbinden lässt.
#include "BasicStepperDriver.h" // Einbinden der Stepper-Programmbibliothek #define MOTOR_STEPS 200 // Schritte pro Umdrehung - ist für dieses Projekt nicht wichtig #define RPM 60 // Stepper Geschwindigkeit #define MICROSTEPS 2 // Microsteps des Stepper-Motors #define DIR 2 // DIR-Pin des Stepper-Motors #define STEP 3 // STEP-Pin des Stepper-Motors #define SLEEP 13 // sleep Funktion des Stepper-Treibers #define MOTOR_ACCEL 2000 #define MOTOR_DECEL 1000 #include "DRV8825.h" #define MODE0 10 #define MODE1 11 #define MODE2 12 DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2); // erzeugt ein stepper-Objekt //BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP, SLEEP); // erzeugt ein stepper-Objekt int motorSpeed = 1; // je größer, desto langsamer bewegt sich der Aufzug int stockwerkSchritte[] = {150, 100, 100, 100, 100, 100, 100, 100, 100}; // speichert die Steps von Etage zu Etage int anzahlStockwerke = 10; // Anzahl der Stockwerke int aktuellesStockwerk = 0; // 0 = unterstes Geschoss int zielStockwerk = 0; // speichert das Zielstockwerk int positionAktuell = 0; // Position in Schritten, an denen sich der Aufzug befindet int positionZiel = 0; // Zielposition in Schritten int wartezeitMin = 1000; // kürzeste Wartezeit im Stockwerk int wartezeitMax = 5000; // längste Wartezeit im Stockwerk void setup() { stepper.begin(RPM, MICROSTEPS); Serial.begin(115200); // Serielle Ausgabe //stepper.setSpeed(30); // hier kann man die Drehgeschwindigkeit festlegen randomSeed(analogRead(5)); // startet den Zufalls-Nummerngenerator } void loop() { Serial.print("positionZiel= "); Serial.print(positionZiel); // Serielle Ausgabe Serial.print(" positionAktuell= "); Serial.print(positionAktuell); // Serielle Ausgabe if (positionAktuell == positionZiel) { // Aufzug ist am Ziel angekommen Serial.println(" Angekommen"); delay(wartezeitMin + random(wartezeitMax - wartezeitMin));// Warte eine zufällige Zeit zwischen wartezeitMin und Max zielStockwerk = random(anzahlStockwerke); // ermittelt ein neues Stockwerk while (zielStockwerk == aktuellesStockwerk) { // solange das neue Stockwerk aber das aktuelle Stockwerk ist zielStockwerk = random(anzahlStockwerke); // wird noch einmal ermittelt } positionZiel=0; // Zielposition zurücksetzen for (int i = 0; i < zielStockwerk; i++) { // Schritte bis zur Zielposition vom Nullpunkt berechnen positionZiel += stockwerkSchritte[i]; } } else if (positionAktuell < positionZiel) { // nach oben fahren Serial.println(" Nach oben fahren"); // Serielle Ausgabe stepper.rotate(2); //stepper.step(1); // erhöhe um einen Step positionAktuell++; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } else if (positionAktuell > positionZiel) { // nach unten fahren Serial.println(" Nach unten fahren"); // Serielle Ausgabe stepper.rotate(-2); //stepper.step(-1); // erhöhe um einen Step positionAktuell--; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } }
Um die Position des Fahrstuhls bei Stromausfall zwischenzuspeichern, kann man den Eprom (Speicher) des Arduino-Boards verwenden:
#include <EEPROM.h> #include "BasicStepperDriver.h" // Einbinden der Stepper-Programmbibliothek #define MOTOR_STEPS 200 // Schritte pro Umdrehung #define RPM 60 // Stepper Geschwindigkeit #define MICROSTEPS 2 // Microsteps des Stepper-Motors #define DIR 2 // DIR-Pin des Stepper-Motors #define STEP 3 // STEP-Pin des Stepper-Motors #define SLEEP 13 // sleep Funktion des Stepper-Treibers #define MOTOR_ACCEL 2000 #define MOTOR_DECEL 1000 #include "DRV8825.h" #define MODE0 10 #define MODE1 11 #define MODE2 12 DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2); // erzeugt ein stepper-Objekt int motorSpeed = 1; // je größer, desto langsamer bewegt sich der Aufzug int stockwerkSchritte[] = {150, 100, 100, 100, 100, 100, 100, 100, 100}; // speichert die Steps von Etage zu Etage int anzahlStockwerke = 10; // Anzahl der Stockwerke int aktuellesStockwerk = 0; // 0 = unterstes Geschoss int zielStockwerk = 0; // speichert das Zielstockwerk int positionAktuell = 0; // Position in Schritten, an denen sich der Aufzug befindet int positionZiel = 0; // Zielposition in Schritten int wartezeitMin = 1000; // kürzeste Wartezeit im Stockwerk int wartezeitMax = 5000; // längste Wartezeit im Stockwerk void setup() { stepper.begin(RPM, MICROSTEPS); Serial.begin(115200); // Serielle Ausgabe randomSeed(analogRead(5)); // startet den Zufalls-Nummerngenerator // Position aus dem EEPROM laden positionAktuell = EEPROM.read(0) | (EEPROM.read(1) << 8); aktuellesStockwerk = berechneStockwerk(positionAktuell); Serial.print("EEPROM: PositionAktuell = "); Serial.println(positionAktuell); } void loop() { Serial.print("positionZiel= "); Serial.print(positionZiel); // Serielle Ausgabe Serial.print(" positionAktuell= "); Serial.print(positionAktuell); // Serielle Ausgabe if (positionAktuell == positionZiel) { // Aufzug ist am Ziel angekommen Serial.println(" Angekommen"); delay(wartezeitMin + random(wartezeitMax - wartezeitMin));// Warte eine zufällige Zeit zwischen wartezeitMin und Max zielStockwerk = random(anzahlStockwerke); // ermittelt ein neues Stockwerk while (zielStockwerk == aktuellesStockwerk) { // solange das neue Stockwerk aber das aktuelle Stockwerk ist zielStockwerk = random(anzahlStockwerke); // wird noch einmal ermittelt } positionZiel = berechnePosition(zielStockwerk); // Zielposition in Schritten berechnen } else if (positionAktuell < positionZiel) { // nach oben fahren Serial.println(" Nach oben fahren"); // Serielle Ausgabe stepper.rotate(2); positionAktuell++; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } else if (positionAktuell > positionZiel) { // nach unten fahren Serial.println(" Nach unten fahren"); // Serielle Ausgabe stepper.rotate(-2); positionAktuell--; // erhöhe die Positionsvariable delay(motorSpeed); // Wartezeit nimmt Einfluss auf Aufzugsgeschwindigkeit } // Position im EEPROM speichern EEPROM.write(0, positionAktuell & 0xFF); // Niedriges Byte EEPROM.write(1, (positionAktuell >> 8) & 0xFF); // Hohes Byte } // Hilfsfunktion: Stockwerk basierend auf der Position berechnen int berechneStockwerk(int position) { int summe = 0; for (int i = 0; i < anzahlStockwerke; i++) { summe += stockwerkSchritte[i]; if (position < summe) { return i; } } return anzahlStockwerke - 1; } // Hilfsfunktion: Zielposition basierend auf dem Stockwerk berechnen int berechnePosition(int stockwerk) { int position = 0; for (int i = 0; i < stockwerk; i++) { position += stockwerkSchritte[i]; } return position; }
Änderungen im Detail:
- EEPROM Integration:
EEPROM.read()
undEEPROM.write()
werden verwendet, um die aktuelle Position zu speichern und zu laden.- Position wird auf zwei Bytes im EEPROM gespeichert.
- Helferfunktionen:
berechneStockwerk()
undberechnePosition()
helfen, die Position und das Stockwerk zu bestimmen.
- Setup-Funktion:
- Die aktuelle Position wird beim Start aus dem EEPROM geladen.
- Loop-Funktion:
- Nach jeder Bewegung wird die aktuelle Position im EEPROM gespeichert.
So bleibt die Position des Fahrstuhls auch nach einem Stromausfall erhalten.
Wenn dir das Projekt gefallen hat und du von weiteren interessanten Projekten inspiriert werden willst, sieh dir doch mal mein neues E-Book »Arduino Projekte Volume 1« an!
- Die beliebtesten Arduino-Projekte von StartHardware
- Inklusive Schaltplan, Beschreibung und Code
- Arduino-Schnellstart-Kapitel
- Kompakter Programmierkurs
Hallo, das ist ein sehr schönes Projekt.
Es ist mir jedoch nicht klar, wie die Synchronisation erfolgt. Der Fahrstuhl wir eingerichtet und befindet sich im Erdgeschoss. Danach laufen die Zufallsgeneratoren ab und die Steuerung funktioniert wunderbar. Der Aufzug befindet sich, angenommen in der Etage 5. Nun schalte ich die Spannung aus. Nun schalte ich irgendwann wieder, um weiter zu spielen. Nach Spannungswiederkehr werden jedoch istposition auf 0 in der Setup Routine gesetzt. Die Berechnung geht nun von einer falschen Voraussetzung aus und nimmt die aktuelle Position( vor dem Ausschalten 5) als aktuelle Position 0 an.
Irre ich mich da und habe etwas übersehen?
Ich hoffe auf eine Antwort.
Viele Grüße.
Hi Christian, absolut richtig. Die Position wird nicht gespeichert. Ich hab das mal eingefügt. Um die Position des Fahrstuhls bei Stromausfall zwischenzuspeichern, kann man den Eprom (Speicher) des Arduino-Boards verwenden. Code ist jetzt im Beitrag. Vielen Dank und liebe Grüße Stefan