Can't allocate SSD1306 OLED on Arduino Uno

I’m using an Arduino Uno for a little project with an SSD1306 OLED (connected to the SCL and SDA pins of the Arduino Uno, the ones above D13). I’m making an interactive menu to display on the OLED and using an SE055 encoder to interact with it. The code runs perfectly fine until a certain point where I try to add one more sub-menu; when I add the sub-menu code and I upload the code, the OLED for some reason doesn’t allocate.

This code runs perfectly:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//Encoder connections to Arduino digital ports
int A = 2; //DT
int B = 4; //CLK
int C = 3; //SW

//For the turnings of the encoder
int previous = 0; 
int current = 0;

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

int menu = 4; //0 = temperature option, 1 = humidity option, 2 = pressure option, 3 = altitude option, 4 = language menu, 5 = main menu; Starts in language menu when turned on.
int language = 0; //0 = english/inglés, 1 = spanish/español
int menuOption = 0; //0 = temperature, 1 = humidity, 2 = pressure, 3 = altitude, 4 = language

//Special characters
char letter1 = 164; //ñ
char letter2 = 162; //ó
char downArrow = 31; //▼
char upArrow = 30; //▲

void setup() {
  pinMode(A, INPUT);
  pinMode(B, INPUT);
  pinMode(C, INPUT_PULLUP);
  
  Serial.begin(9600);

  //pins 2 & 3 have interrupts
  attachInterrupt(digitalPinToInterrupt(A), encoder, LOW);

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.setTextSize(1);      // Normal 1:1 pixel scale, 21 chars fit horizontally, 8 vertically
  display.setTextColor(WHITE); // Draw white text
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  languageMenu();
}

void loop() {
  //When the encoder is turned
  if (current != previous) {
    current = previous;
    switch (menu) {
      case 4:
        languageMenu(); break;
      case 5:
        mainMenu(); break;
    }
  }

  //When the encoder is pressed
  if (digitalRead(C) == LOW) {
    while (digitalRead(C) == LOW) {} //So the encoder has to stop being pressed to advance
    if (menu == 4) {
      menu++;
      mainMenu();
    }
    while (digitalRead(C) == LOW) {} //Maybe redundant, I'll take care of this later
  }
}

//For the interruptor (when the encoder turns)
void encoder() {
  static unsigned long lastInterruption = 0;
  unsigned long interruption = millis();

  if (interruption - lastInterruption > 5) {
    if (digitalRead(B) == HIGH) {
      //Move cursor up
      current--;
      if (menu == 4) language--;
      else if (menu == 5) menuOption--;
    } else {
      //Move cursor down
      current++;
      if (menu == 4) language++;
      else if (menu == 5) menuOption++;
    }
    lastInterruption = interruption;
    
    //for looping around the menus' options
    if (language > 1) language = 0;
    else if (language < 0) language = 1;
    if (menuOption > 4) menuOption = 0;
    else if (menuOption < 0) menuOption = 4;
  }
}

void languageMenu() {
  display.clearDisplay();
  display.setCursor(0, 0);
  if (language == 0) {
    display.print("\n   Choose Language\n   Elija Lenguaje");
    display.print("\n\n  >English  Espa");
    display.print(letter1);
    display.print("ol");
  } else {
    display.print("\n   Choose Language\n   Elija Lenguaje");
    display.print("\n\n   English >Espa");
    display.print(letter1);
    display.print("ol");
  }
  display.display();
}

