APRSDroid and dual port Direwolf

In my mobile setup, I want to run a Direwolf instance with two ports (VHF and HF) on an Orange Pi Zero. APRSDroid is used to monitor both ports and send messages through them. Since APRSDroid only supports one TNC port, you I had to find a way to make it talk to two TNC channels.


I assume that you have the following:

  • A working dual port Direwolf configuration
  • Ideally Direwolf is being run as systemd service.
  • You are running a relatively recent Debian base distro, in my case it is Armbian Bullseye. If you are using any other distro, it has to be systemd based.
  • Working Bluetooth on your Pi device and your phone is already paired with the Pi.
  • You have installed socat version or greater. This will not work with any older version!
  • You know how to use nano editor


The whole thing is based around some bash scripts, systemd services and socat. There is a shellscript which I can “TNCMultiplexer” which aggregates packets from both ports and presents them to APRSDroid. Packets coming from APRSDroid are replicated toward both ports. The ports do not see each other. Below is a rough block diagram of how it works.


As previously mentioned, socat v1.8.0.0 is required. To check your version just run following command.

 socat -VCode language: Bash (bash)

if output looks similar to the block below you can skip to next chapter.

socat by Gerhard Rieger and contributors - see www.dest-unreach.org
socat version on Jun 10 2024 10:24:31
   running on Linux version #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023, release 6.1.21-v8+, machine aarch64
  #define WITH_HELP 1
  #define WITH_STATS 1
  #define WITH_STDIO 1
  #define WITH_FDNUM 1
  #define WITH_FILE 1
  #define WITH_CREAT 1
  #define WITH_GOPEN 1
  #define WITH_TERMIOS 1
  #define WITH_PIPE 1
  #define WITH_UNIX 1
  #define WITH_IP4 1
  #define WITH_IP6 1
  #define WITH_RAWIP 1
  #define WITH_INTERFACE 1
  #define WITH_TCP 1
  #define WITH_UDP 1
  #define WITH_SCTP 1
  #define WITH_DCCP 1
  #define WITH_UDPLITE 1
  #define WITH_LISTEN 1
  #define WITH_POSIXMQ 1
  #define WITH_SOCKS4 1
  #define WITH_SOCKS4A 1
  #define WITH_SOCKS5 1
  #define WITH_VSOCK 1
  #define WITH_PROXY 1
  #define WITH_SYSTEM 1
  #define WITH_SHELL 1
  #define WITH_EXEC 1
  #define WITH_TUN 1
  #define WITH_PTY 1
  #define WITH_OPENSSL 1
  #undef WITH_FIPS
  #define WITH_SYCLS 1
  #define WITH_FILAN 1
  #define WITH_RETRY 1
  #define WITH_MSGLEVEL 0 /*debug*/
  #define WITH_DEFAULT_IPV 0
Code language: Bash (bash)

If you have something older than you can compile it yourself.

First, make sur socat is uninstalled

sudo apt purge socatCode language: Bash (bash)

Get v1.8.0.0 source code and build it, I assume you have all the required build tools already installed.

cd ~
wget http://www.dest-unreach.org/socat/download/socat-
tar xzf socat-
cd socat-
sudo make installCode language: JavaScript (javascript)

Re-run the command to check the version, it should now say you have version

Configuring Direwolf

Edit your configuration, remove any metnion of KISSPORT directive and add the following:

# HF
KISSPORT 8002 2Code language: PHP (php)

This assumes Direwolf TNC channel 0 is VHF and channel 2 is HF, adjust to your own needs.

As per Direwolf documentation, this will expose each TNC channels on exclusively on the specified port.

The TNC multiplexer script

This script is intended to be exclusively run as a systemd. When run from command line it will spawn rogue processes that are difficult to kill so do not start it from command line!

Edit the file:

sudo nano /usr/local/bin/tnc_mux.shCode language: Bash (bash)

Add following content to the file, save it, close the editor.


# Direwolf VHF <=> aggregator
/bin/bash -c "while true; do socat tcp4:,reuseaddr,forever,interval=3 tcp4:,reuseaddr,forever,interval=3 ; sleep 5 ;  done" &

# Direwolf HF <=> aggretor
/bin/bash -c "while true; do socat tcp4:,reuseaddr,forever,interval=3 tcp4:,reuseaddr,forever,interval=3 ; sleep 5 ; done" &

/bin/bash -c "while true; do socat-mux.sh tcp4-listen:3333,reuseaddr,fork tcp4-listen:8004,reuseaddr,fork ; sleep 5 ; done" &Code language: Bash (bash)

Make it executable

sudo chmod +x  /usr/local/bin/tnc_mux.sh

Now create a systemd unit file for the script

sudo nano /etc/systemd/system/tnc_mux.service

This is the content of the systemd unit file

Description=TNC Mux Service


WantedBy=multi-user.targetCode language: PHP (php)

Save the file, close the editor and register and start the newly created service.

sudo systemctl enable tnc_mux.service
sudo systemctl start tnc_mux.serviceCode language: CSS (css)

Check that the service is running, if there is anything red than the service failed to start.

sudo systemctl status tnc_mux.serviceCode language: CSS (css)

TNC Multiplexer to Bluetooth SPP

Since we want APRSDroid to talk to the TNC Multiplexer rather than directly to Direwolf throuf SERIALKISSPOLL, we need an additional small systemd service which forwards port 2004 to Bluetooth SPP.

What it does is it uses the ability of the rfcomm bluetooth utility to run commands in order to start a socat command that will connect to the TNC multiplexer and read/write data to/from the /dev/rfcomm device

Edit the file

sudo nano /etc/systemd/system/tnc_mux_bluetooth.service

Here is the content of the file

Description=RFCOMM service for Direwolf
ExecStart=/usr/bin/bash -c "/usr/bin/rfcomm --raw watch hci0 1 /usr/local/bin/socat TCP4:,forever,interval=5 {}"
WantedBy=multi-user.targetCode language: JavaScript (javascript)

Save the file, close the editor, resgister and start the service.

sudo systemctl enable tnc_mux_bluetooth.service
sudo systemctl start tnc_mux_bluetooth.serviceCode language: CSS (css)

Make sure it is running, everything should be green.

sudo systemctl statustnc_mux_bluetooth.serviceCode language: CSS (css)

From there on you should be able to connect to your device and make usage of the TNC Multiplexer

Force APRSDroid refresh

I have all the beacons generated inside direwolf using TBEACON. Since you cannot tell APRSDroid to not beacon at all, I have set it to periodic localization with time interval of 600 minutes and refresh distance of 100km. This brings the issue that whenever Direwolf is sending out a beacon, APRSDroid will not know about it and will not refresh.

There is a bug in APRSDroid which we can exploit to circumvent this: whenever APRSdroid receives a frame which has not been repeated with its own callsign as source, it will refresh its display.

To achieve this we can just duplicate our TBEACON config lines and tell them to be sent to channel R0 and R2. This will simulate that the beacon have been received, they will make their way through the TNC multiplexer up to APRSdroid.

Here is how it looks in Direwolf config:

# The fake beacon to force APRSDroid refresh
# The actual beacon going on air


We now have a dual port Direwolf setup that we can monitor using APRSDroid. Pakcets sent by APRSDroid will be replicated to both ports, i.e. sending a message will send it on both HF and VHF.

The TNC Multiplexer script can be extended to add almost an infinite numebr of ports.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.