· 3 months ago · Jun 12, 2025, 06:10 PM
1/*
2 ESP32 Motor Timer with Web Setup, EEPROM Backup, GPIO Reset, and Telegram Notification
3
4 DESCRIPTION:
5 -------------
6 This sketch controls a motor (or relay) connected to an ESP32, allowing you to schedule its operation for specific hours each day.
7 - Configuration is done via a WiFi Access Point and web interface hosted by the ESP32.
8 - All settings (schedule, run time, time) are saved to EEPROM for power-loss recovery.
9 - A special reset mode (by connecting GPIO14 to 3.3V and resetting) clears all settings.
10 - After each scheduled motor run, a notification is sent to your Telegram account.
11
12 FEATURES:
13 ---------
14 - Web-based setup: Set current time, motor run time (5–300s), and select operation hours (per hour, 24h grid).
15 - Settings backup: All settings are stored in EEPROM and restored after power loss.
16 - Reset mode: Connect GPIO14 to 3.3V and reset to clear all settings and re-enter setup.
17 - Telegram notification: After each motor run, a message is sent to your Telegram bot.
18
19 WIRING:
20 -------
21 - MOTOR_PIN: GPIO33 (connect to relay or motor driver)
22 - LED_PIN: GPIO32 (status LED)
23 - RESET_PIN: GPIO14 (connect to 3.3V and reset to clear settings)
24 - Ensure your relay/motor driver is compatible with 3.3V logic.
25
26 USAGE INSTRUCTIONS:
27 -------------------
28 1. Power on the ESP32. It will start as a WiFi Access Point (SSID: ESP32-Motor-Timer, Password: 12345678).
29 2. Connect to this WiFi network with your phone or computer.
30 3. Open a browser and go to the address shown in the Serial Monitor (usually http://192.168.4.1).
31 4. Follow the web interface steps:
32 - Set the current time (syncs with your device).
33 - Set the motor run time (slider, 5–300 seconds).
34 - Select the hours you want the motor to run (checkbox grid).
35 - When all steps are complete, click "Start Operation".
36 5. The ESP32 will enter deep sleep and wake up at the scheduled hours to run the motor.
37 6. After each run, a Telegram message will be sent (see below for setup).
38 7. To reset all settings, connect GPIO14 to 3.3V and reset the ESP32.
39
40 TELEGRAM NOTIFICATION SETUP:
41 ----------------------------
42 1. Create a Telegram bot using BotFather and get your bot token.
43 2. Start a chat with your bot and get your chat ID (see UniversalTelegramBot library examples).
44 3. Enter your WiFi credentials, bot token, and chat ID in the placeholders at the top of this sketch.
45 4. Install the UniversalTelegramBot and WiFiClientSecure libraries via the Arduino Library Manager.
46 5. Ensure your ESP32 can connect to your WiFi for Telegram notifications.
47
48 DEPENDENCIES:
49 -------------
50 - ESP32 board support (install via Arduino Board Manager)
51 - UniversalTelegramBot library
52 - WiFiClientSecure library
53 - ESP32Time, WiFi, EEPROM libraries (included with ESP32 core)
54
55*/
56
57#include "soc/soc.h" // Brownout error fix
58#include "soc/rtc_cntl_reg.h" // Brownout error fix
59
60#include "driver/rtc_io.h" // https://github.com/pycom/esp-idf-2.0/blob/master/components/driver/include/driver/rtc_io.h
61
62#include <ESP32Time.h>
63#include <WiFi.h>
64#include <EEPROM.h>
65#include <WiFiClientSecure.h>
66#include <UniversalTelegramBot.h>
67
68#define uS_TO_S_FACTOR 1000000ULL
69#define DEFAULT_MOTOR_ON_TIME 5
70#define MIN_MOTOR_ON_TIME 5
71#define MAX_MOTOR_ON_TIME 300
72#define MOTOR_PIN 33
73#define LED_PIN 32
74#define WAKEUP_GPIO GPIO_NUM_14 // Only RTC IO are allowed
75#define EEPROM_SIZE 512
76
77// EEPROM Memory Map
78#define EEPROM_MAGIC_ADDR 0
79#define EEPROM_SCHEDULE_ADDR 4
80#define EEPROM_RUNTIME_ADDR 28
81#define EEPROM_LAST_SYNC_ADDR 32
82#define EEPROM_MAGIC_NUMBER 0xDEADBEEF
83#define MAX_TIME_STALENESS 7 * 24 * 3600
84
85const char *ssid = "ESP32-Motor-Timer";
86const char *password = "12345678";
87
88ESP32Time rtc(3600);
89WiFiServer server(80);
90
91// RTC memory variables
92RTC_DATA_ATTR bool timeWasSet = false;
93RTC_DATA_ATTR bool scheduleWasSet = false;
94RTC_DATA_ATTR bool runTimeWasSet = false;
95RTC_DATA_ATTR int bootCount = 0;
96RTC_DATA_ATTR unsigned long webServerStartTime = 0;
97RTC_DATA_ATTR bool usingBackupSettings = false;
98RTC_DATA_ATTR bool hourlySchedule[24] = {0};
99RTC_DATA_ATTR int motorRunTime = DEFAULT_MOTOR_ON_TIME;
100
101// --- Telegram Bot Placeholders ---
102const char* TELEGRAM_WIFI_SSID = "YOUR_WIFI_SSID";
103const char* TELEGRAM_WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
104const char* BOT_TOKEN = "YOUR_BOT_TOKEN";
105const char* CHAT_ID = "YOUR_CHAT_ID";
106
107WiFiClientSecure secured_client;
108UniversalTelegramBot bot(BOT_TOKEN, secured_client);
109
110struct EEPROMSettings {
111 uint32_t magic;
112 bool schedule[24];
113 int runTime;
114 uint32_t lastSyncTime;
115};
116
117void setup() {
118 Serial.begin(115200);
119 delay(1000);
120 EEPROM.begin(EEPROM_SIZE);
121
122 bootCount++;
123 Serial.println("Boot count: " + String(bootCount));
124
125 pinMode(MOTOR_PIN, OUTPUT);
126 pinMode(LED_PIN, OUTPUT);
127 digitalWrite(MOTOR_PIN, LOW);
128
129 printWakeupReason();
130
131 // Check if woken up by GPIO14 - clear EEPROM and reset
132 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
133 Serial.println("=== GPIO RESET TRIGGERED ===");
134 Serial.println("Clearing EEPROM settings...");
135 clearEEPROMSettings();
136
137 // Reset RTC memory flags
138 timeWasSet = false;
139 scheduleWasSet = false;
140 runTimeWasSet = false;
141 usingBackupSettings = false;
142
143 Serial.println("EEPROM cleared - entering setup mode");
144 webServerStartTime = millis();
145 setupWebServer();
146 return;
147 }
148
149 // Normal wakeup logic
150 if (timeWasSet && scheduleWasSet && runTimeWasSet && esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
151 handleScheduledWakeup();
152 } else {
153 if (tryLoadBackupSettings()) {
154 Serial.println("=== POWER FAILURE RECOVERY ===");
155 Serial.println("Loaded backup settings from EEPROM");
156 printCurrentSettings();
157
158 timeWasSet = false;
159 usingBackupSettings = true;
160
161 EEPROMSettings settings;
162 loadSettingsFromEEPROM(settings);
163 uint32_t currentEpoch = settings.lastSyncTime + (millis() / 1000);
164 rtc.setTime(currentEpoch);
165 timeWasSet = true;
166
167 Serial.println("Estimated time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
168 scheduleNextWakeup();
169 } else {
170 Serial.println("No backup settings found - entering setup mode");
171 webServerStartTime = millis();
172 setupWebServer();
173 }
174 }
175}
176
177void loop() {
178 if (!timeWasSet || !scheduleWasSet || !runTimeWasSet) {
179 if (millis() - webServerStartTime > 240000) {
180 if (tryLoadBackupSettings()) {
181 Serial.println("Timeout reached - falling back to EEPROM settings");
182 usingBackupSettings = true;
183
184 EEPROMSettings settings;
185 loadSettingsFromEEPROM(settings);
186 uint32_t estimatedTime = settings.lastSyncTime + (millis() / 1000);
187 rtc.setTime(estimatedTime);
188 timeWasSet = true;
189
190 scheduleNextWakeup();
191 } else {
192 Serial.println("No backup settings - entering 3-hour sleep");
193 setupSleepWakeup();
194 esp_deep_sleep_start();
195 }
196 }
197 handleWebClient();
198 }
199}
200
201void clearEEPROMSettings() {
202 // Clear magic number to invalidate settings
203 uint32_t clearMagic = 0x00000000;
204 EEPROM.put(EEPROM_MAGIC_ADDR, clearMagic);
205 EEPROM.commit();
206 Serial.println("EEPROM settings cleared");
207}
208
209bool tryLoadBackupSettings() {
210 EEPROMSettings settings;
211 if (loadSettingsFromEEPROM(settings)) {
212 for (int i = 0; i < 24; i++) {
213 hourlySchedule[i] = settings.schedule[i];
214 }
215 motorRunTime = settings.runTime;
216 scheduleWasSet = true;
217 runTimeWasSet = true;
218 return true;
219 }
220 return false;
221}
222
223bool loadSettingsFromEEPROM(EEPROMSettings &settings) {
224 uint32_t magic;
225 EEPROM.get(EEPROM_MAGIC_ADDR, magic);
226
227 if (magic != EEPROM_MAGIC_NUMBER) {
228 Serial.println("No valid EEPROM settings found");
229 return false;
230 }
231
232 EEPROM.get(EEPROM_MAGIC_ADDR, settings.magic);
233 for (int i = 0; i < 24; i++) {
234 settings.schedule[i] = EEPROM.read(EEPROM_SCHEDULE_ADDR + i);
235 }
236 EEPROM.get(EEPROM_RUNTIME_ADDR, settings.runTime);
237 EEPROM.get(EEPROM_LAST_SYNC_ADDR, settings.lastSyncTime);
238
239 if (settings.runTime < MIN_MOTOR_ON_TIME || settings.runTime > MAX_MOTOR_ON_TIME) {
240 Serial.println("Invalid run time in EEPROM: " + String(settings.runTime));
241 return false;
242 }
243
244 Serial.println("Valid EEPROM settings loaded");
245 return true;
246}
247
248void saveSettingsToEEPROM() {
249 Serial.println("Saving settings to EEPROM...");
250
251 EEPROMSettings settings;
252 settings.magic = EEPROM_MAGIC_NUMBER;
253
254 for (int i = 0; i < 24; i++) {
255 settings.schedule[i] = hourlySchedule[i];
256 }
257 settings.runTime = motorRunTime;
258 settings.lastSyncTime = rtc.getEpoch();
259
260 EEPROM.put(EEPROM_MAGIC_ADDR, settings.magic);
261 for (int i = 0; i < 24; i++) {
262 EEPROM.write(EEPROM_SCHEDULE_ADDR + i, settings.schedule[i]);
263 }
264 EEPROM.put(EEPROM_RUNTIME_ADDR, settings.runTime);
265 EEPROM.put(EEPROM_LAST_SYNC_ADDR, settings.lastSyncTime);
266 EEPROM.commit();
267
268 Serial.println("Settings saved to EEPROM successfully");
269 printCurrentSettings();
270}
271
272void printCurrentSettings() {
273 Serial.println("Current Settings:");
274 Serial.println("- Motor run time: " + String(motorRunTime) + " seconds");
275 Serial.print("- Schedule: ");
276
277 int count = 0;
278 for (int h = 0; h < 24; h++) {
279 if (hourlySchedule[h]) {
280 if (count > 0) Serial.print(", ");
281 Serial.print(String(h < 10 ? "0" : "") + String(h) + ":00");
282 count++;
283 }
284 }
285
286 if (count == 0) {
287 Serial.println("No hours scheduled");
288 } else {
289 Serial.println(" (" + String(count) + " times/day)");
290 Serial.println("- Total daily runtime: " + String(count * motorRunTime) + " seconds");
291 }
292}
293
294void handleScheduledWakeup() {
295 Serial.println("\n=== Scheduled Wake-up ===");
296 Serial.println("Current time: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
297 Serial.println("Motor run time: " + String(motorRunTime) + " seconds");
298
299 if (usingBackupSettings) {
300 Serial.println("Running on backup settings from EEPROM");
301 }
302
303 runMotor();
304 scheduleNextWakeup();
305}
306
307void runMotor() {
308 Serial.println("Starting motor for " + String(motorRunTime) + " seconds...");
309
310 digitalWrite(LED_PIN, HIGH);
311 digitalWrite(MOTOR_PIN, HIGH);
312
313 for (int i = motorRunTime; i > 0; i--) {
314 Serial.println("Motor running... " + String(i) + "s remaining");
315 delay(1000);
316
317 int blinkInterval = (motorRunTime < 10) ? 1 : 5;
318 if (i % blinkInterval == 0) {
319 digitalWrite(LED_PIN, LOW);
320 delay(100);
321 digitalWrite(LED_PIN, HIGH);
322 }
323 }
324
325 digitalWrite(MOTOR_PIN, LOW);
326 digitalWrite(LED_PIN, LOW);
327 Serial.println("Motor stopped.");
328
329 // --- Send Telegram notification ---
330 String msg = "Motor finished running for " + String(motorRunTime) + " seconds at " + rtc.getTime("%H:%M:%S %d/%m/%Y");
331 sendTelegramMessage(msg);
332}
333
334void setupSleepWakeup() {
335 // Setup timer wakeup
336 esp_sleep_enable_timer_wakeup(3 * 3600 * uS_TO_S_FACTOR);
337
338 // Setup GPIO wakeup for reset functionality
339 esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1); // 1 = High trigger
340 rtc_gpio_pullup_dis(WAKEUP_GPIO);
341 rtc_gpio_pulldown_en(WAKEUP_GPIO);
342 Serial.println("Setup ESP32 to wake up on GPIO14 trigger (reset mode)");
343}
344
345void scheduleNextWakeup() {
346 struct tm timeinfo = rtc.getTimeStruct();
347 int currentHour = timeinfo.tm_hour;
348 int currentMinute = timeinfo.tm_min;
349
350 unsigned long sleepTime = calculateSleepTime(currentHour, currentMinute);
351
352 if (sleepTime > 0) {
353 Serial.println("Next wake-up in " + String(sleepTime / 3600) + " hours and " +
354 String((sleepTime % 3600) / 60) + " minutes");
355
356 esp_sleep_enable_timer_wakeup(sleepTime * uS_TO_S_FACTOR);
357
358 // Always enable GPIO wakeup for reset functionality
359 esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);
360 rtc_gpio_pullup_dis(WAKEUP_GPIO);
361 rtc_gpio_pulldown_en(WAKEUP_GPIO);
362
363 Serial.println("Entering deep sleep... (GPIO14 HIGH = reset)");
364 Serial.flush();
365 esp_deep_sleep_start();
366 } else {
367 Serial.println("No scheduled hours found - entering 24-hour sleep");
368 esp_sleep_enable_timer_wakeup(24 * 3600 * uS_TO_S_FACTOR);
369
370 esp_sleep_enable_ext0_wakeup(WAKEUP_GPIO, 1);
371 rtc_gpio_pullup_dis(WAKEUP_GPIO);
372 rtc_gpio_pulldown_en(WAKEUP_GPIO);
373
374 esp_deep_sleep_start();
375 }
376}
377
378unsigned long calculateSleepTime(int currentHour, int currentMinute) {
379 int nextHour = -1;
380
381 for (int h = currentHour + 1; h < 24; h++) {
382 if (hourlySchedule[h]) {
383 nextHour = h;
384 break;
385 }
386 }
387
388 if (nextHour == -1) {
389 for (int h = 0; h < 24; h++) {
390 if (hourlySchedule[h]) {
391 nextHour = h + 24;
392 break;
393 }
394 }
395 }
396
397 if (nextHour == -1) return 0;
398
399 int currentTotalMinutes = currentHour * 60 + currentMinute;
400 int nextTotalMinutes = (nextHour % 24) * 60;
401
402 if (nextHour >= 24) {
403 nextTotalMinutes += 24 * 60;
404 }
405
406 int sleepMinutes = nextTotalMinutes - currentTotalMinutes;
407 return sleepMinutes * 60;
408}
409
410void setupWebServer() {
411 Serial.println("\n=== Setting up Web Server for Configuration ===");
412 Serial.println("Connect to WiFi network: " + String(ssid));
413 Serial.println("Password: " + String(password));
414 Serial.println("TIMEOUT: 4 minutes");
415 Serial.println("RESET: Connect GPIO14 to 3.3V and reset ESP32");
416
417 WiFi.softAP(ssid, password);
418 IPAddress IP = WiFi.softAPIP();
419 Serial.println("Web interface: http://" + IP.toString());
420 server.begin();
421
422 for (int i = 0; i < 10; i++) {
423 digitalWrite(LED_PIN, HIGH);
424 delay(200);
425 digitalWrite(LED_PIN, LOW);
426 delay(200);
427 }
428}
429
430void handleWebClient() {
431 WiFiClient client = server.available();
432
433 if (client) {
434 Serial.println("Client connected");
435 String currentLine = "";
436
437 while (client.connected()) {
438 if (client.available()) {
439 char c = client.read();
440 if (c == '\n') {
441 if (currentLine.length() == 0) {
442 sendWebPage(client);
443 break;
444 } else {
445 currentLine = "";
446 }
447 } else if (c != '\r') {
448 currentLine += c;
449 }
450
451 if (currentLine.endsWith("POST /syncTime")) {
452 handleTimeSyncRequest(client);
453 } else if (currentLine.endsWith("POST /setRunTime")) {
454 handleRunTimeRequest(client);
455 } else if (currentLine.endsWith("POST /setSchedule")) {
456 handleScheduleRequest(client);
457 }
458 }
459 }
460 client.stop();
461 }
462}
463
464// Simplified web page (keeping core functionality)
465void sendWebPage(WiFiClient &client) {
466 unsigned long elapsed = millis() - webServerStartTime;
467 unsigned long remaining = (240000 - elapsed) / 1000;
468
469 client.println("HTTP/1.1 200 OK");
470 client.println("Content-type:text/html");
471 client.println();
472 client.println("<!DOCTYPE html><html>");
473 client.println("<head><title>ESP32 Motor Timer Setup</title>");
474 client.println("<meta name='viewport' content='width=device-width, initial-scale=1'>");
475 client.println("<style>");
476 client.println("body{font-family:Arial;text-align:center;padding:20px;background:#f0f0f0;}");
477 client.println(".container{max-width:600px;margin:0 auto;background:white;padding:30px;border-radius:10px;}");
478 client.println("input,button{padding:10px;margin:5px;font-size:16px;border:1px solid #ddd;border-radius:5px;}");
479 client.println("button{background:#4CAF50;color:white;border:none;cursor:pointer;padding:15px 30px;}");
480 client.println(".timeout{color:red;font-weight:bold;}");
481 client.println(".step{margin:20px 0;padding:15px;background:#f9f9f9;border-radius:5px;}");
482 client.println(".schedule-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin:20px 0;}");
483 client.println(".hour-checkbox{display:flex;align-items:center;padding:8px;background:white;border:1px solid #ddd;border-radius:5px;}");
484 client.println(".reset-info{background:#FFE6E6;padding:10px;border-radius:5px;margin:10px 0;border:1px solid #FF9999;}");
485 client.println("</style></head>");
486 client.println("<body>");
487 client.println("<div class='container'>");
488 client.println("<h1>ESP32 Motor Timer Setup</h1>");
489
490 client.println("<div class='reset-info'>");
491 client.println("<strong>Reset Instructions:</strong><br>");
492 client.println("To clear settings: Connect GPIO14 to 3.3V and reset ESP32");
493 client.println("</div>");
494
495 client.println("<div class='timeout'>Timeout: <span id='countdown'>" + String(remaining) + "</span> seconds</div>");
496
497 // Step 1: Time sync
498 client.println("<div class='step'>");
499 client.println("<h3>Step 1: Set Current Time</h3>");
500 if (timeWasSet) {
501 client.println("<p>✅ Time set: " + rtc.getTime("%H:%M:%S %d/%m/%Y") + "</p>");
502 } else {
503 client.println("<h4 id='currentTime'></h4>");
504 client.println("<form action='/syncTime' method='POST'>");
505 client.println("<input type='hidden' name='epochTime' id='hiddenEpochTime'>");
506 client.println("<button type='submit'>Sync Time</button>");
507 client.println("</form>");
508 }
509 client.println("</div>");
510
511 // Step 2: Motor run time
512 client.println("<div class='step'>");
513 client.println("<h3>Step 2: Set Motor Run Time</h3>");
514 if (runTimeWasSet) {
515 client.println("<p>✅ Run time set: " + String(motorRunTime) + " seconds</p>");
516 } else {
517 client.println("<form action='/setRunTime' method='POST'>");
518 client.println("<input type='range' name='runTime' min='5' max='300' value='" + String(motorRunTime) + "'>");
519 client.println("<span id='runTimeDisplay'>" + String(motorRunTime) + "s</span>");
520 client.println("<button type='submit'>Set Run Time</button>");
521 client.println("</form>");
522 }
523 client.println("</div>");
524
525 // Step 3: Schedule
526 client.println("<div class='step'>");
527 client.println("<h3>Step 3: Select Operation Hours</h3>");
528 if (scheduleWasSet) {
529 client.println("<p>✅ Schedule set!</p>");
530 } else {
531 client.println("<form action='/setSchedule' method='POST'>");
532 client.println("<div class='schedule-grid'>");
533 for (int h = 0; h < 24; h++) {
534 client.println("<div class='hour-checkbox'>");
535 client.println("<input type='checkbox' name='hour" + String(h) + "' value='1'>");
536 client.println("<label>" + String(h < 10 ? "0" : "") + String(h) + ":00</label>");
537 client.println("</div>");
538 }
539 client.println("</div>");
540 client.println("<button type='submit'>Set Schedule</button>");
541 client.println("</form>");
542 }
543 client.println("</div>");
544
545 if (timeWasSet && scheduleWasSet && runTimeWasSet) {
546 client.println("<div class='step'>");
547 client.println("<h3>✅ Setup Complete!</h3>");
548 client.println("<p>ESP32 will now enter scheduled operation mode.</p>");
549 client.println("<button onclick='startOperation()'>Start Operation</button>");
550 client.println("</div>");
551 }
552
553 client.println("</div>");
554
555 // Simplified JavaScript
556 client.println("<script>");
557 client.println("var countdown=" + String(remaining) + ";");
558 client.println("function updateTime(){");
559 client.println("var now=new Date();");
560 client.println("if(document.getElementById('currentTime'))");
561 client.println("document.getElementById('currentTime').innerHTML='Current Time: '+now.toLocaleString();");
562 client.println("if(document.getElementById('hiddenEpochTime'))");
563 client.println("document.getElementById('hiddenEpochTime').value=Math.floor(now.getTime()/1000);");
564 client.println("}");
565 client.println("function updateCountdown(){");
566 client.println("countdown--;document.getElementById('countdown').innerHTML=countdown;");
567 client.println("}");
568 client.println("function startOperation(){");
569 client.println("document.body.innerHTML='<h2>Starting operation...</h2>';");
570 client.println("}");
571 client.println("setInterval(updateTime,1000);setInterval(updateCountdown,1000);updateTime();");
572 client.println("</script></body></html>");
573}
574
575void handleTimeSyncRequest(WiFiClient &client) {
576 String requestBody = "";
577 while (client.available()) {
578 requestBody += (char)client.read();
579 }
580
581 int epochIndex = requestBody.indexOf("epochTime=");
582 if (epochIndex != -1) {
583 long epochTime = requestBody.substring(epochIndex + 10).toInt();
584 rtc.setTime(epochTime);
585 timeWasSet = true;
586 Serial.println("Time synchronized: " + rtc.getTime("%A, %B %d %Y %H:%M:%S"));
587 }
588
589 client.println("HTTP/1.1 302 Found");
590 client.println("Location: /");
591 client.println();
592}
593
594void handleRunTimeRequest(WiFiClient &client) {
595 String requestBody = "";
596 while (client.available()) {
597 requestBody += (char)client.read();
598 }
599
600 int runTimeIndex = requestBody.indexOf("runTime=");
601 if (runTimeIndex != -1) {
602 int newRunTime = requestBody.substring(runTimeIndex + 8).toInt();
603 if (newRunTime >= MIN_MOTOR_ON_TIME && newRunTime <= MAX_MOTOR_ON_TIME) {
604 motorRunTime = newRunTime;
605 runTimeWasSet = true;
606 Serial.println("Motor run time set to: " + String(motorRunTime) + " seconds");
607 }
608 }
609
610 client.println("HTTP/1.1 302 Found");
611 client.println("Location: /");
612 client.println();
613}
614
615void handleScheduleRequest(WiFiClient &client) {
616 String requestBody = "";
617 while (client.available()) {
618 requestBody += (char)client.read();
619 }
620
621 for (int h = 0; h < 24; h++) {
622 hourlySchedule[h] = false;
623 }
624
625 for (int h = 0; h < 24; h++) {
626 String hourParam = "hour" + String(h) + "=1";
627 if (requestBody.indexOf(hourParam) != -1) {
628 hourlySchedule[h] = true;
629 }
630 }
631
632 scheduleWasSet = true;
633
634 // Save to EEPROM when configuration is complete
635 if (timeWasSet && scheduleWasSet && runTimeWasSet) {
636 saveSettingsToEEPROM();
637 delay(2000);
638 scheduleNextWakeup();
639 }
640
641 client.println("HTTP/1.1 302 Found");
642 client.println("Location: /");
643 client.println();
644}
645
646void printWakeupReason() {
647 esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
648
649 switch (wakeup_reason) {
650 case ESP_SLEEP_WAKEUP_TIMER:
651 Serial.println("Wake-up: Scheduled timer");
652 break;
653 case ESP_SLEEP_WAKEUP_EXT0:
654 Serial.println("Wake-up: GPIO14 external signal (RESET MODE)");
655 break;
656 default:
657 Serial.println("Wake-up: Power on or reset");
658 break;
659 }
660}
661
662// Helper: Connect to WiFi for Telegram
663void connectToWiFiForTelegram() {
664 if (WiFi.status() == WL_CONNECTED) return;
665 WiFi.mode(WIFI_STA);
666 WiFi.begin(TELEGRAM_WIFI_SSID, TELEGRAM_WIFI_PASSWORD);
667 unsigned long startAttemptTime = millis();
668 while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 15000) {
669 delay(500);
670 }
671}
672
673// Helper: Send Telegram message
674void sendTelegramMessage(const String& message) {
675 connectToWiFiForTelegram();
676 secured_client.setInsecure(); // For ESP32, disables certificate validation
677 bot.sendMessage(CHAT_ID, message, "");
678}
679