PJLink zur Netzwerksteuerung von Projektoren

In diesem Teil der Artikelserie über die Netzwerk-Steuerung eines Heimkinos geht es um die Bedienung eines Projektors über die standardisierte Schnittstelle PJLink. Es gibt einige Parallelen zur Steuerung eines AV-Receivers von Yamaha, die wir uns letztes Mal näher angesehen haben (wer es nachmachen will, sollte den Artikel vorher lesen). Die Schnittstelle ist jedoch eine völlig andere, sowohl in ihrer Funktionsweise als auch was die Befehle angeht.

Der Mitsubishi HC7800D unterstützt das Protokoll PJLink

PJLink ist ein Standard, der von den meisten modernen Projektoren und anderen Displays unterstützt wird. Die Idee dahinter ist, dass in großen Firmen und Organisationen, wo mehrere Beamer installiert sind, diese zentral verwaltet und gesteuert werden können. So könnten zum Beispiel in einem Museum Informationen von Beamern an die Wände projiziert werden. Zu den Öffnungszeiten müssen alle Beamer eingeschaltet und am Abend wieder ausgeschaltet werden. Dazu will ja keiner mit 10 Fernbedienungen rumlaufen und überall an die Decke zielen müssen. Über einen zentralen PC, der im selben Netzwerk wie die Projektoren hängt, lassen sich alle Geräte verwalten. Neben der eigentlichen Steuerung lassen sich so auch Statusinformationen abfragen, wie etwa die Lampenlaufzeit oder aufgetretene Fehler.

Das ist ja alles sehr schön, aber wir nutzen das im Heimkino einfach nur, um unseren Beamer per Netzwerk fernzusteuern. So können wir ihn in unsere App integrieren und benötigen keine zusätzliche Fernbedienung mehr. Über ein paar Vorteile, die das nebenbei mit sich bringt, habe ich im einleitenden Artikel schon mehr geschrieben.

Ausgangssituation

Ich kann das heute stark abkürzen, denn die Voraussetzungen sind im Grunde die selben, wie für die Yamaha-Steuerung. Der Projektor muss per Kabel (oder WLAN, falls möglich) mit dem Netzwerk verbunden sein. Grundvoraussetzung für PJLink: der Anschluss des Beamers per Netzwerkkabel, zum Beispiel über einen Netzwerk-SwitchEventuelle Einstellungen bezüglich der Erreichbarkeit im Netzwerk müssen nach Bedienungsanleitung korrekt vorgenommen worden sein – das versteht sich ja von selbst.

Ich gehe nicht weiter auf die Einstellungen meines Projektors, dem Mitsubishi HC7800D, ein, denn PJLink ist wie schon gesagt ein offener Standard, den die Projektoren verschiedenster Hersteller unterstützen können. Es ist also ziemlich egal, mit welchem Beamer Ihr das versucht, solange er PJLink unterstützt. Dafür gibt es neben den Netzwerkeinstellungen wahrscheinlich auch noch zusätzliche Einstellmöglichkeiten, etwa um die PJLink-Unterstützung überhaupt zu aktivieren.

Darüber hinaus kann ein Passwort festgelegt werden, welches bei den Anfragen übergeben wird. Das Passwort ist erforderlich, damit der Beamer prüfen kann, ob die empfangenen Befehle von einer autorisierten Quelle stammen – einer Quelle wie unserer App, die das Passwort kennt. Bei der Yamaha-Schnittstelle gibt es dieses zusätzliche Sicherheitskonzept nicht. Dort können bestimmte Geräte anhand ihrer MAC-Adresse berechtigt werden, auf die Schnittstelle zuzugreifen. Beide Wege haben Vor- und Nachteile, auf die ich jetzt aber nicht näher eingehen will.

Kommunikation mit dem Projektor

Ich werde wieder Codebeispiele in PHP und Node.js zeigen, wie die Kommunikation über PJLink abläuft. Ich verzichte wieder auf detaillierte Fehlerüberprüfung, um es nicht zu sehr aufzublähen.

Grundsätzlich nutzt PJLink nicht das HTTP-Protokoll, sondern reines TCP. Das macht die Sache ein kleines bisschen umständlicher, da man es mit rohen Streams zu tun hat, die entsprechend gelesen, geschrieben und abgeschlossen werden wollen. Deshalb arbeiten wir in PHP nicht mit der cURL-Erweiterung und bei Node.js nicht mit dem http-Modul, sondern nutzen in beiden Fällen „rohe“ Socket-Verbindungen. Zuerst das Beispiel für PHP:

$ip = '192.168.0.51';
$port = 4352;
$password = 'geheim';
$command = '%1INST ?';

$url = 'tcp://' . $ip . ':' . $port;
$client = stream_socket_client($url, $errorCode, $errorMessage);
stream_set_timeout($client, 2);

if ($client === false) {
    die('Device not responding');
}

$result = stream_get_line($client, 1024, "\r");

if (substr($result, 0, 8) == 'PJLINK 1')
{
    $pwtoken = substr($result, 9, 8);
    $command = md5($pwtoken . $password) . $command;
}

fwrite($client, $command . "\r");
$result = stream_get_line($client, 1024, "\r");

$resultCode = substr($result, strpos($result, '=') + 1);

$resultMessages = array(
    'OK' => 'Successful execution',
    'ERR1' => 'Undefined command',
    'ERR2' => 'Out of parameter',
    'ERR3' => 'Unavailable time',
    'ERR4' => 'Projector/Display failure'
);

if (in_array($resultCode, array_keys($resultMessages))) {
    die($resultCode . ': ' . $resultMessages[$resultCode]);
}

fclose($client);

echo $result;

Und hier das gleiche als Modul für Node.js – das kann dann parallel zu dem beim letzten Mal erstellten Modul für Yamaha verwendet werden.

var net = require('net');
var crypto = require('crypto');

function executeCommand (data, onResult)
{
    var host = data.ip || '192.168.0.51';
    var port = data.port || 4352;
    var password = data.password || 'geheim';
    var command = data.command || '%1INST ?';
    
    var socket = new net.Socket();
    
    socket.connect(port, host, function() {
        console.log('CONNECTED TO: ' + host + ':' + port);
        socket.write('');
    });
    
    socket.setTimeout(2000, function () {
        socket.end();
        socket.destroy();
        console.log('Device not responding');
    });

    socket.on('data', function(message)
    {
        message = message.toString('utf8').trim();
        
        switch (message.substring(0, 8))
        {
            case 'PJLINK 1':
                
                var pwtoken = message.substring(9, 17);
                
                var digest = crypto.createHash("md5")
                    .update(pwtoken + password)
                    .digest("hex");
                
                socket.write(digest);
                
            case 'PJLINK 0':
                
                socket.write(command);
                socket.write("\r");
                return;
        }
        
        console.log("PJLink Response: " + message);
        
        var resultCode = message.substring(message.indexOf('=') + 1)
                                .substring(message.indexOf(' ') + 1);
        
        var resultMessages = {
            OK:   'Successful execution',
            ERR1: 'Undefined command',
            ERR2: 'Out of parameter',
            ERR3: 'Unavailable time',
            ERR4: 'Projector/Display failure',
            ERRA: 'Authentification error'
        };
        
        if (resultCode.substring(0, 3) == 'ERR') {
            console.log(resultMessages[resultCode]);
        }
        
        onResult(message);
        
        socket.destroy();
    });

    socket.on('close', function() {
        console.log('PJLink: Connection closed');
    });
}

module.exports = {
    executeCommand: executeCommand
};

Der komplizierteste Teil ist hier eigentlich die Anmeldung. Sobald man die erste Anfrage an das Gerät stellt, antwortet dieses zuerst mit der Bitte um Authentifizierung. Dafür schickt es einen achtstelligen Code mit. Dieser Code wird mit dem Passwort kombiniert, md5-verschlüsselt und anschließend an das Gerät zurück geschickt. Ist der Beamer einverstanden, ist er für alle Anfragen bereit, solange die TCP-Verbindung geöffnet bleibt. Dann kann ein Befehl oder eine Statusabfrage gesendet werden.

Da man in der Regel aber nur eine Taste drückt und zwischen den Befehlen ohnehin etwas „Bearbeitungszeit“ bleiben sollte, ist dieser Vorgang praktisch jedes Mal notwendig. Das macht aber nichts, da das alles so schnell geht, dass man keinerlei Verzögerung bemerkt.

Die Befehle der PJLink-Schnittstelle sind sehr kurz gehalten, ebenso die Antworten vom Gerät. Ziel der Schnittstelle ist es unter anderem, möglichst wenig Datenverkehr im Netzwerk zu generieren. Im Vergleich zur XML-Schnittstelle von Yamaha sind die Befehle so zwar nicht immer selbsterklärend, dafür aber übersichtlicher und sparsamer.

Im Folgenden zeige ich einige Beispiele, was über PJLink möglich ist.

Einige Befehle von PJLink

Grundsätzlich beginnen alle PJLink-Befehle mit %1, was wohl so viel heißen soll wie „Achtung, jetzt kommt was neues“. Danach folgt eine vierstellige Abkürzung der gewünschten Funktion.

Wenn Werte gesetzt werden sollen, werden diese anschließend durch ein Leerzeichen getrennt angehängt – sollen Werte gelesen werden, wird ein Fragezeichen angehängt. Als Antwort kommt in der Regel die selbe Syntax zurück, mit dem Unterschied, dass der aktuelle Wert angehängt wurde. Das war die Kurzfassung, reicht aber völlig aus, um die wesentliche Vorgehensweise zu erklären.

Power

%1POWR 1

Dieser einfache Befehl zeigt sehr schön das immer wiederkehrende Muster von PJLink. Nach dem Kürzel POWR folgt entweder eine 1 (an) oder eine 0 (aus) – mehr ist das nicht. Zu beachten ist dabei aber, dass der Projektor sich eine Reaktionszeit vorbehalten darf, bevor ein weiterer Befehl akzeptiert wird. Das ist ja eigentlich auch logisch.

Interessant wird es, wenn man den Status des Wertes abfragt:

%1POWR ?

Der Beamer kann hier nicht nur mit 1 (an) und 0 (aus) antworten, sondern auch mit 2 (aufwärmen) oder 3 (abkühlen). Würde man also den Power-Zustand auf 1 setzen und ihn innerhalb ca. einer Minute danach abfragen, würde nicht etwa 1 zurückgegeben, sondern 2 – ähnlich beim Ausschalten.

Was die Schnittstelle für manche vielleicht interessant macht ist die Tatsache, dass das Ausschalten darüber ohne mehrfaches Senden des Befehls möglich ist. Normalerweise muss man Beamer immer durch zweimaliges Drücken ausschalten, um ein versehentliches Deaktivieren zu verhindern. Über PJLink geht das direkt.

Input

%1INPT 31

Die Eingangswahl funktioniert ähnlich. Hier wird ebenfalls eine Zahl mitgegeben, die für einen der verfügbaren Eingänge steht. Welche Zahlen das sind, definiert das Gerät selbst. Sie können mit einem anderen Befehl abgefragt werden:

%1INST ?

Das gibt eine Auflistung aller verfügbaren Eingänge zurück. Das ist eine schöne Sache, wenn man eine Steuerung entwickeln will, die sich dynamisch an das Gerät anpasst. Schließlich haben verschiedene Geräte auch verschiedene Eingänge. Die Zahlen werden vom Hersteller halbwegs sinnvoll nach Art des Eingangs festgelegt. So sind bei Mitsubishi zum Beispiel alle HDMI-Eingänge im 30er-Bereich.

Auch hier ist interessant, dass die Eingangswahl bei PJLink ohne weitere Spezialitäten des Projektors auskommt. Normalerweise wird nach dem Umschalten immer für ein paar Sekunden der Name des gewählten Eingangs im Bild angezeigt – bei PJLink nicht. Das wird wohl kaum jemandem was bringen, aber für Freaks wie mich, die sich eine Filmvorführung ohne irgendwelche technischen Details im Bild wünschen, ist das eine nützliche Eigenschaft.

