Interface de surveillance DAB+ pour radio associative, basée sur un Raspberry Pi 4 et une clé RTL-SDR.
DAB+ Monitor permet de surveiller en continu un multiplex DAB+ : niveaux audio, DLS (titre en cours), slideshow, métriques RF (SNR, FIC quality, gain), alertes email en cas de silence ou de perte de service, et écoute du bouquet via un player intégré.
- Dashboard en temps réel : SNR, FIC quality, gain RTL-SDR, correction de fréquence (moyenne glissante 15s)
- Grille des services : tous les services du bouquet avec niveaux audio et DLS
- Panneau de détail : niveaux L/R, slideshow MOT, compteurs d'erreurs AAC/Frame/RS, infos techniques
- Player audio intégré : écoute du service sélectionné via Icecast2
- Mode veille automatique : arrêt du streaming après 15 min, surveillance seule active
- Alertes email : silence audio, perte de service, ensemble dégradé (avec rate limiting)
- Scanner Band III : découverte automatique des multiplex disponibles
- Statistiques uptime : taux de présence de chaque service sur 24h
- Historique alertes : 24h glissantes + base de données persistante
- Export/import de configuration
- Authentification par login/mot de passe (session unique)
RTL-SDR (NooElec NESDR SmartEE recommandé)
↓
welle-cli (fork welle-monitor)
↓ HTTP API /mux.json, /mp3/<SID>
DABPlusMonitor (Python)
├── Poll /mux.json toutes les 2s
├── Métriques RF + alertes email
├── Streaming Icecast2 via ffmpeg
└── API Flask / SSE → Dashboard
| Matériel | Statut | Notes |
|---|---|---|
| Raspberry Pi 4 (4 Go RAM) | ✅ Recommandé | Configuration de référence |
| NooElec NESDR SmartEE (R820T) | ✅ Recommandé | SNR élevé, stable 24h/24 |
| RTL-SDR Blog V4 (R828D) | Instabilité USB observée après plusieurs heures ; peut nécessiter le script de reset USB | |
| Hub USB alimenté (Lindy 43228) | ✅ Recommandé | Alimentation séparée pour le dongle RTL-SDR |
| Canal 9A — 202.928 MHz | ✅ Testé | LA ROCHE SUR YON (0xf030) |
Note RTL-SDR Blog V4 : des instabilités USB ont été observées après plusieurs heures de fonctionnement continu avec le Blog V4 sur Pi4. Le NooElec NESDR SmartEE (R820T) est plus stable en usage 24h/24. Le Blog V4 peut fonctionner avec le script de reset USB (
/usr/local/bin/rtlsdr-usb-reset.sh) configuré en sudoers.
- Debian 13 (Trixie) / Raspbian
- Python 3.11+
- welle-monitor (fork de welle.io avec améliorations pour le monitoring 24h/24)
- Icecast2
- ffmpeg
# Télécharger et lancer le script d'installation
curl -fsSL https://raw.githubusercontent.com/LyonelB/dabplus-monitor/main/install.sh -o install.sh
bash install.sh
⚠️ Ne pas utilisercurl ... | bash— la saisie du mot de passe nécessite un terminal interactif.
Le script installe et configure automatiquement :
- Dépendances système
- Règles udev RTL-SDR
- Mesure automatique du PPM (~2 min)
- Icecast2 (sans interaction)
- welle-monitor (compilation)
- DAB+ Monitor (venv, config, service systemd)
- Journal persistant + logrotate
À la fin de l'installation, ouvrez http://<ip>:5000 et lancez le scan Band III pour découvrir votre multiplex DAB+.
sudo apt install cmake g++ libfaad-dev libfftw3-dev \
librtlsdr-dev libmp3lame-dev libmpg123-dev libusb-1.0-0-dev xxd
git clone https://github.com/LyonelB/welle-monitor.git
cd welle-monitor/build
cmake .. -DRTLSDR=1 -DBUILD_WELLE_IO=OFF -DBUILD_WELLE_CLI=ON
make -j4
sudo cp welle-cli /usr/bin/welle-cliNécessaire pour le RTL-SDR Blog V4, optionnel pour les autres dongles :
sudo tee /usr/local/bin/rtlsdr-usb-reset.sh > /dev/null << 'EOF'
#!/bin/bash
for dev in /sys/bus/usb/devices/*/product; do
if grep -qi RTL "$dev" 2>/dev/null; then
devpath=$(dirname "$dev")
echo 0 > "$devpath/authorized"
sleep 3
echo 1 > "$devpath/authorized"
fi
done
EOF
sudo chmod +x /usr/local/bin/rtlsdr-usb-reset.sh
echo 'graffiti ALL=(ALL) NOPASSWD: /usr/local/bin/rtlsdr-usb-reset.sh' | \
sudo tee /etc/sudoers.d/rtlsdr-reset
sudo chmod 440 /etc/sudoers.d/rtlsdr-resetsudo apt install icecast2Configurer /etc/icecast2/icecast.xml avec un mountpoint /dabmonitor.
git clone https://github.com/LyonelB/dabplus-monitor.git
cd dabplus-monitor
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtCopier et adapter la configuration :
cp config.example.json config.json
nano config.jsonGénérer les credentials :
python3 -c "
import json
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt()
h = bcrypt.generate_password_hash('votre_mot_de_passe').decode('utf-8')
c = json.load(open('config.json'))
c.setdefault('auth', {}).update({'username': 'admin', 'password_hash': h})
json.dump(c, open('config.json','w'), indent=2)
print('OK')
"sudo cp dabplus-monitor.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable dabplus-monitor
sudo systemctl start dabplus-monitorLe fichier config.json contient :
{
"ensemble": {
"channel": "9A",
"frequency_mhz": 202.928
},
"rtl_sdr": {
"gain": -1,
"ppm_error": 0
},
"welle_cli": {
"port": 7979,
"poll_interval": 2,
"carousel_size": 1
},
"monitoring": {
"snr_threshold": 5.0,
"fic_quality_threshold": 80,
"audio_silence_threshold": -60,
"audio_silence_duration": 30,
"service_loss_duration": 60
},
"streaming": {
"enabled": true,
"icecast_url": "icecast://source:password@localhost:8000/dabmonitor",
"bitrate": "128k",
"output_rate": "44100"
},
"email": {
"enabled": false,
"smtp_server": "smtp.example.com",
"smtp_port": 587,
"sender_email": "[email protected]",
"sender_password": "app_password_16_chars",
"recipient_emails": ["[email protected]"],
"cooldown_minutes": 30,
"max_alerts_per_hour": 20
}
}0: mode-D(décode tous les services simultanément) — ~300% CPU sur Pi41: mode-C 1(carousel, un service à la fois) — ~65% CPU sur Pi4 ✅ recommandé
Le mot de passe doit être un mot de passe d'application (App Password) et non le mot de passe du compte. Pour Gmail : Compte Google → Sécurité → Validation en deux étapes → Mots de passe des applications.
Ce projet utilise welle-monitor, un fork de welle.io avec des améliorations spécifiques au monitoring 24h/24 :
std::timed_mutexsurrx_mut: résout le freeze HTTP en signal dégradéGET /status: endpoint léger pour le watchdog, jamais bloquantPOST /reset: redémarrage du décodeur sans tuer le processusPOST /carousel/pin/<sid>: force le décodage immédiat d'un serviceserver_timeetcurrent_carousel_siddansmux.jsontimed_mutexdanshandle_phs(): évite le freeze du thread de gestion des services
Une issue a été ouverte sur le dépôt upstream : welle.io #913 — freeze
/mux.jsonaprès ~92 min en mode carousel.
| Fichier | Contenu |
|---|---|
logs/gunicorn-error.log |
Logs applicatifs Python (INFO/WARNING/ERROR) |
logs/welle-cli.log |
Stdout/stderr de welle-cli |
logs/access.log |
Accès HTTP Gunicorn |
Rotation quotidienne via logrotate (/etc/logrotate.d/dabplus-monitor), 7 jours, compression.
GPL-2.0-or-later
- welle.io — Albrecht Lohofener & Matthias P. Braendli
- Développement DAB+ Monitor : Lyonel B. — Graffiti Radio