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

HTTP-Webservice mit Node.js zur Steuerung eines Schrittmotors

Für eine Leinwand-Maskierung, die sich mechanisch verschieben lässt, haben wir einen Schrittmotor mit Gewindespindel als Antrieb vorgesehen. Ein Raspberry Pi steuert den Motor mit Hilfe eines Python-Scripts. Weil das für die Einbindung in die Heimkino-Steuerung etwas unpraktisch ist, wollen wir hier noch einen kleinen Webservice drum herum stricken, der die Steuerung der Maskierung im hausinternen Netzwerk zur Verfügung stellt.

Ein Raspberry Pi 3 mit diversen belegten Pins, die mit dem Schrittmotor-Treiber verdrahtet sind.

Der Vorteil eines HTTP-Webservices ist, dass es mittlerweile sehr viele Systeme zur Hausautomation gibt, die diesen ansprechen können. Dazu gehören unter vielen anderen die beliebte Logitech Harmony und der Light-Manager Air. Zwar ist mit der hier gezeigten Umsetzung noch keine direkte Anbindung an einen Sprachassistenten möglich, aber über den Umweg eines der genannten Systeme ist das nur noch Fleißarbeit. Außerdem wollen wir unsere Maskierung ja nicht gleich der ganzen Welt öffnen.

HTTP-basierte Schnittstellen sind im Grunde nichts anderes als gewöhnliche Internetseiten – nur eben nicht mit einem hübschen Layout. Stattdessen werden pure Daten ausgetauscht und Befehle gesendet. Dazu benötigen wir nur einen kleinen Webserver – was komplizierter klingt, als es ist. Anstatt den Webservice im Internet zur Verfügung zu stellen (was sicherheitstechnisch zumindest bedenklich wäre), läuft dieser auf dem Raspberry Pi, der auch den Schrittmotor steuert, und stellt ihn im lokalen Netzwerk bereit.

In diesem Artikel stelle ich dir den kompletten Code zur Verfügung, der dafür notwendig ist und zeige dir, wo du Anpassungen vornehmen musst. Die Python-Scripts zur Steuerung des Motors sind ebenfalls enthalten und im Vergleich zu unseren einfachen Versuchen aus dem vorherigen Artikel nun weitestgehend ausgereift.

Weiterhin gehe ich darauf ein, wie du den Webservice nutzen kannst und wie er sich in deine Heimkino-Steuerung einbinden lässt.

Grundvoraussetzung ist, dass du einen Raspberry Pi als Controller für den Schrittmotor einsetzt. Mit einem Arduino funktioniert die hier vorgestellte Lösung nicht out-of-the-box. Ausgangslage ist ein NEMA-17-Schrittmotor mit 4 Kabeln, ein TB6600-Treiber und besagter Raspberry Pi. Den Aufbau und die Installation von Raspbian als Betriebssystem habe ich im vorherigen Beitrag vorgestellt. Deshalb erwähne ich das hier nur nochmal sehr knapp.

Es ist sicher möglich, die Python-Scripts samt Webservice auch auf anderen Systemen laufen zu lassen – wenn du das willst, wirst du schon wissen, was du tust.

Legen wir los.

1. Installation auf dem Raspberry Pi

Du würdest diesen Artikel vermutlich nicht lesen, wenn du nicht bereits erste Schritte mit einem Schrittmotor (ha!) gemacht hättest. Ich gehe deshalb davon aus, dass du Raspbian als Betriebssystem installiert hast und bereits Python-Scripts ausführen konntest.

Für den Anfang solltest du nochmal das System auf den neuesten Stand bringen, was etwas dauern könnte:

sudo apt-get update
sudo apt-get upgrade

1.1 Node.js installieren

Zuerst installierst du Node.js einschließlich npm (Node Package Manager) nach diesem Tutorial. Das ist leider etwas unübersichtlich, funktioniert aber sonst ganz gut.

Anmerkung: Die Node-Website verlinkt ein *.tar.xz-File – im Tutorial wird aber von einem *.tar.gz ausgegangen. Ändere einfach beim WGET die Dateiendung ab; auf dem Server liegt beides.

1.2 Den Webservice einrichten

Den kompletten Code, den du für den Webservice benötigst, kannst du von GitHub herunterladen. Python-Scripts für die Motorsteuerung sind ebenfalls enthalten. Das Projekt legst du am besten im Home-Verzeichnis deines Pi-Standardbenutzers in einem Unterverzeichnis screen-masking ab. Darin sollte sich unter anderem die Datei index.js befinden.

1.3 Node-Module installieren

Stelle sicher, dass du dich im Projektverzeichnis befindest:

cd ~/screen-masking

Dann kannst du die Komponenten für das Projekt installieren:

npm install

Wie das bei Node-Projekten so üblich ist, sind darin alle Abhängigkeiten definiert. Das Projekt weiß also selbst, was es braucht.

1.4 Testlauf starten

Jetzt wäre es an der Zeit, zu testen, ob die App grundsätzlich läuft.

node index.js

Die App sollte starten und ausgeben, unter welchem Port sie erreichbar ist. Tippe die folgende URL in einen Webbrowser ein, der sich im selben Netzwerk wie der Raspberry Pi befindet (oder nutze curl, wenn dir das einfacher erscheint).

http://<raspberry-pi-ip>:<port>/force-by/500

Den Teil <raspberry-pi-ip> ersetzt du natürlich durch die IP-Adresse des Raspberry Pi und <port> durch die Portnummer, die beim Start der App angezeigt wurde.

An dieser Stelle eine kleine Warnung: Teste die Funktionalität immer nur dann, wenn der Motor nicht mit deiner Maskierung verbunden ist. Wenn der Motor über die mechanischen Grenzen der Maskierung hinaus fährt, wird sehr wahrscheinlich etwas kaputt gehen. Einmal richtig eingerichtet, kümmert sich die App darum, dass sowas nicht passiert.

1.5 App mit dem System starten

Du wirst die App wahrscheinlich automatisch mit dem System starten lassen wollen. So kannst du einfach mal den Stecker ziehen. Nach einem Neustart wird sie dann automatisch wieder gestartet. Dieses Tutorial erklärt, wie das geht.

sudo vi /etc/rc.local

Füge hier die folgende Zeile ein:

su pi -c 'cd /home/pi/screen-masking; node index.js < /dev/null &'

Nach einem Neustart (init 6) sollte die App automatisch im Hintergrund gestartet werden. Der Testlauf durch Aufruf der URL wie oben beschrieben sollte immer noch funktionieren.

1.6 Konfiguration anpassen

Die Node.js-App ist nur ein „Wrapper“ für die Python-Scripts, um diese auf einem standardisierten und breit unterstützten Weg im Netzwerk verfügbar zu machen. An der App selbst muss nichts konfiguriert werden (außer du möchtest unbedingt den äußerst sorgfältig gewählten Port ändern).

Allerdings solltest du das Verhalten des Schrittmotors konfigurieren und an deine Hardware anpassen. Das erledigst du in der Datei python/tb6600/stepper.py. Alle wichtigen Einstellungen sind als Konstanten definiert.

  • Du kannst die Pin-Nummern des Raspberry Pi für DIRPUL und ENA anpassen, falls du sie abweichend von der Beschreibung in python/tb6600/wiring-nema17-4pin.md verkabelt hast.
  • Der Winkel eines Motorschritts ist in STEP_ANGLE mit 0,9° vorkonfiguriert, was bei den meisten Motoren einem Halbschritt entspricht.
  • Die Rampe ist in RAMP_LENGTH mit 600 Schritten angegeben, um den Motor sanft anfahren und abbremsen zu lassen.
  • MIN_RPM und MAX_RPM definieren die langsamste und schnellste Geschwindigkeit des Motors in Umdrehungen pro Minute. Die Werte sollten so klein bzw. groß wie möglich sein, so dass der Motor keine Aussetzer hat oder übermäßig starke Resonanzen erzeugt.
  • MAX_STEPS ist von allen Einstellungen die wichtigste. Sie besagt, wie viele Schritte der Motor fahren darf, bevor er die Obergrenze des Fahrwegs erreicht.

Noch ein paar Informationen zu MAX_STEPS. Wie bereits erwähnt stellt das Python-Script sicher, dass der Motor nur im Bereich von 0 bis MAX_STEPS bewegt werden kann. Er kann sich nicht oberhalb oder unterhalb dieser Grenze bewegen, sondern wird vorher anhalten. Damit wird sichergestellt, dass der Motor nicht versehentlich versucht, deine Maskierung über ihre mechanischen Grenzen hinaus zu ziehen.

Bevor du das Script an deiner Maskierung ausprobierst, musst du genau ermitteln, wie viele Schritte dein Motor machen muss, um die Maskierung über ihren vollen mechanischen Fahrweg zu ziehen. Wenn der Motor eine Gewindespindel antreibt, wird der Schlitten sehr wahrscheinlich die selbe Strecke zurücklegen müssen wie die Maskierung. In meinem Fall sind das 20 cm. Abhängig von der Steigung des Gewindes könnten dafür zum Beispiel 10.000 Schritte notwendig sein. Du solltest vorab mit dem Motor gewisse Schrittzahlen abgefahren sein und dabei mit einem Lineal ausgemessen haben, welche Strecke dabei zurückgelegt wurde. So kommst du am Ende darauf, wie viele Schritte für die Strecke notwendig sind. Diesen Wert trägst du für MAX_STEPS ein.

Bedenke dabei auch: Um die selbe Strecke zurückzulegen sind doppelt so viele Halbschritte (0,9°) notwendig, wie ganze Schritte (1,8°) erforderlich wären. MAX_STEPS hängt also auch mit STEP_ANGLE zusammen und mit der Schrittgröße, die du mit den kleinen Schaltern am TB6600-Treiber festgelegt hast.

2. API des Webservice

Du kannst im Grunde die Python-Scripts direkt ausführen, wie du es bereits gewohnt bist. Die meisten davon sind nur Abkürzungen und greifen am Ende immer auf Funktionen zu, die in stepper.py definiert sind. Der Weg, den wir hier einschlagen wollen, ist aber der, die App zu nutzen.

Wie bereits mit dem Test weiter oben erfolgt, rufst du verschiedene URLs mit Parametern auf, um die einzelnen Funktionen anzusteuern. Nachfolgend sind alle Möglichkeiten aufgelistet. Verwende HTTP GET um Werte abzufragen und PUT um Aktionen auszuführen. Rückgabewerte werden im JSON-Format ausgegeben.

GET /position
Gibt die aktuelle Position des Motors in Anzahl Schritten zurück. Der Wert liegt zwischen 0 und MAX_STEPS.

GET /power
Gibt den Zustand eines Relais-Moduls zurück, sofern du eines verwendest, um die Stromzufuhr zum Treiber zu kontrollieren. Mögliche Werte sind 1 oder 0. Mehr dazu später.

PUT /move-to/1000
Bewegt den Motor absolut an die angegebene Schrittposition. Nimmt Werte im Bereich von 0 bis MAX_STEPS entgegen. Die Bewegung erfolgt möglichst schnell und verwendet die Rampe.

Nach jeder Bewegung bekommst du die neue Position in Schritten zurück. Du könntest diesen Wert zum Beispiel verwenden, um das Bildseitenverhältnis der Leinwand anzuzeigen.

PUT /move-by/250
Bewegt den Motor relativ um die angegebene Schrittzahl. Nimmt eine beliebige positive oder negative Ganzzahl entgegen. Beispiel: Der Motor steht aktuell an Position 4350, du rufst /move-by/-350 auf, die neue Position nach der Bewegung ist 4000. Die Bewegung erfolgt möglichst schnell und verwendet die Rampe.

Du kannst dem Motor so zwar befehlen, über die Ober- und Untergrenze hinaus zu fahren, aber er wird rechtzeitig anhalten und auch nur die tatsächlich erreichte Position zurückgeben.

PUT /force-by/250
Bewegt den Motor relativ um die angegebene Schrittzahl. Nimmt eine beliebige positive oder negative Ganzzahl entgegen. Die Bewegung erfolgt so langsam wie möglich und verwendet die Rampe nicht. Die Unter- und Obergrenze der Bewegung wird ignoriert! Verwende das nur mit großer Vorsicht. Bei entsprechenden Aufrufen wird sich der Motor aus den mechanischen Grenzen der Maskierung hinaus bewegen. Die Funktion ist dafür gedacht, die Position korrigieren zu können. Alternativ könntest du einfach am Motor drehen.