Video-Mute

%1AVMT 31

Was man von anderen Geräten als Ton aus (Mute) kennt, gibt es bei einem Beamer auch als Bild aus: Video-Mute. Auch das kann eine wahnsinnig nützliche Funktion sein, wenn man vor dem Start des Films nicht das Kapitel-Menü der Blu-ray im Pause-Modus sehen will. Der Beamer zeigt dann einfach ein komplett schwarzes Bild an, bis man die Funktion wieder deaktiviert. Interessant ist, dass es diese Möglichkeit über die Original-Fernbedienung oder am Gerät gar nicht gibt.

PJLink unterscheidet zwischen Audio-Mute, Video-Mute und Audio+Video-Mute – also Ton und Bild jeweils getrennt oder gleichzeitig. Da mein Mitsubishi keine Unterstützung für Ton bietet, zeigt verständlicherweise auch die Audio-Mute-Funktion keinerlei Reaktion. Man sollte meinen, dass nur Video-Mute etwas bewirkt, aber auch das funktioniert nicht. Trotz des Fehlens von Ton-Unterstützung wirkt sich nur Audio+Video-Mute aus. Das kann verstehen wer will – ist wohl eine Laune des Herstellers und ein gutes Beispiel dafür, dass es mit Schnittstellen nicht immer so genau genommen wird. Hauptsache es funktioniert irgendwie.

Statusabfrage

%1LAMP ?
%1NAME ?
%1INF1 ?
%1INF2 ?
%1INFO ?
Nutzt PJLink als Schnittstelle: ProjectorView Global+
Nicht schön, aber funktional: Mitsubishis Software ProjectorView Global+

Neben den gezeigten Befehlen lassen sich unter anderem auch Geräteinformationen abfragen. Dazu gehören Hersteller und Modell, Lampenlaufzeit und Fehlerstatus. Für unsere Steuerung ist das wenig relevant, wohl aber für den eingangs erwähnten Anwendungsfall in großen Organisationen. Schließlich muss eine Software zur Überwachung die Geräte auflisten und dabei irgendwie benennen können. Wenn Ihr Euch schon immer gefragt habt, warum Ihr Eurem Beamer einen Namen geben könnt, wisst Ihr jetzt, wozu das gut ist. Mitsubishi stellt mit ProjectorView Global+ eine solche Software zur Verfügung – nicht hübsch, aber funktional.

Dokumentation

Die Schnittstelle PJLink wurde von einer Organisation ins Leben gerufen, die auch die Website dazu betreibt. Dort findet sich eine ausführliche Dokumentation aller Befehle im PDF-Format. Viel mehr benötigt man dazu eigentlich nicht, denn so wahnsinnig umfangreich ist das alles ja nicht.

Welche Befehle aber tatsächlich unterstützt werden, ist eine ganz andere Sache. Der Mitsubishi HC7800D unterstützt die meisten Befehle, ein paar weniger wichtige aber auch nicht. Ein Blick in die Bedienungsanleitung oder Anleitung zur Netzwerksteuerung des Gerätes sollte Aufschluss darüber geben. Die entsprechenden Abschnitte gehen meistens auf eine weitere Art der Bedienung ein: Viele Geräte werden mit einem Web-Interface ausgeliefert, das durch die Eingabe der IP-Adresse des Gerätes in den Webbrowser aufgerufen wird. Das taugt in der Regel nicht allzu viel, lädt aber zum Ausprobieren ein.

Eine selbst entwickelte Steuerungsmöglichkeit, wie ich sie hier gezeigt habe, wird meistens nicht direkt propagiert, weshalb die mit dem Gerät ausgelieferten Unterlagen auch nicht weiter darauf eingehen. Wie auch bei Yamaha kann man sich nur denken: Wenn es eine App oder Browser-Steuerung für das Geräte gibt, kann man diese Befehle auch durch eine andere Software verschicken. Im Falle von PJLink ist das eben durch eine unabhängige Stelle gut dokumentiert und wird vom Hersteller der Hardware bestenfalls angedeutet.


