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.
| Board | PSRAM | Type |
|---|---|---|
| TinyPICO | 4 MB | Quad |
| TinyPICO Nano | 4 MB | Quad |
| FeatherS2 | 8 MB | Quad |
| FeatherS2 Neo | 2 MB | Quad |
| TinyS2 | 2 MB | Quad |
| Board | PSRAM | Type |
|---|---|---|
| FeatherS3 | 8 MB | Quad |
| FeatherS3 Neo | 2 MB | Quad |
| TinyS3 | 8 MB | Quad |
| ProS3 | 8 MB | Quad |
| NanoS3 | 8 MB | Quad |
| OMGS3 | 2 MB | Quad |
| Board | PSRAM | Type |
|---|---|---|
| SQUiXL | 8 MB | Octal |
| FeatherS3[D] | 8 MB | Quad |
| TinyS3[D] | 8 MB | Quad |
| ProS3[D] | 8 MB | Quad |
| EdgeS3[D] | 2 MB | Quad |
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