Use PSRAM

This article explains how to use the additional external RAM of the uPesy ESP32 Wrover Devkit board on the Arduino Framework and on the ESP-IDF Framework.

Presentation and utility

Since the internal RAM of the microcontrollers is quite low, an additional external RAM can be added. Even, if the ESP32 is a microcontroller that has a lot of RAM, it may not be sufficient, especially when you want to handle huge files like JSON, HTML …

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 SPI bus of the ESP32 is used to communicate with the flash memory (which contains the program) and with the PSRAM.

../_images/psram.jpg

Internal overview of the ESP-WROVER-B module

Note

There is an ambiguity about the amount of real memory in the PSRAM. The real size of the PSRAM (on the silicon chip) is not 4MB but 8MB.However, only 4MB can be easily accessed by the software. To use the 8 MB of PSRAM, it is quite complex Use 8 MB of PSRAM This said 4 MB more RAM is already huge (8 times the internal RAM)!

The advantage of an external RAM is to be able to relieve the internal RAM for the storage of important data. We can :

We can for example:
  • Download large Json files

  • Store entire WEB pages

  • Make a powerful web server

  • Create huge arrays

  • Read and manipulate data from large files

How to use it ?

Warning

On the Arduino IDE, to be able to use the PSRAM, you must select a compatible board, such as the ESP32 Wrover Module board.

By default, the PSRAM does not appear in the ESP32 memory table, so it is normal that the size of the RAM indicated on the Arduino IDE or Plateformio is always 320 KB. By default, there is therefore a clear separation between the internal RAM of the ESP32 (320 KB) and the external RAM (4 MB).

To store a variable in the PSRAM, dynamic allocation must be done with dedicated functions for the PSRAM of the ESP32.

Note

It is not necessary to have advanced knowledge of C / C ++ to use PSRAM. Knowledge of pointers, dynamic allocation functions like malloc() or calloc() is a plus, but the examples offered are sufficient in themselves to use PSRAM in electronic projects.

You can therefore either use libraries that support PSRAM or make dynamic allocations yourself to create arrays, buffers, etc.

Arduino / ESP32 libraries

To avoid taking the lead with dynamic allocations, some Arduino libraries (compatible with ESP32) support PSRAM. The best known is ArduinoJson which allows you to easily handle JSON files on Arduino and ESP32

ArduinoJson

If you are new to ArduinoJson, read this tutorial 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 downloads a 65 KB JSON file. The JSON contains the results of the search “ESP32 json” on Google.

#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(){
}

Terminal output:

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 also

See how to use the external RAM on the ESP32 for JSON files on the official ArduinoJson site

Arduino framework

The main functions to use are the 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 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, it is enough to modify the values ​​in the examples to use them in your projects.

  • This example shows how to create an integer array of 1000 elements stored in the PSRAM:

int n_elements = 1000;

void setup(){
        Serial.begin(9600);
        //Initialisation
        if(psramInit()){
        Serial.println("\nLa PSRAM est correctement initialisée");
        }else{
        Serial.println("La PSRAM ne fonctionne pas");
        }

        //Création d'un tableau de n_elements
        int tailleInitiale = ESP.getFreePsram();
        Serial.println((String)"Mémoire disponible PSRAM (octets): " + tailleInitiale); // Affiche le nombre d'octets encore disponible dans la PSRAM

        int *tableau = (int *) ps_malloc(n_elements * sizeof(int)); //Créer un tableau d'entiers de n_elements
        tableau[0] = 42;
        tableau[999] = 42; //On accéde aux éléments comme pour un tableau classique

        int taillePSRAM = ESP.getFreePsram();
        Serial.println((String)"Mémoire disponible PSRAM (octets): " + taillePSRAM); // L'espace libre de la mémoire a diminuée
        int tailleTableau = tailleInitiale - taillePSRAM;
        Serial.println((String)"Taille du tableau dans la mémoire en octets : " + tailleTableau);

        //Suppression du tableau
        free(tableau); //La memoire alloué est libérée.
        Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
}

