SSD1306 allocation failed

Hi there,
i wanted to integrate an oled display to my project (ssd1306), however there seems to be a shortage of RAM by the arduino nano. I tested it with the Arduino Mega and had no problem. However, I already made the whole pcb designed for the nano.

Can you help me optimize my code, for using less ram?

/*
Setup:
1x Arduino Nano
1x OLED Display SSD1306 128×64
5x NTC_Thermistor 3950 100k Ohm
4x PWM Fan 12V
*/


// Include the library
#include <FanController.h>
#include <Thermistor.h>
#include <NTC_Thermistor.h>

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

// Fan settings
#define RPM_PIN_1 8
#define RPM_PIN_2 7
#define RPM_PIN_3 4
#define RPM_PIN_4 2

#define PWM_PIN_1 11
#define PWM_PIN_2 10
#define PWM_PIN_3 9
#define PWM_PIN_4 6

unsigned int rpm1 = 0;
unsigned int rpm2 = 0;
unsigned int rpm3 = 0;
unsigned int rpm4 = 0;

const byte dutycycle_min_fan_1 = 0;
const byte dutycycle_max_fan_1 = 100;
const byte dutycycle_min_fan_2 = 0;
const byte dutycycle_max_fan_2 = 100;
const byte dutycycle_min_fan_3 = 0;
const byte dutycycle_max_fan_3 = 100;
const byte dutycycle_min_fan_4 = 0;
const byte dutycycle_max_fan_4 = 100;

byte dutycycle_fan_1 = 0;
byte dutycycle_fan_2 = 0;
byte dutycycle_fan_3 = 0;
byte dutycycle_fan_4 = 0;

const long setfanspeed_intervall = 10000;
unsigned long previousMillis_setfanspeed = 0;

const bool set_active_fan1 = true;   //fan for cooling enclosure
const bool set_active_fan2 = false;  //fan for hba expander
const bool set_active_fan3 = false;  //fan for hba
const bool set_active_fan4 = false;  //fan for 10g nic

// Temperatur settings
#define TEMPSENSOR_PIN_1 A0
#define TEMPSENSOR_PIN_2 A1
#define TEMPSENSOR_PIN_3 A2
#define TEMPSENSOR_PIN_4 A3
#define TEMPSENSOR_PIN_5 A6
#define POTISENSOR_PIN_1 A7  //currently not used!
#define ERRORBUZZER_PIN 5

const bool set_active_tempsensor_1 = true;   //temp sensor ambient, reference, before intake, should never be set false!
const bool set_active_tempsensor_2 = true;   //temp sensor hdd
const bool set_active_tempsensor_3 = false;  //temp sensor hba expander
const bool set_active_tempsensor_4 = false;  //temp sensor hba
const bool set_active_tempsensor_5 = false;  //temp sensor 10g nic

#define REFERENCE_RESISTOR 100000  // Resistance value of the resistor connected in series with the NTC
#define NOMINAL_RESISTANCE 100000  // Resistance of the NTC at nominal temperature
#define NOMINAL_TEMPERATURE 25     // Temperature at which the NTC has the specified resistance
#define B_VALUE 3950               // Beta coefficient (to be found in the NTC data sheet)

Thermistor* thermistor1;
Thermistor* thermistor2;
Thermistor* thermistor3;
Thermistor* thermistor4;
Thermistor* thermistor5;

//IMPORTANT: all temp values are in unit celsius (°C) !
double temp_0 = 0;  //ambient, reference
double temp_1 = 0;  //hdd
double temp_2 = 0;  //hba expander
double temp_3 = 0;  //hba
double temp_4 = 0;  //10g nic

double temp_diff_0_1 = 0;  //= temp_1 - temp_0
double temp_diff_0_2 = 0;  //= temp_2 - temp_0
double temp_diff_0_3 = 0;  //= temp_3 - temp_0
double temp_diff_0_4 = 0;  //= temp_4 - temp_0

const double temp_max_0 = 37;  // max temp for sensor 0 (ambient temp)
const double temp_max_1 = 36;  // max temp for sensor 1 (hdd)
const double temp_max_2 = 29;  // max temp for sensor 2 (hba expander)
const double temp_max_3 = 29;  // max temp for sensor 3 (hba)
const double temp_max_4 = 29;  // max temp for sensor 4 (10g nic)

//double temp_diff_max_1_1 = 0;  //= temp_max_1 - temp_1
//double temp_diff_max_2_2 = 0;  //= temp_max_2 - temp_2
//double temp_diff_max_3_3 = 0;  //= temp_max_3 - temp_3
//double temp_diff_max_4_4 = 0;  //= temp_max_4 - temp_4

