3D-Drucker über OpenHAB Sitemap steuern

3D-Drucker über OpenHAB Sitemap steuern

Ganz genau!

Dieser Blog-Eintrag basiert auf dem hier. Also überspringe ich relativ viel zu Anfang.

Damit wir unseren 3D-Drucker steuern können, brauchen wir wieder ein PlugIn, nämlich das MQTT Subscribe PlugIn. Dieses setzt auch das MQTT PlugIn aus dem vorherigen Post voraus.

Das MQTT-Publish PlugIn ist auch ganz cool. So könnt ihr aus OctoPrint eure MQTT-Plugs per Knopfdruck steuern. Z.B. die Stromzufuhr zum Drucker, Lampen oder eine ESP32-CAM. Dies kann beispielsweise die Funktion der Octoprint PSU-Control übernehmen.

Außerdem hat jneilliii auch noch ein TasmotaMQTT PlugIn geschrieben. Damit könnt ihr den ganzen OpenHAB kram überspringen und direkt an eure Tasmota Geräte funken.

Wenn ihr die gewünschten PlugIns installiert habt, müsst ihr einmal Octoprint neu Starten.

Dann gehen wir in die PlugIn Konfiguration und drücken zuerst im Feld unter General bei API KEY auf das blaue + und dann oben rechts im PopUp auf Erlauben. So erhält das PlugIn eine API um mit Octoprint zu kommunizieren. Octoprint empfielt nicht den HauptAPI Key zu benutzen, da man den Apps sonst vollen Adminzugriff gibt. Stattdessen werden für die PlugIns neue Keys generiert. Genau das passiert wenn ihr auf das blaue Plus klickt. Im Menü unter Application Keys könnt ihr alle API Keys finden, die eure PlugIns benutzen.

Druck pausieren

Nun funktioniert MQTT ja so, dass wir wieder einem Topic Folgen, bzw. es Abonnieren / Subscriben. Ich lege also ein Topic

octoPrint/commands/pause 

an. Wenn es hier ein Update gibt soll Octoprint den API-Befehl “toggle” ausführen. Also wenn der Switch seinen Status ändert, soll das Gegenstück im Octoprint das gleiche tun. Faktisch ignorieren wir also die Chat-Nachricht und gucken nur ob überhaupt was passiert.

Im PaperUI müssen wir dann auch einen entsprechenden Channel im Octoprint Thing anlegen:

Welches wir mit einem Item verbinden.

Switch OctoPrintPausePrint "MQTT: Octoprint Druck Pausieren"       { channel="mqtt:topic:Octoprint_AnyCubic:pauseprint"}

Nun können wir den Schalter in die SiteMap einbinden und testen.

Switch item=OctoPrintPausePrint label="Druck pausieren" icon="switch"

Wenn Alles wie erwartet funktioniert, sollte ein blaues PopUp auf der Benutzeroberfläche erscheinen, in dem auch steht, welche Nachricht Octoprint über MQTT bekommen hat.

Wenn es nicht klappt, erscheint ein rotes PopUp mit einer Fehlermeldung. Zum Troubleshooting hilft es, z.B. mit MQTTfx den response channel zu Abonieren und sich die Rückgabe des MQTT befehls anzuschauen. In diesem Fall wäre es

octoPrint/commands/pause/response

Druck abbrechen

Ein weiterer Switch wäre beispielsweise den Druck abzubrechen. Auch hier brauchen wir die genaue Nachricht eigentlich nicht und führen bei jeder betätigung des Switch den Cancel Befehl durch. Also sowohl beim aktivieren, also auch beim deaktivieren.

Drucker Verbinden

Natürlich kann man auch den Payload (Also die MQTT-Nachricht) an die RestAPI übertragen. Das ist zum Beispiel bei einem Switch nützlich um Octoprint mit dem Drucker zu verbinden. Dazu muss das Feld Rest Parameters angepasst werden. Wenn wir den Switch aktivieren, möchten wir den “connect” Befehl übertragen, wenn wir ihn deaktivieren, den “disconnect” Befehl.

Die 0 steht für die Variable cmd, die wir im JSON String übertragen. Dazu müssen wir im OpenHAB bzw. im PaperUI natürlich den korrekten Befehl ausgeben. Dazu schreiben wir beim Feld “Custom Open Value” bzw. “Custom Close Value” einen JSON String mit den korrekten Befehlen. Achtet auf die Anführungszeichen und die geschweiften Klammern.

Das funktionierte bei mir leider komischer Weise nicht sofort. Der MQTT Befehl kommt an, wird an die RestAPI übergeben aber im Channel response sagt es: expecting valid command… Ein Reboot des RapberryPi hat abhilfe geschaffen.

Düsentemperatur einstellen

Jetzt wird es interessant. Wenn ich aus OpenHAB die Düsentemperatur einstellen möchte, gibt es verschiedene Varianten. Entweder will ich händisch eine Zahl eintragen oder ich nutze Pfeiltasten oder ich nutze Vordefinierte Temperaturen. Um einen freien Text einzugeben muss man etwas weiter ausholen, das behandle ich in einem eigenständigen Blogpost. Wir müssen uns zunächst also mit Presets (Wie z.B. Preheat Button) und Pfeiltasten begnügen. Dazu nutzen wir in der Sitemap das SetPoint Item.

Im Octoprint MQTTSubscribe PlugIn erzeugen wir also ein neues Topic:

Hier wird die Variable diesesmal ohne Anführungszeichen übertragen, da es diesmal kein String ist, sondern vom Typ Number. Wenn ihr einen Dualextruder habt, könnt ihr die Zweite düse mit tool1 ansprechen.

Im OpenHAB in der PaperUI lege ich einen neuen Channel vom Typ Number an. Diesesmal legen wir einen min und einen max Value fest. Außerdem müssen wir sagen, in welchem Format die MQTT Nachricht gesendet werden soll.

Also Channel (ganz wichtig auch im Command Topic):

octoPrint/commands/nozzleTempTarget

Outgoing Value Format

{"cmd": "%s"}

Item:

Number OctoPrintSetNozzleTemp "MQTT: Octoprint Düsentemperatur Setzen" { channel="mqtt:topic:Octoprint_AnyCubic:setnozzleTemp"}

Den Rest erledigen wir mit der Sitemap

Setpoint item=OctoPrintSetNozzleTemp label="Düsentemperatur Setzen" step=1 minValue=1 maxValue=300 icon="heating"

Und Falls ihr einen Preheat button wollt könnt ihr noch zusatzlich folgenden Code in die SiteMap einfügen:

Switch item=OctoPrintSetNozzleTemp label="Preheat Nozzle" mappings=[0="Aus", 210="PLA", 250="ABS"] icon="heating"

Das ganze sieht dann so aus:

Druckbett Temperatur

Im Prinzip ist das Vorgehen für das Druckbett genau gleich, nur müsst ihr hier nicht noch speziell das Target auswählen, denn es gibt nur eins.

Druckkopf Bewegen

Um den Druckkopf zu bewegen müssen wir Werte für X, Y und Z übertragen. Dazu können wir drei Channels Anlegen, sowie einen vierten für die Homing Funktion.

Davon brauchen wir einen wo wir alle x mit y ersetzen und einen mit z.

Sowie einen Channel für das Homing. Falls ihr nur X und Y nutzen möchtet, löscht ihr einfach das “z” heraus. Auf der PaperUI Seite müssen wir natürlich wieder jeweils den gleichen Channel vom Typ Number anlegen.

Achtet hier auf die Formatierung der Outgoing Value. Ich habe bei x auch ein x verwendet. Man kann hier aber auch cmd oder was anderes nutzen, nur muss das im Octoprint PlugIn halt gleich sein. Außerdem müssen noch die Items angelegt werden:

