Serie Leinwand-Maskierung zur Bildverbesserung (Teil 1 2 3 4 5 6 7 8)

Schrittmotor-Steuerung mit TB6600-Treiber und Raspberry Pi

Um meine Leinwand-Maskierung elektrisch zu betreiben und damit automatisieren zu können, musste ein passender Motor her. Mit der richtigen Antriebsmechanik als Grundlage fiel mir die Entscheidung für einen Schrittmotor nicht schwer. Unter den denkbaren Motoren und Steuerungssystemen hatten Schrittmotoren die besten Features für mein Vorhaben zu bieten.

Unteres Ende der Gewindespindel mit angeschlossenem Schrittmotor und Gummipuffern als Dämpfer.

Das Problem bei der Ansteuerung von Schrittmotoren ist, dass einem niemand dabei helfen kann. Es gibt eine ganze Latte verschiedener Schrittmotoren am Markt, teils sehr ähnlicher Bauart, teils aber auch stark abweichend. Viel schlimmer ist die große Auswahl an Treiber-Platinen. Die Situation wird dadurch nicht besser, dass es viele Motoren und Treiber in sehr ähnlicher Bauweise noch von einem Dutzend verschiedener Hersteller gibt.

Wenn du nach Anleitungen suchst, findest du zuerst eine Reihe nichtssagender Videos und anschließend einige Tutorials. Dummerweise sind alle Tutorials in irgend einer Weise unvollständig. Die Auswahl der Hardware wird nicht beschrieben, es wird nicht auf die Eigenheiten der verwendeten Hardware-Kombination eingegangen oder wichtige Details wie die Verkabelung oder der richtige Umgang mit dem Motor und dem Treiber werden einfach übergangen.

Das macht nicht wirklich viel Spaß. Es hat mich einige Tage gekostet, die richtige Kombination aus Hardware, Verkabelung und Steuerungslogik herauszufinden. Hier nun das Ergebnis. Du kannst das entweder genau so nachbauen oder deine eigenen Erfahrungen machen. Es gibt kein richtig oder falsch – nur lernen, lernen, lernen.

Die Hardware und ihre Aufgaben

Bei Licht betrachtet sind Schrittmotoren aber eigentlich nicht weiter kompliziert. Nur der unüberschaubare Dschungel an Hardware und fehlenden oder falschen Informationen macht die ganze Angelegenheit kompliziert.

Schauen wir doch mal, was wir benötigen.

Schrittmotor

Schrittmotoren gibt es in verschiedenen Größen, Bauarten und Stärken. Sehr beliebt und verbreitet im Hobby-Bereich sind die NEMA-Baugrößen (National Electrical Manufacturers Association), zum Beispiel NEMA 17 oder 23. Die häufigsten Anwendungsgebiete – und der Grund, weshalb man die Dinger hinterher geschmissen bekommt – ist der Einsatz in 3D-Druckern und CNC-Fräsen.

Die Größe wirkt sich meist auch unmittelbar auf die Leistung des Motors aus. Für unsere Zwecke reicht aber eigentlich ein kleiner NEMA-17-Motor. In der Produktbeschreibung findest du zwei wichtige Angaben:

  • Die Kraft des Motors, angegeben in N·m (Newtonmeter, auch kurz Nm). Dieser Wert sollte so groß wie möglich sein, damit der Motor mehr halten bzw. ziehen kann. Die höchsten Werte liegen bei NEMA-17-Motoren meist um die 0,49 N·m. Gerne werden die Meter (m) auch als Zentimeter (cm) angegeben, damit die Zahl größer aussieht. 0,49 N·m = 49 N·cm.
  • Die Stromstärke, mit der der Motor arbeiten kann, angegeben in A (Ampere). Auch dieser Wert sollte möglichst hoch sein, wird bei NEMA 17 aber kaum 2 A überschreiten. Mehr Strom bedeutet mehr Kraft und größere Flexibilität bei der Zusammenarbeit mit Treibern.

Nimm nicht gleich den billigsten Motor, der im Dreierpack angeboten wird, sondern einen einzelnen mit passenden Werten und guter Bewertung.

Tipp: Für Recherchen verwendest du besser den englischen Begriff “stepper motor”.

Treiber

Ein Schrittmotor erzeugt die Drehung durch ein definiertes Muster, nach dem die Spulen im Inneren angesteuert werden. Wie das im Einzelnen abläuft, kannst du dir sehr schön animiert bei Nanotec ansehen.

Für den korrekten Ablauf dieses Musters ist der Treiber verantwortlich. Seine interne Schaltung schickt den Strom mit dem passenden Muster zu den Spulen des Motors, um so eine Drehung zu erzeugen, und nicht etwa irgend ein wahlloses Gezappel.

Wenn du nach Schrittmotor-Treibern suchst, findest du eine ganze Palette an Produkten, von denen einige immer wieder auftauchen:

Einige davon sind billiges Spielzeug, das meist in Zusammenarbeit mit einem Arduino zum Einsatz kommt. Andere gehören schon eher in den professionellen Bereich, arbeiten zuverlässiger, werden nicht so schnell warm oder lassen sich einfacher ansteuern.

Übrigens: Wärme ist für dieses Vorhaben nicht das Problem, wenn du es richtig machst. Der Motor ist nicht dauerhaft im Einsatz, sondern allerhöchstens mehrmals pro Stunde für ein paar Sekunden. Eine spürbare Erwärmung wie bei einem Dauereinsatz im 3D-Drucker gibt es nicht.

Controller: Raspberry Pi oder Arduino

Im Umfeld der Schrittmotoren kommt vor allem der Arduino als beliebte Spielwiese gern zum Einsatz. “Spielwiese” trifft es allerdings ganz gut, denn viel mehr ist der Arduino eigentlich nicht. Er eignet sich hervorragend für Prototypen und schnelle Resultate. Zwar kann er auch im produktiven Einsatz ohne USB-Verbindung zu einem PC arbeiten – dennoch sind die Möglichkeiten so beschränkt wie die Rechenleistung.

Weil ich die Steuerung der Maskierung mittels einer HTTP-Schnittstelle im Netzwerk zur Verfügung stellen wollte, brauchte ich etwas mächtigeres, womit ich auch problemlos Node.js-Anwendungen laufen lassen kann und WLAN kein Extra-Modul erfordern sollte.

Der Raspberry Pi ist daher aus meiner Sicht wesentlich besser geeignet und im produktiven Einsatz deutlich solider aufgestellt. Wenn du statt WLAN auf eine Ethernet-Verbindung setzen kannst, genügt auch eine ältere Version des Raspberry Pi. Die Steuerung des Treibers erfolgt über GPIO, die “Bastler-Pins” der Platine, und funktioniert daher bis hinunter zu Version 1. Ich habe einen Raspberry Pi 3 B+ verwendet, das geht ganz sicher.

Netzteil

Eine Tatsache, die in den meisten Tutorials völlig übergangen wird, ist die Notwendigkeit einer zusätzlichen Spannungsversorgung für den Motor. Ein Schrittmotor bekommt seine Spannung vom Treiber, und der kann nur weitergeben was er selbst bekommt (und verträgt).

Der Raspberry Pi oder Arduino sendet nur Steuersignale mit 3,3 V und wenigen Ampere, und kann darüber hinaus nur noch eine 5 V Dauerversorgung leisten. Selbst ein Netzteil mit 2,5 A liefert nicht genug Saft, um einen Raspberry Pi und einen Motor zu betreiben. Das ist äußerst limitierend und reduziert das Drehmoment des Motors erheblich. Dadurch kommt es leicht zu Schrittverlusten.

Schrittverlust: Der Treiber glaubt, er hätte den Motor gedreht, aber der Motor hat die Drehung nicht geschafft, weil er den mechanischen Widerstand nicht überwinden konnte.

Das verfälscht die bekannte Position des Motors und damit auch die der Maskierung – das willst du nicht haben.

Welche Spannung das Netzteil liefern muss, kannst du den Angaben am Treiber entnehmen. Üblich sind hier Werte von 9–42 V bei bis zu 4 A.

Eine gute und verbreitete Marke bei Netzteilen ist MeanWell. Hier gibt es eine große Auswahl, sowohl was die technischen Daten als auch die Bauart angeht.

Auch ein guter Mittelweg sind Netzteile, wie sie für 24-V-LED-Anwendungen oder Notebooks hergestellt werden. Die Suche nach einem “Universalnetzteil” kann auch sehr hilfreich sein: Hier kannst du die Spannung einstellen und bekommst manchmal noch verschiedene Adapter für den einfacheren Anschluss.

Einkaufsliste

Im Hinblick auf einen dauerhaft sicheren und stabilen Betrieb und ausreichend Kraft bei leichter Bauweise habe ich mich für folgende Komponenten entschieden:

Für die Übertragung der Motordrehung in eine ziehende Bewegung verwende ich eine 40 cm lange Gewindespindel mit dazugehörigen Lagern.

Montage der Zugmechanik und Elektronik

Ich empfehle dir, die Ansteuerung des Treibers und Schrittmotors, wie ich sie weiter unten beschreibe, zuerst als Prototyp auf dem Tisch auszuprobieren. Du solltest erst ein Gefühl dafür bekommen, wie der Motor reagiert, wie die Gewindespindel arbeitet und was wie verkabelt wird, bevor du alles fest montierst.

Unteres Ende der Gewindespindel mit angeschlossenem Schrittmotor und Gummipuffern als Dämpfer.

Trotzdem ist es gut zu wissen, wie es weiter geht. Deshalb ziehe ich die Montage an dieser Stelle vor. Wirklich helfen kann ich dir dabei ohnehin nicht, denn das ist ja sehr individuell von deiner Leinwand abhängig.

Oberes Ende der Gewindespindel mit Gummipuffern als Dämpfer.

Wie ich beim Bau einer manuellen mechanischen Maskierung beschrieben habe, war die Befestigung beliebiger Bauteile an meiner Visivo-Rahmenleinwand ganz einfach dadurch möglich, dass ich eine 3 mm starke Möbelrückwandplatte hinten in eine Kerbe im Rahmen geklemmt habe. Ich konnte mich also auf einer flachen Spielwiese austoben, auf der ich beliebige Dinge festschrauben konnte.

Oberes Ende der Gewindespindel mit Gummipuffern als Dämpfer.

Ich empfehle, am Ende der Kette zu beginnen und die Gewindespindel mit dem Motor an einer für den Seilzug praktischen Position zu befestigen. In meinem Fall war das genau die Mitte.

Für NEMA-17-Motoren gibt es passende Montagewinkel, die häufig schon mitgeliefert werden. Sehr zu empfehlen sind dabei auch passende Schwingungsdämpfer, die dafür sorgen, dass das Motorengeräusch nicht auf den Winkel und den Untergrund übertragen wird.

Ein Schwingungsdämpfer, der zwischen Schrittmotor und Montagewinkel geschraubt wird, um Schwingungen vom Motor nicht auf den Untergrund zu übertragen.
Schwingungsdämpfer, der zwischen Motor und Montagewinkel geschraubt wird.

Den Höhenausgleich zwischen dem Motor und den Kugellagern der Gewindespindel musst du irgendwie bewerkstelligen. Bei allen Arten von Montagearbeiten dieser Art war mir dabei mein alter Metallbaukasten eine große Hilfe, der jede Menge Schrauben, Winkel und andere Metallteile bietet. Nach einer ersten Installation fiel mir aber auf, dass die mechanische Reibung und damit einhergehende Vibrationen der Gewindespindel erhebliche Resonanzen in der Grundplatte hervorriefen. Kurzum: das Ding war unerträglich laut! Abhilfe schafften hier zwei Maßnahmen:

  1. Montage der Kugellager auf Reckhorn-Gummidämpfern, so dass keine feste Verbindung zwischen den Lagern und dem Untergrund besteht. Diese Dämpfer sind eigentlich dafür da, Sofas vom Boden zu entkoppeln, wenn daran Bass Shaker betrieben werden.
  2. Schmieren der Gewindespindel und aller beweglichen Teile mit Siliconfett. Ist zwar eklig bei offenen Installationen, aber besser als eine laute Maskierung.
Unteres Ende der Gewindespindel mit angeschlossenem Schrittmotor und Gummipuffern als Dämpfer.

Der TB6600-Treiber lässt sich sehr leicht in der Nähe zum Motor mit 2 Schrauben montieren, wenn der Aufbau nicht äußerst flach sein muss.

Für den Raspberry Pi gibt es unzählige Gehäuse, die eine Montage zulassen. Ich hatte noch ein Gehäuse mit Kabelschlitzen, das ich immer wieder gerne verwende. Wichtig bei der Montage ist der ausreichend kurze Abstand zum Treiber, damit die Jumper-Kabel reichen.

Weiteres optionales Zubehör:

  • ein Relais zum Kappen der Stromversorgung für den Treiber (auf einigen Bildern hier zu sehen)
  • eine Lichtschranke als Endschalter

Mehr dazu am Ende des Artikels.

Verkabelung

Wie wird nun der NEMA-17-Schrittmotor mit dem TB6600-Treiber verbunden? Und wie verbindet man den TB6600 mit dem Raspberry Pi? Es gibt kaum Anleitungen hierzu – die meisten sind für Arduino und andere Treiber. Auf die richtige Spur brachte mich ein alter Beitrag in einem Forum (den ich aber an dieser Stelle nicht verlinke, weil selbst der noch unvollständig war).

NEMA-17-Schrittmotor mit TB6600-Treiber verbinden

Der hier verwendete Schrittmotor hat – wie die meisten – 4 Kabel. Auf dem Motor müsste irgend eine Seriennummer stehen, wie zum Beispiel 17HS19-2004S1. Wenn du nach dieser Nummer googelst, findest du das Datenblatt zum Motor, das dir Aufschluss über die interne Verdrahtung gibt.

Ein TB6600 Schrittmotor-Treiber mit farbiger Verkabelung zum Schrittmotor und Raspberry Pi.

Die Farben der Kabel werden dabei den Anschlüssen A+ und A- sowie B+ und B- zugeordnet, die du am TB6600 findest. Das Plus wird gerne mal weggelassen; das Minus wird auch mal durch einen \ oder irgendwie anders gekennzeichnet. Nach diesen Angaben im Datenblatt musst du die Verbindungen herstellen.

Wenn der Motor einen flachen Stecker hat, kannst du Jumper-Kabel verwenden, um das letzte Stück zu überbrücken. Für die endgültige Festinstallation habe ich den Stecker abgeschnitten, das Kabel gekürzt, die Isolierung für 6 mm entfernt (Abisolierzange) und alles direkt mit dem Treiber verschraubt.

Switch des TB6600 einstellen

Auf einer Seite des Treibers sollten dir 6 kleine Schalter aufgefallen sein. Diese konfigurieren den Treiber für deinen Motor. Auf der Oberseite ist eine Tabelle aufgedruckt, die die Schalterstellungen erklärt. Am Schalter müsste ein kaum erkennbarer Pfeil darauf hinweisen, welche Stellung “On” ist. Die jeweils ersten und letzten 3 Schalter gehören zusammen und erfüllen gemeinsam eine Aufgabe.

  • Schalter 1–3 stellst du erstmal auf on-on-off. Damit legst du die Schrittgröße fest. Wir wollen für den Anfang normale Einzelschritte machen (i.d.R. sind das 200 Schritte für 1 vollständige Umdrehung, entspricht 1,8° Drehung pro Schritt). Später könntest du mit on-off-on auf Halbschritte umstellen (400 Schritte für 1 Umdrehung, entspricht 0,9° Drehung pro Schritt), was den Motor etwas ruhiger laufen lässt.
  • Schalter 4–6 definiert die Ausgangsstromstärke für den Motor. Im Datenblatt des Motors ist diese mit der Einheit Ampere (gelegentlich komisch abgekürzt, z.B. “Amps”) angegeben, etwa mit dem Wert 2,0. Das sind die Ampere, die du dauerhaft in den Motor reinjagen darfst, ohne dass er wie ein Komet verglüht. Die Tabelle auf der Oberseite des Treibers gibt an, dass 2,0 A (bei kurzen Spitzenwerten von 2,2 A) mit der Schalterstellung 4–6 auf on-off-off ausgegeben werde.

Bedenke: Beginne stehts mit niedrigeren Werten und arbeite dich langsam nach oben.

  • zu wenig Strom = Motor zu schwach, Aussetzer und Schrittverluste sind die Folge
  • zu viel Strom = Motor wird heiß und brennt irgendwann durch

Netzanschluss

Testweise kannst du jetzt bereits das Netzteil mit dem Treiber verbinden. Der Pluspol kommt an den Anschluss VCC, der Minuspol an GND. Mit GND bekommst du es noch öfter zu tun – das steht für “ground”, zu Deutsch “Masse”, was in der Elektrotechnik sowas wie der Abfluss ist.

Wenn du Saft Strom auf das Netzteil gibst (bitte keinen Saft drüber schütten), sollte der Motor bereits jetzt minimale Geräusche von sich geben, etwa ein unregelmäßiges Surren oder Kratzen. Er sollte sich dann nicht mehr von Hand drehen lassen. Das bedeutet, dass er seine Kraft aufwendet, um die Position zu halten. Alles in Ordnung, später mehr dazu. Vorerst solltest du den Strom wieder trennen.

TB6600-Treiber mit Raspberry Pi verbinden

Ein Raspberry Pi 3 mit diversen belegten Pins, die mit dem Schrittmotor-Treiber verdrahtet sind.
Pin-Belegung am Raspberry Pi (relevant sind nur die farbigen Kabel, die anderen schalten ein Relais)

Du hast noch 6 Anschlüsse am Treiber übrig – die werden mit dem Raspberry Pi verbunden. Dazu ist es sinnvoll, wenn du mal nach “Raspberry Pi Pin Layout” googelst und dir eine schöne Grafik dazu ansiehst.

Drei der Anschlüsse am TB6600 sind ganz schnell erledigt:

  • ENA-
  • PUL-
  • DIR-

Diese drei verbindest du mit drei der Pins am Raspberry Pi, die mit GND angegeben sind. Dort geht der Strom der Steuersignale wieder raus.

Die Pins haben eine fortlaufende Nummerierung, etwa so wie Hausnummern, und eine BCM-Nummerierung. Letztere ignorieren wir erstmal.

  • ENA+ schließt du an Pin 37 an
  • PUL+ schließt du an Pin 35 an
  • DIR+ schließt du an Pin 33 an

Das genügt erstmal. Jetzt ist es Zeit für …

Etwas mehr Hintergrundwissen

Die Pins am Raspberry Pi sind die Schnittstelle für Programme in die echte Welt. Programme können Pins ein- und ausschalten. Eingeschaltet liegen 3,3 V an, ausgeschaltet 0 V.

Die GND-Pins können nicht geschaltet werden. Sie dienen praktisch als gemeinsamer Minus-Pol oder eben “Abfluss” für alle Schaltungen.

Der TB6600-Treiber nimmt 3 verschiedene Signale entgegen, die der Raspberry Pi ihm liefern kann:

  • DIR bestimmt die Richtung (Direction) in der sich der Motor dreht. Signal an ist eine Richtung, Signal aus die andere. Weil Autovergleiche immer so toll funktionieren: Du bestimmst damit, ob der Vorwärtsgang oder der Rückwärtsgang eingelegt ist.
  • PUL steht für Pulse und ist ein schnell zwischen an und aus wechselndes Signal. Die Frequenz dieses Wechsels bestimmt, wie schnell sich der Motor dreht. Je schneller die Abstände, desto schneller der Motor. Das ist das Gaspedal. Stell dir vor, du müsstest mit dem Gaspedal schneller “pumpen” um schneller zu fahren. Die Pedale eines Fahrrads treffen es fast noch besser.
  • ENA aktiviert den Motor grundsätzlich (“Enable”). Die Bezeichnung ist irreführend, denn es ist genau anders herum: Signal an deaktiviert den Motor und schaltet ihn in den Leerlauf, Signal aus aktiviert ihn. Am Auto ist das die Kupplung: voll durchgedrückt kann keine Kraftübertragung mehr stattfinden.

Besonders das ENA-Signal hat mich lange verwirrt, weil es ein unerwartetes Verhalten des Motors auslöst, wenn es nicht gesendet wird.

So lange ENA aus ist, hat der Treiber den Motor unter seiner Kontrolle. Er kann dann mit ihm machen, was er will – zum Beispiel ihn drehen mit einer Geschwindigkeit die man ihm vorgibt (PUL) in der gewünschten Richtung (DIR). Wenn er keine Vorgaben bekommt, hält der Treiber den Motor einfach fest – magnetisch, in dem eine der Spulen aktiv bleibt.

Dieses “Halten” führt der Motor mit seiner verfügbaren Kraft aus, mit der er sich genauso gut drehen könnte. Man spricht hier von “holding torque” (Haltemoment, im Gegensatz zu Drehmoment, der Kraft in der Bewegung). Genau genommen ist die Kraft beim Halten, beim Anlaufen und je nach Geschwindigkeit eine andere, aber das würde jetzt hier zu weit führen. Diese Haltekraft ist auch die Ursache dafür, dass der Motor auch Geräusche verursacht, wenn er sich nicht dreht, und dass er warm wird, obwohl er nicht viel zu tun hat.

Wenn ENA an ist, also der Raspberry Pi Strom anlegt, drückt der Treiber die Kupplung durch und entlässt den Motor damit in den Leerlauf. Das hat diverse gewollte oder ungewollte Folgen:

  • Du kannst den Motor von Hand drehen,
  • ein Gewicht, das der Motor eigentlich ziehen oder halten sollte, kann seinerseits zurück ziehen und den Motor drehen,
  • Steuersignale haben keine Auswirkungen mehr (wie Gas geben bei gedrückter Kupplung),
  • der Motor verbraucht keinen Strom mehr,
  • macht keine Geräusche und
  • kühlt ab.

Diesen Zustand solltest du immer bevorzugen, so lange der Motor nicht gebraucht wird.

  • Entweder ein Signal auf ENA geben, damit der Motor sich entspannen kann,
  • oder den Strom vom Treiber nehmen, damit endgültig Ruhe ist.

Der Treiber selbst verbraucht im Ruhezustand nicht sonderlich viel Strom – wenn Strom verbraucht wird, dann nur durch den Motor, so lange dieser nicht deaktiviert wird. Warm wird er auch nicht wirklich. Du könntest den Treiber also immer am Netzteil lassen, so lange das Signal an ENA anliegt. So richtig “aus” ist das aber auch nicht. Ich würde es daher stets bevorzugen, dem Treiber die Stromversorgung zu nehmen – etwa so, wie du auch den Beamer ausschaltest. Das wäre eine Erweiterung wert: den Treiber an den Trigger-Ausgang vom Beamer hängen. Darüber kannst du dir selbst Gedanken machen. Ich verweise stattdessen vorerst auf die Lösung über ein Relais (siehe unten).

Schrittmotor mit dem Raspberry Pi steuern

Kommen wir endlich zum Kern unseres Vorhabens. Wie erteilen wir dem Motor Anweisungen mit Hilfe des Raspberry Pi?

Raspbian als Betriebssystem

Zunächst brauchst du eine SD-Karte, auf die du ein passendes Betriebssystem aufspielst – Raspbian im einfachsten Fall. Wie das geht, habe ich in dieser Anleitung zu einem anderen Thema beschrieben.

Wichtig dabei:

  • SSH vor dem ersten Start aktivieren und das System so im “headless” Modus einrichten (ohne grafische Benutzeroberfläche)
  • zuerst mit angeschlossenem Netzwerkkabel starten, um dich per SSH über PuTTY einloggen zu können, dann ggf. WLAN einrichten
  • um einfacher mit Scripts arbeiten zu können, verwendest du am besten WinSCP parallel zu PuTTY und loggst dich darüber ebenfall mit den SSH-Zugangsdaten ein

Es dürfte einfacher zu verstehen sein, wenn du die Schritte im verlinkten Artikel durchführst und alles selbst Schritt für Schritt nachvollziehst.

Python Script zur Motorsteuerung

Starten wir mit einem einfachen Script, um den Motor – oder besser: den Treiber – über Python anzusteuern. Das funktioniert Out-of-the-Box, du musst nichts installieren.

Erstelle eine Datei stepper.py oder mit einem beliebigen anderen Namen im Home-Verzeichnis:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BOARD)

# Raspberry Pi Pin-Belegung für TB6600 Treiber
DIR = 33
PUL = 35
ENA = 37

DIR_Left = GPIO.HIGH
DIR_Right = GPIO.LOW

ENA_Locked = GPIO.LOW
ENA_Released = GPIO.HIGH

GPIO.setwarnings(False)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(PUL, GPIO.OUT)
GPIO.setup(ENA, GPIO.OUT)

# Motor aktivieren und halten
GPIO.output(ENA, ENA_Locked)

# Richtung festlegen
GPIO.output(DIR, DIR_Left)

for i in range(200):

    # Puls modulieren
    GPIO.output(PUL, GPIO.HIGH)
    time.sleep(0.0001875)

    GPIO.output(PUL, GPIO.LOW)
    time.sleep(0.0001875)

# Motor freigeben
GPIO.output(ENA, ENA_Released)

Dann führst du dieses Script über Python aus:

python stepper.py

Das Script bindet zunächst die benötigten Module ein, bevor es die verwendeten Pins des Raspberry Pi sowie einige hilfreiche Konstanten definiert. Anschließend wird der Motor aktiviert, in einer Schleife über 200 Schritte der Puls “moduliert” und anschließend der Motor wieder freigegeben.

Normalerweise würde man am Ende des Scripts die Pins mittels GPIO.clear()wieder abschalten, d. h. “aufräumen”. Dabei würde aber auch das Signal für ENA abgestellt, was, wie wir oben erfahren haben, bedeutet, dass der Motor dauerhaft aktiv bleibt und sich damit erwärmt. Da der Raspberry Pi keine anderen Aufgaben hat, geht das aber klar, wenn die Pins einfach in ihrem letzten, teilweise aktiven Zustand verbleiben.

Schrittzahl als Parameter übergeben

Der Motor sollte sich damit nun zumindest mal eine Runde drehen. Um das ganze ein wenig praktikabler zu machen, erweitern wir das Script noch ein wenig:

import sys

# ...

steps = int(sys.argv[1]) # Anzahl Schritte aus dem Parameter

GPIO.output(ENA, ENA_Locked)

# Richtung abhängig von positivem oder negativem Wert
if (steps < 0):
    GPIO.output(DIR, DIR_Right)
else:
    GPIO.output(DIR, DIR_Left)

for i in range(abs(steps)):

    # ...

Das Script nimmt nun einen Parameter entgegen, der die Anzahl der Schritte angibt, die sich der Motor drehen soll. Aufgerufen wird es so für Vorwärtsdrehungen …

python stepper.py 1000

… oder mit negativem Wert für Rückwärtsdrehungen:

python stepper.py -1000

Bei normaler Konfiguration gängiger Schrittmotoren entsprechen 200 Schritte einer Umdrehung, es werden also 5 Umdrehungen ausgeführt.

Damit hast du eine gute Grundlage als Spielwiese, um dich mit der Schrittmotorsteuerung vertraut zu machen. Im nächsten Teil dieser Serie werden wir uns genauer ansehen, wie man auf Basis dieser Scripts eine HTTP-Schnittstelle baut, um den Motor mit den gängigen Fernbedienungen und Apps steuern zu können.

Weitere Details und Erweiterungen

Im Folgenden sehen wir uns noch ein paar Details an, die für den bisher gezeigten Aufbau praktisch sein können.

Relais zum Kappen der Stromversorgung

Mir ist es immer am liebsten, wenn Geräte, die nicht gebraucht werden, stromlos geschaltet sind. “Gebraucht” ist allerdings sehr dehnbar, denn wenn man sein Heimkino so extrem automatisiert wie ich es gern mache, müssen die meisten Geräte immer im Standby bleiben. Bei guter Technik namhafter Firmen habe ich damit kein Problem. Bei kleinen Platinen und Motoren, die nur ein paar Euro kosten, weil sie sehr wahrscheinlich allerbilligste China-Massenware sind, bin ich gerne etwas vorsichtiger.

Ein Relais, das über einen Raspberry Pi gesteuert werden kann.

Kurzum: ich wollte Motor und Treiber gerne stromlos schalten können, um ein versehentliches Erwärmen durch Softwarefehler oder dergleichen so gut es geht vorzubeugen. Zwar brauchte ich dafür noch mehr China-Massenware – aber zumindest in einem Bereich, wo so schnell nichts warm wird.

Irgendjemand muss also den Stecker vom Netzteil ziehen. Die einfachste Lösung dafür ist eine Funksteckdose, am bestens passend zum ohnehin schon vorhandenen System.

Allerdings betrachte ich die Maskierung ganz gerne als Einheit, als geschlossenes System. Und wenn ich schon den Aufwand mit einer Schnittstelle betreibe, dann sollte die auch das Ein- und Ausschalten abdecken.

Dafür gibt es simple Relais, die sich mit dem Raspberry Pi schalten lassen. Zwar haben Relais die dumme Angewohnheit, zu klicken, aber so habe ich wenigstens ein akustisches Feedback. Gebraucht wird es ohnehin nur vor und nach dem Film – während der Vorstellung bleibt die Maskierung an.

Zum Anschluss wird nur der Plus-Pol vom Netzteil durch das Relais geleitet, bevor er zum Treiber führt. Für die Steuerung bekommt das Relais dauerhaft 5 V vom Raspberry Pi (Pin 2 oder 4), sowie die Masse von einem freien GND-Pin. Das Schaltsignal geht über einen freien Pin raus, zum Beispiel 11:

POWER = 11
GPIO.setup(POWER, GPIO.OUT)

Im Python-Script kannst du den Pin für das Schaltsignal so konfigurieren, wie das auch für DIR und ENA des Motor-Treibers gemacht wird. Je nach Verkabelung des Relais kannst du den Strom dann mit den folgenden Zeilen an und aus schalten.

GPIO.output(POWER, GPIO.LOW)
GPIO.output(POWER, GPIO.HIGH)

Lichtschranke als Endschalter

Es ist mechanisch gesehen eher ungünstig, wenn der Motor die Maskierung weiter fahren will, als sie kann. Irgendwas geht dann früher oder später kaputt – sei es der Motor, weil er festgehalten wird, oder nur ein Zugseil.

Um das zu verhindern, kannst du einen kleinen Endschalter verbauen, der das Script umgehend unterbricht, sobald er ausgelöst wird.

Endschalter oder Kontaktschalter für Raspberry Pi gibt es wie Sand am Meer. Ich würde jedoch empfehlen, eine optische Lösung einzusetzen (Lichtschranke), um mechanische Abnutzung von vorneherein auszuschließen. Daher habe ich mich bei einem Test eines solchen Schalters für dieses Modell hier entschieden.

Ob du einen oder zwei Schalter brauchst, hängt davon ab, wie du diese verbaust und was du damit bezwecken willst. Ich habe mich für einen Versuch auf einen einzelnen Schalter beschränkt und diesen nur zur Rekalibrierung verwendet – nach dem Motto: “Fahre ganz runter bis der Schalter aktiviert wird und setze diese Position als Nullpunkt.”

Hier ein Script, das den Endschalter auf Pin 8 lesbar macht und sein Signal dann in der Schleife nutzt, um diese sofort abzubrechen:

# ...

ENDSTOP = 8
GPIO.setup(ENDSTOP, GPIO.IN)

# ...

for i in range(abs(steps)):

    if (int(GPIO.input(ENDSTOP)) == 1):
         break

    # ...

Wissen wo man ist – die aktuelle Schrittstellung speichern

In Kombination mit einem Endschalter, oder um sich diesen zu sparen, ist es fast noch wichtiger, sich jederzeit die Position der Maskierung zu merken. Die Anzahl der gefahrenen Schritte ist dafür zum Glück ein ziemlich verlässliches Kriterium.

Die aktuelle Schrittposition kannst du dazu ganz einfach nach jeder Änderung in einer Datei speichern. Wann immer du das Script ausführst, wird die letzte Position wieder geladen und die aktuelle Schrittzahl hinzu addiert. Zuvor wird jedoch geprüft, ob das geplante Schrittziel innerhalb des zulässigen Bereichs liegt.

Was der zulässige Bereich ist, hängt von deiner Anwendung ab. Meine Maskierung muss 20 cm weit fahren, was über eine Gewindespindel bewerkstelligt wird. Dazu sind genau 5000 Schritte notwendig. (Du könntest das anhand der Steigung des Gewindes ausrechnen, aber ich verschone dich an dieser Stelle mit Formeln – nimm einfach ein Lineal und miss es aus oder summiere kleine Schritte auf, bis die gewünschte Endposition erreicht ist.)

Hier zwei Funktionen für Python, mit denen du die Schrittposition in eine Datei schreiben und wieder aus ihr lesen kannst:

def getCurrentPosition ():

    try:
        f = open(".position", "r")
        steps = int(f.read())
        f.close()
        return steps
    except IOError:
        return 0

def setCurrentPosition (steps):

    f = open(".position", "w+");
    f.write(str(steps))
    f.close()

Am Ende des Artikels folgt ein komplettes Anwendungsbeispiel, das auch diese Funktionen berücksichtigt.

Rampe rauf und Rampe runter

Fast noch wichtiger ist es, den Motor nicht so ruckartig anfahren zu lassen, wie in den bisherigen Beispielen. Bisher geben wir direkt vom ersten Schritt an Vollgas, ohne die Masse des Rotors (der drehbare Teil des Motors) zu berücksichtigen. Aber kein Rad der Welt beginnt sich mit sofortiger Wirkung in voller Geschwindigkeit zu drehen. Wenn wir das dennoch versuchen, wird das möglicherweise Schrittverluste zur Folge haben. Zudem ruckt der Motor beim Starten und Abbremsen ziemlich stark.

Wir müssen den Schrittmotor langsam anfahren lassen und ihn dann auf volle Geschwindigkeit bringen. Ebenso muss er vor dem Stoppen abgebremst werden. Dazu programmieren wir eine “Rampe”. Eine Rampe ist schlicht und einfach eine Steigerung der Geschwindigkeit bis zu einem bestimmten Punkt. Es wird klarer, wenn wir die Geschwindigkeit im Verlauf der Zeit betrachten:

Eine Rampe rauf und runter: Geschwindigkeit eines Schrittmotors im Verlauf der Zeit

Die Geschwindigkeit wird mit der Frequenz geregelt, mit der dem PUL-Eingang des Treibers ein Signal geliefert wird. Wir müssen also nur die Pausen zwischen dem Ein- und Ausschalten des entsprechenden Pins vergrößern, um den Motor langsamer laufen zu lassen.

Allerdings macht nicht jeder Motor jede beliebige Geschwindigkeit mit. Wird er zu langsam oder zu schnell angesteuert, wird er sich gar nicht drehen oder nur sporadisch rumruckeln. Besonders an der unteren Grenze gibt es außerdem einen Drehzahlbereich, in dem der Motor extreme Resonanzen entwickelt und daher sehr laut ist. Diesen Bereich musst du vermeiden, um das Laufgeräusch niedrig zu halten. Diese Bereiche sind hier grau dargestellt – deshalb beginnt der Verlauf nicht mit der Geschwindigkeit “Null”.

Die minimale und maximale Frequenz solltest du also für deinen Motor passend ermitteln. Im Script kannst du anhand eines besser verständlichen Wertes, nämlich Umdrehungen pro Minute (rounds per minute, RPM) die minimale und maximale Frequenz automatisch berechnen lassen.

# schnellste Drehung
minFrequency = 1 / (MAX_RPM / 60 * stepsPerRevolution)

# langsamste Drehung
maxFrequency = 1 / (MIN_RPM / 60 * stepsPerRevolution)

Wenn die errechnete Frequenz als Pause zwischen den Pin-Schaltungen verwendet wird, dann halbieren wir sie nochmals, weil eine Frequenz im klassischen Sinne immer den vollen Durchlauf der Pin-Zustände An und Aus bezeichnet.

Darstellung der sich steigernden Frequenz eines Schrittmotor-Treibers bei Anwendung einer Rampe.

Um eine Rampe zu bauen, müssen wir die Abstände zwischen den Signalen mit jedem Schritt ein Stück verkürzen. Dazu benötigen wir die minimale und maximale Frequenz wie oben berechnet, sowie die gewünschte Rampenlänge. Die Rampenlänge ist die Anzahl Schritte, über die die Beschleunigung oder Verzögerung abläuft.

rampSlope = (maxFrequency - minFrequency) / RAMP_LENGTH

Sobald der Befehl für eine Drehung um eine Anzahl Schritte erfolgt, muss also am Anfang und Ende automatisch die Rampe eingebaut werden. Das macht das Script ein wenig komplizierter. Das liegt nicht zuletzt daran, dass die Rampen auch teilweise abgearbeitet werden müssen, wenn der gesamte Fahrweg kürzer als zwei Rampenlängen ist.

Aber dafür gibt es ja hier abschließend ein Script mit fertiger Rampe. Außerdem wird die zuletzt angefahrene Schrittposition gespeichert und beim nächsten Aufruf des Scripts wieder geladen.

import RPi.GPIO as GPIO
import sys
import time

GPIO.setmode(GPIO.BOARD)

# TB6600 Treiber Setup
DIR = 33
PUL = 35
ENA = 37

DIR_Left = GPIO.HIGH
DIR_Right = GPIO.LOW

ENA_Locked = GPIO.LOW
ENA_Released = GPIO.HIGH

GPIO.setwarnings(False)
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(PUL, GPIO.OUT)
GPIO.setup(ENA, GPIO.OUT)

# Motor Setup
STEP_ANGLE = 1.8 # degree
RAMP_LENGTH = 600 # steps
MIN_RPM = 250
MAX_RPM = 800

# maximale Schrittzahl, die sich der Motor vom Nullpunkt weg bewegen darf
MAX_STEPS = 5000

# Frequenzberechnung
stepsPerRevolution = 360 / STEP_ANGLE

minFrequency = 1 / (MAX_RPM / 60 * stepsPerRevolution)
maxFrequency = 1 / (MIN_RPM / 60 * stepsPerRevolution)

rampSlope = (maxFrequency - minFrequency) / RAMP_LENGTH

def getCurrentPosition ():

    try:
        f = open(".position", "r")
        steps = int(f.read())
        f.close()
        return steps
    except IOError:
        return 0

def setCurrentPosition (steps):

    f = open(".position", "w+");
    f.write(str(steps))
    f.close()

# dreht den Motor absolut an eine bestimmte Position
def moveTo (position):

    if (position < 0):
        position = 0
    if (position > MAX_STEPS):
        position = MAX_STEPS

    currentPosition = getCurrentPosition()
    steps = position - currentPosition

    return moveBy(steps)

# dreht den Motor relativ um eine bestimmte Anzahl Schritte
def moveBy (steps):

    GPIO.output(ENA, ENA_Locked)

    currentFreqency = maxFrequency
    currentPosition = getCurrentPosition()

    # relative Schrittzahl vorberechnen, die sich der Motor bewegen muss
    targetPosition = currentPosition + steps
    if (targetPosition > MAX_STEPS):
        steps = MAX_STEPS - currentPosition
    if (targetPosition < 0):
        steps = currentPosition * -1

    for i in range(abs(steps)):

        # sofort stoppen, wenn sich der Motor über
        # die obere Grenze hinaus bewegen würde
        if (steps >= 0 and currentPosition >= MAX_STEPS):
            GPIO.output(ENA, ENA_Released)
            setCurrentPosition(currentPosition)
            return currentPosition

        # sofort stoppen, wenn sich der Motor über
        # die untere Grenze hinaus bewegen würde
        if (steps < 0 and currentPosition <= 0):
            GPIO.output(ENA, ENA_Released)
            setCurrentPosition(currentPosition)
            return currentPosition

        # Richtung festlegen
        if (steps < 0):
            GPIO.output(DIR, DIR_Right)
        else:
            GPIO.output(DIR, DIR_Left)

        # Schritt ausführen
        GPIO.output(PUL, GPIO.HIGH)
        time.sleep(currentFreqency / 2)
        
        GPIO.output(PUL, GPIO.LOW)
        time.sleep(currentFreqency / 2)
        
        # aktuelle Schrittposition mitzählen
        if (steps < 0):
            currentPosition -= 1
        else:
            currentPosition += 1

        # Rampensteigung auf aktuelle Frequenz anwenden
        if (abs(steps) > 2 * RAMP_LENGTH):
            if (i < RAMP_LENGTH):
                currentFreqency -= rampSlope
            else:
                if (i > abs(steps) - RAMP_LENGTH):
                    currentFreqency += rampSlope
        else:
            if (i < abs(steps) / 2):
                currentFreqency -= rampSlope
            else:
                currentFreqency += rampSlope

        #print(currentFreqency)

    setCurrentPosition(currentPosition)
    GPIO.output(ENA, ENA_Released)

    return currentPosition

# Parameter vom Scriptaufruf abfragen
if (len(sys.argv) != 2):
    print("Fehlender Parameter: Anzahl Schritte");
    sys.exit();

steps = int(sys.argv[1])
print("Schritte: " + str(steps))

# Funktion moveBy() mit dem Parameter füttern
# probiere auch: moveTo()
currentPosition = moveBy(steps)
print("Erreichte Position: " + str(currentPosition))

Damit bist du für den Anfang gut gerüstet, einen Schrittmotor mit Gewindespindel über einen festen Streckenabschnitt fahren zu lassen. Du kannst jeden beliebigen Punkt auf der Strecke immer wieder auf den Millimeter genau ansteuern. Jetzt muss das Gebilde nur noch an deine Leinwand-Maskierung gekoppelt werden, um diese zu ziehen.

Bei meinem Projekt war mir die exakte und gezielte Positionierung der Maskierung besonders wichtig. Dieses Ziel konnte ich nur mit einem Schrittmotor erreichen. Das ist nichts, was man mal eben aus einer Schachtel auspackt und an die Wand hängt. Ich habe ungefähr 3 Monate lang daran herum gebastelt, bis alles funktioniert hat. Mit dieser Anleitung und ein wenig Vorerfahrung mit dem Raspberry Pi sollte zumindest der Teil der Motorsteuerung deutlich schneller vonstatten gehen.

Im nächsten Teil dieser Serie gehe ich dann noch einen Schritt weiter. Das Script muss um ein paar kleine Features vervollständigt werden. Hauptsächlich brauchen wir aber noch einen kleinen Webservice, der die Schnittstelle von außen zum Raspberry Pi bildet, damit die Maskierung an die Haus- und Heimkinoautomation angebunde werden kann.

Über Bert Kößler

Leidenschaftlicher Filmvorführer, Popcorn-Koch, Kartenabreißer, Platzanweiser, Programmchef, Projektionist, Reinigungsfachkraft und Kabelmann in einer Person. Neigt zu ausgeprägtem Fanatismus, wenn es um die Steuerung und Automatisierung des Heimkinos geht. Konnte sich zwischen zwei Filmen dazu motivieren, Heimkino Praxis als Ventil für gelegentliche Schreibanfälle zu gründen.

