Skip to content

Instantly share code, notes, and snippets.

@Tech500
Last active February 23, 2026 21:38
Show Gist options
  • Select an option

  • Save Tech500/e3db24ef063edc78dacc3542c1d71fcf to your computer and use it in GitHub Desktop.

Select an option

Save Tech500/e3db24ef063edc78dacc3542c1d71fcf to your computer and use it in GitHub Desktop.
EoRa-S3-900TB---FreeRTOS--WOR--Dual-Core LoRa Project
#define EoRa_PI_V1
#include <Arduino.h>
#include <RadioLib.h>
#include "boards.h"
#include "packet_struct.h"
#include "utilities.h"
#include "radio_eora.h"
#include "driver/rtc_io.h"
#include "esp_sleep.h"
// -------------------- Camera Power Pins --------------------
#define KY002S_TRIGGER 10 // Camera ON/OFF control
#define KY002S_STATUS 11 // Optional status input
// -------------------- Flags --------------------
extern SX1262 radio;
extern volatile bool enableInterrupt;
extern volatile bool receivedFlag; // already defined in your ISR file
extern void setFlag(); // ISR from radio_eora
// -------------------- Forward Declarations --------------------
void handleWorWake();
void handleTimerWake();
void enterWorSleep();
// -------------------- Setup --------------------
void setup() {
pinMode(KY002S_TRIGGER, OUTPUT);
digitalWrite(KY002S_TRIGGER, LOW); // camera OFF by default
pinMode(KY002S_STATUS, INPUT_PULLDOWN);
Serial.begin(115200);
delay(300);
esp_sleep_wakeup_cause_t wake = esp_sleep_get_wakeup_cause();
Serial.print("Wake reason: ");
Serial.println((int)wake);
if (wake == ESP_SLEEP_WAKEUP_EXT0) {
handleWorWake();
}
else if (wake == ESP_SLEEP_WAKEUP_TIMER) {
handleTimerWake();
}
else {
// First boot or reset → go straight into WOR mode
initRadio();
enterWorSleep();
}
}
void loop() {
delay(1000); // should never run
}
// -------------------- WOR Mode Entry --------------------
void enterWorSleep() {
Serial.println("Entering WOR mode...");
radio.setDio1Action(setFlag);
int state = radio.startReceiveDutyCycleAuto();
if (state != RADIOLIB_ERR_NONE) {
Serial.print("WOR start failed: ");
Serial.println(state);
}
esp_sleep_enable_ext0_wakeup((gpio_num_t)RADIO_DIO1_PIN, 1);
Serial.println("Deep sleeping (WOR)...");
delay(50);
esp_deep_sleep_start();
}
// -------------------- WOR Wake Handler --------------------
void handleWorWake() {
Serial.println("WOR wake: waiting for packet...");
uint32_t start = millis();
while (!receivedFlag && (millis() - start) < 5000) {
delay(1);
}
if (!receivedFlag) {
Serial.println("No packet after WOR wake. Re-arming WOR.");
initRadio();
enterWorSleep();
}
receivedFlag = false;
LoraPacket pkt;
int state = radio.readData((uint8_t*)&pkt, sizeof(pkt));
if (state != RADIOLIB_ERR_NONE) {
Serial.print("readData failed: ");
Serial.println(state);
initRadio();
enterWorSleep();
}
Serial.print("Packet: type=");
Serial.print(pkt.type);
Serial.print(" cmd=");
Serial.print(pkt.cmd);
Serial.print(" ts=");
Serial.println(pkt.timestamp);
if (pkt.cmd == 1) {
Serial.println("Turning camera ON...");
digitalWrite(KY002S_TRIGGER, HIGH);
Serial.println("Putting radio to sleep...");
radio.sleep(); // eliminates 8mA WOR spikes
const uint64_t ON_DURATION_US = 2ULL * 60ULL * 1000000ULL; // 2 minutes
esp_sleep_enable_timer_wakeup(ON_DURATION_US);
Serial.println("Deep sleeping (camera ON timer)...");
delay(50);
esp_deep_sleep_start();
}
// Unknown command → re-arm WOR
initRadio();
enterWorSleep();
}
// -------------------- Timer Wake Handler --------------------
void handleTimerWake() {
Serial.println("Timer wake: turning camera OFF.");
digitalWrite(KY002S_TRIGGER, LOW);
initRadio();
enterWorSleep();
}
#define EoRa_PI_V1
#include <Arduino.h>
#include "radiolib.h"
#include "boards.h"
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "radio_eora.h" // your SX1262 wrapper
#include "structs.h" // your LoraPacket struct
#include "index7.h" // HTML7 + processor7
// -------------------- WiFi Credentials --------------------
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// -------------------- Web Server --------------------
AsyncWebServer server(80);
// -------------------- TX Flags --------------------
volatile bool sendRequested = false;
// -------------------- Forward Declarations --------------------
void txTask(void* parameter);
void sendLoRaPacket();
// -------------------- Setup --------------------
void setup() {
Serial.begin(115200);
delay(300);
// WiFi
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.println();
Serial.print("TX IP: ");
Serial.println(WiFi.localIP());
// Radio init
initRadio();
// -------------------- Web Route --------------------
server.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request) {
// 1. Serve HTML page from PROGMEM
request->send_P(200, PSTR("text/html"), HTML7, processor7);
// 2. Trigger LoRa send
sendRequested = true;
});
server.begin();
// -------------------- TX Task (Core 1) --------------------
xTaskCreatePinnedToCore(
txTask,
"txTask",
4096,
NULL,
1,
NULL,
1
);
}
void loop() {
// Nothing here — TX is event-driven
}
// -------------------- TX Task --------------------
void txTask(void* parameter) {
for (;;) {
if (sendRequested) {
sendRequested = false;
sendLoRaPacket();
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
// -------------------- Send WOR + Payload --------------------
void sendLoRaPacket() {
Serial.println("TX: Sending WOR preamble...");
// 1. WOR preamble
radio.transmitWORPreamble();
// 2. Short delay (RX needs this)
delay(30);
// 3. Build packet
LoraPacket pkt;
pkt.type = 1;
pkt.cmd = 1;
pkt.timestamp = (uint32_t)millis(); // or your NTP timestamp
Serial.println("TX: Sending payload struct...");
// 4. Send struct
int state = radio.transmit((uint8_t*)&pkt, sizeof(pkt));
if (state == RADIOLIB_ERR_NONE) {
Serial.println("TX: Packet sent OK.");
} else {
Serial.print("TX: Packet send failed, code ");
Serial.println(state);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment