Memory Configuration
Memory Overview
The N16R8 variant provides substantial memory resources:
| Memory | Size | Interface | Speed |
|---|---|---|---|
| Internal SRAM | 512 KB | - | CPU clock |
| Flash | 16 MB | Quad SPI (QIO) | 80 MHz |
| PSRAM | 8 MB | Octal SPI (OPI) | 80 MHz |
Flash Memory
Flash Configuration
| Parameter | Value |
|---|---|
| Size | 16 MB (128 Mbit) |
| Interface | Quad SPI (4-bit) |
| Speed | 80 MHz |
| Address Range | 0x00000000 - 0x00FFFFFF |
Default Partition Table
ESP-IDF default for 16MB flash:
| Partition | Type | Offset | Size | Notes |
|---|---|---|---|---|
| nvs | data | 0x9000 | 24 KB | Non-volatile storage |
| phy_init | data | 0xF000 | 4 KB | PHY calibration |
| factory | app | 0x10000 | 1 MB | Main application |
| (unused) | - | - | ~15 MB | Available for expansion |
Custom Partition Table for 16MB
Create partitions.csv:
# Name, Type, SubType, Offset, Size, Flagsnvs, data, nvs, 0x9000, 0x6000,phy_init, data, phy, 0xf000, 0x1000,factory, app, factory, 0x10000, 0x300000,ota_0, app, ota_0, 0x310000, 0x300000,ota_1, app, ota_1, 0x610000, 0x300000,spiffs, data, spiffs, 0x910000, 0x6F0000,This provides:
- 3 MB for factory app
- 3 MB each for 2 OTA slots
- 7 MB for SPIFFS/LittleFS
Configuration
In menuconfig:
Serial flasher config → Flash size → 16 MB Flash SPI speed → 80 MHz Flash SPI mode → QIOIn platformio.ini:
board_build.flash_mode = qioboard_upload.flash_size = 16MBboard_build.partitions = partitions.csvPSRAM (Octal SPI)
PSRAM Configuration
| Parameter | Value |
|---|---|
| Size | 8 MB (64 Mbit) |
| Interface | Octal SPI (8-bit) |
| Speed | 80 MHz |
| Type | ESP-PSRAM64H or equivalent |
Enabling PSRAM
In menuconfig:
Component config → ESP PSRAM → [*] Support for external, SPI-connected RAM SPI RAM config → Mode (QUAD/OCT) → Octal Mode SPI RAM access method → Make RAM allocatable using malloc SPIRAM size → 8MBIn platformio.ini:
board_build.psram_type = opiboard_build.arduino.memory_type = qio_opibuild_flags = -DBOARD_HAS_PSRAMUsing PSRAM
#include <Arduino.h>
void setup() { Serial.begin(115200);
if (psramFound()) { Serial.printf("PSRAM Size: %d bytes\n", ESP.getPsramSize()); Serial.printf("Free PSRAM: %d bytes\n", ESP.getFreePsram()); }}
void loop() { // Allocate in PSRAM uint8_t* largeBuffer = (uint8_t*)ps_malloc(1024 * 1024); // 1MB
if (largeBuffer) { Serial.println("Allocated 1MB in PSRAM"); // Use buffer... free(largeBuffer); }
// Allocate with preference for PSRAM uint8_t* buffer = (uint8_t*)heap_caps_malloc(64 * 1024, MALLOC_CAP_SPIRAM);
delay(5000);}Internal Memory
SRAM Layout
| Region | Size | Use |
|---|---|---|
| DRAM | 320 KB | Variables, heap |
| IRAM | 128 KB | Code execution, ISRs |
| RTC Fast | 8 KB | Fast RTC functions |
| RTC Slow | 8 KB | ULP code/data |
Memory Capabilities
| Capability | Description |
|---|---|
| MALLOC_CAP_DEFAULT | Any memory |
| MALLOC_CAP_INTERNAL | Internal SRAM only |
| MALLOC_CAP_SPIRAM | PSRAM only |
| MALLOC_CAP_DMA | DMA-capable memory |
| MALLOC_CAP_EXEC | Executable memory |
Checking Available Memory
void printMemoryInfo() { Serial.printf("Total heap: %d\n", ESP.getHeapSize()); Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize()); Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram());
// Detailed breakdown Serial.printf("Internal free: %d\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); Serial.printf("DMA-capable free: %d\n", heap_caps_get_free_size(MALLOC_CAP_DMA));}XIP (Execute In Place)
The ESP32-S3 supports executing code directly from flash:
Cache Configuration
| Parameter | Value |
|---|---|
| I-Cache | 16 KB |
| D-Cache | 16 KB (configurable) |
PSRAM for Code
With XIP enabled, code can run from PSRAM:
Component config → ESP PSRAM → SPI RAM config → [*] Move instructions in flash to PSRAM [*] Move constant data in flash to PSRAMMemory Optimization Tips
1. Use PSRAM for Large Buffers
// Good: Large buffers in PSRAMuint8_t* imageBuffer = (uint8_t*)ps_malloc(320 * 240 * 2);
// Bad: Large buffers in internal RAMuint8_t imageBuffer[320 * 240 * 2]; // Stack overflow risk2. DMA Buffers in Internal RAM
// DMA requires internal RAMuint8_t* dmaBuffer = (uint8_t*)heap_caps_malloc(4096, MALLOC_CAP_DMA);3. Place Strings in Flash
// Good: String in flashSerial.println(F("This string stays in flash"));
// Or using PROGMEMconst char message[] PROGMEM = "Flash string";4. Monitor Heap Fragmentation
void checkFragmentation() { size_t largest = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT); size_t total = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); Serial.printf("Fragmentation: %.1f%%\n", 100.0 * (1.0 - (float)largest / total));}Troubleshooting
PSRAM Not Detected
- Verify Octal mode configuration
- Check GPIO35-37 are not used elsewhere
- Confirm board is N8R8 or N16R8 variant
Out of Memory Errors
- Check heap usage with
ESP.getFreeHeap() - Move large allocations to PSRAM
- Use LittleFS instead of SPIFFS for storage
Stack Overflow
- Increase task stack size in FreeRTOS config
- Avoid large local arrays
- Use heap allocation for big buffers