iiOTESP32 Brasil
Modo de contingencia: las páginas públicas y las descargas siguen disponibles; el inicio de sesión, la comunidad y los datos del servidor pueden no estar disponibles.
Volver al catálogo de tutoriales
Ilustração: duas placas ESP32 com antena LoRa, uma em cada ponto de uma paisagem com lago e colinas, trocando sinal de rádio de longo alcance
Imagem ilustrativa gerada por IA
IntermedioIIoTLectura: 14 min

LoRa en el ESP32: comunicación punto a punto de largo alcance, sin LoRaWAN

Dos Heltec WiFi LoRa 32 V2 conversando a cientos de metros: SF, ancho de banda y tiempo en el aire, lo que medimos en laboratorio, las trampas de antena, cristal y ACK, y la pérdida de paquetes por secuencia.

Tecnologías:#ESP32#LoRa#SX1276#Heltec#RadioLib#915 MHz

Requisitos de hardware:

  • Duas placas Heltec WiFi LoRa 32 V2 (ESP32 + SX1276), versão 915 MHz, com antena
  • Dois cabos USB e o PlatformIO

LoRa no es Wi-Fi con más alcance

El Wi-Fi del ESP32 resuelve una sala, una casa, un galpón pequeño. Cuando el sensor está al otro lado del patio, en el tanque de agua del fondo del terreno o en un pozo sin energía ni router, el Wi-Fi se acaba, y ahí entra LoRa: una radio de largo alcance y bajo consumo, que lleva pocos bytes a cientos de metros, y en campo abierto a kilómetros.

El precio es la velocidad. LoRa es una modulación de radio (de Semtech) que cambia tasa de datos por sensibilidad: envías algunas decenas de bytes cada vez, de vez en cuando, a una tasa que va de cientos de bits por segundo a pocos kilobits. Sirve para temperatura, nivel, conteo, estado de una puerta, una alarma. No sirve para fotos, audio, actualizaciones grandes de firmware ni para nada que dependa de una conversación rápida de ida y vuelta.

Dos confusiones tropiezan a quien empieza, y conviene aclararlas pronto:

  • LoRa no es LoRaWAN. LoRa es la capa de radio. LoRaWAN es un protocolo de red encima de ella, con gateways, servidor, claves y reglas de uso. En este artículo trabajas con LoRa puro, de un ESP32 a otro, sin gateway y sin cuenta en ningún servicio. Es la forma más simple de entender la radio, y sirve a muchos proyectos de campo.
  • LoRa no tiene confirmación de entrega. Transmites al aire y quien esté escuchando con los mismos parámetros recibe. Si el paquete se pierde, nadie avisa. Quien necesita garantía construye la suya: un contador de secuencia y, si hace falta, una respuesta de vuelta.

Lo que hay dentro de la cuenta: factor de esparcimiento, ancho de banda y tiempo en el aire

Tres parámetros definen el comportamiento de la radio, y los dos lados deben usar los mismos, o no se entienden:

  • Factor de esparcimiento (SF, de 7 a 12). Cuanto mayor, más sensible el receptor y mayor el alcance, y mucho más lento el paquete.
  • Ancho de banda (BW). Lo común es 125 kHz. Más banda es más rápido, pero oye menos lejos.
  • Tasa de código (CR). Redundancia contra errores, de 4/5 a 4/8. Más redundancia tolera más interferencia y gasta más tiempo.

Lo que más sorprende es el tiempo en el aire. Para un paquete de 20 bytes con banda de 125 kHz y CR 4/5, el cálculo de Semtech da:

  • SF7: unos 57 ms, o unos 5,5 kbps
  • SF8: unos 103 ms
  • SF9: unos 185 ms
  • SF10: unos 371 ms
  • SF11: unos 741 ms
  • SF12: unos 1,3 s, o unos 300 bps

Cada paso de SF duplica el tiempo en el aire. Eso pesa en tres lugares: en la batería (la radio transmitiendo es lo que más gasta), en la probabilidad de colisión con otros dispositivos y en las reglas de uso del espectro. Elige el menor SF que aún cierra el enlace, y no el mayor por precaución.

Las trampas que queman un fin de semana

1. Transmitir sin antena. El amplificador de salida de un módulo LoRa fue hecho para ver una antena. Transmitir sin ella devuelve la energía al propio chip y puede dañarlo. Conecta la antena antes de encender la placa, siempre.

2. La frecuencia del módulo. Hay módulos de 433, 868 y 915 MHz, y la diferencia está en el acoplamiento de impedancia y el filtro: no es solo cambiar un número en el código. Muchas placas baratas vienen de tiendas de Europa, en 868 MHz. En Brasil, el módulo y el código deben quedar en la banda de 915 MHz. Comprueba lo que está escrito en la placa antes de comprar y antes de configurar.

3. Parámetros distintos. SF, ancho de banda, CR, frecuencia y la palabra de sincronismo (sync word) iguales en los dos lados. Un bit distinto y el receptor queda en silencio, sin ningún error.

4. La alimentación. El pico de corriente en la transmisión es alto para un sensor, del orden de un centenar de miliamperios a la potencia máxima. Una fuente débil, un cable fino o un regulador al límite tumban el ESP32 en el momento de transmitir, y el síntoma es un reinicio que solo aparece cuando la radio dispara. Un condensador cerca del módulo ayuda. En nuestras pruebas, el nodo (ESP32 con Wi-Fi y radio encendidos) tuvo picos de 250 a 350 mA, y un nodo con una fuente débil se reinició (brownout) en medio de un cambio de configuración. Cuidado también con ciertos power banks: se apagan solos cuando el consumo cae por debajo de 50 a 100 mA, y un nodo de bajo consumo es justamente eso.

5. Los pines del SPI. La radio conversa con el ESP32 por SPI, y los pines de chip select, de interrupción (DIO0) y de reset deben ser exactamente los que el código declara. Cada placa tiene los suyos: la Heltec WiFi LoRa 32 V2 no usa los pines estándar del SPI del ESP32, y la V3 cambia incluso el chip de radio. Lee el esquema de tu placa en lugar de copiar números de otro tutorial.

6. Las reglas de uso del espectro. La banda de 915 MHz es de uso regulado. Usa equipo adecuado, con la potencia dentro de lo que la regulación permite para tu aplicación, y comprueba la norma vigente antes de instalar algo en campo. En Europa, la banda de 868 MHz tiene un límite de tiempo en el aire por hora (el duty cycle del 1%); en Brasil las reglas son otras, pero la buena práctica vale en cualquier lugar: transmite solo lo necesario.

7. Cualquiera puede escuchar. Quien tenga un receptor con los mismos parámetros lee todo lo que transmites. LoRa puro no cifra ni autentica nada. No envíes datos sensibles, y no dejes que un comando de campo (encender una bomba, abrir una puerta) dependa de un paquete que cualquiera puede copiar y repetir.

8. El cristal que no es TCXO. El cristal de la Heltec V2 no está compensado en temperatura, y dos placas distintas quedan con la frecuencia un poco desplazada una de otra. Medimos una diferencia de unos 8 kHz entre las dos, unos 8,7 ppm. Con 125 kHz de ancho de banda el receptor tolera unos ±31 kHz y esto pasa sin notarse. Con bandas más estrechas, como 62,5 kHz, el margen se reduce a la mitad y la deriva con la temperatura en campo empieza a importar. Quien busque la ganancia teórica de 3 dB de una banda más estrecha necesita corregir la frecuencia (AFC) o usar módulos con TCXO.

9. El tiempo de espera del ACK. Si confirmas paquetes, la espera del ACK tiene que acompañar al SF. El SX1276 solo avisa "paquete recibido" después de demodular el último símbolo, y el símbolo dura 1 ms en el SF7 y unos 33 ms en el SF12. Un margen fijo de 150 ms, que funciona en el SF7, queda justo en un SF alto y genera retransmisiones. En nuestro proyecto, del 30 al 40% de los paquetes se retransmitían hasta que hicimos que el margen acompañara al símbolo; después, 8 comandos de 8 pasaron sin ninguna retransmisión.

Lo que necesitas

  • Dos placas Heltec WiFi LoRa 32 V2: ESP32, radio SX1276 y pantalla OLED en una sola pieza, en la versión de 915 MHz, con una antena cada una
  • Dos cables USB y PlatformIO, como en el artículo de primeros pasos en VS Code

Como la radio ya viene soldada a la placa, no hay ningún cable que conectar. Lo que existe es el pinout interno entre el ESP32 y el SX1276, y es el que el código debe declarar:

  • SCK en el GPIO 5, MISO en el GPIO 19, MOSI en el GPIO 27
  • NSS (chip select) en el GPIO 18
  • DIO0 en el GPIO 26 y DIO1 en el GPIO 35
  • RESET en el GPIO 14