PUT /calibrate
Setzt die intern gespeicherte Position auf 0 zurück. Rufe das auf, nachdem du die Maskierung an die untere Grenze gesetzt hast.

PUT /power/1
Legt den Schaltzustand eines optional an Pin 11 angeschlossenen Relais-Moduls fest. Nimmt die Werte 1 oder 0 entgegen. Das Relais wird vor jeder Motorbewegung automatisch aktiviert, falls nicht bereits geschehen. Einschalten musst du es also eigentlich gar nicht. Wenn du kein Relais-Modul verwendest, ist der Strom sowieso immer an, bis du den Stecker ziehst.

3. Einbindung in bestehende Steuersysteme

An dieser Stelle sollte es dir also möglich sein, den Schrittmotor über HTTP-Aufrufe zu steuern. Somit kannst du die Steuerung der Maskierung in jedes beliebige System zur Hausautomation einbauen, die das unterstützt.

3.1 Light-Manager Air

Beispielhaft zeige ich hier mal, wie das mit dem Light-Manager Air funktioniert.

In AirStudio legst du in der Aktorenverwaltung einen neuen Aktor vom Typ IR–Funk–LAN an, wählst LAN aus und klickst bei Taste 1 auf Konfigurieren. Hier wählst du PUT aus und gibst die URL ohne vorangestelltes http:// ein. Daten werden nicht benötigt.

Die Taste kannst du noch wie das damit eingestellte Bildformat benennen. Für Taste 2 könntest du beispielsweise den gegenläufigen Befehl senden. Oder du verwendest /move-by/200 und nutzt die beiden Tasten, um die Maskierung schrittweise hoch und runter zu bewegen.

3.2 Einbinden in CinemaVision-Actions

Du kannst die Steuerung der Maskierung als Teil einer CinemaVision-Action aufrufen.

http://<IP-Adresse>:<Port>/move-to/3500
PUT: { }

So kannst du beispielsweise zum Ende des Werbeblocks automatisch die Maskierung von 16:9 auf 21:9 reinfahren lassen.

Nun konnte ich diese App natürlich nicht in jedem denkbaren Szenario testen. Deshalb bin ich auf deine Mithilfe angewiesen, eventuelle Fehler zu finden und zu beheben. Das betrifft sowohl das Script selbst als auch die Anleitung in diesem Artikel. Wie gut konntest du das nachvollziehen? Ist etwas nicht schlüssig oder fehlen wichtige Informationen? Schreibe bitte einen Kommentar, wenn du etwas in der Art findest.

Gleiches gilt, wenn du ein weiteres Beispiel hast, wie du den Webservice nutzt. In welches System bindest du die Aufrufe ein? Weitere Beispiele sind hier gern gesehen.

Ü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.

2 Gedanken zu „HTTP-Webservice mit Node.js zur Steuerung eines Schrittmotors

  1. Hallo Bert!
    Vielen Dank für die Fortsetzung der Reihe.
    Gedanklich wäre eine Lösung ideal, welche das Seitenverhältnis der Quelle ausliest (zur Vereinfachung vom Webserver über Kodi) Und die Maskierung automatisch entsprechend anpasst. Hast du hierfür vielleicht eine Anregung wie das ganze umgesetzt werden könnte?

    Grüße Dennis

    1. Hi Dennis,

      das wäre zu ungenau, weil Videos in 16:9 inklusive schwarzen Balken vorliegen können. Ermittlung anhand der Pixel wäre zu aufwendig und ungenau. Und alkes wäre auf Kodi eingeschränkt.

      Ich würde eher dazu neigen, z. B. themoviedb.org anzuzapfen. Müsste man mal sehen, ob die diese Daten vorliegen haben.

      Tendenziell empfinde ich es aber als ausreichend einfach, verschiedene Buttons für die wichtigsten Formate zu haben. Die 2% Filme mit Sonderformaten korrigiert man dann manuell.

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