Hotspot Setup & Configuration
What is the role of the dvmhost?
- Host software that connects to the DVM modems (both air interface for repeater and hotspot or P25 DFSI for commerical P25 hardware) and is the primary data processing application for digital modes. See configuration to configure and calibrate.
Prerequisites
- An Internet connect Raspberry Pi running Raspbian 12 (Bookworm) with a MMDVM hat flashed with the latest firmware from the DVMProject.
- This guide will assume it is a Raspberry Pi Model 3B+
- This guide will assume that there are no
iptable
rules or other firewalls currently installed within Raspbian 12.
- The
dvmhost
binary from the latestdvmhost
release for your CPU architecture.- At the time of writing, R04H31 2025-05-25 was the latest release.
Connect to the Raspberry Pi & Download The dvmhost Release For Your CPU Architecture
- Download the archive from the DVMPRoject GitHub
ssh user@raspberrypi
cd /opt/
sudo wget https://github.com/DVMProject/dvmhost/releases/download/2025-05-25/dvmhost-2025-05-25-arm.tar.gz
- Extract & remove the archive
sudo tar zxvf dvmhost-2025-05-25-arm.tar.gz
# Optional step
sudo rm /opt/dvmhost-2025-05-25-arm.tar.gz
Organize the working directory
cd /opt/dvm/
sudo mkdir /opt/dvm/examples/
sudo mv /opt/dvm/*.example.* /opt/dvm/examples/
Copy the following example configurations as live versions.
sudo mkdir /opt/dvm/rules/
sudo cp /opt/dvm/examples/rid_acl.example.dat /opt/dvm/rules/rid_acl.dat
sudo cp /opt/dvm/examples/talkgroup_rules.example.yml /opt/dvm/rules/talkgroup_rules.yml
sudo cp /opt/dvm/examples/peer_list.example.dat /opt/dvm/rules/peer_list.dat
Review & Populate the iden_table.dat
The example iden_table.dat
includes the UHF R2 range, if you are using frequencies that fall into this range, there is no additional calculation needed and you will use 2
as the channelId
later in config.yml
.
If you need to calculate a frequency pair, perform the following steps:
- Navigate to: https://dvmproject.io/iden-calc-web/
- Enter the appropriate values into each of the following fields:
- Downlink Frequency (MHz)
- This field represents the frequency on which the hotspot will receive transmissions.
- Base Frequency (MHz)
- This field defines a starting or reference frequency for a specific block or range of channels within the P25 system's frequency plan. It is used in a formula, along with the Channel ID and Spacing, to calculate the specific frequency for a given channel.
- Spacing (kHz)
- This field indicates the separation between the center frequencies of adjacent channels, often called the channel step or channel raster. It is typically measured in kilohertz (kHz) (e.g., 12.5 kHz, 6.25 kHz for P25). This value is crucial for calculating the exact frequency of a channel based on its ID and the base frequency.
- Offset (MHz)
- This field most commonly refers to the standard frequency difference between the uplink and downlink frequencies for a repeater channel pair in a specific band (e.g., +5 MHz or -5 MHz). It is measured in megahertz (MHz) and ensures that radios transmit on one frequency and receive on another, allowing for simultaneous transmission and reception through a repeater.
- Channel ID (dec)
- This field is a numerical identifier for a specific radio channel within the P25 system. This decimal value is typically used in a formula with the Base Frequency and Spacing to determine the precise operational frequency (often the downlink) for that channel.
- Uplink Frequency (MHz)
- Calculated automatically.
- Downlink Frequency (MHz)
Copy the base configuration file to the working directory.
sudo cp /opt/dvm/examples/config.example.yml /opt/dvm/config.yml
Define the settings for P25 communication in config.yml
sudo nano /opt/dvm/config.yml
These dotted paths below represent nodes within the YAML syntax of fne-config.yml
- network.address: [ADDRESS-OF-FNE]
- network.id: [6-DIGIT-ID-OF-HOTSPOT]
- network.password: [PASSWORD-FROM-FNE-CONFIG.YML-ON-FNE]
- system.identity: MYHOTSPOT
- protocols.dmr.enable: false
- protocols.nxdn.enable: false
- system.modem.protocol.type: uart
- system.modem.protocol.mode: air
- system.modem.protocol.uart.port: /dev/ttyAMA0
- system.modem.protocol.uart.speed: 115200
- system.config.channelId: 2
- If you defined your own
iden_table.dat
use your associatedchannelId
- If you defined your own
- system.config.channelNo: [VALUE-CALCULATED-FROM-IDEN-CALC-WEB]
- system.config.voiceChNo.channelId: 2
- If you defined your own
iden_table.dat
use your associatedchannelId
- If you defined your own
- system.config.voiceChNo.channelNo: [VALUE-CALCULATED-FROM-IDEN-CALC-WEB]
- systtem.config.iden_table.file: /opt/dvm/rules/iden_table.dat
- system.config.radio_acl.file: /opt/dvm/rules/radio_acl.dat
- system.config.talkgroup_id.file: /opt/dvm/rules/talkgroup_rules.yml
Optional: Enable ACLs based on radio ID or talkgroup ID
sudo nano /opt/dvm/config.yml
- Radio ID
- system.config.radio_id.acl: true
- Talkgroup ID
- system.config.talkgroup_id.acl: true
Verify settings in the TUI
Note: You can use your mouse in the TUI to click through the menu's and visually confirm the settings set in config.yml
sudo /opt/dvm/bin/dvmhost -c /opt/dvm/config.yml --setup
Connect to the modem
- Press F8
- The modem should initialize and connect successfully.
- This is where alignment would take place if necessary. Alignment is generally needed if the Hotspot does not activate when receiving a transmission from a subscriber radio. At this time this guide will not cover offsets or alignments on the hotspot.
- Press F2
- This will save your current settings and "flatten" your
config.yml
file removing any comments.
- This will save your current settings and "flatten" your
- Press F3
- This will take you back to the console.
Example: Finalized Config.yml
daemon: true
log:
activityFilePath: .
disableNonAuthoritiveLogging: false
displayLevel: 1
fileLevel: 1
filePath: /opt/dvm/log
fileRoot: DVM
useSyslog: false
network:
address: [REDACTED]
allowActivityTransfer: true
allowDiagnosticTransfer: true
allowStatusTransfer: true
debug: false
enable: true
encrypted: false
id: 9039148
jitter: 360
password: [REDACTED]
port: 62031
presharedKey: [REDACTED]
restAddress: 127.0.0.1
restDebug: false
restEnable: false
restPassword: PASSWORD
restPort: 9990
restSsl: false
restSslCertificate: web.crt
restSslKey: web.key
rpcAddress: 127.0.0.1
rpcDebug: false
rpcPassword: "ULTRA-VERY-SECURE-DEFAULT"
rpcPort: 9890
saveLookups: false
slot1: true
slot2: true
updateLookups: false
protocols:
dmr:
backOff: 1
beacons:
duration: 3
enable: false
interval: 60
callHang: 5
control:
dedicated: false
disableGrantSourceIdCheck: false
enable: false
slot: 1
convNetGrantDemand: false
debug: false
disableNetworkGrant: false
disableUnitRegTimeout: false
dumpCsbkData: false
dumpDataPacket: false
dumpTAData: true
embeddedLCOnly: false
enable: false
frameLossThreshold: 2
ignoreAffiliationCheck: false
nRandWait: 8
queueSize: 31
repeatDataPacket: true
silenceThreshold: 21
txHang: 8
verbose: true
verifyReg: false
nxdn:
callHang: 5
control:
broadcast: true
dedicated: false
disableGrantSourceIdCheck: false
duration: 1
enable: false
interval: 300
debug: false
disableUnitRegTimeout: false
dumpRcchData: false
enable: false
frameLossThreshold: 4
ignoreAffiliationCheck: false
queueSize: 31
silenceThreshold: 14
verbose: true
verifyAff: false
verifyReg: false
p25:
allowExplicitSourceId: true
callHang: 5
control:
ackRequests: true
broadcast: true
dedicated: false
disableAdjSiteBroadcast: false
disableGrantSourceIdCheck: false
disableTSDUMBF: false
duration: 1
enable: false
enableTimeDateAnn: false
interval: 300
notifyActiveTG: false
redundantGrantTransmit: false
redundantImmediate: true
controlOnly: false
convNetGrantDemand: false
debug: false
demandUnitRegForRefusedAff: true
disableNetworkGrant: false
disableNetworkHDU: false
disableUnitRegTimeout: false
dumpDataPacket: false
dumpTsbkData: false
enable: true
frameLossThreshold: 6
ignoreAffiliationCheck: false
inhibitUnauthorized: false
legacyGroupGrnt: true
legacyGroupReg: false
noMessageAck: true
noStatusAck: false
queueSize: 12
repeatDataPacket: true
requireLLAForReg: false
silenceThreshold: 124
sndcpSupport: false
tduPreambleCount: 6
unitToUnitAvailCheck: false
verbose: true
verifyAff: false
verifyReg: false
system:
activeTickDelay: 5
config:
announcementGroup: FFFE
authoritative: true
channelId: 2
channelNo: 8c0
colorCode: 1
controlCh:
notifyEnable: true
presence: 120
rpcAddress: 127.0.0.1
rpcPassword: "ULTRA-VERY-SECURE-DEFAULT"
rpcPort: 0
dmrNetId: 1
nac: 293
netId: BB800
pSuperGroup: FFFE
ran: 1
rfssId: 1
secure:
key: 000102030405060708090A0B0C0D0E0F
siteId: 1
supervisor: false
sysId: 001
voiceChNo:
- channelId: 2
channelNo: 8c0
rpcAddress: 127.0.0.1
rpcPassword: "ULTRA-VERY-SECURE-DEFAULT"
rpcPort: 9890
cwId:
callsign: ABCD123
enable: true
time: 15
disableWatchdogOverflow: false
duplex: true
fixedMode: false
iden_table:
file: /opt/dvm/rules/iden_table.dat
time: 30
identity: W25HS02
idleTickDelay: 5
info:
height: 1
latitude: "-83.689428"
location: "Repeater Site, Antarctica"
longitude: "-39.194973"
power: 10
localTimeOffset: 0
modeHang: 10
modem:
cosLockout: false
dcBlocker: true
debug: false
dfsi:
callTimeout: 200
dfsiTIAMode: false
diu: true
fsc: false
fscHeartbeat: 5
initiator: false
jitter: 200
rtrt: true
disableOFlowReset: false
dmrFifoLength: 560
dmrRxDelay: 7
dumpModemStatus: false
fdmaPreamble: 80
hotspot:
adfGainMode: 2
afcEnable: true
afcKI: 13
afcKP: 5
afcRange: 1
dmrDiscBWAdj: 0
dmrPostBWAdj: 0
nxdnDiscBWAdj: 0
nxdnPostBWAdj: 0
p25DiscBWAdj: 0
p25PostBWAdj: 0
rxTuning: 0
txTuning: 0
ignoreModemConfigArea: false
nxdnFifoLength: 538
nxdnFifoLenth: 538
p25CorrCount: 8
p25FifoLength: 522
packetPlayoutTime: 10
protocol:
mode: air
type: uart
uart:
port: /dev/ttyAMA0
speed: 115200
pttInvert: false
repeater:
dmrSymLvl1Adj: 0
dmrSymLvl3Adj: 0
nxdnSymLvl1Adj: 0
nxdnSymLvl3Adj: 0
p25SymLvl1Adj: 0
p25SymLvl3Adj: 0
rssiMappingFile: RSSI.dat
rxDCOffset: 0
rxInvert: false
rxLevel: 50
softpot:
rssiCoarse: 127
rssiFine: 127
rxCoarse: 127
rxFine: 127
txCoarse: 127
txFine: 127
trace: false
txDCOffset: 0
txInvert: false
txLevel: 50
radio_id:
acl: false
file: /opt/dvm/rules/rid_acl.dat
time: 2
rfTalkgroupHang: 10
simplexSameFrequency: false
talkgroup_id:
acl: false
file: /opt/dvm/rules/talkgroup_rules.yml
time: 2
timeout: 180
Example: Finalized iden_table.dat
#
# This file sets the valid bandplan identity table.
# (It is recommended to use the included iden_channel_calc.py Python script
# to generate entries for this file.)
#
# ChId,Base Freq,Spacing (khz),Input Offset (mhz),Bandwidth (khz),<newline>
#0,851006250,6.25,-45.000,12.5,
#1,762006250,6.25,30.000,12.5,
#15,935001250,6.25,-39.00000,12.5,
2,450000000,6.25,5.000,12.5,
#3,146000000,6.25,1.000,12.5,
Start the dvmhost process as a daemon.
/opt/dvm/start-dvm.sh /opt/dvm/config.yml
If the start is successful, you should see an entry as denoted in Log Entry Examples subsection, Peer Connecting.
Configure dvmhost to start as a system service.
sudo nano /etc/systemd/system/dvmhost.service
dvmhost.service Example
[Unit]
Description=dvmhost
After=network.target
[Service]
ExecStart=/opt/dvm/bin/dvmhost -c /opt/dvm/config.yml -f
User=root
Type=forking
Restart=on-abnormal
TimeoutSec=infinity
[Install]
WantedBy=multi-user.target
Enable and start the dvmhost service
sudo systemctl daemon-reload
sudo systemctl enable dvmhost.service
sudo systemctl start dvmhost.service