Este pinout es de la V2. Otras versiones de Heltec (la V3, por ejemplo) traen otro chip de radio y otros pines: lee el esquema de tu placa antes de copiar los números. Quien use un módulo suelto, como el RFM95W, conecta las mismas señales a pines libres del ESP32 y ajusta las constantes.

Código: el proyecto

La biblioteca usada es RadioLib, que cubre los chips SX127x y SX126x con la misma interfaz. El platformio.ini:

code.cpp
cpp
1[env:heltec-v2]
2platform = espressif32
3board = heltec_wifi_lora_32_V2
4framework = arduino
5monitor_speed = 115200
6lib_deps =
7 jgromes/RadioLib@^7.0.0

Los parámetros que comparten los dos lados quedan al inicio de cada programa, y deben ser idénticos:

code.cpp
cpp
1#include <Arduino.h>
2#include <SPI.h>
3#include <RadioLib.h>
4const int PIN_SCK = 5;
5const int PIN_MISO = 19;
6const int PIN_MOSI = 27;
7const int PIN_NSS = 18;
8const int PIN_RST = 14;
9const int PIN_DIO0 = 26;
10const int PIN_DIO1 = 35;
11const float FREQ_MHZ = 915.0;
12const float BW_KHZ = 125.0;
13const uint8_t SF = 9;
14const uint8_t CR = 5;
15const uint8_t SYNC_WORD = 0x12;
16const int8_t POTENCIA_DBM = 10;
17SX1276 radio = new Module(PIN_NSS, PIN_DIO0, PIN_RST, PIN_DIO1);

El SPI de esta placa usa pines distintos del estándar del ESP32, por eso el SPI.begin() con los pines explícitos antes de iniciar la radio. La potencia de 10 dBm es a propósito: en el banco, con los dos módulos a un metro, la potencia máxima solo satura el receptor.

Código: el emisor

El emisor envía un número de secuencia y la temperatura interna del ESP32, solo para tener un dato que cambia:

code.cpp
cpp
1uint32_t seq = 0;
2void setup() {
3 Serial.begin(115200);
4 SPI.begin(PIN_SCK, PIN_MISO, PIN_MOSI, PIN_NSS);
5 int st = radio.begin(FREQ_MHZ, BW_KHZ, SF, CR, SYNC_WORD, POTENCIA_DBM, 8);
6 if (st != RADIOLIB_ERR_NONE) {
7 Serial.printf("radio.begin falhou: %d\n", st);
8 while (true) delay(1000);
9 }
10 radio.setCRC(true);
11}
12void loop() {
13 char msg[32];
14 snprintf(msg, sizeof(msg), "%lu,%.1f", (unsigned long)seq, temperatureRead());
15 int st = radio.transmit(msg);
16 Serial.printf("[TX] %s -> %s (%d)\n", msg, st == RADIOLIB_ERR_NONE ? "ok" : "falhou", st);
17 seq++;
18 delay(10000);
19}

transmit() bloquea hasta que el paquete sale, lo que lleva cientos de milisegundos en SF9. Para una demostración, está bien. En un proyecto con otras tareas, la transmisión debe quedar fuera del bucle principal.

Código: el receptor

El receptor no hace bucle de espera: el módulo avisa por el pin DIO0 cuando llega un paquete, y el loop solo trata el aviso. Es el mismo principio del artículo de reconexión MQTT: nada bloquea el firmware esperando al mundo.

code.cpp
cpp
1volatile bool chegou = false;
2uint32_t ultimaSeq = 0;
3uint32_t perdidos = 0;
4bool primeiro = true;
5void IRAM_ATTR aoChegarPacote() {
6 chegou = true;
7}
8void setup() {
9 Serial.begin(115200);
10 SPI.begin(PIN_SCK, PIN_MISO, PIN_MOSI, PIN_NSS);
11 int st = radio.begin(FREQ_MHZ, BW_KHZ, SF, CR, SYNC_WORD, POTENCIA_DBM, 8);
12 if (st != RADIOLIB_ERR_NONE) {
13 Serial.printf("radio.begin falhou: %d\n", st);
14 while (true) delay(1000);
15 }
16 radio.setCRC(true);
17 attachInterrupt(digitalPinToInterrupt(PIN_DIO0), aoChegarPacote, RISING);
18 radio.startReceive();
19}

Y el tratamiento del paquete, con la cuenta de pérdidas por el número de secuencia:

