A lightweight C++ hotspot bridge that connects a DV4mini USB dongle to a DMR master server using the MMDVM/Homebrew protocol.
The application moves DMR traffic in both directions:
DMR radio <-> DV4mini <-> dv4mini-hs <-> DMR master
It is designed for FreeDMR-compatible/Homebrew master connections and includes configurable talkgroup filtering, RF/TX tuning, RSSI monitoring, serial handling, and automatic reconnects.
- Bidirectional RF -> network and network -> RF DMR bridging
- MMDVM/Homebrew master login, authentication, configuration, and options exchange
- Windows and POSIX serial-port support at 115200 baud
- Configurable RX/TX frequency and independent frequency offsets
- Configurable DV4mini frequency byte order (
MSBorLSB) - Automatic RF receive detection for bit packing and polarity
- Configurable TX bit order and polarity
- Raw DMRD frame passthrough or sync/slot/embedded-field rewrite mode
- MS-sourced, BS-sourced, or direct-mode TX sync selection
- Firmware-buffered or host-paced network-to-RF transmission
- Configurable USB batching and repeated voice LC headers
- Primary, listen, and dial talkgroup handling
- Optional network talkgroup filtering and private-call filtering
- FreeDMR-style options generation when no explicit options string is supplied
- RSSI monitoring with an automatically learned idle baseline
- DV4mini LED status control
- Automatic DMR master reconnection
- Simulation mode for development/testing without a physical serial device
- Graceful shutdown with bridged-frame counters
- A C++11-capable compiler such as
g++ - GNU Make or a compatible
make - Access to the DV4mini serial device, typically
/dev/ttyACM0
No third-party C++ libraries are required by the project.
The included build.bat expects w64devkit in:
C:\w64devkit\bin
If your compiler is installed elsewhere, update the PATH line in build.bat or build with your own MinGW-w64 environment.
makeThe resulting executable is:
dv4mini-hs
Clean the build with:
make cleanRun:
build.bator, from a configured MinGW-w64 shell:
mingw32-make clean
mingw32-makeThe resulting executable is:
dv4mini-hs.exe
The program reads config.ini from the current directory by default.
Start from the included example and replace the callsign, DMR ID, serial device, master address, password, frequency, and talkgroups with values appropriate for your setup.
Example:
[DV4Mini]
Device=/dev/ttyACM0
Frequency=438200000
FrequencyByteOrder=MSB
TxBitOrder=MSB
TxInvert=false
TxFrameMode=PASSTHROUGH
TxSync=MS
TxFrameIntervalMs=60
TxFeedMode=FIRMWARE
TxUsbBatchPeriods=3
TxHeaderRepeats=8
RxOffsetHz=0
TxOffsetHz=0
TxBuffer=600
Power=3
Simulation=false
RSSIMonitor=true
[Hotspot]
DMRId=1234567
Callsign=N0CALL
Latitude=0
Longitude=0
Height=0
Location=Home
Description=DV4mini hotspot
Homepage=
TxPowerDbm=0
[Master]
Host=master.example.net
Port=62031
LocalPort=62032
Password=change-me
Reconnect=10
SendOptions=true
Options=
[DMR]
ColorCode=1
TimeSlot=2
Talkgroup=262
ListenTalkgroups=262,2626,26269
DialTalkgroup=0
TalkgroupTimer=10
AllowTxToListenTalkgroups=true
AllowPrivateCalls=true
FilterNetworkTalkgroups=false
[Logging]
Level=5On Windows, use a COM port instead, for example:
Device=COM10| Setting | Description |
|---|---|
Device |
Serial device. Default is COM3 on Windows and /dev/ttyACM0 elsewhere. |
Frequency |
Base RF frequency in Hz. |
FrequencyByteOrder |
Frequency command byte order: MSB or LSB. |
TxBitOrder |
TX bit packing. MSB selects MSB-first; other values are treated as LSB-first. |
TxInvert |
Invert transmitted DMR bits when true. |
TxFrameMode |
PASSTHROUGH preserves incoming 33-byte DMRD frames. REWRITE regenerates sync/slot/embedded fields. |
TxSync |
Sync used in rewrite mode: MS, BS, or DIRECT. |
TxFrameIntervalMs |
Host-paced frame interval; clamped to 20-100 ms. |
TxFeedMode |
FIRMWARE uses the DV4mini transmit buffer. HOST, HOSTPACED, or PACED enables host pacing. |
TxUsbBatchPeriods |
Number of 72-byte TX periods per USB write; clamped to 1-3. |
TxHeaderRepeats |
Voice LC header repeats before audio; clamped to 1-12. |
RxOffsetHz |
Added to the configured base frequency for RX tuning. |
TxOffsetHz |
Added to the configured base frequency for TX tuning. |
TxBuffer |
DV4mini TX buffer size in milliseconds. Valid range: 100-1500. |
Power |
DV4mini firmware power level. Valid range: 0-9. |
Simulation |
Run the DV4mini interface in simulation mode without opening a physical serial port. |
RSSIMonitor |
Enable RSSI baseline learning and periodic RF-level output. |
PASSTHROUGH is the recommended TX frame mode when the incoming DMRD frames already contain the correct on-air fields.
| Setting | Description |
|---|---|
DMRId |
Hotspot/repeater DMR ID. Must be greater than zero. |
Callsign |
Hotspot callsign; maximum 8 characters. |
Latitude |
Latitude from -90 to 90. |
Longitude |
Longitude from -180 to 180. |
Height |
Antenna/site height value sent to the master. |
Location |
Location text; maximum 20 characters. |
Description |
Description text; maximum 20 characters. |
Homepage |
Homepage URL; maximum 124 characters. |
TxPowerDbm |
TX power metadata reported to the master in dBm. |
| Setting | Description |
|---|---|
Host |
DMR master hostname or IP address. Required. |
Port |
Remote UDP port, normally 62031. |
LocalPort |
Local UDP port. Use 0 to let the OS choose automatically. |
Password |
Master-server password. |
Reconnect |
Seconds between reconnect attempts; minimum 1 second. |
SendOptions |
Send a master options string after connecting. |
Options |
Explicit options string. Leave empty to generate a FreeDMR-style string from the DMR settings. |
When SendOptions=true and Options is empty, the program builds an options string from the selected time slot, primary/listen talkgroups, dial talkgroup, and talkgroup timer.
| Setting | Description |
|---|---|
ColorCode |
DMR color code, 0-15. |
TimeSlot |
DMR time slot, 1 or 2. |
Talkgroup |
Primary group-call talkgroup. |
ListenTalkgroups |
Comma- or semicolon-separated list of additional talkgroups. |
DialTalkgroup |
Optional dial/dynamic talkgroup. Set to 0 to disable. |
TalkgroupTimer |
Timer value used when generating FreeDMR options. |
AllowTxToListenTalkgroups |
Allow RF-originated transmissions to talkgroups listed in ListenTalkgroups. |
AllowPrivateCalls |
Permit private calls in both directions. |
FilterNetworkTalkgroups |
When enabled, drop network group calls that are not in the local RX talkgroup list. |
Talkgroup IDs are limited to the 24-bit DMR ID range (1-16777215). Invalid entries in a talkgroup list are ignored by the INI parser.
Level controls console diagnostics. The source uses progressively more detailed output at higher levels, including additional serial/protocol diagnostics around levels 10 and 20.
With the default configuration file:
./dv4mini-hsOn Windows:
dv4mini-hs.exeUse a different INI file with -c:
./dv4mini-hs -c my-hotspot.iniDisplay command-line help:
./dv4mini-hs --helpStop the hotspot with Ctrl+C. The program closes the master connection, flushes the DV4mini, and prints the RF-to-network and network-to-RF frame totals before exiting.
Group calls are accepted for:
- the primary
Talkgroup - entries in
ListenTalkgroupswhenAllowTxToListenTalkgroups=true - TG9 when
DialTalkgroupis enabled
Other group calls are dropped. Private calls are dropped when AllowPrivateCalls=false.
When FilterNetworkTalkgroups=true, group calls are accepted only when they match the configured local receive list. The receive list is built from the primary talkgroup, listen talkgroups, and the dial-related talkgroups when configured.
Frames for the wrong time slot or frames that cannot be transmitted while RF is busy are rejected by the bridge.
TxFeedMode=FIRMWARENetwork frames are handed to the DV4mini without host-side frame throttling. The DV4mini TX buffer is controlled by TxBuffer.
TxFeedMode=HOST
TxFrameIntervalMs=60Network-to-RF frames are queued and released by the host at the configured interval. The queue is bounded; if it grows too large, the oldest frame is dropped.
With RSSIMonitor=true, the application learns an idle RSSI baseline from initial valid samples and reports changes relative to that baseline.
LED behavior reported by the application:
- Blue: idle / firmware-controlled state
- Red: TX / firmware-controlled state
- Green: decoded RF receive activity, controlled by the host
The green LED indicates decoded DMR activity, not merely RF energy.
If the program reports:
Unable to open DV4mini device '/dev/ttyACM0'.
check that the configured device exists and that your user has permission to open it. On many Linux distributions, serial devices are assigned to a group such as dialout or uucp.
Also verify that another application is not already using the DV4mini.
Check:
HostandPortPasswordDMRIdandCallsign- local firewall rules for UDP traffic
- whether
LocalPortconflicts with another process
The program prints each connection phase: disconnected, login, authentication, configuration, options, and connected.
Verify:
FrequencyByteOrder=MSBSome firmware variants may require LSB. Also check RxOffsetHz and TxOffsetHz.
Check the configured frequency and color code first. The receive parser automatically probes common bit-packing/polarity transformations, while the RSSI monitor can help distinguish RF energy from successfully decoded DMR frames.
Check:
TimeSlotFilterNetworkTalkgroups- the primary/listen talkgroup lists
AllowPrivateCallsTxFrameModeTxBitOrderandTxInvert- whether the RF side is already busy
Raise Logging.Level for additional diagnostics.
.
├── build.bat
├── config.ini
├── include/
│ ├── config.h
│ ├── dmrhotspot.h
│ ├── dv4mini.h
│ ├── homebrewclient.h
│ ├── homebrewpacket.h
│ ├── platform.h
│ ├── rs232lib.h
│ └── ...
├── resource/
│ ├── dv4mini.ico
│ └── dv4mini.rc
├── source/
│ ├── config.cpp
│ ├── dmrhotspot.cpp
│ ├── dv4mini-hotspot.cpp
│ ├── dv4mini.cpp
│ ├── homebrewclient.cpp
│ ├── homebrewpacket.cpp
│ ├── rs232lib.cpp
│ └── ...
├── LICENSE
└── makefile
Configure frequencies, power, talkgroups, IDs, and operating parameters for your own station and network. Make sure your use of the DV4mini and DMR frequencies complies with the licensing rules and band plan that apply in your location.
Do not publish a real master password in a public Git repository. Keep secrets in a local configuration file or use a private deployment workflow.
This project is released under the MIT License. See LICENSE for the full text.