Reduce dynamic memory

Hello everyone,
I made a code that uses a lot of dynamic memory. The problem is that when I add code, I have this message "Global variables use 2341 bytes (114%) of dynamic memory". Do you have any advice to reduce this dynamic memory? Thank you :slight_smile:

/*The circuit:
 * RX is digital pin 10 (connect to TX of other device)
 * TX is digital pin 11 (connect to RX of other device)
*/
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <SD.h> 
#include <MS5611.h> //https://github.com/gronat/MS5611


File fichierSD;                                                                               
int data=0;
 
SoftwareSerial mySerial(9, 10);
Adafruit_GPS GPS(&mySerial);
 
 
#define GPSECHO  false
 
boolean usingInterrupt = false;
boolean QNHCaptured = false;

void useInterrupt(boolean);
#define MOVAVG_SIZE 32
MS5611 baro;
int32_t pressure; // in Pascal


int32_t QNH; //Calcul du QNH en fonction de l'alti du GPS
int32_t temperature;
float movavg_buff[MOVAVG_SIZE];
int movavg_i=0;
 


void setup()
{
  Serial.begin(115200);
  SD.begin(4);                                                                         

 
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  GPS.sendCommand(PGCMD_ANTENNA);
 
  useInterrupt(true);
 // Start barometer
  baro = MS5611();
  baro.begin();
//   populate movavg_buff before starting loop
  for(int i=0; i<MOVAVG_SIZE; i++) {
    movavg_buff[i] = baro.getPressure();
  }
  
  delay(1000);
}
 
SIGNAL(TIMER0_COMPA_vect)
{
  char c = GPS.read();
#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;
#endif
}
 
void useInterrupt(boolean v)
{
  if (v)
  {
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  }
  else
  {
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}
 
uint32_t timer = millis();
 
void loop()
{
  

  if (GPS.newNMEAreceived())
  {
 
    if (!GPS.parse(GPS.lastNMEA()))
      return;
  }

  // Read pressure (Pascal)
  pressure = (baro.getPressure());
  pushAvg(pressure);
  pressure=getAvg(movavg_buff, MOVAVG_SIZE);
  
  // Read temperature (°C x100)
  temperature = (baro.getTemperature());
  
  if (GPS.altitude!=0 && QNHCaptured == false){  
    QNH = sea_pressure(pressure,GPS.altitude);
    QNHCaptured = true;  
  }
    
  if (GPS.altitude!=0) {
   Serial.println(getAltitude(pressure,temperature,QNH));       
  }
  else{
    Serial.println("WAIT...");  
  }
    
}
//################    FONCTIONS    #################################################

float getAltitude(float press, float temp, float sea_pressure) {
  return ((pow((sea_pressure / press), 1/5.257) - 1.0) * (temp*0.01 + 273.15)) / 0.0065;
}

// Enregistre un echantillon de pression
void pushAvg(float val) {
  movavg_buff[movavg_i] = val;
  movavg_i = (movavg_i + 1) % MOVAVG_SIZE;
}

// Retourne la moyenne des echantillons de pression
float getAvg(float * buff, int size) {
  float sum = 0.0;
  for(int i=0; i<size; i++) {
    sum += buff[i];
  }
  return sum / size;
}
// Calcul le QNH
float sea_pressure (float press, float GPS_alti){
  return (press/pow(1-(GPS_alti/44330.0),5.255));}

Use the F() macro as much as possible.
Is that all your code?

(Is your username your postcode? I'm in Pau in a couple of weeks - any recommendations?)

AWOL:
Use the F() macro as much as possible.
Is that all your code?

(Is your username your postcode? I'm in Pau in a couple of weeks - any recommendations?)

The code I wanted to test is below. I sent you a private message about Pau :slight_smile:

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_GPS.h>
#include <Adafruit_SSD1306.h>
#include <SoftwareSerial.h>
#include <Button.h>
#include <EEPROM.h>
#include "EEPROMAnything.h"
#include <UTFT.h>
#include <MS5611.h>
#include <SD.h> 




#define OLED_RESET 5
Adafruit_SSD1306 display(OLED_RESET);
 
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2

#define BUTTON1_PIN     7

#define HPA 0
#define METER 1
#define DEG 2
#define MVOLT 3



extern uint8_t Font24x40[];
extern uint8_t Symbol[];
extern uint8_t Splash[];
extern uint8_t Battery[];

SoftwareSerial mySerial(9, 10);
Adafruit_GPS GPS(&mySerial);
#define GPSECHO  false

Button button1 = Button(BUTTON1_PIN,BUTTON_PULLDOWN);
int value, etat = 0;
float denivele;
float altiMin = 9999.0;
float altiMax = 0.0;
float lastValue = 0.0;
long lastVcc, vcc = 0;


int screen = 0; // numero d'ecran
#define NB_SCREENS 6
String debugMsg;
// init QNH
float altitude;
boolean usingInterrupt = false;
boolean QNHCaptured = false;

void useInterrupt(boolean);
#define MOVAVG_SIZE 32
MS5611 baro;
int32_t pressure; // in Pascal

int32_t QNH; //Calcul du QNH en fonction de l'alti du GPS
int32_t temperature;
float movavg_buff[MOVAVG_SIZE];
int movavg_i=0;

/* ------------------------------------ setup ------------------------------------------ */
void setup()   {                

  Serial.begin(9600);
  display.begin(SSD1306_SWITCHCAPVCC);
  
  
  delay(100);
  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
  GPS.sendCommand(PGCMD_ANTENNA);
 useInterrupt(true);
 // Start barometer
  baro = MS5611();
  baro.begin();
 for(int i=0; i<MOVAVG_SIZE; i++) {
    movavg_buff[i] = baro.getPressure();
  }
  display.clearDisplay(); 
  display.drawBitmap(0, 0,  Splash, 128, 64, 1);
  display.display();
  delay(1000);
  display.clearDisplay(); 
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);

  

  button1.isPressed();
  screen = 1;
 
}
SIGNAL(TIMER0_COMPA_vect)
{
  char c = GPS.read();
#ifdef UDR0
  if (GPSECHO)
    if (c) UDR0 = c;
#endif
}
 
void useInterrupt(boolean v)
{
  if (v)
  {
    OCR0A = 0xAF;
    TIMSK0 |= _BV(OCIE0A);
    usingInterrupt = true;
  }
  else
  {
    TIMSK0 &= ~_BV(OCIE0A);
    usingInterrupt = false;
  }
}
 
uint32_t timer = millis();
 
/* ------------------------------------ loop ------------------------------------------ */
void loop() {
  char status;
  long vcc;


  button1.isPressed();

denivele=altiMax-altiMin;
//pression=pressure.readPressure()*0.01;
  // Read pressure (Pascal)
  pressure = (baro.getPressure());
  pushAvg(pressure);
  pressure=getAvg(movavg_buff, MOVAVG_SIZE);
//print(pression);
  temperature = (baro.getTemperature());


//pression = getAvg(samplesBuffer, MAX_SAMPLES);

if (GPS.altitude!=0 && QNHCaptured == false){  
    QNH = sea_pressure(pressure,GPS.altitude);
    QNHCaptured = true;  // set QNHCaptured to true so the if statement will be false on next loop and QNH won't be updated
  }
    
  if (GPS.altitude!=0) {
   altitude=getAltitude(pressure,temperature,QNH); 
   setAltiMinMax();
      
  }
  else{
    Serial.println("WAIT...");  
  }




  if(etat==0){


    switch (screen) {
    case 1: // Altitude
      if (lastValue != altitude) {
        showScreen("ALTITUDE", altitude, METER);
        lastValue = altitude;
      }  
      break;

    case 2: // Altitude Max
      if (lastValue != altiMax) {
        showScreen("ALTITUDE MAX", altiMax, METER);
        lastValue = altiMax;
      }  
      break;

    case 3:  // Altitude Min
      if (lastValue != altiMin) {
        showScreen("ALTITUDE MIN", altiMin, METER);
        lastValue = altiMin;
      }  
      break;

    case 4:  // Pression
      if (lastValue != pressure) {
        showScreen("PRESSION", pressure, HPA);
        lastValue = pressure;
      }  
      break;

    case 5:  // Temperature
      if (lastValue != temperature) {
        showScreen("TEMPERATURE", temperature, DEG);
        lastValue = temperature;
      }  
      break;
      
      
     case 6:  // Dénivelé
      if (lastValue != denivele) {
        showScreen("DENIVELE", denivele, METER);
        lastValue = denivele;
      }  
      break;
    }
}

  

  delay(50);
}
/* -------------------- fonctions --------------------  */
float getAltitude(float press, float temp, float sea_pressure) {
  return ((pow((sea_pressure / press), 1/5.257) - 1.0) * (temp*0.01 + 273.15)) / 0.0065;
}
// Affiche les données d'un ecran
void showScreen(String label, double value, int unit) {
  display.clearDisplay(); 
  showBatterylevel(readVcc());
  display.setCursor(0,0);
  display.println(label);
  drawFloatValue(0, 20, value, unit);
  display.display();  
}




// Enregistre un echantillon de pression
void pushAvg(float val) {
  movavg_buff[movavg_i] = val;
  movavg_i = (movavg_i + 1) % MOVAVG_SIZE;
}

float getAvg(float * buff, int size) {
  float sum = 0.0;
  for(int i=0; i<size; i++) {
    sum += buff[i];
  }
  return sum / size;
}
// Enregistre les altitudes Min & Max
void setAltiMinMax() {
  if (altitude > altiMax) altiMax = altitude;
  if (altitude < altiMin) altiMin = altitude;
}
void resetAltiMinMax() {
  altiMax = altiMin = altitude;
}




// Affiche un caractére en x, y
void drawCar(int sx, int sy, int num, uint8_t *font, int fw, int fh, int color) {
  byte row;
  for(int y=0; y<fh; y++) {
    for(int x=0; x<(fw/8); x++) {
      row = pgm_read_byte_near(font+x+y*(fw/8)+(fw/8)*fh*num);
      for(int i=0;i<8;i++) {
        if (bitRead(row, 7-i) == 1) display.drawPixel(sx+(x*8)+i, sy+y, color);
      }
    }
  }
}

// Affiche un gros caractére en x, y
void drawBigCar(int sx, int sy, int num) {
  drawCar(sx, sy, num, Font24x40, 24, 40, WHITE) ;
}

void drawDot(int sx, int sy, int h) {
  display.fillRect(sx, sy-h, h, h, WHITE);
}

// Affiche un symbole en x, y
void drawSymbol(int sx, int sy, int num) {
  drawCar(sx, sy, num, Symbol, 16, 16, WHITE) ;
}

// Affiche un nombre decimal
void drawFloatValue(int sx, int sy, double val, int unit) {
  char charBuf[15];
  if (val < 10000) {
    dtostrf(val, 3, 1, charBuf); 
    int nbCar = strlen(charBuf);
    if (nbCar > 5) { // pas de decimal
      for (int n=0; n<4; n++) drawBigCar(sx+n*26, sy, charBuf[n]- '0');
      drawSymbol(108,sy, unit);
    }
    else {
      drawBigCar(sx+86, sy, charBuf[nbCar-1]- '0');
      drawDot(78, sy+39, 6);
      nbCar--;
      if (--nbCar > 0) drawBigCar(sx+52, sy, charBuf[nbCar-1]- '0');
      if (--nbCar > 0) drawBigCar(sx+26, sy, charBuf[nbCar-1]- '0');
      if (--nbCar > 0) drawBigCar(sx, sy, charBuf[nbCar-1]- '0');
      drawSymbol(112,sy, unit);
    }
  }
}
// Show battery level icon
void showBatterylevel(long vcc) {
  if (vcc > 3600) display.drawBitmap(104, 1,  Battery, 20, 7, 1); 
  else if (vcc > 3400) display.drawBitmap(104, 1,  Battery+21, 20, 7, 1); 
  else if (vcc > 3200) display.drawBitmap(104, 1,  Battery+42, 20, 7, 1); 
  else if (vcc > 3000) display.drawBitmap(104, 1,  Battery+63, 20, 7, 1); 
  else display.drawBitmap(104, 1,  Battery+84, 20, 7, 1); 
}
// Read VCC
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}
// Calcul le QNH
float sea_pressure (float press, float GPS_alti){
  return (press/pow(1-(GPS_alti/44330.0),5.255));}

Delta_G:
That code by itself isn't very big. It must be the libraries that are eating up all the memory. I know the GPS library is big and the SD library is HUGE because of the read buffer it needs. You may just need to get a bigger Arduino to use all those things together.

Indeed I see that the library GFX, SD and GPS are big but I do not have the knowledge to know what I can remove. I will inquire more. Thank you

Adafruit's GPS library is ... HUGE! I was able to build a device using it, but I had to go with a 32u4 (more RAM), use the F macro, and completely expunge Strings from my program, as well as use a 3rd party oled library to get it to fit! While it has excellent usability and transparency, there are other gps libraries that may do what you need.

Hello, thank you for your answers. I do not use a mega because it is to make an altimeter so I would like a module that is small. I will use an M0 if I can not manage to gain memory. I will try at first to reduce the size of my libraries by putting only the functions that I need. For example, for the GPS library, I only need GPS.altitude and GPS.longitudeDegrees GPS.latitudeDegrees. I will try to remove everything else. I planned to use the SD library to record my position every second to see my route. I will see if I gain some memory. I will look at tutorials to use macro F. Thank you

You can find NeoGPS by SlashDevin in the library manager. It can be configured down to 10 bytes of RAM, if necessary.

I'd like to help but I can't find some of the libraries you reference. The "Button by Michael Adams" library I have from Library Manager doesn't match the library your sketch expects. The MS5611 library in Library Manager doesn't match the one your sketch expects.

Hello, thank you for your answers.

johnwasser:
I'd like to help but I can't find some of the libraries you reference. The "Button by Michael Adams" library I have from Library Manager doesn't match the library your sketch expects. The MS5611 library in Library Manager doesn't match the one your sketch expects.

The libraries that take the most space are GPS and SD in my opinion. I think I will remove the data storage on SD. I fall to 114% of dynamic memory if I remove the SD library. If I can find a GPS library that is less big I will be able to get the code working on the 328P.

clem64000:
... If I can find a GPS library that is less big I will be able to get the code working on the 328P.

John Wasser already told you about using neoGPS.

clem64000:
Hello, thank you for your answers. The libraries that take the most space are GPS and SD in my opinion. I think I will remove the data storage on SD. I fall to 114% of dynamic memory if I remove the SD library. If I can find a GPS library that is less big I will be able to get the code working on the 328P.

You said previously you were at 114% with the SD library in use, you would expect removing it to reduce memory consumption sigificantly.

Hello and thanks for your help.
I will look at the NeoGPS library in detail. I saw that there is a gps ublox specific library (that's the one I use) so I'll try it.

srnet:
You said previously you were at 114% with the SD library in use, you would expect removing it to reduce memory consumption sigificantly.

Excuse me, on the last code I posted (# 3) I use 144% of dynamic memory with the included SD library and 114% without this library. That's why I think to remove the SD module. I wanted to use an SD card to record my position every 10 seconds in order to see the path of my hike on my computer.