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.
Klippy extra for USB/IP reconnection (reattachment) support. Gist contains both parts for USB/IP client and server.

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.

#!/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
# 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
#!/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"
#!/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"
# 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
#
# Klipper USB/IP integration module.
# Author: Roman Rikhter <tdrkDev>
#
# 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
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
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)
#!/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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment