Partition tables and flash size
A menu item most people pick without reading decides whether OTA is possible, how big a sketch may be, and how much room is left for files. It is written into the flash when you upload, and changing it later erases everything.
What each scheme gives you
Decide it once, at the start
The table is written by the uploader, so changing it moves every boundary. NVS moves, the filesystem moves, and everything stored in either is gone. That is fine on day one and expensive on day two hundred.
Pick a scheme that leaves headroom for the libraries you have not added yet. BLE, HTTPS and a display driver together are well over a megabyte before your own code exists.
When the default is not enough
- BLE plus OTA on 4 MB — tight. Use NimBLE, or buy an 8 MB board.
- A camera — you want PSRAM more than flash, but also a big app slot.
- A web interface with real assets — a bigger filesystem, or serve them from a microSD card.
Custom tables are a CSV file next to your sketch, and easier than they look. The
IDE picks up partitions.csv from the sketch folder automatically.
The code
Print the table the board is actually running. It takes ten seconds and settles arguments about what scheme got flashed, which the IDE menu will not tell you after the fact.
#include <esp_partition.h>
#include <esp_ota_ops.h>
void setup() {
Serial.begin(115200);
delay(200);
esp_partition_iterator_t it =
esp_partition_find(ESP_PARTITION_TYPE_ANY,
ESP_PARTITION_SUBTYPE_ANY, nullptr);
while (it) {
const esp_partition_t *p = esp_partition_get(it);
Serial.printf("%-10s type %d sub %2d at 0x%06X size %7u\n",
p->label, p->type, p->subtype, p->address, p->size);
it = esp_partition_next(it);
}
const esp_partition_t *run = esp_ota_get_running_partition();
Serial.printf("running from %s, sketch uses %u of %u\n",
run->label, ESP.getSketchSize(), run->size);
}
void loop() {}getSketchSize against the app partition size is the number behind Sketch too big. Watch it as you add libraries rather than discovering it at the end.
MicroPython exposes the same table. The vfs partition is where your files live, and its size is the one people find out about when an upload fails.
import esp32, os
for kind in (esp32.Partition.TYPE_APP, esp32.Partition.TYPE_DATA):
for p in esp32.Partition.find(kind):
print(p)
st = os.statvfs('/')
print('filesystem', st[0] * st[2] // 1024, 'kB total,',
st[0] * st[3] // 1024, 'kB free')A MicroPython firmware image already chose a layout. Changing it means building your own firmware, which is a bigger step than changing a menu item in Arduino.
When it does not work
Half of it is the second OTA slot and a chunk is the filesystem. The app partition is what your code has to fit in, not the flash size.
The partition scheme changed, so every boundary moved. Fix the scheme at the start of a project and never change it in the field.
The Flash Size menu item is a claim, not a detection. Set it to what the board actually has, or you get a table that ends before the flash does.
You are on a no-OTA scheme. There is no second slot to write into, and the error arrives at the end of the download rather than the start.
Storage sorted. The next chapter is the reason most people bought this chip: getting it onto a network.
Wi-Fi station mode →Edit this page — content/esp32/partition-tables-and-flash-size.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.