const double temp_diff_0_1_max = 5;  // max temp difference between sensor 0 (ambient, reference) and sensor 1 (hdd)
const double temp_diff_0_2_max = 5;  // max temp difference between sensor 0 (ambient, reference) and sensor 2 (hba expander)
const double temp_diff_0_3_max = 5;  // max temp difference between sensor 0 (ambient, reference) and sensor 3 (hba)
const double temp_diff_0_4_max = 5;  // max temp difference between sensor 0 (ambient, reference) and sensor 4 (10g nic)

const double valid_temp_min = 0;
const double valid_temp_max = 100;

//error codes
const byte errorcode_temp_sensor = 10;

const double map_factor = 1000;  //factor for elimintaing rounding errors for small values of double class

// Choose a threshold in milliseconds between readings.
// A smaller value will give more updated results,
// while a higher value will give more accurate and smooth readings
#define SENSOR_THRESHOLD 1000

bool debug = true;           //enables debug mode, when set true
bool buzzer_active = false;  //enables buzzer, when set true

// Initialize library
FanController fan1(RPM_PIN_1, SENSOR_THRESHOLD, PWM_PIN_1);
FanController fan2(RPM_PIN_2, SENSOR_THRESHOLD, PWM_PIN_2);
FanController fan3(RPM_PIN_3, SENSOR_THRESHOLD, PWM_PIN_3);
FanController fan4(RPM_PIN_4, SENSOR_THRESHOLD, PWM_PIN_4);

// Display SETTINGS
#define SCREEN_WIDTH 128     // OLED display width, in pixels
#define SCREEN_HEIGHT 64     // OLED display height, in pixels
#define OLED_RESET -1        // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C  ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const long displayrefreshintervall = 500;
unsigned long previousMillis_displayrefresh = 0;
String display_status_message = "NaN";

void getfanspeed(void) {
  // Call fan.getSpeed() to get fan RPM
  if (set_active_fan1) {
    rpm1 = fan1.getSpeed();
  }
  if (set_active_fan2) {
    rpm2 = fan2.getSpeed();
  }
  if (set_active_fan3) {
    rpm3 = fan3.getSpeed();
  }
  if (set_active_fan4) {
    rpm4 = fan4.getSpeed();
  }

  if (debug) {
    if (set_active_fan1) {
      Serial.print("Current speed fan1: ");
      Serial.print(rpm1);
      Serial.println(" RPM");
    }
    if (set_active_fan2) {
      Serial.print("Current speed fan2: ");
      Serial.print(rpm2);
      Serial.println(" RPM");
    }
    if (set_active_fan3) {
      Serial.print("Current speed fan3: ");
      Serial.print(rpm3);
      Serial.println(" RPM");
    }
    if (set_active_fan4) {
      Serial.print("Current speed fan4: ");
      Serial.print(rpm4);
      Serial.println(" RPM");
    }
  }
}
void gettemp(void) {
  for (int i = 0; i <= 999; i++) {
    if (set_active_tempsensor_1) {
      temp_0 = temp_0 + thermistor1->readCelsius(); 
    }
    if (set_active_tempsensor_2) {
      temp_1 = temp_1 + thermistor2->readCelsius(); 
    }
    if (set_active_tempsensor_3) {
      temp_2 = temp_2 + thermistor3->readCelsius();  
    }
    if (set_active_tempsensor_4) {
      temp_3 = temp_3 + thermistor4->readCelsius();  
    }
    if (set_active_tempsensor_5) {
      temp_4 = temp_4 + thermistor5->readCelsius(); 
    }
  }

  if (set_active_tempsensor_1) {
    temp_0 = temp_0 / 1000;
  }
  if (set_active_tempsensor_2) {
    temp_1 = temp_1 / 1000;
    //temp_diff_max_1_1 = abs(temp_max_1 - temp_1);
  }
  if (set_active_tempsensor_3) {
    temp_2 = temp_2 / 1000;
    //temp_diff_max_2_2 = abs(temp_max_2 - temp_2);
  }
  if (set_active_tempsensor_4) {
    temp_3 = temp_3 / 1000;
    //temp_diff_max_3_3 = abs(temp_max_3 - temp_3);
  }
  if (set_active_tempsensor_5) {
    temp_4 = temp_4 / 1000;
    //temp_diff_max_4_4 = abs(temp_max_4 - temp_4);
  }

  if (set_active_tempsensor_1 && set_active_tempsensor_2) {
    temp_diff_0_1 = abs(temp_1 - temp_0);
  }
  if (set_active_tempsensor_1 && set_active_tempsensor_3) {
    temp_diff_0_1 = abs(temp_2 - temp_0);
  }
  if (set_active_tempsensor_1 && set_active_tempsensor_4) {
    temp_diff_0_1 = abs(temp_3 - temp_0);
  }
  if (set_active_tempsensor_1 && set_active_tempsensor_5) {
    temp_diff_0_1 = abs(temp_4 - temp_0);
  }
  if (debug) {
    if (set_active_tempsensor_1) {
      Serial.print("Temperatur1: ");
      Serial.print(temp_0);
      Serial.println(" °C ");
    }
    if (set_active_tempsensor_2) {
      Serial.print("Temperatur2: ");
      Serial.print(temp_1);
      Serial.println(" °C ");
    }
    if (set_active_tempsensor_3) {
      Serial.print("Temperatur3: ");
      Serial.print(temp_2);
      Serial.println(" °C ");
    }
    if (set_active_tempsensor_4) {
      Serial.print("Temperatur4: ");
      Serial.print(temp_3);
      Serial.println(" °C ");
    }
    if (set_active_tempsensor_5) {
      Serial.print("Temperatur5: ");
      Serial.print(temp_4);
      Serial.println(" °C");
    }

    delay(100);
  }
}
void setfanspeed(void) {
  if (validatetempsensors()) {  //check, if each temp sensor is within valid_temp_min and valid_temp_max (only when set_active_tempsensor_X = true)
    unsigned long currentMillis = millis();
    display_status_message = "normal operation";
    if (currentMillis - previousMillis_setfanspeed >= setfanspeed_intervall) {  // check whether the last setfanspeed() run was more than setfanspeed_intervall ago
      previousMillis_setfanspeed = currentMillis;
      // fan1
      if (set_active_fan1) {
        if (temp_1 > temp_max_1 || temp_diff_0_1 > temp_diff_0_1_max) {  //check, if the temp is out of limits
          display_status_message = "hdd temp critical!";
          fan1.setDutyCycle(dutycycle_max_fan_1);
          if (debug) {
            Serial.print("fan1 duty cycle set at max (");
            Serial.print(dutycycle_max_fan_1);
            Serial.println(")");
          }
        } else {
          double temp_factored_0 = map_factor * temp_0;
          double temp_factored_1 = map_factor * temp_1;
          double temp_diff_factored_0_1 = map_factor * temp_diff_0_1;
          double temp_diff_factored_0_1_max = map_factor * temp_diff_0_1_max;
          double temp_factored_max_1 = map_factor * temp_max_1;
          long dutycycle_fan_1 = 0;

          if (debug) {
            Serial.print("temp_0 = ");
            Serial.println(temp_0);
            Serial.print("temp_1 = ");
            Serial.println(temp_1);
            Serial.print("temp_diff_0_1 = ");
            Serial.println(temp_diff_0_1);
            Serial.print("temp_diff_0_1_max = ");
            Serial.println(temp_diff_0_1_max);
            Serial.print("temp_max_1 = ");
            Serial.println(temp_max_1);
            delay(10000);
          }

          if (temp_factored_max_1 - temp_factored_1 >= temp_diff_factored_0_1_max - temp_diff_factored_0_1) {
            dutycycle_fan_1 = map(temp_diff_factored_0_1, 0, temp_diff_factored_0_1_max, dutycycle_min_fan_1, dutycycle_max_fan_1);
            if (debug) {
              Serial.println("A");
              Serial.println("(temp_factored_max_1 - temp_factored_1 >= temp_diff_factored_0_1_max - temp_diff_factored_0_1");
            }

          } else {
            dutycycle_fan_1 = map(temp_factored_1, temp_factored_0, temp_factored_max_1, dutycycle_min_fan_1, dutycycle_max_fan_1);
            if (debug) {
              Serial.println("B");
              Serial.println("(temp_factored_max_1 - temp_factored_1 < temp_diff_factored_0_1_max - temp_diff_factored_0_1");
            }
          }
          fan1.setDutyCycle(dutycycle_fan_1);
          if (debug) {
            Serial.print("fan1 duty cycle set at ");
            Serial.print(dutycycle_fan_1);
            Serial.println(" ");
          }

          /*
          if (temp_diff_0_1 >= temp_diff_max_1_1) {
            // control fan1 based on the temperature difference between (temp_max_1 and temp_1 = temp_diff_max_1_1)
             // ################# HIER WEITER #################
             
            byte dutycycle_fan_1 = map(temp_diff_0_1, 0, temp_diff_0_1_max, dutycycle_min_fan_1, dutycycle_max_fan_1);
            fan1.setDutyCycle(dutycycle_fan_1);
            if (debug) {
              Serial.print("fan1 duty cycle set at ");
              Serial.print(dutycycle_fan_1);
              Serial.println(" ");
            }
          } else {
            byte dutycycle_fan_1 = map(temp_diff_0_1, 0, temp_diff_0_1_max, dutycycle_min_fan_1, dutycycle_max_fan_1);
            fan1.setDutyCycle(dutycycle_fan_1);
            if (debug) {
              Serial.print("fan1 duty cycle set at ");
              Serial.print(dutycycle_fan_1);
              Serial.println(" ");
            }
          }*/
        }
      }
    }
  } else {
    display_status_message = "temp sensor error";
    setfanspeedmax();
  }
}
void setfanspeedatboot(void) {
  display_status_message = "booting";
  if (set_active_fan1) {
    fan1.setDutyCycle(dutycycle_max_fan_1);
  }
  if (set_active_fan2) {
    fan2.setDutyCycle(dutycycle_max_fan_2);
  }
  if (set_active_fan3) {
    fan3.setDutyCycle(dutycycle_max_fan_3);
  }
  if (set_active_fan4) {
    fan4.setDutyCycle(dutycycle_max_fan_4);
  }
  if (debug) {
    Serial.println("all fans at max speed");
  }

  delay(500);
  if (set_active_fan1) {
    fan1.setDutyCycle(dutycycle_min_fan_1);
  }
  if (set_active_fan2) {
    fan2.setDutyCycle(dutycycle_min_fan_2);
  }
  if (set_active_fan3) {
    fan3.setDutyCycle(dutycycle_min_fan_3);
  }
  if (set_active_fan4) {
    fan4.setDutyCycle(dutycycle_min_fan_4);
  }
  if (debug) {
    Serial.println("all fans at min speed");
  }
}
void setfanspeedmax(void) {
  if (set_active_fan1) {
    fan1.setDutyCycle(dutycycle_max_fan_1);
  }
  if (set_active_fan2) {
    fan2.setDutyCycle(dutycycle_max_fan_2);
  }
  if (set_active_fan3) {
    fan3.setDutyCycle(dutycycle_max_fan_3);
  }
  if (set_active_fan4) {
    fan4.setDutyCycle(dutycycle_max_fan_4);
  }
  if (debug) {
    Serial.println("all fans at max speed");
  }
}
bool validatetempsensors(void) {
  if (set_active_tempsensor_1) {
    if (temp_0 > valid_temp_min && temp_0 < temp_max_0) {  //check sensor 0; attention: max temp for temp_0 is temp_max_0 not valid_temp_max (due to uninfluenceable ambient temp by fans)!
    } else {
      if (debug) {
        Serial.println("Sensortemp 0 critical!");
      }
      errorbuzzer(errorcode_temp_sensor);
      return false;
    }
  }
  if (set_active_tempsensor_2) {
    if (temp_1 > valid_temp_min && temp_1 < valid_temp_max) {  //check sensor 1
    } else {
      if (debug) {
        Serial.println("Sensortemp 1 critical!");
      }
      errorbuzzer(errorcode_temp_sensor);
      return false;
    }
  }
  if (set_active_tempsensor_3) {
    if (temp_2 > valid_temp_min && temp_2 < valid_temp_max) {  //check sensor 2
    } else {
      if (debug) {
        Serial.println("Sensortemp 2 critical!");
      }
      errorbuzzer(errorcode_temp_sensor);
      return false;
    }
  }
  if (set_active_tempsensor_4) {
    if (temp_3 > valid_temp_min && temp_3 < valid_temp_max) {  //check sensor 3
    } else {
      if (debug) {
        Serial.println("Sensortemp 3 critical!");
      }
      errorbuzzer(errorcode_temp_sensor);
      return false;
    }
  }
  if (set_active_tempsensor_5) {
    if (temp_4 > valid_temp_min && temp_4 < valid_temp_max) {  //check sensor 4
    } else {
      if (debug) {
        Serial.println("Sensortemp 4 critical!");
      }
      errorbuzzer(errorcode_temp_sensor);
      return false;
    }
  }
  if (debug) {
    Serial.println("All Sensortemps are uncritical");
  }
  return true;
}
void errorbuzzer(byte errorcode) {
  if (buzzer_active) {
    switch (errorcode) {
      case errorcode_temp_sensor:  // validatetempsensors = false
        tone(ERRORBUZZER_PIN, 5); 
        delay(50);                
        noTone(ERRORBUZZER_PIN);  
        break;
      default:
        // Statement(s)
        break;  
    }
  }
}
void initiate_display() {
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;  // Don't proceed, loop forever
  }
  display.display();
  delay(20); 
  // Clear the buffer
  display.clearDisplay();  
  delay(20);
  display.setTextColor(SSD1306_WHITE);  // Draw white text
  display.cp437(true);
  display.setTextSize(1);
}
void update_display() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis_displayrefresh >= displayrefreshintervall) {  // check whether the last setfanspeed() run was more than setfanspeed_intervall ago
    previousMillis_displayrefresh = currentMillis;
    display.clearDisplay();
    display.setCursor(0, 0);
    display.print("Temp    ");
    display.write(0xF8);
    display.println("C");
    display.print("Ambient ");
    display.println(temp_0);
    display.print("HDD     ");
    display.println(temp_1);
    display.print("HBA Ex. ");
    display.println(temp_2);
    display.print("HBA     ");
    display.println(temp_3);
    display.print("10g NIC ");
    display.println(temp_4);

    display.setCursor(62, 0);
    display.print("Cycle %");
    display.println(" RPM");
    display.setCursor(62, 16);

    display.print("Fan1 ");
    display.print(dutycycle_fan_1);
    display.print(" ");
    display.println(rpm1);

    display.setCursor(62, 24);
    display.print("Fan2 ");
    display.print(dutycycle_fan_2);
    display.print(" ");
    display.println(rpm2);

    display.setCursor(62, 32);
    display.print("Fan3 ");
    display.print(dutycycle_fan_3);
    display.print(" ");
    display.println(rpm3);

    display.setCursor(62, 40);
    display.print("Fan4 ");
    display.print(dutycycle_fan_4);
    display.print(" ");
    display.println(rpm4);
    display.println("Status: ");
    display.print(display_status_message);
    display.display();
  }
}

