· 3 months ago · Jun 12, 2025, 05:56 PM
1/*
2 Key Features:
3
4 Scheduled Operation: Motor runs for 30 seconds at 7:10 AM and 8:45 PM daily
5 Deep Sleep: ESP32 sleeps between scheduled times for maximum power efficiency
6 MOSFET Control: GPIO10 controls the motor via MOSFET gate
7 Web Setup: Initial time sync via WiFi access point
8
9 Setup Process:
10
11 Upload the sketch to your ESP32
12 On first boot, connect to WiFi network "ESP32-Motor-Timer" (password: "12345678")
13 Visit the IP address shown in serial monitor, usually 192.168.4.1
14 Click "Set Time & Start Schedule" to sync time and begin operation
15
16 Hardware Connections:
17
18 GPIO10 → MOSFET Gate (controls motor)
19 GPIO33 → Built-in LED (status indication)
20 Motor circuit through MOSFET as usual
21
22 Power Management:
23
24 Uses RTC memory to preserve settings through deep sleep
25 Automatically calculates sleep duration until next scheduled time
26 Very low power consumption between motor operations
27
28 The sketch handles all edge cases like crossing midnight and provides clear serial output for debugging.
29
30*/
31
32#include <ESP32Time.h>
33#include <WiFi.h>
34
35#define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds
36#define MOTOR_ON_TIME 30 // Motor run time in seconds
37#define MOTOR_PIN 33 // GPIO pin connected to MOSFET gate
38#define LED_PIN 2 // Built-in LED for status indication
39
40// WiFi credentials for initial time sync
41const char *ssid = "ESP32-Motor-Timer";
42const char *password = "12345678";
43
44//ESP32Time rtc;
45ESP32Time rtc(3600); // offset in seconds GMT+1 Portugal Summer Time
46WiFiServer server(80);
47
48// RTC memory variables (survive deep sleep)
49RTC_DATA_ATTR bool timeWasSet = false;
50RTC_DATA_ATTR int bootCount = 0;
51
52// Schedule times (24-hour format)
53const int MORNING_HOUR = 8;
54const int MORNING_MINUTE = 30;
55const int EVENING_HOUR = 20;
56const int EVENING_MINUTE = 30;
57
58void setup() {
59 Serial.begin(115200);
60 delay(1000);
61
62 bootCount++;
63 Serial.println("Boot count: " + String(bootCount));
64
65 // Initialize pins
66 pinMode(MOTOR_PIN, OUTPUT);
67 pinMode(LED_PIN, OUTPUT);
68 digitalWrite(MOTOR_PIN, LOW); // Ensure motor is off initially
69
70 printWakeupReason();
71
72 // Check if this is a scheduled wake-up and time has been set
73 if (timeWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
74 handleScheduledWakeup();
75 } else {
76 // First boot or manual reset - need to set time via web interface
77 setupWebServer();
78 }
79}
80
81void loop() {
82 // Handle web server for time setting
83 if (!timeWasSet) {
84 handleWebClient();
85 }
86}
87
88void handleScheduledWakeup() {
89 Serial.println("\n=== Scheduled Wake-up ===");
90 Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
91
92 // Run the motor
93 runMotor();
94
95 // Calculate next wake-up time
96 scheduleNextWakeup();
97}
98
99void runMotor() {
100 Serial.println("Starting motor...");
101
102 // Blink LED to indicate motor operation
103 digitalWrite(LED_PIN, HIGH);
104
105 // Turn on motor via MOSFET
106 digitalWrite(MOTOR_PIN, HIGH);
107
108 // Run for specified time with status updates
109 for (int i = MOTOR_ON_TIME; i > 0; i--) {
110 Serial.println("Motor running... " + String(i) + "s remaining");
111 delay(1000);
112
113 // Blink LED every 5 seconds during operation
114 if (i % 5 == 0) {
115 digitalWrite(LED_PIN, LOW);
116 delay(100);
117 digitalWrite(LED_PIN, HIGH);
118 }
119 }
120
121 // Turn off motor
122 digitalWrite(MOTOR_PIN, LOW);
123 digitalWrite(LED_PIN, LOW);
124
125 Serial.println("Motor stopped.");
126}
127
128void scheduleNextWakeup() {
129 struct tm timeinfo = rtc.getTimeStruct();
130 int currentHour = timeinfo.tm_hour;
131 int currentMinute = timeinfo.tm_min;
132
133 // Calculate seconds until next scheduled time
134 unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
135
136 Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
137 String((sleepTime % 3600) / 60) + " minutes");
138
139 // Configure and enter deep sleep
140 esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
141 Serial.println("Entering deep sleep...");
142 Serial.flush();
143 esp_deep_sleep_start();
144}
145
146unsigned long calculateSleepTime(int currentHour, int currentMinute) {
147 int currentTotalMinutes = currentHour * 60 + currentMinute;
148 int morningTotalMinutes = MORNING_HOUR * 60 + MORNING_MINUTE;
149 int eveningTotalMinutes = EVENING_HOUR * 60 + EVENING_MINUTE;
150
151 int nextWakeupMinutes;
152
153 if (currentTotalMinutes < morningTotalMinutes) {
154 // Before morning time - wake up at morning time
155 nextWakeupMinutes = morningTotalMinutes;
156 } else if (currentTotalMinutes < eveningTotalMinutes) {
157 // Between morning and evening - wake up at evening time
158 nextWakeupMinutes = eveningTotalMinutes;
159 } else {
160 // After evening time - wake up next morning
161 nextWakeupMinutes = morningTotalMinutes + (24 * 60); // Next day
162 }
163
164 int sleepMinutes = nextWakeupMinutes - currentTotalMinutes;
165 return sleepMinutes * 60; // Convert to seconds
166}
167
168void setupWebServer() {
169 Serial.println("\n=== Setting up Web Server for Time Sync ===");
170 Serial.println("Connect to WiFi network: " + String(ssid));
171 Serial.println("Password: " + String(password));
172
173 WiFi.softAP(ssid, password);
174 IPAddress IP = WiFi.softAPIP();
175 Serial.println("Web interface: http://" + IP.toString());
176 server.begin();
177
178 // Blink LED to indicate setup mode
179 for (int i = 0; i < 10; i++) {
180 digitalWrite(LED_PIN, HIGH);
181 delay(200);
182 digitalWrite(LED_PIN, LOW);
183 delay(200);
184 }
185}
186
187void handleWebClient() {
188 WiFiClient client = server.available();
189
190 if (client) {
191 Serial.println("Client connected");
192 String currentLine = "";
193
194 while (client.connected()) {
195 if (client.available()) {
196 char c = client.read();
197 if (c == '\n') {
198 if (currentLine.length() == 0) {
199 sendWebPage(client);
200 break;
201 } else {
202 currentLine = "";
203 }
204 } else if (c != '\r') {
205 currentLine += c;
206 }
207
208 if (currentLine.endsWith("POST /syncTime")) {
209 handleTimeSyncRequest(client);
210 }
211 }
212 }
213 client.stop();
214 }
215}
216
217void sendWebPage(WiFiClient &client) {
218 client.println("HTTP/1.1 200 OK");
219 client.println("Content-type:text/html");
220 client.println();
221 client.println("<!DOCTYPE html><html>");
222 client.println("<head><title>ESP32 Motor Timer Setup</title>");
223 client.println("<style>body{font-family:Arial;text-align:center;padding:20px;}");
224 client.println("button{padding:15px 30px;font-size:18px;background:#4CAF50;color:white;border:none;border-radius:5px;cursor:pointer;}");
225 client.println("button:hover{background:#45a049;}</style></head>");
226 client.println("<body>");
227 client.println("<h1>ESP32 Motor Timer</h1>");
228 client.println("<p>Schedule: 07:00 and 19:30 daily</p>");
229 client.println("<p>Motor run time: 30 seconds</p>");
230 client.println("<h2 id='currentTime'></h2>");
231 client.println("<h3 id='epochTime'></h3>");
232 client.println("<form action='/syncTime' method='POST'>");
233 client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
234 client.println("<button type='submit'>Set Time & Start Schedule</button>");
235 client.println("</form>");
236 client.println("<script>");
237 client.println("function updateTime(){");
238 client.println("var now=new Date();");
239 client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
240 client.println("var epoch=Math.floor(now.getTime()/1000);");
241 client.println("document.getElementById('epochTime').innerHTML='Epoch: '+epoch;");
242 client.println("document.getElementById('hiddenEpochTime').value=epoch;");
243 client.println("}");
244 client.println("setInterval(updateTime,1000);updateTime();");
245 client.println("</script></body></html>");
246 client.println();
247}
248
249void handleTimeSyncRequest(WiFiClient &client) {
250 String requestBody = "";
251 while (client.available()) {
252 requestBody += (char)client.read();
253 }
254
255 int epochIndex = requestBody.indexOf("epochTime=");
256 if (epochIndex != -1) {
257 long epochTime = requestBody.substring(epochIndex + 10).toInt();
258 rtc.setTime(epochTime);
259 timeWasSet = true;
260
261 Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
262
263 // Send success response
264 client.println("HTTP/1.1 200 OK");
265 client.println("Content-type:text/html");
266 client.println();
267 client.println("<html><body><h1>Time Set Successfully!</h1>");
268 client.println("<p>ESP32 will now enter scheduled operation.</p></body></html>");
269
270 delay(2000);
271
272 // Calculate initial sleep time and enter deep sleep
273 struct tm timeinfo = rtc.getTimeStruct();
274 scheduleNextWakeup();
275 }
276}
277
278void printWakeupReason() {
279 esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
280
281 switch (wakeup_reason) {
282 case ESP_SLEEP_WAKEUP_TIMER:
283 Serial.println("Wake-up: Scheduled timer");
284 break;
285 case ESP_SLEEP_WAKEUP_EXT0:
286 Serial.println("Wake-up: External signal RTC_IO");
287 break;
288 case ESP_SLEEP_WAKEUP_EXT1:
289 Serial.println("Wake-up: External signal RTC_CNTL");
290 break;
291 default:
292 Serial.println("Wake-up: Power on or reset");
293 break;
294 }
295}
296