Not enough dynamic memory

Hey all,

I'm creating an air quality sensor and I'm running out of dynamic memory. I'm trying to write the data to an SD card but the library is too large and I haven't been able to find a good replacement. Here is the code I have. It runs out of memory before even adding the code to write to the SD card. I am using an uno for the testing but would like to use a nano for the final product.

#include "s8_uart.h"
#include "SparkFun_SGP30_Arduino_Library.h"
#include <Wire.h>
#include "SHT31.h"
#include <SPI.h>
#include <SD.h>
#include "Adafruit_PM25AQI.h"
#include <SoftwareSerial.h>
#include <AltSoftSerial.h>

//CO2
SoftwareSerial S8_serial(7, 6);
S8_UART *sensor_S8;
S8_sensor sensor;

//TVOC
SGP30 tvoc;

//Temp and Humidity
SHT31 temp;

//PM
AltSoftSerial pmSerial(9, 8);
Adafruit_PM25AQI aqi = Adafruit_PM25AQI();

//SD

File myFile;


void setup() {
  // Configure serial port
  Serial.begin(115200);
  Wire.begin();

  //Initialize CO2 sensor

  S8_serial.begin(S8_BAUDRATE);
  sensor_S8 = new S8_UART(S8_serial);
  // Check if S8 is available
  sensor_S8->get_firmware_version(sensor.firm_version);
  int len = strlen(sensor.firm_version);
  if (len == 0) {
    Serial.println(F("SenseAir S8 CO2 sensor not found!"));
    while (1) {
      delay(1);
    };
  }
  Serial.println(F("CO2 Connected"));
  //sensor_S8->manual_calibration();

  //Initialize PM sensor
  pmSerial.begin(9600);
  if (! aqi.begin_UART(&pmSerial)) {
    Serial.println(F("Could not find PM 2.5 sensor!"));
    while (1) delay(10);
  }
  Serial.println(F("PM Connected"));

  //Initialize TVOC sensor
  if (!tvoc.begin()) {
    Serial.println(F("No SGP30 Detected. Check connections."));
    while (1);
  }
  tvoc.initAirQuality();
  Serial.println(F("TVOC Connected"));

  temp.begin(0x44);
  Wire.setClock(100000);
  uint16_t tempHumid = temp.readStatus();
  Serial.println(F("Temp and Humidity Connected"));



  Serial.print(F("Initializing SD card..."));
  if (!SD.begin(4)) {
    Serial.println(F("initialization failed!"));
    return;
  }
  Serial.println(F("initialization done."));

  
  Serial.println(F("Warming up sensors..."));
  delay(15000); //Time for sensors to warp up
  Serial.flush();
}


void loop() {
  //CO2


  sensor.co2 = sensor_S8->get_co2();

  Serial.print(F("CO2: "));
  Serial.print(sensor.co2);
  Serial.println(F(" ppm"));
  Serial.flush();

  //Temp and Humidity
  temp.read();
  Serial.print(F("Temperature: "));
  Serial.print(temp.getTemperature());
  Serial.println(F(" C"));
  Serial.flush();

  Serial.print(F("Humidity: "));
  Serial.print(temp.getHumidity());
  Serial.println(F(" %"));
  Serial.flush();


  //TVOC
  tvoc.measureAirQuality();
  Serial.print(F("TVOC: "));
  Serial.print(tvoc.TVOC);
  Serial.println(F(" ppb"));
  Serial.flush();


  //PM
  PM25_AQI_Data data;
  if (!aqi.read(&data)) {
    Serial.println("Could not read from PM sensor");
  } else {
    Serial.print(F("PM 1.0: "));
    Serial.println(data.pm10_standard);
    Serial.print(F("PM 2.5: "));
    Serial.println(data.pm25_standard);
    Serial.print(F("PM 10: "));
    Serial.println(data.pm100_standard);
  }
  Serial.flush();

  Serial.print(F("---------------------------------------"));
  Serial.println(F(""));

  delay(5000);
}

Running out of SRAM and using multiple software serial ports just screams that you are using the wrong processor. A Mega would solve both problems. If it is the size that is a stopping point, consider the Mega Pro board. Smaller than an Uno, 8K SRAM and 3 extra UARTs.

Or one of the Teensy boards.

Thanks for your recommendation! Do you know where I can find documentation for the Mega Pro board? Would you happen to know of a similar board that is more breadboard friendly?

The Mega Pro is just a Mega in a different board so the Mega documentation will cover it.

Sorry, no.

If you solder single-row headers then it will fit on a breadboard.

You can try to optimize your code. For example:

  • Among SoftwareSerial and AltSoftSerial class, use one class with two object.
  • Do not use print() and println() together. Use print() only. In case of printing newline, just add "\r\n" at the end of string.
    ... and more on the internet

The SD card library is quite large.
You may be able to distribute the application over 2 processors.
One would have the role of "data logger" and have the SD card device attached.
The other would have the role of air quality measurement.
You could join the 2 over say I2C or SPI

Alternatively, the Nano Every has a larger RAM (6KB) but since the processor is different to the standard Uno/Nano, you have to ensure that the libraries are compatible.

Oh dear dear, it’s time to say goodbye to your old friend and get yourself a new one with more memory and more power!

This won't save much code space and no data space (dynamic memory) at all.

I did a quick test:

and

It is not much, but can save both code and data space

nevermind

But for data space it works only once. Even if you has hundreds Serial.print()'s - the difference will be the same 2 bytes as for one Serial.print().
So it hardly makes sense to take into account

yes, I agree^^

Crossroads Fencing designed one. Although the owner passed away, the family seems to have indicated that they would continue the business.