Put this code together but not enough memory!

see full code at end

So i have managed to put this code together, but it takes up too much storage space normally when everything isn't commented out. The issue is adding my final 3rd sensor and it's code, the Grove Dust sensor (Grove PM2.5 Laser Sensor.pdf (744.8 KB)).

I commented out the SD file just to see if it all runs fine with less storage space taken up (around 500 bytes ish..) which is fine. But when i try to run the programme i get this error , even thought the sensor works when I run the code and sensor isolated or just with my CJMCU-MS1100 TVOC sensor:

“S =-2
HM330X init failed!!!
_Library-master (1)\libraries\Grove___Laser_PM2_5_Sensor_HM3301-1.0.2\Seeed_HM330X.cpp50 error code =-2
HM330X init failed!!!
Serial start
C:\Users\callu\Documents\Arduino\Arduino\Adafruit_BMP280_Library-master (1)\libraries\Grove___Laser_PM2_5_Sensor_HM3301-1.0.2\Seeed_HM330X.cpp50 error code =-2
HM330X init failed!!!”

This error means a comm error, yet unsure how or why?

If anybody could suggest anything that would be great, if not, can anyone help me in reducing this code's storage/memory uptake?

I am aiming to run 3 sensors to take readings every 15 minutes for 5 minutes, and write to the SD card in a CSV format with an elegoo uno R3 and adafruit data logger shield:

BME280 (BST-BME280-DS002-1509607.pdf (1.4 MB))

CJMCU-MS1100 (
MS1100-ETC.pdf (433.6 KB))

Grove PM sensor (Grove PM2.5 Laser Sensor.pdf (744.8 KB))

Code with nothing commented out:

#include <Wire.h>
#include "RTClib.h"
#include <BME280I2C.h>
#include <SPI.h>
#include <SD.h>
#include <Seeed_HM330X.h>

BME280I2C bme;

#ifdef  ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL_OUTPUT SerialUSB
#else
#define SERIAL_OUTPUT Serial
#endif

HM330X sensor;
uint8_t buf[30];



HM330XErrorCode print_result(const char* str, uint16_t value) {
  if (NULL == str) {
    return ERROR_PARAM;
  }
  SERIAL_OUTPUT.print(str);
  SERIAL_OUTPUT.println(value);
  return NO_ERROR;
}

/*parse buf with 29 uint8_t-data*/
HM330XErrorCode parse_result(uint8_t* data) {
  const char* str[] = {"sensor num: ", "PM1.0 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
                       "PM2.5 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
                       "PM10 concentration(CF=1,Standard particulate matter,unit:ug/m3): ",
                       "PM1.0 concentration(Atmospheric environment,unit:ug/m3): ",
                       "PM2.5 concentration(Atmospheric environment,unit:ug/m3): ",
                       "PM10 concentration(Atmospheric environment,unit:ug/m3): ",
                      };
  uint16_t value = 0;
  if (NULL == data) {
    return ERROR_PARAM;
  }
  for (int i = 1; i < 8; i++) {
    value = (uint16_t) data[i * 2] << 8 | data[i * 2 + 1];
    print_result(str[i - 1], value);

  }

  return NO_ERROR;
}

HM330XErrorCode parse_result_value(uint8_t* data) {
  if (NULL == data) {
    return ERROR_PARAM;
  }
  for (int i = 0; i < 28; i++) {
    SERIAL_OUTPUT.print(data[i], HEX);
    SERIAL_OUTPUT.print("  ");
    if ((0 == (i) % 5) || (0 == i)) {
      SERIAL_OUTPUT.println("");
    }
  }
  uint8_t sum = 0;
  for (int i = 0; i < 28; i++) {
    sum += data[i];
  }
  if (sum != data[28]) {
    SERIAL_OUTPUT.println("wrong checkSum!!!!");
  }
  SERIAL_OUTPUT.println("");
  return NO_ERROR;
}

#define Aout A0
#define Dout 2

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

RTC_DS1307 rtc;


uint32_t delayMS;

const unsigned long eventInterval = 5000;

unsigned long previousTime = 0;
const int chipSelect = 10;
const int pingPin = 7;


//*******VOID SETUP LOOP********//
void setup() {

  SERIAL_OUTPUT.begin(115200);
  delay(100);
  SERIAL_OUTPUT.println("Serial start");
  if (sensor.init()) {
    SERIAL_OUTPUT.println("HM330X init failed!!!");
    while (1);
  }


  //Open serial communications and wait for port to open and run setup code once:
  Serial.begin(9600);

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");

    //The following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

    //This line sets the RTC with an explicit date & time, for example to set January 21, 2014 at 3am you would call:
    //rtc.adjust(DateTime(2020, 12, 16, 10, 36, 0));
  }

  while (! bme.begin())
  {
    Serial.println("Could not find BME280 sensor!");
    delay(1000);
  }

  bme.chipID(); // Deprecated. See chipModel().
    switch(bme.chipModel())
    {
       case BME280::ChipModel_BME280:
         Serial.println("Found BME280 sensor! Success.");
         break;
       case BME280::ChipModel_BMP280:
         Serial.println("Found BMP280 sensor! No Humidity available.");
         break;
       default:
         Serial.println("Found UNKNOWN sensor! Error!");
    }

  {
    //*******Define digital and analogue inputs from the CJMCU1100 Sensor//
    pinMode(Aout, INPUT);
    pinMode(Dout, INPUT);
  }


  Serial.print("Initializing SD");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("SD failed/missing");
    // don't do anything more:
    while (1);
  }
  Serial.println("card initialized.");
}


//This is the main coding loop, writing the obtained values to the screen and to the SD card//
void loop() {

  /* Updates frequently */
  unsigned long currentTime = millis();

  /* This is the event */
  if (currentTime - previousTime >= eventInterval) {

    //*****************Get Temp, Pressure and Humidity********************//

    float temp(NAN), hum(NAN), pres(NAN);

    BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
    BME280::PresUnit presUnit(BME280::PresUnit_Pa);

    bme.read(pres, temp, hum, tempUnit, presUnit);

    float preshpa = (pres / 100); //create space for conversion of pascals to hectopascals (hPa) which is more usual measure
    float corr_temp = (temp - 0.0); //create space for correction of temperature according to unique sensor validation (will be different for each sensor)
    float corr_hum = (hum + 0); //create space for correction of humidity according to unique sensor validation (will be different for each sensor)
    float corr_preshpa = (preshpa + 7); //create space for correction of pressure according to unique sensor validation (will be different for each sensor)

    Serial.print("Temp: ");
    Serial.print(corr_temp);
       Serial.print("°"+ String(tempUnit == BME280::TempUnit_Celsius ? 'C' :'F'));
       Serial.print("\t\tHumidity: ");
       Serial.print(corr_hum);
       Serial.print("% RH");
       Serial.print("\t\tPressure: ");
       Serial.print(corr_preshpa);
       Serial.println(" hPa");

    //*********GET VOC AIR QUALITY **************//
    //The on-board POT sets the threshold value beyond which the digital output returns "High"//

    float TVOC_value = analogRead(Aout);
    int a = analogRead(Aout);
    int b = digitalRead(Dout);
      Serial.print("High TVOC?:");
      if(b<1) {Serial.print("No");}
      else {Serial.print ("Yes");}
      Serial.print("   TVOC:");
      Serial.print(TVOC_value);
      Serial.println(" ppm");

    //*********GET PARTICULATE AIR QUALITY **************//
    if (sensor.read_sensor_value(buf, 29)) {
              SERIAL_OUTPUT.println("HM330X read result failed!!!");
    }
    parse_result_value(buf);
    parse_result(buf);
        SERIAL_OUTPUT.println("");

    /*********GET TIME ************/
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));

    DateTime now = rtc.now();

        Serial.print(now.year(), DEC);
        Serial.print('/');
        Serial.print(now.month(), DEC);
        Serial.print('/');
        Serial.print(now.day(), DEC);
        Serial.print(" (");
        Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
        Serial.print(") ");
        Serial.print(now.hour(), DEC);
        Serial.print(':');
        Serial.print(now.minute(), DEC);
        Serial.print(':');
        Serial.print(now.second(), DEC);
        Serial.println();

    //Serial.print(" since midnight 1/1/1970 = ");
    //Serial.print(now.unixtime());
    //Serial.print("s = ");
    //Serial.print(now.unixtime() / 86400L);
    //Serial.println("d");


    //DateTime now = rtc.now();


    unsigned int rtcs = now.second();
    unsigned int rtcmin = now.minute();
    unsigned int rtch = now.hour();
    unsigned int rtcd = now.day();
    unsigned int rtcm = now.month();
    unsigned int rtcy = now.year();



    /*********STORE DATA TO SD CARD ************/

    // open the file. note that only one file can be open at a time,so you may have to close one before opening another.
    File dataFile = SD.open("datalog.csv", FILE_WRITE);
    dataFile.seek(EOF);
    // if the file is available, write to it:
    if (dataFile) {

   
      dataFile.print(rtch);              dataFile.print(":");
      dataFile.print(rtcmin);             dataFile.print(":");

      //fprintf(dataFile,
      dataFile.print(rtcs);               dataFile.print(",TIME,");
      dataFile.print(rtcd);               dataFile.print("/");
      dataFile.print(rtcm);               dataFile.print("/");
      dataFile.print(rtcy);             dataFile.print(",DATE,");
      dataFile.print(corr_temp);             dataFile.print(",Temperature,");
      dataFile.print(corr_preshpa);             dataFile.print(",Pressure,");
      dataFile.print(corr_hum);             dataFile.print(",Humidity,");
      dataFile.print(b);             dataFile.print(",TVOC Digital,");
      dataFile.print(a);             dataFile.println(",TVOC Analogue");
      delay(1000);
      dataFile.close();
      Serial.print("Saved");
      delay(10);
      // print to the serial port too:
      //Serial.println(cm);
    }
    // if the file isn't open, pop up an error:
    else {
         Serial.println("error opening datalog.csv");
    }
    delay(20);

    /* Update the timing for the next time around */
    previousTime = currentTime;

  }
}

