How do I reduce the memory usage of my sketch?

I'm new to Arduino, and I can't figure out how to reduce the memory usage of my sketch. I am trying to make a heart rate and spo2 monitor with an oled screen and just compiled the codes I found online (I used the ones from the SparkFun, but I cannot test it as the code can't be uploaded to my Arduino Uno). I have looked into the link below and other forums but I can't really figure it out. Sorry if the information is already available somewhere. Here's my code:

#include <Adafruit_GFX.h>        //OLED  libraries
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include "MAX30105.h"           //MAX3010x library
#include "heartRate.h"          //Heart rate  calculating algorithm
#include "spo2_algorithm.h"

MAX30105 particleSensor;

#define SCREEN_WIDTH  128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display  height, in pixels
#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino  reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);  //Declaring the display name (display)

#define MAX_BRIGHTNESS 255

#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//Arduino Uno doesn't have enough SRAM to store 100 samples of IR led data and red led data in 32-bit format
//To solve this problem, 16-bit MSB of the sampled data will be truncated. Samples become 16-bit data.
uint16_t irBuffer[100]; //infrared LED sensor data
uint16_t redBuffer[100];  //red LED sensor data
#else
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data
#endif

int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid

byte pulseLED = 11; //Must be on PWM pin
byte readLED = 13; //Blinks with each data read

void setup()
{
  Serial.begin(115200); // initialize serial communication at 115200 bits per second:

  pinMode(pulseLED, OUTPUT);
  pinMode(readLED, OUTPUT);

  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
  {
    Serial.println(F("MAX30105 was not found. Please check wiring/power."));
    while (1);
  }

  byte ledBrightness = 60; //Options: 0=Off to 255=50mA
  byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
  byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
  byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
  int pulseWidth = 411; //Options: 69, 118, 215, 411
  int adcRange = 4096; //Options: 2048, 4096, 8192, 16384

  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings
}

void loop() {

long irValue = particleSensor.getIR();    //Reading the IR value it will permit us to know if there's a finger on the  sensor or not
                                           //Also detecting a heartbeat
if(irValue  > 7000){                                           //If a finger is detected
    display.clearDisplay();                                   //Clear the display
    display.setTextSize(1);                                   //Near  it display the average BPM you can display the BPM if you want
    display.setTextColor(WHITE);  
    display.setCursor(50,0);                
    display.println(F("BPM"));             
    display.setCursor(100,0);                
    display.println(heartRate, DEC); 
    display.setCursor(50,20);                
    display.println(F("SPO2"));
    display.setCursor(100,20);
    display.println(spo2, DEC);
    display.display();
    
  if (checkForBeat(irValue) == true)                        //If  a heart beat is detected
  {
    display.clearDisplay();                                //Clear  the display
    display.setTextSize(1);                                //And  still displays the average BPM
    display.setTextColor(WHITE);             
    display.setCursor(50,0);                
    display.println(F("BPM"));             
    display.setCursor(100,0);                
    display.println(heartRate, DEC); 
    display.setCursor(50,20);                
    display.println(F("SPO2"));
    display.setCursor(100,20);
    display.println(spo2, DEC);
    display.display();
    tone(3,1000);                                        //And  tone the buzzer for a 100ms you can reduce it it will be better
    delay(100);
    noTone(3);
{
  bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps

  //read the first 100 samples, and determine the signal range
  for (byte i = 0 ; i < bufferLength ; i++)
  {
    while (particleSensor.available() == false) //do we have new data?
      particleSensor.check(); //Check the sensor for new data

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample

    Serial.print(F("red="));
    Serial.print(redBuffer[i], DEC);
    Serial.print(F(", ir="));
    Serial.println(irBuffer[i], DEC);
  }
}

  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);

  //Continuously taking samples from MAX30102.  Heart rate and SpO2 are calculated every 1 second
  while (1)
  {
    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for (byte i = 25; i < 100; i++)
    {
      redBuffer[i - 25] = redBuffer[i];
      irBuffer[i - 25] = irBuffer[i];
    }

    //take 25 sets of samples before calculating the heart rate.
    for (byte i = 75; i < 100; i++)
    {
      while (particleSensor.available() == false) //do we have new data?
        particleSensor.check(); //Check the sensor for new data

      digitalWrite(readLED, !digitalRead(readLED)); //Blink onboard LED with every data read

      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); //We're finished with this sample so move to next sample

      //send samples and calculation result to terminal program through UART
      Serial.print(F("red="));
      Serial.print(redBuffer[i], DEC);
      Serial.print(F(", ir="));
      Serial.print(irBuffer[i], DEC);

      Serial.print(F(", HR="));
      Serial.print(heartRate, DEC);

      Serial.print(F(", HRvalid="));
      Serial.print(validHeartRate, DEC);

      Serial.print(F(", SPO2="));
      Serial.print(spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(validSPO2, DEC);
    }

    //After gathering 25 new samples recalculate HR and SP02
    maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
  }
}
}
}

Here's the error message:

Sketch uses 19836 bytes (61%) of program storage space. Maximum is 32256 bytes.
Global variables use 2097 bytes (102%) of dynamic memory, leaving -49 bytes for local variables. Maximum is 2048 bytes.
Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
data section exceeds available space in board

Compilation error: data section exceeds available space in board

I really need the help as this is for our final. Please be kind.

Can you reduce the buffer size to 25 ?

Also all those libraries (probably the ones for the screen are the most impactful) take space

May be you should use a more capable arduino (mega for example or go ARM) or dumb down the display by using a plain old alphanumeric LCD.

It's way worse than you think.

That number doesn't take into account the 1024 bytes of RAM that Adafruit_SSD1306 will want to allocate at runtime. So you're not 49 bytes over the edge, you're 1073 bytes over - and that doesn't take into account the at least a couple of hundred bytes you'll need for stack and local variables - to say nothing of any runtime allocations by other libraries.

Basically, it's not going to happen. You need a board with more RAM. A lot more RAM.

Maybe the problem is in these includes that you did not show. They are in other tabs in your sketch.

But I also suspect the adafruit SSD1306 library. I noticed it's a memory hog when I tried to use it in the past.

It is not a smart idea to use an old Uno nowadays. There a lot of new Arduino compatible boards with a way more available memory - ESP32, rp2040 ...

Reducing the buffer size to 50 worked, but we've yet to test it on our actual circuit. Thank you, and will update if it does work!

Unfortunately, it's the one required for us. We don't have much choice :sweat:

It won't work.

You're missing the display.begin call without which your display won't function.

But it still won't work even if you add it because you don't have 1024 bytes of RAM available for the screen buffer that the begin call will try to allocate.

Why?
RP2040 board (my favourite) has about 150 times more RAM , works about 10 times faster and costs less than Uno,

you likely have to modify the algorithm too. Do you understand the code?

➜ read this

You should give up that screen, it's not meant for the UNO. Get an I2C LCD 1602 or 2004 and be done with it. you can draw simple custom chars for the heart or an alarm if needed

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