Skip to main content

Using PSRAM on Unexpected Maker Boards

Most Unexpected Maker boards include PSRAM (Pseudo-Static RAM), giving you significantly more memory to work with beyond the ESP32's limited internal SRAM.

BoardPSRAMType
TinyPICO4 MBQuad
TinyPICO Nano4 MBQuad
FeatherS28 MBQuad
FeatherS2 Neo2 MBQuad
TinyS22 MBQuad
BoardPSRAMType
FeatherS38 MBQuad
FeatherS3 Neo2 MBQuad
TinyS38 MBQuad
ProS38 MBQuad
NanoS38 MBQuad
OMGS32 MBQuad
BoardPSRAMType
SQUiXL8 MBOctal
FeatherS3[D]8 MBQuad
TinyS3[D]8 MBQuad
ProS3[D]8 MBQuad
EdgeS3[D]2 MBQuad

CircuitPython & MicroPython

PSRAM is automatically detected and used in both CircuitPython and MicroPython. You don't need to do anything — the runtime will use PSRAM for heap allocations transparently. You can verify it's available:

MicroPython:

import micropython
micropython.mem_info()

CircuitPython:

import gc
gc.collect()
print(f"Free memory: {gc.mem_free()} bytes")

Arduino

In Arduino, you need to enable PSRAM in your board settings. For ESP32-S2 and ESP32-S3 boards, PSRAM is typically enabled by default in the board definition.

Allocating Memory in PSRAM

Use ps_malloc() for a simple allocation, or heap_caps_malloc() for more control:

// Simple PSRAM allocation
uint8_t *buffer = (uint8_t *)ps_malloc(1024 * 64); // 64KB in PSRAM
if (buffer == NULL) {
Serial.println("PSRAM allocation failed!");
}

// Using heap_caps_malloc for explicit PSRAM allocation
uint8_t *data = (uint8_t *)heap_caps_malloc(1024 * 128, MALLOC_CAP_SPIRAM);
if (data != NULL) {
// Use the buffer...

// Free when done
free(data);
}

Checking Available PSRAM

void setup() {
Serial.begin(115200);

Serial.printf("Total PSRAM: %d bytes\n", ESP.getPsramSize());
Serial.printf("Free PSRAM: %d bytes\n", ESP.getFreePsram());
}

ESP-IDF

In ESP-IDF, enable PSRAM via menuconfig under Component config → ESP PSRAM.

Allocating and Freeing PSRAM

#include "esp_heap_caps.h"

// Allocate from PSRAM
void *buf = heap_caps_malloc(65536, MALLOC_CAP_SPIRAM);
if (buf != NULL) {
// Use the buffer...

// Free when done (standard free works)
free(buf);
}

// Allocate with calloc (zeroed memory)
void *zbuf = heap_caps_calloc(1024, sizeof(uint32_t), MALLOC_CAP_SPIRAM);

// Reallocate
buf = heap_caps_realloc(buf, 131072, MALLOC_CAP_SPIRAM);

Automatic PSRAM Usage with malloc()

If CONFIG_SPIRAM_USE_MALLOC is enabled, standard malloc() calls will automatically fall back to PSRAM when internal memory is exhausted, or when the allocation exceeds the threshold set by CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL (default 16KB).

What Cannot Be Stored in PSRAM

PSRAM has some important limitations due to its external bus connection:

  • DMA buffers — DMA hardware cannot access external RAM. Buffers for SPI, I2S, ADC DMA, etc. must be in internal memory. Use heap_caps_malloc(size, MALLOC_CAP_DMA) for these.
  • Task stacks — By default, FreeRTOS task stacks must be in internal memory. While this can be overridden, it is not recommended for tasks that call ROM functions.
  • TLS/SSL buffers — Some TLS operations require internal memory for certain allocations. If you're running into TLS handshake failures, check whether PSRAM is being used where internal memory is needed.
  • Interrupt handler memory — Anything accessed from an interrupt context during flash cache misses must be in internal RAM.
  • Flash cache dependency — When flash is being written (e.g. during OTA or NVS writes), PSRAM becomes temporarily inaccessible since it shares the cache.

Performance Considerations

PSRAM is slower than internal SRAM due to the external SPI/OPI bus. For performance-critical code, keep frequently accessed data in internal memory and use PSRAM for large buffers like frame buffers, audio buffers, or data storage.


Last Updated: February 2026

spiram spi-ram externalram external-ram himem