Advertisement
kodilivetv

esp32_timer_ontime_eeprom_gpioreset_24h

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