Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- ESP32 Scheduled Motor Control with Deep Sleep - Hourly Scheduling
- DESCRIPTION:
- This sketch creates an automated motor control system that runs a motor at user-selected
- hours of the day while maintaining ultra-low power consumption between operations.
- FEATURES:
- • Allows selection of any hours (00:00-23:00) for motor operation via web interface
- • Motor runs for 30 seconds at each selected hour
- • Uses deep sleep between operations for maximum battery efficiency
- • Web-based setup interface with 24 hourly checkboxes
- • Built-in LED status indicators during setup and operation
- • RTC memory storage preserves settings through deep sleep cycles
- • Automatic timeout protection (enters sleep if no configuration within 4 minutes)
- HARDWARE REQUIREMENTS:
- • ESP32 development board
- • MOSFET (for motor control) connected to GPIO 33
- • Motor connected through MOSFET
- • Built-in LED on GPIO 2 (most ESP32 boards)
- • Optional: External RTC battery for better timekeeping
- SETUP PROCESS:
- 1. Upload sketch to ESP32
- 2. Connect to WiFi network "ESP32-Motor-Timer" (password: 12345678)
- 3. Navigate to the IP address shown in Serial Monitor
- 4. Sync current time from your browser
- 5. Check boxes for desired operation hours (e.g., 09:00, 19:00, 21:00)
- 6. ESP32 automatically enters scheduled operation mode
- OPERATION:
- • ESP32 sleeps in deep sleep mode (uses ~10µA)
- • Wakes up at each selected hour
- • Runs motor for 30 seconds with LED indication
- • Calculates next scheduled hour and returns to deep sleep
- • Cycle repeats based on selected schedule
- POWER CONSUMPTION:
- • Deep sleep: ~10µA (months of battery life)
- • Active operation: ~80mA for 30 seconds
- • Setup mode: ~150mA (4 minute timeout)
- TROUBLESHOOTING:
- • If setup doesn't complete, ESP32 sleeps for 3 hours then restarts setup
- • Press reset button to restart setup process
- • Check Serial Monitor (115200 baud) for debugging information
- • Time is automatically adjusted for Portuguese Summer Time (+1 hour)
- LIBRARIES REQUIRED:
- • ESP32Time (for RTC functionality)
- • WiFi (built-in ESP32 library)
- MIT License - Free to use and modify
- Based on ESP32Time library examples and ESP32 deep sleep functionality
- */
- /*
- Major Changes:
- Replaced morning/evening variables with a 24-hour boolean array hourlySchedule[24]
- New Web Interface with:
- 24 checkboxes arranged in a 4-column grid (00:00 to 23:00)
- Visual feedback showing selected hours and total count
- Better styling for the checkbox grid
- Updated Sleep Calculation (calculateSleepTime()):
- Searches for the next enabled hour starting from current hour + 1
- If no hours found today, searches from 00:00 next day
- Returns 0 if no hours are scheduled (enters 24-hour sleep)
- Enhanced Schedule Handling:
- Parses checkbox form data (checks for hour0=1, hour1=1, etc.)
- Resets all hours first, then enables checked ones
- Shows detailed feedback in Serial Monitor
- How It Works:
- Setup: User checks boxes for desired hours (e.g., 09:00, 19:00, 21:00)
- Operation: Motor runs for 30 seconds at each selected hour
- Sleep: ESP32 calculates time until next scheduled hour and sleeps
- Repeat: Wakes up at next scheduled time and repeats
- Example Usage:
- Check boxes for 09:00, 19:00, and 21:00
- Motor will run 3 times per day at those exact hours
- ESP32 sleeps between operations for maximum battery life
- The interface now provides much more flexibility, allowing users to create custom schedules like:
- Every 4 hours: 00:00, 04:00, 08:00, 12:00, 16:00, 20:00
- Business hours: 09:00, 12:00, 15:00, 18:00
- Or any other combination of hours
- */
- #include <ESP32Time.h>
- #include <WiFi.h>
- #define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds
- #define MOTOR_ON_TIME 30 // Motor run time in seconds
- #define MOTOR_PIN 33 // GPIO pin connected to MOSFET gate
- #define LED_PIN 2 // Built-in LED for status indication
- // WiFi credentials for initial time sync
- const char *ssid = "ESP32-Motor-Timer";
- const char *password = "12345678";
- //ESP32Time rtc;
- ESP32Time rtc(3600); // offset in seconds GMT+1 Portugal Summer Time
- WiFiServer server(80);
- // RTC memory variables (survive deep sleep)
- RTC_DATA_ATTR bool timeWasSet = false;
- RTC_DATA_ATTR bool scheduleWasSet = false;
- RTC_DATA_ATTR int bootCount = 0;
- RTC_DATA_ATTR unsigned long webServerStartTime = 0;
- // Hourly schedule - 24 hour array (0=disabled, 1=enabled)
- RTC_DATA_ATTR bool hourlySchedule[24] = {0}; // All hours disabled by default
- void setup() {
- Serial.begin(115200);
- delay(1000);
- bootCount++;
- Serial.println("Boot count: " + String(bootCount));
- // Initialize pins
- pinMode(MOTOR_PIN, OUTPUT);
- pinMode(LED_PIN, OUTPUT);
- digitalWrite(MOTOR_PIN, LOW); // Ensure motor is off initially
- printWakeupReason();
- // Check if this is a scheduled wake-up and both time and schedule have been set
- if (timeWasSet && scheduleWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
- handleScheduledWakeup();
- } else {
- // First boot, manual reset, or incomplete setup - need web interface
- webServerStartTime = millis();
- setupWebServer();
- }
- }
- void loop() {
- // Handle web server for time and schedule setting
- if (!timeWasSet || !scheduleWasSet) {
- // Check for 4-minute timeout
- if (millis() - webServerStartTime > 240000) { // 4 minutes = 240,000 ms
- Serial.println("Web server timeout - entering 3-hour sleep to save battery");
- esp_sleep_enable_timer_wakeup(3 * 3600 * uS_TO_S_FACTOR); // 3 hours
- esp_deep_sleep_start();
- }
- handleWebClient();
- }
- }
- void handleScheduledWakeup() {
- Serial.println("\n=== Scheduled Wake-up ===");
- Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
- // Run the motor
- runMotor();
- // Calculate next wake-up time
- scheduleNextWakeup();
- }
- void runMotor() {
- Serial.println("Starting motor...");
- // Blink LED to indicate motor operation
- digitalWrite(LED_PIN, HIGH);
- // Turn on motor via MOSFET
- digitalWrite(MOTOR_PIN, HIGH);
- // Run for specified time with status updates
- for (int i = MOTOR_ON_TIME; i > 0; i--) {
- Serial.println("Motor running... " + String(i) + "s remaining");
- delay(1000);
- // Blink LED every 5 seconds during operation
- if (i % 5 == 0) {
- digitalWrite(LED_PIN, LOW);
- delay(100);
- digitalWrite(LED_PIN, HIGH);
- }
- }
- // Turn off motor
- digitalWrite(MOTOR_PIN, LOW);
- digitalWrite(LED_PIN, LOW);
- Serial.println("Motor stopped.");
- }
- void scheduleNextWakeup() {
- struct tm timeinfo = rtc.getTimeStruct();
- int currentHour = timeinfo.tm_hour;
- int currentMinute = timeinfo.tm_min;
- // Calculate seconds until next scheduled hour
- unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
- if (sleepTime > 0) {
- Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
- String((sleepTime % 3600) / 60) + " minutes");
- // Configure and enter deep sleep
- esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
- Serial.println("Entering deep sleep...");
- Serial.flush();
- esp_deep_sleep_start();
- } else {
- Serial.println("No scheduled hours found - entering 24-hour sleep");
- esp_sleep_enable_timer_wakeup(24 * 3600 * uS_TO_S_FACTOR); // 24 hours
- esp_deep_sleep_start();
- }
- }
- unsigned long calculateSleepTime(int currentHour, int currentMinute) {
- // Find next scheduled hour
- int nextHour = -1;
- // First, check if there are any hours scheduled later today
- for (int h = currentHour + 1; h < 24; h++) {
- if (hourlySchedule[h]) {
- nextHour = h;
- break;
- }
- }
- // If no hours found later today, check from beginning of next day
- if (nextHour == -1) {
- for (int h = 0; h < 24; h++) {
- if (hourlySchedule[h]) {
- nextHour = h + 24; // Add 24 to indicate next day
- break;
- }
- }
- }
- // If still no hours found, return 0 (no schedule set)
- if (nextHour == -1) {
- return 0;
- }
- // Calculate sleep time
- int currentTotalMinutes = currentHour * 60 + currentMinute;
- int nextTotalMinutes = (nextHour % 24) * 60; // Target minute is always 0
- // If next hour is tomorrow, add 24 hours worth of minutes
- if (nextHour >= 24) {
- nextTotalMinutes += 24 * 60;
- }
- int sleepMinutes = nextTotalMinutes - currentTotalMinutes;
- return sleepMinutes * 60; // Convert to seconds
- }
- void setupWebServer() {
- Serial.println("\n=== Setting up Web Server for Configuration ===");
- Serial.println("Connect to WiFi network: " + String(ssid));
- Serial.println("Password: " + String(password));
- Serial.println("TIMEOUT: 4 minutes (will sleep for 3 hours if no configuration)");
- WiFi.softAP(ssid, password);
- IPAddress IP = WiFi.softAPIP();
- Serial.println("Web interface: http://" + IP.toString());
- server.begin();
- // Blink LED to indicate setup mode
- for (int i = 0; i < 10; i++) {
- digitalWrite(LED_PIN, HIGH);
- delay(200);
- digitalWrite(LED_PIN, LOW);
- delay(200);
- }
- }
- void handleWebClient() {
- WiFiClient client = server.available();
- if (client) {
- Serial.println("Client connected");
- String currentLine = "";
- while (client.connected()) {
- if (client.available()) {
- char c = client.read();
- if (c == '\n') {
- if (currentLine.length() == 0) {
- sendWebPage(client);
- break;
- } else {
- currentLine = "";
- }
- } else if (c != '\r') {
- currentLine += c;
- }
- if (currentLine.endsWith("POST /syncTime")) {
- handleTimeSyncRequest(client);
- } else if (currentLine.endsWith("POST /setSchedule")) {
- handleScheduleRequest(client);
- }
- }
- }
- client.stop();
- }
- }
- void sendWebPage(WiFiClient &client) {
- // Calculate remaining time
- unsigned long elapsed = millis() - webServerStartTime;
- unsigned long remaining = (240000 - elapsed) / 1000; // seconds remaining
- client.println("HTTP/1.1 200 OK");
- client.println("Content-type:text/html");
- client.println();
- client.println("<!DOCTYPE html><html>");
- client.println("<head><title>ESP32 Motor Timer Setup</title>");
- client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
- client.println("<style>");
- client.println("body{font-family:Arial;text-align:center;padding:20px;background:#f0f0f0;}");
- 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);}");
- client.println("input,button{padding:10px;margin:5px;font-size:16px;border:1px solid #ddd;border-radius:5px;}");
- client.println("button{background:#4CAF50;color:white;border:none;cursor:pointer;padding:15px 30px;}");
- client.println("button:hover{background:#45a049;}");
- client.println(".timeout{color:red;font-weight:bold;}");
- client.println(".step{margin:20px 0;padding:15px;background:#f9f9f9;border-radius:5px;}");
- client.println(".schedule-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:20px 0;}");
- client.println(".hour-checkbox{display:flex;align-items:center;padding:8px;background:white;border:1px solid #ddd;border-radius:5px;}");
- client.println(".hour-checkbox input{margin-right:8px;}");
- client.println(".hour-checkbox:hover{background:#f0f8ff;}");
- client.println(".selected-hours{background:#e8f5e8;padding:10px;border-radius:5px;margin:10px 0;}");
- client.println("</style></head>");
- client.println("<body>");
- client.println("<div class='container'>");
- client.println("<h1>ESP32 Motor Timer Setup</h1>");
- client.println("<div class='timeout'>Timeout: <span id='countdown'>" + String(remaining) + "</span> seconds</div>");
- // Step 1: Time sync
- client.println("<div class='step'>");
- client.println("<h3>Step 1: Set Current Time</h3>");
- if (timeWasSet) {
- client.println("<p>✅ Time set: " + rtc.getTime("%H:%M:%S %d/%m/%Y") + "</p>");
- } else {
- client.println("<h4 id='currentTime'></h4>");
- client.println("<form action='/syncTime' method='POST'>");
- client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
- client.println("<button type='submit'>Sync Time</button>");
- client.println("</form>");
- }
- client.println("</div>");
- // Step 2: Schedule setup
- client.println("<div class='step'>");
- client.println("<h3>Step 2: Select Operation Hours</h3>");
- client.println("<p>Check the boxes for hours when the motor should run (30 seconds each time):</p>");
- if (scheduleWasSet) {
- client.println("<div class='selected-hours'>");
- client.println("<p>✅ Schedule set! Motor will run at:</p>");
- String selectedHours = "";
- int count = 0;
- for (int h = 0; h < 24; h++) {
- if (hourlySchedule[h]) {
- if (count > 0) selectedHours += ", ";
- selectedHours += String(h < 10 ? "0" : "") + String(h) + ":00";
- count++;
- }
- }
- if (count == 0) {
- client.println("<p>No hours selected</p>");
- } else {
- client.println("<p>" + selectedHours + "</p>");
- client.println("<p>Total: " + String(count) + " times per day</p>");
- }
- client.println("</div>");
- } else {
- client.println("<form action='/setSchedule' method='POST'>");
- client.println("<div class='schedule-grid'>");
- // Create 24 checkboxes for each hour
- for (int h = 0; h < 24; h++) {
- client.println("<div class='hour-checkbox'>");
- client.println("<input type='checkbox' name='hour" + String(h) + "' value='1'" +
- (hourlySchedule[h] ? " checked" : "") + ">");
- client.println("<label>" + String(h < 10 ? "0" : "") + String(h) + ":00</label>");
- client.println("</div>");
- }
- client.println("</div>");
- client.println("<button type='submit'>Set Schedule</button>");
- client.println("</form>");
- }
- client.println("</div>");
- if (timeWasSet && scheduleWasSet) {
- client.println("<div class='step'>");
- client.println("<h3>✅ Setup Complete!</h3>");
- client.println("<p>ESP32 will now enter scheduled operation mode.</p>");
- client.println("<button onclick='startOperation()'>Start Operation</button>");
- client.println("</div>");
- }
- client.println("</div>");
- // JavaScript for time updates and countdown
- client.println("<script>");
- client.println("var countdown = " + String(remaining) + ";");
- client.println("function updateTime(){");
- client.println("var now=new Date();");
- client.println("if(document.getElementById('currentTime'))");
- client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
- client.println("if(document.getElementById('hiddenEpochTime'))");
- client.println("document.getElementById('hiddenEpochTime').value=Math.floor(now.getTime()/1000);");
- client.println("}");
- client.println("function updateCountdown(){");
- client.println("countdown--;");
- client.println("document.getElementById('countdown').innerHTML=countdown;");
- client.println("if(countdown<=0){");
- client.println("document.body.innerHTML='<h2>Timeout reached - ESP32 entering sleep mode</h2>';");
- client.println("}");
- client.println("}");
- client.println("function startOperation(){");
- client.println("document.body.innerHTML='<h2>Starting scheduled operation...</h2><p>ESP32 entering deep sleep mode</p>';");
- client.println("setTimeout(function(){window.location.reload();},2000);");
- client.println("}");
- client.println("setInterval(updateTime,1000);");
- client.println("setInterval(updateCountdown,1000);");
- client.println("updateTime();");
- client.println("</script></body></html>");
- client.println();
- }
- void handleTimeSyncRequest(WiFiClient &client) {
- String requestBody = "";
- while (client.available()) {
- requestBody += (char)client.read();
- }
- int epochIndex = requestBody.indexOf("epochTime=");
- if (epochIndex != -1) {
- long epochTime = requestBody.substring(epochIndex + 10).toInt();
- rtc.setTime(epochTime);
- timeWasSet = true;
- Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
- // Send redirect back to main page
- client.println("HTTP/1.1 302 Found");
- client.println("Location: /");
- client.println();
- }
- }
- void handleScheduleRequest(WiFiClient &client) {
- String requestBody = "";
- while (client.available()) {
- requestBody += (char)client.read();
- }
- Serial.println("Schedule request body: " + requestBody);
- // Reset all hours first
- for (int h = 0; h < 24; h++) {
- hourlySchedule[h] = false;
- }
- // Parse checkbox data
- for (int h = 0; h < 24; h++) {
- String hourParam = "hour" + String(h) + "=1";
- if (requestBody.indexOf(hourParam) != -1) {
- hourlySchedule[h] = true;
- Serial.println("Hour " + String(h) + " enabled");
- }
- }
- scheduleWasSet = true;
- Serial.println("Schedule updated:");
- int enabledCount = 0;
- for (int h = 0; h < 24; h++) {
- if (hourlySchedule[h]) {
- Serial.println("- " + String(h) + ":00");
- enabledCount++;
- }
- }
- Serial.println("Total enabled hours: " + String(enabledCount));
- // If both time and schedule are set, start operation
- if (timeWasSet && scheduleWasSet) {
- delay(2000); // Allow user to see confirmation
- scheduleNextWakeup();
- }
- // Send redirect back to main page
- client.println("HTTP/1.1 302 Found");
- client.println("Location: /");
- client.println();
- }
- void printWakeupReason() {
- esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
- switch (wakeup_reason) {
- case ESP_SLEEP_WAKEUP_TIMER:
- Serial.println("Wake-up: Scheduled timer");
- break;
- case ESP_SLEEP_WAKEUP_EXT0:
- Serial.println("Wake-up: External signal RTC_IO");
- break;
- case ESP_SLEEP_WAKEUP_EXT1:
- Serial.println("Wake-up: External signal RTC_CNTL");
- break;
- default:
- Serial.println("Wake-up: Power on or reset");
- break;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement