Help reducing dynamic memory usage

I have a combined code that read heartrate and SPO2 then sent the data using LoRaWAN. Using these 2 library that work best by itself LoraWAN lib | MAX30102 lib. My board is Arduino Pro or Pro Mini with ATmega 328P (3.3V, 8 MHz) Processor.

Global variables use 2098 bytes (102%) of dynamic memory, leaving -50 bytes for local variables. Maximum is 2048 bytes

I've tried reducing char array size to 8,removing some Serial.print stuff, and the best i can get is the code use exactly 2048 bytes and getting uploaded but it wont run.
Here's my Arduino code

    #include <DFRobot_MAX30102.h>
    #include <lorawan.h>
    DFRobot_MAX30102 particleSensor;
    
    //ABP Credentials 
    const char *devAddr = "27FD.....";
    const char *nwkSKey = "9E74A99C9FE480........................";
    const char *appSKey = "E2A779DC190B343.......................";
    
    const unsigned long interval = 20000;    // 10 s interval to send message
    unsigned long previousMillis = 0;  // will store last time message sent
    
    char myStr[50];
    
    const sRFM_pins RFM_pins = {
      .CS = 10,
      .RST = 9,
      .DIO0 = 2,
      .DIO1 = 6,
    };
    
    
    void setup()
    {
      //Init serial
      Serial.begin(115200);
      lora.init();
      particleSensor.begin();
    
      particleSensor.sensorConfiguration(/*ledBrightness=*/50, /*sampleAverage=*/SAMPLEAVG_4, \
                            /*ledMode=*/MODE_MULTILED, /*sampleRate=*/SAMPLERATE_100, \
                            /*pulseWidth=*/PULSEWIDTH_411, /*adcRange=*/ADCRANGE_16384);
    
      // Set LoRaWAN Class change CLASS_A or CLASS_C
      lora.setDeviceClass(CLASS_C);
    
      // Set Data Rate
      lora.setDataRate(SF12BW125);
    
      // set channel to random
      lora.setChannel(MULTI);
      
      // Put ABP Key and DevAddress here
      lora.setNwkSKey(nwkSKey);
      lora.setAppSKey(appSKey);
      lora.setDevAddr(devAddr);
    }
    
    int32_t SPO2; //SPO2
    int8_t SPO2Valid; //Flag to display if SPO2 calculation is valid
    int32_t heartRate; //Heart-rate
    int8_t heartRateValid; //Flag to display if heart-rate calculation is valid 
    
    void loop(){
      if(millis() - previousMillis > interval) {
        previousMillis = millis(); 
        particleSensor.heartrateAndOxygenSaturation(/**SPO2=*/&SPO2, /**SPO2Valid=*/&SPO2Valid, /**heartRate=*/&heartRate, /**heartRateValid=*/&heartRateValid);
        //Print result 
        Serial.print(heartRate, DEC);
        Serial.println(SPO2, DEC);
    
        sprintf(myStr, "%d,%d", heartRate,SPO2); 
    
        Serial.print(F("Send: "));
        Serial.println(myStr);
        
        lora.sendUplink(myStr, strlen(myStr), 0);
      }
      lora.update();
    }

Not much you can do with that code. You are simply asking too much of an MCU with a microscopic amount of RAM.

These (obscured) constants could be put in program memory:

    const char *devAddr = "27...";
    const char *nwkSKey = "9E74...";
    const char *appSKey = "E2A7...";

If these are private keys, it is not a good idea to post them on a public forum.

1 Like

If you do get it to run, it won't run for long because you are creating a NEW big string every time through the loop() and the is no way to recover the memory being used each time.

Not that I can see. Can you explain what you mean?

You are right! He has a global string of 50 bytes that is always reused. OOPS!

How do i put these constant in the program memory ?.
Thanks for the reminder

Use PROGMEM to store a character constant, and use strcpy_P() to copy it to a character array.

But the real problem is the huge libraries, and their use of RAM. You need a Mega or bigger.

1 Like

nano Every has 6k of RAM, and would be a better physical size match.

1 Like

The problem is iam using development board that already have LoRaWAN module already soldered in. So iam afrain i cant change my microcontroller :frowning:

i'am just following what the library wants, if i change the type width it wont work. If i change the library file it wont work too, idk why

Using simple LoRaWAN code that send message counter like this

#include <lorawan.h>

//ABP Credentials 
const char *devAddr = "27F....";
const char *nwkSKey = "9E74A99C9FE4.............";
const char *appSKey = "E2A779DC190..............";

const unsigned long interval = 20000;    // 10 s interval to send message
unsigned long previousMillis = 0;  // will store last time message sent
unsigned int counter = 0;     // message counter

char myStr[50];
char outStr[255];
byte recvStatus = 0;

const sRFM_pins RFM_pins = {
  .CS = 10,
  .RST = 9,
  .DIO0 = 2,
  .DIO1 = 6,
};


void setup() {
  // Setup loraid access
  Serial.begin(115200);
  delay(5000);
  Serial.println("Start..");
  if(!lora.init())
  {
    Serial.println("RFM95 not detected");
    delay(5000);
    return;
  }

  // Set LoRaWAN Class change CLASS_A or CLASS_C
  lora.setDeviceClass(CLASS_C);

  // Set Data Rate
  lora.setDataRate(SF12BW125);

  // set channel to random
  lora.setChannel(MULTI);
  
  // Put ABP Key and DevAddress here
  lora.setNwkSKey(nwkSKey);
  lora.setAppSKey(appSKey);
  lora.setDevAddr(devAddr);
}

void loop() {
  if(millis() - previousMillis > interval) {
    previousMillis = millis(); 

    sprintf(myStr, "Counter-%d", counter); 

    Serial.print("Sending: ");
    Serial.println(myStr);
    
    lora.sendUplink(myStr, strlen(myStr), 0);
    counter++;
  }

  recvStatus = lora.readData(outStr);
  if(recvStatus) {
    Serial.print("====>> ");
    Serial.println(outStr);
  }
  
  // Check Lora RX
  lora.update();
}

use 57% of dynamic memory. The thing i have a combined code of sending GPS data through LoRaWAN and it only uses 74%

The are a few places in the libraries where the F() macro could save you some memory, but not enough to make a major difference.

The myStr array does not need to be anywhere near 50 characters long. I would make it long enough to hold nwkSKey and appSKey so that it could be used as a buffer for copying those out of PROGMEM. Alternatively, you could modify the library to accept data stored in PROGMEM, but if you go to that much trouble just store the keys in binary instead of ASCII, since the library converts them to binary for its internal storage.

Use sprintf_P() instead of sprintf() so the pattern can be stored in PROGMEM.

1 Like

Try to be serious. If the project can't be made to fit, you have at least three choices: abandon the project, cut parts out of the project or use an MCU with more memory.

It is really not all that complicated.

How much dynamic memory does the sensor library use? If you compile the example sketches from the sensor library, what does that tell you?

I took a look at the library code, and I found variables that might take half the atmega328's dynamic memory. Combine that with the LoRaWAN library, and that could explain the problem.

For example the code here

creates several arrays that look like they might use hundreds of bytes each.

#define FreqS 25    //sampling frequency
#define BUFFER_SIZE (FreqS * 4) 
#define MA4_SIZE 4 // DONOT CHANGE
//#define min(x,y) ((x) < (y) ? (x) : (y)) //Defined in Arduino.h

//uch_spo2_table is approximated as  -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99, 
              99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 
              100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 
              97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91, 
              90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 
              80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67, 
              66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 
              49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29, 
              28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5, 
              3, 2, 1 } ;
static  int32_t an_x[ BUFFER_SIZE]; //ir
static  int32_t an_y[ BUFFER_SIZE]; //red

If that uch_spo2_table array was in PROGMEM, that might make a good saving by itself.

It seems to be used in only one place:

n_spo2_calc= uch_spo2_table[n_ratio_average] ;

So that could be changed to

n_spo2_calc = pgm_read_byte_near(uch_spo2_table + n_ratio_average);

I also wonder if those 2 other arrays an_x & an_y really need to be int32_t, or with some adaptation they could be int16_t.

1 Like

Would you verify that the link to the LoraWAN library is correct? I get an error with the following line, the library wants a 4th argument to the sendUplink() function.

    lora.sendUplink(myStr, strlen(myStr), 0);

yeah sorry, i guess i was just desperate

alright ill try that

I just checked its modified to fit my LoRaWAN signal specification and most likely outdated since i got the library from my friend and when i check the readme on the libraries folder it credited Beelan.
I'll upload a fork for the repo / the library itself if its needed

This save 159 Bytes, but my program still wont run :frowning:

What is it now? About 93%? Need to save another 200 bytes maybe.