1

I am aware that there is a way to create a dump of the flash via esptool, but is there a way to read the contents of the sketch (just the sketch, not the full flash or SPIFFS) memory via the sketch itself? As in, have a program on the ESP8266 able to read its own machine code from the flash to do something with? I do not need write access, just read access.

I would like to be able to create a backup of the current sketch in SPIFFS so that it is possible to revert a bad sketch so long as the reversion trigger isn't broken. (For example, if a later part of the boot process is messed up, or if a function acts slightly wrong, a button could be held, and it would restore from the saved image. The restore process is something that I've figured out from my previous question, but I would like a way to write the sketch to SPIFFS that does not require a separate manual upload or a wrapper around the OTA process.)

[edit:] Thank you for the comments.

I have created this code that SHOULD have copied the sketch to SPIFFS. However, it does not seem to have done so properly (since the result does not have the same checksums as the original file, and attempts to write it yield a nonfunctional device). Can I have someone look over it and see if they can figure out why?

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();
6
  • 1
    I started looking into this after your previous question (because it's something I think would be useful for me as well) - if you look at how EEPROM is done, you'll see it uses spi_flash_read -which is defined in tools/sdk/include/spi_flash.h - now I think the firmware would be loaded starting at address 0 - and perhaps this python code (obviously converted to c++) may help in determining the exact size of the loaded firmware Apr 24, 2019 at 9:02
  • actually, ... there's ESP.getSketchSize() to get the size of the sketch :p - so, in theory, it's just a matter of using spi_flash_read to read the sketch (in chunks, obviously, since the sketch is larger than RAM) and write it out to SPIFFS Apr 24, 2019 at 9:57
  • I can't seem to get that to work. I've posted the code I wrote in the question now, but something is not working. I get a file with different contents, though it IS the right size. Apr 25, 2019 at 8:33
  • what would be nice is if you could read it to see what's there - the unit32_t looks wrong for a start, your data would be 4 x SPI_FLASH_SEC_SIZE Apr 25, 2019 at 8:35
  • I can understand that flash reading may be done with 32bits at a time, but not sure if the address is being manipulated correctly ... perhaps addr += SPI_FLASH_SEC_SIZE * 4;? Apr 25, 2019 at 8:41

1 Answer 1

3

I think you're reading of flash is slightly wrong

while ESP.flashRead takes a unit32_t pointer for output data, the read "count" argument is in bytes - looking at Updater.cpp code it does this

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

so, the the address and count are in bytes - given that SPI_FLASH_SEC_SIZE is 0x1000 or 4096, your code is doing the following

  • reading 4096 bytes into a 16384 buffer
  • writing 16384 bytes (so the last 12288 bytes are zero, or random, not sure)
  • adding 4096 to the address
  • subtracting 16384 from size
  • repeat until all read

So, the written size will be correct, but the data will be three quarters wrong :p

Therefore, your code should be

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();
2
  • That seems to have worked very well. Thank you. I will note that as written, it causes a compile error since f.write does not accept uint32_t. I still cast the data for flash read, but defined 'data' as a uint8_t instead. Apr 25, 2019 at 16:47
  • Oops I missed a unt32 Apr 25, 2019 at 22:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.