Can anyone help me reduce my sketches dynamic memory usage, please?

Hi guys,

I have all the code for my project that should work but currently, the sketch uses 150% of my nano's dynamic memory and therefore cannot upload, I would appreciate any help and advice as to how to reduce it. I have attached the code.

sketch_sep01c.ino (8.75 KB)

Where did you get all the libraries ? include them in you post or at least the link to them.

At what point in your development did the memory explode?

Paul

https://www.forward.com.au/pfod/ArduinoProgramming/TimingDelaysInArduino.html#using

That's all of the libraries I have had to install for the code I believe

I don't know exactly when the code started chucking up this error as there were many others needing to be fixed before I got onto it

After commenting out 'U8g2lib' the sketch uses only 64% of program memory (FLASH) and 82% of dynamic memory (RAM).

You can save some RAM space by using:
.print(F("string constant"));
.println(F("string constant"));

in place of:
.print("string constant");
.println("string constant");

The F() macro tricks the compiler into printing the string constant directly from FLASH rather than making a copy in RAM and printing from that.

Looking over the U8g2 library, if you can live with 8x8 fonts, text only, and no graphics, it can be run without a memory buffer. Have a look at the examples under U8g2 > u8x8. That, along with the F() micro for the print statements, gets the memory usage down to 91%, still too close for comfort.

Go through all your variables, and see if you can save some space there. I have not looked at the code closely, but things like minutes and seconds rarely need to be an integer, byte will usually suffice for something that will never go negative and never exceed 60. There are some other variables that you only use once, such as the global integer variable sensorValue, where you store the value read from the analog port, but then only use it once to calculate the voltage on the analog port, which you then store in a float variable, which again is only used once in a comparison for an if statement. (granted, you do this twice, once in setup and once in loop). If you are never going to use the variables anywhere else, putting the analogRead() directly in the if statement would give the same results and save six bytes of memory.

david_2018:
I have not looked at the code closely, but things like minutes and seconds rarely need to be an integer int

I have now got the sketch down to 111% by removing the SD library and related code. I have tried the F() macro trick to further reduce the dynamic memory use but would prefer some graphics and font freedom if possible as the project is reporting information to the screen to be read while rowing. I have attached the new code, please could someone help with the F () macro trick or other memory saving advice.

Tom

sketch_sep01c.ino (7.29 KB)

Please, post your code, so that we can all see it.

eh what is all this 'debug' Serial communication doing in there ?
what are you doing with variable 'm' here ?

  {
    uint32_t m = micros();  //Main timer initialization
    u8g2.clearBuffer();          // clear the internal memory
    u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
    u8g2.drawStr(0, 0, "Initializing..."); // write something to the internal memory
    u8g2.sendBuffer();          // transfer internal memory to the display
    delay(3000);
    secdelay.start(1000); //Start secondary timer delay (1 Second)
    u8g2.clearBuffer();          // clear the internal memory
  }

i suppose the compiler does nothing with it..
You haven't changed a lot of the 16-bit variables into 8-bit ones as was suggested.
And somehow i think you actually should be able to put the font(s) into progmem that could be your biggest saving.

Sadly that also chucked up a fair amount of bugs because I tried to change them all but it meant changing the screen definition and when I did that how the manual said, it wasn't recognised.

Oops yeah that's another testing thing that could probably come out I'll try that now

#include <SPI.h>
#include <SandTimer.h>
#include <millisDelay.h>
#include <Adafruit_GPS.h>   //Libraries
#define GPSSerial Serial
Adafruit_GPS GPS(&GPSSerial);
SandTimer timer1;
#define GPSECHO false
#include <Arduino.h>
#include <U8g2lib.h>
#ifdef U8X8_HAVE_HW_SPI
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);
uint32_t timer = millis();
const int chipSelect = 4;
float ms = 0; //Meters/Second variable
float avems = 0;  //Average M/S variable
float mscounter = 0;  //Speed total (for average)
int integcounter = 0; //Counts half-seconds
float meterstraveled = 0; //Total meters
int splittotal = 0;   //Split total (in seconds)  (Essential to be an integer)
int splitseconds = 0; //Seconds on split display  (Essential to be an integer)
int splitminutes = 0; //Minutes on split display  (Essential to be an integer)
int seconds = 0;
int minutes = 0;
int hours = 0;    //Self-explanatory (for timers)
int splitaveragetotal = 0; //All split seconds added together (for average split)
int splitaverageseconds = 0; //Split average seconds
int splitaverageminutes = 0; //Split average minutes
int splitaverage = 0; //Split average
int splitaveragesecondsdisp = 0; //Display variables, fixes a bug
int splitaverageminutesdisp = 0;
float splitmdisp = 0;
float splitsdisp = 0;  //Display Variables
int tc = 0; //Counter variable
int dp = 0; //Data-point variable
int dpave = 0; //Average of the data-points
int dprec = 0; //Counter variable
float knot = 0;//Speed in knots recorded from GPS
float aveknots = 0;  //Speed in knots averaged over 1/2 second (equal to 1 data point and 5 knot readings)
int knotcounter = 0; //Counter variable
int sensorValue = analogRead(A3); //For the data write switch
float msfm = 0; //M/S for the meters calculation

millisDelay secdelay;   //Timing delay (for timer)



void setup() {
  float voltage = sensorValue * (5.0 / 1023.0);   //Converts A3 reading into a 5v equivalent
  Wire.begin();
  Wire.setClock(400000L);
  Serial.begin(115200);
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PGCMD_ANTENNA);
  delay(1000);    //GPS initialization
  u8g2.begin();
  GPSSerial.println(PMTK_Q_RELEASE);
  int realhours = GPS.hour - 8;
  if (realhours < 0) {
    realhours = realhours + 24; //For SD timestamp (24 hour format)
  }
}

void loop() {

  sensorValue = analogRead(A3);
  float voltage = sensorValue * (5.0 / 1023.0);

  if (secdelay.justFinished()) {
    seconds++;  //Checks if 1 second has passed, updates seconds variable
    secdelay.repeat();
  }
  if (seconds == 60) {
    seconds = 0;  //Checks if 60 seconds have passed, resets seconds, increases minutes
    minutes++;
  }
  if (minutes == 60) {
    minutes = 0;  //Checks if 60 minutes have passed, resets minutes, increases hours
    hours++;
  }
  char c = GPS.read();
  if (GPSECHO)
    if (c) Serial.print(c);
  if (GPS.newNMEAreceived()) {
    Serial.println(GPS.lastNMEA());
    if (!GPS.parse(GPS.lastNMEA()));  //Serial GPS info for debugging
  }

  if (timer > millis()) timer = millis();   //Restarts main timer


  if (timer1.finished()) {
    knot = knot + GPS.speed; knotcounter++;
    timer1.startOver();
  }

  if (millis() - timer > 500) {   //Runs every half-second (responsible for screen-flicker)
    timer = millis(); // Resets timer
    if (GPS.fix) {

      aveknots = knot / knotcounter;
      Serial.println(aveknots);
      ms = (aveknots * 0.5144444); //Converts knots to meters/second
      Serial.println(ms);
      knot = 0;
      tc = 0;
      knotcounter = 0;
      aveknots = 0;
      if (ms <= 0.75) {
        u8g2.clearBuffer();         // clear the internal memory
        u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font
        u8g2.drawStr(0, 10, "Error: IDLE"); // write something to the internal memory
        u8g2.drawStr(1, 10, "Battery: ");
        u8g2.setFont(u8g2_font_ncenB14_tr);
        u8g2.setCursor(1, 80);
        u8g2.drawStr(2, 10, "Meters:");
        u8g2.setFont(u8g2_font_ncenB14_tr);
        u8g2.setCursor(2, 80);
        u8g2.print(meterstraveled);


        u8g2.sendBuffer();          // transfer internal memory to the display






        if (ms > 0.75)
        {

          msfm = ms;
          mscounter = (mscounter + msfm); //Continually adds speed to total (for averages)
          integcounter = (integcounter + 1); //Adds +1 to variable every half second (for averages)
          avems = mscounter / integcounter; //Generates average meters/second (for total)
          meterstraveled = avems * (integcounter / 2); //Calculates total (meters/second divided by seconds)
          meterstraveled = round(meterstraveled);
          splittotal = 500 / (ms); //Generates seconds per 500m
          dp = dp + splittotal;  //Adds data-points for dpave variable
          dprec++;
          if (dprec >= 6) {
            dpave = dp / 6; //Calculates average
            dprec = 0;
            dp = 0; //Resets data-points and counter variable


            splitminutes = dpave / 60; //Generates split minutes from split seconds
            splitseconds = dpave - (splitminutes * 60); //Displays remaining seconds
            splitaveragetotal = splitaveragetotal + splittotal; //All split times (in seconds) added together
            splitaverage = splitaveragetotal / (integcounter); //Averages out split times w/ seconds
            splitaverageminutes = splitaverage / 60; //Calculates split minutes
            splitaverageseconds = splitaverage - splitaverageminutes * 60; //Calculates split seconds
            splitaverageminutesdisp = splitaverageminutes;
            splitaveragesecondsdisp = splitaverageseconds;
            splitmdisp = splitminutes;
            splitsdisp = splitseconds;      //Display variables
            u8g2.clearBuffer();          // clear the internal memory
            u8g2.setFont(u8g2_font_ncenB08_tr); // choose a suitable font --------------------CHANGE THIS FONT TO SOMETHING LARGER
            u8g2.drawStr(0, 10, "M:");
            u8g2.setFont(u8g2_font_ncenB14_tr);

            u8g2.setCursor(0, 25);
            u8g2.setFont(u8g2_font_ncenB14_tr);
            u8g2.print(meterstraveled);
            u8g2.drawStr(1, 10, "S:");

            u8g2.setFont(u8g2_font_ncenB08_tr);
            u8g2.setCursor(1, 25);
            u8g2.print(splitmdisp);
            u8g2.drawStr(1, 50, ":");

            u8g2.setFont(u8g2_font_ncenB08_tr);
            u8g2.setCursor(1, 75);
            u8g2.print(splitsdisp); //adjust pixels to get a good display
            u8g2.sendBuffer();          // transfer internal memory to the display

          }
        }
      }
    }
  }
}

Deva_Rishi:
And somehow i think you actually should be able to put the font(s) into progmem that could be your biggest saving.

Looking over the library, the actual fonts are stored in PROGMEM, and there is a section in the library wiki about saving flash memory space by only specifying portions of a font (such as letters, numbers, etc).

You can save a good deal of memory by changing from a frame buffer to a page buffer, that takes your memory usage down to 67%, but you will need to modify the display commands a bit. I'm not familiar with the library or the display, there is a considerable amount of information on the wiki page.