Advertisement
kodilivetv

esp32_24h_timer_ontime_eeprom_gpioreset_universaltelegram

Jun 12th, 2025
1,028
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 22.75 KB | None | 0 0
  1. /*
  2.   ESP32 Motor Timer with Web Setup, EEPROM Backup, GPIO Reset, and Telegram Notification
  3.  
  4.   DESCRIPTION:
  5.   -------------
  6.   This sketch controls a motor (or relay) connected to an ESP32, allowing you to schedule its operation for specific hours each day.
  7.   - Configuration is done via a WiFi Access Point and web interface hosted by the ESP32.
  8.   - All settings (schedule, run time, time) are saved to EEPROM for power-loss recovery.
  9.   - A special reset mode (by connecting GPIO14 to 3.3V and resetting) clears all settings.
  10.   - After each scheduled motor run, a notification is sent to your Telegram account.
  11.  
  12.   FEATURES:
  13.   ---------
  14.   - Web-based setup: Set current time, motor run time (5–300s), and select operation hours (per hour, 24h grid).
  15.   - Settings backup: All settings are stored in EEPROM and restored after power loss.
  16.   - Reset mode: Connect GPIO14 to 3.3V and reset to clear all settings and re-enter setup.
  17.   - Telegram notification: After each motor run, a message is sent to your Telegram bot.
  18.  
  19.   WIRING:
  20.   -------
  21.   - MOTOR_PIN: GPIO33 (connect to relay or motor driver)
  22.   - LED_PIN:   GPIO32 (status LED)
  23.   - RESET_PIN: GPIO14 (connect to 3.3V and reset to clear settings)
  24.   - Ensure your relay/motor driver is compatible with 3.3V logic.
  25.  
  26.   USAGE INSTRUCTIONS:
  27.   -------------------
  28.   1. Power on the ESP32. It will start as a WiFi Access Point (SSID: ESP32-Motor-Timer, Password: 12345678).
  29.   2. Connect to this WiFi network with your phone or computer.
  30.   3. Open a browser and go to the address shown in the Serial Monitor (usually http://192.168.4.1).
  31.   4. Follow the web interface steps:
  32.      - Set the current time (syncs with your device).
  33.      - Set the motor run time (slider, 5–300 seconds).
  34.      - Select the hours you want the motor to run (checkbox grid).
  35.      - When all steps are complete, click "Start Operation".
  36.   5. The ESP32 will enter deep sleep and wake up at the scheduled hours to run the motor.
  37.   6. After each run, a Telegram message will be sent (see below for setup).
  38.   7. To reset all settings, connect GPIO14 to 3.3V and reset the ESP32.
  39.  
  40.   TELEGRAM NOTIFICATION SETUP:
  41.   ----------------------------
  42.   1. Create a Telegram bot using BotFather and get your bot token.
  43.   2. Start a chat with your bot and get your chat ID (see UniversalTelegramBot library examples).
  44.   3. Enter your WiFi credentials, bot token, and chat ID in the placeholders at the top of this sketch.
  45.   4. Install the UniversalTelegramBot and WiFiClientSecure libraries via the Arduino Library Manager.
  46.   5. Ensure your ESP32 can connect to your WiFi for Telegram notifications.
  47.  
  48.   DEPENDENCIES:
  49.   -------------
  50.   - ESP32 board support (install via Arduino Board Manager)
  51.   - UniversalTelegramBot library
  52.   - WiFiClientSecure library
  53.   - ESP32Time, WiFi, EEPROM libraries (included with ESP32 core)
  54.  
  55. */
  56.  
  57. #include "soc/soc.h"           // Brownout error fix
  58. #include "soc/rtc_cntl_reg.h"  // Brownout error fix
  59.  
  60. #include "driver/rtc_io.h" // https://github.com/pycom/esp-idf-2.0/blob/master/components/driver/include/driver/rtc_io.h
  61.  
  62. #include <ESP32Time.h>
  63. #include <WiFi.h>
  64. #include <EEPROM.h>
  65. #include <WiFiClientSecure.h>
  66. #include <UniversalTelegramBot.h>
  67.  
  68. #define uS_TO_S_FACTOR 1000000ULL
  69. #define DEFAULT_MOTOR_ON_TIME 5
  70. #define MIN_MOTOR_ON_TIME 5
  71. #define MAX_MOTOR_ON_TIME 300
  72. #define MOTOR_PIN 33
  73. #define LED_PIN 32
  74. #define WAKEUP_GPIO GPIO_NUM_14 // Only RTC IO are allowed
  75. #define EEPROM_SIZE 512
  76.  
  77. // EEPROM Memory Map
  78. #define EEPROM_MAGIC_ADDR 0
  79. #define EEPROM_SCHEDULE_ADDR 4
  80. #define EEPROM_RUNTIME_ADDR 28
  81. #define EEPROM_LAST_SYNC_ADDR 32
  82. #define EEPROM_MAGIC_NUMBER 0xDEADBEEF
  83. #define MAX_TIME_STALENESS 7 * 24 * 3600
  84.  
  85. const char *ssid = "ESP32-Motor-Timer";
  86. const char *password = "12345678";
  87.  
  88. ESP32Time rtc(3600);
  89. WiFiServer server(80);
  90.  
  91. // RTC memory variables
  92. RTC_DATA_ATTR bool timeWasSet = false;
  93. RTC_DATA_ATTR bool scheduleWasSet = false;
  94. RTC_DATA_ATTR bool runTimeWasSet = false;
  95. RTC_DATA_ATTR int bootCount = 0;
  96. RTC_DATA_ATTR unsigned long webServerStartTime = 0;
  97. RTC_DATA_ATTR bool usingBackupSettings = false;
  98. RTC_DATA_ATTR bool hourlySchedule[24] = {0};
  99. RTC_DATA_ATTR int motorRunTime = DEFAULT_MOTOR_ON_TIME;
  100.  
  101. // --- Telegram Bot Placeholders ---
  102. const char* TELEGRAM_WIFI_SSID = "YOUR_WIFI_SSID";
  103. const char* TELEGRAM_WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
  104. const char* BOT_TOKEN = "YOUR_BOT_TOKEN";
  105. const char* CHAT_ID = "YOUR_CHAT_ID";
  106.  
  107. WiFiClientSecure secured_client;
  108. UniversalTelegramBot bot(BOT_TOKEN, secured_client);
  109.  
  110. struct EEPROMSettings {
  111.   uint32_t magic;
  112.   bool schedule[24];
  113.   int runTime;
  114.   uint32_t lastSyncTime;
  115. };
  116.  
  117. void setup() {
  118.   Serial.begin(115200);
  119.   delay(1000);
  120.   EEPROM.begin(EEPROM_SIZE);
  121.  
  122.   bootCount++;
  123.   Serial.println("Boot count: " + String(bootCount));
  124.  
  125.   pinMode(MOTOR_PIN, OUTPUT);
  126.   pinMode(LED_PIN, OUTPUT);
  127.   digitalWrite(MOTOR_PIN, LOW);
  128.  
  129.   printWakeupReason();
  130.  
  131.   // Check if woken up by GPIO14 - clear EEPROM and reset
  132.   if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
  133.     Serial.println("=== GPIO RESET TRIGGERED ===");
  134.     Serial.println("Clearing EEPROM settings...");
  135.     clearEEPROMSettings();
  136.    
  137.     // Reset RTC memory flags
  138.     timeWasSet = false;
  139.     scheduleWasSet = false;
  140.     runTimeWasSet = false;
  141.     usingBackupSettings = false;
  142.    
  143.     Serial.println("EEPROM cleared - entering setup mode");
  144.     webServerStartTime = millis();
  145.     setupWebServer();
  146.     return;
  147.   }
  148.  
  149.   // Normal wakeup logic
  150.   if (timeWasSet && scheduleWasSet && runTimeWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
  151.     handleScheduledWakeup();
  152.   } else {
  153.     if (tryLoadBackupSettings()) {
  154.       Serial.println("=== POWER FAILURE RECOVERY ===");
  155.       Serial.println("Loaded backup settings from EEPROM");
  156.       printCurrentSettings();
  157.      
  158.       timeWasSet = false;
  159.       usingBackupSettings = true;
  160.      
  161.       EEPROMSettings settings;
  162.       loadSettingsFromEEPROM(settings);
  163.       uint32_t currentEpoch = settings.lastSyncTime + (millis() / 1000);
  164.       rtc.setTime(currentEpoch);
  165.       timeWasSet = true;
  166.      
  167.       Serial.println("Estimated time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  168.       scheduleNextWakeup();
  169.     } else {
  170.       Serial.println("No backup settings found - entering setup mode");
  171.       webServerStartTime = millis();
  172.       setupWebServer();
  173.     }
  174.   }
  175. }
  176.  
  177. void loop() {
  178.   if (!timeWasSet || !scheduleWasSet || !runTimeWasSet) {
  179.     if (millis() - webServerStartTime > 240000) {
  180.       if (tryLoadBackupSettings()) {
  181.         Serial.println("Timeout reached - falling back to EEPROM settings");
  182.         usingBackupSettings = true;
  183.        
  184.         EEPROMSettings settings;
  185.         loadSettingsFromEEPROM(settings);
  186.         uint32_t estimatedTime = settings.lastSyncTime + (millis() / 1000);
  187.         rtc.setTime(estimatedTime);
  188.         timeWasSet = true;
  189.        
  190.         scheduleNextWakeup();
  191.       } else {
  192.         Serial.println("No backup settings - entering 3-hour sleep");
  193.         setupSleepWakeup();
  194.         esp_deep_sleep_start();
  195.       }
  196.     }
  197.     handleWebClient();
  198.   }
  199. }
  200.  
  201. void clearEEPROMSettings() {
  202.   // Clear magic number to invalidate settings
  203.   uint32_t clearMagic = 0x00000000;
  204.   EEPROM.put(EEPROM_MAGIC_ADDR, clearMagic);
  205.   EEPROM.commit();
  206.   Serial.println("EEPROM settings cleared");
  207. }
  208.  
  209. bool tryLoadBackupSettings() {
  210.   EEPROMSettings settings;
  211.   if (loadSettingsFromEEPROM(settings)) {
  212.     for (int i = 0; i < 24; i++) {
  213.       hourlySchedule[i] = settings.schedule[i];
  214.     }
  215.     motorRunTime = settings.runTime;
  216.     scheduleWasSet = true;
  217.     runTimeWasSet = true;
  218.     return true;
  219.   }
  220.   return false;
  221. }
  222.  
  223. bool loadSettingsFromEEPROM(EEPROMSettings &settings) {
  224.   uint32_t magic;
  225.   EEPROM.get(EEPROM_MAGIC_ADDR, magic);
  226.  
  227.   if (magic != EEPROM_MAGIC_NUMBER) {
  228.     Serial.println("No valid EEPROM settings found");
  229.     return false;
  230.   }
  231.  
  232.   EEPROM.get(EEPROM_MAGIC_ADDR, settings.magic);
  233.   for (int i = 0; i < 24; i++) {
  234.     settings.schedule[i] = EEPROM.read(EEPROM_SCHEDULE_ADDR + i);
  235.   }
  236.   EEPROM.get(EEPROM_RUNTIME_ADDR, settings.runTime);
  237.   EEPROM.get(EEPROM_LAST_SYNC_ADDR, settings.lastSyncTime);
  238.  
  239.   if (settings.runTime < MIN_MOTOR_ON_TIME || settings.runTime > MAX_MOTOR_ON_TIME) {
  240.     Serial.println("Invalid run time in EEPROM: " + String(settings.runTime));
  241.     return false;
  242.   }
  243.  
  244.   Serial.println("Valid EEPROM settings loaded");
  245.   return true;
  246. }
  247.  
  248. void saveSettingsToEEPROM() {
  249.   Serial.println("Saving settings to EEPROM...");
  250.  
  251.   EEPROMSettings settings;
  252.   settings.magic = EEPROM_MAGIC_NUMBER;
  253.  
  254.   for (int i = 0; i < 24; i++) {
  255.     settings.schedule[i] = hourlySchedule[i];
  256.   }
  257.   settings.runTime = motorRunTime;
  258.   settings.lastSyncTime = rtc.getEpoch();
  259.  
  260.   EEPROM.put(EEPROM_MAGIC_ADDR, settings.magic);
  261.   for (int i = 0; i < 24; i++) {
  262.     EEPROM.write(EEPROM_SCHEDULE_ADDR + i, settings.schedule[i]);
  263.   }
  264.   EEPROM.put(EEPROM_RUNTIME_ADDR, settings.runTime);
  265.   EEPROM.put(EEPROM_LAST_SYNC_ADDR, settings.lastSyncTime);
  266.   EEPROM.commit();
  267.  
  268.   Serial.println("Settings saved to EEPROM successfully");
  269.   printCurrentSettings();
  270. }
  271.  
  272. void printCurrentSettings() {
  273.   Serial.println("Current Settings:");
  274.   Serial.println("- Motor run time: " + String(motorRunTime) + " seconds");
  275.   Serial.print("- Schedule: ");
  276.  
  277.   int count = 0;
  278.   for (int h = 0; h < 24; h++) {
  279.     if (hourlySchedule[h]) {
  280.       if (count > 0) Serial.print(", ");
  281.       Serial.print(String(h < 10 ? "0" : "") + String(h) + ":00");
  282.       count++;
  283.     }
  284.   }
  285.  
  286.   if (count == 0) {
  287.     Serial.println("No hours scheduled");
  288.   } else {
  289.     Serial.println(" (" + String(count) + " times/day)");
  290.     Serial.println("- Total daily runtime: " + String(count * motorRunTime) + " seconds");
  291.   }
  292. }
  293.  
  294. void handleScheduledWakeup() {
  295.   Serial.println("\n=== Scheduled Wake-up ===");
  296.   Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  297.   Serial.println("Motor run time: " + String(motorRunTime) + " seconds");
  298.  
  299.   if (usingBackupSettings) {
  300.     Serial.println("Running on backup settings from EEPROM");
  301.   }
  302.  
  303.   runMotor();
  304.   scheduleNextWakeup();
  305. }
  306.  
  307. void runMotor() {
  308.   Serial.println("Starting motor for " + String(motorRunTime) + " seconds...");
  309.  
  310.   digitalWrite(LED_PIN, HIGH);
  311.   digitalWrite(MOTOR_PIN, HIGH);
  312.  
  313.   for (int i = motorRunTime; i > 0; i--) {
  314.     Serial.println("Motor running... " + String(i) + "s remaining");
  315.     delay(1000);
  316.  
  317.     int blinkInterval = (motorRunTime < 10) ? 1 : 5;
  318.     if (i % blinkInterval == 0) {
  319.       digitalWrite(LED_PIN, LOW);
  320.       delay(100);
  321.       digitalWrite(LED_PIN, HIGH);
  322.     }
  323.   }
  324.  
  325.   digitalWrite(MOTOR_PIN, LOW);
  326.   digitalWrite(LED_PIN, LOW);
  327.   Serial.println("Motor stopped.");
  328.  
  329.   // --- Send Telegram notification ---
  330.   String msg = "Motor finished running for " + String(motorRunTime) + " seconds at " + rtc.getTime("%H:%M:%S %d/%m/%Y");
  331.   sendTelegramMessage(msg);
  332. }
  333.  
  334. void setupSleepWakeup() {
  335.   // Setup timer wakeup
  336.   esp_sleep_enable_timer_wakeup(3 * 3600 * uS_TO_S_FACTOR);
  337.  
  338.   // Setup GPIO wakeup for reset functionality
  339.   esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1); // 1 = High trigger
  340.   rtc_gpio_pullup_dis(WAKEUP_GPIO);
  341.   rtc_gpio_pulldown_en(WAKEUP_GPIO);
  342.   Serial.println("Setup ESP32 to wake up on GPIO14 trigger (reset mode)");
  343. }
  344.  
  345. void scheduleNextWakeup() {
  346.   struct tm timeinfo = rtc.getTimeStruct();
  347.   int currentHour = timeinfo.tm_hour;
  348.   int currentMinute = timeinfo.tm_min;
  349.  
  350.   unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
  351.  
  352.   if (sleepTime > 0) {
  353.     Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
  354.                    String((sleepTime % 3600) / 60) + " minutes");
  355.  
  356.     esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
  357.    
  358.     // Always enable GPIO wakeup for reset functionality
  359.     esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);
  360.     rtc_gpio_pullup_dis(WAKEUP_GPIO);
  361.     rtc_gpio_pulldown_en(WAKEUP_GPIO);
  362.    
  363.     Serial.println("Entering deep sleep... (GPIO14 HIGH = reset)");
  364.     Serial.flush();
  365.     esp_deep_sleep_start();
  366.   } else {
  367.     Serial.println("No scheduled hours found - entering 24-hour sleep");
  368.     esp_sleep_enable_timer_wakeup(24 * 3600 * uS_TO_S_FACTOR);
  369.    
  370.     esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);
  371.     rtc_gpio_pullup_dis(WAKEUP_GPIO);
  372.     rtc_gpio_pulldown_en(WAKEUP_GPIO);
  373.    
  374.     esp_deep_sleep_start();
  375.   }
  376. }
  377.  
  378. unsigned long calculateSleepTime(int currentHour, int currentMinute) {
  379.   int nextHour = -1;
  380.  
  381.   for (int h = currentHour + 1; h < 24; h++) {
  382.     if (hourlySchedule[h]) {
  383.       nextHour = h;
  384.       break;
  385.     }
  386.   }
  387.  
  388.   if (nextHour == -1) {
  389.     for (int h = 0; h < 24; h++) {
  390.       if (hourlySchedule[h]) {
  391.         nextHour = h + 24;
  392.         break;
  393.       }
  394.     }
  395.   }
  396.  
  397.   if (nextHour == -1) return 0;
  398.  
  399.   int currentTotalMinutes = currentHour * 60 + currentMinute;
  400.   int nextTotalMinutes = (nextHour % 24) * 60;
  401.  
  402.   if (nextHour >= 24) {
  403.     nextTotalMinutes += 24 * 60;
  404.   }
  405.  
  406.   int sleepMinutes = nextTotalMinutes - currentTotalMinutes;
  407.   return sleepMinutes * 60;
  408. }
  409.  
  410. void setupWebServer() {
  411.   Serial.println("\n=== Setting up Web Server for Configuration ===");
  412.   Serial.println("Connect to WiFi network: " + String(ssid));
  413.   Serial.println("Password: " + String(password));
  414.   Serial.println("TIMEOUT: 4 minutes");
  415.   Serial.println("RESET: Connect GPIO14 to 3.3V and reset ESP32");
  416.  
  417.   WiFi.softAP(ssid, password);
  418.   IPAddress IP = WiFi.softAPIP();
  419.   Serial.println("Web interface: http://" + IP.toString());
  420.   server.begin();
  421.  
  422.   for (int i = 0; i < 10; i++) {
  423.     digitalWrite(LED_PIN, HIGH);
  424.     delay(200);
  425.     digitalWrite(LED_PIN, LOW);
  426.     delay(200);
  427.   }
  428. }
  429.  
  430. void handleWebClient() {
  431.   WiFiClient client = server.available();
  432.  
  433.   if (client) {
  434.     Serial.println("Client connected");
  435.     String currentLine = "";
  436.  
  437.     while (client.connected()) {
  438.       if (client.available()) {
  439.         char c = client.read();
  440.         if (c == '\n') {
  441.           if (currentLine.length() == 0) {
  442.             sendWebPage(client);
  443.             break;
  444.           } else {
  445.             currentLine = "";
  446.           }
  447.         } else if (c != '\r') {
  448.           currentLine += c;
  449.         }
  450.  
  451.         if (currentLine.endsWith("POST /syncTime")) {
  452.           handleTimeSyncRequest(client);
  453.         } else if (currentLine.endsWith("POST /setRunTime")) {
  454.           handleRunTimeRequest(client);
  455.         } else if (currentLine.endsWith("POST /setSchedule")) {
  456.           handleScheduleRequest(client);
  457.         }
  458.       }
  459.     }
  460.     client.stop();
  461.   }
  462. }
  463.  
  464. // Simplified web page (keeping core functionality)
  465. void sendWebPage(WiFiClient &client) {
  466.   unsigned long elapsed = millis() - webServerStartTime;
  467.   unsigned long remaining = (240000 - elapsed) / 1000;
  468.  
  469.   client.println("HTTP/1.1 200 OK");
  470.   client.println("Content-type:text/html");
  471.   client.println();
  472.   client.println("<!DOCTYPE html><html>");
  473.   client.println("<head><title>ESP32 Motor Timer Setup</title>");
  474.   client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
  475.   client.println("<style>");
  476.   client.println("body{font-family:Arial;text-align:center;padding:20px;background:#f0f0f0;}");
  477.   client.println(".container{max-width:600px;margin:0 auto;background:white;padding:30px;border-radius:10px;}");
  478.   client.println("input,button{padding:10px;margin:5px;font-size:16px;border:1px solid #ddd;border-radius:5px;}");
  479.   client.println("button{background:#4CAF50;color:white;border:none;cursor:pointer;padding:15px 30px;}");
  480.   client.println(".timeout{color:red;font-weight:bold;}");
  481.   client.println(".step{margin:20px 0;padding:15px;background:#f9f9f9;border-radius:5px;}");
  482.   client.println(".schedule-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:20px 0;}");
  483.   client.println(".hour-checkbox{display:flex;align-items:center;padding:8px;background:white;border:1px solid #ddd;border-radius:5px;}");
  484.   client.println(".reset-info{background:#FFE6E6;padding:10px;border-radius:5px;margin:10px 0;border:1px solid #FF9999;}");
  485.   client.println("</style></head>");
  486.   client.println("<body>");
  487.   client.println("<div class='container'>");
  488.   client.println("<h1>ESP32 Motor Timer Setup</h1>");
  489.  
  490.   client.println("<div class='reset-info'>");
  491.   client.println("<strong>Reset Instructions:</strong><br>");
  492.   client.println("To clear settings: Connect GPIO14 to 3.3V and reset ESP32");
  493.   client.println("</div>");
  494.  
  495.   client.println("<div class='timeout'>Timeout: <span id='countdown'>" + String(remaining) + "</span> seconds</div>");
  496.  
  497.   // Step 1: Time sync
  498.   client.println("<div class='step'>");
  499.   client.println("<h3>Step 1: Set Current Time</h3>");
  500.   if (timeWasSet) {
  501.     client.println("<p>✅ Time set: " + rtc.getTime("%H:%M:%S %d/%m/%Y") + "</p>");
  502.   } else {
  503.     client.println("<h4 id='currentTime'></h4>");
  504.     client.println("<form action='/syncTime' method='POST'>");
  505.     client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
  506.     client.println("<button type='submit'>Sync Time</button>");
  507.     client.println("</form>");
  508.   }
  509.   client.println("</div>");
  510.  
  511.   // Step 2: Motor run time
  512.   client.println("<div class='step'>");
  513.   client.println("<h3>Step 2: Set Motor Run Time</h3>");
  514.   if (runTimeWasSet) {
  515.     client.println("<p>✅ Run time set: " + String(motorRunTime) + " seconds</p>");
  516.   } else {
  517.     client.println("<form action='/setRunTime' method='POST'>");
  518.     client.println("<input type='range' name='runTime' min='5' max='300' value='" + String(motorRunTime) + "'>");
  519.     client.println("<span id='runTimeDisplay'>" + String(motorRunTime) + "s</span>");
  520.     client.println("<button type='submit'>Set Run Time</button>");
  521.     client.println("</form>");
  522.   }
  523.   client.println("</div>");
  524.  
  525.   // Step 3: Schedule
  526.   client.println("<div class='step'>");
  527.   client.println("<h3>Step 3: Select Operation Hours</h3>");
  528.   if (scheduleWasSet) {
  529.     client.println("<p>✅ Schedule set!</p>");
  530.   } else {
  531.     client.println("<form action='/setSchedule' method='POST'>");
  532.     client.println("<div class='schedule-grid'>");
  533.     for (int h = 0; h < 24; h++) {
  534.       client.println("<div class='hour-checkbox'>");
  535.       client.println("<input type='checkbox' name='hour" + String(h) + "' value='1'>");
  536.       client.println("<label>" + String(h < 10 ? "0" : "") + String(h) + ":00</label>");
  537.       client.println("</div>");
  538.     }
  539.     client.println("</div>");
  540.     client.println("<button type='submit'>Set Schedule</button>");
  541.     client.println("</form>");
  542.   }
  543.   client.println("</div>");
  544.  
  545.   if (timeWasSet && scheduleWasSet && runTimeWasSet) {
  546.     client.println("<div class='step'>");
  547.     client.println("<h3>✅ Setup Complete!</h3>");
  548.     client.println("<p>ESP32 will now enter scheduled operation mode.</p>");
  549.     client.println("<button onclick='startOperation()'>Start Operation</button>");
  550.     client.println("</div>");
  551.   }
  552.  
  553.   client.println("</div>");
  554.  
  555.   // Simplified JavaScript
  556.   client.println("<script>");
  557.   client.println("var countdown=" + String(remaining) + ";");
  558.   client.println("function updateTime(){");
  559.   client.println("var now=new Date();");
  560.   client.println("if(document.getElementById('currentTime'))");
  561.   client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
  562.   client.println("if(document.getElementById('hiddenEpochTime'))");
  563.   client.println("document.getElementById('hiddenEpochTime').value=Math.floor(now.getTime()/1000);");
  564.   client.println("}");
  565.   client.println("function updateCountdown(){");
  566.   client.println("countdown--;document.getElementById('countdown').innerHTML=countdown;");
  567.   client.println("}");
  568.   client.println("function startOperation(){");
  569.   client.println("document.body.innerHTML='<h2>Starting operation...</h2>';");
  570.   client.println("}");
  571.   client.println("setInterval(updateTime,1000);setInterval(updateCountdown,1000);updateTime();");
  572.   client.println("</script></body></html>");
  573. }
  574.  
  575. void handleTimeSyncRequest(WiFiClient &client) {
  576.   String requestBody = "";
  577.   while (client.available()) {
  578.     requestBody += (char)client.read();
  579.   }
  580.  
  581.   int epochIndex = requestBody.indexOf("epochTime=");
  582.   if (epochIndex != -1) {
  583.     long epochTime = requestBody.substring(epochIndex + 10).toInt();
  584.     rtc.setTime(epochTime);
  585.     timeWasSet = true;
  586.     Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  587.   }
  588.  
  589.   client.println("HTTP/1.1 302 Found");
  590.   client.println("Location: /");
  591.   client.println();
  592. }
  593.  
  594. void handleRunTimeRequest(WiFiClient &client) {
  595.   String requestBody = "";
  596.   while (client.available()) {
  597.     requestBody += (char)client.read();
  598.   }
  599.  
  600.   int runTimeIndex = requestBody.indexOf("runTime=");
  601.   if (runTimeIndex != -1) {
  602.     int newRunTime = requestBody.substring(runTimeIndex + 8).toInt();
  603.     if (newRunTime >= MIN_MOTOR_ON_TIME && newRunTime <= MAX_MOTOR_ON_TIME) {
  604.       motorRunTime = newRunTime;
  605.       runTimeWasSet = true;
  606.       Serial.println("Motor run time set to: " + String(motorRunTime) + " seconds");
  607.     }
  608.   }
  609.  
  610.   client.println("HTTP/1.1 302 Found");
  611.   client.println("Location: /");
  612.   client.println();
  613. }
  614.  
  615. void handleScheduleRequest(WiFiClient &client) {
  616.   String requestBody = "";
  617.   while (client.available()) {
  618.     requestBody += (char)client.read();
  619.   }
  620.  
  621.   for (int h = 0; h < 24; h++) {
  622.     hourlySchedule[h] = false;
  623.   }
  624.  
  625.   for (int h = 0; h < 24; h++) {
  626.     String hourParam = "hour" + String(h) + "=1";
  627.     if (requestBody.indexOf(hourParam) != -1) {
  628.       hourlySchedule[h] = true;
  629.     }
  630.   }
  631.  
  632.   scheduleWasSet = true;
  633.  
  634.   // Save to EEPROM when configuration is complete
  635.   if (timeWasSet && scheduleWasSet && runTimeWasSet) {
  636.     saveSettingsToEEPROM();
  637.     delay(2000);
  638.     scheduleNextWakeup();
  639.   }
  640.  
  641.   client.println("HTTP/1.1 302 Found");
  642.   client.println("Location: /");
  643.   client.println();
  644. }
  645.  
  646. void printWakeupReason() {
  647.   esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  648.  
  649.   switch (wakeup_reason) {
  650.     case ESP_SLEEP_WAKEUP_TIMER:
  651.       Serial.println("Wake-up: Scheduled timer");
  652.       break;
  653.     case ESP_SLEEP_WAKEUP_EXT0:
  654.       Serial.println("Wake-up: GPIO14 external signal (RESET MODE)");
  655.       break;
  656.     default:
  657.       Serial.println("Wake-up: Power on or reset");
  658.       break;
  659.   }
  660. }
  661.  
  662. // Helper: Connect to WiFi for Telegram
  663. void connectToWiFiForTelegram() {
  664.   if (WiFi.status() == WL_CONNECTED) return;
  665.   WiFi.mode(WIFI_STA);
  666.   WiFi.begin(TELEGRAM_WIFI_SSID, TELEGRAM_WIFI_PASSWORD);
  667.   unsigned long startAttemptTime = millis();
  668.   while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 15000) {
  669.     delay(500);
  670.   }
  671. }
  672.  
  673. // Helper: Send Telegram message
  674. void sendTelegramMessage(const String& message) {
  675.   connectToWiFiForTelegram();
  676.   secured_client.setInsecure(); // For ESP32, disables certificate validation
  677.   bot.sendMessage(CHAT_ID, message, "");
  678. }
  679.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement