diff --git a/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino b/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
index 43d366e..fac5a49 100644
--- a/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
+++ b/ESPTimeCast_ESP32/ESPTimeCast_ESP32.ino
@@ -1481,6 +1481,93 @@ void setupWebServer() {
if (final) f.close(); // finish file
});
+ server.on("/factory_reset", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // If not in AP mode, block and return a 403 response
+ if (!isAPMode) {
+ request->send(403, "text/plain", "Factory reset only allowed in AP mode.");
+ Serial.println(F("[RESET] Factory reset attempt blocked (not in AP mode)."));
+ return;
+ }
+ const char *FACTORY_RESET_HTML = R"rawliteral(
+
+
+
+
+
+ Resetting Device
+
+
+
+ Factory Reset Initiated
+ All saved configuration and Wi-Fi credentials are now being erased.
+
+ ⚠️ ACTION REQUIRED
+
+ The device is rebooting and will be temporarily offline for about 45 seconds.
+
+ Your browser will disconnect automatically.
+
+
+ Next steps:
+
1. Wait about 45 seconds for the reboot to finish.
+ 2. Reconnect your PC or phone to the Wi-Fi network: ESPTimeCast.
+ 3. Open your browser and go to 192.168.4.1 to continue setup.
+
+
+
+ )rawliteral";
+ request->send(200, "text/html", FACTORY_RESET_HTML);
+ Serial.println(F("[RESET] Factory reset requested, initiating cleanup..."));
+
+ // Use onDisconnect() to ensure the HTTP response is fully sent before the disruptive actions
+ request->onDisconnect([]() {
+ // Small delay to ensure the response buffer is flushed before file ops
+ delay(500);
+
+ // --- Remove configuration and uptime files ---
+ const char *filesToRemove[] = { "/config.json", "/uptime.dat" };
+ for (auto &file : filesToRemove) {
+ if (LittleFS.exists(file)) {
+ if (LittleFS.remove(file)) {
+ Serial.printf("[RESET] Deleted %s\n", file);
+ } else {
+ Serial.printf("[RESET] ERROR deleting %s\n", file);
+ }
+ } else {
+ Serial.printf("[RESET] %s not found, skipping delete.\n", file);
+ }
+ }
+
+// --- Clear Wi-Fi credentials ---
+#if defined(ESP8266)
+ WiFi.disconnect(true); // true = wipe credentials
+#elif defined(ESP32)
+ WiFi.disconnect(true, true); // (erase=true, wifioff=true)
+#endif
+
+ Serial.println(F("[RESET] Factory defaults restored. Rebooting..."));
+ delay(500);
+ ESP.restart();
+ });
+ });
+
server.on("/generate_204", HTTP_GET, handleCaptivePortal); // Android
server.on("/fwlink", HTTP_GET, handleCaptivePortal); // Windows
server.on("/hotspot-detect.html", HTTP_GET, handleCaptivePortal); // iOS/macOS
@@ -1491,8 +1578,14 @@ void setupWebServer() {
void handleCaptivePortal(AsyncWebServerRequest *request) {
- Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
- Serial.println(request->url());
+ String uri = request->url();
+
+ // Filter out system-generated probe requests
+ if (!uri.endsWith("/204") && !uri.endsWith("/ipv6check") && !uri.endsWith("connecttest.txt") && !uri.endsWith("/generate_204") && !uri.endsWith("/fwlink") && !uri.endsWith("/hotspot-detect.html")) {
+
+ Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
+ Serial.println(uri);
+ }
if (isAPMode) {
IPAddress apIP = WiFi.softAPIP();
diff --git a/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino b/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
index d42b5ff..17abde2 100644
--- a/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
+++ b/ESPTimeCast_ESP8266/ESPTimeCast_ESP8266.ino
@@ -1477,6 +1477,93 @@ void setupWebServer() {
if (final) f.close(); // finish file
});
+ server.on("/factory_reset", HTTP_GET, [](AsyncWebServerRequest *request) {
+ // If not in AP mode, block and return a 403 response
+ if (!isAPMode) {
+ request->send(403, "text/plain", "Factory reset only allowed in AP mode.");
+ Serial.println(F("[RESET] Factory reset attempt blocked (not in AP mode)."));
+ return;
+ }
+ const char *FACTORY_RESET_HTML = R"rawliteral(
+
+
+
+
+
+ Resetting Device
+
+
+
+ Factory Reset Initiated
+ All saved configuration and Wi-Fi credentials are now being erased.
+
+ ⚠️ ACTION REQUIRED
+
+ The device is rebooting and will be temporarily offline for about 45 seconds.
+
+ Your browser will disconnect automatically.
+
+
+ Next steps:
+
1. Wait about 45 seconds for the reboot to finish.
+ 2. Reconnect your PC or phone to the Wi-Fi network: ESPTimeCast.
+ 3. Open your browser and go to 192.168.4.1 to continue setup.
+
+
+
+ )rawliteral";
+ request->send(200, "text/html", FACTORY_RESET_HTML);
+ Serial.println(F("[RESET] Factory reset requested, initiating cleanup..."));
+
+ // Use onDisconnect() to ensure the HTTP response is fully sent before the disruptive actions
+ request->onDisconnect([]() {
+ // Small delay to ensure the response buffer is flushed before file ops
+ delay(500);
+
+ // --- Remove configuration and uptime files ---
+ const char *filesToRemove[] = { "/config.json", "/uptime.dat" };
+ for (auto &file : filesToRemove) {
+ if (LittleFS.exists(file)) {
+ if (LittleFS.remove(file)) {
+ Serial.printf("[RESET] Deleted %s\n", file);
+ } else {
+ Serial.printf("[RESET] ERROR deleting %s\n", file);
+ }
+ } else {
+ Serial.printf("[RESET] %s not found, skipping delete.\n", file);
+ }
+ }
+
+// --- Clear Wi-Fi credentials ---
+#if defined(ESP8266)
+ WiFi.disconnect(true); // true = wipe credentials
+#elif defined(ESP32)
+ WiFi.disconnect(true, true); // (erase=true, wifioff=true)
+#endif
+
+ Serial.println(F("[RESET] Factory defaults restored. Rebooting..."));
+ delay(500);
+ ESP.restart();
+ });
+ });
+
server.on("/generate_204", HTTP_GET, handleCaptivePortal); // Android
server.on("/fwlink", HTTP_GET, handleCaptivePortal); // Windows
server.on("/hotspot-detect.html", HTTP_GET, handleCaptivePortal); // iOS/macOS
@@ -1486,8 +1573,14 @@ void setupWebServer() {
}
void handleCaptivePortal(AsyncWebServerRequest *request) {
- Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
- Serial.println(request->url());
+ String uri = request->url();
+
+ // Filter out system-generated probe requests
+ if (!uri.endsWith("/204") && !uri.endsWith("/ipv6check") && !uri.endsWith("connecttest.txt") && !uri.endsWith("/generate_204") && !uri.endsWith("/fwlink") && !uri.endsWith("/hotspot-detect.html")) {
+
+ Serial.print(F("[WEBSERVER] Captive Portal triggered for URL: "));
+ Serial.println(uri);
+ }
if (isAPMode) {
IPAddress apIP = WiFi.softAPIP();