//***********************End of Code -- you are DONE!*******************************
  • Start by looking at what can be done by putting your arrays of strings in PROGMEM

  • Use the F() macro when printing strings

Google will help you find how to use both techniques

This reply assumes it is your RAM that is running out not your FLASH storage (your code itself looks modest)

For print("...") use
Serial.print(F("...")); instead
for the array of " "
modify this code to be only two dimensions

// with PROGMEM
//Sketch uses 3780 bytes (11%) of program storage space. Maximum is 32256 bytes.
//Global variables use 202 bytes (9%) of dynamic memory, leaving 1846 bytes for local variables. Maximum is 2048 bytes.
// without PROGMEM
//Sketch uses 3778 bytes (11%) of program storage space. Maximum is 32256 bytes.
//Global variables use 696 bytes (33%) of dynamic memory, leaving 1352 bytes for local variables. Maximum is 2048 bytes.

const char Menu[15][3][11] PROGMEM  =
{ {"Set Clocks", "", ""},
  {"", "Clk0", ""},
  {"", "Clk1", ""},
  {"", "Clk2", ""},
  {"", "Save", ""},
  {"Set Sweep", "", ""},
  {"", "Start freq", ""},
  {"", "Stop freq", ""},
  {"", "Step size", ""},
  {"", "Sweep", ""},
  {"", "", "Single"},
  {"", "", "Continuous"},
  {"", "Time (ms)", ""},
  {"", "Save", ""},
  {"Reset", "", ""},
};

void setup() {
  Serial.begin(115200);
  for (int i = 10; i > 0; i--) {
    Serial.print(' '); Serial.print(i);
    delay(500);
  }
  Serial.println();
  for (size_t i = 0; i < 15; i++) {
    String element0(reinterpret_cast<const __FlashStringHelper *>(Menu[i][0]));
    String element1(reinterpret_cast<const __FlashStringHelper *>(Menu[i][1]));
    String element2(reinterpret_cast<const __FlashStringHelper *>(Menu[i][2]));
    Serial.print(element0); Serial.print(" , "); Serial.print(element1); Serial.print(" , "); Serial.print(element2); Serial.println();
  }
}
void loop() {
}

However it may some other problem. What is the error code returned from init()
typedef enum {
NO_ERROR = 0,
ERROR_PARAM = -1,
ERROR_COMM = -2,
ERROR_OTHERS = -128,
} HM330XErrorCode;

It is ERROR_COMM=-2 that is returned from Init()!