Control and monitor multiple RTL-SDR command line tools from the comfort of your web browser.
| public | ||
| package.json | ||
| README.md | ||
| screenshot.png | ||
| server.js | ||
📡 SDR-Commander
A modern web-based control panel for managing RTL-SDR dongles on headless Linux servers. Switch between multiple radio modes, decode pager messages, capture sensor data, and stream raw IQ samples—all from your browser.
Features
- Multi-Mode Operation — Seamlessly switch between pager decode, RTL_433, and RTL_TCP modes
- Real-Time WebSocket Updates — Live data streaming to your browser
- Persistent SQLite Database — All decoded messages and sensor readings are stored
- Telegram Notifications — Get pager alerts with flexible whitelist/blacklist filtering
- Modern Web UI — Clean, responsive interface with dark mode aesthetics
Operating Modes
| Mode | Description | Tools Used |
|---|---|---|
| 📟 Pager Decode | Decodes POCSAG pager messages (fire/EMS/hospital) | rtl_fm + multimon-ng |
| 🌡️ RTL_433 Decode | Scans 433 MHz for weather stations, sensors, TPMS | rtl_433 |
| 📶 RTL_TCP | Streams raw IQ data over network for remote SDR apps | rtl_tcp |
Note: The SDR dongle can only run one mode at a time. Switching modes automatically stops the previous service.
Requirements
Hardware
- RTL-SDR USB dongle (RTL2832U-based)
Software
- OS: Debian/Ubuntu Linux (tested on Debian Trixie)
- Node.js: 18+
- RTL-SDR tools:
rtl_fm,rtl_tcp - Decoders:
multimon-ng,rtl_433
Install Dependencies (Debian/Ubuntu)
sudo apt install rtl-sdr multimon-ng rtl-433
Installation
# Clone the repository
git clone https://git.wardnet.me/kurtis/SDR-Commander.git
# Install Node.js dependencies
npm install
# Start the server
npm start
The control panel will be available at http://your-server-ip:3000
Configuration
Configuration is stored in config.json (created automatically on first run):
{
"pager": {
"frequency": "152240000",
"gain": 40,
"sampleRate": 22050
},
"telegram": {
"enabled": false,
"botToken": "",
"chatId": "",
"topicId": "",
"whitelist": {
"capcodes": [],
"types": [],
"protocols": []
},
"blacklist": {
"capcodes": [],
"types": [],
"protocols": []
}
}
}
Telegram Notifications
- Create a bot with @BotFather on Telegram
- Get your Chat ID (use @userinfobot)
- Enter credentials in Settings tab or edit
config.json - Optionally configure whitelist/blacklist to filter notifications
Systemd Services
For production use, set up systemd services for automatic startup:
/etc/systemd/system/sdr-control.service
[Unit]
Description=SDR Control Panel
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/path/to/sdr-control
ExecStart=/usr/bin/node server.js
Restart=on-failure
[Install]
WantedBy=multi-user.target
sudo systemctl enable sdr-control
sudo systemctl start sdr-control
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/status |
GET | Current mode and service status |
/api/mode/:mode |
POST | Switch mode (pager, 433, tcp, stop) |
/api/config |
GET/POST | Read/update configuration |
/api/pager |
GET | Fetch pager messages (with pagination) |
/api/sensors |
GET | Fetch sensor readings (with pagination) |
/api/debug |
GET | System debug information |
/api/telegram/test |
POST | Send test notification |
WebSocket
Connect to /ws for real-time updates:
{ type: 'pager', data: {...} }— New pager message{ type: 'sensor', data: {...} }— New sensor reading{ type: 'log', data: {...} }— Debug log entry
Project Structure
sdr-control/
├── server.js # Express + WebSocket server
├── config.json # Runtime configuration (auto-generated)
├── sdr_data.db # SQLite database
├── package.json
└── public/
├── index.html # Main UI
├── app.js # Frontend JavaScript
└── styles.css # Styling
Database Schema
Pager Messages
| Column | Type | Description |
|---|---|---|
id |
INTEGER | Primary key |
timestamp |
DATETIME | Receive time |
capcode |
TEXT | Pager address |
protocol |
TEXT | POCSAG512/1200/2400 |
type |
TEXT | alpha/numeric |
message |
TEXT | Decoded content |
frequency |
TEXT | Receive frequency |
Sensor Data
| Column | Type | Description |
|---|---|---|
id |
INTEGER | Primary key |
timestamp |
DATETIME | Receive time |
model |
TEXT | Device model |
sensor_id |
TEXT | Device ID |
temperature |
REAL | Temperature reading |
humidity |
REAL | Humidity percentage |
battery |
TEXT | Battery status |
Tech Stack
- Backend: Node.js, Express, WebSocket (
ws) - Database: SQLite (
better-sqlite3) - Frontend: Vanilla HTML/CSS/JS
- SDR Tools: rtl_fm, multimon-ng, rtl_433, rtl_tcp
Built for headless SDR servers 🛰️
