Чтение содержимого ESP8266 Flash изнутри скетча

Я знаю, что есть способ создать дамп флэш-памяти с помощью esptool, но есть ли способ прочитать содержимое памяти скетча (только скетча, а не полной флэш-памяти или SPIFFS) через скетч сам? Например, есть ли программа на ESP8266, способная читать собственный машинный код из флэш-памяти и что-то с ней делать? Мне не нужен доступ на запись, только доступ на чтение.

Я хотел бы иметь возможность создать резервную копию текущего скетча в SPIFFS, чтобы можно было вернуть плохой скетч, пока не сломан триггер возврата. (Например, если более поздняя часть процесса загрузки испорчена или если функция работает немного неправильно, можно удерживать кнопку, и она будет восстановлена из сохраненного образа. Процесс восстановления — это то, что я выяснил из моего предыдущего вопроса, но мне хотелось бы найти способ написать скетч в SPIFFS, который не требует отдельной ручной загрузки или оболочки вокруг процесса OTA.)

[править:] Спасибо за комментарии.

Я создал этот код, который ДОЛЖЕН скопировать скетч в SPIFFS. Однако, похоже, это было сделано неправильно (поскольку результат не имеет тех же контрольных сумм, что и исходный файл, и попытки его записи приводят к неработоспособному устройству). Могу ли я попросить кого-нибудь просмотреть это и посмотреть, смогут ли они понять, почему?

unsigned long addr = 0;
unsigned long rem = ESP.getSketchSize();
uint32_t *data = new uint32_t[SPI_FLASH_SEC_SIZE];
File f = SPIFFS.open("/fw.bin", "w");
while (rem > 0) {
  ESP.flashRead(addr, data, SPI_FLASH_SEC_SIZE);
  int sz = SPI_FLASH_SEC_SIZE * sizeof(uint32_t);
  if (rem < sz)
    sz = rem;
  f.write(reinterpret_cast<uint8_t *>(&data[addr]), sz);
  addr += SPI_FLASH_SEC_SIZE;
  rem -= sz;
  yield();
  Serial.println(rem);
}
f.flush();
f.close();

, 👍1

Обсуждение

Я начал изучать это после вашего предыдущего вопроса (потому что, я думаю, это тоже будет полезно для меня) - если вы посмотрите, как устроена EEPROM, вы увидите, что она использует spi_flash_read - который определен в tools/ sdk/include/spi_flash.h — теперь я **думаю**, что прошивка будет загружаться, начиная с адреса 0 — и, возможно, [этот код Python](https://microcontrollerelectronics.com/decoding-an-esp8266- прошивка-image/) (очевидно, преобразованная в C++) может помочь определить точный размер загруженной прошивки., @Jaromanda X

на самом деле... есть ESP.getSketchSize() для получения размера скетча :p - так что теоретически это просто вопрос использования spi_flash_read для чтения скетча (очевидно, частями, поскольку скетч больше, чем ОЗУ) и запишите его в SPIFFS, @Jaromanda X

Кажется, я не могу заставить это работать. Я разместил код, который написал в вопросе сейчас, но что-то не работает. Я получаю файл с другим содержимым, хотя он ИСТИННОГО размера., @RDragonrydr

было бы неплохо, если бы вы могли прочитать это и посмотреть, что там - для начала unit32_t выглядит неправильно, ваши данные будут 4 x SPI_FLASH_SEC_SIZE, @Jaromanda X

Я понимаю, что чтение флэш-памяти может выполняться с 32 битами за раз, но не уверен, что адрес обрабатывается правильно... возможно, addr += SPI_FLASH_SEC_SIZE * 4;?, @Jaromanda X

Я также думаю, что вы получаете только байты SPI_FLASH_SEC_SIZE, а не dword (32 бита)... так что вы будете писать изрядное количество мусора - опять же, не уверен, документация в лучшем случае расплывчата., @Jaromanda X


1 ответ


Лучший ответ:

3

Я думаю, что вы читаете Flash немного неправильно

хотя ESP.flashRead принимает указатель unit32_t для выходных данных, аргумент чтения «count» находится в байтах — если посмотреть на код Updater.cpp, он делает это

uint8_t buff[128];
for(int i = 0; i < binSize; i += sizeof(buff)) {
  ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff));
  .....
}

Итак, адрес и количество указаны в байтах - учитывая, что SPI_FLASH_SEC_SIZE равен 0x1000 или 4096, ваш код выполняет следующее

  • чтение 4096 байт в буфер 16384
  • запись 16384 байт (поэтому последние 12288 байт нулевые или случайные, не уверен)
  • добавление 4096 к адресу
  • вычитая 16384 из размера.
  • повторять, пока все не будет прочитано.

Итак, написанный размер будет правильным, но данные будут неправильными на три четверти :p

Поэтому ваш код должен быть

unsigned long addr = 0;
unsigned long rem = ESP.getSketchSize();
uint8_t *data = new uint8_t[SPI_FLASH_SEC_SIZE];
int sz = SPI_FLASH_SEC_SIZE;
File f = SPIFFS.open("/fw.bin", "w");

while (rem > 0) {
    ESP.flashRead(addr, (uint32_t *)data, SPI_FLASH_SEC_SIZE);
    if (rem < sz)
        sz = rem;
    f.write(data, sz);
    addr += SPI_FLASH_SEC_SIZE;
    rem -= sz;
    yield();
    Serial.println(rem);
}

f.flush();
f.close();
,

Кажется, это сработало очень хорошо. Спасибо. Отмечу, что как написано, это вызывает ошибку компиляции, так как f.write не принимает uint32_t. Я по-прежнему использовал данные для чтения флэш-памяти, но вместо этого определил «данные» как uint8_t., @RDragonrydr

Упс, я пропустил unt32, @Jaromanda X