Help needed with writing real time date stamp to SD card data

Let me start by saying that I have read the forum guidelines and I have tried to get this right!

I am building an environmental data logger to record temp, atmos pressure, humidity etc over a period of several weeks. I want to leave it in my second home whilst I am away.

I have adapted a sketch from SDFat which works (although I do not understand some of the code).
However I cannot find a way of writing actual time to each data record. Using the millis time stamp soon becomes unmanageable when read as a .csv file.
I have tried using library sketches such as RTClib and HCRTC with no success. Here is my current Sketch

//This was based on the example sketch at SDfat now in SD/datalogger



#include <Time.h>
#include <TimeLib.h>
#include <RTClib.h>
#include <DHT.h>  //humidity/temperature sensor library
#include <DallasTemperature.h> //DS18B20 library
#include <OneWire.h> // enables DS18B20 on one pin
#include <SPI.h>      // 
#include "SdFat.h"  // Sd card library

// SD chip select pin.  Be sure to disable any other SPI devices such as Enet.


const uint8_t chipSelect = SS;  //   ????????
int photocellPin=0;           // assign photocell tho Analog pin 0
int photocellReading;         // photocell value
#define DHTPIN 8                //assign Humidity / Temp sensor to pin 8
#define DHTTYPE DHT11          // describe sensor type
DHT dht(DHTPIN,DHTTYPE);       //???????



// Interval between data records in milliseconds.
// The interval must be greater than the maximum SD write latency plus the
// time to acquire and write data to the SD to avoid overrun errors.
// Run the bench example to check the quality of your SD card.


const uint32_t SAMPLE_INTERVAL_MS = 500;

// Log file base name.  Must be six characters or less.

#define FILE_BASE_NAME "Data"


RTC_DS1307 RTC; // define the Real Time Clock object

#define ONE_WIRE_BUS 2 //*-(ConnectDs18B20 to Pin 2 )-*/

/*-----( Declare objects )-----*/

/* Set up a oneWire instance to communicate with any OneWire device*/
OneWire ourWire(ONE_WIRE_BUS);

/* Tell Dallas Temperature Library to use oneWire Library */
DallasTemperature sensors(&ourWire);

DeviceAddress Probe={0x28,0xFF,0x1B,0x76,0x75,0x16,0x03,0x9B};
//------------------------------------------------------------------------------
// File system object.
SdFat sd;

// Log file.
SdFile file;

// Time in micros for next data record.
uint32_t logTime;

//==============================================================================
// User functions.  Edit writeHeader() and logData() for your requirements.

const uint8_t ANALOG_COUNT = 4; // this seems to control the number of times the logtime is written before the data????


//------------------------------------------------------------------------------
// Write data header.   // non of this works!!!

 void writeHeader() {
  file.print(F("micros"));
  for (uint8_t i = 0; i < ANALOG_COUNT; i++) {
    file.print(F(",Reading"));
    file.print(i, DEC);
  }
  file.println();
}

 


 
//------------------------------------------------------------------------------
// Log a data record.
void logData() 
{
  int h=dht.readHumidity();
  int t=dht.readTemperature();
  sensors.requestTemperatures(); // Send the command to get temperatures
  photocellReading=analogRead(photocellPin);
 

 if (isnan(t)||isnan(h))
 {Serial.print ("Failed to read from DHT");}
 else


 Serial.println (logTime/1000000); // reduce time in millis to a manageable number
 Serial.println(photocellReading); // print analogue light cell output
  Serial.println(sensors.getTempC(Probe)); // print temp from DS18B20
  Serial.println(h);  // print humidity
  Serial.println(t);  // print temp from humidity sensor
  // Read all channels to avoid SD write latency between readings.
 // for (uint8_t i = 0; i < ANALOG_COUNT; i++) 
   
  
  // Write data to file.  Start with log time in micros.
   file.print(logTime/1000000);
  file.write(',');  // comma  separation for csv file
  file.print(photocellReading/10);
  file.write(',');
  file.print(sensors.getTempC(Probe));
  file.write(',');
  file.print(h);
  file.write(',');
  file.print(t);


  // Write ADC data to CSV record.
 
  file.println();
  }
 

