Weird problem regarding SD card data logging

I’m have a data logger (with RTC) which measures temp/rh (SHT85) and particulate matter (SPS30). I have problems getting everything logged to the SD card. If I comment lines:

PM1 = val.MassPM1;
  PM25 = val.MassPM2;
  PM4 = val.MassPM4;
  PM10 = val.MassPM10;

everything works fine although rarely the code print error in writing data. But as soon as I try to measure also particles, the code doesn’t log anything to the SD card. I have ensured that all the components (RTC, SD card, sensors) work individually and I don’t think this is a loose connection/bad wiring problem. I’m using 10k pull-up resistors for the I2Cs. The SPS30 library is from GitHub - paulvha/sps30: Sensirion SPS30 driver for ESP32, SODAQ, MEGA2560, UNO, ESP8266, Particle-photon on UART OR I2C coummunication. Any ideas?

E: Did some more testing and I can get it to work if I remove either the RTC or the SHT85 measurement/logging from the code. Is this a memory problem (I’m using uno)? With the full code I get 63 % and 83% memory space used and with only RTC, SPS30 and SD logging I get 58% and 80%.

// Sensirion SPS30
#include "sps30.h"
#define SP30_COMMS I2C_COMMS
#define TX_PIN 0
#define RX_PIN 0
#define DEBUG 0

SPS30 sps30;

unsigned long starttime;
unsigned long sampletime_ms = 4000;

float PM1;
float PM25;
float PM4;
float PM10;

// Sensirion SHT85 (temp & rh)
#include <Wire.h>
#include "SHTSensor.h"
SHTSensor sht(SHTSensor::SHT3X);

float temp;
float rh;

// Real Time Clock pcf8523
#include <RTClib.h>
RTC_PCF8523 RTC;

// SD card
#include <SPI.h>
#include <SD.h>
const int chipSelect = 10;
File dataFile;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  
  if (!SD.begin(chipSelect)) {
    Serial.println(F("SD card initialization failed!"));
    while (1);
  }
  
  Wire.begin();
  RTC.begin();
  pinMode(10,OUTPUT);

  if (sht.init()) {
    //Serial.print(F("init(): success\n")); Say nothing if no problems
    ;
  } else {
    Serial.print(F("SHT85 init(): failed\n"));
  }
  
  sht.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM); // only supported by SHT3x
  sps30.EnableDebugging(DEBUG);


  if (sps30.begin(SP30_COMMS) == false) {
    Errorloop("could not initialize communication channel.", 0);
  }
  if (sps30.probe() == false) {
    Errorloop("could not probe / connect with SPS30.", 0);
  }
  else
    ;
    
  if (sps30.reset() == false) {
    Errorloop("could not reset.", 0);
  }

  if (sps30.start() == true)
    ;
  else
    Errorloop("Could NOT start measurement", 0);

  if (SP30_COMMS == I2C_COMMS) {
    if (sps30.I2C_expect() == 4)
      ;
  }
}

void loop() {

  read_SPS30(); // Read SPS30

  read_SHT85(); // Read temp & rh

  serial_out(); // Serial print all data

  save_SD(); // Write all data to SD card

  starttime = millis(); // Wait for the next sample
    while ((millis()-starttime) < sampletime_ms){
    }
}

//////////////////////////////////// Functions //////////////////////////////////////////////

bool read_SPS30(){
  uint8_t ret, error_cnt = 0;
  struct sps_values val;

  // loop to get data
  do {
    ret = sps30.GetValues(&val); // Gets SPS30 data
    if (ret == ERR_DATALENGTH){
        if (error_cnt++ > 3) {
          ErrtoMess("Error during reading values: ",ret);
          return(false);
        }
        delay(1000);
    }
    else if(ret != ERR_OK) {
      ErrtoMess("Error during reading values: ",ret);
      return(false);
    }
  } 
  while (ret != ERR_OK);
  
  /*PM1 = val.MassPM1; // If commented then everything works fine for the most part. Only occasional failures in writing data to SD card.
  PM25 = val.MassPM2;
  PM4 = val.MassPM4;
  PM10 = val.MassPM10;*/
}

void Errorloop(char *mess, uint8_t r)
{
  if (r) ErrtoMess(mess, r);
  else Serial.println(mess);
  Serial.println(F("Program on hold"));
  for(;;) delay(100000);
}

void ErrtoMess(char *mess, uint8_t r)
{
  char buf[80];
  Serial.print(mess);
  sps30.GetErrDescription(r, buf, 80);
  Serial.println(buf);
}

void read_SHT85(){
  sht.readSample(); // Reads SHT85 sensor
  temp = sht.getTemperature(); // Gets temperature from the SHT85 sensor
  rh = sht.getHumidity(); // Gets relative humidity from the SHT85 sensor
}

void serial_out(){
  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(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.print(",");
  Serial.print(PM1);
  Serial.print(F(","));
  Serial.print(PM25);
  Serial.print(F(","));
  Serial.print(PM4);
  Serial.print(F(","));
  Serial.print(PM10);
  Serial.print(F(","));
  Serial.print(temp);
  Serial.print(F(","));
  Serial.print(rh);
  Serial.print(F("\n"));
}

void save_SD(){
  DateTime now = RTC.now();
  dataFile = SD.open("data.txt", FILE_WRITE);
  if (dataFile){
    dataFile.print(now.year(), DEC);
    dataFile.print('/');
    dataFile.print(now.month(), DEC);
    dataFile.print('/');
    dataFile.print(now.day(), DEC);
    dataFile.print(' ');
    dataFile.print(now.hour(), DEC);
    dataFile.print(':');
    dataFile.print(now.minute(), DEC);
    dataFile.print(':');
    dataFile.print(now.second(), DEC);
    dataFile.print(",");
    dataFile.print(PM1);
    dataFile.print(",");
    dataFile.print(PM25);
    dataFile.print(",");
    dataFile.print(PM4);  
    dataFile.print(",");
    dataFile.print(PM10);
    dataFile.print(",");
    dataFile.print(temp);
    dataFile.print(",");
    dataFile.println(rh);
    dataFile.close();
    Serial.println(F("Data saved"));
    } 
  else {
    Serial.println(F("Error writing data to SD card"));
    }
  }

If PM measurement commented:

2020/2/27 10:38:20,0.00,0.00,0.00,0.00,23.24,12.48
Data saved
2020/2/27 10:38:24,0.00,0.00,0.00,0.00,23.31,12.57
Data saved
2020/2/27 10:38:28,0.00,0.00,0.00,0.00,23.31,12.58
Error writing data to SD card
2020/2/27 10:38:32,0.00,0.00,0.00,0.00,23.31,12.54
Data saved
2020/2/27 10:38:36,0.00,0.00,0.00,0.00,23.33,12.57
Data saved
2020/2/27 10:38:40,0.00,0.00,0.00,0.00,23.38,12.47
Data saved
2020/2/27 10:38:44,0.00,0.00,0.00,0.00,23.30,12.52
Data saved
2020/2/27 10:38:48,0.00,0.00,0.00,0.00,23.34,12.49
Data saved
2020/2/27 10:38:52,0.00,0.00,0.00,0.00,23.36,12.55
Data saved
2020/2/27 10:38:56,0.00,0.00,0.00,0.00,23.31,12.55
Data saved
2020/2/27 10:39:0,0.00,0.00,0.00,0.00,23.36,12.57
Error writing data to SD card
2020/2/27 10:39:4,0.00,0.00,0.00,0.00,23.33,12.47
Data saved
2020/2/27 10:39:8,0.00,0.00,0.00,0.00,23.24,12.42
Data saved
2020/2/27 10:39:13,0.00,0.00,0.00,0.00,23.28,12.57
Data saved

If uncommented:

2020/2/27 10:52:4,0.78,0.82,0.82,0.82,23.99,13.37
Error writing data to SD card
2020/2/27 10:52:8,0.72,0.76,0.76,0.76,23.95,13.37
Error writing data to SD card
2020/2/27 10:52:12,0.68,0.72,0.72,0.72,23.95,13.52
Error writing data to SD card
2020/2/27 10:52:16,0.64,0.68,0.68,0.68,23.93,13.60
Error writing data to SD card
2020/2/27 10:52:20,0.58,0.61,0.61,0.61,23.88,13.55
Error writing data to SD card
2020/2/27 10:52:24,0.53,0.56,0.56,0.56,23.89,13.47
Error writing data to SD card
2020/2/27 10:52:28,0.50,0.53,0.53,0.53,23.91,13.41
Error writing data to SD card
2020/2/27 10:52:32,0.44,0.46,0.46,0.46,23.96,13.54
Error writing data to SD card
2020/2/27 10:52:36,0.39,0.41,0.41,0.41,23.96,13.49
Error writing data to SD card
2020/2/27 10:52:40,0.34,0.36,0.36,0.36,23.96,13.51
Error writing data to SD card

I don't know why the sd.write fails in one case but not the other, but the file should not be opened every time you write.

Open the file once in setup() and close it when you are done logging.

Start with an empty SD card.

i don’t see an exact problem but have several comments

in setup(), what is the benefit of the following except to consume code space? if there is a problem, flashing an LED would be better

while (!Serial) {

in setup(), the following is unnecessary

 else
    ;

in setup(), why not do

 if (sps30.start() == false)
    Errorloop("Could NOT start measurement", 0);

instead of

 if (sps30.start() == true)
    ;
  else
    Errorloop("Could NOT start measurement", 0);

in setup, what is the purpose of the test

   if (sps30.I2C_expect() == 4)
      ;

why not simply

 if (SP30_COMMS == I2C_COMMS) {
    sps30.I2C_expect() == 4);
  }

in loop(), test whether something should be done rather than waiting

void loop() {
  static unsigned long msecLast = 0;
        unsigned msec = millis();
  if (msec - msecLast < sampletime_ms)
      return;
  msecLast = msec;

instead of

 starttime = millis(); // Wait for the next sample
    while ((millis()-starttime) < sampletime_ms){
    }

in read_SP30(), why stop execution? what if the error was intermittent?

 while (ret != ERR_OK);

in serial_out() and save_SD() you can do

   char s [40];
    sprintf (s, "%d/%d/%d %d:%d:%d:%d,%d,%d,%d,%d,%d\n",
        now.year(), now.month(), now.day(),
        now.hour(), now.minute(), now.second(),
        PM1, PM25, PM4, PM10, temp, rh);
    Serial.println(s);

instead of

 Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.print(",");
  Serial.print(PM1);
  Serial.print(F(","));
  Serial.print(PM25);
  Serial.print(F(","));
  Serial.print(PM4);
  Serial.print(F(","));
  Serial.print(PM10);
  Serial.print(F(","));
  Serial.print(temp);
  Serial.print(F(","));
  Serial.print(rh);
  Serial.print(F("\n"));