Диагностика сохранения EEPROM: проверять commit() и перечитывание

Настройки не сохраняются после перезагрузки. Причина пока не установлена,
это шаг сбора улик, а не исправление.

Найден дефект, мешавший диагностике: результат EEPROM.commit()
игнорировался, поэтому лог печатал «Конфигурация сохранена» безусловно —
даже при провалившейся записи. Теперь запись подтверждается повторным
чтением сектора из флеша и сравнением всех полей; при расхождении в лог
идут записанные и прочитанные значения.

Добавлена проверка геометрии флеша при старте: несовпадение размера по
настройке IDE с физическим уводит сектор EEPROM за пределы микросхемы,
и запись молча теряется — симптом ровно тот же.

Проверкой чтения исключены: корректность библиотеки EEPROM, раскладка
адресов (258 из 512, без перекрытий), стирание сектора со стороны
WiFiManager::resetSettings() — он вызывает только WiFi.disconnect().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 16:29:47 +07:00
co-authored by Claude Opus 5
parent cf61617a46
commit fa54e5790f
@@ -149,16 +149,57 @@ static void eepromReadStr(int addr, int fieldLen, char* dst) {
dst[fieldLen] = '\0';
}
void eepromWriteConfig() {
// Возвращает true только если запись подтверждена перечитыванием из флеша.
// Проверка нужна потому, что EEPROM.commit() умеет вернуть false (например при
// неверно выбранном размере флеша в IDE), и молчаливая потеря настроек выглядит
// точно так же, как «не сохраняется».
bool eepromWriteConfig() {
EEPROM.put(EEPROM_MAGIC_ADDR, EEPROM_MAGIC_V2);
eepromWriteStr(EEPROM_URL_ADDR, WEBHOOK_MAX_LEN, webhook_url);
eepromWriteStr(EEPROM_DEVICE_ADDR, DEVICE_MAX_LEN, device_name);
EEPROM.put(EEPROM_ALARM_ADDR, alarmDelaySec);
EEPROM.put(EEPROM_EMERG_ADDR, emergDelaySec);
EEPROM.commit();
Serial.printf("[EEPROM] Конфигурация сохранена: device=%s, авария=%u с, резерв=%u с\n",
if (!EEPROM.commit()) {
Serial.println(F("[EEPROM] ОШИБКА: commit() вернул false, настройки НЕ сохранены"));
return false;
}
// Повторный begin() перечитывает сектор из флеша в буфер — это и есть проверка,
// что записанное действительно легло на флеш, а не только в ОЗУ.
EEPROM.begin(EEPROM_SIZE);
uint16_t magicChk, alarmChk, emergChk;
char urlChk[WEBHOOK_MAX_LEN + 1];
char devChk[DEVICE_MAX_LEN + 1];
EEPROM.get(EEPROM_MAGIC_ADDR, magicChk);
eepromReadStr(EEPROM_URL_ADDR, WEBHOOK_MAX_LEN, urlChk);
eepromReadStr(EEPROM_DEVICE_ADDR, DEVICE_MAX_LEN, devChk);
EEPROM.get(EEPROM_ALARM_ADDR, alarmChk);
EEPROM.get(EEPROM_EMERG_ADDR, emergChk);
bool ok = (magicChk == EEPROM_MAGIC_V2) &&
(strcmp(urlChk, webhook_url) == 0) &&
(strcmp(devChk, device_name) == 0) &&
(alarmChk == alarmDelaySec) &&
(emergChk == emergDelaySec);
if (!ok) {
Serial.println(F("[EEPROM] ОШИБКА: запись не подтвердилась при перечитывании!"));
Serial.printf("[EEPROM] magic: записан 0x%04X, прочитан 0x%04X\n",
EEPROM_MAGIC_V2, magicChk);
Serial.printf("[EEPROM] device: записан '%s', прочитан '%s'\n", device_name, devChk);
Serial.printf("[EEPROM] выдержки: записаны %u/%u, прочитаны %u/%u\n",
alarmDelaySec, emergDelaySec, alarmChk, emergChk);
Serial.println("[EEPROM] URL записан: " + String(webhook_url));
Serial.println("[EEPROM] URL прочитан: " + String(urlChk));
return false;
}
Serial.printf("[EEPROM] Сохранено и проверено: device=%s, авария=%u с, резерв=%u с\n",
device_name, alarmDelaySec, emergDelaySec);
Serial.println("[EEPROM] URL: " + String(webhook_url[0] ? webhook_url : "(не задан)"));
return true;
}
static void configSetDefaults() {
@@ -670,6 +711,21 @@ void setup() {
Serial.println(F("[OLED] Дисплей не найден! Проверь адрес (0x3C/0x3D) и провода."));
}
// Диагностика флеша. Если «размер по настройке IDE» больше физического, то
// сектор EEPROM оказывается за пределами микросхемы и запись молча теряется —
// выглядит это ровно как «настройки не сохраняются».
uint32_t flashIde = ESP.getFlashChipSize();
uint32_t flashReal = ESP.getFlashChipRealSize();
Serial.printf("[FLASH] по настройке IDE: %u Б, физически: %u Б -> %s\n",
flashIde, flashReal,
flashIde == flashReal ? "совпадает" : "!!! НЕ СОВПАДАЕТ !!!");
Serial.printf("[FLASH] скетч %u Б, свободно под скетч %u Б\n",
ESP.getSketchSize(), ESP.getFreeSketchSpace());
if (flashIde != flashReal) {
Serial.println(F("[FLASH] ВНИМАНИЕ: выберите в IDE верный размер флеша,"));
Serial.println(F("[FLASH] иначе EEPROM не сохраняется."));
}
EEPROM.begin(EEPROM_SIZE);
eepromReadConfig();