//==============================================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))
//------------------------------------------------------------------------------
void setup()
  {Serial.begin(9600);
  dht.begin();
  sensors.begin();

  const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
  char fileName[13] = FILE_BASE_NAME "00.csv";


  
  // Wait for USB Serial 
  while (!Serial) {
    SysCall::yield();
  }
  delay(1000);

  Serial.println(F("Type any character to start"));
  while (!Serial.available()) {
    SysCall::yield();
  }
  
  // Initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!sd.begin(chipSelect, SPI_HALF_SPEED)) {
    sd.initErrorHalt();
  }

  // Find an unused file name.
  if (BASE_NAME_SIZE > 6) {
    error("FILE_BASE_NAME too long");
  }
  while (sd.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
      fileName[BASE_NAME_SIZE + 1]++;
    } else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } else {
      error("Can't create file name");
    }
  }
  if (!file.open(fileName, O_CREAT | O_WRITE | O_EXCL)) {
    error("file.open");
  }
  // Read any Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  Serial.print(F("Logging to: "));
  Serial.println(fileName);
  Serial.println(F("Type any character to stop"));

  // Write data header.


  // Start on a multiple of the sample interval.
  logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
  logTime *= 1000UL*SAMPLE_INTERVAL_MS;
}
//------------------------------------------------------------------------------
void loop() {
  // Time for next record.
  logTime += 10000UL*SAMPLE_INTERVAL_MS;

  // Wait for log time.
  int32_t diff;
  do {
    diff = micros() - logTime;
  } while (diff < 0);

  // Check for data rate too high.
  if (diff > 10) {
    error("Missed data record");
  }

  logData();

  // Force data to SD and update the directory entry to avoid data loss.
  if (!file.sync() || file.getWriteError()) {
    error("write error");
  }

  if (Serial.available()) {
    // Close file and stop.
    file.close();
    Serial.println(F("Done"));
    SysCall::halt();
  }
}

Also attached is the Fritzing sketch which is very much a test bed.

Any help would be appreciated

I have tried using library sketches such as RTClib and HCRTC with no success.

With, or without, the requisite RTC?

If you have an RTC, the problem is trivial. If you don't, well, the Arduino has no idea what time it is. It only knows how long it has been running. Which, as you've noted, is useless information.

You appear to be using a DS1307 RTC, but perhaps you are merely calling it and not actually using it. The code appears to be junk from end to end.

Below is one that does much the same and reads a DS1037
http://homepages.ihug.com.au/~npyner/Arduino/Bluetooth_graphic_MINMAX.ino
Strip out what you don't need and add your humidity stuff. Note that the loop is one second but the SD writes are only every ten seconds

The DS1307 isn't that flash for long-time use but, if the environment is reasonably stable, it might be OK. If not, get a DS3231. There is no need to change the code if you do that.

Nick
Thanks for the prompt response.
I have got the sketch to compile after a couple of minor corrections.
After adding a correct DS18B20 address it is printing data to the serial monitor.
However it is not writing to the SD card.
I obviously need to study it and try to understand its content and components.
Perhaps I will start by getting a Nokia 5110 as well.
Thanks again.

If you can send data to serial, you should have no trouble sending it to SD. The procedure is more or less the same, as is shown by the subroutines.

Ensure the SD is formatted in the proper manner. There is a sticky around here about that.

Since there is nobody around to read the 5110, I don't suppose there is any rush to use one, but I find them great, as they provide six lines of data. Avoid the ones promoted as having "cool blue" backlights, there is nothing cool about them at all, and even if you have one with an ordinary white light, you only need it in the dark.

Well after some slight mods I have confirmed that the time stamp is working with this sketch.

