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

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 = 4,9 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:

# ...

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 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 = stepper.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.

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