code.cpp
cpp
1void loop() {
2 if (!chegou) return;
3 chegou = false;
4 String texto;
5 int st = radio.readData(texto);
6 if (st == RADIOLIB_ERR_NONE) {
7 uint32_t seq = strtoul(texto.c_str(), nullptr, 10);
8 if (!primeiro && seq > ultimaSeq + 1) perdidos += seq - ultimaSeq - 1;
9 primeiro = false;
10 ultimaSeq = seq;
11 Serial.printf("[RX] %s rssi=%.0f snr=%.1f perdidos=%lu\n", texto.c_str(), radio.getRSSI(), radio.getSNR(), (unsigned long)perdidos);
12 } else if (st == RADIOLIB_ERR_CRC_MISMATCH) {
13 Serial.println("[RX] pacote corrompido (CRC)");
14 } else {
15 Serial.printf("[RX] erro %d\n", st);
16 }
17 radio.startReceive();
18}

Dos cosas merecen comentario. El RSSI es la potencia recibida, en dBm, y cuanto más negativo, más débil la señal. El SNR es la relación señal-ruido: LoRa decodifica señal por debajo del ruido, así que un SNR negativo, dentro de los límites del SF, aún funciona. Y el startReceive() del final reabre el oído: sin él, el módulo recibe un paquete y se queda sordo.

Tutorial: de cero al primer paquete

Paso 1. Monta los dos conjuntos, con antena en cada módulo, y deja las placas a unos dos metros una de otra.

Paso 2. Graba el programa del emisor en una placa y el del receptor en la otra. Si compilas los dos en el mismo proyecto, usa dos entornos en el platformio.ini, cada uno con su archivo principal.

Paso 3. Abre el monitor serie de cada una. En el emisor, cada diez segundos:

code.cpp
cpp
1[TX] 0,41.7 -> ok (0)
2[TX] 1,41.9 -> ok (0)

En el receptor, lo que prueba el enlace:

code.cpp
cpp
1[RX] 0,41.7 rssi=-52 snr=9.8 perdidos=0
2[RX] 1,41.9 rssi=-53 snr=9.5 perdidos=0

Tus valores de RSSI variarán; lo que importa es que aparezca el paquete con los mismos números de la otra placa.

Paso 4. Qué hacer si no llega nada. El emisor imprime "ok" y el receptor no escribe nada: comprueba, en este orden, los parámetros iguales en los dos lados (frecuencia, SF, ancho de banda, CR y sync word), las antenas conectadas, y el módulo en la frecuencia correcta. Si radio.begin falla con un error, el problema es el SPI: el código de error y el pinout dicen dónde buscar, y un chip que no responde suele ser un cable suelto, la alimentación o un pin equivocado.

Paso 5. Mide el alcance de verdad. Lleva el receptor lejos, con la antena en vertical, y ve alejándote mientras observas el RSSI y la cuenta de perdidos. En un ambiente con obstáculos, el alcance real es mucho menor que el de los folletos, que miden en campo abierto, con línea de vista. Registra los resultados de tu terreno. Una precaución: el RSSI solo existe cuando llega un paquete. En SF12, con un envío por minuto, el número en la pantalla solo cambia cada minuto y la prueba de alcance parece colgada. Durante la caminata, envía cada pocos segundos (unos 8 s) y vuelve al intervalo normal después: en nuestras pruebas esto ocupa un 30% del tiempo en el aire y gasta más corriente.

Paso 6. Cambia el SF. Sube el SF de 9 a 11 en los dos lados y compara el RSSI mínimo con el que el paquete aún llega. Verás la ganancia de alcance y su costo: el tiempo en el aire se cuadruplica respecto al SF9, y la batería lo siente.

Lo que medimos en laboratorio con dos Heltec V2

