Skip to content

Instantly share code, notes, and snippets.

@Tech500
Last active January 11, 2026 01:46
Show Gist options
  • Select an option

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

Select an option

Save Tech500/e1160b2607240adbba73d91295491209 to your computer and use it in GitHub Desktop.
E220 WOR Remote Switch Demo
/*
* E220_WOR_Diagnostic_Tool.ino (E220 Module configuration tool; best one to use!!!
* Tests WOR transmission and helps identify configuration issues
* 01/09/2026
*
* Upload this to your TRANSMITTER and watch Serial Monitor
* It will test WOR and report detailed diagnostics
*/
#include "Arduino.h"
#include "LoRa_E220.h"
// CONFIGURATION - MODIFY THESE TO MATCH YOUR SETUP
#define MY_ADDRESS 0x03 // This device's address
#define DEST_ADDRESS 0x03 // Where to send (receiver's address)
#define CHANNEL 23
#define FREQUENCY_915
// Pin definitions for ESP32
#define RXD2 16
#define TXD2 17
#define M0_PIN 21
#define M1_PIN 19
#define AUX_PIN 15
LoRa_E220 e220ttl(&Serial2, AUX_PIN, M0_PIN, M1_PIN);
void waitForAux(int timeout = 5000) {
uint32_t start = millis();
Serial.print(" Waiting for AUX...");
while (digitalRead(AUX_PIN) == LOW && (millis() - start < timeout)) {
delay(10);
}
if (digitalRead(AUX_PIN) == HIGH) {
Serial.println(" ✓ Ready");
} else {
Serial.println(" ✗ TIMEOUT!");
}
}
void printConfiguration(Configuration config) {
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ E220 CONFIGURATION DETAILS ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.print("Address High (ADDH): 0x");
Serial.println(config.ADDH, HEX);
Serial.print("Address Low (ADDL): 0x");
Serial.println(config.ADDL, HEX);
Serial.print("Channel (CHAN): ");
Serial.println(config.CHAN, DEC);
Serial.println("\n--- UART Settings ---");
Serial.print("UART Parity: ");
Serial.println(config.SPED.getUARTParityDescription());
Serial.print("UART Baud Rate: ");
Serial.println(config.SPED.getUARTBaudRateDescription());
Serial.print("Air Data Rate: ");
Serial.println(config.SPED.getAirDataRateDescription());
Serial.println("\n--- Transmission Mode ---");
Serial.print("Fixed Transmission: ");
Serial.print(config.TRANSMISSION_MODE.fixedTransmission, BIN);
Serial.print(" (");
Serial.print(config.TRANSMISSION_MODE.getFixedTransmissionDescription());
Serial.println(")");
Serial.print("WOR Period: ");
Serial.print(config.TRANSMISSION_MODE.WORPeriod, BIN);
Serial.print(" (");
Serial.print(config.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
Serial.println(")");
Serial.print("Enable LBT: ");
Serial.println(config.TRANSMISSION_MODE.getLBTEnableByteDescription());
Serial.print("Enable RSSI: ");
Serial.println(config.TRANSMISSION_MODE.getRSSIEnableByteDescription());
Serial.println("\n--- Power & Options ---");
Serial.print("Transmission Power: ");
Serial.println(config.OPTION.getTransmissionPowerDescription());
Serial.print("Sub Packet Setting: ");
Serial.println(config.OPTION.getSubPacketSetting());
Serial.print("RSSI Ambient Noise: ");
Serial.println(config.OPTION.getRSSIAmbientNoiseEnable());
Serial.println("════════════════════════════════════════\n");
}
void testMode(OPERATING_MODE mode, const char* modeName) {
Serial.print("Setting mode: ");
Serial.println(modeName);
ResponseStatus rs = e220ttl.setMode(mode);
Serial.print(" Status: ");
Serial.println(rs.getResponseDescription());
delay(100);
waitForAux();
// Read back mode pins to verify
bool m0 = digitalRead(M0_PIN);
bool m1 = digitalRead(M1_PIN);
Serial.print(" M0=");
Serial.print(m0 ? "HIGH" : "LOW");
Serial.print(", M1=");
Serial.println(m1 ? "HIGH" : "LOW");
Serial.println();
}
void testWORTransmission() {
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ WOR TRANSMISSION TEST ║");
Serial.println("╚════════════════════════════════════════╝\n");
Serial.print("Transmitting FROM address: 0x");
Serial.println(MY_ADDRESS, HEX);
Serial.print("Sending TO address: 0x");
Serial.println(DEST_ADDRESS, HEX);
Serial.print("Channel: ");
Serial.println(CHANNEL);
if (MY_ADDRESS == DEST_ADDRESS) {
Serial.println("\n⚠️ WARNING: You're sending to your own address!");
Serial.println(" This won't work with Fixed Transmission.");
Serial.println(" TX and RX must have DIFFERENT addresses.\n");
}
// Step 1: Switch to WOR Transmitter mode
Serial.println("Step 1: Switching to MODE_1_WOR_TRANSMITTER...");
testMode(MODE_1_WOR_TRANSMITTER, "WOR Transmitter");
// Step 2: Send preamble
Serial.println("Step 2: Sending WOR preamble...");
Serial.println(" (This should wake the receiver if configured correctly)");
ResponseStatus rs = e220ttl.sendFixedMessage(0, DEST_ADDRESS, CHANNEL, "WAKE");
Serial.print(" Send status: ");
Serial.println(rs.getResponseDescription());
if (rs.code == 1) {
Serial.println(" ✓ Preamble sent successfully");
} else {
Serial.println(" ✗ Preamble send FAILED!");
}
waitForAux();
// Step 3: Wait for receiver to wake
Serial.println("\nStep 3: Waiting 4 seconds for receiver to wake...");
for (int i = 4; i > 0; i--) {
Serial.print(" ");
Serial.println(i);
delay(1000);
}
// Step 4: Send data message
Serial.println("\nStep 4: Sending data message...");
struct TestMessage {
int32_t testValue;
char message[20];
} __attribute__((packed));
TestMessage msg;
msg.testValue = 12345;
strcpy(msg.message, "WOR Test Data");
rs = e220ttl.sendFixedMessage(0, DEST_ADDRESS, CHANNEL, &msg, sizeof(TestMessage));
Serial.print(" Send status: ");
Serial.println(rs.getResponseDescription());
if (rs.code == 1) {
Serial.println(" ✓ Data sent successfully");
} else {
Serial.println(" ✗ Data send FAILED!");
}
waitForAux();
// Return to normal mode
Serial.println("\nReturning to MODE_0_NORMAL...");
testMode(MODE_0_NORMAL, "Normal Mode");
Serial.println("\n════════════════════════════════════════");
Serial.println("WOR transmission test complete!");
Serial.println("════════════════════════════════════════\n");
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n");
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ E220 WOR DIAGNOSTIC TOOL v1.0 ║");
Serial.println("╚════════════════════════════════════════╝\n");
// Initialize pins
pinMode(AUX_PIN, INPUT);
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
// Initialize UART
Serial.println("Initializing UART (9600 baud)...");
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
delay(100);
// Initialize E220
Serial.println("Initializing E220 module...");
e220ttl.begin();
delay(100);
// Set to normal mode for configuration reading
Serial.println("Setting MODE_0_NORMAL for configuration...");
e220ttl.setMode(MODE_0_NORMAL);
delay(100);
waitForAux();
// Read configuration
Serial.println("\nReading module configuration...");
ResponseStructContainer c = e220ttl.getConfiguration();
if (c.status.code == 1) {
Configuration config = *(Configuration*) c.data;
printConfiguration(config);
// Verification checks
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ VERIFICATION CHECKS ║");
Serial.println("╚════════════════════════════════════════╝\n");
bool hasErrors = false;
// Check 1: Address
if (config.ADDL == MY_ADDRESS) {
Serial.print("✓ Address matches (0x");
Serial.print(config.ADDL, HEX);
Serial.println(")");
} else {
Serial.print("✗ Address MISMATCH! Config=0x");
Serial.print(config.ADDL, HEX);
Serial.print(", Expected=0x");
Serial.println(MY_ADDRESS, HEX);
hasErrors = true;
}
// Check 2: Channel
if (config.CHAN == CHANNEL) {
Serial.print("✓ Channel matches (");
Serial.print(config.CHAN);
Serial.println(")");
} else {
Serial.print("✗ Channel MISMATCH! Config=");
Serial.print(config.CHAN);
Serial.print(", Expected=");
Serial.println(CHANNEL);
hasErrors = true;
}
// Check 3: Fixed Transmission
if (config.TRANSMISSION_MODE.fixedTransmission == FT_FIXED_TRANSMISSION) {
Serial.println("✓ Fixed Transmission ENABLED");
} else {
Serial.println("✗ Fixed Transmission NOT enabled!");
Serial.println(" WOR requires Fixed Transmission!");
hasErrors = true;
}
// Check 4: WOR Period
Serial.print("✓ WOR Period: ");
Serial.println(config.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
Serial.println(" ** Receiver MUST have same WOR period! **");
// Check 5: Air Data Rate
Serial.print("✓ Air Data Rate: ");
Serial.println(config.SPED.getAirDataRateDescription());
Serial.println(" ** Receiver MUST have same Air Data Rate! **");
if (hasErrors) {
Serial.println("\n⚠️ CONFIGURATION ERRORS DETECTED!");
Serial.println("Fix these before testing WOR.\n");
} else {
Serial.println("\n✓ All configuration checks passed!\n");
}
} else {
Serial.println("✗ Failed to read configuration!");
Serial.print("Error code: ");
Serial.println(c.status.code);
}
c.close();
Serial.println("\n════════════════════════════════════════");
Serial.println("Press 'T' to test WOR transmission");
Serial.println("Press 'C' to read configuration again");
Serial.println("Press 'N' to set Normal mode");
Serial.println("Press 'W' to set WOR Transmitter mode");
Serial.println("Press 'R' to set WOR Receiver mode");
Serial.println("════════════════════════════════════════\n");
}
void loop() {
if (Serial.available() > 0) {
char cmd = Serial.read();
switch (cmd) {
case 'T':
case 't':
testWORTransmission();
break;
case 'C':
case 'c':
e220ttl.setMode(MODE_0_NORMAL);
delay(100);
waitForAux();
ResponseStructContainer c = e220ttl.getConfiguration();
if (c.status.code == 1) {
Configuration config = *(Configuration*) c.data;
printConfiguration(config);
}
c.close();
break;
case 'N':
case 'n':
testMode(MODE_0_NORMAL, "Normal Mode");
break;
case 'W':
case 'w':
testMode(MODE_1_WOR_TRANSMITTER, "WOR Transmitter");
break;
case 'R':
case 'r':
testMode(MODE_2_WOR_RECEIVER, "WOR Receiver");
break;
}
}
delay(10);
}
/*
* E220_WOR_DualCore_Receiver_Final.ino
* Dual-Core FreeRTOS with Deep Sleep & WOR
* Fixed with correct pin assignments and timing
* 01/10/2026
*
* Core 0: Radio communication
* Core 1: Logic and sleep management
*/
#include <Arduino.h>
#include "LoRa_E220.h"
#include "driver/rtc_io.h"
#define FREQUENCY_915
#define CHANNEL 23
#define MY_ADDRESS 3
#define TRANSMITTER_ADDRESS 2
// Hardware Pins - CORRECTED
#define RXD2 16
#define TXD2 17
#define M0_PIN GPIO_NUM_21 // FIXED - was 19
#define M1_PIN GPIO_NUM_19 // FIXED - was 21
#define AUX_PIN GPIO_NUM_15
#define KY002S_TRIGGER 32
#define KY002S_STATUS 33
LoRa_E220 e220ttl(&Serial2, AUX_PIN, M0_PIN, M1_PIN);
const int MAX_dateTime_LENGTH = 40;
struct Message {
int32_t switchData;
char dateTime[40];
} __attribute__((packed));
// Cross-core communication
volatile bool inboxReady = false;
Message inbox;
const uint16_t PULSE_MS = 200;
RTC_DATA_ATTR int bootCount = 0;
// Task handles
TaskHandle_t commTaskHandle = NULL;
TaskHandle_t logicTaskHandle = NULL;
// Forward declarations
void enterDeepSleep();
void commTask(void* parameter);
void logicTask(void* parameter);
// ================================================================
// Wait for AUX
// ================================================================
void waitForAux(int timeout = 5000) {
uint32_t start = millis();
while (digitalRead(AUX_PIN) == LOW && (millis() - start < timeout)) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
// ================================================================
// Setup
// ================================================================
void setup() {
Serial.begin(115200);
delay(500);
bootCount++;
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ E220 WOR Dual-Core Receiver ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.print("Boot #");
Serial.println(bootCount);
// Initialize pins
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
pinMode(AUX_PIN, INPUT);
pinMode(KY002S_TRIGGER, OUTPUT);
pinMode(KY002S_STATUS, INPUT);
digitalWrite(KY002S_TRIGGER, LOW);
// Check wake cause
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause == ESP_SLEEP_WAKEUP_EXT0) {
// =============================================
// WOKE FROM WOR PREAMBLE
// =============================================
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ WOR WAKE-UP DETECTED! ║");
Serial.println("╚════════════════════════════════════════╝\n");
// Release GPIO holds
gpio_hold_dis(M0_PIN);
gpio_hold_dis(M1_PIN);
gpio_deep_sleep_hold_dis();
// Initialize UART and E220
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
vTaskDelay(pdMS_TO_TICKS(100));
e220ttl.begin();
vTaskDelay(pdMS_TO_TICKS(100));
// Switch to NORMAL mode
Serial.println("Switching to NORMAL mode...");
e220ttl.setMode(MODE_0_NORMAL);
vTaskDelay(pdMS_TO_TICKS(100));
waitForAux();
Serial.println("Radio ready. Launching tasks...\n");
// Launch dual-core tasks
xTaskCreatePinnedToCore(commTask, "CommTask", 4096, NULL, 2, &commTaskHandle, 0);
xTaskCreatePinnedToCore(logicTask, "LogicTask", 4096, NULL, 1, &logicTaskHandle, 1);
} else {
// =============================================
// INITIAL POWER-ON
// =============================================
Serial.println("Power-on wake. Initializing...\n");
// Initialize UART and E220
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
delay(100);
e220ttl.begin();
delay(100);
e220ttl.setMode(MODE_0_NORMAL);
delay(100);
waitForAux();
// Verify configuration
ResponseStructContainer c = e220ttl.getConfiguration();
if (c.status.code == 1) {
Configuration config = *(Configuration*)c.data;
Serial.print("Address: 0x");
Serial.println(config.ADDL, HEX);
Serial.print("Channel: ");
Serial.println(config.CHAN);
Serial.print("WOR Period: ");
Serial.println(config.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
}
c.close();
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ Entering WOR Sleep Mode ║");
Serial.println("╚════════════════════════════════════════╝\n");
delay(1000);
enterDeepSleep();
}
}
// ================================================================
// Core 0: Communication Task - Handle E220 Reception
// ================================================================
void commTask(void* parameter) {
Serial.print("CommTask running on core ");
Serial.println(xPortGetCoreID());
Serial.println("Waiting for data message...");
unsigned long startTime = millis();
int attempts = 0;
bool dataReceived = false;
// Wait up to 12 seconds for data to arrive
while (attempts < 120 && !dataReceived) {
if (e220ttl.available() > 0) {
dataReceived = true;
break;
}
vTaskDelay(pdMS_TO_TICKS(100));
attempts++;
if (attempts % 10 == 0) {
Serial.print(" Still waiting... (");
Serial.print(attempts / 10);
Serial.println(" seconds)");
}
}
if (dataReceived || e220ttl.available() > 0) {
Serial.println("\n>>> DATA DETECTED <<<");
ResponseStructContainer rsc = e220ttl.receiveMessageRSSI(sizeof(Message));
if (rsc.status.code == 1 && rsc.data != nullptr) {
Message* msgPtr = (Message*)rsc.data;
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ MESSAGE RECEIVED! ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.print("Switch Data: ");
Serial.println(msgPtr->switchData);
Serial.print("Timestamp: ");
Serial.println(msgPtr->dateTime);
Serial.print("RSSI: ");
Serial.print(rsc.rssi);
Serial.println(" dBm");
Serial.print("Receive time: ");
Serial.print(millis() - startTime);
Serial.println("ms\n");
// Safely copy to inbox for Core 1
memset(&inbox, 0, sizeof(Message));
memcpy(&inbox, rsc.data, sizeof(Message));
inbox.dateTime[MAX_dateTime_LENGTH - 1] = '\0';
inboxReady = true;
} else {
Serial.println("✗ Receive error");
Serial.print("Status: ");
Serial.println(rsc.status.getResponseDescription());
}
rsc.close();
} else {
Serial.println("\n⚠️ No data received in 12 seconds");
Serial.println("Going back to sleep...");
// Signal Core 1 to sleep
inboxReady = false;
vTaskDelay(pdMS_TO_TICKS(100));
}
// Task complete - delete self
vTaskDelete(NULL);
}
// ================================================================
// Core 1: Logic Task - Process Message & Control KY002S
// ================================================================
void logicTask(void* parameter) {
Serial.print("LogicTask running on core ");
Serial.println(xPortGetCoreID());
// Wait for data from Core 0
for (int i = 0; i < 150; i++) { // 15 second timeout
if (inboxReady) {
break;
}
vTaskDelay(pdMS_TO_TICKS(100));
}
if (inboxReady) {
inboxReady = false;
Message msg = inbox;
bool isCurrentlyOn = (digitalRead(KY002S_STATUS) == HIGH);
Serial.println("\n--- Processing Command ---");
Serial.print("Current state: ");
Serial.println(isCurrentlyOn ? "ON" : "OFF");
Serial.print("Command: ");
Serial.println(msg.switchData == 1 ? "TURN ON" : "TURN OFF");
// Toggle if needed
if ((msg.switchData == 1 && !isCurrentlyOn) || (msg.switchData == 2 && isCurrentlyOn)) {
Serial.println(">>> Toggling KY002S switch <<<");
digitalWrite(KY002S_TRIGGER, HIGH);
vTaskDelay(pdMS_TO_TICKS(PULSE_MS));
digitalWrite(KY002S_TRIGGER, LOW);
Serial.println("✓ Switch toggled");
} else {
Serial.println("ℹ️ Already in requested state - no action needed");
}
// Send acknowledgment
Serial.println("\nSending ACK...");
waitForAux();
ResponseStatus rs = e220ttl.sendFixedMessage(0, TRANSMITTER_ADDRESS, CHANNEL, "ACK");
Serial.print("ACK status: ");
Serial.println(rs.getResponseDescription());
waitForAux();
}
Serial.println("\n--- Processing Complete ---");
vTaskDelay(pdMS_TO_TICKS(500));
// Return to deep sleep
Serial.println("Entering deep sleep...\n");
enterDeepSleep();
// Never reached, but included for clarity
vTaskDelete(NULL);
}
// ================================================================
// Power Management
// ================================================================
void enterDeepSleep() {
Serial.println(">>> Entering WOR Sleep Mode <<<");
Serial.flush();
// Manually set WOR Receiver mode pins
digitalWrite(M0_PIN, LOW); // M0 = LOW
digitalWrite(M1_PIN, HIGH); // M1 = HIGH
vTaskDelay(pdMS_TO_TICKS(100));
waitForAux();
// Hold pins during sleep
gpio_hold_en(M0_PIN);
gpio_hold_en(M1_PIN);
gpio_deep_sleep_hold_en();
// Enable wake on AUX LOW
esp_sleep_enable_ext0_wakeup(AUX_PIN, 0);
Serial.println("Waiting for WOR preamble...\n");
Serial.flush();
esp_deep_sleep_start();
}
void loop() {
// Never reached - device sleeps after setup
vTaskDelay(pdMS_TO_TICKS(1000));
}
/*
* E220_WOR_Transmitter_Final.ino Claude v15
* Fixed addressing: TX=0x02, RX=0x03
* 01/09/2026
*
* PREREQUISITES:
* Both modules must be configured first using E220_WOR_Configurator.ino
* - Transmitter: Address 0x02
* - Receiver: Address 0x03
* - Both: Same WOR period (2000ms), Fixed Transmission enabled
*
* E220 Wiring (ESP32):
* M0 -> GPIO 21
* M1 -> GPIO 19
* TX -> GPIO 16 (RX2)
* RX -> GPIO 17 (TX2)
* AUX -> GPIO 15
* VCC -> 3.3V
* GND -> GND
*/
#define MY_ADDRESS_ADDL 2 // THIS transmitter's address
#define RECEIVER_ADDRESS_ADDL 3 // Send TO this address
#define FREQUENCY_915
#define CHANNEL 23
#include "Arduino.h"
#include "LoRa_E220.h"
#include "WiFi.h"
#include <WiFiUdp.h>
#include <time.h>
#include <Ticker.h>
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
#include "index7.h"
// ---------------------------
// E220 Setup
// ---------------------------
LoRa_E220 e220ttl(&Serial2, 15, 21, 19);
#define RXD2 16
#define TXD2 17
#define M0_PIN 21
#define M1_PIN 19
#define AUX_PIN 15
#define TRIGGER 32 // KY002S MOSFET Bi-Stable Switch (drive)
#define KY002S_PIN 33 // KY002S MOSFET Bi-Stable Switch (sense)
#define ALERT 4 // ina226 Battery Monitor
// ---------------------------
// Network
// ---------------------------
const char *ssid = "R2D2";
const char *password = "Sky7388500";
AsyncWebServer server(80);
// ---------------------------
// Message Structure
// ---------------------------
const int MAX_dateTime_LENGTH = 40;
char time_output[MAX_dateTime_LENGTH];
struct Message {
int32_t switchData;
char dateTime[40];
} __attribute__((packed));
Message outgoing;
// ---------------------------
// State Variables
// ---------------------------
volatile bool countdownExpired = false;
volatile bool sendRequested = false; // NEW: Flag for web request
int needAnotherCountdown = 0;
int pulseDuration = 100;
String linkAddress = "192.168.12.27:80";
// NTP Setup
WiFiUDP udp;
const int udpPort = 1157;
const char *udpAddress1 = "pool.ntp.org";
const char *udpAddress2 = "time.nist.gov";
#define TZ "EST+5EDT,M3.2.0/2,M11.1.0/2"
// Tickers
Ticker onceTick;
// ---------------------------
// ISR: Countdown Timer
// ---------------------------
void IRAM_ATTR countdownTrigger() {
countdownExpired = true;
}
// ---------------------------
// Utility: Wait for AUX HIGH
// ---------------------------
void waitForAux(int timeout = 5000) {
uint32_t start = millis();
while (digitalRead(AUX_PIN) == LOW && (millis() - start < timeout)) {
delay(10);
yield(); // Feed watchdog
}
if (digitalRead(AUX_PIN) == LOW) {
Serial.println(" ⚠️ AUX timeout!");
}
}
// ---------------------------
// Get formatted time string
// ---------------------------
String get_time() {
time_t now;
time(&now);
strftime(time_output, MAX_dateTime_LENGTH, "%a %m/%d/%y %T", localtime(&now));
return String(time_output);
}
// ---------------------------
// Configure NTP Time
// ---------------------------
void configTimeCustom() {
configTime(0, 0, udpAddress1, udpAddress2);
setenv("TZ", TZ, 1);
tzset();
Serial.print("Waiting for NTP time sync");
while (time(nullptr) < 100000ul) {
Serial.print(".");
delay(1000);
}
Serial.println("\nSystem Time synchronized");
get_time();
Serial.println(time_output);
}
// ---------------------------
// WiFi Setup with Static IP
// ---------------------------
void wifi_Start() {
IPAddress local_IP(192, 168, 12, 27);
IPAddress gateway(192, 168, 12, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);
IPAddress secondaryDNS(8, 8, 4, 4);
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("Static IP configuration failed");
}
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi Connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
// ---------------------------
// Initialize E220 Radio
// ---------------------------
void initRadio() {
Serial.println("Initializing E220 radio...");
pinMode(AUX_PIN, INPUT);
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
delay(100);
e220ttl.begin();
delay(100);
// Start in NORMAL mode
e220ttl.setMode(MODE_0_NORMAL);
delay(100);
waitForAux();
// Verify configuration
ResponseStructContainer c = e220ttl.getConfiguration();
if (c.status.code == 1) {
Configuration config = *(Configuration*) c.data;
Serial.println("\n--- E220 Configuration ---");
Serial.print("Address: 0x");
Serial.println(config.ADDL, HEX);
Serial.print("Channel: ");
Serial.println(config.CHAN);
Serial.print("Fixed TX: ");
Serial.println(config.TRANSMISSION_MODE.fixedTransmission == FT_FIXED_TRANSMISSION ? "YES" : "NO");
Serial.print("WOR Period: ");
Serial.println(config.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
if (config.ADDL != MY_ADDRESS_ADDL) {
Serial.println("\n⚠️ WARNING: Module address doesn't match!");
Serial.print(" Expected: 0x");
Serial.println(MY_ADDRESS_ADDL, HEX);
Serial.print(" Got: 0x");
Serial.println(config.ADDL, HEX);
Serial.println(" Use E220_WOR_Configurator.ino to fix this!");
}
if (config.TRANSMISSION_MODE.fixedTransmission != FT_FIXED_TRANSMISSION) {
Serial.println("\n⚠️ WARNING: Fixed Transmission not enabled!");
Serial.println(" Use E220_WOR_Configurator.ino to enable it!");
}
Serial.println("-------------------------\n");
}
c.close();
Serial.println("E220 Ready\n");
}
// ---------------------------
// Send WOR Preamble
// Wakes the receiver from WOR sleep mode
// ---------------------------
void sendPreamble() {
unsigned long startTime = millis();
Serial.println("\n=== STEP 1: WOR PREAMBLE ===");
Serial.print(" From: 0x");
Serial.println(MY_ADDRESS_ADDL, HEX);
Serial.print(" To: 0x");
Serial.println(RECEIVER_ADDRESS_ADDL, HEX);
Serial.print(" Ch: ");
Serial.println(CHANNEL);
waitForAux();
// Switch to WOR Transmitter mode
unsigned long modeStart = millis();
Serial.println(" Setting WOR TX mode...");
e220ttl.setMode(MODE_1_WOR_TRANSMITTER);
delay(100);
waitForAux();
Serial.print(" Mode switch took: ");
Serial.print(millis() - modeStart);
Serial.println("ms");
// Send short preamble - the WOR preamble is what wakes receiver
unsigned long sendStart = millis();
Serial.println(" Sending preamble...");
ResponseStatus rs = e220ttl.sendFixedMessage(0, RECEIVER_ADDRESS_ADDL, CHANNEL, "WAKE");
Serial.print(" Status: ");
Serial.println(rs.getResponseDescription());
Serial.print(" Preamble transmission took: ");
Serial.print(millis() - sendStart);
Serial.println("ms");
waitForAux();
// Critical: Wait for receiver to wake and be ready
// WOR wake-up sequence:
// 1. Receiver detects preamble (~100-500ms)
// 2. Receiver wakes from sleep (~500-1000ms)
// 3. Receiver switches to normal mode (~100ms)
// 4. Receiver ready to receive data
Serial.println(" Waiting for RX wake...");
// 3 second wait with progress and watchdog feeding
for (int i = 3; i > 0; i--) {
Serial.print(" ");
Serial.print(i);
Serial.println(" seconds...");
// Break into smaller delays to feed watchdog
for (int j = 0; j < 10; j++) {
delay(100);
yield(); // Feed the watchdog
}
}
Serial.print("=== PREAMBLE COMPLETE (Total: ");
Serial.print(millis() - startTime);
Serial.println("ms) ===\n");
}
// ---------------------------
// Send Data Message
// Sends actual payload after receiver is awake
// ---------------------------
void sendDataMessage(int switchValue) {
unsigned long startTime = millis();
Serial.println("=== STEP 2: DATA MESSAGE ===");
Serial.print(" Switch value: ");
Serial.println(switchValue);
waitForAux();
// Prepare message
unsigned long prepStart = millis();
memset(&outgoing, 0, sizeof(Message));
outgoing.switchData = switchValue;
get_time();
strncpy(outgoing.dateTime, time_output, MAX_dateTime_LENGTH - 1);
outgoing.dateTime[MAX_dateTime_LENGTH - 1] = '\0';
Serial.print(" Timestamp: ");
Serial.println(outgoing.dateTime);
Serial.print(" Message prep took: ");
Serial.print(millis() - prepStart);
Serial.println("ms");
// Send message (still in WOR TX mode)
unsigned long sendStart = millis();
Serial.println(" Sending data...");
ResponseStatus rs = e220ttl.sendFixedMessage(0, RECEIVER_ADDRESS_ADDL, CHANNEL,
&outgoing, sizeof(Message));
Serial.print(" Status: ");
Serial.println(rs.getResponseDescription());
Serial.print(" Data transmission took: ");
Serial.print(millis() - sendStart);
Serial.println("ms");
waitForAux();
// Return to NORMAL mode
unsigned long modeStart = millis();
Serial.println(" Returning to normal mode...");
e220ttl.setMode(MODE_0_NORMAL);
delay(100);
waitForAux();
Serial.print(" Mode switch took: ");
Serial.print(millis() - modeStart);
Serial.println("ms");
Serial.print("=== DATA COMPLETE (Total: ");
Serial.print(millis() - startTime);
Serial.println("ms) ===\n");
}
// ---------------------------
// Main WOR Send Function
// ---------------------------
void performWORSend(int switchValue) {
Serial.println("\n╔════════════════════════════════════════╗");
Serial.println("║ WOR TRANSMISSION STARTING ║");
Serial.println("╚════════════════════════════════════════╝");
if (switchValue == 1) {
Serial.println("Event: Battery Switch ON");
} else if (switchValue == 2) {
Serial.println("Event: Battery Switch OFF");
}
// Two-step WOR process:
// 1. Send preamble to wake receiver
sendPreamble();
// 2. Send actual data message
sendDataMessage(switchValue);
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ WOR TRANSMISSION COMPLETE ║");
Serial.println("╚════════════════════════════════════════╝\n");
}
// ---------------------------
// Template processor for web page
// ---------------------------
String processor7(const String &var) {
if (var == F("LINK"))
return linkAddress;
return String();
}
// ---------------------------
// Setup
// ---------------------------
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n\n╔════════════════════════════════════════╗");
Serial.println("║ E220 WOR Transmitter v2.0 ║");
Serial.println("╚════════════════════════════════════════╝\n");
// Initialize pins
pinMode(TRIGGER, OUTPUT);
pinMode(KY002S_PIN, INPUT);
pinMode(ALERT, INPUT);
digitalWrite(TRIGGER, LOW);
// Start WiFi
wifi_Start();
// Sync time
configTimeCustom();
// Initialize radio
initRadio();
// Check KY002S switch state
int value = digitalRead(KY002S_PIN);
if (value == HIGH) {
Serial.println("Toggling KY002S switch...");
digitalWrite(TRIGGER, HIGH);
delay(pulseDuration);
digitalWrite(TRIGGER, LOW);
}
// Setup web server route
server.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, PSTR("text/html"), HTML7, processor7);
// DON'T do the transmission here - just set a flag!
sendRequested = true;
});
server.begin();
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ SYSTEM READY ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.println("URL: http://192.168.12.27/relay\n");
Serial.println("RECEIVER MUST BE:");
Serial.println(" • Address: 0x03");
Serial.println(" • Mode: MODE_2_WOR_RECEIVER");
Serial.println(" • WOR Period: 2000ms");
Serial.println(" • Fixed TX: Enabled");
Serial.println(" • AUX: Monitored for wake signal\n");
}
// ---------------------------
// Main Loop
// ---------------------------
void loop() {
// Check if web request triggered a send
if (sendRequested) {
sendRequested = false;
Serial.println("\n🌐 WEB REQUEST RECEIVED");
performWORSend(1);
}
delay(10);
}
//index7.h
//Add to same folder as "E220_WOR_Transmitter.ino"
const char HTML7[] PROGMEM = R"====(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
margin: 0; /* Set margin to 0 to remove any unwanted spacing */
padding: 0;
height: 100%;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height: 100vh; /* Use viewport height for container */
}
@media (max-width: 768px) {
.container {
flex: 1 1 auto;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
}
header {
margin: 5vh auto 0;
width: 100%;
text-align: center;
}
main {
display: flex;
align-items: center;
justify-content: center;
}
iframe {
aspect-ratio: 16 / 9;
width: 1300px; /* Set the width to 100% to occupy the entire container */
flex: 1; /* Use flex: 1 to make the iframe expand and fill the available space */
}
footer {
width: 100%;
text-align: center;
padding: 10px; /* Add some padding to the footer for spacing */
}
</style>
</head>
<body>
<div class="container">
<header>
<br><br>This is original H264 video encoded by IP camera; server doesn't do any transcoding. Wyze Cam v3 video feeds
<br>Wyse-Bridge Docker, container; which provides webRTC video URL. Camera maybe offline; depending on battery discharge state.
<br><br>
</header>
<main>
<iframe
class="iframe"
width="1300"
height="731"
allow="autoplay"
src="https://drive.google.com/file/d/1olthuXRahBW5wzcizsOCfolLoD8-W_Cr/preview"
></iframe>
</main>
<footer>
<h2><a href="http://%LINK%/Weather" >ESP32 Server</a></h2>
</footer>
</div>
</body>
</html>
)====";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment