Dynamically load binary file to flash memory

Hello,

The goal is to load a binary file from the SD/TF card into flash memory and then execute that uploaded code.

I've been reading different approaches. The code for this purpose is below. It is divided on the following sections:

  • find address where to write binary file with enough space -> calculate_flash_offset()
  • write the binary to flash memory -> writeFromFileToFlash()
  • launch the function -> runFile()

The code is able to find the partitions, to calculate the offset where the binary should be loaded but then fails on the message Failed to erase flash memory portion.

root:$ run app.bin
Running /app.bin
Maximum binary app size that can be loaded into partition: 1441792
Size of binary app to be loaded: 238336
Writing to address: 3288704
Failed to erase flash memory: ESP_ERR_INVALID_ARG

I'm not familiar with these libraries, would someone here know how to solve the invalid arguments? I've looked into options such as LittleFS but there seems to exist no way to get the offset where each file gets placed. Perhaps there is a way?

Many thanks in advance!

#include <SdFat.h>-
#include <esp_partition.h>

/**
 * Provides a safe offset for loading the binary from disk storage
 */
uint32_t calculate_flash_offset(File32 file, Stream* response) {
    const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
    uint32_t max_binary_app_size = partition->size;
    uint32_t file_size = file.size();

    response->print("Maximum binary app size that can be loaded into partition: ");
    response->println(max_binary_app_size);

    response->print("Size of binary app to be loaded: ");
    response->println(file_size);

    // Check if file size is larger than available free memory
    if (file_size > max_binary_app_size) {
        response->println("File size is larger than available free memory");
        return -1;
    }

    /*
    Ensure that the binary app is placed in a location
    that is aligned with the memory requirements of the ESP32,
    while also providing enough space for it to run reliably.
    */
    uint32_t offset = partition->address + ((max_binary_app_size - file_size) / 2);
    return offset;
}

void writeFromFileToFlash(uint32_t address, File32 file, Stream* response) {
    // Open file
    if (!file) {
        response->println("Failed to open app file");
        return;
    }

    // Get file size
    size_t fileSize = file.size();

    // Find partition
    const esp_partition_t* partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
    if (!partition) {
        response->println("Failed to find partition for writing");
        file.close();
        return;
    }

    // Erase flash memory
    esp_err_t err = esp_partition_erase_range(partition, address, fileSize);
    if (err != ESP_OK) {
        response->print("Failed to erase flash memory: ");
        response->println(esp_err_to_name(err));
        file.close();
        return;
    }

    // Allocate buffer for file data
    byte* buffer = new byte[fileSize];

    // Read file data into buffer
    if (file.read(buffer, fileSize) != fileSize) {
        response->println("Failed to read app file");
        delete[] buffer;
        file.close();
        return;
    }

    // Write data to flash memory
    err = esp_partition_write(partition, address, buffer, fileSize);
    if (err != ESP_OK) {
        response->println("Failed to write data to flash memory");
        delete[] buffer;
        file.close();
        return;
    }

    // Free buffer memory and close file
    delete[] buffer;
    file.close();

    response->println("Data written to flash memory successfully");
}

/**
 *  Runs a binary app.
 *  Returns false when something went wrong.
 */
boolean runFile(File32 file, Stream* response) {
    uint32_t address = calculate_flash_offset(file, response);
    if (address == -1) {
        response->println("Unable to load app from file");
        return false;
    }
    response->print("Writing to address: ");
    response->println(address);
    writeFromFileToFlash(address, file, response);
    // now run the app
    void (*firmwareEntry)(void) = (void (*)())(address);
    firmwareEntry();
    return true;
}

Maybe a factory reset:
Failed to erase flash memory: ESP_ERR_INVALID_ARG - Search (bing.com)

That message "Failed to write flash memory" comes from my own code. Factory reset doesn't even apply for ESP32, that is something more akin to cellphones.

Few things on this forum are more annoying that an Op that is self- acclaimed at being smarter than those trying to assist:

Again, that article has very little to do with the topic.

Here we are talking about a partial firmware update instead of a full update as mentioned on that article, and we are talking about updating the firmware from inside a running arduino, not from the outside.

Have you done something to make the new binary code position-independent, or will each bit need to be located at a particular address in memory ("overlays") (and does ESP have any support for compiling code into either PIC or Overlay-style binaries?)

    // Erase flash memory
    esp_err_t err = esp_partition_erase_range(partition, address, fileSize);
    if (err != ESP_OK) {
        response->print("Failed to erase flash memory: ");
        response->println(esp_err_to_name(err));
        file.close();
        return;
    }

How about printing the values of partition, address, and fileSize in that error message?
Can you erase arbitrary sizes of partition, or do you need to round up to the next page boundary or something?

1 Like

Here are the full printed values. They seem to be OK and the code is able to query them.

root:$ run app.bin
Running /app.bin
Maximum binary app size that can be loaded into partition: 1441792
Size of binary app to be loaded: 238336
Writing to address: 3288704
Failed to erase flash memory: ESP_ERR_INVALID_ARG

Don't know about the other questions, sorry. :expressionless:

ping

Can you erase arbitrary sizes of partition, or do you need to round up to the next page boundary or something?

Don't know about the other questions, sorry.

Maybe you should investigate more...
From the ESP SDK Docs:

esp_err_t esp_flash_erase_region(esp_flash_t *chip, uint32_t start, uint32_t len)

Erase a region of the flash chip.

Sector size is specifyed in chip->drv->sector_size field (typically 4096 bytes.) ESP_ERR_INVALID_ARG will be returned if the start & length are not a multiple of this size.

Size of binary app to be loaded: 238336
Writing to address: 3288704

Neither of those looks to be a multiple of a like flash block size.

I do not know wneough about the ESP to offer any further advice; my comments thus far haave been based on "general knowledge" of flash memory operation.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.