Using a remote USB serial device locally (Linux)

Sometimes you want to use a USB serial device at a different location of where it is “plugged in”. With two tools (ser2net and socat) this is quite easy to do.

Within my home I make use of Home automation. I combine a few technologies to make it all work, among them Z-Wave and Zigbee. I started with Z-Wave and for that I used a Vera, a (now) quite old device which basically is an appliance running Linux.

Through time I’ve started to use Zigbee devices as well and for that I’m using zigbee2mqtt, a neat piece of software which pushes zigbee messages towards an mqtt server.

Zigbee2mqtt uses an USB adapter to connect to the Zigbee network. This USB adapter is basically a serial device and becomes the coordinator of the Zigbee mesh network.

As the Raspberry Pi on which I am running zigbee2mqtt is not in the best location for the adapter and the Vera already was, I decided to try to make it work via the Vera.

Browsing through the Linux installation at the Vera I discovered it contained the ser2net command. This is a little daemon which is capable of sending serial port traffic over a network connection. So that would be my starting point.

After initial testing I discovered the Vera had the nasty habit of re-initializing most of the system and killing ser2net whenever something in the system changed. (Vera uses ser2net itself as well)
So I wrote a little script with an endless loop which would restart the ser2net command if it was not running anymore. The script itself is started as a “respawn” via the inittab, so it’s always running.

#!/bin/sh
#
# Starts and restarts ser2net

while [ true ] ; do 
  ps ax|grep ser2net|grep -q ACM
  if [ $? == 1 ]; then 
  	/usr/sbin/ser2net -C '4096:raw:0:/dev/ttyACM0:38400 NONE 1STOPBIT 8DATABITS'
  else
  	echo "Sleeping..."
  fi
  sleep 29
done

Ser2net is being instructed to open device /dev/ttyACM0 and listen on port 4096 for any connections towards the device. The communication is “raw”, which means everything that comes in is send as-is to the device.

The above script would work on any Linux device, not just the Vera.

So, the “listening” side of things has been set-up. Next up, the host on which zigbee2mqtt runs.

Again it’s very easy. By using socat to connect to the Vera device on which ser2net is listening we can create a “local” serial port. And by putting it in a Systemd configuration everything will be set-up together with the running of zigbee2mqtt.

[Unit]
Description=SOCat to control Zigbee sniffer

[Service]
ExecStart=/usr/bin/socat pty,link=/dev/ttyRemote,raw,user=root,group=dialout,mode=777 tcp:192.168.1.1:4096
ExecStartPost=service zigbee2mqtt restart
StandardOutput=null
Restart=always
RestartSec=30s

[Install]
WantedBy=multi-user.target

The configuration for socat is quite easy. It’s being told to create a pseudo terminal (pty) and link it to /dev/ttyRemote. Communication is raw and it sets the ownership to something you would expect with a tty. Finally the host to connect to is 192.168.1.1 port 4096

Systemd will make sure socat stays running and restarts zigbee2mqtt whenever it is started or restarted.

Links