The time stamp is not very accurate which I suspect is down to the DS1307 chip on the deek-robot shield I am using.
So far I have not been able to source the version B shield with the alternative chip.

I had great difficulty getting the sketch to initiate the SD card. To cut a long story short I went to the SdFat library which suggested replacing

#include<SD.h> with

#Include(SdFat.h>
SdFat SD;

after changing 4 to 10 in this line
const int chipSelect = 10;

Now to build the sensor inputs

Thanks again

the card initialized and was written to.

The DS1307 has poor or non existent temperature compensation but it is really OK for most purposes if it is in a stable environment. The DS3231 is only a dollar or so more, and I now use them exclusively, but they are a lot harder to accommodate. One big problem is the battery holder, for chrissakes.

If you have the SD working, fine, but I'm sure changing libraries was not the real solution - something else happened along the way. I have never seen a good reason for using SDFat, and every time I see someone having grief with it on this forum, I see a good reason for not using it.

Following the helpful advice from Nick Pyner I replaced the SD logger shield with the newer version using the 8523 clock chip and immediately got stable time stamps. I still had to use the SDfat library to communicate with the SD though.
Next step was to integrate BMP180 atmos press device into the code. After four days I have it working as I want apart from being able to achieve a date stamped file name on the SD file.
The code( based on the sketch recommended by Nick) worked OK with the DS 1307 based shield but just returns 00000.csv as the file name when copied into the new sketch.

My sketch is too long to include here in total but these seem to be the relevant lines

#include <Wire.h>
#include "SdFat.h"
SdFat SD;
#include <DHT.h>
#include <SFE_BMP180.h>
#include <RTClib.h>
RTC_PCF8523 rtc;
#define PCF8523_ADDRESS 0x68
#define ALTITUDE 123.0

#define DHTPIN 8                //assign Humidity / Temp sensor to pin 8
#define DHTTYPE DHT11          // describe sensor type
 DHT dht(DHTPIN,DHTTYPE);       //???????

char filename[] = "00000000.CSV";
File myFile;
char dumpName[] = "00000000.CSV";
File dumpFile;

byte  second, minute, hour, weekDay, day, month, year;
int k=0;

and

void getFileName(){
sprintf(filename, "%02d%02d%02d.csv", year, month, day);
}

void GetClock(){
  // Reset the register pointer
  Wire.beginTransmission(PCF8523_ADDRESS);
  byte zero = 0x00;
  Wire.write(zero);
  Wire.endTransmission();
  Wire.requestFrom(PCF8523_ADDRESS, 7);

  second = bcdToDec(Wire.read());
  minute = bcdToDec(Wire.read());
  hour = bcdToDec(Wire.read() & 0b111111); //24 hour time
  weekDay = bcdToDec(Wire.read()); //0-6 -> sunday - Saturday
  day = bcdToDec(Wire.read());
  month = bcdToDec(Wire.read());
  year = bcdToDec(Wire.read());
}

void getDump()

I have simply replaced

#define DS1307_ADDRESS 0x68
with
#define PCF8523_ADDRESS 0x68

I have to admit I really do not understand the code so please can somebody help me.

My sketch is too long to include here

Really? Up to 1M byte attachments are allowed. I can't imagine a 1M+ byte file compiling to fit on any Arduino.

Yep
The preview told me I had used my 9000 characters ( or some similar figure).
Like jumping through hoops using this forum sometimes.

Sedbergh:
Yep
The preview told me I had used my 9000 characters ( or some similar figure).
Like jumping through hoops using this forum sometimes.

Nonsense. The instructions are in the first three posts in this section of the forum, and in the first 1 to 3 in other sections.

Sedbergh:
Yep
The preview told me I had used my 9000 characters ( or some similar figure).
Like jumping through hoops using this forum sometimes.

You are right - and there is really no need for another hoop to jump through.
There are a number of options

The best is to reduce the code to that which is relevant i.e. involves the problem and should work, but doesn't. This means a bit of work on your part. Just showing a snippet might be OK, but usually isn't.