umber OctoPrintjogPrintHeadX "MQTT: Octoprint Druckkopf X-Achse" { channel="mqtt:topic:Octoprint_AnyCubic:jogPrintHeadX"}
Number OctoPrintjogPrintHeadY "MQTT: Octoprint Druckkopf Y-Achse" { channel="mqtt:topic:Octoprint_AnyCubic:jogPrintHeadY"}
Number OctoPrintjogPrintHeadZ "MQTT: Octoprint Druckkopf Z-Achse" { channel="mqtt:topic:Octoprint_AnyCubic:jogPrintHeadZ"}
Switch OctoPrintHomePrintHead "MQTT: Octoprint Druckkopf Homing" { channel="mqtt:topic:Octoprint_AnyCubic:HomePrintHead"}

In der Sitemap können wir dann jeweils einen Switch mit Mapping anlegen:

Switch item=OctoPrintHomePrintHead label="Home Printer" icon="movecontrol"
                Switch item=OctoPrintjogPrintHeadX label="X Achse mm" mappings=[-10="<", 10=">"] icon="movecontrol"
                Switch item=OctoPrintjogPrintHeadY label="Y Achse mm" mappings=[-10="<", 10=">"] icon="movecontrol"
                Switch item=OctoPrintjogPrintHeadZ label="Z Achse mm" mappings=[-10="<", 10=">"] icon="movecontrol"

Alternative

Schöner wäre natürlich alle drei Koordinaten in einem Channel zu übertragen. Dazu können wir die drei Koordinaten-Channels im PlugIn auf einen Reduzieren und diesen wie folgt konfigurieren:

Jetzt erkennt man auch gut wie die JSON Substitution verläuft. Die Variablen werden einfach nach Position hochgezählt. X entspricht 0 und Z entspricht 2. Die zweite geschweifte Klammer nicht vergessen…

In der PaperUI brauchen wir jetzt allerdings einen Channel vom Typ String, da wir nicht mehr so einfach mit der Outgoing Value Transformation arbeiten können. Unser Output soll jetzt ja wie folgt aussehen:

{"x":"%s0", "y":"%s1", "z":"%s2"}

Die Geschweiften klammern können wir noch leicht mit der Value Transformation erzeugen

{%s}

Danach erzeugen wir unser Item und verbinden es mit den Channel:

String OctoprintJogPrintHead "MQTT: Octoprint Druckkopf Bewegen" { channel="mqtt:topic:Octoprint_AnyCubic:jogPrintHead"}

Wobei die s0 bis s2 nur Platzhalter sind. Hier wollen wir ja unsere Input Werte dynamisch eintragen. Damit wir wieder die Achsen Einzeln steuern können brauchen wir dennoch drei Items, also löschen wir hier einfach die Channel in der Item File und behalten die Items aus der ersten Version. Außerdem können wir bei der Gelegenheit auch die Schrittgröße als Variable einführen. Dazu lege ich ein neues Number Item an.

Number OctoPrintStepSize "MQTT: Octoprint Druckkopf Schrittweite"

Und in der Sitemap können wir die Schritte wieder über ein Mapping festlegen:

Switch item=OctoPrintStepSize     label="Stepsize mm" mappings=[1="1", 10="10", 100="100"] icon="movecontrol"

Die Sitemap für die Achsensteuerung können wir nun jeweils auf -1 oder 1 ändern. Hier müssen wir nur die Richtung unterscheiden können. Wenn ihr diese als String anlegt könnt ihr auch + oder – verwenden. Ich bleibe bei Number und nehme -1 und 1.

Switch item=OctoPrintjogPrintHeadX label="X Achse mm" mappings=[-1="<", 1=">"] icon="movecontrol"
                Switch item=OctoPrintjogPrintHeadY label="Y Achse mm" mappings=[-1="<", 1=">"] icon="movecontrol"
                Switch item=OctoPrintjogPrintHeadZ label="Z Achse mm" mappings=[-1="<", 1=">"] icon="movecontrol"