void loop() {
}

Terminal output:

La PSRAM est corectement initialisée
Mémoire disponible PSRAM (octets): 4194252
Mémoire disponible PSRAM (octets): 4190236
Taille du tableau dans la mémoire en octets : 4016
Mémoire disponible PSRAM (octets): 4194252

Note

The size in bytes of the array is 4016 for an array of 1000 integers (int). The type int is stored 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 arrays of other types :

char *tableau1 = (char *) ps_malloc(n_elements * sizeof(char)); //Créer un tableau vide de n_elements caractères
float *tableau = (float *) ps_malloc(n_elements * sizeof(float)); //Créer un tableau de nombres avec virgules
  • We can also create arrays that are filled with zeros with ps_calloc():

int n_elements = 20;
Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
Serial.println("Tableau d'entiers initialisés à 0");
int *tableau = (int *) ps_calloc(n_elements, sizeof(int));
Serial.print("[tableau] : ");
for(int i=0; i!= n_elements;i++){
        Serial.print((String)tableau[0] + " ");
}
Serial.println((String)"\nMémoire disponible PSRAM (octets): " +ESP.getFreePsram());

Terminal output:

Mémoire disponible PSRAM (octets): 4194252
Tableau d'entiers initialisés à 0
[tableau] : 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Mémoire disponible PSRAM (octets): 4194156
  • Here are 2 ways to create 2-dimensional arrays:

int n_lignes = 10;
int n_colonnes = 20;


void setup(){
          Serial.begin(9600);
          //Initialisation
          if(psramInit()){
            Serial.println("\nLa PSRAM est corectement initialisée");
          }else{
            Serial.println("La PSRAM ne fonctionne pas");
          }

          //Création d'un tableau de n_elements
          int tailleInitiale = ESP.getFreePsram();
          Serial.println((String)"Mémoire disponible PSRAM (octets): " + tailleInitiale); // Affiche le nombre d'octets encore disponible dans la PSRAM

          //Création d'un tableau à deux dimensions avec un tableau à une dimension
          int *tableau2D = (int *) ps_malloc(n_lignes * n_colonnes * sizeof(int)); // Création d'un tableau de n_lignes x n_colonnes d'entiers codés sur 4 octets
          //Pour accéder à la case situé à la ligne 5 à la colonne 10
          int ligne_i = 5;
          int colonne_j = 10;
          tableau2D[colonne_j * n_colonnes +ligne_i] = 43;

          //Création d'un tableau rempli de zero à deux dimensions avec un tableau de pointeurs
          int **tableau2Dbis = (int **) ps_calloc(n_lignes, sizeof(int *));
          for (int i =0; i!= n_colonnes;i++){
                tableau2Dbis[i] = (int *) ps_calloc(n_colonnes , sizeof(int)); //Créer un tableau de pointeurs
          }
          //Pour accéder à la case situé à la ligne 5 à la colonne 10
          tableau2Dbis[ligne_i][colonne_j] = 42;


          int taillePSRAM = ESP.getFreePsram();
          Serial.println((String)"Mémoire disponible PSRAM (octets): " + taillePSRAM); // L'espace libre de la mémoire a diminuée
          int tailleTableau2D = tailleInitiale - taillePSRAM;
          Serial.println((String)"Taille du tableau dans la mémoire en octets : " + tailleTableau2D);

          //Suppression du tableau
          free(tableau2D); //La memoire alloué est libérée.
          free(tableau2Dbis);
          Serial.println((String)"Mémoire disponible PSRAM (octets): " +ESP.getFreePsram());
}

void loop() {
}

Functions details

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 initializes it.

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 – Total number of bytes that you want to store in RAM.

Returns

A pointer of the type given in argument which 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 argument which 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 – Memory address of the memory block 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 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 zone and the original memory block will be released 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