Skip to content

Instantly share code, notes, and snippets.

@Tech500
Last active December 19, 2025 23:52
Show Gist options
  • Select an option

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

Select an option

Save Tech500/3f6b0540f3fa074851d8479e403b3e75 to your computer and use it in GitHub Desktop.
E220 LoRa, Remote Switch --Receiver and Transmitter 12/17/2025 @ 17:03 EST
//E220_Remote_Switch_Sender.ino Vrsion XI
//William Lucid in colboration with ChatGPT Updated 12/17/2025 @ 07:27 EST
//E220 Module is set to ADDL 3
//Fully connectd schema AUX connected to ESP32, GPIO18
//Ardino IDE: ESP32 Board Manager, Version 2.0.17
// See library downloads for each library license.
// With FIXED SENDER configuration
#include <Arduino.h>
#include "WiFi.h"
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <time.h>
#include <Ticker.h>
#include "LoRa_E220.h"
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
#include "esp_sleep.h"
#include <Wire.h>
#include <queue>
#include "Message.h"
#include "XorWake.h"
#include "stdio.h"
#import "index7.h" //Video feed HTML; do not remove
// Replace with your network details
const char *ssid = "R2D2";
const char *password = "Sky7388500";
#define DESTINATION_ADDL 2
#define FREQENCY_927
#define CHANNEL 23
#define RXD2 16
#define TXD2 17
#define M0_PIN GPIO_NUM_21
#define M1_PIN GPIO_NUM_19
#define AUX_PIN GPIO_NUM_27
#define CONTROL_PIN 13 //RTC_IO wake capable for XOR function
// ---------- esp32 pins ----------------
LoRa_E220 e220ttl(&Serial2, 27, 21, 19); // RX AUX M0 M1
// Struct to hold date and time components
struct DateTime {
int year;
int month;
int day;
int hour;
int minute;
int second;
};
// Define the maximum length for the timestamp
const int MAX_dateTime_LENGTH = 30;
int data = 0;
int switchState;
struct Messages {
int switchState;
char dateTime[MAX_dateTime_LENGTH]; // Array to hold date/time string
};
Messages outgoing;
void updateTimestamp() {
String timestamp = get_time();
timestamp.toCharArray(outgoing.dateTime, MAX_dateTime_LENGTH);
}
struct Task {
int id;
void (*func)();
};
std::queue<Task> taskQueue;
void runTask(Task t);
int delayTime = 100; //setmode delay duration
//bool sendWORFlag = false;
char time_output[MAX_dateTime_LENGTH];
WiFiClient client;
///Are we currently connected?
boolean connected = false;
WiFiUDP udp;
// local port to listen for UDP packets
const int udpPort = 1337;
char incomingPacket[255];
char replyPacket[] = "Hi there! Got the message :-)";
//NTP Time Servers
const char *udpAddress1 = "pool.ntp.org";
const char *udpAddress2 = "time.nist.gov";
#define TZ "EST+5EDT,M3.2.0/2,M11.1.0/2"
// Function to get current date and time
DateTime getCurrentDateTime() {
DateTime currentDateTime;
time_t now = time(nullptr);
struct tm *ti = localtime(&now);
// Extract individual components
currentDateTime.year = ti->tm_year + 1900;
currentDateTime.month = ti->tm_mon + 1;
currentDateTime.day = ti->tm_mday;
currentDateTime.hour = ti->tm_hour;
currentDateTime.minute = ti->tm_min;
currentDateTime.second = ti->tm_sec;
return currentDateTime;
}
// Function to get the dateTime
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); // returns dateTime in the specified format
}
AsyncWebServer server(80);
volatile bool switchWebFlag = false;
void IRAM_ATTR wakeUp() {
// Do not use Serial on interrupt callback
switchWebFlag = true;
}
Ticker oneTick;
Ticker onceTick;
Ticker sendDelay;
String linkAddress = "192.123.12.27:80";
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
volatile int watchdogCounter;
int totalwatchdogCounter;
int cameraPowerOff = 0;
int watchDog;
void ISRwatchdog() {
portENTER_CRITICAL_ISR(&mux);
//watchdogCounter++;
if (watchdogCounter >= 5000) {
watchDog = 1;
}
portEXIT_CRITICAL_ISR(&mux);
}
void sendWOR() {
if(switchWebFlag){
switchWebFlag = false;
uint16_t duration_ms = 2600;
e220ttl.setMode(MODE_1_WOR_TRANSMITTER);
uint8_t preamble[4] = { 0xAA, 0xAA, 0xAA, 0xAA };
uint32_t start = millis();
while ((millis() - start) < duration_ms) {
ResponseStatus rs = e220ttl.sendFixedMessage(0, 0x02, CHANNEL, preamble, sizeof(preamble));
while (digitalRead(AUX_PIN) == LOW) { delay(1); } // pace by AUX
// Send actual message
rs = e220ttl.sendFixedMessage(0, DESTINATION_ADDL, CHANNEL, &outgoing, sizeof(Message));
Serial.println(rs.getResponseDescription());
}
// Return to normal mode
e220ttl.setMode(MODE_0_NORMAL);
}
}
int switchOne(int data) {
switchState = data;
if (data == 1) {
data = 1;
Serial.println("\nESP32 waking from Deep Sleep");
Serial.println("Battery Switch is ON\n");
}
if (data == 2) {
data = 2;
Serial.println("\nBattery power switched OFF");
Serial.println("ESP32 going to Deep Sleep\n");
}
// 1. Prepare Message
memset(&outgoing, 0, sizeof(Message));
outgoing.switchState = data;
strncpy(outgoing.dateTime, time_output, MAX_dateTime_LENGTH);
Serial.print("Sending WOR message for switchState: ");
Serial.println(outgoing.switchState);
Serial.println(outgoing.dateTime);
sendWOR();
/*
ResponseStatus rs = e220ttl.sendFixedMessage(0, DESTINATION_ADDL, CHANNEL, &outgoing, sizeof(Message));
e220ttl.setMode(MODE_0_NORMAL);
Serial.println(rs.getResponseDescription());
*/
//delay(1000);
switchWebFlag = true;
return data;
}
int cameraFlag;
int needAnotherCountdown = 0;
int lastXorResult = 0;
void batteryOff() {
int data = 2;
switchOne(data);
oneTick.detach();
}
void ISRcamera() {
batteryOff();
}
//e220 state --when to wakeup to prevent crashing xor function
// LoRa State Machine
enum LoRaState { LORA_IDLE,
LORA_BUSY,
LORA_READY };
LoRaState currentState = LORA_IDLE;
void updateState() {
if (digitalRead(AUX_PIN) == LOW) currentState = LORA_BUSY;
else currentState = LORA_READY;
}
void processTasks() {
updateState();
if (currentState == LORA_READY && !taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
runTask(t);
currentState = LORA_BUSY;
}
}
void enterDeepSleep() {
Serial.println("Preparing for deep sleep...");
Serial.flush();
delay(100); // Allow time for final serial output
// Set E220 to WOR receiver mode
e220ttl.setMode(MODE_2_WOR_RECEIVER);
delay(50);
// Hold E220 mode pins
gpio_hold_en(GPIO_NUM_21); // M0
gpio_hold_en(GPIO_NUM_19); // M1
gpio_deep_sleep_hold_en();
// Set EXT0 wake on AUX pin (LOW)
pinMode(GPIO_NUM_33, INPUT); // external pulldown/pullup required
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0);
Serial.println("Entering deep sleep now...");
Serial.flush();
delay(50);
esp_deep_sleep_start(); // 🔌💤
}
void prepareForSleep() {
detachInterrupt(AUX_PIN);
esp_sleep_enable_ext0_wakeup((gpio_num_t)AUX_PIN, 0);
enterDeepSleep();
}
void setup() {
Serial.begin(9600);
while (!Serial) {};
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
e220ttl.begin();
e220ttl.setMode(MODE_1_WOR_TRANSMITTER);
wifi_Start();
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
pinMode(AUX_PIN, INPUT);
pinMode(CONTROL_PIN, INPUT);
//XOR e220 AUX Control
int xorResult = switchWebFlag ^ cameraFlag;
digitalWrite(CONTROL_PIN, xorResult);
lastXorResult = xorResult; // Initialize latch variable
attachInterrupt(GPIO_NUM_27, wakeUp, FALLING);
Serial.println("\n\n\nWebserver and");
Serial.println("E220-900T30D Remote Switch\n");
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
// See https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv for Timezone codes for your region
setenv("TZ", "EST+5EDT,M3.2.0/2,M11.1.0/2", 3); // this sets TZ to Indianapolis, Indiana
server.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, PSTR("text/html"), HTML7, processor7);
data = 1;
});
oneTick.attach(1.0, ISRwatchdog); //watchdog ISR triggers every 1 second
attachInterrupt(GPIO_NUM_27, wakeUp, FALLING);
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
if (!ESP_SLEEP_WAKEUP_EXT0 == wakeup_reason) {
Serial.println("Waked up from Power on, Reset or Other!");
enterDeepSleep();
} else {
e220ttl.setMode(MODE_0_NORMAL);
delay(1000);
Serial.println("Waked up from external GPIO!");
gpio_hold_dis(GPIO_NUM_21);
gpio_hold_dis(GPIO_NUM_19);
gpio_deep_sleep_hold_dis();
e220ttl.sendFixedMessage(0, DESTINATION_ADDL, 23, "We have waked up from message, but we can't read It!");
e220ttl.setMode(MODE_1_WOR_TRANSMITTER); //Lowest LoRa Power setting
delay(1000);
Serial.println();
Serial.println("Start sleep!");
delay(100);
if (ESP_OK == gpio_hold_en(GPIO_NUM_21)) {
Serial.println("HOLD 21");
} else {
Serial.println("NO HOLD 21");
}
if (ESP_OK == gpio_hold_en(GPIO_NUM_19)) {
Serial.println("HOLD 19");
} else {
Serial.println("NO HOLD 19");
}
esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, 0);
gpio_deep_sleep_hold_en();
//Go to sleep now
Serial.println("Going to sleep now");
enterDeepSleep();
delay(1);
}
e220ttl.setMode(MODE_0_NORMAL);
delay(1000);
Serial.println("\n\nWake and start listening!");
}
void loop() {
if (switchWebFlag) {
switchWebFlag = false;
detachInterrupt(AUX_PIN);
switchOne(data);
needAnotherCountdown = 1;
countdownTrigger();
}
int xorResult = switchState ^ cameraFlag;
if (xorResult != lastXorResult) {
digitalWrite(CONTROL_PIN, xorResult);
lastXorResult = xorResult;
}
if (xorWakeCondition(AUX_PIN, CONTROL_PIN)) {
Serial.println("Wake condition met.");
if (!taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
if (t.func) t.func(); // now valid
}
}
Task t = taskQueue.front();
currentState = (digitalRead(AUX_PIN) == LOW) ? LORA_BUSY : LORA_READY;
if (xorWakeCondition(AUX_PIN, CONTROL_PIN)) {
Serial.println("Wake condition met.");
if (!taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
}
if (t.func) t.func();
}
get_time();
DateTime currentDateTime = currentDateTime;
//udp only send data when connected
if (connected) {
//Send a packet
udp.beginPacket(udpAddress1, udpPort);
udp.printf("Seconds since boot: %u", millis() / 1000);
udp.endPacket();
}
// If data available
if (e220ttl.available() > 1) {
// read the String message
ResponseContainer rc = e220ttl.receiveMessage();
// Is something goes wrong print error
if (rc.status.code != 1) {
Serial.println(rc.status.getResponseDescription());
} else {
// Print the data received
Serial.println(rc.status.getResponseDescription());
Serial.println(rc.data);
}
}
}
String processor7(const String &var) {
//index7.h
if (var == F("LINK"))
return linkAddress;
return String();
}
void configTime() {
configTime(0, 0, udpAddress1, udpAddress2);
setenv("TZ", "EST+5EDT,M3.2.0/2,M11.1.0/2", 3); // this sets TZ to Indianapolis, Indiana
tzset();
//udp only send data when connected
if (connected) {
//Send a packet
udp.beginPacket(udpAddress1, udpPort);
udp.printf("Seconds since boot: %u", millis() / 1000);
udp.endPacket();
}
Serial.print("wait for first valid dateTime");
while (time(nullptr) < 100000ul) {
Serial.print(".");
delay(5000);
}
Serial.println("\nSystem Time set\n");
get_time();
Serial.println(outgoing.dateTime);
}
void countdownTrigger() {
// Perform countdown actions here
Serial.println("\nCountdown timer triggered!\n");
//getDateTime();
// Schedule the next countdown if needed
if (needAnotherCountdown == 1) {
onceTick.once(60, ISRcamera);
data = 1;
switchOne(data);
needAnotherCountdown = 0;
}
}
void webInterface() {
String data = "http://192.123.12.27/relay";
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http; // Declare object of class HTTPClient
http.begin(data); // Specify request destination
// No need to add content-type header for a simple GET request
int httpCode = http.GET(); // Send the GET request
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString(); // Get the response payload
Serial.print("HttpCode: ");
Serial.print(httpCode); // Print HTTP return code
Serial.println("\n");
//Serial.print(" Data echoed back from Hosted website: ");
//Serial.println(payload); // Print payload response
http.end(); // Close HTTPClient
} else {
Serial.print("HttpCode: ");
Serial.print(httpCode); // Print HTTP return code
Serial.println(" URL Request failed.");
http.end(); // Close HTTPClient
}
} else {
Serial.println("Error in WiFi connection");
}
}
void printParameters(struct Configuration configuration) {
DEBUG_PRINTLN("----------------------------------------");
DEBUG_PRINT(F("HEAD : "));
DEBUG_PRINT(configuration.COMMAND, HEX);
DEBUG_PRINT(" ");
DEBUG_PRINT(configuration.STARTING_ADDRESS, HEX);
DEBUG_PRINT(" ");
DEBUG_PRINTLN(configuration.LENGHT, HEX);
DEBUG_PRINTLN(F(" "));
DEBUG_PRINT(F("AddH : "));
DEBUG_PRINTLN(configuration.ADDH, HEX);
DEBUG_PRINT(F("AddL : "));
DEBUG_PRINTLN(configuration.ADDL, HEX);
DEBUG_PRINTLN(F(" "));
DEBUG_PRINT(F("Chan : "));
DEBUG_PRINT(configuration.CHAN, DEC);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.getChannelDescription());
DEBUG_PRINTLN(F(" "));
DEBUG_PRINT(F("SpeedParityBit : "));
DEBUG_PRINT(configuration.SPED.uartParity, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.SPED.getUARTParityDescription());
DEBUG_PRINT(F("SpeedUARTDatte : "));
DEBUG_PRINT(configuration.SPED.uartBaudRate, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.SPED.getUARTBaudRateDescription());
DEBUG_PRINT(F("SpeedAirDataRate : "));
DEBUG_PRINT(configuration.SPED.airDataRate, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.SPED.getAirDataRateDescription());
DEBUG_PRINTLN(F(" "));
DEBUG_PRINT(F("OptionSubPacketSett: "));
DEBUG_PRINT(configuration.OPTION.subPacketSetting, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.OPTION.getSubPacketSetting());
DEBUG_PRINT(F("OptionTranPower : "));
DEBUG_PRINT(configuration.OPTION.transmissionPower, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.OPTION.getTransmissionPowerDescription());
DEBUG_PRINT(F("OptionRSSIAmbientNo: "));
DEBUG_PRINT(configuration.OPTION.RSSIAmbientNoise, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.OPTION.getRSSIAmbientNoiseEnable());
DEBUG_PRINTLN(F(" "));
DEBUG_PRINT(F("TransModeWORPeriod : "));
DEBUG_PRINT(configuration.TRANSMISSION_MODE.WORPeriod, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.TRANSMISSION_MODE.getWORPeriodByParamsDescription());
DEBUG_PRINT(F("TransModeEnableLBT : "));
DEBUG_PRINT(configuration.TRANSMISSION_MODE.enableLBT, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.TRANSMISSION_MODE.getLBTEnableByteDescription());
DEBUG_PRINT(F("TransModeEnableRSSI: "));
DEBUG_PRINT(configuration.TRANSMISSION_MODE.enableRSSI, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.TRANSMISSION_MODE.getRSSIEnableByteDescription());
DEBUG_PRINT(F("TransModeFixedTrans: "));
DEBUG_PRINT(configuration.TRANSMISSION_MODE.fixedTransmission, BIN);
DEBUG_PRINT(" -> ");
DEBUG_PRINTLN(configuration.TRANSMISSION_MODE.getFixedTransmissionDescription());
DEBUG_PRINTLN("----------------------------------------");
}
void wifi_Start() {
// Static IP configuration
IPAddress local_IP(192, 168, 12, 27); // your chosen IP
IPAddress gateway(192, 168, 12, 1); // router gateway
IPAddress subnet(255, 255, 255, 0); // subnet mask
IPAddress primaryDNS(8, 8, 8, 8); // optional
IPAddress secondaryDNS(8, 8, 4, 4);
// Configure static IP BEFORE WiFi.begin()
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
//Serial.println("STA Failed to configure");
}
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
//E220_Remote_Switch_Sender.ino
//William Lucid in colboration with ChatGPT Updated 12/17/2025 @ 17:03 EST
// Both skwtces compile with bo errors. Transmitter continues to trigger wakeup by ext 0 GPIO when no web request!
//E220 Module is set to ADDL 3
//Fully connectd schema AUX connected to ESP32, GPIO18
//Ardino IDE: ESP32 Board Manager, Version 2.0.17
// See library downloads for each library license.
// With FIXED SENDER configuration
#include <Arduino.h>
#include "WiFi.h"
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <time.h>
#include <Ticker.h>
#include "LoRa_E220.h"
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
#include "esp_sleep.h"
#include <Wire.h>
#include <queue>
#include "Message.h"
#include "XorWake.h"
#include "stdio.h"
#import "index7.h" //Video feed HTML; do not remove
// Replace with your network details
const char *ssid = "R2D2";
const char *password = "Sky7388500";
#define DESTINATION_ADDL 2
#define FREQENCY_915
#define CHANNEL 23
#define RXD2 16
#define TXD2 17
#define M0_PIN GPIO_NUM_21
#define M1_PIN GPIO_NUM_19
#define AUX_PIN GPIO_NUM_15
#define CONTROL_PIN 13 //RTC_IO wake capable for XORR function
// ---------- esp32 pins ----------------
LoRa_E220 e220ttl(&Serial2, 15, 21, 19); // RX AUX M0 M1
// Struct to hold date and time components
struct DateTime {
int year;
int month;
int day;
int hour;
int minute;
int second;
};
// Define the maximum length for the timestamp
const int MAX_dateTime_LENGTH = 30;
int data = 0;
int switchState;
struct Messages {
int switchState;
char dateTime[MAX_dateTime_LENGTH]; // Array to hold date/time string
};
Messages outgoing;
void updateTimestamp() {
String timestamp;
timestamp.toCharArray(outgoing.dateTime, MAX_dateTime_LENGTH);
}
struct Task {
int id;
void (*func)();
};
std::queue<Task> taskQueue;
void runTask(Task t);
int delayTime = 100; //setmode delay duration
bool sendWORFlag = false;
char time_output[MAX_dateTime_LENGTH];
WiFiClient client;
///Are we currently connected?
boolean connected = false;
WiFiUDP udp;
// local port to listen for UDP packets
const int udpPort = 1337;
char incomingPacket[255];
char replyPacket[] = "Hi there! Got the message :-)";
//NTP Time Servers
const char *udpAddress1 = "pool.ntp.org";
const char *udpAddress2 = "time.nist.gov";
#define TZ "EST+5EDT,M3.2.0/2,M11.1.0/2"
AsyncWebServer server(80);
Ticker oneTick;
Ticker onceTick;
Ticker sendDelay;
String linkAddress = "192.123.12.27:80";
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
volatile int watchdogCounter;
int totalwatchdogCounter;
int cameraPowerOff = 0;
int watchDog;
void ISRwatchdog() {
portENTER_CRITICAL_ISR(&mux);
//watchdogCounter++;
if (watchdogCounter >= 5000) {
watchDog = 1;
}
portEXIT_CRITICAL_ISR(&mux);
}
int cameraFlag;
int needAnotherCountdown = 0;
void batteryOff() {
int data = 2;
switchOne(data);
oneTick.detach();
}
void ISRcamera() {
batteryOff();
}
volatile bool switchWebFlag = false;
void IRAM_ATTR wakeUp() {
// Do not use Serial on interrupt callback
switchWebFlag = true;
}
void sendWOR() {
uint16_t duration_ms = 2600;
e220ttl.setMode(MODE_1_WOR_TRANSMITTER);
uint8_t preamble[4] = { 0xAA, 0xAA, 0xAA, 0xAA };
uint32_t start = millis();
while ((millis() - start) < duration_ms) {
ResponseStatus rs = e220ttl.sendFixedMessage(0, 0x02, CHANNEL, preamble, sizeof(preamble));
while (digitalRead(AUX_PIN) == LOW) { delay(1); } // pace by AUX
// Send actual message
rs = e220ttl.sendFixedMessage(0, DESTINATION_ADDL, CHANNEL, &outgoing, sizeof(Message));
Serial.println(rs.getResponseDescription());
// Return to normal mode
e220ttl.setMode(MODE_0_NORMAL);
switchWebFlag = true;
}
}
int switchOne(int data) {
if (data == 1) {
data = 1;
Serial.println("\nESP32 waking from Deep Sleep");
Serial.println("Battery Switch is ON\n");
}
if (data == 2) {
data = 2;
Serial.println("\nBattery power switched OFF");
Serial.println("ESP32 going to Deep Sleep\n");
}
// 1. Prepare Message
memset(&outgoing, 0, sizeof(Message));
outgoing.switchState = data;
strncpy(outgoing.dateTime, time_output, MAX_dateTime_LENGTH);
Serial.print("Sending WOR message for switchState: ");
Serial.println(outgoing.switchState);
Serial.println(outgoing.dateTime);
sendWOR();
/*
ResponseStatus rs = e220ttl.sendFixedMessage(0, DESTINATION_ADDL, CHANNEL, &outgoing, sizeof(Message));
e220ttl.setMode(MODE_0_NORMAL);
Serial.println(rs.getResponseDescription());
//delay(1000);
// 4. Wait for Transmission to Complete
// Use the AUX pin to check for completion, or just delay
// (Preamble time + Message time + buffer time).
// Since preamble is 2000ms, wait a little longer than that.
// 2500ms is a safe minimum.
*/
return data;
}
//e220 state --when to wakeup to prevent crashing xor function
// LoRa State Machine
enum LoRaState { LORA_IDLE,
LORA_BUSY,
LORA_READY };
LoRaState currentState = LORA_IDLE;
void updateState() {
if (digitalRead(AUX_PIN) == LOW) currentState = LORA_BUSY;
else currentState = LORA_READY;
}
void processTasks() {
updateState();
if (currentState == LORA_READY && !taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
runTask(t);
currentState = LORA_BUSY;
}
}
#include "driver/rtc_io.h"
void enterDeepSleep() {
Serial.println("Preparing for deep sleep...");
// 1. Flush and Close Serial to prevent UART glitches
Serial2.flush();
delay(50);
Serial2.end();
// 2. Set E220 Mode Pins (M0=0, M1=1 for WOR Transmitter)
digitalWrite(GPIO_NUM_21, LOW); // M0
digitalWrite(GPIO_NUM_19, HIGH); // M1
// 3. SET CONTROL PIN LOW (To mismatch with AUX=1)
pinMode(GPIO_NUM_13, OUTPUT);
digitalWrite(GPIO_NUM_13, LOW);
// 4. LOCK PINS IN RTC DOMAIN
// This is more robust than standard gpio_hold for deep sleep
rtc_gpio_hold_en(GPIO_NUM_21);
rtc_gpio_hold_en(GPIO_NUM_19);
rtc_gpio_hold_en(GPIO_NUM_13);
// 5. Final check of the XOR output before diving in
// If it's already 0, something is wrong (AUX is low or logic is flipped)
if (digitalRead(GPIO_NUM_33) == 0) {
Serial.println("Warning: XOR output is LOW. Sleep will trigger immediately!");
}
// 6. Set EXT0 wake on LOW (0)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 0);
Serial.println("Entering deep sleep...");
Serial.flush();
esp_deep_sleep_start();
}
void prepareForSleep() {
detachInterrupt(AUX_PIN);
esp_sleep_enable_ext0_wakeup((gpio_num_t)AUX_PIN, 0);
enterDeepSleep();
}
void setup() {
Serial.begin(9600);
while (!Serial) {};
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
e220ttl.begin();
ResponseStructContainer c = e220ttl.getConfiguration();
Configuration configuration = *(Configuration *)c.data;
Serial.println("\n=== Current Configuration ===");
Serial.print("ADDH: 0x");
Serial.println(configuration.ADDH, HEX);
Serial.print("ADDL: 0x");
Serial.println(configuration.ADDL, HEX);
Serial.print("Channel: ");
Serial.println(configuration.CHAN);
Serial.print("WOR Period: ");
Serial.println(configuration.TRANSMISSION_MODE.WORPeriod, BIN);
Serial.print("Fixed Trans: ");
Serial.println(configuration.TRANSMISSION_MODE.fixedTransmission, BIN);
c.close();
e220ttl.setMode(MODE_1_WOR_TRANSMITTER);
wifi_Start();
pinMode(M0_PIN, OUTPUT);
pinMode(M1_PIN, OUTPUT);
pinMode(AUX_PIN, INPUT);
attachInterrupt(GPIO_NUM_15, wakeUp, FALLING);
Serial.println("\n\n\nWebserver and");
Serial.println("E220-900T30D Remote Switch\n");
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
// See https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv for Timezone codes for your region
setenv("TZ", "EST+5EDT,M3.2.0/2,M11.1.0/2", 3); // this sets TZ to Indianapolis, Indiana
server.on("/relay", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, PSTR("text/html"), HTML7, processor7);
});
oneTick.attach(1.0, ISRwatchdog); //watchdog ISR triggers every 1 second
attachInterrupt(GPIO_NUM_15, wakeUp, FALLING);
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
if (!ESP_SLEEP_WAKEUP_EXT0 == wakeup_reason) {
Serial.println("Waked up from Power on, Reset or Other!");
enterDeepSleep();
} else {
e220ttl.setMode(MODE_0_NORMAL);
delay(1000);
Serial.println("Waked up from external GPIO!");
gpio_hold_dis(GPIO_NUM_21);
gpio_hold_dis(GPIO_NUM_19);
gpio_deep_sleep_hold_dis();
e220ttl.sendFixedMessage(0, DESTINATION_ADDL, 23, "We have waked up from message, but we can't read It!");
e220ttl.setMode(MODE_3_SLEEP); //Lowest LoRa Power setting
delay(1000);
Serial.println();
Serial.println("Start sleep!");
delay(100);
if (ESP_OK == gpio_hold_en(GPIO_NUM_21)) {
Serial.println("HOLD 21");
} else {
Serial.println("NO HOLD 21");
}
if (ESP_OK == gpio_hold_en(GPIO_NUM_19)) {
Serial.println("HOLD 19");
} else {
Serial.println("NO HOLD 19");
}
esp_sleep_enable_ext0_wakeup(GPIO_NUM_15, 0);
gpio_deep_sleep_hold_en();
//Go to sleep now
Serial.println("Going to sleep now");
enterDeepSleep();
delay(1);
}
e220ttl.setMode(MODE_0_NORMAL);
delay(1000);
Serial.println("\n\nWake and start listening!");
}
void loop() {
if (switchWebFlag) {
switchWebFlag = false;
detachInterrupt(AUX_PIN);
data = 1;
needAnotherCountdown = 1;
countdownTrigger();
switchOne(data); //Remote switch is on or off
}
if (xorWakeCondition(AUX_PIN, CONTROL_PIN)) {
Serial.println("Wake condition met.");
if (!taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
if (t.func) t.func(); // now valid
}
}
Task t = taskQueue.front();
currentState = (digitalRead(AUX_PIN) == LOW) ? LORA_BUSY : LORA_READY;
if (xorWakeCondition(AUX_PIN, CONTROL_PIN)) {
Serial.println("Wake condition met.");
if (!taskQueue.empty()) {
Task t = taskQueue.front();
taskQueue.pop();
}
if (t.func) t.func();
}
get_time();
DateTime currentDateTime = currentDateTime;
//udp only send data when connected
if (connected) {
//Send a packet
udp.beginPacket(udpAddress1, udpPort);
udp.printf("Seconds since boot: %u", millis() / 1000);
udp.endPacket();
}
// If data available
if (e220ttl.available() > 1) {
// read the String message
ResponseContainer rc = e220ttl.receiveMessage();
// Is something goes wrong print error
if (rc.status.code != 1) {
Serial.println(rc.status.getResponseDescription());
} else {
// Print the data received
Serial.println(rc.status.getResponseDescription());
Serial.println(rc.data);
}
}
}
String processor7(const String &var) {
//index7.h
if (var == F("LINK"))
return linkAddress;
return String();
}
void configTime() {
configTime(0, 0, udpAddress1, udpAddress2);
setenv("TZ", "EST+5EDT,M3.2.0/2,M11.1.0/2", 3); // this sets TZ to Indianapolis, Indiana
tzset();
//udp only send data when connected
if (connected) {
//Send a packet
udp.beginPacket(udpAddress1, udpPort);
udp.printf("Seconds since boot: %u", millis() / 1000);
udp.endPacket();
}
Serial.print("wait for first valid dateTime");
while (time(nullptr) < 100000ul) {
Serial.print(".");
delay(5000);
}
Serial.println("\nSystem Time set\n");
get_time();
Serial.println(outgoing.dateTime);
}
void countdownTrigger() {
// Perform countdown actions here
Serial.println("\nCountdown timer triggered!\n");
//getDateTime();
// Schedule the next countdown if needed
if (needAnotherCountdown == 1) {
onceTick.once(60, ISRcamera);
data = 1;
switchOne(data);
needAnotherCountdown = 0;
}
}
// Function to get current date and time
DateTime getCurrentDateTime() {
DateTime currentDateTime;
time_t now = time(nullptr);
struct tm *ti = localtime(&now);
// Extract individual components
currentDateTime.year = ti->tm_year + 1900;
currentDateTime.month = ti->tm_mon + 1;
currentDateTime.day = ti->tm_mday;
currentDateTime.hour = ti->tm_hour;
currentDateTime.minute = ti->tm_min;
currentDateTime.second = ti->tm_sec;
return currentDateTime;
}
// Function to get the dateTime
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); // returns dateTime in the specified format
}
void webInterface() {
String data = "http://192.123.12.27/relay";
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http; // Declare object of class HTTPClient
http.begin(data); // Specify request destination
// No need to add content-type header for a simple GET request
int httpCode = http.GET(); // Send the GET request
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString(); // Get the response payload
Serial.print("HttpCode: ");
Serial.print(httpCode); // Print HTTP return code
Serial.println("\n");
//Serial.print(" Data echoed back from Hosted website: ");
//Serial.println(payload); // Print payload response
http.end(); // Close HTTPClient
} else {
Serial.print("HttpCode: ");
Serial.print(httpCode); // Print HTTP return code
Serial.println(" URL Request failed.");
http.end(); // Close HTTPClient
}
} else {
Serial.println("Error in WiFi connection");
}
}
void wifi_Start() {
// Static IP configuration
IPAddress local_IP(192, 168, 12, 27); // your chosen IP
IPAddress gateway(192, 168, 12, 1); // router gateway
IPAddress subnet(255, 255, 255, 0); // subnet mask
IPAddress primaryDNS(8, 8, 8, 8); // optional
IPAddress secondaryDNS(8, 8, 4, 4);
// Configure static IP BEFORE WiFi.begin()
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
//Serial.println("STA Failed to configure");
}
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment