Saving Memory on large project

Hello There,

I am currently working on a rather complex project involving a real-time-clock, a sd-card-module and a OLED display on my Arduino UNO.

I am more and more running into memory space problems due to complexity. When I add new functions at certain places the Arduino resets itself due to an overflow of the dynamic memory i guess.

Switching to a mega works but it kinda feels like the lazy solution and is not nice due to the larger board.

I'll copy all parts of the code below, which are related to the display module. Any ideas how to make it more "memory-efficient"?

I'm thankful for any advice. Best regards!

#include "U8glib.h"
U8GLIB_SH1106_128X64 u8g(9, A3, A0, A1, A2);  // SCL=A4, SDA=A3, CS=A0, DC=A1, Reset=A2

void setup () {
}

void loop() {
  //
  //Stuff happening which changes Display State
  //

  digitalWrite(A0,HIGH);
  //delay(100);
  u8g.firstPage();  
  do {
    u8g.setFont(u8g_font_9x15B);  //10 px heiht
    u8g.drawStr(10, 10, "Smartpot 1.0");  
    
    u8g.setFont(u8g_font_8x13); //Optimales copyright Zeichen
    u8g.setPrintPos(30, 24);
    u8g.print(char(169));
    
    u8g.setFont(u8g_font_5x8);
    u8g.setPrintPos(40, 22);
    u8g.print("MY Name");
    u8g.setPrintPos(25, 30);
    u8g.print("www.mydomain.tbd");

    //Page 0
    if(iDisplayState == 0){
      u8g.setPrintPos(0, 40);
      u8g.print("Pflanzen: ");
      u8g.print(iPlantCount);
      if(iSlaveDataIDs[0] != 0){
        u8g.setPrintPos(0, 48);
        u8g.print("ID 1: ");
        u8g.print(iSlaveDataMoisture[0]);
      }
      if(iSlaveDataIDs[1] != 0){
        u8g.setPrintPos(50, 48);
        u8g.print("ID 2: ");
        u8g.print(iSlaveDataMoisture[1]);
      }
      if(iSlaveDataIDs[2] != 0){
        u8g.setPrintPos(0, 56);
        u8g.print("ID 3: ");
        u8g.print(iSlaveDataMoisture[2]);
      }
      if(iSlaveDataIDs[3] != 0){
        u8g.setPrintPos(50, 56);
        u8g.print("ID 4: ");
        u8g.print(iSlaveDataMoisture[3]);
      }
      if(iSlaveDataIDs[4] != 0){
        u8g.setPrintPos(0, 64);
        u8g.print("ID 5: ");
        u8g.print(iSlaveDataMoisture[4]);
      }
      if(iSlaveDataIDs[5] != 0){
        u8g.setPrintPos(50, 64);
        u8g.print("ID 6: ");
        u8g.print(iSlaveDataMoisture[5]);
      }
    }

    ///////////////////////////////////
    /////// Optionen Bewaesserung /////
    ///////////////////////////////////
    u8g.setFont(u8g_font_5x8);
    if(iDisplayState == 3){
      u8g.setPrintPos(0, 48);
      u8g.print("Bewaesserung");
      u8g.setPrintPos(0, 56);
      u8g.print("starten");
    }
    else if(iDisplayState == 4){
      u8g.setPrintPos(0, 48);
      u8g.print("Bewaesserung");
      u8g.setPrintPos(0, 56);
      u8g.print("wirklich");
      u8g.setPrintPos(0, 64);
      u8g.print("starten?");
      
    }
    else if(iDisplayState == 5){
      u8g.setPrintPos(0, 48);
      u8g.print("Bewaesserung");
      u8g.setPrintPos(0, 56);
      u8g.print("gestartet");
      if(bFillingInProcess == false){
        serial_value_command(0, iDataTypeState, iDataValueStateBeginFilling); 
        bFillingInProcess = true;
        delay(500);
      }
    }
    
    ///////////////////////////////////
    //////// Optionen Licht An ////////
    ///////////////////////////////////
    if(iDisplayState == 6){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("anschalten");
    }
    else if(iDisplayState == 7){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("wirklich");
      u8g.setPrintPos(0, 64);
      u8g.print("anschalten?");
      
    }
    else if(iDisplayState == 8){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("angeschaltet");
    }
    
    ///////////////////////////////////
    /////// Optionen Licht Aus ////////
    ///////////////////////////////////
    if(iDisplayState == 9){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("ausschalten");
    }
    else if(iDisplayState == 10){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("wirklich");
      u8g.setPrintPos(0, 64);
      u8g.print("ausschalten?");
      
    }
    else if(iDisplayState == 11){
      u8g.setPrintPos(0, 48);
      u8g.print("Lichter");
      u8g.setPrintPos(0, 56);
      u8g.print("ausgeschaltet");
    }
    
    u8g.setFont(u8g_font_5x8);
    u8g.setPrintPos(100, 56);
    u8g.print(clock_get_month());
    u8g.print("/");
    u8g.print(clock_get_day());
    u8g.setPrintPos(100, 64);
    u8g.print(clock_get_hour());
    u8g.print(":");
    u8g.print(clock_get_minute());

    //Print a single pixel if hold is true
    if(bIsHold == true){
      u8g.drawPixel(0, 0);
    }
    
  } while( u8g.nextPage() );
  digitalWrite(A0,LOW);
}

Hello There,

