Use PSRAM
This article explains how to use the additional external RAM (PSRAM) available on some ESP32 boards ( uPesy ESP32 Wrover DevKit , TinyPICO ) with the Arduino language and on the ESP-IDF framework.
Overview
Since the internal RAM of the microcontrollers is relatively low, an additional external RAM can be added. Even if the ESP32 is a microcontroller with a lot of RAM, it may not be enough, especially when you want to handle huge files such as JSON or HTML files.
- The PSRAM is an additional external RAM of 4 MB, which is present in some ESP32 modules:
-
-
ESP32-WROVER-B
-
ESP32-WROVER-I
-
The ESP32 communicates with the PSRAM by SPI. This is why it is also called SPI RAM . The 3rd ESP32 SPI bus is used to communicate with the flash memory (which contains the program) and the PSRAM.
Note
There is an ambiguity about the amount of the PSRAM’s real memory. In fact, the actual size of the PSRAM (on the silicon chip) is not 4MB but 8MB. However, only 4MB can be easily accessed by the software. It is pretty complex to use the 8 MB of PSRAM, Use 8 MB of PSRAM . This said, 4 MB more RAM is already massive (8 times the internal RAM)!
The advantage of an external RAM is to be able to relieve the internal RAM for the storage of essential data:
- We can, for example:
-
-
Handling large JSON files
-
Handling entire HTML WEB pages
-
Creating a powerful webserver
-
Creating huge arrays
-
Read and handle large files from the SPIFFS or SD card.
-
How to use it?
On Arduino IDE, to use the PSRAM, you have to select a compatible board, for example, the ESP32 Wrover Module board, which works for all ESP32 boards that have a PSRAM.

If you are using a board found in the list, like the TinyPICO board, check that the PSRAM is enabled.

Warning
The PSRAM does not appear in the ESP32 memory table, so it is expected that the RAM size indicated on Arduino IDE or Platform is always 327 KB . There is a clear separation between the internal RAM of the ESP32 (327 KB) and the external RAM (4 MB).

To use PSRAM, you can either use libraries that support PSRAM or use dynamic allocations to create arrays, buffers or download HTLM, JSON files …
Arduino / ESP32 libraries
To avoid dealing with dynamic allocations (and struggle), some Arduino libraries (compatible with ESP32) support the PSRAM. The most famous is ArduinoJson , which easily handles JSON files on Arduino and ESP32.
ArduinoJson
If you are new to ArduinoJson, read this guide to install and learn
how to use the library
. To use the PSRAM with ArduinoJson, you must create a specific
JsonDocument
that is placed before the
setup()
function:
struct SpiRamAllocator {
void* allocate(size_t size) {
return ps_malloc(size);
}
void deallocate(void* pointer) {
free(pointer);
}
};
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;
Then we use the
SpiRamJsonDocument
like a classic
``
DynamicJsonDocument
``
:
SpiRamJsonDocument doc(100000); //100 KB here
deserializeJson(doc, input);
Here is an example, which allows downloading a 65 KB JSON file. The JSON contains research results “ESP32 JSON” on Google.
Example
#include <WiFi.h>
#include <HTTPClient.h>
#define ARDUINOJSON_DECODE_UNICODE 1
#include <ArduinoJson.h>
//WiFi
const char *ssid = "WiFi name";
const char *password = "WiFi password";
const char *url = "https://raw.githubusercontent.com/uPesy/ESP32_Tutorials/master/JSON/bigJsonExample.json";
struct SpiRamAllocator {
void* allocate(size_t size) {
return ps_malloc(size);
}
void deallocate(void* pointer) {
free(pointer);
}
};
using SpiRamJsonDocument = BasicJsonDocument<SpiRamAllocator>;
void setup(){
delay(500);
psramInit();
Serial.begin(115200);
Serial.println((String)"Memory available in PSRAM : " +ESP.getFreePsram());
//Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED){
delay(100);
Serial.print(".");
}
Serial.print("\nWiFi connected with IP : ");
Serial.println(WiFi.localIP());
Serial.println("Downloading JSON");
HTTPClient http;
http.useHTTP10(true);
http.begin(url);
http.GET();
SpiRamJsonDocument doc(100000); //Create a JSON document of 100 KB
DeserializationError error = deserializeJson(doc, http.getStream());
if(error){
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
}
http.end();
Serial.println((String)"JsonDocument Usage Memory: " + doc.memoryUsage());
for (int i=0; i!=10;i++){
Serial.println("\n[+]");
Serial.println(doc["items"][i]["title"].as<String>());
Serial.println("------------------------------");
Serial.println(doc["items"][i]["snippet"].as<String>());
Serial.println("------------------------------");
Serial.println((String) "URL : " + doc["items"][i]["link"].as<String>());
}
}
void loop(){
}
Serial monitor:
Memory available in PSRAM: 4194252
..............
WiFi connected with IP: 192.168.43.7
Downloading JSON
JsonDocument Usage Memory: 62515
[+]
Installing ESP32 in Arduino IDE (Windows, Mac OS X, Linux ...
------------------------------
Installing ESP32 Add-on in Arduino IDE Windows, Mac OS X, Linux open. Enter
https://dl.espressif.com/dl/package_esp32_index.json into the "Additional Board ...
------------------------------
URL : https://randomnerdtutorials.com/installing-the-esp32-board-in-arduino-ide-windows-instructions/
[+]
ESP32: Parsing JSON – techtutorialsx
------------------------------
Apr 26, 2017 ... The objective of this post is to explain how to parse JSON messages with the
ESP32 and the ArduinoJson library. Introduction The objective of ...
------------------------------
URL : https://techtutorialsx.com/2017/04/26/esp32-parsing-json/
...
See my Instructable , that explains how you use Google Search on an ESP32 and display the result in the serial monitor. (Also works without PSRAM)
See also
See how to use the external RAM on the ESP32 for JSON files on the official ArduinoJson site.
Arduino framework
To store a variable in the PSRAM, the dynamic allocation must be used with dedicated functions for the ESP32 PSRAM.
Note
It is not necessary to have advanced knowledge on C / C ++ to use PSRAM
! Knowledge of pointers, dynamic allocation functions like
malloc()
or
calloc()
is a plus, but the proposed examples are sufficient on their own to use PSRAM in electronic projects.
The main functions to use are the following
psramInit()
,
ESP.getFreePsram()
,
ps_malloc()
or
ps_calloc()
and
free()
.
The
psramInit()
function is used to initialize the PSRAM, the
ESP.getFreePsram()
function returns the amount of memory available in the PSRAM. The other 3 functions are used for dynamic allocation.
Examples
This section provides examples of how to use the PSRAM. They allow you to understand how to use the functions mentioned above. It is not necessary to understand everything to be able to use the PSRAM. You just have to modify the values in the examples to use them in your projects.
Variables
-
This example shows how to create variables that are stored in the PSRAM:
//Create an integer number
int *var_int = (int *) ps_malloc(sizeof(int));
*var_int = 42;
//Create a float number
float *var_float = (float *) ps_malloc(sizeof(float));
*var_float = 42.42;
Here is the complete sketch:
Example
void setup() {
Serial.begin(115200);
//PSRAM Initialisation
if(psramInit()){
Serial.println("\nThe PSRAM is correctly initialized");
}else{
Serial.println("\nPSRAM does not work");
}
//Create an integer
int *var_int = (int *) ps_malloc(sizeof(int));
*var_int = 42;
//Create a float
float *var_float = (float *) ps_malloc(sizeof(float));
*var_float = 42.42;
Serial.println((String)"var_int = " + *var_int);
Serial.print("var_float = ");
Serial.println(*var_float);
}
Terminal output:
The PSRAM is correctly initialized
var_int = 42
var_float = 42.42
Array
For arrays, the syntax is very similar:
//Create an array of 1000 integers
int n_elements = 1000;
int *int_array = (int *) ps_malloc(n_elements * sizeof(int));
//We access array values like a classic array
int_array[0] = 42;
int_array[42] = 42;
int_array[999] = 42;
-
This is the complete sketch example that creates an array of integers of 1000 elements stored in the PSRAM:
Example
int n_elements = 1000;
void setup(){
Serial.begin(115200);
//Init
if(psramInit()){
Serial.println("\nPSRAM is correctly initialized");
}else{
Serial.println("PSRAM not available");
}
//Create an array of n_elements
int available_PSRAM_size = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available (bytes): " + available_PSRAM_size);
int *array_int = (int *) ps_malloc(n_elements * sizeof(int)); //Create an integer array of n_elements
array_int[0] = 42;
array_int[999] = 42; //We access array values like classic array
int available_PSRAM_size_after = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available (bytes): " + available_PSRAM_size_after); // Free memory space has decreased
int array_size = available_PSRAM_size - available_PSRAM_size_after;
Serial.println((String)"Array size in PSRAM in bytes: " + array_size);
//Delete array
free(array_int); //The allocated memory is freed.
Serial.println((String)"PSRAM Size available (bytes): " +ESP.getFreePsram());
}
void loop() {
}
Terminal output:
PSRAM is correctly initialized
PSRAM Size available (bytes): 4194252
PSRAM Size available (bytes): 4190236
Array size in PSRAM in bytes: 4016
PSRAM Size available (bytes): 4194252
Note
The size in bytes is 4016 for an array of 1000 integers (int). The type
int
is encrypted on 4 bytes. There are, therefore 4 * 1000 bytes use by the array. The remaining 16 bytes contain information on the memory block (size, flags).
-
For other array types:
char * array1 = (char *) ps_malloc (n_elements * sizeof (char)); // Create an empty array of n_elements characters
float * array = (float *) ps_malloc (n_elements * sizeof (float)); // Create an array of n_elements float number
-
We can also create arrays that are filled with zeros with
ps_calloc()
:
int n_elements = 20;
Serial.println((String)"PSRAM Size available (bytes): " +ESP.getFreePsram());
Serial.println("Array of integers initialized to 0");
int *array = (int *) ps_calloc(n_elements, sizeof(int));
Serial.print("[array] : ");
for(int i=0; i!= n_elements;i++){
Serial.print((String)array[0] + " ");
}
Serial.println((String)"\nPSRAM Size available (bytes): " +ESP.getFreePsram());
Terminal output:
PSRAM Size available (bytes): 4194252
Array of integers initialized to 0
[array]: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
PSRAM Size available (bytes): 4194156
-
Here are 2 ways to create 2-dimensional arrays:
Example
int n_rows = 10;
int n_columns = 20;
void setup(){
Serial.begin(115200);
//Initialisation
if(psramInit()){
Serial.println("\nPSRAM is correctly initialized");
}else{
Serial.println("PSRAM not available");
}
//Create an array of n_elements
int initialPSRAMSize = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available (bytes): " + initialPSRAMSize);
//Creating a two-dimensional array with a one-dimensional array
int *array2D = (int *) ps_malloc(n_rows * n_columns * sizeof(int)); // Create an array of n_rows x n_columns
//To access the value located in row 5 to column 10
int row_i = 5;
int column_j = 10;
array2D[column_j * n_columns + row_i] = 42;
//Creating a two-dimensional zero-filled array with an array of pointers
int **array2Dbis = (int **) ps_calloc(n_rows, sizeof(int *));
for (int i =0; i!= n_rows;i++){
array2Dbis[i] = (int *) ps_calloc(n_columns , sizeof(int));
}
//To access the value located in row 5 to column 10
array2Dbis[row_i][column_j] = 42;
int PSRAMSize = ESP.getFreePsram();
Serial.println((String)"PSRAM Size available (bytes): " + PSRAMSize);
int array2D_size = initialPSRAMSize - PSRAMSize;
Serial.println((String)"Size of the array in PSRAM in bytes: " + array2D_size);
//Delete 2D arrays
free(array2D); //Allocated memory is freed.
free(array2Dbis);
Serial.println((String)"PSRAM Size available (bytes): " +ESP.getFreePsram());
}
void loop() {
}
String
String object is just a
char
array:
int n_elements = 20;
char *str = (char *) ps_calloc(n_elements, sizeof(char)); //Create an array of n_elements null characters ('\0')
str[0] = '4';
str[1] = '2';
Note
The use of
ps_calloc()
is more relevant here because it allows the ESP32 to detect the character string’s end.
Here is an example sketch:
void setup() {
Serial.begin(115200);
if(psramInit()){
Serial.println("\nThe PSRAM is correctly initialized");
}else{
Serial.println("\nPSRAM does not work");
}
int n_elements = 20;
char *str = (char *) ps_calloc(n_elements, sizeof(char)); //Create an array of n_elements null characters ('\0')
for(int i = 0; i < 10;i+=3){
str[i] = '4';
str[i+1] = '2';
str[i+2] = '_';
}
Serial.println(str);
}
Terminal output:
The PSRAM is correctly initialized
42_42_42_42_
HTML/JSON
Here is an example that allows you to download any text file like HTLM pages or JSON files (less than 4MB) and store it in the PSRAM.
#include <WiFi.h>
#include <HTTPClient.h>
#define TIMEOUT 5000
const char *ssid = "Nom Box WiFi";
const char *password = "mdp";
const char *url = "https://fr.wikipedia.org/wiki/ESP32";
void setup() {
Serial.begin(115200);
//Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED){
delay(100);
Serial.print(".");
}
Serial.print("\nConnecté au WiFi avec comme IP : ");
Serial.println(WiFi.localIP());
char *page = (char *) ps_malloc(sizeof(char));
int lengthHtml = 0;
HTTPClient http;
http.begin(url);
http.useHTTP10(true); //Permet d'essayer d'avoir un Content-Length dans le header en utilisant HTTP1
Serial.println((String) "Try GET "+ url);
int httpCode = http.GET();
if (httpCode > 0){
// Affiche le réponse le code HTTP de la requete GET
Serial.printf("[HTTP] GET... code: %d\n", httpCode);
// Si le fichier existe (HTML, JSON, ...)
if (httpCode == HTTP_CODE_OK){ // HTTP_CODE_OK = 200
// Essaye de récupérer la taille du ficher ( -1 s'il n'y a pas de Content-Length dans l'header)
int tempLength = http.getSize();
Serial.println((String) "Content Length :" + tempLength);
//Stockage de la page dans la PSRAM
Serial.printf("Adresse mémoire de la page : %p \n", page);
// Récupère le stream TCP
WiFiClient *stream = http.getStreamPtr();
//Initialisation position du buffer dans la PSRAM
int position = 0;
uint32_t currentTime = 0;
uint8_t timeoutArboted = 1;
// Récupère toute les données du fichier
while(http.connected() && (tempLength > 0 || tempLength == -1)){
// Récupère les données disponibles (les données arrivent packets par packets)
size_t size = stream->available();
if (size){
page = (char*) ps_realloc(page, position + size + 1);
stream->readBytes(page+position, size);
position += size;
if (tempLength > 0){
tempLength -= size;
}
timeoutArboted = 1;
}else{
//Si on ne connaît pas la taille du fichier, on suppose que toutes les données sont recues avant le timeout
if(timeoutArboted){
//Lance le timer
currentTime = millis();
timeoutArboted = 0;
}else{
if(millis()-currentTime > TIMEOUT){
//On a atteind le Timeout
Serial.println("Timeout reached");
break;
}
}
}
}
*(page+position) = '\0';
lengthHtml = position;
Serial.println((String)"Downloaded " + lengthHtml + " Octets");
}
}
else{
Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
//Ferme le client HTTP
http.end();
delay(1500);
Serial.println(page); // Pour afficher la page HTML entière
}
Terminal output:
...........
WiFi connected with IP : 192.168.43.7
Try GET https://en.wikipedia.org/wiki/ESP32
[HTTP] GET... code: 200
Content Length :117615
Memory address of HTML page in PSRAM : 0x3f800024
Downloaded 117615 Octets
<!DOCTYPE html>
<html class="client-nojs" lang="en" dir="ltr">
<head>
<meta charset="UTF-8"/>
<title>ESP32 - Wikipedia</title>
...
...
Note
You can notice that it is much easier to use the ArduinoJson library to download JSON files.
API Reference
- psramInit()
-
- Returns
-
true if the PSRAM has successfully initialized, false otherwise
- Return type
-
boolean
This function ensures that the PSRAM can be used by making various tests, then it initializes.
- ESP.getFreePsram()
-
- Returns
-
The memory available in bytes of the PSRAM.
- Return type
-
int
This function allows you to find out the memory available in the PSRAM. The maximum size is 4,194,252 bytes .
- ps_malloc(size_t tailleBloc)
-
- Parameters
-
-
tailleBloc – The total number of bytes that you want to store in RAM.
-
- Returns
-
A pointer of the type given in the argument contains the address of the memory block.
- Return type
-
void *
This function allows you to allocate a memory block in the PSRAM.
- ps_calloc(size_t nElements, int tailleElement)
-
- Parameters
-
-
nElements – Number of elements that we want to store in the RAM
-
tailleElement – Size in bytes of a single element.
-
- Returns
-
A pointer of the type given in the argument contains the address of the memory block.
- Return type
-
void *
This function allocates a memory block of nElements , each of size sizeElement in the PSRAM and initializes all these bytes to the value of 0 .
- ps_realloc(void * adresseBloc, nouvelleTailleBloc)
-
- Parameters
-
-
adresseBloc – The memory address of the memory block is to be reallocated.
-
nouvelleTailleBloc – New size in bytes of the memory block that we want to reserve in RAM.
-
- Returns
-
A pointer of the type is given as an argument to the reallocated memory area.
- Return type
-
void *
This function allows you to reallocate a memory block previously allocated with
ps_malloc()
or
ps_calloc()
. If the free memory space following the block to be reallocated is large enough for the new block size, the original memory block is simply enlarged. On the other hand, if the free space is not sufficient, a new memory block will be allocated, the content of the original zone copied into the new location, and the actual memory block will be freed automatically.
- free(void *pointer)
-
- Parameters
-
-
pointer ( *int, *char, *double, *long... ) – The address of the memory block to be deallocated
-
This function frees a dynamically allocated memory block with
ps_malloc()
or
ps_calloc()
.
ESP-IDF Framework
The functions used on the Arduino framework are based on those of the ESP-IDF Framework. Thus the functions described below are used implicitly on the Arduino-ESP32 Framework.
The functions to be used are the following
esp_spiram_init()
,
heap_caps_get_free_size()
,
heap_caps_malloc()
or
heap_caps_calloc()
and
free()
See also
For further information, see the official documentation on PSRAM