Jetzt fehlt nur noch die Logik. Diese konstruieren wir in einer Regel. Genauer gesagt in drei (bei mir noch zwei Extra zum einfachen debuggen) Regeln.

rule "Update Stepsize"
    when 
        Item OctoPrintStepSize received update 
    then 
        logInfo("keller.rules", "Anycubic - Updated Stepsize to "+OctoPrintStepSize.state +" mm")
end

rule "Druckkopf Bewegen X"
    when 
        Item OctoPrintjogPrintHeadX received update 
    then 
        if (OctoPrintjogPrintHeadX.state == 1){
            logInfo("keller.rules", "Anycubic - Druckkopf X Achse soll sich in + bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"" +OctoPrintStepSize.state +"\", \"y\":\"0\", \"z\":\"0\"" );
            
        }
        if (OctoPrintjogPrintHeadX.state == -1){
            logInfo("keller.rules", "Anycubic - Druckkopf X Achse soll sich in - bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"-" +OctoPrintStepSize.state +"\", \"y\":\"0\", \"z\":\"0\"" );
            
        }
        
end

rule "Druckkopf Bewegen Y"
    when 
        Item OctoPrintjogPrintHeadY received update     
    then 
        if (OctoPrintjogPrintHeadY.state == 1){
            logInfo("keller.rules", "Anycubic - Druckkopf Y Achse soll sich in + bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"0\", \"y\":\"" +OctoPrintStepSize.state +"\", \"z\":\"0\"" );
            
        }
        if (OctoPrintjogPrintHeadY.state == -1){
            logInfo("keller.rules", "Anycubic - Druckkopf Y Achse soll sich in - bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"0\", \"y\":\"-" +OctoPrintStepSize.state +"\", \"z\":\"0\"" );
            
        }
end

rule "Druckkopf Bewegen Z"
    when 
        Item OctoPrintjogPrintHeadZ received update
    then 
        if (OctoPrintjogPrintHeadZ.state == 1){
            logInfo("keller.rules", "Anycubic - Druckkopf Z Achse soll sich in + bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"0\", \"y\":\"0\", \"z\":\"" +OctoPrintStepSize.state +"\"" );
            
        }
        if (OctoPrintjogPrintHeadZ.state == -1){
            logInfo("keller.rules", "Anycubic - Druckkopf Z Achse soll sich in - bewegen, Schrittgröße: " +OctoPrintStepSize.state +" mm");
            OctoPrintJogPrintHead.sendCommand("\"x\":\"0\", \"y\":\"0\", \"z\":\"-" +OctoPrintStepSize.state +"\"" );
            
        }
end

rule "Debug Jog PrintHead"
    when 
        Item OctoPrintJogPrintHead received update 
    then 
        logInfo("keller.rules", "Neuer String wird gesendet: " +OctoPrintJogPrintHead.state);
end

Das sieht etwas verwirrend aus, aber wir brauchen die Anführungszeichen in unserem String.

Am Ende sieht es im Log so aus:

Jetzt gucken wir auf der Octoprint seite noch einmal ob alles funktioniert. Das erkennen wir an der blauen Box.

WebCamFeed Einbinden

Den Webcam Stream von Octoprint in eure OpenHAB Sitemap einzubinden ist eigentlich nicht schwer. Dazu brauchen wir nur die IP von Octoprint, bzw. den Link zum Webcam Stream. In der Sitemap können wir dann einfach folgenden Code einbetten:

Webview url="http://192.168.0.42/webcam/?action=stream" label="OctoCam"

Wobei ihr eure IP halt eintragen müsstet. Das Label ist optional.

Mit diesen Elementen sieht meine Octoprint Sitemap so aus:

Sonstiges

Weitere Befehle findet man in der Dokumentation zur Octoprint Rest API. Ihr könnt ja jetzt versuchen noch einen Extrude Button zu bastlen 🙂 Ggf auch mit einer Variablen für die Extrusionslänge.

1 Kommentar

Schreibe einen Kommentar