Wenn das mal jemand ausprobieren sollte – vielleicht auch mit einer anderen Programmiersprache – würde ich mich sehr freuen, von den Ergebnissen zu hören. Da PJLink scheinbar von mehreren hundert Geräten unterstützt wird, ist die Wahrscheinlichkeit recht groß, dass auch Euer Projektor damit umgehen kann.

Im nächsten Artikel der Serie gehe ich näher auf die Steuerung eines Mitsubishi-Projektors ein. Wie unschwer zu erkennen ist, bietet PJLink ja keinen besonders großen Befehlsumfang. Die Steuerung des Menüs oder das Festlegen von Einstellungen ist damit nicht möglich, weil diese von Gerät zu Gerät zu unterschiedlich sind – eine einheitliche Schnittstelle würde da kaum zum Ziel führen. Für diesen Zweck bietet Mitsubishi eine sehr ähnliche Schnittstelle, die weit mehr Befehle kennt.

13 Gedanken zu „PJLink zur Netzwerksteuerung von Projektoren

  1. Grüß Gott Herr Kößler,
    ich möchte über die Pjlinks-Steuerbefehle meinen Projektor(Panasonic DW730) ansteueren. Das Problem ist, dass ich mich mit der Übertragung der Befehle nicht auskenne. Ich kann zwar über den Webbrowser auf den Projektor zugreifen aber der Rest ist mir ein Rätsel. In der Anleitung habe ich die Steuercodes der PJlink für DW730 aber wie sie übertrage weiße ich nicht. Ich meine ich brauche bestimmt irgendein Programm wo ich diese Befehle eintippen kann. Würde mich sehr freuen, wenn Sie mir Schritt für Schritt erklären könnten.

    LG
    Noori

    1. Das Programm müssen Sie selbst programmieren. Die beiden Beispiele oben lassen sich in fast jeder beliebigen Programmiersprache umsetzen. Die Frage ist ja auch, was Sie überhaupt damit erreichen wollen.

      1. Was ich damit erreichen will ist, dass ich meinen Projektor über einen einfachen Befehl von PJlinkProtocol ein und ausschalten und evtl den intern eingebauten Shutter auf und zumachen kann.
        DANKE
        LG

        1. Beides ist über PJLink möglich, sofern der Projektor die Befehle unterstützt. Ein- und Ausschalten wird er wohl unterstützen, das wäre sonst witzlos.

          Mit “interner Shutter” ist vermutlich die AV-Mute-Funktion gemeint, also das Bild schwarz schalten. PJLink bietet hier per Definition die Varianten Audio-Mute, Video-Mute und Audio-Video-Mute. Ob und wie der Projektor das unterstützt, muss man testen. Mein Mitsubishi reagiert nur auf Audio-Video-Mute, nicht auf Video-Mute — und das obwohl er gar keine Audio-Ausgabe kennt.

  2. Ich weiß, dieser Thread ist schon etwas älter… Ich nutze dieses Script in einem Adapter für den ioBroker und dabei sind mir ein paar Fehler aufgefallen, die ich in einer etwas überarbeitete Version gefixed habe (z.B. funktioniert in dem o.g. Script die Kommunikation mit Password nicht). Und auch das Error-Handling habe ich etwas ergänzt…

    Hier der Link zu GitHub: https://github.com/oberstel/ioBroker.pjlinkv2/blob/master/lib/pjlink.js

    1. Hallo Gerald,

      mit JavaScript geht das nur über Node.js, da du eine serverseitige Umgebung benötigst, die nicht eingeschränkt ist wie ein Browser. Das PHP-Beispiel hier kannst du mit den nötigen Kenntnissen für Node.js-Code umschreiben.

Stelle eine Frage

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

Hinweise zur Verarbeitung Deiner Angaben und Widerspruchsrechte: Datenschutzerklärung