Advertisement
kodilivetv

esp32_motor_scheduler_24h

Jun 4th, 2025 (edited)
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.19 KB | Software | 0 0
  1. /*
  2.    ESP32 Scheduled Motor Control with Deep Sleep - Hourly Scheduling
  3.  
  4.    DESCRIPTION:
  5.    This sketch creates an automated motor control system that runs a motor at user-selected
  6.    hours of the day while maintaining ultra-low power consumption between operations.
  7.  
  8.    FEATURES:
  9.    • Allows selection of any hours (00:00-23:00) for motor operation via web interface
  10.    • Motor runs for 30 seconds at each selected hour
  11.    • Uses deep sleep between operations for maximum battery efficiency
  12.    • Web-based setup interface with 24 hourly checkboxes
  13.    • Built-in LED status indicators during setup and operation
  14.    • RTC memory storage preserves settings through deep sleep cycles
  15.    • Automatic timeout protection (enters sleep if no configuration within 4 minutes)
  16.  
  17.    HARDWARE REQUIREMENTS:
  18.    • ESP32 development board
  19.    • MOSFET (for motor control) connected to GPIO 33
  20.    • Motor connected through MOSFET
  21.    • Built-in LED on GPIO 2 (most ESP32 boards)
  22.    • Optional: External RTC battery for better timekeeping
  23.  
  24.    SETUP PROCESS:
  25.    1. Upload sketch to ESP32
  26.    2. Connect to WiFi network "ESP32-Motor-Timer" (password: 12345678)
  27.    3. Navigate to the IP address shown in Serial Monitor
  28.    4. Sync current time from your browser
  29.    5. Check boxes for desired operation hours (e.g., 09:00, 19:00, 21:00)
  30.    6. ESP32 automatically enters scheduled operation mode
  31.  
  32.    OPERATION:
  33.    • ESP32 sleeps in deep sleep mode (uses ~10µA)
  34.    • Wakes up at each selected hour
  35.    • Runs motor for 30 seconds with LED indication
  36.    • Calculates next scheduled hour and returns to deep sleep
  37.    • Cycle repeats based on selected schedule
  38.  
  39.    POWER CONSUMPTION:
  40.    • Deep sleep: ~10µA (months of battery life)
  41.    • Active operation: ~80mA for 30 seconds
  42.    • Setup mode: ~150mA (4 minute timeout)
  43.  
  44.    TROUBLESHOOTING:
  45.    • If setup doesn't complete, ESP32 sleeps for 3 hours then restarts setup
  46.    • Press reset button to restart setup process
  47.    • Check Serial Monitor (115200 baud) for debugging information
  48.    • Time is automatically adjusted for Portuguese Summer Time (+1 hour)
  49.  
  50.    LIBRARIES REQUIRED:
  51.    • ESP32Time (for RTC functionality)
  52.    • WiFi (built-in ESP32 library)
  53.  
  54.    MIT License - Free to use and modify
  55.    Based on ESP32Time library examples and ESP32 deep sleep functionality
  56. */
  57.  
  58. /*
  59.     Major Changes:
  60.  
  61.     Replaced morning/evening variables with a 24-hour boolean array hourlySchedule[24]
  62.     New Web Interface with:
  63.  
  64.     24 checkboxes arranged in a 4-column grid (00:00 to 23:00)
  65.     Visual feedback showing selected hours and total count
  66.     Better styling for the checkbox grid
  67.  
  68.  
  69.     Updated Sleep Calculation (calculateSleepTime()):
  70.  
  71.     Searches for the next enabled hour starting from current hour + 1
  72.     If no hours found today, searches from 00:00 next day
  73.     Returns 0 if no hours are scheduled (enters 24-hour sleep)
  74.  
  75.  
  76.     Enhanced Schedule Handling:
  77.  
  78.     Parses checkbox form data (checks for hour0=1, hour1=1, etc.)
  79.     Resets all hours first, then enables checked ones
  80.     Shows detailed feedback in Serial Monitor
  81.  
  82.  
  83.  
  84.     How It Works:
  85.  
  86.     Setup: User checks boxes for desired hours (e.g., 09:00, 19:00, 21:00)
  87.     Operation: Motor runs for 30 seconds at each selected hour
  88.     Sleep: ESP32 calculates time until next scheduled hour and sleeps
  89.     Repeat: Wakes up at next scheduled time and repeats
  90.  
  91.     Example Usage:
  92.  
  93.     Check boxes for 09:00, 19:00, and 21:00
  94.     Motor will run 3 times per day at those exact hours
  95.     ESP32 sleeps between operations for maximum battery life
  96.  
  97.     The interface now provides much more flexibility, allowing users to create custom schedules like:
  98.  
  99.     Every 4 hours: 00:00, 04:00, 08:00, 12:00, 16:00, 20:00
  100.     Business hours: 09:00, 12:00, 15:00, 18:00
  101.     Or any other combination of hours
  102. */
  103.  
  104. #include <ESP32Time.h>
  105. #include <WiFi.h>
  106.  
  107. #define uS_TO_S_FACTOR 1000000ULL  // Conversion factor for micro seconds to seconds
  108. #define MOTOR_ON_TIME 30           // Motor run time in seconds
  109. #define MOTOR_PIN 33               // GPIO pin connected to MOSFET gate
  110. #define LED_PIN 2                  // Built-in LED for status indication
  111.  
  112. // WiFi credentials for initial time sync
  113. const char *ssid = "ESP32-Motor-Timer";
  114. const char *password = "12345678";
  115.  
  116. //ESP32Time rtc;
  117. ESP32Time rtc(3600);  // offset in seconds GMT+1 Portugal Summer Time
  118. WiFiServer server(80);
  119.  
  120. // RTC memory variables (survive deep sleep)
  121. RTC_DATA_ATTR bool timeWasSet = false;
  122. RTC_DATA_ATTR bool scheduleWasSet = false;
  123. RTC_DATA_ATTR int bootCount = 0;
  124. RTC_DATA_ATTR unsigned long webServerStartTime = 0;
  125.  
  126. // Hourly schedule - 24 hour array (0=disabled, 1=enabled)
  127. RTC_DATA_ATTR bool hourlySchedule[24] = {0}; // All hours disabled by default
  128.  
  129. void setup() {
  130.   Serial.begin(115200);
  131.   delay(1000);
  132.  
  133.   bootCount++;
  134.   Serial.println("Boot count: " + String(bootCount));
  135.  
  136.   // Initialize pins
  137.   pinMode(MOTOR_PIN, OUTPUT);
  138.   pinMode(LED_PIN, OUTPUT);
  139.   digitalWrite(MOTOR_PIN, LOW);  // Ensure motor is off initially
  140.  
  141.   printWakeupReason();
  142.  
  143.   // Check if this is a scheduled wake-up and both time and schedule have been set
  144.   if (timeWasSet && scheduleWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
  145.     handleScheduledWakeup();
  146.   } else {
  147.     // First boot, manual reset, or incomplete setup - need web interface
  148.     webServerStartTime = millis();
  149.     setupWebServer();
  150.   }
  151. }
  152.  
  153. void loop() {
  154.   // Handle web server for time and schedule setting
  155.   if (!timeWasSet || !scheduleWasSet) {
  156.     // Check for 4-minute timeout
  157.     if (millis() - webServerStartTime > 240000) { // 4 minutes = 240,000 ms
  158.       Serial.println("Web server timeout - entering 3-hour sleep to save battery");
  159.       esp_sleep_enable_timer_wakeup(3 * 3600 * uS_TO_S_FACTOR); // 3 hours
  160.       esp_deep_sleep_start();
  161.     }
  162.     handleWebClient();
  163.   }
  164. }
  165.  
  166. void handleScheduledWakeup() {
  167.   Serial.println("\n=== Scheduled Wake-up ===");
  168.   Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  169.  
  170.   // Run the motor
  171.   runMotor();
  172.  
  173.   // Calculate next wake-up time
  174.   scheduleNextWakeup();
  175. }
  176.  
  177. void runMotor() {
  178.   Serial.println("Starting motor...");
  179.  
  180.   // Blink LED to indicate motor operation
  181.   digitalWrite(LED_PIN, HIGH);
  182.  
  183.   // Turn on motor via MOSFET
  184.   digitalWrite(MOTOR_PIN, HIGH);
  185.  
  186.   // Run for specified time with status updates
  187.   for (int i = MOTOR_ON_TIME; i > 0; i--) {
  188.     Serial.println("Motor running... " + String(i) + "s remaining");
  189.     delay(1000);
  190.  
  191.     // Blink LED every 5 seconds during operation
  192.     if (i % 5 == 0) {
  193.       digitalWrite(LED_PIN, LOW);
  194.       delay(100);
  195.       digitalWrite(LED_PIN, HIGH);
  196.     }
  197.   }
  198.  
  199.   // Turn off motor
  200.   digitalWrite(MOTOR_PIN, LOW);
  201.   digitalWrite(LED_PIN, LOW);
  202.  
  203.   Serial.println("Motor stopped.");
  204. }
  205.  
  206. void scheduleNextWakeup() {
  207.   struct tm timeinfo = rtc.getTimeStruct();
  208.   int currentHour = timeinfo.tm_hour;
  209.   int currentMinute = timeinfo.tm_min;
  210.  
  211.   // Calculate seconds until next scheduled hour
  212.   unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
  213.  
  214.   if (sleepTime > 0) {
  215.     Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
  216.                    String((sleepTime % 3600) / 60) + " minutes");
  217.  
  218.     // Configure and enter deep sleep
  219.     esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
  220.     Serial.println("Entering deep sleep...");
  221.     Serial.flush();
  222.     esp_deep_sleep_start();
  223.   } else {
  224.     Serial.println("No scheduled hours found - entering 24-hour sleep");
  225.     esp_sleep_enable_timer_wakeup(24 * 3600 * uS_TO_S_FACTOR); // 24 hours
  226.     esp_deep_sleep_start();
  227.   }
  228. }
  229.  
  230. unsigned long calculateSleepTime(int currentHour, int currentMinute) {
  231.   // Find next scheduled hour
  232.   int nextHour = -1;
  233.  
  234.   // First, check if there are any hours scheduled later today
  235.   for (int h = currentHour + 1; h < 24; h++) {
  236.     if (hourlySchedule[h]) {
  237.       nextHour = h;
  238.       break;
  239.     }
  240.   }
  241.  
  242.   // If no hours found later today, check from beginning of next day
  243.   if (nextHour == -1) {
  244.     for (int h = 0; h < 24; h++) {
  245.       if (hourlySchedule[h]) {
  246.         nextHour = h + 24; // Add 24 to indicate next day
  247.         break;
  248.       }
  249.     }
  250.   }
  251.  
  252.   // If still no hours found, return 0 (no schedule set)
  253.   if (nextHour == -1) {
  254.     return 0;
  255.   }
  256.  
  257.   // Calculate sleep time
  258.   int currentTotalMinutes = currentHour * 60 + currentMinute;
  259.   int nextTotalMinutes = (nextHour % 24) * 60; // Target minute is always 0
  260.  
  261.   // If next hour is tomorrow, add 24 hours worth of minutes
  262.   if (nextHour >= 24) {
  263.     nextTotalMinutes += 24 * 60;
  264.   }
  265.  
  266.   int sleepMinutes = nextTotalMinutes - currentTotalMinutes;
  267.   return sleepMinutes * 60; // Convert to seconds
  268. }
  269.  
  270. void setupWebServer() {
  271.   Serial.println("\n=== Setting up Web Server for Configuration ===");
  272.   Serial.println("Connect to WiFi network: " + String(ssid));
  273.   Serial.println("Password: " + String(password));
  274.   Serial.println("TIMEOUT: 4 minutes (will sleep for 3 hours if no configuration)");
  275.  
  276.   WiFi.softAP(ssid, password);
  277.   IPAddress IP = WiFi.softAPIP();
  278.   Serial.println("Web interface: http://" + IP.toString());
  279.   server.begin();
  280.  
  281.   // Blink LED to indicate setup mode
  282.   for (int i = 0; i < 10; i++) {
  283.     digitalWrite(LED_PIN, HIGH);
  284.     delay(200);
  285.     digitalWrite(LED_PIN, LOW);
  286.     delay(200);
  287.   }
  288. }
  289.  
  290. void handleWebClient() {
  291.   WiFiClient client = server.available();
  292.  
  293.   if (client) {
  294.     Serial.println("Client connected");
  295.     String currentLine = "";
  296.  
  297.     while (client.connected()) {
  298.       if (client.available()) {
  299.         char c = client.read();
  300.         if (c == '\n') {
  301.           if (currentLine.length() == 0) {
  302.             sendWebPage(client);
  303.             break;
  304.           } else {
  305.             currentLine = "";
  306.           }
  307.         } else if (c != '\r') {
  308.           currentLine += c;
  309.         }
  310.  
  311.         if (currentLine.endsWith("POST /syncTime")) {
  312.           handleTimeSyncRequest(client);
  313.         } else if (currentLine.endsWith("POST /setSchedule")) {
  314.           handleScheduleRequest(client);
  315.         }
  316.       }
  317.     }
  318.     client.stop();
  319.   }
  320. }
  321.  
  322. void sendWebPage(WiFiClient &client) {
  323.   // Calculate remaining time
  324.   unsigned long elapsed = millis() - webServerStartTime;
  325.   unsigned long remaining = (240000 - elapsed) / 1000; // seconds remaining
  326.  
  327.   client.println("HTTP/1.1 200 OK");
  328.   client.println("Content-type:text/html");
  329.   client.println();
  330.   client.println("<!DOCTYPE html><html>");
  331.   client.println("<head><title>ESP32 Motor Timer Setup</title>");
  332.   client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
  333.   client.println("<style>");
  334.   client.println("body{font-family:Arial;text-align:center;padding:20px;background:#f0f0f0;}");
  335.   client.println(".container{max-width:600px;margin:0 auto;background:white;padding:30px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}");
  336.   client.println("input,button{padding:10px;margin:5px;font-size:16px;border:1px solid #ddd;border-radius:5px;}");
  337.   client.println("button{background:#4CAF50;color:white;border:none;cursor:pointer;padding:15px 30px;}");
  338.   client.println("button:hover{background:#45a049;}");
  339.   client.println(".timeout{color:red;font-weight:bold;}");
  340.   client.println(".step{margin:20px 0;padding:15px;background:#f9f9f9;border-radius:5px;}");
  341.   client.println(".schedule-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:20px 0;}");
  342.   client.println(".hour-checkbox{display:flex;align-items:center;padding:8px;background:white;border:1px solid #ddd;border-radius:5px;}");
  343.   client.println(".hour-checkbox input{margin-right:8px;}");
  344.   client.println(".hour-checkbox:hover{background:#f0f8ff;}");
  345.   client.println(".selected-hours{background:#e8f5e8;padding:10px;border-radius:5px;margin:10px 0;}");
  346.   client.println("</style></head>");
  347.   client.println("<body>");
  348.   client.println("<div class='container'>");
  349.   client.println("<h1>ESP32 Motor Timer Setup</h1>");
  350.   client.println("<div class='timeout'>Timeout: <span id='countdown'>" + String(remaining) + "</span> seconds</div>");
  351.  
  352.   // Step 1: Time sync
  353.   client.println("<div class='step'>");
  354.   client.println("<h3>Step 1: Set Current Time</h3>");
  355.   if (timeWasSet) {
  356.     client.println("<p>✅ Time set: " + rtc.getTime("%H:%M:%S %d/%m/%Y") + "</p>");
  357.   } else {
  358.     client.println("<h4 id='currentTime'></h4>");
  359.     client.println("<form action='/syncTime' method='POST'>");
  360.     client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
  361.     client.println("<button type='submit'>Sync Time</button>");
  362.     client.println("</form>");
  363.   }
  364.   client.println("</div>");
  365.  
  366.   // Step 2: Schedule setup
  367.   client.println("<div class='step'>");
  368.   client.println("<h3>Step 2: Select Operation Hours</h3>");
  369.   client.println("<p>Check the boxes for hours when the motor should run (30 seconds each time):</p>");
  370.  
  371.   if (scheduleWasSet) {
  372.     client.println("<div class='selected-hours'>");
  373.     client.println("<p>✅ Schedule set! Motor will run at:</p>");
  374.     String selectedHours = "";
  375.     int count = 0;
  376.     for (int h = 0; h < 24; h++) {
  377.       if (hourlySchedule[h]) {
  378.         if (count > 0) selectedHours += ", ";
  379.         selectedHours += String(h < 10 ? "0" : "") + String(h) + ":00";
  380.         count++;
  381.       }
  382.     }
  383.     if (count == 0) {
  384.       client.println("<p>No hours selected</p>");
  385.     } else {
  386.       client.println("<p>" + selectedHours + "</p>");
  387.       client.println("<p>Total: " + String(count) + " times per day</p>");
  388.     }
  389.     client.println("</div>");
  390.   } else {
  391.     client.println("<form action='/setSchedule' method='POST'>");
  392.     client.println("<div class='schedule-grid'>");
  393.  
  394.     // Create 24 checkboxes for each hour
  395.     for (int h = 0; h < 24; h++) {
  396.       client.println("<div class='hour-checkbox'>");
  397.       client.println("<input type='checkbox' name='hour" + String(h) + "' value='1'" +
  398.                      (hourlySchedule[h] ? " checked" : "") + ">");
  399.       client.println("<label>" + String(h < 10 ? "0" : "") + String(h) + ":00</label>");
  400.       client.println("</div>");
  401.     }
  402.  
  403.     client.println("</div>");
  404.     client.println("<button type='submit'>Set Schedule</button>");
  405.     client.println("</form>");
  406.   }
  407.   client.println("</div>");
  408.  
  409.   if (timeWasSet && scheduleWasSet) {
  410.     client.println("<div class='step'>");
  411.     client.println("<h3>✅ Setup Complete!</h3>");
  412.     client.println("<p>ESP32 will now enter scheduled operation mode.</p>");
  413.     client.println("<button onclick='startOperation()'>Start Operation</button>");
  414.     client.println("</div>");
  415.   }
  416.  
  417.   client.println("</div>");
  418.  
  419.   // JavaScript for time updates and countdown
  420.   client.println("<script>");
  421.   client.println("var countdown = " + String(remaining) + ";");
  422.   client.println("function updateTime(){");
  423.   client.println("var now=new Date();");
  424.   client.println("if(document.getElementById('currentTime'))");
  425.   client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
  426.   client.println("if(document.getElementById('hiddenEpochTime'))");
  427.   client.println("document.getElementById('hiddenEpochTime').value=Math.floor(now.getTime()/1000);");
  428.   client.println("}");
  429.   client.println("function updateCountdown(){");
  430.   client.println("countdown--;");
  431.   client.println("document.getElementById('countdown').innerHTML=countdown;");
  432.   client.println("if(countdown<=0){");
  433.   client.println("document.body.innerHTML='<h2>Timeout reached - ESP32 entering sleep mode</h2>';");
  434.   client.println("}");
  435.   client.println("}");
  436.   client.println("function startOperation(){");
  437.   client.println("document.body.innerHTML='<h2>Starting scheduled operation...</h2><p>ESP32 entering deep sleep mode</p>';");
  438.   client.println("setTimeout(function(){window.location.reload();},2000);");
  439.   client.println("}");
  440.   client.println("setInterval(updateTime,1000);");
  441.   client.println("setInterval(updateCountdown,1000);");
  442.   client.println("updateTime();");
  443.   client.println("</script></body></html>");
  444.   client.println();
  445. }
  446.  
  447. void handleTimeSyncRequest(WiFiClient &client) {
  448.   String requestBody = "";
  449.   while (client.available()) {
  450.     requestBody += (char)client.read();
  451.   }
  452.  
  453.   int epochIndex = requestBody.indexOf("epochTime=");
  454.   if (epochIndex != -1) {
  455.     long epochTime = requestBody.substring(epochIndex + 10).toInt();
  456.     rtc.setTime(epochTime);
  457.     timeWasSet = true;
  458.  
  459.     Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
  460.  
  461.     // Send redirect back to main page
  462.     client.println("HTTP/1.1 302 Found");
  463.     client.println("Location: /");
  464.     client.println();
  465.   }
  466. }
  467.  
  468. void handleScheduleRequest(WiFiClient &client) {
  469.   String requestBody = "";
  470.   while (client.available()) {
  471.     requestBody += (char)client.read();
  472.   }
  473.  
  474.   Serial.println("Schedule request body: " + requestBody);
  475.  
  476.   // Reset all hours first
  477.   for (int h = 0; h < 24; h++) {
  478.     hourlySchedule[h] = false;
  479.   }
  480.  
  481.   // Parse checkbox data
  482.   for (int h = 0; h < 24; h++) {
  483.     String hourParam = "hour" + String(h) + "=1";
  484.     if (requestBody.indexOf(hourParam) != -1) {
  485.       hourlySchedule[h] = true;
  486.       Serial.println("Hour " + String(h) + " enabled");
  487.     }
  488.   }
  489.  
  490.   scheduleWasSet = true;
  491.  
  492.   Serial.println("Schedule updated:");
  493.   int enabledCount = 0;
  494.   for (int h = 0; h < 24; h++) {
  495.     if (hourlySchedule[h]) {
  496.       Serial.println("- " + String(h) + ":00");
  497.       enabledCount++;
  498.     }
  499.   }
  500.   Serial.println("Total enabled hours: " + String(enabledCount));
  501.  
  502.   // If both time and schedule are set, start operation
  503.   if (timeWasSet && scheduleWasSet) {
  504.     delay(2000); // Allow user to see confirmation
  505.     scheduleNextWakeup();
  506.   }
  507.  
  508.   // Send redirect back to main page
  509.   client.println("HTTP/1.1 302 Found");
  510.   client.println("Location: /");
  511.   client.println();
  512. }
  513.  
  514. void printWakeupReason() {
  515.   esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
  516.  
  517.   switch (wakeup_reason) {
  518.     case ESP_SLEEP_WAKEUP_TIMER:
  519.       Serial.println("Wake-up: Scheduled timer");
  520.       break;
  521.     case ESP_SLEEP_WAKEUP_EXT0:
  522.       Serial.println("Wake-up: External signal RTC_IO");
  523.       break;
  524.     case ESP_SLEEP_WAKEUP_EXT1:
  525.       Serial.println("Wake-up: External signal RTC_CNTL");
  526.       break;
  527.     default:
  528.       Serial.println("Wake-up: Power on or reset");
  529.       break;
  530.   }
  531. }
  532.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement