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();
1 Answer 1
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();
-
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.user47164– user471642019年04月25日 16:47:32 +00:00Commented Apr 25, 2019 at 16:47
-
Oops I missed a unt32Jaromanda X– Jaromanda X2019年04月25日 22:14:16 +00:00Commented Apr 25, 2019 at 22:14
spi_flash_read
-which is defined intools/sdk/include/spi_flash.h
- now I think the firmware would be loaded starting at address0
- and perhaps this python code (obviously converted to c++) may help in determining the exact size of the loaded firmwareESP.getSketchSize()
to get the size of the sketch :p - so, in theory, it's just a matter of usingspi_flash_read
to read the sketch (in chunks, obviously, since the sketch is larger than RAM) and write it out to SPIFFSdata
would be 4 x SPI_FLASH_SEC_SIZEaddr += SPI_FLASH_SEC_SIZE * 4;
?