I am currently working on a rather complex project involving a real-time-clock, a sd-card-module and a OLED display on my Arduino UNO.

I am more and more running into memory space problems due to complexity. When I add new functions at certain places the Arduino resets itself due to an overflow of the dynamic memory i guess.

Switching to a mega works but it kinda feels like the lazy solution and is not nice due to the larger board.

I’ll copy all parts of the code below, which are related to the sd-card module. Any ideas how to make it more “memory-efficient”?

I’m thankful for any advice.
Best regards!

//SD card module
#include <SPI.h>
#include <SD.h>
File myFile;
#define chipSelect (uint8_t)10
#define pinLEDRed (uint8_t)2

uint8_t iSlaveDataIDs[10];
uint16_t iSlaveDataMoisture[10];

void setup () {
  pinMode(chipSelect, OUTPUT); // SD Card CS
  pinMode(pinLEDRed,OUTPUT);

  if (!SD.begin(chipSelect)) {
    digitalWrite(pinLEDRed,HIGH);
    while (1);
  }
}
void loop(){
  //
  // Gather Some Data
  //

  sd_write_values();
}

boolean sd_write_values(){

  //In the first section I create a unique filename for the new txt-file

  char sFilename[20]="";
  uint8_t iNewIndex = 0;
 
  //Next two lines get the year from the clock module and convert it into a single unique char (some letter)
  uint8_t iYear = clock_get_year();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iYear);
  iNewIndex++;
    
  //Next two lines get the month from the clock module and convert it into a single unique char (some letter)
  uint8_t iMonth = clock_get_month();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iMonth);
  iNewIndex++;

  //Same as above
  uint8_t iDay  = clock_get_day();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iDay);
  iNewIndex++;
  
  //Same as above
  uint8_t iHour = clock_get_hour();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iHour);
  iNewIndex++;

  //Same as above
  uint8_t iMinute = clock_get_minute();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iMinute);
  iNewIndex++;
  
  //Same as above
  uint8_t iSecond = clock_get_second();
  sFilename[iNewIndex] = sd_convert_int_to_ascii_code(iSecond);
  iNewIndex++;

  sFilename[iNewIndex] = '.';
  iNewIndex++;

  sFilename[iNewIndex] = 't';
  iNewIndex++;

  sFilename[iNewIndex] = 'x';
  iNewIndex++;

  sFilename[iNewIndex] = 't';
  iNewIndex++;


  
  //Re-Initializing SD-Card to reset possible error state
  if (!SD.begin(chipSelect)) {
    return false;
  }
  delay(1000);

  myFile = SD.open(sFilename, FILE_WRITE);
  if (myFile) {
    myFile.println(1);

    //ProectID
    myFile.println(iProjectID);
    
    //Date and Time
    myFile.println(iYear);
    myFile.println(iMonth);
    myFile.println(iDay);
    myFile.println(iHour);
    myFile.println(iMinute);
    myFile.println(iSecond);
    
    //Temperature
    myFile.println(rtc.getTemperature());
    
    //Moisture Values (arrays filled in main loop)
    for(int k = 0; k<sizeof(iSlaveDataIDs); k++){
      if(iSlaveDataIDs[k] > 0){
        myFile.println(iSlaveDataMoisture[k]);
      }
    }
    
    // close the file:
    myFile.close();
    delay(1000);
    return true;
  } 
  else {
    // if the file didn't open:
    return false;
  }
}

Which type of memory is getting low? RAM? or FLASH?

You could use the Arduino proprietary, AVR specific macros for strings to ensure they are not copied to RAM. i.e. put your string constants inside the F() macro.

F("Smartpot 1.0")

etc...

I would seriously consider switching to a more powerful platform like one of the ESP modules like the ESP8266. Smaller, and much faster with lots more resources like flash and RAM.

The ESP parts would give you lots of room to grow, since you have WiFi capability which means you can use a web browser for configuration or even NTP for time. You can even have a very accurate clock without an RTC. Also has some nice over the air update and source level debugging capabilities built in.

--- bill

Given how cheap this kind of hardware has become, an upgrade is an easy decision. There are plenty of alternatives that aren't as big as a mega. Consider a Nano every or a Teensy perhaps.

You may be able to claw back some memory by switching to a text only driver for the OLED.

boredengineer: Switching to a mega works but it kinda feels like the lazy solution

No it isn't, it is simply a matter of using the right tool for the job. There is nothing "big" about your project, it is just a typical data-logger - and beyond the capabilities of a Uno.

You may get some respite by reading reply #4 again, and this may be useful in that arena, but the sooner you bow to the inevitable the better. You can spend only so much time banging your head against the wall as you try to fit a quart into a pint pot and, the way things are with Arduino, the quarts are inclined to get bigger as time goes by.

If the size of the Mega scares you, there is the Pro Mega in small format, or you may consider the NodeMCU, which brings its own benefits of speed, memory and WiFi - and is smaller than a Uno. Another board I have but never used is the Heltec ESP32. If its on-board OLED is the size you want, it is definitely worth a look. It is also much smaller than a Uno. Needless to say, none of these alternatives will accept any Uno/Mega boards you already may have.....

When I write and debug code I use the processor like my target with the most available memory. The final process of programming is to compact and cleaning up the code, optimizing it, and to be sure it has no extra features (bugs). Is this a complex program, that is a subjective decision, not an objective one. I do not know of any dynamic memory in the UNO, are you referring to RAM?