← How-Tos
electronics 1 hr ago ◯ 4 min read

ESP32 PSRAM and Custom Partition Tables: Allocating Large Buffers, OTA Layouts, and Flash Maps

esp32psrampartition-tableotalittlefsarduinoesp32-s3memory

Two of the most common limits an ESP32 project hits are running out of RAM and running out of flash for the sketch or filesystem. PSRAM gives you megabytes of extra data memory, and a custom partition table lets you decide how the flash is divided between firmware, OTA updates and storage. This guide covers enabling PSRAM, allocating buffers in it, and writing a partition table that fits your project.

Which Boards Have PSRAM

PSRAM is an external RAM chip attached to the ESP32 over SPI. It is only present on certain modules, so check the part number before you assume it is available.

Module / boardPSRAMArduino setting ESP32-WROVER (classic)4 or 8 MB, quad SPIPSRAM: Enabled ESP32-S3 N8R2 / N16R22 MB, quad SPIPSRAM: QSPI PSRAM ESP32-S3 N8R8 / N16R88 MB, octal SPIPSRAM: OPI PSRAM ESP32-CAM (AI Thinker)4 MBPSRAM: Enabled ESP32-WROOM / DevKitCNoneNot applicable

On octal-PSRAM ESP32-S3 modules, GPIO35, 36 and 37 are used internally by the PSRAM and are not available for your own wiring. Choosing the wrong PSRAM mode in the board settings is a very common cause of boot loops, so match it to the module suffix.

Enabling and Testing PSRAM

In the Arduino IDE, open Tools and set the PSRAM option that matches your board. Then verify it from code:

Allocating in PSRAM

The Arduino core provides ps_malloc() and ps_calloc(), and the ESP-IDF interface provides heap_caps_malloc(size, MALLOC_CAP_SPIRAM). Both return a pointer to memory in external RAM. You can also make the default malloc fall back to PSRAM for large allocations in ESP-IDF menuconfig, where large allocations spill into external RAM automatically.

Good candidates for PSRAM are camera frame buffers, display framebuffers, audio ring buffers, large JSON documents, and machine-learning tensors. Poor candidates are anything accessed by DMA on the classic ESP32, or time-critical data such as interrupt handler variables, because PSRAM is slower than internal RAM and goes through a cache.

Limits and Gotchas

Reading the Partition Table

The partition table is stored at flash offset 0x8000 and describes each region as: Name, Type, SubType, Offset, Size. The Arduino core ships several presets in Tools, Partition Scheme, such as Default (two OTA slots and a small SPIFFS area), Huge APP (one large app slot, no OTA), and layouts with a larger filesystem.

PartitionPurpose nvsNon-volatile storage for Wi-Fi credentials and Preferences otadataRecords which OTA slot to boot app0 / app1Two firmware slots, so an update can be written while the current firmware runs spiffs (LittleFS)Files: web pages, config, logs coredumpOptional crash dump storage

Writing a Custom Partition Table

Create a file called partitions.csv in your sketch folder. The Arduino core uses it in preference to the Tools selection. An example for a 4 MB flash with two OTA slots and a larger filesystem:

# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, otadata, data, ota, 0xe000, 0x2000, app0, app, ota_0, 0x10000, 0x1E0000, app1, app, ota_1, 0x1F0000, 0x1E0000, spiffs, data, spiffs, 0x3D0000, 0x30000,

App partitions must start on a 64 KB (0x10000) boundary, and the last partition must end at or before the size of your flash chip. The example above ends exactly at 4 MB (0x400000). On a 16 MB flash board you can enlarge both slots and give the filesystem several megabytes.

Choosing a Layout

NeedLayout Sketch too large for the default slot, no OTASingle large app partition (Huge APP) Wireless updatesTwo equal app slots and otadata Web assets or data loggingEnlarge the filesystem, shrink app slots Rollback safetyTwo OTA slots with rollback enabled in the IDF

Uploading and Recovery

Changing the partition table changes where the filesystem lives, so re-upload the filesystem image after changing it, and expect to lose stored files. If a bad table leaves the board stuck in a boot loop, erase the flash entirely with esptool.py erase_flash and upload a known-good sketch. Keep a copy of a working partitions.csv for each board.

Closing

PSRAM handles the "too much data" problem, and a custom partition table handles the "too much firmware" problem. Together they let a standard ESP32 or ESP32-S3 do jobs, such as camera streaming, web dashboards and large OTA images, that the default configuration cannot.