Meshtastic firmware already runs on top of FreeRTOS (ESP‑IDF’s RTOS), so any “periodic” work you add has to live somewhere in that scheduler. You’re right that Meshtastic already provides everything you need to send a LoRa packet—what you need is just a way to trigger that send on a timer rather than over BLE. You have two almost‐equivalent options:
Meshtastic’s “brains” live in a task that repeatedly calls AppPlatform::loop() (in src/main.cpp or src/app.cpp). You can drop in a simple millis‑based check so that, every N milliseconds, you call radio_iface->sendText(...):
#include "RadioInterface.h"
#include "Platform.h" // for AppPlatform::Get()->GetMs()
extern RadioInterface* radio_iface;
static uint32_t lastSendMs = 0;
static constexpr uint32_t kIntervalMs = 60 * 1000; // 1 minute
extern "C" void app_main() {
// … existing Meshtastic init …
AppPlatform::Get()->loop();
uint32_t now = AppPlatform::Get()->GetMs();
if (now - lastSendMs >= kIntervalMs) {
radio_iface->sendText("Hello from timer", /*wantAck=*/false);
// let the RTOS scheduler run other tasks
vTaskDelay(pdMS_TO_TICKS(10));
Pros:
- No new RTOS objects, just plain C++
- Easy to tweak interval or payload
Cons:
- You must pick a delay (here
vTaskDelay(10ms)) that balances CPU use vs. latency
Under the hood, Meshtastic already creates timers for BLE scans and such. You can create your own one‑shot or auto‑reloading timer and let the RTOS fire your callback exactly on schedule:
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
#include "RadioInterface.h"
extern RadioInterface* radio_iface;
static constexpr TickType_t kAutoSendInterval = pdMS_TO_TICKS(5 * 60 * 1000);
static void autoSendCallback(TimerHandle_t) {
radio_iface->sendText("Automated LoRa message", /*wantAck=*/false);
extern "C" void app_main() {
// … existing Meshtastic setup …
TimerHandle_t autoTimer = xTimerCreate(
pdTRUE, // reload each time
xTimerStart(autoTimer, 0);
// now hand off to Meshtastic’s scheduler:
AppPlatform::Get()->loopTaskStart();
Pros:
- Precise timing, zero‑overhead in your loop
- Clean separation: scheduler → callback
Cons:
- You need to include a couple of FreeRTOS headers (but they’re already in your include path)
- If you want the absolute simplest patch, go with Option 1: just sprinkle a
GetMs() check in the existing loop, call sendText(), and yield with vTaskDelay().
- If you’d rather lean on the RTOS (and already saw how Meshtastic uses timers elsewhere), Option 2 is a bit cleaner and more “correct” as a periodic job.
Both approaches end up using FreeRTOS under the covers, because ESP32/ESP‑IDF gives you no other scheduler. You’re not learning FreeRTOS new—you’re simply using the same scheduler Meshtastic already does.
Pick the style you’re most comfortable with, drop that snippet into your src/app.cpp, rebuild for tlora-v2-1-1.6 (or 1.8) in PlatformIO or IDF, flash, and your module will start firing off LoRa messages on its own cadence—no BLE app needed.