Estos números salieron de un proyecto propio con dos Heltec V2 (915 MHz), en pruebas de laboratorio y no en una instalación real. Valen para ese ambiente y esa antena, y no como promesa para el tuyo:

  • Sensibilidad típica del SX1276 con 125 kHz y CR 4/5: unos −123 dBm en el SF7, −129 dBm en el SF9, −134 dBm en el SF11 y −137 dBm en el SF12.
  • Tiempo en el aire: un paquete de 56 bytes lleva unos 100 ms en el SF7 y 2,6 s en el SF12.
  • Alcance (prueba de laboratorio, no de campo): con una placa dentro de un edificio y la otra llevada a pie, el alcance horizontal fue de unos 50 m en el SF7 a unos 150 m en el SF12, tres veces más. El cálculo teórico, para ambiente interior, preveía unas 3,7 veces. El ambiente urbano con obstáculos quedó muy por debajo de los kilómetros de los folletos.
  • El límite aparece en los números: en el punto más lejano el RSSI pasó de −128 dBm y el SNR llegó a −20 dB, que es el límite del SF12. Ahí cayó el enlace.
  • Ruido del canal: unos −121 dBm, casi sin tiempo por encima de −105 dBm: canal limpio.
  • Actualización del RSSI: en el perfil de SF12 el nodo enviaba cada 60 s (más unos 20 s de reintentos) y la señal se actualizaba cada 62 s. Con un modo de caminata que envía cada unos 8 s, pasó a cada 10 s (entre 9 y 14 s), seis veces más rápido, al costo de un 30% del tiempo en el aire. Un promedio móvil que daba un peso del 25% al paquete nuevo retrasaba aún más; subirlo al 50% ayudó.
  • Cambio de SF por el aire (una placa manda el comando, la otra cambia): del SF7 al SF12 funcionó, cada cambio en 2 a 4 s, con vuelta automática al perfil anterior si la confirmación no llega.

Lo que esto enseña, en orden: el SF ayuda y cuesta tiempo en el aire; el ambiente manda más que el SF; y todo temporizador de tu protocolo (ACK, reintento, "nodo desconectado") debe acompañar el tiempo en el aire del SF elegido.

Cuando algo no funciona

Reinicio cada vez que transmite. La alimentación no aguanta el pico de corriente. Usa una fuente mejor, cables más cortos y un condensador en el módulo.

Llegan paquetes con CRC inválido. Interferencia, antena mala o enlace al límite. Acércate, cambia la antena, sube el SF.

Funcionó un día y se detuvo. Antena floja u oxidada, batería débil u otro dispositivo en la misma frecuencia. Mide el RSSI de nuevo y compáralo con la primera prueba.

El receptor informa perdidos todo el tiempo. Puede ser colisión con otro transmisor, el receptor sordo por falta de startReceive() o el enlace al límite. Aumentar el intervalo entre paquetes disminuye la probabilidad de colisión.

Qué no cubre este artículo

  • LoRaWAN. Gateway, servidor de red, claves y el plan de frecuencias regional (en Brasil, AU915). Es el camino cuando hay muchos dispositivos e internet en medio, y merece un artículo propio.
  • Confirmación y retransmisión. El contador de secuencia detecta la pérdida, pero no la corrige. Un ACK de vuelta, con tiempo límite y nuevo intento, es el paso siguiente, y la trampa 9 muestra por qué el tiempo límite debe acompañar al SF. Es también la hora de pensar cuánto tiempo en el aire puede gastar tu proyecto.
  • Cifrado y autenticación del paquete. Necesarios cuando el dato o el comando importan. El ESP32 tiene aceleración de AES en hardware, que ayuda.
  • Bajo consumo. El ESP32 en sueño profundo despertando solo para transmitir es lo que hace que una batería dure meses, y depende de un diseño de energía cuidadoso.

Nota de honestidad

Lo que se dice sobre la radio (alcance, sensibilidad, tiempo en el aire, cristal, ACK, alimentación) viene de un proyecto propio probado en laboratorio en dos placas Heltec WiFi LoRa 32 V2, en ESP-IDF y con un driver propio, no de un folleto. Fue una prueba de laboratorio: aún no hay validación en una instalación de campo. El código de ejemplo de este artículo es otro: una versión mínima en Arduino con la biblioteca RadioLib, escrita para ser leída, y, aunque la versión del kit compila con PlatformIO para los dos programas (emisor y receptor), no fue ejecutada en placa. Los nombres de las funciones pueden cambiar entre versiones de la biblioteca. Antes de llevarlo a campo, compílalo en tu entorno, confirma el pinout de tu módulo y mide el alcance en tu terreno. Las referencias a la regulación son orientaciones generales: la norma vigente de Anatel es la que manda.

Para llevar el dato recibido más lejos, el receptor puede publicar en MQTT, como en el artículo ESP32 publicando datos en Mosquitto, y con TLS si la red no es de confianza. Y para que el emisor sobreviva a los fallos, mira el watchdog.

Ver más tutoriales técnicosIIoT ESP32 Brasil — Documentación práctica