feat: triple power-cycle factory reset (no FTDI needed)
Power-cycle the device 3 times within 10 seconds to trigger factory reset: - Clears WiFi credentials (SSID + password) in EEPROM - Shows "FACTORY RESET / WiFi: TJ56654-Setup / Pass: 12345678" on display - Reboots into WiFiManager AP mode for reconfiguration Counter stored at EEPROM offset 480 (well past Config ~260 bytes). Clears automatically after 10s of stable operation. Prevents the need for FTDI/USB recovery when credentials are corrupted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e9dc158a4e
commit
5b8a58715e
@@ -167,4 +167,16 @@ const unsigned long NTP_TIMEOUT_MS = 5000; // 5 second timeout
|
|||||||
// WiFi timeout
|
// WiFi timeout
|
||||||
const unsigned long WIFI_TIMEOUT_MS = 10000; // 10 second timeout
|
const unsigned long WIFI_TIMEOUT_MS = 10000; // 10 second timeout
|
||||||
|
|
||||||
|
// Triple power-cycle factory reset
|
||||||
|
// Counter stored at EEPROM offset 480, well past Config (~260 bytes)
|
||||||
|
#define RESET_COUNTER_ADDR 480
|
||||||
|
#define RESET_COUNTER_MAGIC 0xA5
|
||||||
|
#define RESET_COUNTER_WINDOW 10000UL // 10s: if device runs longer, counter clears
|
||||||
|
#define RESET_COUNTER_TRIPS 3 // 3 quick power cycles = factory reset
|
||||||
|
|
||||||
|
struct ResetCounter {
|
||||||
|
uint8_t magic;
|
||||||
|
uint8_t count;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // CONFIG_H
|
#endif // CONFIG_H
|
||||||
|
|||||||
@@ -110,6 +110,64 @@ void ICACHE_FLASH_ATTR safeStringCopy(const String& src, char* dest, size_t maxL
|
|||||||
dest[maxLen - 1] = '\0';
|
dest[maxLen - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============ Triple power-cycle factory reset ============
|
||||||
|
//
|
||||||
|
// How it works: on each boot we increment a counter in EEPROM.
|
||||||
|
// If the device runs for >10s the counter is cleared back to 0.
|
||||||
|
// 3 quick power cycles before the 10s window = factory reset:
|
||||||
|
// clears WiFi credentials, reboots into WiFiManager AP mode.
|
||||||
|
|
||||||
|
void ICACHE_FLASH_ATTR checkFactoryReset() {
|
||||||
|
EEPROM.begin(512);
|
||||||
|
ResetCounter rc;
|
||||||
|
EEPROM.get(RESET_COUNTER_ADDR, rc);
|
||||||
|
|
||||||
|
if (rc.magic != RESET_COUNTER_MAGIC) {
|
||||||
|
rc.magic = RESET_COUNTER_MAGIC;
|
||||||
|
rc.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc.count++;
|
||||||
|
Serial.printf("Boot counter: %d/%d (power-cycle %d more times within 10s to factory reset)\n",
|
||||||
|
rc.count, RESET_COUNTER_TRIPS, RESET_COUNTER_TRIPS - rc.count);
|
||||||
|
|
||||||
|
if (rc.count >= RESET_COUNTER_TRIPS) {
|
||||||
|
Serial.println("!!! FACTORY RESET triggered !!!");
|
||||||
|
rc.count = 0;
|
||||||
|
EEPROM.put(RESET_COUNTER_ADDR, rc);
|
||||||
|
EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
|
|
||||||
|
// Clear WiFi credentials only — keep other settings
|
||||||
|
memset(config.ssid, 0, sizeof(config.ssid));
|
||||||
|
memset(config.password, 0, sizeof(config.password));
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
// Show reset screen
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.setCursor(8, 4);
|
||||||
|
display.println("FACTORY");
|
||||||
|
display.setCursor(8, 24);
|
||||||
|
display.println("RESET!");
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(2, 48);
|
||||||
|
display.println("WiFi: TJ56654-Setup");
|
||||||
|
display.setCursor(2, 57);
|
||||||
|
display.println("Pass: 12345678");
|
||||||
|
display.display();
|
||||||
|
|
||||||
|
delay(4000);
|
||||||
|
ESP.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EEPROM.put(RESET_COUNTER_ADDR, rc);
|
||||||
|
EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
|
}
|
||||||
|
|
||||||
// ============ EEPROM functions ============
|
// ============ EEPROM functions ============
|
||||||
|
|
||||||
void ICACHE_FLASH_ATTR loadConfig() {
|
void ICACHE_FLASH_ATTR loadConfig() {
|
||||||
@@ -219,6 +277,9 @@ void setup() {
|
|||||||
// Load configuration
|
// Load configuration
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
|
// Check for triple power-cycle factory reset (must be after display+config init)
|
||||||
|
checkFactoryReset();
|
||||||
|
|
||||||
// Setup WiFi
|
// Setup WiFi
|
||||||
setupWiFi();
|
setupWiFi();
|
||||||
|
|
||||||
@@ -325,6 +386,18 @@ void loop() {
|
|||||||
fetchWeatherAsync();
|
fetchWeatherAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear factory-reset boot counter after 10s of normal operation
|
||||||
|
static bool resetCounterCleared = false;
|
||||||
|
if (!resetCounterCleared && millis() > RESET_COUNTER_WINDOW) {
|
||||||
|
EEPROM.begin(512);
|
||||||
|
ResetCounter rc = { RESET_COUNTER_MAGIC, 0 };
|
||||||
|
EEPROM.put(RESET_COUNTER_ADDR, rc);
|
||||||
|
EEPROM.commit();
|
||||||
|
EEPROM.end();
|
||||||
|
resetCounterCleared = true;
|
||||||
|
Serial.println("Boot counter cleared — stable operation confirmed");
|
||||||
|
}
|
||||||
|
|
||||||
// Update weather periodically (static flag avoids millis() > 10000 rollover trap)
|
// Update weather periodically (static flag avoids millis() > 10000 rollover trap)
|
||||||
static bool weatherBootReady = false;
|
static bool weatherBootReady = false;
|
||||||
if (!weatherBootReady && millis() > 10000UL) weatherBootReady = true;
|
if (!weatherBootReady && millis() > 10000UL) weatherBootReady = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user