45 Gedanken zu „Schrittmotor-Steuerung mit TB6600-Treiber und Raspberry Pi

  1. Hallo Bert! Danke für deinen hoch interessanten Beitrag.
    Möchte 2 Stepper-Motoren hintereinander ansteuern. Ist das mit zwei Treibern TB6600 möglich?

    LG Hannes

    1. Hallo Hannes,

      ja, du brauchst für jeden Motor einen Treiber und eine Stromquelle. Du kannst aber problemlos mehrere Treiber mit dem selben Raspberry Pi steuern – so lange eben Pins verfügbar sind. Die müssen sich dann nur alsbald die Masse (GND) teilen. Da kannst du z. B. mit einem Breadboard nachhelfen oder musst dir ein Spezialkabel besorgen.

    2. Du kannst beliebig viele Motoren gleichzeitig an einen Treiber anschließen. Nur darf ihre Stromaufnahme nicht das Maximum des Treibers überschreiten.
      Nimmt jeder Deiner Motoren z.B. 1.4 A auf, muss Dein Treiber 2.8 oder besser 3 Ampere liefern können. Beide Motoren laufen dann natürlich, solange keine mechanischen Probleme auftreten, synchron. Dabei musst Du allerdings noch darauf achten, das alle Motoren die SELBE Stromaufnahme haben und nicht einer sagen wir 1 A und der Andere 2 A. Das gäbe Probleme. Auch MUSST Du innerhlb der Grenzen Deiner Spannungsversorgung bleiben. Wenn die nur 3 A liefern kann, werden zwei 1.4 A Motoren sie schon arg fordern und zwei 2 A Motoren ergeben dann einen tollen Test für Deine Rauchmelder.

  2. pi@raspberryRobi:~/Desktop/GPIO/003_Schritt $ python3 Test3.py 1000
    Traceback (most recent call last):
    File “Test3.py”, line 141, in
    if (len(sys.argv) != 2):
    NameError: name ‘sys’ is not defined

    Wenn ich import sys oben mit eingebe dann, kommt:
    Schritte: 1000
    Traceback (most recent call last):
    File “Test3.py”, line 150, in
    currentPosition = stepper.moveBy(steps)
    NameError: name ‘stepper’ is not defined

    so , jetzt weiss ich nicht mehr weiter, so gut kenne ich Python nicht…

    1. Hallo Robert,

      da hat sich tatsächlich noch ein kleiner Fehler im Script eingeschlichen.

      • am Anfang fehlte import sys
      • am Ende muss es einfach nur moveBy(steps) heißen, anstatt stepper.moveBy(steps)

      Da war ich wohl schon einen Schritt zu weit. Das Script ist jetzt korrigiert.

  3. Hallo Bert, vielen Dank für das Teilen deiner tollen Umsetzungen. Ich bin auch gerade dabei dein Script mit meinem Motor auszuprobieren, aber stecke bei dem gleichen Problem wie Robert fest. Da fehlt wohl eine Zeile die “stepper” initialisiert.
    NameError: name ’sys‘ is not defined konnte ich mit “import sys” ganz oben lösen 🙂

    1. Das kann viele Ursachen haben. Da wirst du dich langsam herantasten müssen. Probier mal mit dem normalen Script ohne Rampe verschiedene Frequenzen. Du musst ein Gefühl dafür bekommen, welche Geschwindigkeiten/Umdrehungen dein Motor überhaupt kann. Wenn man ihn mit den falschen Frequenzen anspricht, kann das dazu führen, dass er nur kurz ruckt und dann nicht mehr reagiert. Kommt auch sehr stark auf den Motor und die Einstellungen am Treiber an.

  4. Danke,
    script läuft, allerdings Motor ruckelt noch, warscheinlich muss ich noch mit Geschwindigkeiten/Umdrehungen “spielen”.
    Warte sehnssüchtig auf die WEB Lösung.
    Nochmal , tausend Dank. Vorbildlich….

  5. Hallo! Tolle Arbeit. Vielen Dank, dass Sie es mit allen geteilt haben.

    Im Moment baue ich meinen Bildschirm mit Maskierung auf, und ich möchte es automatisch tun, so wie Sie. Es ist alles ziemlich klar, aber ich weiß nicht genau, wie die Seile mit dem Motor und den Schubladenschienen verbunden sind.

    Wäre es möglich, ein vollständiges Diagramm zu sehen, wie Sie alles miteinander verbunden haben?

    Vielen Dank im Voraus!

    1. Hallo Rafael,

      in Teil 4 der Serie habe ich ja einige mögliche Antriebsmechaniken vorgestellt. Ich würde in jedem Fall eine der Seilzug-Varianten empfehlen, bei denen 4 Seile in Mitte zusammen laufen. 2 Seile ziehen, die anderen 2 Seile “schieben” (werden zurück gezogen). Wo diese Seile am Motor bzw. am Schlitten der Gewindespindel befestigt werden müssen, dürfte anhand der Bilder hier nicht schwer zu erraten sein. 😉

  6. Hallo Bert, wirklich ein tolles Projekt. Ein Teil würde ich davon gerne umsetzen habe aber ein Problem. Ich möchte meinen Motor die Schritte vorgeben, so wie du das im ersten Teil deines Beitrags beschreibst. Wenn ich das Skript mit python stepper.py -1000, oder auch anderen Werten starte, ruckelt der Motor kurz nach rechts, dann welchst er auf Linksfahrt und beendet diese nicht mehr. Weißt du eventuell woran das liegen kann?
    Vielen Dank.

    Gruß Mathias

    1. Hallo Mathias,

      das ist schwer aus der Ferne zu sagen. Vielleicht kommt die Kombination aus Motor und Treiber nicht mit den Parametern im Script zurecht oder es ist was falsch angeschlossen.

      Ich schlage vor, du tastest dich mal von der ersten, einfachsten Version des Scripts aus heran und spielst mit den Parametern, um das besser zu verstehen. Grundsätzlich muss mal eine einfache, dauerhafte Drehung in einer simplen Schleife ohne viel Schnickschnack möglich sein.

  7. Wirklich gute Beschreibungen. Ich habe das ganz ähnlich nur statt raspberry einen esp32 Chip verwendet der die treiberplatine steuert, ansprechbar via mqtt.

    Einen Punkt habe ich bei mir direkt mit optimiert. Denn die Ansteuerung der Position über Schritte ist ok aber wird über Die Zeit nicht mehr passen da u.a. die Seile sich dehnen. Kann man dann natürlich nachsteuern, aber es geht auch anders 🙂
    Und zwar mit einem Laser und einer photodiode pro Position (< 1€). Diese müssen einmal genau positioniert werden. Der Laser fährt auf dem Schlitten mit hoch und runter und die diode meldet via gpio die aktuelle Position. Damit sind sämtliche Varianten ausgeschlossen 🙂
    Ich habe bspw. 6 Positionen von 1:78 bis 2:40, die dann auch gleichzeitig die Endpunkte darstellen.
    Alles zusammen unter 20 Euro, aber einiges an Zeit 😉

    1. Danke für die ergänzende Idee! Es ist nur nicht ganz logisch, dass die Abstandsmessung mit auf dem Schlitten der Gewindespindel erfolgt. Denn Veränderungen über längere Zeit, wie die angesprochene Seildehnung, kommen ja erst hinter dem Antrieb. Somit bringt das nichts.

      Ich konnte bei meinem Aufbau bisher keine Veränderung feststellen (fast ein Jahr jetzt). So lange es keine Schrittverluste gibt (kann kaum vorkommen bei einer Gewindespindel als Antrieb), kann sich an der Position des Schlittens rein gar nichts verändern.

  8. Nein es ist natürlich der Schlitten der Schubladenauszüge gemeint. Auf der spindel macht es in der Tat keinen Sinn.
    Einfach gesagt kann man den kleinen Laser an der Oberkante des unteren Maskierungsbretts anbringen und in der jeweiligen Höhe wo das Maskierungsbrett stoppen soll die jeweiligen Fotodiode. Bei einer DIY Leinwand gibt es genug Befestigungspunkte, bei einer fixen Rahmenleinwand muss man halt schauen.
    Hoffe das war eins besser beschrieben.

  9. Es ist noch viel einfacher. Der Laser misst gar nix. Der ist nur die Lichtquelle für die diode. Diese schließt bei Licht und sendet damit einen Impuls an den Chip/die Software. Für jede Höhe 1:78 bis 2:40 hängt je eine Fotodiode und zeigt damit an auf welcher Höhe das Brett sich grade befindet. Viel genauer geht es nicht 😉

    1. Wenn du eine Maskierung hast, die du auf den Millimeter genau beliebig einstellen kannst, finde ich das einen Rückschritt. Die 2,4:1 treffen nicht immer zu, bis runter auf 2,33:1 ist alles möglich. Ebenso im Bereich 1,78:1 oder 1,85:1 usw.

      Ich sehe da wie gesagt keinen Grund für so eine Einschränkung. Wenn man die Mechanik richtig baut, stimmt die Schrittposition immer. Die einzige Abweichung ergibt sich mechanisch bedingt durch die Richtung, aus der die Maskierung gerade kommt (fährt sie raus oder rein?), wenn sie einen Punkt erreicht. Aber das ist verschmerzbar und auch nicht wirklich wahrnehmbar.

  10. Hallo Bert,
    super Artikel, so etwas habe ich schon lange gesucht. Insbesondere wie die Rampen umgesetzt werden. Was ich aber nicht verstehe, der Pi schaltet 3,3V an den GPIO Ausgängen, der Treiber hat aber einen 5V Schaltpegel. Bei TTL würde er High ab 2,4V erkennen. Benutzt der Treiber also TTL und entspricht das im Datenblatt den 5V?
    Gruß
    Gregor

    1. Da bin ich zu wenig Elektriker, um das richtig beantworten zu können. Ich verstehe die 5 V vom Treiber als Maximalwert, der nicht überschritten werden darf, um die Elektronik nicht zu killen. Die 3,3 V genügen jedenfalls, damit er ein Signal erkennt.

  11. Hallo Bert,

    echt eine coole Beschreibung die du hier verfasst hast . Top.

    Ich hätte eine kurze Frage zu deinem 1. Beispielscript, dort hast du ein time.sleep(0.0001875) drinnen.

    Wie bist du auf die 0.0001875 gekommen?

    Danke für deine Antwort.

    Mit freundlichen Grüßen
    Christian Jungwirth

    1. Hallo Christian,

      das ist nur irgend ein Wert, den ich aus einem Beispiel irgendwo im Internet gefischt habe. Er stellt die halbe Frequenz dar, mit der der Treiber befeuert wird (die Hälfte der Zeit an, die andere Hälfte aus).

      In den nachfolgenden Scripts siehst du, wie der Wert, der an sleep übergeben wird, berechnet wird. So kannst du die dynamische Berechnung nachvollziehen. Ob das allerdings 100%ig korrekt ist, kann ich nicht mit Sicherheit sagen. Am Ende zählt immer, was die optimale Frequenz für den jeweiligen Motor ist und welche Werte eben ganz unten am Ende der Berechnung ankommen.

  12. Könnte ich mit einem Nema 23 auch eine Selbstbau-Leinwand hoch- und runterfahren? Die Kraft müsste eigentlich reichen wenn ich das richtig ausrechne. Damit die Leinwand ohne Strom “gehalten” werden kann würde ich gerne eine Schneckenwelle mit Schneckenrad verwenden, leider finde ich keine Lösung die irgendwie passt. Hat jemand einen Tipp oder eine bessere Idee?

    1. Ein Nema 23 mit genügend Kraft sollte das auf jeden Fall schaffen. Über eine Gewindespindel sowieso. Kommt ja aber letztendlich darauf an, wie die Mechanik generell funktionieren soll.

      Ich frage mich nur, ob es dafür wirklich ein Schrittmotor sein muss. Außer den Endpunkten hast du ja nicht die Notwendigkeit der exakten Positionierung. Dafür ist die Ansteuerung aus meiner Sicht zu komplex.

      1. Ich möchte gerne eine Leinwand basierend auf zwei Rollen bauen. Eine maskierte Leinwand befindet sich auf Rolle 1. Auf Rolle 2 befindet sich nur ein Tuch für die Maskierung. Beide Rollen laufen synchron über einen Motor in die gleiche Richtung.

        Rolle 1 möchte ich nur so weit ausfahren wie für das Bildformat notwendig und habe damit dann die untere Maskierung. Die obere Maskierung erledigt Rolle 2. Auf Rolle 2 befindet sich nur exakt so viel Maskierung die bis zur Mitte des Projektionsbereichs reicht + Hälfte der unteren Maskierung. Fahre ich dann Leinwand+Maskierung gleichzeitg aus und beide Rollen laufen immer weiter, dann sollte sich die Maskierung auf Rolle 2 beim Erreichen des Endes automatisch wieder oben aufwickeln. Der unmaskierte Bereich für die Projektion zieht sich dann praktisch von der Mitte der Leinwand her auf.

        Wo bekomme ich denn günstig ein Schneckenrad und eine Schneckenwelle, die auf ein Nema Motor passt? Auf ebay bin ich nicht wirklich fündig geworden.

        1. Hallo Tobi,

          ich fürchte, dann kann ich dir nicht wirklich weiterhelfen, da ich sowas noch nicht selbst ausprobiert habe. Ich habe sämtliche mechanische Zutaten beim großen A gekauft.

          Ich denke, ich habe deine Idee ungefähr verstanden. Im Hinblick auf Kosten, Arbeitszeit und zu erwartendem Ergebnis sehe ich hier aber keinen wirklichen Vorteil gegenüber einer fertigen maskierbaren Rolloleinwand.

  13. Hallo Bert
    Seit nunmehr mehrere Monate arbeite ich an einem Projekt. Der Schrittmotor für den Seilzug und auch Lift werden im Kern mit dem hier beschriebenen Script angesteuert und funktioniert soweit sehr gut. Vielen Dank an dieser Stelle für den super beschrieb.
    Leider ruckelt der Motor unregelmässig und muss mittlerweile davon ausgehen, das die RPi.GPIO Lib das Problem auslöst. Es sieht so aus dass wenn der Motor etwas länger läuft diese “Aussetzer” zeigt. Je nach Installation dessen gar sehr gut hörbar. Dabei werden offensichtlich keine Schritte verloren. Auch die Bewegung des Schlittens bleiben ruhig und linear. Zumindest von blossem Auge nicht sichtbar. In einem Forum habe ich gelesen dass die pigpio lib dafür um einiges stabiler ist und wurde auch so bestätigt.
    Leider kenne ich selbst die nicht und kann folge dessen dein Script nicht direkt umschreiben.
    Mich würde interessieren ob andere dasselbe Problem hatten und ob es eventuel einfacher Lösungen gäbe.

    1. Hallo Daniel,

      ausschließen kann man es natürlich nicht, aber ich halte es für relativ unwahrscheinlich, dass die GPIO Lib das Problem ist. Die macht ja nicht viel mehr als Pins an und aus. Da halte ich es für naheliegender, dass eine ungünstige System-/Software-Kombination zu Mikro-Aussetzern führt, die sich dann natürlich bemerkbar machen. Das ist jetzt aber nur ins Blaue hinein geraten.

      Spiel vielleicht erstmal mit der minimalen und maximalen (eher letzteres) RPM rum, sprich mit der Frequenz, mit der der Treiber letztendlich angesteuert wird. Ich habe die Erfahrung gemacht, dass hier jeder Motor so seine persönliche Lieblingsfrequenz hat, bei der er ruhig und sauber läuft.

      1. Hallo Bert
        Ich weiss. Es scheint so als wenn das ein ausschalten zeitweise und unregelmässig pausiert. Der pi,, 3bplus, ist ohne weiteren Programme oder ähnlichem.

        Das hab ich mehrfach versucht. Momentan liegt der min bei 180 RPM und Max bei 300. Ebenfalls hab ich einen 2. Treiber sowie mehrere Motoren Typen getestet. Die mögliche 5V Falle konnte auch definitiv ausgeschlossen werden. Das Problem tritt zu unterschiedlichen times auf. Manchmal 1 mal dann wieder mehrmals während dem Lauf.
        Das Problem fällt auch auf bei einfachen test scripts.
        Um ehrlich zu sein bin ich am Ende meines Latein.

        1. Probier mal, die RPM-Werte in kleinen Schritten zu verändern. Manchmal sind ganz bestimmte Geschwindigkeiten einfach irgendwie doof.

          Du kannst auch mal im Script nachverfolgen, was mit dem Max-RPM passiert und wie sich das bis zum Ende in die Sleep-Zeit innerhalb der Schleife berechnet. Da kommt irgend eine 0,000XXXX Zahl bei raus. Die kannst du in der Schleife mal manuell eintragen (2 Stellen) und dann direkt verändern. Beziehungsweise macht das erste Scripts oben im Artikel ja genau das schon. So kommst du dem Problem vielleicht auf die Schliche. Wer weiß, am Ende beißt sich die Frequenz vielleicht mit dem Prozessortakt oder so. So genau kenne ich mich da aber auch nicht aus. 😉

          Als weitere Ursache könnte ich mir noch das Netzteil vorstellen.

  14. Hallo Bert,
    super Artikel auf den ich durch Zufall gestoßen bin. Eigentlich habe ich nach Informationen zur Ansteuerung eines Steppermotors gesucht. Umso überraschter war ich, was man damit alles realisieren kann. Toll.

    Meine Experimente haben aber gezeigt, dass deine Implementierung der Rampen zu einfach ist. Bei mir kommt der Steppermotor nicht an den gewünschten Positionen zum Stehen. Also habe ich wieder die alte Schul-Mathematik herausgekramt und einmal nachgerechnet. Am Ende deiner Rampe komme ich auf eine rechnerische Beschleunigung von 7733 RPM/s = (800 – 797,1) RPM / 0,000375 s. Das würde auch die Abweichungen erklären, da mein Stepper solchen Änderungen niemals folgen kann.

    Also habe ich weiter gesucht und bin dabei auf folgenden Link gestoßen:
    https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591185
    Ich bin gerade dabei den Beispielcode umzusetzen. Erste Versuche sehen vielversprechend aus.
    Vielleicht hilft der Link auch anderen Interessierten weiter, genauso wie mir dieser Artikel weiter geholfen hat.

    Danke Bert

    1. Hallo Michael,

      danke für dein Feedback und die Ergänzung! Ich konnte bei mir bisher keine Abweichungen durch die Rampe feststellen. Die Maskierung läuft seit mittlerweile über einem Jahr und hat noch nicht die kleinste Abweichung gezeigt. Ich will aber nicht ausschließen, dass das bei bestimmten Hardware-Kombinationen passieren kann. Eventuell ist es aber auch auf einen Schrittverlust zurückzuführen.

  15. Hallo Bert.

    Ich bin auch nur durch Zufall auf diesen Artikel gestoßen weil ich keine Maskierung habe automatisieren wollen. Dem Motor ist es aber egal was er bewegen soll, darum konnte ich für meine Zwecke ‚dem bewegen einer Schiebetüre‘ hier alles finden und selbst für mich ohne Progamierkentnisse funktioniert mit dieser Anleitung meine Türe schon fast perfekt.

    Umgesetzt habe ich das ganze ohne Seilzug sondern direkt mit einer 1m Gewindestange was bei meiner leichten Türe kein Problem (für diesen Motor) darstellte. Da ich so gut keine Ahnung von Python habe könnte ich Hilfe benötigen um „Endstop“ in stepper.py zu integrieren, ich weiß nicht wo ich das einfügen muss damit es funktioniert und ich würde gerne (nur zur Sicherheit) mit zwei Endschaltern arbeiten. Wo die Definition des (der) GPIO hin kommt ist ja klar aber wo kommt die „for i… Schleife“ hin, habe verschiedenes versucht aber wenn der ENDSTOP den Motor stoppt bewegt es sich danach überhaupt nicht mehr, wie komme ich da wieder weg.

    Kaum zu fassen das ich die Tür mit Siri öffnen und schließen kann und das flexibel zwischen 0 und 100% aber das mit dem ENDSTOP bekomme ich nicht auf die Reihe.

    Ein kurzer Überblick:
    Die Verbindung zu HomeKit erfolgt bei mir über Node-Red und die Befehle für den Motor setze ich mit SSH ab, funktionier tadellos. Auf beiden Seiten der Türe habe ich noch Touch-Sensoren die (immer 75%) öffnen bzw. schließen je nach Zustand der Türe. Für meine Türe benötige ich 38000 Schritte bei 0,9° meine Rampe ist 1100, MIN 250 und MAX 850, an den genauen Werten arbeite ich noch aber bei denen hier ist die Geschwindigkeit okay und auch die Geräusche halten sich in Mietwohnung akzeptablen grenzen.

    Gruß Stefan

    1. Hallo Stefan,

      ich kann dir jetzt natürlich nicht den Code schreiben, ohne ihn selbst testen zu können. Das ist leider die bittere Realität: Wenn du mit Programmieren nichts am Hut hast, bist du bei solchen Sachen aufgeschmissen. ¯\_(ツ)_/¯

      Wie auch immer, klingt ja nach einer coolen Lösung. Das würde ich gerne mal in Aktion sehen. So viel kann ich sagen: Wenn du hier ohnehin schon meine Software verwendest, dann brauchst du keine Endstops. Du kalibrierst ja genau die Nullposition und gibst die maximale Schrittzahl im Konfigurationsbereich an. Die dürfte erheblich höher liegen als in meinem Beispiel, macht aber nichts. Aber ansonsten funktioniert das dann genau wie eine Maskierung. So lange du keine Schrittverluste hast (unwahrscheinlich bei einer Gewindespindel), wird das immer exakt funktionieren.

      1. Hallo Bert.

        Danke für diese Information, die ENDSTOP‘s hatte ich nur als zusätzliche Sicherheit gedacht, aber es stimmt schon, nötig sind die eigentlich nicht.

        Nochmals vielen Dank – Ohne diese tolle Anleitung hätte ich das nicht geschafft!

        Gruß Stefan

  16. Hallo Bert,

    auch ich habe dein Skript erfolgreich im Einsatz, vielen Dank dafür. tolle Anleitung.

    ich habe allerdings noch zwei Fragen:

    ich habe den gleichen Motor Treiber wie du mit einem Nema17 High Torque Motor, allerdings Micro Stepping aktiviert, aktuell 1/8 entsprechend 1600 Steps pro Umdrehung.
    Habe natürlich auch die Parameter angepasst bei Grad pro Step 0.225. Funktioniert zwar aber habe mal testweise die rpm mit einer Stoppuhr gemessen und die kommt überhaupt nicht hin die gemessene rpm ist teilweise fünf mal so niedrig wie der eingetragene Wert. Hast du eine Idee woran das liegen könnte? Kommt der Code vielleicht nicht mehr hinterher? bei höheren eingetragenen rpm Werten wird der Motor außerdem gar nicht mehr schneller. ca. ab 1500 🙂 Aber selbst bei niedrigeren rpm Werten stimmt die um Drehungsgeschwindigkeit nicht.

    zweite Frage:

    Ich habe es leider nicht geschafft das Motor Skript im laufenden Betrieb durch eine variable zu stoppen. Hast du eventuell eine Idee wie ich das Skript im laufenden Betrieb durch eine variable oder Ähnliches stoppen kann?

    Vielen Dank und alles Gute, schöne Grüße Mike

    1. Hallo Mike,

      ja, das ist mir auch aufgefallen. Rein rechnerisch müsste es stimmen, aber die tatsächliche Geschwindigkeit ist anders. Ich vermute, das hängt mit Prozessortakt oder sonst irgendeiner Hardwaregeschichte zusammen. Ist aber im Grunde egal. Man stellt ihn ja so schnell ein wie es einem gefällt bzw. wie er eben mitmacht. Man könnte es als “Quasi-RPM” nennen.

      Stoppen im laufenden Betrieb geht nur, indem du vorzeitig aus der Schleife ausbrichst. Kann ich aber nicht empfehlen, weil du so ja die Rampe umgehst und damit Schrittverluste riskierst.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Hinweise zur Verarbeitung Deiner Angaben und Widerspruchsrechte: Datenschutzerklärung