void setup(void) {
  // start serial port
  Serial.begin(9600);
  initiate_display();

  pinMode(TEMPSENSOR_PIN_1, INPUT_PULLUP);
  pinMode(TEMPSENSOR_PIN_2, INPUT_PULLUP);
  pinMode(TEMPSENSOR_PIN_3, INPUT_PULLUP);
  pinMode(TEMPSENSOR_PIN_4, INPUT_PULLUP);
  pinMode(TEMPSENSOR_PIN_5, INPUT_PULLUP);

  // Start up the library
  fan1.begin();
  fan2.begin();
  fan3.begin();
  fan4.begin();
  
  //initiate temp sensors
  thermistor1 = new NTC_Thermistor(TEMPSENSOR_PIN_1, REFERENCE_RESISTOR, NOMINAL_RESISTANCE, NOMINAL_TEMPERATURE, B_VALUE);
  thermistor2 = new NTC_Thermistor(TEMPSENSOR_PIN_2, REFERENCE_RESISTOR, NOMINAL_RESISTANCE, NOMINAL_TEMPERATURE, B_VALUE);
  thermistor3 = new NTC_Thermistor(TEMPSENSOR_PIN_3, REFERENCE_RESISTOR, NOMINAL_RESISTANCE, NOMINAL_TEMPERATURE, B_VALUE);
  thermistor4 = new NTC_Thermistor(TEMPSENSOR_PIN_4, REFERENCE_RESISTOR, NOMINAL_RESISTANCE, NOMINAL_TEMPERATURE, B_VALUE);
  thermistor5 = new NTC_Thermistor(TEMPSENSOR_PIN_5, REFERENCE_RESISTOR, NOMINAL_RESISTANCE, NOMINAL_TEMPERATURE, B_VALUE);

  setfanspeedatboot();
}
void loop(void) {
  gettemp();
  getfanspeed();
  setfanspeed();
  update_display();
}

Switch your library to SS_OLED by Larry Bank - I used it with my Nano successfully - but it will reduce your font flexibility. Load up his example, and read through it.

1 Like

The 128x64 display uses half of the Nano's RAM memory for the graphics display buffer. If you are not using graphics, this text-only library will work.

Save RAM by using the F macro to put print character strings in program memory:
replace strings like this:
Serial.print("Current speed fan1: ");
with
Serial.print(F("Current speed fan1: "));

1 Like

You can also try the U8g2 library with a page buffer, that can take considerably less ram than a full buffer.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.