Add factory reset functionality and update README instructions
This commit is contained in:
@@ -77,6 +77,19 @@ After connecting, the device is reachable at:
|
|||||||
- `http://m1730.local` (mDNS, works on most local networks)
|
- `http://m1730.local` (mDNS, works on most local networks)
|
||||||
- `http://<IP address>` (shown in the serial monitor on boot)
|
- `http://<IP address>` (shown in the serial monitor on boot)
|
||||||
|
|
||||||
|
### Factory reset (forgot password)
|
||||||
|
|
||||||
|
If you lose the web UI password, hold the **BOOT button** (GPIO0) for 3 seconds during startup. The firmware resets all settings to factory defaults **except** meter pin assignments and calibration (maxDuty), which are preserved. On acknowledgement, all connected meters sweep 0→100→0.
|
||||||
|
|
||||||
|
Serial output confirms the reset:
|
||||||
|
|
||||||
|
```
|
||||||
|
[BOOT] BOOT button held, hold 3s for factory reset...
|
||||||
|
[BOOT] factory reset triggered!
|
||||||
|
...
|
||||||
|
[BOOT] factory reset complete, restarting...
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Web UI
|
## Web UI
|
||||||
@@ -223,4 +236,4 @@ Config is stored as JSON in LittleFS at `/config.json`.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The file is written by the web UI and should not need manual editing. To reset to factory defaults, delete the file or erase flash with `pio run --target erase`.
|
The file is written by the web UI and should not need manual editing. To reset to factory defaults (keeping pin/calibration), hold the BOOT button for 3 seconds at startup. To erase everything including flash, use `pio run --target erase`.
|
||||||
|
|||||||
+106
-1
@@ -477,7 +477,7 @@ static void handleRoot() {
|
|||||||
html += ".count-row label{margin-bottom:4px}";
|
html += ".count-row label{margin-bottom:4px}";
|
||||||
html += ".btn{width:100%;margin-top:8px;padding:12px;background:#4361ee;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:background .2s}";
|
html += ".btn{width:100%;margin-top:8px;padding:12px;background:#4361ee;color:#fff;border:none;border-radius:8px;font-size:15px;font-weight:600;cursor:pointer;transition:background .2s}";
|
||||||
html += ".btn:hover{background:#3651d4}.mac{font-size:12px;color:#999;text-align:center;margin-top:20px}";
|
html += ".btn:hover{background:#3651d4}.mac{font-size:12px;color:#999;text-align:center;margin-top:20px}";
|
||||||
html += ".tgl-lbl{margin-bottom:0!important;line-height:18px}.switch{position:relative;display:inline-block;width:32px;height:18px;margin-left:auto;flex-shrink:0}";
|
html += ".tgl-lbl{margin-bottom:0!important;line-height:18px}.switch{position:relative;display:inline-block;width:32px;height:18px;margin-left:auto;flex-shrink:0}fieldset .row label.switch{width:32px}";
|
||||||
html += ".switch input{opacity:0;width:0;height:0}.slid{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#ccc;border-radius:18px;transition:.3s}";
|
html += ".switch input{opacity:0;width:0;height:0}.slid{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background:#ccc;border-radius:18px;transition:.3s}";
|
||||||
html += ".slid::before{content:\"\";position:absolute;height:14px;width:14px;left:2px;top:2px;background:#fff;border-radius:50%;transition:.3s;box-shadow:0 1px 3px rgba(0,0,0,.2)}";
|
html += ".slid::before{content:\"\";position:absolute;height:14px;width:14px;left:2px;top:2px;background:#fff;border-radius:50%;transition:.3s;box-shadow:0 1px 3px rgba(0,0,0,.2)}";
|
||||||
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(14px)}";
|
html += ".switch input:checked+.slid{background:#4361ee}.switch input:checked+.slid::before{transform:translateX(14px)}";
|
||||||
@@ -612,6 +612,109 @@ static void startServer() {
|
|||||||
Serial.println("HTTP server started");
|
Serial.println("HTTP server started");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Factory reset (hold BOOT button 3s at startup)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void factoryReset() {
|
||||||
|
Serial.println("[BOOT] factory reset triggered!");
|
||||||
|
|
||||||
|
int count = meterCount;
|
||||||
|
int pins[MAX_METERS];
|
||||||
|
float maxD[MAX_METERS];
|
||||||
|
float cur[MAX_METERS];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
pins[i] = meters[i].pin;
|
||||||
|
maxD[i] = meters[i].maxDuty;
|
||||||
|
cur[i] = meters[i].currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset hostname, auth, MQTT, meter metadata
|
||||||
|
strlcpy(hostname, HOSTNAME_DEFAULT, sizeof(hostname));
|
||||||
|
hostnameParam.setValue(hostname, strlen(hostname));
|
||||||
|
|
||||||
|
authCfg.enabled = false;
|
||||||
|
authCfg.pass[0] = '\0';
|
||||||
|
|
||||||
|
mqttCfg.enabled = false;
|
||||||
|
mqttCfg.host[0] = '\0';
|
||||||
|
mqttCfg.port = 1883;
|
||||||
|
mqttCfg.user[0] = '\0';
|
||||||
|
mqttCfg.pass[0] = '\0';
|
||||||
|
strlcpy(mqttCfg.prefix, "m1730", sizeof(mqttCfg.prefix));
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_METERS; i++) {
|
||||||
|
meters[i].pin = 0;
|
||||||
|
meters[i].maxDuty = 0;
|
||||||
|
meters[i].currentValue = 0;
|
||||||
|
meters[i].name[0] = '\0';
|
||||||
|
meters[i].unit[0] = '\0';
|
||||||
|
meters[i].rangeMin = 0.0;
|
||||||
|
meters[i].rangeMax = 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore pins, calibration, current state
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
meters[i].pin = pins[i];
|
||||||
|
meters[i].maxDuty = maxD[i];
|
||||||
|
meters[i].currentValue = cur[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
// Attach meters temporarily for the sweep acknowledgement
|
||||||
|
for (int i = 0; i < count && i < 8; i++) {
|
||||||
|
if (meters[i].pin > 0) {
|
||||||
|
ledcSetup(i, PWM_FREQ, PWM_RES);
|
||||||
|
ledcAttachPin(meters[i].pin, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sweep 0→100→0 over ~2 seconds
|
||||||
|
const int steps = 20;
|
||||||
|
for (int phase = 0; phase < 2; phase++) {
|
||||||
|
for (int s = 0; s <= steps; s++) {
|
||||||
|
float pct = (phase == 0) ? (float)s / steps * 100.0f : (float)(steps - s) / steps * 100.0f;
|
||||||
|
for (int i = 0; i < count && i < 8; i++) {
|
||||||
|
if (meters[i].pin > 0 && meters[i].maxDuty > 0) {
|
||||||
|
float duty = pct / 100.0f * meters[i].maxDuty / 100.0f;
|
||||||
|
ledcWrite(i, constrain((int)(duty * 1023), 0, 1023));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore current values
|
||||||
|
for (int i = 0; i < count && i < 8; i++) {
|
||||||
|
if (meters[i].pin > 0 && meters[i].maxDuty > 0) {
|
||||||
|
float pct = cur[i] / 100.0f * meters[i].maxDuty / 100.0f;
|
||||||
|
ledcWrite(i, constrain((int)(pct * 1023), 0, 1023));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("[BOOT] factory reset complete, restarting...");
|
||||||
|
delay(200);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkFactoryReset() {
|
||||||
|
pinMode(0, INPUT_PULLUP);
|
||||||
|
delay(10);
|
||||||
|
if (digitalRead(0) == LOW) {
|
||||||
|
Serial.println("[BOOT] BOOT button held, hold 3s for factory reset...");
|
||||||
|
unsigned long start = millis();
|
||||||
|
while (millis() - start < 3000) {
|
||||||
|
delay(50);
|
||||||
|
if (digitalRead(0) != LOW) {
|
||||||
|
Serial.println("[BOOT] factory reset aborted (button released)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
factoryReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Setup & loop
|
// Setup & loop
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -624,6 +727,8 @@ void setup() {
|
|||||||
loadConfig();
|
loadConfig();
|
||||||
hostnameParam.setValue(hostname, strlen(hostname));
|
hostnameParam.setValue(hostname, strlen(hostname));
|
||||||
|
|
||||||
|
checkFactoryReset();
|
||||||
|
|
||||||
attachMeters();
|
attachMeters();
|
||||||
applyMeters();
|
applyMeters();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user