void mainMenu() {
  display.clearDisplay();
  display.setCursor(0, 0);
  if (language == 0) {
    switch (menuOption) {
      case 0:
        display.print("  > Temperature\n\n");
        display.print("    Humidity\n\n");
        display.print("    Pressure\n\n");
        display.print("    Altitude\n          ");
        display.print(downArrow); break;
      case 1:
        display.print("    Temperature\n\n");
        display.print("  > Humidity\n\n");
        display.print("    Pressure\n\n");
        display.print("    Altitude\n          ");
        display.print(downArrow); break;
      case 2:
        display.print("    Temperature\n\n");
        display.print("    Humidity\n\n");
        display.print("  > Pressure\n\n");
        display.print("    Altitude\n          ");
        display.print(downArrow); break;
      case 3:
        display.print("    Temperature\n\n");
        display.print("    Humidity\n\n");
        display.print("    Pressure\n\n");
        display.print("  > Altitude\n          ");
        display.print(downArrow); break;
      case 4:
        display.print("          ");
        display.print(upArrow);
        display.print("\n  > Language"); break;
    }
  } else {
    switch (menuOption) {
      case 0:
        display.print("  > Temperatura\n\n");
        display.print("    Humedad\n\n");
        display.print("    Presi");
        display.print(letter2);
        display.print("n\n\n");
        display.print("    Altitud\n          ");
        display.print(downArrow); break;
      case 1:
        display.print("    Temperatura\n\n");
        display.print("  > Humedad\n\n");
        display.print("    Presi");
        display.print(letter2);
        display.print("n\n\n");
        display.print("    Altitud\n          ");
        display.print(downArrow); break;
      case 2:
        display.print("    Temperatura\n\n");
        display.print("    Humedad\n\n");
        display.print("  > Presi");
        display.print(letter2);
        display.print("n\n\n");
        display.print("    Altitud\n          ");
        display.print(downArrow); break;
      case 3:
        display.print("    Temperatura\n\n");
        display.print("    Humedad\n\n");
        display.print("    Presi");
        display.print(letter2);
        display.print("n\n\n");
        display.print("  > Altitud\n          ");
        display.print(downArrow); break;
      case 4:
        display.print("          ");
        display.print(upArrow);
        display.print("\n  > Lenguaje"); break;
    }
  }
  display.display();
}

void tempMenu() {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.write("Temperature here");
  display.display();
  display.setTextSize(1);
  delay(10000);
  mainMenu();
}

The display works without problems and I get this after compiling:
Sketch uses 15368 bytes (47%) of program storage space. Maximum is 32256 bytes.
Global variables use 884 bytes (43%) of dynamic memory, leaving 1164 bytes for local variables. Maximum is 2048 bytes.

However, when I add an else if to the “encoder is pressed” part of the loop (which now looks like this:)

  //When the encoder is pressed
  if (digitalRead(C) == LOW) {
    while (digitalRead(C) == LOW) {} //So the encoder has to stop being pressed to advance
    if (menu == 4) {
      menu++;
      mainMenu();
      
    //Added segment
    } else if (menu == 5) {
      menu = menuOption;
      switch (menuOption) {
        case 0:
          tempMenu();
      }
    }
    //End of added segment
    
    while (digitalRead(C) == LOW) {} //Maybe redundant, I'll take care of this later
  }

The OLED doesn’t allocate anymore after running it (I get “SSD1306 allocation failed” in the Serial monitor).

The compiler gives me this:
Sketch uses 15502 bytes (48%) of program storage space. Maximum is 32256 bytes.
Global variables use 902 bytes (44%) of dynamic memory, leaving 1146 bytes for local variables. Maximum is 2048 bytes.

I’m pretty new with the Arduino stuff so I have no idea what is going on. Maybe it has something to do with the memory, even though I’m only using 48% of it? If anybody can see the problem, I’d greatly appreciate the help. Thanks.

The SSD1306 library needs to allocate 1 KiB of memory for the frame buffer (128×64/8 bytes). The Arduino UNO only has 2 KiB of RAM, so if you add too much variables or call too many functions, you use more than 1 KiB, leaving no space for the frame buffer.

Try moving some of your strings to the flash memory instead of RAM, using the F macro: Arduino Playground - Memory. If that's not enough, you'll have to upgrade to a board that has more RAM.

Pieter

That did the trick. Thank you so much.

It’s always elucidating to look at the source code for libraries that you’re using. The first line of the ‘begin()’ method from ‘Adafruit_SSD1306.cpp’ tells the story:

boolean Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr, boolean reset,
  boolean periphBegin) {

  if((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8))))
    return false;

You can see that it returns false if the call to malloc() fails. So, the problem was exactly as @PieterP stated - you ran out of memory. At least this library takes the time to check. Many poorly-written ones just barge ahead assume they got the requested memory and then end up dereference a null pointer.