Skip to content

Instantly share code, notes, and snippets.

@tdrkDev
Last active May 28, 2025 21:37
Show Gist options
  • Select an option

  • Save tdrkDev/1646dbbe863cbf0050a0160a7da81ad4 to your computer and use it in GitHub Desktop.

Select an option

Save tdrkDev/1646dbbe863cbf0050a0160a7da81ad4 to your computer and use it in GitHub Desktop.

Revisions

  1. tdrkDev revised this gist May 28, 2025. 8 changed files with 154 additions and 0 deletions.
    9 changes: 9 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    A solution to run Klipper with the printer connected to a Wi-Fi router, while the host may be in a different place at all.
    Code files contain comments for you to understand the installation process.

    On Keenetic router you must have Entware installed and have these packages installed:
    ```
    opkg install usbip2 usbip2-client usbip2-server
    ```

    On host you must have `usbip` tools installed.
    24 changes: 24 additions & 0 deletions S75usbipdevice
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    #!/bin/sh
    # Service for hosting USB/IP server on Keenetic router with Entware.
    # We've used Keenetic Extra.
    #
    # Put this into /opt/etc/init.d/S75usbipdevice on router
    # Dont't forget to do chmod +x
    # Make sure there's no S74usbipd init.d script.

    ###############
    # Settings
    VENDORID="1d50"
    DEVICEID="614e"
    ###############

    PATH="/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin:/bin:/sbin:/usr/bin:/usr/sbin"

    usbipd -D &

    insmod /lib/modules/4.9-ndm-5/usbip-core.ko
    insmod /lib/modules/4.9-ndm-5/usbip-host.ko
    echo "/opt/bin/hotplug_trigger" > /proc/sys/kernel/hotplug

    BUSID=`usbip list -p -l | grep "$VENDORID:$DEVICEID" | cut -d '#' -f 1 | cut -d '=' -f 2 | xargs`
    [ ! -z "$BUSID" ] && usbip bind -b "$BUSID"
    33 changes: 33 additions & 0 deletions hotplug_trigger
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,33 @@
    #!/bin/sh
    # Linux hotplug triggered script.
    #
    # Put this into /opt/bin/hotplug_trigger on router
    # Dont't forget to do chmod +x

    ###############
    # Settings
    VENDORID="1d50"
    DEVICEID="614e"
    ###############

    PATH="/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin:/bin:/sbin:/usr/bin:/usr/sbin"
    USBIP="/opt/sbin/usbip"

    case "$PRODUCT" in
    "$VENDORID/$DEVICEID"*)
    if [ "$ACTION" = "add" ]; then
    sleep 1
    BUSID=`usbip list -p -l | grep "$VENDORID:$DEVICEID" | cut -d '#' -f 1 | cut -d '=' -f 2 | xargs`
    echo "BUSID=$BUSID" >>/tmp/hotplug_bind.log
    "$USBIP" bind -b "$BUSID" >>/tmp/hotplug_bind.log 2>&1
    fi
    ;;
    esac

    TS=`date "+%s"`
    {
    echo "INTERFACE=$1";
    echo "ACTION=$ACTION";
    echo "PRODUCT=$PRODUCT";
    echo "DATE=$TS";
    } >/tmp/last_hotplug.log
    21 changes: 21 additions & 0 deletions klipper.service
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    # Modified Klipper systemd service with USB/IP connection before Klipper start.
    # Klipper user - klipper

    [Unit]
    Description=Klipper 3D Printer Firmware SV1
    Documentation=https://www.klipper3d.org/
    After=network-online.target usbip-setup.service
    Wants=udev.target

    [Install]
    WantedBy=multi-user.target

    [Service]
    Type=simple
    User=klipper
    RemainAfterExit=yes
    WorkingDirectory=/var/lib/klipper/klipper
    EnvironmentFile=/var/lib/klipper/printer_data/systemd/klipper.env
    ExecStart=/var/lib/klipper/klippy-env/bin/python $KLIPPER_ARGS
    Restart=always
    RestartSec=10
    38 changes: 38 additions & 0 deletions usbip-setup
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    #!/bin/bash
    # Script to connect to a USB device via USB/IP.
    # Don't forget to do chmod +x and fill in "HOST" variable
    #
    # Put this into /usr/bin/usbip-setup on host

    ###############
    # Settings
    HOST="X.X.X.X"
    VENDORID="1d50"
    DEVICEID="614e"
    ###############

    USBID="$VENDORID:$DEVICEID"

    while ! `nc -z "$HOST" 3240`; do
    echo "Waiting for USB/IP..."
    sleep 1
    done

    bus_id() {
    usbip list -r "$HOST" -p | grep "$USBID" | cut -d ':' -f 1 | xargs
    }

    is_connected() {
    lsusb -d "$USBID" >/dev/null && return 0
    BUSID=`bus_id`
    [ -z "$BUSID" ] && return 1
    usbip attach -r "$HOST" -b "$BUSID" && return 0
    return 1
    }

    while ! `is_connected`; do
    echo "Retrying..."
    sleep 1
    done

    echo "USB device has been connected"
    13 changes: 13 additions & 0 deletions usbip-setup.service
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    # USB/IP service for Klipper

    [Unit]
    Description=USB/IP setup
    After=network-online.target
    Wants=udev.target

    [Install]
    WantedBy=multi-user.target

    [Service]
    Type=oneshot
    ExecStart=/usr/bin/usbip-setup
    3 changes: 3 additions & 0 deletions usbip.py
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,9 @@
    #
    # Uses ~/usbip.sh script where you may specify the script to connect
    # the MCU.
    #
    # 1) Put this script into ~/klipper/klippy/extras/usbip.py on host
    # 2) Define [usbip] section in your printer.cfg
    #

    import os
    13 changes: 13 additions & 0 deletions usbip.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    #!/bin/bash

    # Use common with systemd service script. Requires you
    # to allow klipper user to run "usbip-setup" without sudo password.
    #
    # Add this to sudoers:
    # klipper ALL = NOPASSWD: /usr/bin/usbip-setup,/usr/bin/usbip
    #
    # Where "klipper" is your klipper user.
    #
    # Put this into ~/usbip.sh on host
    # Don't forget to do chmod +x
    sudo usbip-setup
  2. tdrkDev revised this gist May 28, 2025. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion usbip.py
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,6 @@ def __init__(self, config):
    self.serial = config.getsection("mcu").get("serial")

    self.reconnecting = False
    self.threads = []

    if not self._exists():
    self._handle_connect()
  3. tdrkDev created this gist May 28, 2025.
    55 changes: 55 additions & 0 deletions usbip.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,55 @@
    #
    # Klipper USB/IP integration module.
    # Author: Roman Rikhter <tdrkDev>
    #
    # Uses ~/usbip.sh script where you may specify the script to connect
    # the MCU.
    #

    import os
    import time

    class UsbIpSupport:
    def __init__(self, config):
    self.printer = config.get_printer()
    self.gcode = self.printer.lookup_object("gcode")

    self.printer.register_event_handler(
    "klippy:disconnect", self._handle_disconnect)

    self.serial = config.getsection("mcu").get("serial")

    self.reconnecting = False
    self.threads = []

    if not self._exists():
    self._handle_connect()

    def _exists(self):
    return os.access(self.serial, os.R_OK | os.W_OK)

    def _connect(self):
    if self._exists(): return

    self.reconnecting = True
    self._log("USB/IP: Connecting...")
    os.system(os.environ.get("HOME") + "/usbip.sh")
    self._log("USB/IP: Connected")
    self.reconnecting = False

    def _handle_disconnect(self):
    if self.reconnecting: return
    self._log("USB/IP: Reconnection due to a disconnect event")
    time.sleep(0.25)
    self._connect()

    def _handle_connect(self):
    if self.reconnecting: return
    self._log("USB/IP: Initial connection")
    self._connect()

    def _log(self, msg):
    self.gcode.respond_info(msg)

    def load_config(config):
    return UsbIpSupport(config)