Skip to main content

A slim D-STAR gateway on a Raspberry Pi 2

··2287 words·11 mins

Installation of Archlinux #

I usually setup any Raspberry Pi without screen and keyboard but I make use of the serial console.

This procedure is taken from archlinuxarm.org – a more detailed tutorial is shown there.

Raspi 2 with wires connected to GPIO Pins GND, RXD and TXD
I haven’t changed a thing of the initial configuration – the serial console works just out of the box

Preparations (microSD card) #

Partition the microSD card on your PC or laptop.

$ sudo fdisk /dev/sda
Device         Boot  Start      End  Sectors  Size Id Type
/dev/sda1        2048   411647   409600  200M  c W95 FAT32 (LBA)
/dev/sda2      411648 15759359 15347712  7.3G 83 Linux

Format filesystems.

$ sudo mkfs.vfat /dev/sda1
$ sudo mkfs.ext4 /dev/sda2

I am curerntly in ~/mnt.

$ mkdir boot root
$ sudo mount /dev/sda1 boot
$ sudo mount /dev/sda2 root
$ wget http://os.archlinuxarm.org/os/ArchLinuxARM-rpi-armv7-latest.tar.gz
$ sudo bsdtar -xpf ArchLinuxARM-rpi-armv7-latest.tar.gz -C root
$ sync
$ sudo mv root/boot/* boot/
$ sudo umount boot root

So, place the microSD card in the Raspberry Pi and boot it up (with the serial console connected).

First start #

There are the following two users pre-defined:

UsernamePassword
rootroot
alarmalarm

I prefer my username as dominic, so I changed it:

# usermod -l dominic -d /home/dominic -m alarm
# groupmod -n dominic alarm
The user alarm may come from ArchLinux ARM.

So the first real thing is upgrading the system. We start as this:

# pacman-key --init
# pacman-key --populate archlinuxarm
# pacman -Syu

Some general system administration tasks, such as timezone setup network setup etc…

I’m using NetworkManager on the Raspi so I install it

# pacman -S networkmanager
# systemctl enable --now NetworkManager
# nmcli device wifi connect {network-ssid} --ask

Now we may login via ssh.

I had some problems with date and time, so a look at systemd-timesyncd.service, timedatectl status, timedatectl show-timesync and timedatectl timesync-status could be useful.

Installation of DStarGateway #

I prefer compiling as normal user so I login as dominic. We will need some packages.

$ sudo pacman -S --needed base-devel wget boost man-db gtest bind

I hope I got all that we need, if you run into errors, just install the missing ones 😉

$ mkdir git && cd git
$ git clone https://github.com/F4FXL/DStarGateway.git
$ cd DStarGateway
$ make

This ran for 38 minutes – I will not forget to run make -j4 the next time 🙄

Update: This took 3m 40s on my Raspberry Pi 4 2GB when run with make -j4.

You would now typically install the files but this is the part that made me stop for a while.

$ sudo make install

It will break, but at least it installs the binary files into /usr/local/bin.

Update: I did not have these problems on the Raspberry Pi 4 any more.

Whatever I was doing, it won’t work automated. I’m not a developer, but to me this looks like as if make -C enters the directory before it runs the top-level Makefile so the export ... lines never get executed and the Makefiles in the sub-directories will never know about them, I have to manually install the Data folder contents (AMBE files, Hostfiles).

Move to the Data directory and add the following line on top of the file:

export DATA_DIR=/usr/local/share/dstargateway.d

Then run sudo make install within the Data directory again and all should be fine.

Also install the hostfiles (this will need the program wget).

$ sudo make newhostfiles

Copy the systemd unit files to the right directory per hand:

Update: Again, on the Pi 4 this was not needed. It looks like I messed something up on the Raspberry Pi 2. Yet I leave the hints, they may become useful hopefully.
$ sudo cp debian/* /usr/lib/systemd/system/
Inspect them because you may edit some paths.

Also have a look at the configuration files in /usr/local/etc/.

Enable the services, but I don’t start them yet (except for a short test) because the hotspot will connect to the DSTAR reflector but we can’t talk or hear anything. Once they are enabled, they will autostart at the next reboot.

To enable the services:

$ sudo systemctl daemon-reload
$ sudo systemctl enable dstargateway.service
$ sudo systemctl enable dgwtimeserver.service

The second is only needed if you want time announcements.

Installation of MMDVMHost #

Also this requires special packages:

$ sudo pacman -S libsamplerate
$ git clone git@github.com:g4klx/MMDVMHost.git
$ cd MMDVMHost
$ make -j4
$ sudo make install-service

That would probably fail, but we can do it by hand.

Setup the user mmdvm:

$ sudo useradd --user-group -M --system mmdvm --shell /bin/false
$ sudo usermod --groups uucp --append mmdvm

So we run the command one more time:

$ sudo make install-service

Binaries are installed and the systemd unit files too.

Modify the configuration file /etc/MMDVM.ini.

Enable the service:

$ sudo systemctl daemon-reload
$ sudo systemctl enable mmdvmhost.service

Setup the UART #

We can’t start MMDVMHost right away (well, we can, but it will not work yet).

Enable the UART in /boot/config.txt:

enable_uart=1

Update: On the Raspberry Pi 4 again, we would write something like

enable_uart=1
dtoverlay=disable-bt

But we should not need to disable the serial console serial-getty@ttyAMA0.service.

Add this near the top or after [All].

We need to disable the serial console because we need the UART at the GPIO pins for our modem hardware.

Disable the serial console service:

$ sudo systemctl disable serial-getty@ttyAMA0.service

Open /boot/cmdline.txt and remove console=serial0,115200 from the line.

Save and reboot.

Configuration #

Make sure to check them before bringing up any service.

DStarGateway #

I removed unused configuration parts, you can leave them in your config, but I want this code here as small as possible without loosing too much of information.

[Gateway]
type=hotspot
callsign=CALLSIGN
address=0.0.0.0
icomAddress=172.16.0.20
icomPort=20000
hbAddress=
hbPort=20010
latitude=0.0
longitude=0.0
description1=
description2=
url=
language=english_us

[ircddb_1]
enabled=true
hostname=ircv4.openquad.net
username=
password=

[ircddb_2]
enabled=false
# [...]

[ircddb_3]
enabled=false
# [...]

[ircddb_4]
enabled=false
# [...]

[Repeater_1]
enabled=true
band=E
callsign=CALLSIGN
address=
port=20011
type=hb
reflector=REF096 A
reflectorAtStartup=
reflectorReconnect=5
frequency=432.7625
offset=-0.0
rangeKm=20
latitude=0.0
longitude=0.0
agl=
description1=
description2=
url=
band1=
band2=
band3=

[Repeater_2]
enabled=false
# [...]

[Repeater_3]
enabled=false
# [...]

[Repeater_4]
enabled=false
# [...]

[APRS]
enabled=false
hostname=rotate.aprs2.net
port=14580
password=12345
positionSource=

[GPSD]
address=
port=

[Log]
path=/var/log/dstargateway/
fileRoot=
fileRotate=
fileLevel=
displayLevel=

[Paths]
data=/usr/local/share/dstargateway.d

[DExtra]
enabled=true
maxDongles=5

[DPlus]
enabled=true
maxDongles=5
login=

[DCS]
enabled=true

[XLX]
enabled=true
hostfileUrl=http://xlxapi.rlx.lu/api.php?do=GetXLXDMRMaster

[DRats]
enabled=false

[Remote]
enabled=false
port=4242
password=CHANGE_ME

[AccessControl]
whiteList=
blackList=
restrictList=

[Daemon]
daemon=false
pidfile=/var/run/dstargateway/dstargateway.pid
user=dstar

You can set band=B or band=C in [Repeater_1]. I have E because I sometimes test a dual-hat hotspot which used to have B set and I’ve already setup E in my DSTAR terminal settings on regist.dstargateway.org.

MMDVMHost #

This would be a very long list, I removed things that I did not change from the example config file. I also changed some values to not have a duplicate of my hotspot running wild somewhere, so make sure you change all the options to match your own setup.

[General]
Callsign=CALLSIGN
Id={DMR_ID} # or CCS or whatever this is called
Timeout=180
Duplex=0
RFModeHang=10
NetModeHang=3
Display=None
Daemon=1

[Info]
RXFrequency=432762500
TXFrequency=432762500
Power=1
Latitude=0.0
Longitude=0.0
Height=0
Location=Home
Description=DSTAR Hotspot
URL=https://oe7drt.com

[Log]
DisplayLevel=0
FileLevel=2
FilePath=/var/log/mmdvm/
FileRoot=MMDVM
FileRotate=1

[CW Id]
Enable=0
Time=10

[DMR Id Lookup]
File=DMRIds.dat
Time=24

[NXDN Id Lookup]
File=NXDN.csv
Time=24

[Modem]
Protocol=uart
UARTPort=/dev/ttyAMA0
UARTSpeed=115200
TXInvert=1
RXInvert=0
PTTInvert=0
TXDelay=100
RXOffset=-75
TXOffset=-75
DMRDelay=0
RXLevel=50
TXLevel=50
RXDCOffset=0
TXDCOffset=0
RFLevel=100
RSSIMappingFile=/usr/local/etc/RSSI.dat
UseCOSAsLockout=0
Trace=0
Debug=0

[Transparent Data]
Enable=0
# [...]

[D-Star]
Enable=1
Module=E
SelfOnly=1
AckReply=1
AckTime=750
AckMessage=0
ErrorReply=1
RemoteGateway=0
WhiteList=

[DMR]
Enable=0
# [...]

[System Fusion]
Enable=0
# [...]

[P25]
Enable=0
# [...]

[NXDN]
Enable=0
# [...]

[M17]
Enable=0
# [...]

[FM]
Enable=0
# [...]

[AX.25]
Enable=0
# [...]

[D-Star Network]
Enable=1
LocalAddress=127.0.0.1
LocalPort=20011
GatewayAddress=127.0.0.1
GatewayPort=20010
Debug=0

[DMR Network]
Enable=0
# [...]

[System Fusion Network]
Enable=0
# [...]

[P25 Network]
Enable=0
# [...]

[NXDN Network]
Enable=0
# [...]

[M17 Network]
Enable=0
# [...]

[POCSAG Network]
Enable=0
# [...]

[FM Network]
Enable=0
# [...]

[AX.25 Network]
Enable=0
# [...]
$ sudo cp /home/dominic/git/MMDVMHost/RSSI.dat /usr/local/etc/

dgwtimeserver #

[TimeServer]
callsign=CALLSIGN
address=
format=voice
language=english_us_2
interval=60

[Paths]
data=/usr/local/share/dstargateway.d/

[Repeater_1]
enabled=true
band=E

[Repeater_2]
enabled=false
band=

[Repeater_3]
enabled=false
band=

[Repeater_4]
enabled=false
band=

[Log]
path=/var/log/dstargateway/
fileRoot=
fileRotate=
fileLevel=
displayLevel=

[Daemon]
daemon=false
pidfile=/var/run/dstargateway/dstargateway.pid
user=dstar

Updating hostfiles #

This paragraph has been added on 18 Feb 2024

So we will probably update our hostfiles and I made a little script that fetches actual hostfiles from trusted sources. But since Archlinux does not come with any cron installed I could either install one or just use systemd timers.

I went with the latter one of the options and assuming the script that updates the hostfiles is located in /home/dominic/bin/update-hosts.sh I used these unit-files that I put into /usr/lib/systemd/system/:

update-hosts.service #

[Unit]
Description="Update D-STAR hostfiles"
After=network.target

[Service]
Type=oneshot
ExecStart=/home/dominic/bin/update-hosts.sh
ExecStartPost=/usr/bin/systemctl restart dstargateway.service

[Install]
WantedBy=default.target

update-hosts.timer #

[Unit]
Description="Update D-STAR hostfiles timer"

[Timer]
OnBootSec=5min
OnCalendar=daily
RandomizedDelaySec=20min
Unit=update-hosts.service

[Install]
WantedBy=default.target

Enable the timer and it will run now and daily in future.

$ sudo systemctl enable --now update-hosts.timer

Install a dashboard #

I will install the dashboard from John Hays (K7VE) as my first look at it looked promising.

I’ve created an issue in January 2024 regarding the crash of the dashboard when other gateways connect to our hotspot and the actual problem is still not yet resolved - there have been made some modifications to actually enable redirecting of the HTTP port but the HTTPS certs are still needed and since I don’t use them on the hotspot and all HTTPS traffic is handled by a reverse proxy in my network this is just another complexity that I don’t need.

I have also uploaded my fork of it (including the modifications below) on my git repo: https://gits.oe7drt.net/dominic/dsgwdashboard/src/branch/only_http

You can see the dashboard in action at: https://hotspot.oe7drt.net/

But: I will not install this as it is in his instructions, because I don’t like when these kind of applications (simple dashboards for example) have to be run as the root user. I will therefore create a new user called dashboard who will run the webserver later.

We need a few packages for this:

$ sudo pacman -S nodejs npm

Create and impersonate our new user:

$ sudo useradd --user-group -m --system dashboard --shell /bin/bash
$ sudo su - dashboard

Now we are the user dashboard and we will install the dashboard:

$ git@github.com:johnhays/dsgwdashboard.git
$ cd dsgwdashboard
$ node -v
$ npm install -save

We would now install some certificates to let the webserver be accessible via HTTPS, but I maintain a reverse-proxy at my home which will take care of all the https-connections.

Therefore I modify the index.js file according to the following patch:

diff --git a/index.js b/index.js
index 0c71092..b8a7aba 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,4 @@
-const https = require("https");
+const http = require("http");
 const fs = require("fs");
 const ini = require("ini");
 const lineReader = require('line-reader');
@@ -32,12 +32,8 @@ updatelinks();

 let serverPort = inifile.config.port;

-const server = https
+const server = http
 	.createServer(
-		{
-			key: fs.readFileSync("key.pem"),
-			cert: fs.readFileSync("cert.pem"),
-		},
 		app
 	)
 	.listen(serverPort, ()=>{
@@ -83,7 +79,7 @@ function updatelinks() {
 	};
 	let i = 0;
 	while (i < lines.length) {
-		if(lines[i] != "") {
+		if(lines[i] != "" && lines[i].match(linksregex)) {
 			var mylinks = lines[i].match(linksregex);
 			// console.log(JSON.stringify(lines[i]));
 			var linkrec = {'timestamp':mylinks[1].substr(0,19) , 'protocol':mylinks[2] , 'device':mylinks[4],

Modify the dashboard.ini file to change the port from 443 to 8443. Why? Because I want to run the webserver as non-root user1!

[config]
dgwconfig=/usr/local/etc/dstargateway.cfg
host=hotspot.oe7drt.net
port=8443
This might be confusing now, the actual host above does not listen to port 8443 because there is a reverse-proxy in-between (and actually a firewall/router too).

The actual path of this host and how it will be routed:

%%{init: {"flowchart": {"htmlLabels": false}} }%% graph LR; A([Internet user]):::usr -- "`**HTTPS**`" -->B["`router/firewall _hotspot.oe7drt.net_`"]:::fw; B-- "`**HTTPS**`" -->C["`reverse-proxy _proxy.lan_`"]:::rev; C-- "`**HTTP**`" -->D["`hotspot dashboard _hotspot.lan_`"]:::dash; classDef usr stroke:#faa classDef fw stroke:#f55 classDef rev stroke:#9f9 classDef dash stroke:#0f0

This configuration is now as slim as I could make, removing encryption on the dashboard made it even better in terms of performance and maintainability as I don’t have to worry about the certificates on this host and no direct port-forwarding to this host has been made.

I will now disable the shell for the dashboard user because I won’t need to login as the dashboard user again.

$ sudo chsh -s /bin/false dashboard

Start the dashboard with systemd #

John already provides a systemd unit file, we need to copy this to the right directory.

$ sudo cp /home/dashboard/dsgwdashboard/util/dsgwdashboard.service /usr/lib/systemd/system/

Edit the file, because John uses different paths than we do.

[Unit]
Description=D-STAR Gateway Dashboard
After=network.target network-online.target
Wants=network-online.target

[Service]
User=dashboard
Type=simple
WorkingDirectory=/home/dashboard/dsgwdashboard
ExecStart=/usr/bin/node index.js
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=60
StartLimitBurst=0
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target

Enable and start the dashboard:

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now dsgwdashboard.service

DSTAR Registration #

A DSTAR registration is needed if you want your transmission to be heard on original ICOM repeaters. Otherwise your transmission will not be forwarded and you may be searching for errors…

I registered at https://regist.dstargateway.org/ but there is one important thing to add to the webui there: do not choose long passwords (like those from a password manager) because it will get cut off somewhere and it took me quite a while to realize that.

I can’t believe that there are still websites in 2024 that limit the lenght of a password!

This can be an indication, that our passwords are saved in cleartext.

This is one reason to not use the same password on different websites/services.

For your information: 12 characters work – I couldn’t bring me back up to test more. Many password-reset emails have been sent for this already so I couldn’t be bothered to investigate even more into that.

Final words #

So this note ends right here, hopefully this will be of any help to me in the future or somebody that found it online.


  1. Ports below 1024 can only be used as the root user. Those are socalled privileged ports. To run the program as non-root user we need to set the port to something above 1024↩︎