You can attach the full bottle as a text file. The 1Mb limit is more than you will ever need.

You can post it as code in several posts. I believe it is possible to to do several codes in one post.

For God's sake, don't insult people by posting it as a *.ino file, or post a link into some obscure site in the cloud. I have seen people refer to some site that requires registration and password. I guess they are still wondering why nobody bothers to look at it.

I have never known a good reason to use SDFat but, if it works, you don't have a good reason to use SD.
When I switched to the DS3231, there was no change to the DS1307 code. It still says
#define DS1307_ADDRESS 0x68
I don't know anything about the 8523, but it wouldn't surprise me if the same applies. You should be able test what you are doing with a simple clock programme that sends filenames to the serial monitor.

Nick. Thanks for your considered reply.
Regarding the chip, I had great difficulty sourcing the Rev B version of the logger shield here in the UK ( at least at a sensible price). I was surprised when it arrived with the 8523 chip but at least it works after replacing
#define DS1307_ADDRESS 0x68
with
#include <RTClib.h>
RTC_PCF8523 rtc;
and of course adding SDfat.h
Anyway, regarding my main problem I will take your advice and strip out everything which is not relevant to the file naming process. Much of the present code is about writing to the serial port to enable me to spot what is going on easily. I should be able to reduce the size to one third when I might be able to post it.
Thanks again.

Nick.
I did what you recommended. I progressively stripped all the code out of the original sketch, testing the effect at each step, until I was left with only that which assigned the date stamped filename. I then ensured that all the lines which remained were present in my own sketch. It worked!! Brilliant.
Thanks once again for guiding me to a solution.
Peter

Good. I bet it was a constructive exercise!

Hello!

I need some help in the subject.
I have an RTC, an SD card module, the situattion is the same as mentioned above, I would like to save analog readings to the card with a time stamp like;

2018.05.12 - 21:15 /and the measured value/

My problem is simly with coding.
I'm using SDFat library. I can output the date and time to serialmonitor like this:

void printDateTime(time_t t)
{
    cout << (year(t)) << ' ';
    cout << monthShortStr(month(t)) << ' ';
    cout << ((day(t)<10) ? "0" : "") << day(t) << ' ';
    cout << ((hour(t)<10) ? "0" : "") << hour(t) << ':';
    cout << ((minute(t)<10) ? "0" : "") << minute(t) << ':';
    cout << ((second(t)<10) ? "0" : "") << second(t)<<endl;
}

I already have the time from RTC in "t" so everything is fine. However I'm wondering what is the best way to write to the file.

1, like this?

file.print(year(t));
file.priint(monthShortStr(..... etc

  1. or is there a way like cout << ?

Sorry, probably is quite a dumb question, this cout << thing is new to me and I'm still trying to understand how does it work.

Please advise.

Just to clarify myself

I've tried this:

void writeDateTime(time_t t, File file)
{
    file.print(year(t));
    file.print(" ");
    file.print(monthShortStr(month(t)));
    file.print(" ");
    file.print((day(t)<10) ? "0" : "");
    file.print(day(t));
    file.print(" ");
//    cout << ((hour(t)<10) ? "0" : "") << hour(t) << ':';
//    cout << ((minute(t)<10) ? "0" : "") << minute(t) << ':';
//    cout << ((second(t)<10) ? "0" : "") << second(t)<<endl;
}
        if (file) {
          Serial.print("Writing to meresek.txt...");
          writeDateTime;
          file.print("          ");
          file.println(mert);         
          // close the file:
          file.close();
          Serial.println("done.");
        } else {
          // if the file didn't open, print an error:
          Serial.println("error opening meresek.txt");
        }

but I got this as a result in the file:


4.31
4.32


So just this two rows:

file.print(" ");
file.println(mert);

however there's the function called before these.

%%!@&

Yeah I, know. sorry for that. THe answer:

writeDateTime(t,file)

However I'm still interested in the first question:

How to change the file.print lines to the cout << method in a proper way?