Table of Contents

Raspberry Pi and Linux Kernel AX.25 support

This page documents how to set up a Raspberry Pi running Raspberry Pi OS Lite (32 bit), and was written by Szymon M0GZP.

It is possible to do this on 64 bit Raspberry Pi OS as well, so long as you are consistently using 32 bit or 64 bit throughout; see the notes below.

Alternative reading:

Mike has also written up a guide on his own site for configuring all the bits.

There is also documentation in TLDP: https://tldp.org/HOWTO/Netrom-Node-5.html

Equipment

This example setup consists of:

but the same principles apply with different radios, audio and radio interfaces.

Software Prereqs

sudo apt install libax25 ax25-apps ax25-tools socat direwolf

You do not need to install socat or direwolf packages if you will be using a hardware TNC such as NinoTNC.

Notes:

TNC

The author uses Dire Wolf to run the two radios. Dire Wolf is a fundamentally a KISS modem implemented in software and using a sound card. An alternative to using a sound card based KISS modem is to use a hardware device such as NinoTNC. The same overall steps still apply, just using a piece of hardware as the modem instead of a piece of software.

Here is a suitable Dire Wolf config:

ADEVICE plughw:1,0
ACHANNELS 1

ADEVICE1 plughw:4,0
ACHANNELS 1

CHANNEL 0
MYCALL M0GZP
MODEM 1200
PTT /dev/ttyUSB0 RTS

CHANNEL 2
MYCALL M0GZP-1
MODEM 300 7@30 E
PTT RIG 3073 /dev/ttyUSB1 
IL2PTX 

AGWPORT 0
KISSPORT 8001 0
KISSPORT 8002 2

This sets up two channels:

It also disables AGW, and sets up two KISS listeners on port 8001 (assigned to channel 0) and 8002 (assigned to channel 2)

AX.25 Configuration Files

The config files are all in /etc/ax25. Here is what I set up for the three key files:

axports

This file sets up two AX.25 ports, one for VHF and one for HF

VHF     M0GZP-1         19200   255     4       144.375 Mhz 1k2bps
HF      M0GZP-2         19200   200     4       HF 300bps

nrports

This file sets up a NET/ROM port aliased as GZPNOD

nrvhf   M0GZP-5 GZPVHF  235     VHF NET/ROM Port
nrhf    M0GZP-6 GZPHF   40      HF NET/ROM Port

NOTE: nrports cannot contain any blank lines.

nrbroadcast

This file configures how our AX.25 ports will broadcast NET/ROM nodes. My VHF node sets all seen nodes to a default quality of 220, and will broadcast any node with a minimum quality of 200. The HF node on the other hand sets all nodes to a default quality of 200 (so as not to advertise HF nodes over VHF), and will broadcast nodes with a minimum quality of 200.

VHF     5       200     200     1
HF      5       220     200     1

ax25d.conf

This file configures the ax25 userspace daemon, which determines what processes are run when other users connect in to the node. In my example, I am calling the program uronode (by N1URO, sadly SK). A separate entry is required for each port.

[M0GZP-1 via VHF]
NOCALL   * * * * * *  L
default  * * * * * *  0 root  /usr/local/sbin/uronode   uronode

[M0GZP-2 via HF]
NOCALL   * * * * * *  L
default  * * * * * *  0 root  /usr/local/sbin/uronode   uronode

<nrhf>
NOCALL   * * * * * *  L
default  * * * * * *  0 root  /usr/local/sbin/uronode   uronode

<nrvhf>
NOCALL   * * * * * *  L
default  * * * * * *  0 root  /usr/local/sbin/uronode   uronode

Startup Scripts

I wrote some scripts to start everything up.

/usr/sbin/startRadio.sh

This script will:

#!/bin/bash
echo -n Creating socat sockets...
socat pty,echo=0,link=/tmp/kiss8001,wait-slave tcp:127.0.0.1:8001 &
socat pty,echo=0,link=/tmp/kiss8002,wait-slave tcp:127.0.0.1:8002 &
sleep 1
echo Done
echo -n Attaching KISS interfaces for direwolf...
kissattach -l `ls -l /tmp/kiss8001 | awk '{ print $11 }'` VHF
sleep 1
kissattach -l `ls -l /tmp/kiss8002 | awk '{ print $11 }'` HF
sleep 1
echo Done
echo -n 'Bringing up NET/ROM runtime...'
modprobe netrom
nrattach nrvhf
nrattach nrhf
netromd -i -l -d
echo -n Restoring node tables...
[ -x /etc/ax25/nodesave.sh ] && /etc/ax25/nodesave.sh
echo Done
echo -n Starting mheard daemon...
mheardd -l
echo Done
echo -n Starting ax25 daemon...
ax25d -l
echo Done

For a modem other than Dire Wolf, an alternative script can be used:

#!/bin/bash
echo -n Attaching KISS interfaces...
kissattach /dev/ttyACM0 HF
sleep 1
echo Done
echo -n 'Bringing up NET/ROM runtime...'
modprobe netrom
nrattach netrom
netromd -i -l -d
echo -n Restoring node tables...
[ -x /etc/ax25/nodesave.sh ] && /etc/ax25/nodesave.sh
echo Done

NB this script appears to run before /dev/ttyACM0 is available, so currently doesn't work. I'm coming up with a fix.

/usr/sbin/stopRadio.sh

