Herramienta para capturar, extraer y decodificar tramas LoRa (RH_RF95 / RadioHead) usando un dongle RTL-SDR y gr-lora sobre GNU Radio.
Probado con RTL-SDR Blog V4 (R828D) y ESP32 + RFM95W transmitiendo con la librería RadioHead.
- RTL-SDR compatible (probado con RTL-SDR Blog V4)
- Antena 868 MHz (o la frecuencia que use tu módulo LoRa)
- Transmisor LoRa basado en RH_RF95 (ESP32 + RFM95W, por ejemplo)
sudo apt update
sudo apt install -y \
build-essential cmake git \
libusb-1.0-0-dev rtl-sdr librtlsdr-dev \
gnuradio python3-gnuradio \
liblog4cpp5-dev libboost-all-dev libgnuradio-runtime-dev \
swig python3-dev python3-pip python3-venv \
soapysdr-tools soapysdr-module-rtlsdrEl kernel carga por defecto el driver DVB-T que bloquea el acceso al dongle RTL-SDR. Hay que deshabilitarlo:
sudo tee /etc/modprobe.d/blacklist-rtlsdr.conf <<EOF
blacklist dvb_usb_rtl28xxu
blacklist rtl2832
blacklist rtl2830
EOF
sudo update-initramfs -uDesconectar y reconectar el dongle USB. Verificar:
rtl_test -tLa salida debe mostrar Found 1 device(s) y el nombre del tuner (R828D para Blog V4).
sudo tee /etc/udev/rules.d/20-rtlsdr.rules <<EOF
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE="0666", GROUP="plugdev"
EOF
sudo usermod -aG plugdev $USER
sudo udevadm control --reload-rulesReconectar el dongle y cerrar/abrir sesión para que el grupo aplique.
gr-lora requiere la librería liquid-dsp que no está disponible como paquete en Ubuntu 24.04+:
cd /tmp
git clone https://github.com/jgaeddert/liquid-dsp.git
cd liquid-dsp
./bootstrap.sh
./configure
make -j$(nproc)
sudo make install
sudo ldconfigVerificar:
find /usr/local -name "liquid.h"
# Debe mostrar: /usr/local/include/liquid/liquid.hcd ~/proyectos # o el directorio que prefieras
git clone https://github.com/rpp0/gr-lora.git
cd gr-lora
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install
sudo ldconfigVerificar:
python3 -c "import lora; print('gr-lora OK')"cd lora-sniffer # directorio de este proyecto
pip install -r requirements.txt# Dongle detectado
lsusb | grep -i rtl
# RTL-SDR funcional
rtl_test -t
# SoapySDR lo ve
SoapySDRUtil --probe="driver=rtlsdr"
# gr-lora importable
python3 -c "import lora; print('OK')"python3 lora_sniffer.py --duration 60Esto ejecuta los tres pasos en secuencia:
- Captura 60 segundos de señal IQ en 868 MHz
- Detecta y extrae las tramas LoRa del archivo de captura
- Decodifica cada trama con gr-lora y parsea el payload RadioHead
python3 lora_sniffer.py --capture-only --duration 30python3 lora_sniffer.py --input captura_lora.cu8python3 lora_sniffer.py --decode-only --input-dir ./tramas/| Parámetro | Default | Descripción |
|---|---|---|
--freq |
868000000 | Frecuencia central en Hz |
--sample-rate |
1000000 | Sample rate del RTL-SDR |
--gain |
40 | Ganancia del tuner en dB |
--sf |
7 | Spreading Factor LoRa (7–12) |
--bw |
125000 | Bandwidth LoRa en Hz |
--cr |
4 | Coding Rate (4 = 4/5) |
--duration |
60 | Duración de captura en segundos |
--output-dir |
./output | Directorio de salida |
--input |
— | Archivo .cu8 existente para analizar |
lora-sniffer/
├── README.md
├── requirements.txt
├── lora_sniffer.py # Script principal — proceso unificado
├── lib/
│ ├── __init__.py
│ ├── capture.py # Captura IQ con rtl_sdr
│ ├── extractor.py # Detección y extracción de tramas
│ ├── decoder.py # Decodificación gr-lora
│ └── parser.py # Parseo de payload RadioHead/RH_RF95
└── output/ # Directorio de salida (generado)
├── captura.cu8
├── espectrograma.png
├── tramas/
│ ├── trama_001.cf32
│ └── trama_002.cf32
└── resultados.json
RadioHead añade una cabecera de 4 bytes antes del payload del usuario:
[TO (1B)] [FROM (1B)] [ID (1B)] [FLAGS (1B)] [PAYLOAD...]
Por defecto TO=0xff y FROM=0xff (broadcast). El payload corresponde al struct Message del firmware:
struct Message {
uint32_t id; // ID del mensaje
uint32_t ts; // Timestamp en segundos (millis()/1000)
char text[56]; // Texto del mensaje
};No devices found: verificar blacklist DVB-T, reconectar dongle, comprobar lsusb.
import lora falla: verificar que se ejecutó sudo make install y sudo ldconfig en gr-lora.
liquid/liquid.h not found: instalar liquid-dsp desde source (sección 4).
-llog4cpp not found: ejecutar sudo apt install liblog4cpp5-dev.
Tramas detectadas pero sin decodificar: ajustar SF y BW a los parámetros del transmisor. Los defaults de RadioHead son SF7, BW125k.
Señal con offset de frecuencia: normal en RTL-SDR. gr-lora tiene corrección de drift. Si es muy grande, ajustar --freq unos kHz arriba o abajo.