Raspberry Pi - Make serial USB ports persistent via symlinks

Usually, USB sticks attached to Raspberry Pi will get assigned ports in an increasing manner such as /dev/ttyACM0, /dev/ttyACM1, etc. This may become a problem whenever you reattach your USB devices to a different port, you add new devices, or you simply reboot your RPi. The reason is that one specified device may be recognized earlier than another while booting the RPi. You could make these ports permanent via so called symlinks and give them more telling names in contrast to ttyACM0.

  1. SSH into your RPi
  2. Take a look at the current recognized USB devices (and make a screenshot): lsusb
  3. Now, unplug all your USD devices and issue the lsusb command again. Now you know how your disconnected devices have been named.
  4. Plugin the the USB device one after another and find their IDs. Issue the lsusb command after each reattachment of a single USB device.
    For example, an Aeotec Z-Stick 5G may look like this:
    Bus 001 Device 005: ID 0658:0200 Sigma Designs, Inc.
    A 433Mhz busware CUL may look like this:
    Bus 001 Device 004: ID 03eb:204b Atmel Corp. LUFA USB to Serial Adapter Project
  5. Create a UDEV Rule:
    sudo nano /etc/udev/rules.d/99-usb-serial.rules
    SUBSYSTEM=="tty", ATTRS{idVendor}=="0658", ATTRS{idProduct}=="0200", SYMLINK+="ttyUSB-ZStick", GROUP="dialout", MODE="0666"
    SUBSYSTEM=="tty", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", SYMLINK+="ttyUSB-CUL", GROUP="dialout", MODE="0666"
  6. Add openhab user to the right groups:
    sudo adduser openhab dialout
    sudo adduser openhab tty
  7. Reboot and test if symlink was created:
    ls /dev/tty*
  8. Add symlink to PATH in Java:
    sudo nano /etc/default/openhab2
    Search the following line and modify accordingly:
    EXTRA_JAVA_OPTS="-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB-ZStick:/dev/ttyUSB-CUL"
  9. Change the ports of your devices in your existing configuration files.
    For example, for a Z-Wave stick change the properties in HABMIN accordingly.

    For a 433Mhz CUL that is bound to openHAB via Homegear, change the port accordingly in /etc/homegear/families/intertechno.conf
  10. Reboot your Raspberry Pi
18 Likes

Ok I will try to use your recommendation but I do have a few questions:

  1. So next start the defined devices will get the defined tty, what about new other devices
    will these get again ttyACM0 and so on?
  2. When running Openhabian2.2. why do I need to add openhab to dialout and tty as
    shown in your 6. ?

Thanks a lot

I followed the steps as outlined, but the symlink shows group as ā€˜rootā€™ and permissions as 777, despite having ā€˜dialoutā€™ and 666 in the rule file. Any suggestions why those arenā€™t taking? (This is on a Pi 2 B, fully updated jessie)

Great write up!
Could you add some more information about what the different settings are in:

also how to add several USB devices, I assume you just keep appending to 99-usb-serial.rules ?
And in OH you useSYMLINK+="name of your USB device can be any character??, but needs to be unique for every device" name in config files.

Iā€™ve found the answer to my own question, and some others of the above:

That is intended operation, symlinks are root/777 so that anyone can access them, UDEV sets the group and permission of the real /dev/ttyACM0 device, which is what matters (now I need to figure out why I canā€™t access it using the symlinkā€¦)

The usual ttyACM[0-9] will always be created for every device, the UDEV rule just creates a symlink that provides an alternative way of addressing it.

yes

Iā€™m not sure exactly what youā€™re asking, but in the UDEV rule, SYMLINK+= is the command saying that a symlink with the following string as its name should point to the device matched by the criteria that came before. So /dev/ plus whatever you put between the quotes after the += is what youā€™ll put in OH to refer to the serial port. i.e., SYMLINK+=ā€œttyZStickā€ will create a symlink at /dev/ttyZStick, and you put /dev/ttyZStick in your OH config files wherever you would usually put /dev/ttyACM0

1 Like

Could you please incorporate your answer to your original post, then we can link to it from other post. @ThomDietrich maybe we should incorporate symlinks to openhabian, we scan usb ports and ask user to give them meaningful names, so that they that way can be used in bindings and the bindings will work if order of usb ports changes.

1 Like

Was that directed to me? Iā€™m not the OP, so I canā€™t put the answer in the original post.

In case anyone else is having the same problem as me (UDEV rule seems to work, but OpenHAB canā€™t access the symlink), here are some other points:

  1. In the UDEV rules file, the rule needs to be all in one line, I somehow reproduced the apparent line break from the OP - I donā€™t remember if I didnā€™t just copy/paste, or what, but I had a line break in my rule, and that broke things
  2. When editing the EXTRA_JAVA_OPTS line, make sure you also comment-out the empty version of that line that comes after the examples (or use that line, donā€™t uncomment and change one of the existing example lines)
  3. I ended up removing the group and mode settings from my rule, the udevadm test procedure was throwing errors on them, and it seems to work without them

