Advertisement
kodilivetv

esp32_timer_twicedaily

Jun 12th, 2025
799
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.12 KB | None | 0 0
  1. /*
  2.    ESP32 Scheduled Motor Control with Deep Sleep
  3.    
  4.    DESCRIPTION:
  5.    • Run a motor at two configurable times of day while deep-sleeping between operations.
  6.    
  7.    FEATURES:
  8.    • Schedules motor to run for 30 seconds at two daily times (configurable)
  9.    • Uses deep sleep between operations for maximum battery efficiency
  10.    • Web-based setup interface for time sync and schedule configuration
  11.    • Built-in LED status indicators during setup and operation
  12.    • RTC memory storage preserves settings through deep sleep cycles
  13.    • Automatic timeout protection (enters sleep if no configuration within 4 minutes)
  14.    
  15.    HARDWARE REQUIREMENTS:
  16.    • ESP32 development board
  17.    • MOSFET (for motor control) connected to GPIO 33
  18.    • Motor connected through MOSFET
  19.    • Built-in LED on GPIO 2 (most ESP32 boards)
  20.      
  21.    SETUP PROCESS:
  22.    1. Upload sketch to ESP32
  23.    2. Connect to WiFi network "ESP32-Motor-Timer" (password: 12345678)
  24.    3. Navigate to the IP address shown in Serial Monitor, usually 192.168.4.1
  25.    4. Sync current time from your browser
  26.    5. Set morning and evening schedule times
  27.    6. ESP32 automatically enters scheduled operation mode
  28.    
  29.    OPERATION:
  30.    • ESP32 sleeps in deep sleep mode (uses ~10µA)
  31.    • Wakes up at scheduled times
  32.    • Runs motor for 30 seconds with LED indication
  33.    • Calculates next wake time and returns to deep sleep
  34.    • Cycle repeats indefinitely
  35.    
  36.    DEFAULT SCHEDULE:
  37.    • Morning: 12:30 (configurable via web interface)
  38.    • Evening: 19:30 (configurable via web interface)
  39.    
  40.    TROUBLESHOOTING:
  41.    • If setup doesn't complete, ESP32 sleeps for 3 hours then restarts setup
  42.    • Power cycle to restart setup process
  43.    • Check Serial Monitor (115200 baud) for debugging information
  44.    • Time is automatically adjusted for Portuguese Summer Time (+1 hour)
  45.  
  46. */
  47.  
  48. #include <ESP32Time.h> // https://github.com/fbiego/ESP32Time
  49. #include <WiFi.h>
  50.  
  51. #define uS_TO_S_FACTOR 1000000ULL  // Conversion factor for micro seconds to seconds
  52. #define MOTOR_ON_TIME 30           // Motor run time in seconds
  53. #define MOTOR_PIN 33               // GPIO pin connected to MOSFET gate
  54. #define LED_PIN 2                  // Built-in LED for status indication
  55.  
  56. // WiFi credentials for initial time sync
  57. const char *ssid = "ESP32-Motor-Timer";
  58. const char *password = "12345678";
  59.  
  60. //ESP32Time rtc;
  61. ESP32Time rtc(3600);  // offset in seconds GMT+1 Portugal Summer Time
  62. WiFiServer server(80);
  63.  
  64. // RTC memory variables (survive deep sleep)
  65. RTC_DATA_ATTR bool timeWasSet = false;
  66. RTC_DATA_ATTR bool scheduleWasSet = false;
  67. RTC_DATA_ATTR int bootCount = 0;
  68. RTC_DATA_ATTR unsigned long webServerStartTime = 0;
  69.  
  70. // Schedule times (stored in RTC memory - user configurable)
  71. RTC_DATA_ATTR int MORNING_HOUR = 12;    // Default values
  72. RTC_DATA_ATTR int MORNING_MINUTE = 30;
  73. RTC_DATA_ATTR int EVENING_HOUR = 19;
  74. RTC_DATA_ATTR int EVENING_MINUTE = 30;
  75.  
  76. void setup() {
  77.   Serial.begin(115200);
  78.   delay(1000);
  79.  
  80.   bootCount++;
  81.   Serial.println("Boot count: " + String(bootCount));
  82.  
  83.   // Initialize pins
  84.   pinMode(MOTOR_PIN, OUTPUT);
  85.   pinMode(LED_PIN, OUTPUT);
  86.   digitalWrite(MOTOR_PIN, LOW);  // Ensure motor is off initially
  87.  
  88.   printWakeupReason();
  89.  
  90.   // Check if this is a scheduled wake-up and both time and schedule have been set
  91.   if (timeWasSet && scheduleWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
  92.     handleScheduledWakeup();
  93.   } else {
  94.     // First boot, manual reset, or incomplete setup - need web interface
  95.     webServerStartTime = millis();
  96.     setupWebServer();
  97.   }
  98. }
  99.  
  100. void loop() {
  101.   // Handle web server for time and schedule setting
  102.   if (!timeWasSet || !scheduleWasSet) {
  103.     // Check for 4-minute timeout
  104.     if (millis() - webServerStartTime > 240000) { // 4 minutes = 120,000 ms
  105.       Serial.println("Web server timeout - entering 3-hour sleep to save battery");
  106.       esp_sleep_enable_timer_wakeup(3 * 3600 * uS_TO_S_FACTOR); // 3 hours
  107.       esp_deep_sleep_start();
  108.     }
  109.     handleWebClient();
  110.   }
  111. }
  112.  
  113. void handleScheduledWakeup() {
  114.   Serial.println("\n=== Scheduled Wake-up ===");
  115.   Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  116.  
  117.   // Run the motor
  118.   runMotor();
  119.  
  120.   // Calculate next wake-up time
  121.   scheduleNextWakeup();
  122. }
  123.  
  124. void runMotor() {
  125.   Serial.println("Starting motor...");
  126.  
  127.   // Blink LED to indicate motor operation
  128.   digitalWrite(LED_PIN, HIGH);
  129.  
  130.   // Turn on motor via MOSFET
  131.   digitalWrite(MOTOR_PIN, HIGH);
  132.  
  133.   // Run for specified time with status updates
  134.   for (int i = MOTOR_ON_TIME; i > 0; i--) {
  135.     Serial.println("Motor running... " + String(i) + "s remaining");
  136.     delay(1000);
  137.  
  138.     // Blink LED every 5 seconds during operation
  139.     if (i % 5 == 0) {
  140.       digitalWrite(LED_PIN, LOW);
  141.       delay(100);
  142.       digitalWrite(LED_PIN, HIGH);
  143.     }
  144.   }
  145.  
  146.   // Turn off motor
  147.   digitalWrite(MOTOR_PIN, LOW);
  148.   digitalWrite(LED_PIN, LOW);
  149.  
  150.   Serial.println("Motor stopped.");
  151. }
  152.  
  153. void scheduleNextWakeup() {
  154.   struct tm timeinfo = rtc.getTimeStruct();
  155.   int currentHour = timeinfo.tm_hour;
  156.   int currentMinute = timeinfo.tm_min;
  157.  
  158.   // Calculate seconds until next scheduled time
  159.   unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
  160.  
  161.   Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
  162.                  String((sleepTime % 3600) / 60) + " minutes");
  163.  
  164.   // Configure and enter deep sleep
  165.   esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
  166.   Serial.println("Entering deep sleep...");
  167.   Serial.flush();
  168.   esp_deep_sleep_start();
  169. }
  170.  
  171. unsigned long calculateSleepTime(int currentHour, int currentMinute) {
  172.   int currentTotalMinutes = currentHour * 60 + currentMinute;
  173.   int morningTotalMinutes = MORNING_HOUR * 60 + MORNING_MINUTE;
  174.   int eveningTotalMinutes = EVENING_HOUR * 60 + EVENING_MINUTE;
  175.  
  176.   int nextWakeupMinutes;
  177.  
  178.   if (currentTotalMinutes < morningTotalMinutes) {
  179.     // Before morning time - wake up at morning time
  180.     nextWakeupMinutes = morningTotalMinutes;
  181.   } else if (currentTotalMinutes < eveningTotalMinutes) {
  182.     // Between morning and evening - wake up at evening time
  183.     nextWakeupMinutes = eveningTotalMinutes;
  184.   } else {
  185.     // After evening time - wake up next morning
  186.     nextWakeupMinutes = morningTotalMinutes + (24 * 60); // Next day
  187.   }
  188.  
  189.   int sleepMinutes = nextWakeupMinutes - currentTotalMinutes;
  190.   return sleepMinutes * 60; // Convert to seconds
  191. }
  192.  
  193. void setupWebServer() {
  194.   Serial.println("\n=== Setting up Web Server for Configuration ===");
  195.   Serial.println("Connect to WiFi network: " + String(ssid));
  196.   Serial.println("Password: " + String(password));
  197.   Serial.println("TIMEOUT: 4 minutes (will sleep for 3 hours if no configuration)");
  198.  
  199.   WiFi.softAP(ssid, password);
  200.   IPAddress IP = WiFi.softAPIP();
  201.   Serial.println("Web interface: http://" + IP.toString());
  202.   server.begin();
  203.  
  204.   // Blink LED to indicate setup mode
  205.   for (int i = 0; i < 10; i++) {
  206.     digitalWrite(LED_PIN, HIGH);
  207.     delay(200);
  208.     digitalWrite(LED_PIN, LOW);
  209.     delay(200);
  210.   }
  211. }
  212.  
  213. void handleWebClient() {
  214.   WiFiClient client = server.available();
  215.  
  216.   if (client) {
  217.     Serial.println("Client connected");
  218.     String currentLine = "";
  219.  
  220.     while (client.connected()) {
  221.       if (client.available()) {
  222.         char c = client.read();
  223.         if (c == '\n') {
  224.           if (currentLine.length() == 0) {
  225.             sendWebPage(client);
  226.             break;
  227.           } else {
  228.             currentLine = "";
  229.           }
  230.         } else if (c != '\r') {
  231.           currentLine += c;
  232.         }
  233.  
  234.         if (currentLine.endsWith("POST /syncTime")) {
  235.           handleTimeSyncRequest(client);
  236.         } else if (currentLine.endsWith("POST /setSchedule")) {
  237.           handleScheduleRequest(client);
  238.         }
  239.       }
  240.     }
  241.     client.stop();
  242.   }
  243. }
  244.  
  245. void sendWebPage(WiFiClient &client) {
  246.   // Calculate remaining time
  247.   unsigned long elapsed = millis() - webServerStartTime;
  248.   unsigned long remaining = (120000 - elapsed) / 1000; // seconds remaining
  249.  
  250.   client.println("HTTP/1.1 200 OK");
  251.   client.println("Content-type:text/html");
  252.   client.println();
  253.   client.println("<!DOCTYPE html><html>");
  254.   client.println("<head><title>ESP32 Motor Timer Setup</title>");
  255.   client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
  256.   client.println("<style>");
  257.   client.println("body{font-family:Arial;text-align:center;padding:20px;background:#f0f0f0;}");
  258.   client.println(".container{max-width:500px;margin:0 auto;background:white;padding:30px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}");
  259.   client.println("input,button{padding:10px;margin:5px;font-size:16px;border:1px solid #ddd;border-radius:5px;}");
  260.   client.println("button{background:#4CAF50;color:white;border:none;cursor:pointer;padding:15px 30px;}");
  261.   client.println("button:hover{background:#45a049;}");
  262.   client.println(".timeout{color:red;font-weight:bold;}");
  263.   client.println(".step{margin:20px 0;padding:15px;background:#f9f9f9;border-radius:5px;}");
  264.   client.println("</style></head>");
  265.   client.println("<body>");
  266.   client.println("<div class='container'>");
  267.   client.println("<h1>ESP32 Motor Timer Setup</h1>");
  268.   client.println("<div class='timeout'> Timeout: <span id='countdown'>" + String(remaining) + "</span> seconds</div>");
  269.  
  270.   // Step 1: Time sync
  271.   client.println("<div class='step'>");
  272.   client.println("<h3>Step 1: Set Current Time</h3>");
  273.   if (timeWasSet) {
  274.     client.println("<p>Time set: " + rtc.getTime("%H:%M:%S %d/%m/%Y") + "</p>");
  275.   } else {
  276.     client.println("<h4 id='currentTime'></h4>");
  277.     client.println("<form action='/syncTime' method='POST'>");
  278.     client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
  279.     client.println("<button type='submit'>Sync Time</button>");
  280.     client.println("</form>");
  281.   }
  282.   client.println("</div>");
  283.  
  284.   // Step 2: Schedule setup
  285.   client.println("<div class='step'>");
  286.   client.println("<h3>Step 2: Set Motor Schedule</h3>");
  287.   if (scheduleWasSet) {
  288.     client.println("<p>✅ Schedule set:</p>");
  289.     client.println("<p>Morning: " + String(MORNING_HOUR) + ":" + String(MORNING_MINUTE < 10 ? "0" : "") + String(MORNING_MINUTE) + "</p>");
  290.     client.println("<p>Evening: " + String(EVENING_HOUR) + ":" + String(EVENING_MINUTE < 10 ? "0" : "") + String(EVENING_MINUTE) + "</p>");
  291.     client.println("<p>Motor runs for 30 seconds each time</p>");
  292.   } else {
  293.     client.println("<form action='/setSchedule' method='POST'>");
  294.     client.println("<div>");
  295.     client.println("<h4>Morning Time:</h4>");
  296.     client.println("Hour: <input type='number' name='morningHour' min='0' max='23' value='" + String(MORNING_HOUR) + "' required> ");
  297.     client.println("Minute: <input type='number' name='morningMinute' min='0' max='59' value='" + String(MORNING_MINUTE) + "' required>");
  298.     client.println("</div>");
  299.     client.println("<div>");
  300.     client.println("<h4>Evening Time:</h4>");
  301.     client.println("Hour: <input type='number' name='eveningHour' min='0' max='23' value='" + String(EVENING_HOUR) + "' required> ");
  302.     client.println("Minute: <input type='number' name='eveningMinute' min='0' max='59' value='" + String(EVENING_MINUTE) + "' required>");
  303.     client.println("</div>");
  304.     client.println("<button type='submit'>Set Schedule</button>");
  305.     client.println("</form>");
  306.   }
  307.   client.println("</div>");
  308.  
  309.   if (timeWasSet && scheduleWasSet) {
  310.     client.println("<div class='step'>");
  311.     client.println("<h3>✅ Setup Complete!</h3>");
  312.     client.println("<p>ESP32 will now enter scheduled operation mode.</p>");
  313.     client.println("<button onclick='window.location.reload()'>Start Operation</button>");
  314.     client.println("</div>");
  315.   }
  316.  
  317.   client.println("</div>");
  318.  
  319.   // JavaScript for time updates and countdown
  320.   client.println("<script>");
  321.   client.println("var countdown = " + String(remaining) + ";");
  322.   client.println("function updateTime(){");
  323.   client.println("var now=new Date();");
  324.   client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
  325.   client.println("var epoch=Math.floor(now.getTime()/1000);");
  326.   client.println("document.getElementById('hiddenEpochTime').value=epoch;");
  327.   client.println("}");
  328.   client.println("function updateCountdown(){");
  329.   client.println("countdown--;");
  330.   client.println("document.getElementById('countdown').innerHTML=countdown;");
  331.   client.println("if(countdown<=0){");
  332.   client.println("document.body.innerHTML='<h2>Timeout reached - ESP32 entering sleep mode</h2>';");
  333.   client.println("}");
  334.   client.println("}");
  335.   client.println("setInterval(updateTime,1000);");
  336.   client.println("setInterval(updateCountdown,1000);");
  337.   client.println("updateTime();");
  338.   client.println("</script></body></html>");
  339.   client.println();
  340. }
  341.  
  342. void handleTimeSyncRequest(WiFiClient &client) {
  343.   String requestBody = "";
  344.   while (client.available()) {
  345.     requestBody += (char)client.read();
  346.   }
  347.  
  348.   int epochIndex = requestBody.indexOf("epochTime=");
  349.   if (epochIndex != -1) {
  350.     long epochTime = requestBody.substring(epochIndex + 10).toInt();
  351.  
  352.     rtc.setTime(epochTime);
  353.     timeWasSet = true;
  354.  
  355.     Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  356.  
  357.     // Send redirect back to main page
  358.     client.println("HTTP/1.1 302 Found");
  359.     client.println("Location: /");
  360.     client.println();
  361.   }
  362. }
  363.  
  364. void handleScheduleRequest(WiFiClient &client) {
  365.   String requestBody = "";
  366.   while (client.available()) {
  367.     requestBody += (char)client.read();
  368.   }
  369.  
  370.   // Parse form data
  371.   int morningHourIndex = requestBody.indexOf("morningHour=");
  372.   int morningMinuteIndex = requestBody.indexOf("morningMinute=");
  373.   int eveningHourIndex = requestBody.indexOf("eveningHour=");
  374.   int eveningMinuteIndex = requestBody.indexOf("eveningMinute=");
  375.  
  376.   if (morningHourIndex != -1 && morningMinuteIndex != -1 &&
  377.       eveningHourIndex != -1 && eveningMinuteIndex != -1) {
  378.  
  379.     // Extract values
  380.     String morningHourStr = extractFormValue(requestBody, "morningHour=");
  381.     String morningMinuteStr = extractFormValue(requestBody, "morningMinute=");
  382.     String eveningHourStr = extractFormValue(requestBody, "eveningHour=");
  383.     String eveningMinuteStr = extractFormValue(requestBody, "eveningMinute=");
  384.  
  385.     MORNING_HOUR = morningHourStr.toInt();
  386.     MORNING_MINUTE = morningMinuteStr.toInt();
  387.     EVENING_HOUR = eveningHourStr.toInt();
  388.     EVENING_MINUTE = eveningMinuteStr.toInt();
  389.  
  390.     // Validate times
  391.     if (MORNING_HOUR >= 0 && MORNING_HOUR <= 23 && MORNING_MINUTE >= 0 && MORNING_MINUTE <= 59 &&
  392.         EVENING_HOUR >= 0 && EVENING_HOUR <= 23 && EVENING_MINUTE >= 0 && EVENING_MINUTE <= 59) {
  393.  
  394.       scheduleWasSet = true;
  395.  
  396.       Serial.println("Schedule set:");
  397.       Serial.println("Morning: " + String(MORNING_HOUR) + ":" + String(MORNING_MINUTE));
  398.       Serial.println("Evening: " + String(EVENING_HOUR) + ":" + String(EVENING_MINUTE));
  399.  
  400.       // If both time and schedule are set, start operation
  401.       if (timeWasSet && scheduleWasSet) {
  402.         delay(2000); // Allow user to see confirmation
  403.         struct tm timeinfo = rtc.getTimeStruct();
  404.         scheduleNextWakeup();
  405.       }
  406.     }
  407.   }
  408.  
  409.   // Send redirect back to main page
  410.   client.println("HTTP/1.1 302 Found");
  411.   client.println("Location: /");
  412.   client.println();
  413. }
  414.  
  415. String extractFormValue(String data, String fieldName) {
  416.   int startIndex = data.indexOf(fieldName);
  417.   if (startIndex == -1) return "";
  418.  
  419.   startIndex += fieldName.length();
  420.   int endIndex = data.indexOf("&", startIndex);
  421.   if (endIndex == -1) endIndex = data.length();
  422.  
  423.   return data.substring(startIndex, endIndex);
  424. }
  425.  
  426. void printWakeupReason() {
  427.   esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  428.  
  429.   switch (wakeup_reason) {
  430.     case ESP_SLEEP_WAKEUP_TIMER:
  431.       Serial.println("Wake-up: Scheduled timer");
  432.       break;
  433.     case ESP_SLEEP_WAKEUP_EXT0:
  434.       Serial.println("Wake-up: External signal RTC_IO");
  435.       break;
  436.     case ESP_SLEEP_WAKEUP_EXT1:
  437.       Serial.println("Wake-up: External signal RTC_CNTL");
  438.       break;
  439.     default:
  440.       Serial.println("Wake-up: Power on or reset");
  441.       break;
  442.   }
  443. }
  444.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement