Data-logger doesn't fill the .csv

Dear all,

i’m trying to log the data of my grove multi channel gas sensor v1. My setup consists of Arduino Uno, sd-card Data Logger Shield (Older Version - with the RTC (DS1307)) and Arduino grove base shield.

I used and tested the sd-card with the lighttemplogger tutorial from Adafruit: Link
This showed me that writing on the sd-card is working, as well as creating and writing in a .csv file

Everything worked fine, so i tried to merge this example with the already tested example for the grove multi gas channel: Link

What is working as it should:

  1. creating a logfile
  2. the output to the serial monitor

What isn’t working as it should:

  1. there is no entry in the logfile, not even the header (which is telling me there should already be a mistake in my setup)
[code]

#include "RTClib.h"
#include <SD.h>
#include <MutichannelGasSensor.h>
#include <Wire.h>
#include <SPI.h>

#define LOG_INTERVAL  1000
#define SYNC_INTERVAL 1000
uint32_t syncTime = 0;    // time of last sync()

RTC_DS1307 RTC;     // define the Real Time Clock object
const int chipSelect = 10;    // for the data logging shield, we use digital pin 10 for the SD cs line
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  
  while(1);
}

void setup(void) 
{
    Serial.begin(9600);     // start serial for output
    Serial.println();

    Serial.print("Initializing SD card...");    // initialize the SD card
    pinMode(10, OUTPUT);      // make sure that the default chip select pin is set to output, even if you don't use it

    if (!SD.begin(chipSelect)) 
    {
      error("Card failed, or not present"); // see if the card is present and can be initialized:
    }
    Serial.println("card initialized.");

    char filename[] = "LOGGER00.CSV";      // create a new file
    for (uint8_t i = 0; i < 100; i++) 
    {
      filename[6] = i/10 + '0';
      filename[7] = i%10 + '0';
      if (! SD.exists(filename))    
      {
        logfile = SD.open(filename, FILE_WRITE);    // only open a new file if it doesn't exist
        break;  // leave the loop!
      }
     }

    if (! logfile)
    {
      error("couldnt create file");
    }

    Serial.print("Logging to: ");
    Serial.println(filename);

    Wire.begin();     // connect to RTC
    if (!RTC.begin()) 
    {
      logfile.println("RTC failed");
      Serial.println("RTC failed");
    }

    logfile.println("datetime,NH3,CO,NO2,C3H8,C4H10,CH4,H2,C2H5OH");
    Serial.println("datetime,NH3,CO,NO2,C3H8,C4H10,CH4,H2,C2H5OH");
    
    Serial.println("power on!");    //start of the grove multi channel gas sensor
    gas.begin(0x04);    //the default I2C address of the slave is 0x04
    gas.powerOn();

}

void loop(void) 
{
    DateTime now;
    delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));   // delay for the amount of time we want between readings

    now = RTC.now();    // fetch the time and log it
 
    logfile.print(now.year(), DEC);
    logfile.print("/");
    logfile.print(now.month(), DEC);
    logfile.print("/");
    logfile.print(now.day(), DEC);
    logfile.print(" ");
    logfile.print(now.hour(), DEC);
    logfile.print(":");
    logfile.print(now.minute(), DEC);
    logfile.print(":");
    logfile.print(now.second(), DEC);
    logfile.print(", ");

    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(", ");

    float c;
    c = gas.measure_NH3();
    if (c >= 0) 
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else 
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");
         
    c = gas.measure_CO();
    if (c >= 0)
    {
        Serial.print(c); 
        logfile.print(c);
    } 
    else 
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");
    
    c = gas.measure_NO2();
    if (c >= 0)
    {
        Serial.print(c);           
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", "); 

    c = gas.measure_C3H8();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");   
    
    c = gas.measure_C4H10();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", "); 
       
    c = gas.measure_CH4();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");

    c = gas.measure_H2();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
        
    } 
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");  

    c = gas.measure_C2H5OH();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else
    {
    Serial.print("invalid");
    }
    Serial.println();
    delay(3000);
      
}
[/code]

Any input will be appreciated, thanks!

What do you see in the Serial monitor ?

I saw

  logfile.flush();

in some example just now because I went wondering whether you have to do something like closing the file on the SD card.

a7

As I said, everything works how i intended it.

Thanks a7 for your advice,

in the example listet above “lighttemplogger” which is working, also has no close command / no necessity to close the file, so i would suggest this isn’t the problem.

But I will try it!

The logfile.print() command writes values to a buffer and not the actual sd card. The physical write is by a file.close() or a file.flush() command, or by the workings of the SD.h library itself when the buffer is full and holds 512 bytes.

If you rely on the library to write to the card, you can loose data if you don’t close the file at the end of your sketch.

Did you let the program run long enough for the buffer to fill and have the SD library write the card?

