-
Flash the image to an SD card (https://etcher.balena.io/#download-etcher): (The device already includes a freshly flashed SD card — you can skip this step and go straight to step 2.)
https://storage.googleapis.com/rf-guru/rpi-images/hotspot-2025-12-09.img.gz
-
Insert the SD card into the Raspberry Pi Zero 2W and power it with a stable 5V supply (usb port on the far right of the device).
-
Wait for it to boot up. While waiting for boot you can subscribe to our RF Guru's Analog Hotspot mailinglist. https://listmonk.rf.guru/subscription/form
-
Connect to the Wi-Fi AccessPoint of the Hotspot SSID:
AccessPopup
Password: 1234567890 -
Open the dashboard:
http://192.168.50.5/ (or http://hotspot.local) -
Open Shell → log in:
- Username:
hotspot - Password:
hotspot
- Username:
-
Configure Wi-Fi (add as many networks you want, but do not remove AccessPopup):
sudo nmtui(YouTube how-to) https://youtu.be/EJn0JkStWuY
Note: When you save a new Wi-Fi network, the hotspot may immediately connect to it and disable the AccessPopup access point. Your PC will then lose the AccessPopup Wi-Fi connection (this is normal). Just connect your PC to your own Wi-Fi network, then reopen the portal via: http://hotspot.local (or use the announced IP address if mDNS is not available) login hotspot/hotspot and go to step 12.
-
Reboot:
sudo reboot -
After reboot, connect via:
- http://hotspot.local
or - the spoken IP address announced over RF (434.925 MHz — FM Narrow | 145.925 MHz — FM Narrow)
- If this does not work, check whether the AccessPopup network is still being broadcast over Wi-Fi. If it is, something went wrong while entering your network details. Go back to point 6 and repeat the setup.
- Start configuration:
Use your own email adress ! And use real coordinates, some reflectors do not like hotspots info to be hidden ! Look at the local AUP! Callsigns in UPPERCASE !
sudo hotspot-config(video demo https://youtu.be/bKF9JRo0ORM?t=125)
-
For Belgian users, set the domain:
be.svx.link is the domain, the portal of this domain is https://portal.be.svx.link
-
After initial configuration, your SVXReflector sysop must sign your certificate.
Once signed, your hotspot is fully operational. -
Continue setup:
We encourage everyone to leave Temperature tuning on !
Some people find the callsigns announcements in QSO's ennoying, you can turn this on or off here!
sudo hotspot-options- Belgian users can update talkgroups + button presets:
sudo hotspot-on-webportal- You are now ready to rumble 🎙️📡
Best to install these first — once 4G is up, wlan0 is brought down and
you'll need an alternate way in if anything misbehaves:
- Raspberry Pi Connect — remote shell over the internet
- Installing Bluetooth for the HotSpot companion app — BLE fallback to toggle 4G off if it gets stuck
For the 4G version install the module via: (it's default configured for the https://alwaysconnected.eu/ provider)
sudo install-4gmoduleConfigure another provider
sudo hotspot-4g-config- RF.Guru Analog Hotspot/Transceiver
https://shop.rf.guru/collections/hotspot
300–500 mW Output Power
Supports UHF and VHF on Raspberry Pi Zero 2W.
- 434.925 MHz — FM Narrow
- CTCSS Input: 88.5 Hz
- CTCSS Output: 250.3 Hz
- 145.925 MHz — FM Narrow
- CTCSS Input: 88.5 Hz
- CTCSS Output: 250.3 Hz
https://storage.googleapis.com/rf-guru/rpi-images/hotspot-2025-12-09.img.gz
https://listmonk.rf.guru/subscription/form
-
Connect to:
- SSID: AccessPopup
- Password: 1234567890
-
Open:
http://192.168.50.5/ -
Choose Shell.
- Username:
hotspot - Password:
hotspot
sudo nmtui
Reboot:
sudo reboot
If your router supports mDNS:
If not:
The hotspot announces its IP address over RF on the default frequency. (434.925 MHz — FM Narrow | 145.925 MHz — FM Narrow)
Tutorial video:
https://www.youtube.com/watch?v=bKF9JRo0ORM
D911#— Speaks IP address over RFhotspot-frequency— Quick frequency setuphotspot-options— Thermal + announcementshotspot-talkgroups— Monitored TGs (and CTCSS-to-TG mapping on non-PRO chips); on PRO chips the first monitored TG becomes the default TGhotspot-volume— Audio control
- Raspberry PI4
- Raspberry PI5
- Raspberry PiZero 2W
- Raspberry Compute Module 4
- Raspberry Compute Module 5
- Pin 3 → GPIO2
- Pin 6 → GPIO3
- Pin 35 → GPIO19
- Pin 8 → TX
- Pin 10 → RX
- Pin 12 → CLK
- Pin 32 → GPIO12 (COS input)
- Pin 36 → GPIO16 (PTT)
- Pin 38 → GPIO20
- Pin 40 → GPIO21
- Pin 31 → GPIO6
- Pin 33 → GPIO13
- Pin 25 → GPIO7
- Pin 29 → GPIO5
iOS does not broadcast the hotspot SSID continuously.
Open Settings → Personal Hotspot to force visibility.
sudo tail -f /var/log/svxlinkOnly relevant on 4G hotspots (the install-4gmodule flavour).
The 4G stack logs to journald via two systemd units:
| Service | What it logs |
|---|---|
hotspot-4g.service |
Connection attempts — "Attempt N/99", QMI device wait, registration wait, WDS session start, DHCP, "online" / "giving up after N attempts". Logs once per session/restart. |
hotspot-4g-monitor.service |
Periodic health probes — "probe failed (N/3)", "link recovered after N failed probe(s)", "link stale — restarting hotspot-4g.service". |
# Live, current session + new lines as they come
sudo journalctl -u hotspot-4g -f
sudo journalctl -u hotspot-4g-monitor -f
# Both at once
sudo journalctl -u hotspot-4g -u hotspot-4g-monitor -f
# Just this boot
sudo journalctl -u hotspot-4g -b --no-pager
# Last hour
sudo journalctl -u hotspot-4g --since "1 hour ago" --no-pagerThe companion app's BLE feed (sg field) also shows the live signal strength — same data, no terminal.
These work independently of our scripts — handy for sanity-checking:
# Is it registered? On which network / band?
sudo qmicli -p -d /dev/cdc-wdm0 --nas-get-serving-system
# Signal strength in dBm
sudo qmicli -p -d /dev/cdc-wdm0 --nas-get-signal-strength
# Which IP did the WDS session pull?
ip addr show wwan0
ip route | grep wwan0Healthy output of the first command contains Registration state: 'registered' and your home network (or a roaming partner with Forbidden: 'no').
Symptom — --nas-get-serving-system shows:
Registration state: 'registration-denied'
Roaming status: 'on'
Forbidden: 'yes'
This is your roaming partner saying "your SIM provider isn't on my allowed-roamers list". The modem keeps camping on it because it has the strongest signal at your location, even though other carriers in range would accept the SIM. The fix is to bias the modem toward those other carriers.
sudo qmicli -p -d /dev/cdc-wdm0 --nas-network-scanFor each network you'll get an MCC, MNC, Description, and a Status flag list. Note the ones marked not-forbidden — those are your candidates.
- The scan output already shows it (MCC + MNC per network).
- Or look it up on a reference like https://www.mcc-mnc.com/ — pick the carriers active in your area.
A few examples:
| Country | Carrier | MCCMNC |
|---|---|---|
| Belgium | Proximus | 20601 |
| Belgium | BASE | 20620 |
| Belgium | Orange BE | 20610 |
| Netherlands | KPN | 20408 |
| Netherlands | Vodafone NL | 20404 |
| Germany | Telekom DE | 26201 |
| Germany | Vodafone DE | 26202 |
| France | Orange FR | 20801 |
| France | SFR | 20810 |
sudo qmicli -p -d /dev/cdc-wdm0 \
--nas-set-preferred-networks="<MCCMNC>,<access_tech>,<MCCMNC>,<access_tech>,..."
sudo qmicli -p -d /dev/cdc-wdm0 --nas-force-network-search
sleep 8
sudo qmicli -p -d /dev/cdc-wdm0 --nas-get-serving-system # expect 'registered'<access_tech> varies by your libqmi version. Try in this order until one is accepted:
| Try | Notes |
|---|---|
eutran |
Formal 3GPP name for LTE access. Most common on recent libqmi. |
eutra |
Older libqmi spelling. |
lte |
Newest libqmi spelling. |
unspecified |
Matches any RAT — use as a fallback. |
This setting is persistent in the modem's NV memory — it survives reboots and even hotspot-4g's low-power/online reset cycle. You don't have to redo it after every boot.
For repeatability across SD-card re-flashes or modem factory resets, add it to /etc/hotspot-4g.conf:
PREFERRED_NETWORKS="20610,eutran,20601,eutran"hotspot-4g re-applies this on every script start (only on the first attempt of the connect loop, so retries stay fast), so the bias is always in place after the SD card hits the device.
sudo systemctl restart hotspot-4g
sudo journalctl -u hotspot-4g -fIf the scan in step 1 showed only the forbidden carrier (rural area, no alternative coverage), preference-setting won't help — talk to your SIM provider about adding a roaming agreement with that carrier or switch to a SIM that has one.
To clear a preferred-networks list:
sudo qmicli -p -d /dev/cdc-wdm0 --nas-set-preferred-networks=""DTMF 91<TALKGROUP>#
This is the recommended over-the-air way to switch talkgroups. It doesn't disturb anyone else and it doesn't require being on TG0 first:
- DTMF digits are muted at the hotspot (svxlink's
DTMF_MUTING=1in[Rx1]), so the network never hears your tones. You can't accidentally send a DTMF burst into someone's QSO. - No requirement to be on TG0 first.
91<TG>#jumps straight from whichever talkgroup you're currently on.
You do still need a quiet moment to key your radio: the hotspot is half-duplex, so while it's transmitting (relaying audio from the network out over RF) you can't push DTMF into it. Wait for the hotspot to stop transmitting, then send the sequence — the switch happens locally and the next time you key up you're on the new TG.
Examples:
| What | DTMF |
|---|---|
| Switch to TG 91 | 9191# |
| Switch to TG 9000 | 919000# |
| Return to TG 0 (parking) | 910# |
| Temporarily monitor another TG for one hour | 94<TG># (e.g. 9423#) |
If you want to switch while the hotspot is busy relaying a QSO (so you can't get DTMF in over the air), use one of the out-of-band channels — neither touches the radio:
- Bluetooth companion app (preferred). Tap the talkgroup in the app and you're switched immediately, even mid-traffic. See the Installing Bluetooth section for the install and the Analog-HotSPOT-App.
- Local web portal at
http://hotspot.local(or the announced IP) — one click per talkgroup.
To activate a talkgroup, send the corresponding CTCSS tone from the CTCSS Talkgroup Mapping while in TG0.
You need to be in TG0, and you need to wait until there's no traffic — you'll hear a bleep tone 15 seconds after a QSO ended, that's your cue.
Once it switches it's instant – no double presses like before, you can start speaking immediately.
Caveats vs DTMF: you have to be on TG0 first, you have to remember the tone-to-TG mapping, and it's unavailable on SA818PRO hotspots (no software CTCSS decode — see CTCSS Talkgroup Mapping). DTMF works on every chip from any TG, so prefer DTMF over CTCSS.
To get the current IP address of the hotspot, send:
DTMF D911#
To set up multiple talkgroups for monitoring, configure them in hotspot-config using this format:
8++, 23+, 50, 51, 52, 53, 54, 55
The TX CTCSS tone remains the same across all talkgroups. The plus signs (+) indicate priority levels. More plus signs mean higher priority. While a talk group is selected, and there is activity on a talk group with higher priority, the higher prio talk group will be selected.
Temporarily monitor (for one hour) another talkgroup:
DTMF 94#
Example for TG23:
9423#
Applies to every chip except the SA818PRO. The CTCSS-to-talkgroup mapping is a software-CTCSS feature implemented inside SVXLink — svxlink listens to the audio, decodes the tone, and switches talkgroups. Any radio module that hands raw audio to the Pi falls into this category: the original SA818, the SA818S-CE, the SA868, etc. — regardless of which board generation they're soldered onto. A 4th-gen board can ship with an SA818S-CE chip, in which case it uses this software-CTCSS mapping just like a 1st-gen board does.
Only the SA818PRO is different: it has hardware CTCSS decoding on-chip and drives a single squelch GPIO line, so svxlink doesn't decode anything itself and there's no per-tone mapping. On a PRO hotspot, talkgroup switching uses one of:
- DTMF:
91<TG>#to activate a talkgroup,910#to return to TG0- Local portal (
http://hotspot.local): one click per talkgroup- Bluetooth companion app (Analog-HotSPOT-App) — see the Installing Bluetooth section.
Many users on software-CTCSS boards also prefer DTMF / portal / BLE over the per-tone mapping because the mapping is easy to forget — the options above work everywhere.
You can map talkgroups via CTCSS tones using the following format:
tone:talkgroup, tone:talkgroup, …
Example default mapping:
67.0:8400, 69.3:8, 71.9:23, 74.4:9000, 77.0:50, 79.7:51, 82.5:52, 85.4:53, 88.5:54, 91.5:55
Mapping a single default talkgroup:
88.5:8
This maps CTCSS tone 88.5 to talkgroup 8.
You can access the local dashboard:
- Via hostname (if your network supports mDNS)
- Or via the hotspot’s IP address
We advise selecting a frequency not used by nearby repeaters. Do not use the ISM frequency 433.000 MHz.
Recommended defaults we use:
- 70 cm: 439.100 MHz
- 2 m: 145.250 MHz
- CTCSS: 88.5 Hz
Use a tone/frequency not locally used.
For the CTCSS TX tone best to pick on between 67 and 85.4 (best audio quality)
Enables a BLE GATT service so a phone or laptop can drive the hotspot (send DTMF, restart SVXLink, toggle 4G, watch live state) without SSH.
Install on the hotspot:
sudo /usr/sbin/install-bluetoothIf you get a “command not found” message, run hotspot-config again. Just press Enter and check that all settings are correct. It will download the latest software and update the hotspot, rerun install-bluetooth afterwards:
sudo /usr/sbin/hotspot-configAfter install, the device advertises over BLE as its hostname.
- Turn Bluetooth on in the OS — that's all the OS-level setup needed.
- Do not pair the hotspot in the system Bluetooth settings. You won't see it there anyway, and pairing isn't required.
This is Bluetooth Low Energy (BLE), not classic Bluetooth. BLE works differently from headphones / keyboards / car kits:
- Devices expose GATT services identified by UUID. The companion app scans for the hotspot's service UUID directly and connects to it; the device never shows up in the OS "Bluetooth devices" list.
- No pairing / PIN. The link is unencrypted and the hotspot accepts
any client in radio range that knows the service UUID. (This is fine
for a desk hotspot; if yours is mobile / public, see
BLE.mdfor the bonded-mode flag.) - The OS radio just needs to be powered on so the app can use it.
All companion apps for every platform — macOS, Windows, Linux, Android, iPhone, iPad — are linked from the central download portal:
Analog-HotSPOT-App — standalone, works against any hotspot over
BLE with no reflector-side dependency. Talkgroups are defined by the
hotspot itself (MONITOR_TGS) and pushed to the app over the BLE feed.
Q: I have more than one hotspot — one at home, one in the car, a 4G one for the road. Can they all use my callsign?
Yes — but each hotspot needs its own unique callsign on the reflector. The reason isn't a simple name clash: SVXReflector authenticates every connection with a per-callsign certificate, and only one client can be logged in with that certificate at a time. Two hotspots configured with the same callsign would be trying to use the same certificate, and the reflector will refuse the second login (or kick the first one off, depending on how the sysop configured it).
The fix is a short suffix after a hyphen — up to 4 characters, your
choice. Example layout for OR7F:
| Hotspot | Callsign |
|---|---|
| 4G variant (mobile) | OR7F-LTE |
| Home / Wi-Fi box | OR7F-HOME |
| Car install | OR7F-CAR |
For each unique callsign you'll need to request a separate certificate signing from your reflector sysop — the cert is bound to the callsign, so a new callsign means a new cert. Plan the suffixes before you ask, so the sysop only has to sign once per box rather than chasing your edits.
Set the callsign via sudo hotspot-config on each hotspot, at the
SVXLINK Reflector User Callsign prompt. The value lands in
[ReflectorLogic] CALLSIGN= in /etc/svxlink/svxlink.conf.
What other operators see: on the reflector talker list each hotspot
appears with its full callsign including the suffix
(e.g. OR7F-LTE), so pick suffixes you're OK with being visible to
everyone on the network.
The companion app doesn't need its own callsign. Analog-HotSPOT-App talks to a hotspot over BLE and just remote-controls it — connections to the reflector all come from the hotspot itself under its configured callsign. You can run the app on as many of your devices (phone, tablet, Mac, …) as you like simultaneously without any reflector-side collision.
Edit base talkgroups:
sudo vi /var/www/html/include/tgdb.phpEdit talkgroup buttons:
sudo vi /var/www/html/include/config.inc.phpNote: the reflector certificate is only valid for 90 days and SVXLink renews it automatically as long as the hotspot is online and registered on the reflector. You normally don't need to back it up. This is mostly convenient when flashing a fresh SD card so you can skip the sysop-signing wait on the new install.
The reflector cert/key live under /var/lib/svxlink/pki/. Once your sysop
has signed it, back it up so you don't have to wait for re-signing if you
re-flash the SD card.
The hotspot already runs a local webserver at http://hotspot.local with
its document root at /var/www/html/ — drop the tarball there and pull
it from your laptop.
sudo tar czf /var/www/html/svxlink-pki-$(hostname)-$(date +%Y%m%d).tgz -C /var/lib/svxlink pkiDownload from the browser:
http://hotspot.local/svxlink-pki-<hostname>-<date>.tgz
Then immediately remove it from the web root — the private key is inside, and anyone on the same network can grab it while it sits there:
sudo rm -f /var/www/html/svxlink-pki-*.tgzTo restore on a fresh install (after running hotspot-config once so the
directory exists), copy the tarball back with scp using the default
hotspot / hotspot login.
From your laptop:
scp svxlink-pki-<hostname>-<date>.tgz hotspot@hotspot.local:/tmp/On Windows: modern Windows 10/11 ships scp via OpenSSH so the
command above works as-is in PowerShell. If you prefer a GUI, use
WinSCP — connect to hotspot.local on port 22
with username hotspot / password hotspot, then drag the .tgz file
into /tmp/ on the right-hand pane.
Then on the hotspot:
sudo tar xzf /tmp/svxlink-pki-*.tgz -C /var/lib/svxlink
sudo chown -R svxlink:svxlink /var/lib/svxlink/pki
sudo rm -f /tmp/svxlink-pki-*.tgz
sudo systemctl restart svxlink sudo rm -f /var/lib/svxlink/pki/*
sudo systemctl restart svxlinkConnect remotely to your hotspot.
https://connect.raspberrypi.com
Install rpi-connect
sudo apt -y install rpi-connect-lite
loginctl enable-linger
rpi-connect on
rpi-connect signinThe second USB port can be used for ethernet, or to attach an AMBE dongle for Bridging to DMR !
Extras useful when you're running the box as an actual repeater rather than a personal hotspot.
Watches GPIO16 (PTT) once per second. If the PTT line stays asserted (LOW) for more than 150 seconds, it assumes SVXLink is stuck in a keyed state and runs systemctl restart svxlink. While the pin is LOW, the script logs a countdown every 5 seconds.
Follow it live with:
sudo journalctl -t svxlink-watchdog -fInstall (no clone, no hotspot-config needed):
wget -qO- https://raw.githubusercontent.com/Guru-RF/Analog-HotSPOT-SVXLink/master/install-svxlink-watchdog.sh | sudo bashThe installer pulls the script + systemd unit, enables svxlink-watchdog.service, and starts it.
