Sane management of multiple RTL-SDR dongles in Linux

I was looking to utilize a couple RTL dongles to monitor two ISM band frequencies commonly used in LoRa without buying an SDR with wide enough bandwidth to cover both ranges. I pretty quickly ran into issues with how SpyServer and rtl_tcp enumerate devices, which appears to be based mostly upon the order in which each device had been plugged in. With some work, I think I’ve come upon a flexible and secure solution to handle an arbitrary number of dongles on one system while maintaining deterministic control of each device. This means I can label an individual dongle, connect it to the desired antenna, and then connect to that dongle on the assigned TCP port every time, without regard to the order in which things have been plugged in.

All of these steps have been performed on a fresh installation of Raspbian Stretch.

HOWTO: Sane management of multiple RTL-SDR dongles in Linux

Prepare the system

# get the latest packages and apply any updates
sudo apt-get update && sudo apt-get upgrade
# install required packages
sudo apt install rtl-sdr librtlsdr-dev -y
# get the latest udev rules for common dongles
sudo wget -O /etc/udev/rules.d/20-rtlsdr.rules https://raw.githubusercontent.com/osmocom/rtl-sdr/master/rtl-sdr.rules
# reboot to apply udev rules
sudo reboot

Create unprivileged service user

Here we create a system user “rtltcp” with no login privileges or shell to help protect against shell escapes if opening this service to the internet

sudo adduser --system rtltcp
sudo addgroup rtltcp
sudo usermod -aG dialout,rtltcp -a rtltcp

Define device serial numbers to match TCP port

We’ll define a serial number for each device to match the TCP port. I’ve applied a physical label to each dongle for easy identification, so I can plug in the desired antenna to the correct device and then connect to the TCP port written on the label.

# First, run rtl_eeprom to display installed devices
rtl_eeprom
# Then assign the desired SN to each device.  We want the SN to line up w/ the TCP port, so pick a valid value.
# Here I'm setting up two dongles to present on ports 1230 and 1231.  Change these lines to match your requirements!
rtl_eeprom -d0 -s 1230
rtl_eeprom -d1 -s 1231

Create script to identify dongles by SN

rtl_tcp identifies devices by device number, which is non-deterministic. The script below will accept a serial number as an argument, and return the device ID assigned to the provided serial number.

sudo mkdir /opt/rtltcp
sudo tee /opt/rtltcp/rtlsn2dev.sh << 'EOF'
#!/bin/bash
# usage: ./rtlsn2dev.sh [serial number]
# returns: device ID
if [ $1 ]
then
  rtl_num_devices=$(rtl_eeprom 2>&1 >/dev/null | grep "Found [0-9][0-9]*" | sed -E 's/.*([0-9]+).*/\1/')
  if [ $rtl_num_devices ]
  then
    for i in $(seq 1 $rtl_num_devices);
    do
      rtl_device=$((i-1))
      rtl_serial=$(rtl_eeprom -d$rtl_device 2>&1 >/dev/null | grep "Serial number\:" | sed -E 's/Serial number:[[:blank:]]+//')
      if [ "$1" == "$rtl_serial" ]
      then
        echo $rtl_device
      fi
    done
  fi
fi
EOF
sudo chmod +x /opt/rtltcp/rtlsn2dev.sh
sudo chown -R rtltcp:rtltcp /opt/rtltcp

Create systemd service unit file

Now we’ll make a systemd service template which we’ll then use for each instance we plan to run.

sudo tee /etc/systemd/system/rtltcp@.service << 'EOF'
[Unit]
Description=rtl_tcp radio streaming service
After=network.target

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
User=%p
Restart=always
WorkingDirectory=/opt/rtltcp
ExecStart=/bin/sh -c "/usr/bin/rtl_tcp -d $(/opt/rtltcp/rtlsn2dev.sh %i) -a 0.0.0.0 -p %i"
EOF

# Now reload systemd, create a couple instances of this service, and start them
sudo systemctl --system daemon-reload
sudo systemctl enable rtltcp@1230.service
sudo systemctl enable rtltcp@1231.service
sudo systemctl start rtltcp@1230.service
sudo systemctl start rtltcp@1231.service

And that’s it! Now you have a couple services which will run at startup and can be controlled/monitored via conventional methods for linux system administration, and which will “stick” to the assigned dongle.

Comments