I haven’t done much with SD and this code was written for a teensy but it uses SD.h

the write to SD is this part

    File dataFile = SD.open("datalog.txt", FILE_WRITE);
  
    // if the file is available, write to it:
    if (dataFile) {
      dataFile.println(LogDataLine_PS);
      dataFile.close();

opening and closing the file on each call of my "logToSD-function is not a problem

not sure if an file_append-parameters exists
best regards Stefan

Hey guys,

I’m glad to tell you, it’s working as I intended it. Thanks to you guys!
The file.flush() command was the key, and thanks cattledog for your explanation, really helped me to unterstand what’s going on.

One question is now remaining. If I chose file.close() at end of void.loop() I also have to start the loop with file.open()?
Did I unterstood this correctly, the advantage would be the avoidance of possible dataloss? Is there anything else to it?

Many Thanks, appreciated very much!

Updated code:


#include "RTClib.h"
#include <SD.h>
#include <MutichannelGasSensor.h>
#include <Wire.h>
#include <SPI.h>

#define LOG_INTERVAL  1000
#define SYNC_INTERVAL 1000
uint32_t syncTime = 0;    // time of last sync()

RTC_DS1307 RTC;     // define the Real Time Clock object
const int chipSelect = 10;    // for the data logging shield, we use digital pin 10 for the SD cs line
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  
  while(1);
}

void setup(void) 
{
    Serial.begin(9600);     // start serial for output
    Serial.println();

    Serial.print("Initializing SD card...");    // initialize the SD card
    pinMode(10, OUTPUT);      // make sure that the default chip select pin is set to output, even if you don't use it

    if (!SD.begin(chipSelect)) 
    {
      error("Card failed, or not present"); // see if the card is present and can be initialized:
    }
    Serial.println("card initialized.");

    char filename[] = "LOGGER00.CSV";      // create a new file
    for (uint8_t i = 0; i < 100; i++) 
    {
      filename[6] = i/10 + '0';
      filename[7] = i%10 + '0';
      if (! SD.exists(filename))    
      {
        logfile = SD.open(filename, FILE_WRITE);    // only open a new file if it doesn't exist
        break;  // leave the loop!
      }
     }

    if (! logfile)
    {
      error("couldnt create file");
    }

    Serial.print("Logging to: ");
    Serial.println(filename);

    Wire.begin();     // connect to RTC
    if (!RTC.begin()) 
    {
      logfile.println("RTC failed");
      Serial.println("RTC failed");
    }

    logfile.println("datetime,NH3,CO,NO2,C3H8,C4H10,CH4,H2,C2H5OH");
    Serial.println("datetime,NH3,CO,NO2,C3H8,C4H10,CH4,H2,C2H5OH");
    
    Serial.println("power on!");    //start of the grove multi channel gas sensor
    gas.begin(0x04);    //the default I2C address of the slave is 0x04
    gas.powerOn();

}

void loop(void) 
{
    DateTime now;
    delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));   // delay for the amount of time we want between readings

    now = RTC.now();    // fetch the time and log it
 
    logfile.print(now.year(), DEC);
    logfile.print("/");
    logfile.print(now.month(), DEC);
    logfile.print("/");
    logfile.print(now.day(), DEC);
    logfile.print(" ");
    logfile.print(now.hour(), DEC);
    logfile.print(":");
    logfile.print(now.minute(), DEC);
    logfile.print(":");
    logfile.print(now.second(), DEC);
    logfile.print(", ");

    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(", ");

    float c;
    c = gas.measure_NH3();
    if (c >= 0) 
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else 
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");
         
    c = gas.measure_CO();
    if (c >= 0)
    {
        Serial.print(c); 
        logfile.print(c);
    } 
    else 
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");
    
    c = gas.measure_NO2();
    if (c >= 0)
    {
        Serial.print(c);           
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", "); 

    c = gas.measure_C3H8();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");   
    
    c = gas.measure_C4H10();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", "); 
       
    c = gas.measure_CH4();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    }
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");

    c = gas.measure_H2();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
        
    } 
    else
    {
        Serial.print("invalid");
    }
    Serial.print(",");
    logfile.print(", ");  

    c = gas.measure_C2H5OH();
    if (c >= 0)
    {
        Serial.print(c);
        logfile.print(c);
    } 
    else
    {
    Serial.print("invalid");
    }
    
    logfile.println();   
    Serial.println();

    logfile.flush();
    
    delay(3000);
      
}

You can close the file, but you will need to open it again at the start of loop. You won’t need the flush in that case.

The only real downside is it’s probably a little slower, especially as the file grows but since you have a three second delay at the end of loop anyway, perhaps you won’t care about that.

You should make use of the F() macro to move most of your string constants to PROGMEM.
The Uno has quite low memory and the SD library a high demand of RAM.
If your sketch runs as it is and will not be expanded, it does not matter besides good style.