Transmisión LoRa entre dos ESP32 con FreeRTOS

Resumen

Transmitir mensajes LoRa entre dos ESP32 implica algo más que llamar a una función de envío. Necesitas coordinar la antena para que no envíe y reciba al mismo tiempo, y ahí es donde FreeRTOS se vuelve tu mejor aliado para pausar y reanudar tareas con precisión.

Esta guía es para ti si estás construyendo proyectos de comunicación inalámbrica punto a punto y quieres entender cómo estructurar el código de transmisión sin que tu aplicación crashee.

¿Por qué no puedes enviar y recibir LoRa al mismo tiempo?

La radio LoRa usa una sola antena física, así que intentar transmitir mientras escuchas genera un conflicto de hardware. Si ejecutas las tareas TX y RX en paralelo sin coordinarlas, la aplicación se reinicia sola.

La solución es suspender la tarea de recepción justo antes de transmitir y reanudarla cuando el envío termina. Para eso usas dos funciones que vienen con FreeRTOS: vTaskSuspend y vTaskResume [02:50].

¿Qué hace vTaskSuspend en FreeRTOS? Pausa una tarea específica usando su handle como referencia, hasta que otra parte del código la reanude con vTaskResume. Así evitas conflictos entre tareas que comparten un mismo recurso, como la antena LoRa.

¿Cómo se estructura la función de envío de paquetes LoRa?

La librería expone lora.sendPacket, que recibe dos argumentos: el buffer del mensaje codificado en uint8_t y el tamaño en bytes como entero [01:30]. No retorna nada, solo ejecuta el envío.

Dentro de la tarea TX, el flujo queda así:

  • Imprimes un mensaje de log tipo Send packet para saber qué está pasando.
  • Suspendes la tarea de recepción con su handle.
  • Llamas a lora.sendPacket con el mensaje convertido a uint8_t*.
  • Reanudas la tarea de recepción.
  • Imprimes confirmación del envío.

Un detalle clave: cuando conviertes la cadena de texto al tipo uint8_t, no olvides el asterisco del puntero. Si lo olvidas, el compilador lanza un warning porque estás pasando una lista de caracteres, no un solo carácter [07:45].

¿Qué es un TaskHandle y por qué lo necesitas?

Un TaskHandle_t es una variable global que guarda la referencia a una tarea creada con FreeRTOS. Sin ella, no puedes decirle a vTaskSuspend o vTaskResume cuál tarea controlar.

Declaras la variable, por ejemplo xHandleRxTask, en el bloque de variables globales y la pasas como puntero al crear la tarea de recepción. Si olvidas el puntero al crearla, las funciones de suspend y resume no funcionan, aunque el código compile.

¿Cómo encapsular el envío en una función reutilizable?

Meter toda la lógica directamente dentro de la task funciona, pero te limita. Más adelante vas a querer enviar mensajes desde otras partes del código, no solo desde una task de FreeRTOS.

La solución es crear una función sendMsg que reciba el mensaje como char* y el tamaño como entero [05:30]. Dentro de ella metes la secuencia completa: suspender RX, enviar, reanudar RX, imprimir.

c void sendMsg(char* msg, int size) { vTaskSuspend(xHandleRxTask); lora.sendPacket((uint8_t*)msg, size); vTaskResume(xHandleRxTask); printf("Sent: %s\n", msg); }

Así, llamar sendMsg("ping", 5) desde cualquier lado del programa te garantiza que el ciclo de envío sea seguro.

¿Qué delay usar entre transmisiones LoRa?

El for infinito sin pausa provoca un reinicio del ESP32. Necesitas un vTaskDelay al final del ciclo para liberar el procesador.

Para pruebas, un delay de 2000 milisegundos funciona mejor que el mínimo de 1, porque te da tiempo de leer la terminal entre mensajes [06:50]. Recuerda coordinarlo con pdMS_TO_TICKS o el equivalente para convertir milisegundos a ticks del sistema.

¿Por qué mi ESP32 se reinicia con un for vacío? Porque un ciclo infinito sin vTaskDelay bloquea el watchdog de FreeRTOS, que interpreta la falta de respuesta como un cuelgue y fuerza el reinicio.

¿Cómo verificar que la recepción LoRa funciona?

Después de cargar el programa en las dos tarjetas, una en COM7 y otra en COM9, abres el monitor en ambas terminales. Si solo ves los logs de envío pero nada de recepción, no significa que no esté llegando: significa que te falta un printf dentro de la tarea RX [09:30].

Agrega un print que muestre el contenido y la longitud:

  • receive msg: %s len: %d\n
  • Pasas el buffer recibido y el entero del tamaño.
  • El terminador nulo de la cadena delimita la impresión hasta el largo real.

Con ese cambio, ambas tarjetas confirman que envían y reciben ping cada dos segundos.

¿Qué puedes construir ahora con LoRa punto a punto?

El mensaje viaja como cadena de texto, pero por debajo es una señal de radio binaria. Eso significa que el formato es libre: sensores ambientales, actuadores remotos, telemetría, sistemas de alerta o cualquier comunicación de baja potencia y largo alcance.

La siguiente etapa es construir el backend o API directamente en el ESP32 para conectar esta red LoRa con una aplicación web de chat. ¿Qué proyecto te gustaría montar primero con esta base? Cuéntalo en los comentarios.