Last active
January 11, 2026 01:46
-
-
Save Tech500/e1160b2607240adbba73d91295491209 to your computer and use it in GitHub Desktop.
E220 WOR Remote Switch Demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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)); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //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