Nice !

This reassignment is why I donā€™t use usb devices in windows! I have been burned so many times cause they get reassigned when you least expect it.

I have been using links like this in Unix for many years, not for usb but it works the same. Now that I know I can do it in Linux I am excited.

I just added a new USB device to my Rpi running openhabian (apt.get hasslefree).
And ofcouse I ran into this problem :frowning:
I added the new USB device, and then one of my other devices no longer works in openhab (probably due to changed USB ID, I guess).

I tried to follow your guide, but IĀ“m not quite sure what IĀ“m doing wrongā€¦ Each time I disconnect a device and run lsusb, I get the same result, showing all devices. Even devices which has been disconnected.
IĀ“ve tried reboote as well as restart, and its the sameā€¦

I use a 4-port USB hub, and my root and system drive is a external SSD connected to USB as well. I donĀ“t know if this is the causeā€¦

This is the list of all connected devices:

[21:23:05] openhabian@openHABianPi:~$ lsusb
Bus 001 Device 005: ID 10c4:8856 Cygnal Integrated Products, Inc.
Bus 001 Device 007: ID 152d:9561 JMicron Technology Corp. / JMicron USA Technology Corp.
Bus 001 Device 006: ID 0658:0200 Sigma Designs, Inc.
Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
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

This is the list of devices (two devices is removed) after restart of the Rpi (not reboot, but restart).

[21:31:44] openhabian@openHABianPi:~$ lsusb
Bus 001 Device 005: ID 10c4:8856 Cygnal Integrated Products, Inc.
Bus 001 Device 007: ID 152d:9561 JMicron Technology Corp. / JMicron USA Technology Corp.
Bus 001 Device 006: ID 0658:0200 Sigma Designs, Inc.
Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
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

My devices are:
4-port USB hub (easy to spot in the list).
SSD drive
InCircet RS486 stick
UZB1 Z-wave stick
MeshConnect Zigbee stick

Additionally I have a Elelabs Zigbee coordinator added to the internal PIO of the Rpi. But I donĀ“t think is has any influenceā€¦

When I added the MeshConnect Zigbee stick the RS486 Incircet stick no longer works.

Got some big problems getting this to workā€¦
For my RS846 dongle, I get this error:

2018-11-05 20:35:46.276 [WARN ] [.binding.modbus.internal.ModbusSlave] - ModbusSlave (nilan_holding_device): Error getting a new connection for endpoint ModbusSerialSlaveEndpoint@1185061[portName=/dev/ttyUSB-RS486]. Error was: Pool not open
2018-11-05 20:35:46.278 [WARN ] [.binding.modbus.internal.ModbusSlave] - ModbusSlave (nilan_holding_device) not connected -- aborting read request net.wimpi.modbus.msg.ReadMultipleRegistersRequest@2a3640. Endpoint ModbusSerialSlaveEndpoint@1185061[portName=/dev/ttyUSB-RS486]
2018-11-05 20:35:46.286 [WARN ] [.binding.modbus.internal.ModbusSlave] - ModbusSlave (nilan_input_device): Error getting a new connection for endpoint ModbusSerialSlaveEndpoint@c1fc3c[portName=/dev/ttyUSB-RS486]. Error was: Pool not open
2018-11-05 20:35:46.289 [WARN ] [.binding.modbus.internal.ModbusSlave] - ModbusSlave (nilan_input_device) not connected -- aborting read request net.wimpi.modbus.msg.ReadInputRegistersRequest@e860ed. Endpoint ModbusSerialSlaveEndpoint@c1fc3c[portName=/dev/ttyUSB-RS486]

This is the /etc/udev/rules.d/99-usb-serial.rules

SUBSYSTEM=="tty", ATTRS{idVendor}=="0658", ATTRS{idProduct}=="0200", SYMLINK+="ttyUSB-ZStick", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyUSB-RS486", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8856", SYMLINK+="ttyUSB-MeshConn", GROUP="dialout", MODE="0666"

This is the outcome ls /dev/tty*

[20:35:50] openhabian@openHABianPi:~$ ls /dev/tty*
/dev/tty    /dev/tty18  /dev/tty28  /dev/tty38  /dev/tty48  /dev/tty58    /dev/ttyAMA0
/dev/tty0   /dev/tty19  /dev/tty29  /dev/tty39  /dev/tty49  /dev/tty59    /dev/ttyprintk
/dev/tty1   /dev/tty2   /dev/tty3   /dev/tty4   /dev/tty5   /dev/tty6     /dev/ttyS0
/dev/tty10  /dev/tty20  /dev/tty30  /dev/tty40  /dev/tty50  /dev/tty60    /dev/ttyUSB0
/dev/tty11  /dev/tty21  /dev/tty31  /dev/tty41  /dev/tty51  /dev/tty61    /dev/ttyUSB1
/dev/tty12  /dev/tty22  /dev/tty32  /dev/tty42  /dev/tty52  /dev/tty62    /dev/ttyUSB-MeshConn
/dev/tty13  /dev/tty23  /dev/tty33  /dev/tty43  /dev/tty53  /dev/tty63    /dev/ttyUSB-RS486
/dev/tty14  /dev/tty24  /dev/tty34  /dev/tty44  /dev/tty54  /dev/tty7     /dev/ttyUSB-ZStick
/dev/tty15  /dev/tty25  /dev/tty35  /dev/tty45  /dev/tty55  /dev/tty8
/dev/tty16  /dev/tty26  /dev/tty36  /dev/tty46  /dev/tty56  /dev/tty9
/dev/tty17  /dev/tty27  /dev/tty37  /dev/tty47  /dev/tty57  /dev/ttyACM0
[20:44:09] openhabian@openHABianPi:~$ 

And this is the /etc/default/ophab2

EXTRA_JAVA_OPTS="-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB0:/dev/ttyS0:/dev/ttyS2:/dev/ttyACM0:/dev/ttyAMA0:/dev/ttyUSB-MeshConn:/dev/ttyUSB-RS486:/dev/ttyUSB-Zstick"
sudo adduser openhab dialout
sudo adduser openhab tty

Both responded with user already in the group.

EDITā€¦
Forgotā€¦ This is a lsusb:

[23:03:56] openhabian@openHABianPi:~$ lsusb
Bus 001 Device 005: ID 10c4:8856 Cygnal Integrated Products, Inc. 
Bus 001 Device 008: ID 152d:9561 JMicron Technology Corp. / JMicron USA Technology Corp.
Bus 001 Device 007: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
Bus 001 Device 006: ID 0658:0200 Sigma Designs, Inc.
Bus 001 Device 004: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
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
[23:03:59] openhabian@openHABianPi:~$

ID 5 is a MeshConnect Zigbee controller.
ID 6 is my z-wave dongle
ID 7 is the RS486 dongle

Can anyone help?

I guess I got it sorted by clearing the cache and tmp, and a fresh set of eyes on my modbus.cfg file :wink:

Hi, how did you clear the cache and temp?
I think i have similar problem, after setting all up correctly, after restart i get ā€œbridge communication errorā€, it is enough to perform soft restart of the device from Openahab (Paper) UI and it starts working fine.

It may not be immediately obvious that you can still use this technique when running OH in a docker container, with the change that you do not access the USB device directly via the symlink created by your UDEV rule.

The problem when running in a docker container is that symlinks on the host system arenā€™t usable as symlinks within containers. I scratched my head over this for some time before I arrived at my solution, which is to use the bash command readlink -f <symlink> to ā€œlook upā€ the target of the symlink. My container start script creates and initializes the environment variable ZIGBEE_TTY with this technique:

#!/bin/bash

if [ $# -lt 1 ]; then
  OH_TAG="2.5.0-snapshot-amd64-debian"
else
  OH_TAG="$1"
fi

ZIGBEE_TTY=`readlink -f /dev/ttyUSBzigbee`

docker run \
    --name openhab \
    --tty=true \
    --net=host \
    --device=${ZIGBEE_TTY} \
    -v /etc/localtime:/etc/localtime:ro \
    -v /home/openhab/oh/timezone:/etc/timezone:ro \
    -v /home/openhab/oh/openhab_addons:/openhab/addons \
    -v /home/openhab/oh/openhab_conf:/openhab/conf \
    -v /home/openhab/oh/openhab_userdata:/openhab/userdata \
    --env="EXTRA_JAVA_OPTS=-Duser.timezone=America/Denver -Dgnu.io.rxtx.SerialPorts=${ZIGBEE_TTY} -Xbootclasspath/a:/openhab/conf/automation/jython/jython-standalone-2.7.0.jar -Dpython.home=/openhab/conf/automation/jython -Dpython.path=/openhab/conf/automation/lib/python" \
    --env="OPENHAB_HTTP_PORT=8080" \
    --env="OPENHAB_HTTPS_PORT=8443" \
    --env="USER_ID=1000" \
    --env="GROUP_ID=1000" \
    -d \
    --restart=always \
    openhab/openhab:${OH_TAG}

Start an SSH session (or if you have mapped the folders of you openhabian).
Go to /var/lib/openhab2/ clear the files inside the Cache and Tmp folders.

1 Like

Thanks. It helped it seems as after restarting OS finally OH2 can establish communication to my device without any problems.

On the symlink, I add a
sudo chown -h root:tty /dev/ttyUSB-ZStick
This way I have the same permission of the original. It works.

I donā€™t know if itā€™s necessary. What do you think ?

1 Like

After doing this my Zigbee binding stopped working. The Zigbee stick is ā€œOnlineā€ but all Zigbee devices are ā€œUnknownā€.

i did everything, but the ttyUSB-ZStick dont show up in habmin or in paperui as port to choose. Someone maybe could help?

I have the same problem. I have given up trying to fix it.