Project goal
Setup an RPi as a USB-over-IP server. Run Hass on a VM (or wherever) and connect to a Z-Wave/ZigBee/etc dongle physically plugged into an RPi somewhere on your network.
Background
I started working with Hass using an RPi3 and an HUSBZ-1 combo Z-Wave/ZigBee stick which worked well but I have concerns about long-term reliability of SD cards on the Pi and while performance was OK, I have a virtualization farm here that provides me much better performance and availability than an RPi could. The trouble there is that I still needed access to the USB stick, and physically plugging it into one of my virtualization hosts would prevent me from moving the Hass VM around in response to performance or availability situations in my farm.
The goal here is to create a USB over IP service on a Raspberry Pi, plug the USB radio(s) into the Pi, then place that device on your network somewhere close to the controlled devices. Then Hass can run wherever you like while controlling your devices over an IP link to the radio(s).
Setting up the USB/IP server
Requirements
- Raspberry Pi running Rasbian (or something like it)
- USB dongle to share
Process
SSH to raspbian and execute the following commands:
sudo -s
lsusb
lsusb
should show a list of attached USB devices, hereās what mine looks like:
Bus 001 Device 006: ID 10c4:8a2a Cygnal Integrated Products, Inc.
Bus 001 Device 005: ID 0557:2306 ATEN International Co., Ltd
Bus 001 Device 004: ID 0781:5583 SanDisk Corp.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Weāre looking for the device identifier for the USB radio, which in my case is that Cygnal Integrated Products, Inc.
device with an ID of 10c4:8a2a
. Weāll then setup a systemd service definition that is going to search for that device string and attach it to the USBIPd service. If youāre using a different USB device, change the device ID in the lines below for ExecStartPost
and ExecStop
# Install usbip and setup the kernel module to load at startup
apt-get install usbip
modprobe usbip_host
echo 'usbip_host' >> /etc/modules
# Create a systemd service
vi /lib/systemd/system/usbipd.service
Copy and paste the following service definition:
[Unit]
Description=usbip host daemon
After=network.target
[Service]
Type=forking
ExecStart=/usr/sbin/usbipd -D
ExecStartPost=/bin/sh -c "/usr/sbin/usbip bind --$(/usr/sbin/usbip list -p -l | grep '#usbid=10c4:8a2a#' | cut '-d#' -f1)"
ExecStop=/bin/sh -c "/usr/sbin/usbip unbind --$(/usr/sbin/usbip list -p -l | grep '#usbid=10c4:8a2a#' | cut '-d#' -f1); killall usbipd"
[Install]
WantedBy=multi-user.target
# reload systemd, enable, then start the service
sudo systemctl --system daemon-reload
sudo systemctl enable usbipd.service
sudo systemctl start usbipd.service
Setting up the USB/IP client
Requirements
- Linux server/desktop (Tested on Ubuntu server 17.04. There are some Windows builds but I couldnāt get any of them to work reliably under Win10/Server 2016)
- IP address of your RPi running as a server. Here Iām using
192.168.0.10
.
Process
SSH to the Linux server and execute the following commands:
sudo -s
apt-get install linux-tools-generic -y
modprobe vhci-hcd
echo 'vhci-hcd' >> /etc/modules
Much like we did on the server, weāre going to need to modify the ExecStart
and ExecStop
lines below to search for the correct USB device ID thatās being presented by your USB/IP server. Likewise, change the IP 192.168.0.10
to match your RPi USB server.
vi /lib/systemd/system/usbip.service
Copy and paste the following service definition:
[Unit]
Description=usbip client
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c "/usr/lib/linux-tools/$(uname -r)/usbip attach -r 192.168.0.10 -b $(/usr/lib/linux-tools/$(uname -r)/usbip list -r 192.168.0.10 | grep '10c4:8a2a' | cut -d: -f1)"
ExecStop=/bin/sh -c "/usr/lib/linux-tools/$(uname -r)/usbip detach --port=$(/usr/lib/linux-tools/$(uname -r)/usbip port | grep '<Port in Use>' | sed -E 's/^Port ([0-9][0-9]).*/\\1/')"
[Install]
WantedBy=multi-user.target
# reload systemd, enable, then start the service
sudo systemctl --system daemon-reload
sudo systemctl enable usbip.service
sudo systemctl start usbip.service
You should now be able to access the USB device over the network as if the device was plugged in locally, and you have an auto-starting systemd service to control things.