This script does the opposite to startRadio, killing the processes and taking down the interfaces

#!/bin/bash
echo -n Saving node tables...
/usr/sbin/nodesave > /etc/ax25/nodesave.sh && chmod 755 /etc/ax25/nodesave.sh
echo Done
echo -n Killing mheard daemon...
killall mheardd
sleep 1
echo Done
echo -n Killing ax25 daemon...
killall ax25d
sleep 1
echo Done
echo -n Killing all socat processes in case they do not end cleanly...
killall socat
sleep 1
echo Done
echo -n Killing all kissattach processes...
killall kissattach
sleep 1
echo Done
echo -n Killing the netromd process...
killall netromd
sleep 1
echo Done
echo -n Taking down the netrom interface...
ifconfig nr0 down
sleep 1
echo Done
echo -n Removing kernel modules...
rmmod netrom && sleep 1 && rmmod mkiss && sleep 1 && rmmod ax25 && sleep 1
echo Done
echo -n Taking down the AX.25 interfaces...
ifconfig ax0 > /dev/null 2>&1 && ifconfig ax0 down
ifconfig ax1 > /dev/null 2>&1 && ifconfig ax1 down
sleep 1
echo Done

For a modem other than Dire Wolf, an alternative script can be used:

#!/bin/bash
echo Saving node tables...
/usr/sbin/nodesave > /etc/ax25/nodesave.sh && chmod 755 /etc/ax25/nodesave.sh
echo Killing all kissattach processes
killall kissattach
echo Killing the netromd process
killall netromd
echo Taking down the netrom interface
ifconfig nr0 down
echo Removing kernel modules
rmmod netrom
rmmod mkiss
rmmod ax25
echo Taking down the AX.25 interfaces
ifconfig ax0 > /dev/null 2>&1 && ifconfig ax0 down
echo Done

Don't forget to make these scripts executable:

chmod +x /usr/sbin/startRadio.sh
chmod +x /usr/sbin/stopRadio.sh

/lib/systemd/system/ax25.service

This script registers the ax25 service with systemd

[Unit]
Description=Start up radio processes
After=direwolf.service
Requires=direwolf.service

[Service]
Type=oneshot
ExecStart=/usr/sbin/startRadio.sh
ExecStop=/usr/sbin/stopRadio.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Time to run

To start things up, use systemctl start – then use status to see how it went:

szymon@radio:~ $ sudo systemctl start ax25
szymon@radio:~ $ sudo systemctl status ax25
● ax25.service - Start up radio processes
     Loaded: loaded (/lib/systemd/system/ax25.service; disabled; vendor preset: enabled)
     Active: active (exited) since Mon 2023-04-17 00:36:38 BST; 7s ago
    Process: 3036 ExecStart=/usr/sbin/startRadio.sh (code=exited, status=0/SUCCESS)
   Main PID: 3036 (code=exited, status=0/SUCCESS)
      Tasks: 7 (limit: 4915)
        CPU: 156ms
     CGroup: /system.slice/ax25.service
             ├─3037 socat pty,echo=0,link=/tmp/kiss8001,wait-slave tcp:127.0.0.1:8001
             ├─3038 socat pty,echo=0,link=/tmp/kiss8002,wait-slave tcp:127.0.0.1:8002
             ├─3047 kissattach -l /dev/pts/4 VHF
             ├─3061 kissattach -l /dev/pts/3 HF
             ├─3089 netromd -i -l -d
             ├─3099 mheardd -l
             └─3104 ax25d -l

Apr 17 00:36:38 radio startRadio.sh[3077]: NET/ROM port nrvhf bound to device nr0
Apr 17 00:36:38 radio startRadio.sh[3082]: NET/ROM port nrhf bound to device nr1
Apr 17 00:36:38 radio netromd[3089]: starting
Apr 17 00:36:38 radio startRadio.sh[3036]: Restoring node tables...Done
Apr 17 00:36:38 radio mheardd[3099]: starting
Apr 17 00:36:38 radio startRadio.sh[3036]: Starting mheard daemon...Done
Apr 17 00:36:38 radio startRadio.sh[3036]: Starting ax25 daemon...Done
Apr 17 00:36:38 radio systemd[1]: Finished Start up radio processes.
Apr 17 00:36:38 radio ax25d[3104]: starting
Apr 17 00:36:38 radio ax25d[3104]: new config file loaded successfully
szymon@radio:~ $ 

You will see some new network interfaces on ifconfig (I've removed my normal interfaces):

ax0: flags=67<UP,BROADCAST,RUNNING>  mtu 255
        ax25 M0GZP-1  txqueuelen 10  (AMPR AX.25)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1  bytes 49 (49.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ax1: flags=67<UP,BROADCAST,RUNNING>  mtu 200
        ax25 M0GZP-2  txqueuelen 10  (AMPR AX.25)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

nr0: flags=193<UP,RUNNING,NOARP>  mtu 235
        netrom M0GZP-5  txqueuelen 1000  (AMPR NET/ROM)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

nr1: flags=193<UP,RUNNING,NOARP>  mtu 40
        netrom M0GZP-6  txqueuelen 1000  (AMPR NET/ROM)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

You can now use axcall to contact another node, axlisten to monitor traffic, etc.

If it was all successful, enable the service to start at boot:

sudo systemctl enable ax25.service