SD card always displays a common Date Modified

Jeff,

I’ve attempted to follow the instructions in your previous post.

etc.

Latest version of the sketch:

t#include "SD.h"  // SD card library
#include "Wire.h"  // I2C
#include "Time.h"  // Time Manipulation
#include "DS1307RTC.h"  // DS1307 RTC

File file;  // test file
const uint8_t SD_CS = 10; // SD chip select

 char timestamp[30];

//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {

 sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n", tm.Hour(),tm.Minute(),tm.Second(),tm.Month(),tm.Day(),tm.Year()-2000);
 Serial.println("yy");
 Serial.println(timestamp);
 // return date using FAT_DATE macro to format fields
 *date = FAT_DATE(tm.Year(), tm.Month(),tm.Day());

 // return time using FAT_TIME macro to format fields
 *time = FAT_TIME(tm.Hour(),tm.Minute(),tm.Second());
}

// constants won't change. They're used here to set pin numbers:
const int SWITCH_PIN = 2;       // the number of the microswitch pin
char timedatebuf[65];  // Time and Date string buffer
int year4digit;  // 4 digit year
char shutterStatus[24];

void setup()
{
  Serial.begin(9600);  // Serial monitor used for testing
  // Micro switch code
  pinMode (SWITCH_PIN, INPUT_PULLUP);
  Serial.println("Micro switch project");
  pinMode(10, OUTPUT);
  
  if (!SD.begin(10)) {  // check if card is installed
    Serial.println("No SD Card present in module");
    return;
  }
 // set date time callback function
 SdFile::dateTimeCallback(dateTime);

  Serial.println("SD Card Ready");
}

void loop()
{
  tmElements_t tm;
  // Micro switch code
  int microSwitch = digitalRead(SWITCH_PIN);
  if (microSwitch == LOW)
  {
    strcpy(shutterStatus, "Shutters Open");
  }
  else
  {
    strcpy(shutterStatus, "Shutters Closed");
  }
  if (RTC.read(tm))   // Get Time/Date from RTC1307
  {
    year4digit = tm.Year + 1970;  // 4 digit year variable

    // Format Time & Date string in timedatebuf
    sprintf(timedatebuf, "%s Time: %02d:%02d:%02d   Date:  %02d/%02d/%02d", shutterStatus, tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, year4digit);

    File dataFile = SD.open("SHUTTERS.txt", FILE_WRITE);  // Open or Create file
    if (dataFile)   // Check if file exist on SD Card
    {
      dataFile.println(timedatebuf);
      dataFile.close();  // Close file
      Serial.println(timedatebuf);
    }
    else
    {
      Serial.println("error opening shutters.txt"); // if file not on SD Card
    }
  }
  delay(1000);
}

When I compile it, Arduino generates an error report relative to three lines in the code: 15, 19 and 22. Each error, following the position reference, is described thus: error: expected primary-expression before ‘.’ token referring to Google, I have not found any clear direction as to the amendment required to enable compilation.

Finally, at the end of the error report it states the following:
exit status 1

Compilation error: expected primary-expression before ‘.’ token

2 Likes

Hi All,

Thanks for your amazing work Jeff, and a very interesting dive into file-management in Arduino.

A quick question Chris, is it absolutely necessary that the time in Windows is correct? I’ve found if you attach a timestamp to when data is read you can form nice graphs in software after the fact.

e.g. your filewriter would output a CSV in the format of:
xxxx [Temperature], xxx [Humidity], xxx [Timestamp]

This can then be loaded into python/excel and processed or viewed.
Would something like this work for your project?
Liam

2 Likes

One difference between DS1307RTC and RTCLib is that DS1307RTC references the time values as fields while RTCLib references them as functions. So for RTCLib functions it is
*date = FAT_DATE(tm.Year(), tm.Month(),tm.Day());
and for DS1307RTC fields the code is
*date = FAT_DATE(tm.Year, tm.Month, tm.Day);

This has to happen everywhere they are referenced. In the second line of code I posted yesterday you can see where I missed one of those changes - a good clue to the problem :wink:.

Also you haven’t created your datetime object in the callback routine - note what I said before about variables in the callback routine being quite distinct from those in the main loop - you must declare and initialise them both. This compiles with DS1307RTC:

// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
  tmElements_t tm;
  sprintf(timestamp, "%02d:%02d:%02d %2d/%2d/%2d \n", tm.Hour, tm.Minute, tm.Second, tm.Month, tm.Day, tm.Year - 2000);
  Serial.println("yy");
  Serial.println(timestamp);
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(tm.Year, tm.Month, tm.Day);

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(tm.Hour, tm.Minute, tm.Second);
}

/edit
That now compiles but it doesn’t update the file date!. Based on comments I have been able to uncover there appears to have been a change to the SD library that removed the callback. However the removal must be incomplete because the code to set it compiles OK, and the callback executes. The suggestion is that a change is required to the library header to restore the function. I might not have time to look at this until Monday (certainly not before late Friday), so you might prefer to put this on the back burner until then.
The alternative of SDFat seems far too complex to contemplate.
edit/

2 Likes

Jeff,

Thank you for your advice, above. I too have found that the sketch compiles and captures the desired data, both on the Serial Monitor and the SD card, BUT there’s no Date Modified information on my SD card, as viewed in my File Explorer. I understand this is the component which you’ve consigned to “work in progress”.

The timeline you’ve mentioned for - hopefully, a resolution to this - is fine by me; I’m very grateful for your support.

In an effort to properly comprehend this paragraph (quoted above), I’ve explored many avenues, which have included discussion on various related topics, all of which is good education for me. However, the correct interpretation of your quoted paragraph - immediately, in the context of the subject sketch - remains unclear to me.

2 Likes

Thanks for your commentary, Liam.

The sketch as it stands captures the desired events (switch open or closed) and related date/time periods. I don’t understand how your suggested solution improves on that.

The unresolved issue in the subject of this thread is creating a Date Modified label on the respective files so that each file can be identified as such.

The current version of the sketch has eliminated the Date Modified record which, for each file in previous iterations, recorded the common date: 1/1/2000. I understand that is quite common.

2 Likes

Hi Chris,

Sorry I completely missed the snippet of code above!
The over-arching message of my post was, is it absolutely necessary to have the date modified field in windows update?

For a raw .TXT file a lot of metadata can be attached in other faster to implement ways, for example in the filename or inside the file itself (as you have already implemented).
e.g. a filename with the date it was created - `events-18/10/23.txt’
Then the last record in the list will show when the last modification happened

Just a thought instead of heading down the rabbit hole of making compliant meta-data for Windows OS.
Liam

1 Like

The code
tmElements_t tm;
creates a variable tm that refers to a structure that is populated by the time library. That variable has a scope, determined by where in your code it gets executed. The scope of a variable determines which parts of your code can use that variable. In this case the line is executed inside the dateTime() function, so the variable tm has a scope that is restricted to that function. The same line of code executed in the loop() function also creates a variable that refers to a structure in the library. That variable has a scope of the loop() function. To use a statement such as
*date = FAT_DATE(tm.Year, tm.Month, tm.Day);
to get the time or date values from the structure you need a variable tm that is within scope. That’s why you need to declare the variable within each function. That they happen to have the same name is convenient, but possibly confusing. An alternative would be to declare a variable for the structure once at the global level - then its scope would cover all functions in the sketch. But it wouldn’t automatically update at the start of each function and you would have to include code that ensured the structure it refers to was updated with the current time.

2 Likes

Jeff, thank you for taking the time to explain this element of the sketch in such detail.

I have spent some time familiarising myself with the concepts outlined in each line of this, your most recent post, and I’m pleased with my efforts to comprehend them. The value of your written word is that I can run over it repetitively and each time the components become clearer for me.

To those of you conversant with Arduino language and construction, I guess this is analogous to the English language for someone who has been taught - and spoken - it for many years. I am familiar with English, but Arduino remains far from intuitive to me and I work constantly with the (rather concise) Arduino Language Reference and Google at my side. For me, it’s a steep learning curve!

1 Like

OK. The answer is actually in the last response above, as prompted by your query, but I didn’t see it at the time.

An alternative would be to declare a variable for the structure once at the global level - then its scope would cover all functions in the sketch. But it wouldn’t automatically update at the start of each function …

The declaration does not invoke an update (contrary to my assumption), the update (read) needs to be executed whenever a new time is required, and the declaration can be moved to the global level because it doesn’t have any side effects.

This code works.

#include "SD.h"  // SD card library
#include "Wire.h"  // I2C
#include "Time.h"  // Time Manipulation
#include "DS1307RTC.h"  // DS1307 RTC

File file;  // test file
const uint8_t SD_CS = 10; // SD chip select

char timestamp[30];
tmElements_t tm;

//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
  RTC.read(tm);
  sprintf(timestamp, "Callback: %02d:%02d:%02d %2d/%2d/%2d \n", tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
  Serial.println(timestamp);
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(tm.Year + 1970, tm.Month, tm.Day);

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(tm.Hour, tm.Minute, tm.Second);
}

// constants won't change. They're used here to set pin numbers:
const int SWITCH_PIN = 2;       // the number of the microswitch pin
char timedatebuf[65];  // Time and Date string buffer
int year4digit;  // 4 digit year
char shutterStatus[24];

void setup()
{
  Serial.begin(9600);  // Serial monitor used for testing
  // Micro switch code
  pinMode (SWITCH_PIN, INPUT_PULLUP);
  Serial.println("Micro switch project");
  pinMode(10, OUTPUT);

  if (!SD.begin(10)) {  // check if card is installed
    Serial.println("No SD Card present in module");
    return;
  }
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);

  Serial.println("SD Card Ready");
}

void loop()
{
  // Micro switch code
  int microSwitch = digitalRead(SWITCH_PIN);
  if (microSwitch == LOW)
  {
    strcpy(shutterStatus, "Shutters Open");
  }
  else
  {
    strcpy(shutterStatus, "Shutters Closed");
  }
  if (RTC.read(tm))   // Get Time/Date from RTC1307
  {
    year4digit = tm.Year + 1970;  // 4 digit year variable

    // Format Time & Date string in timedatebuf
    sprintf(timedatebuf, "%s Time: %02d:%02d:%02d   Date:  %02d/%02d/%02d", shutterStatus, tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, year4digit);

    File dataFile = SD.open("SHUTTERS.txt", FILE_WRITE);  // Open or Create file
    if (dataFile)   // Check if file exist on SD Card
    {
      dataFile.println(timedatebuf);
      dataFile.close();  // Close file
      Serial.println(timedatebuf);
    }
    else
    {
      Serial.println("error opening shutters.txt"); // if file not on SD Card
    }
  }
  delay(3000);
}
1 Like

Jeff, That’s great, thank you very much!

I’ve run the code on two Arduino UNOs, each with different RTC and SD card arrangements.

Configuration 1 - has a DS1307 RTC module and Jaycar XC4386 SD card module; and
Configuration 2 - comprises another UNO paired with a recently-acquired Adafruit Data Logger Shield (with inbuilt RTC and SD card components).

Both Configurations reflected Shutter position correctly and the correct Date Modified in File Explorer. Configuration 1 also captured the date and time sequence correctly, although I need to fine-tune the ‘set time’ more accurately on the RTC module.

Configuration 2 appeared to capture the time at which I re-set its RTC, but the time didn’t alter from then on. Immediately after re-setting the RTC, the correct date was captured, but then the date advanced by one day at roughly 2 minute intervals. My Adafruit Data Logger has the PCF8523 RTC (rather than a DS1307) and they recommend setting it with the RTClib-pcf8523 sketch, which I’ve done, but I’m finding it troublesome with other sketches as well as this one.

But for the frustrations with the Adafruit Data Logger Shields, this is a great result.

Thank you again for so generously assisting me.

1 Like

If the time setting is consistently behind the actual time by several seconds then that indicates that the time is being set to the compilation time of the sketch - this is sometimes used as a default setting when the sketch thinks that the device has been reset. It will be a few days before I will be able to check whether or not I have a PCF8523 to test, but a quick look at the example code suggests there is no significant difference between the two devices in terms of actually accessing the time and date. Is the (incorrect) time and date the same in the console display and the file date modified?

1 Like

Thanks for the follow-up, Jeff.

Yes, with the UNO and the Adafruit Data Logger Shield, the (incorrect) date and time is the same on the Serial Monitor and on the SD card. I ran this again a short while ago to confirm that statement and I note that Date Modified in File Explorer is now blank.

I also ran Examples/RTClib/pcf8523 before and after the shutter sketch and the date and time were both correct. I don’t know whether it’s significant, but the delay in pcf8523 is 2000, yet the time intervals between the date/times displayed in the Serial Monitor are typically 3 seconds.

1 Like

If you have a delay(2000) statement in the loop then the time to execute each iteration of the loop will be 2s plus however long the rest of the code takes to execute, including the code that executes inside any libraries. This could easily be long enough for the seconds counter from the clock to increment by 3s for many (but presumably not all) of the loops. If the seconds counter is correct over a longer period, such as several minutes, then the seconds display per loop iteration can be ignored.

Jeff, as my sketch still doesn’t run as desired on the Adafruit Data Logger Shield due, apparently, to conflict between the sketch’s libraries and the PCF8523 RTC, I wonder whether you’ve had a chance to look for your PCF8523. If you don’t have/can’t find one, I would be happy to post you one of my Adafruit Shields, with a reply-paid postage bag (for return), if that would assist, and is acceptable to you.

I don’t have a PCF8523. A good starting point for debugging would be to post the code you are using with the shield and an example of the data you are using to set the time and the results you are getting from it.

I presume that you suspect a conflict in the libraries because the sketch that works OK doesn’t include any other libraries. If I examine the code you are using I may be able to see the conflict without the need to install the device.

Thanks, Jeff.

This is the code I am using…

#include "SD.h"  // SD card library
#include "Wire.h"  // I2C
#include "Time.h"  // Time Manipulation
#include "DS1307RTC.h"  // DS1307 RTC

File file;  // test file
const uint8_t SD_CS = 10; // SD chip select

char timestamp[30];
tmElements_t tm;

//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
  RTC.read(tm);
  sprintf(timestamp, "Callback: %02d:%02d:%02d %2d/%2d/%2d \n", tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
  Serial.println(timestamp);
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(tm.Year + 1970, tm.Month, tm.Day);

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(tm.Hour, tm.Minute, tm.Second);
}

// constants won't change. They're used here to set pin numbers:
const int SWITCH_PIN = 2;       // the number of the microswitch pin
char timedatebuf[65];  // Time and Date string buffer
int year4digit;  // 4 digit year
char shutterStatus[24];

void setup()
{
  Serial.begin(9600);  // Serial monitor used for testing
  // Micro switch code
  pinMode (SWITCH_PIN, INPUT_PULLUP);
  Serial.println("Micro switch project");
  pinMode(10, OUTPUT);

  if (!SD.begin(10)) {  // check if card is installed
    Serial.println("No SD Card present in module");
    return;
  }
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);

  Serial.println("SD Card Ready");
}

void loop()
{
  // Micro switch code
  int microSwitch = digitalRead(SWITCH_PIN);
  if (microSwitch == LOW)
  {
    strcpy(shutterStatus, "Shutters Open");
  }
  else
  {
    strcpy(shutterStatus, "Shutters Closed");
  }
  if (RTC.read(tm))   // Get Time/Date from RTC1307
  {
    year4digit = tm.Year + 1970;  // 4 digit year variable

    // Format Time & Date string in timedatebuf
    sprintf(timedatebuf, "%s Time: %02d:%02d:%02d   Date:  %02d/%02d/%02d", shutterStatus, tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, year4digit);

    File dataFile = SD.open("SHUTTERS.txt", FILE_WRITE);  // Open or Create file
    if (dataFile)   // Check if file exist on SD Card
    {
      dataFile.println(timedatebuf);
      dataFile.close();  // Close file
      Serial.println(timedatebuf);
    }
    else
    {
      Serial.println("error opening shutters.txt"); // if file not on SD Card
    }
  }
  delay(2000);
}

and this is a sample of what’s being displayed on the Serial Monitor.

Shutters Closed Time: 08:00:00 Date: 20/09/2001
Callback: 08:00:00 20/ 9/2001

Shutters Closed Time: 08:00:00 Date: 20/09/2001
Callback: 08:00:00 20/ 9/2001

I attach data captured on the SD card this morning, having just run the sketch with the Adafruit Shield for a short while. You will note that the time doesn’t change, but the date advances by a day at seemingly regular intervals.
Adafruit test 01-02-2023 - SD card.pdf (54.5 KB)

What example are you following for the shield code? The example I found uses RTCLIB, not DS1307RTC

Code Walkthrough | Adafruit Data Logger Shield | Adafruit Learning System

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

Thank you, Jeff.

Further to your reference, I have revisited the linked material on the Core Electronics website relative to the Adafruit Data Logger and have run the RTC and SD card codes provided in that material. On that basis, my Adafruit Data Logger / UNO pairing seems fine. Correct time (within a few seconds) and no errors.

I then amended my ‘Shutters’ sketch by deleting the Time.h and DS1307RTC.h libraries and added the RTClib.h library.

This is that sketch:

#include "SD.h"  // SD card library
#include <Wire.h>  // I2C

#include "RTClib.h"  // DS1307 RTC

File file;  // test file
const uint8_t SD_CS = 10; // SD chip select

char timestamp[30];
tmElements_t tm;

//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
  RTC.read(tm);
  sprintf(timestamp, "Callback: %02d:%02d:%02d %2d/%2d/%2d \n", tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year + 1970);
  Serial.println(timestamp);
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(tm.Year + 1970, tm.Month, tm.Day);

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(tm.Hour, tm.Minute, tm.Second);
}

// constants won't change. They're used here to set pin numbers:
const int SWITCH_PIN = 2;       // the number of the microswitch pin
char timedatebuf[65];  // Time and Date string buffer
int year4digit;  // 4 digit year
char shutterStatus[24];

void setup()
{
  Serial.begin(9600);  // Serial monitor used for testing
  // Micro switch code
  pinMode (SWITCH_PIN, INPUT_PULLUP);
  Serial.println("Micro switch project");
  pinMode(10, OUTPUT);

  if (!SD.begin(10)) {  // check if card is installed
    Serial.println("No SD Card present in module");
    return;
  }
  // set date time callback function
  SdFile::dateTimeCallback(dateTime);

  Serial.println("SD Card Ready");
}

void loop()
{
  // Micro switch code
  int microSwitch = digitalRead(SWITCH_PIN);
  if (microSwitch == LOW)
  {
    strcpy(shutterStatus, "Shutters Open");
  }
  else
  {
    strcpy(shutterStatus, "Shutters Closed");
  }
  if (RTC.read(tm))   // Get Time/Date from RTC1307
  {
    year4digit = tm.Year + 1970;  // 4 digit year variable

    // Format Time & Date string in timedatebuf
    sprintf(timedatebuf, "%s Time: %02d:%02d:%02d   Date:  %02d/%02d/%02d", shutterStatus, tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, year4digit);

    File dataFile = SD.open("SHUTTERS.txt", FILE_WRITE);  // Open or Create file
    if (dataFile)   // Check if file exist on SD Card
    {
      dataFile.println(timedatebuf);
      dataFile.close();  // Close file
      Serial.println(timedatebuf);
    }
    else
    {
      Serial.println("error opening shutters.txt"); // if file not on SD Card
    }
  }
  delay(3000);
}

It failed to compile, returning the following errors:

10:1: error: ‘tmElements_t’ does not name a type
tmElements_t tm;
^~~~~~~~~~~~

In function ‘void dateTime(uint16_t*, uint16_t*)’:

15:3: error: ‘RTC’ was not declared in this scope
RTC.read™;
^~~

15:12: error: ‘tm’ was not declared in this scope
RTC.read™;
^~
15:12: note: suggested alternative: ‘time’
RTC.read™;
^~
time

In function ‘void loop()’:
61:7: error: ‘RTC’ was not declared in this scope
if (RTC.read™) // Get Time/Date from RTC1307
^~~

61:16: error: ‘tm’ was not declared in this scope
if (RTC.read™) // Get Time/Date from RTC1307
^~
exit status 1
Compilation error: ‘tmElements_t’ does not name a type

I then ran the same code - using an Arduino UNO and separate DS3231RTC (not a DS1307RTC) and micro SD Card Shield - with the following libraries (in this order): SD.h, Wire.h, Time.h and DS1307RTC.h. It compiled and ran correctly.

Do you mean this one:
Overview | Adafruit Data Logger Shield | Adafruit Learning System
The problem is that there are two versions of the datalogger, and that description includes only a brief passing reference to the PCF8523.

You can’t switch libraries without also adjusting the code to match the library you are using. As a start, when you create the clock object you need to indicate to the library the device it is being asked to manage. This is a global declaration.

RTC_PCF8523 rtc;

Then you have to start it (in setup())

    rtc.start;

In the loop and the callback, instead of creating a tmElements_t structure and using a read() method to populate it, you will call the now() function of the library to create and initialize a dateTime object, and access the required data as properties of that object.

    DateTime now = rtc.now();

    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    <etc>    

Note that the ‘now’ variable in your program and the ‘now()’ method of the library are two different things - naming them the same is very confusing, but is common practice for C.

Jeff,

Thank you for your last post (above).

I agree, the information on the PCF8523 RTC in the above resource is not very helpful.

Thanks to your guidance, I have, however created a code (below), which compiles and produces sensible information in both Serial Monitor and SD card formats.

#include <SPI.h>
#include <SD.h>  // SD card library
#include <Wire.h>  // I2C
#include <TimeLib.h>
#include <RTClib.h>

File file;  // test file
const uint8_t SD_CS = 10; // SD chip select
RTC_PCF8523 rtc;  // define the Real Time Clock object
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};  
 char timestamp[30];

//------------------------------------------------------------------------------
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
 DateTime now = rtc.now();   //RTC amended to rtc
 sprintf(timestamp, "Callback: %02d:%02d:%02d %2d/%2d/%2d \n", now.hour(),now.minute(),now.second(),now.month(),now.day(),now.year()-2000);

 Serial.println(timestamp);
 // return date using FAT_DATE macro to format fields
 *date = FAT_DATE(now.year(), now.month(), now.day());

 // return time using FAT_TIME macro to format fields
 *time = FAT_TIME(now.hour(), now.minute(), now.second());
}

// constants won't change. They're used here to set pin numbers:
const int SWITCH_PIN = 2;       // the number of the microswitch pin
char timedatebuf[65];  // Time and Date string buffer
int year4digit;  // 4 digit year
char shutterStatus[24];

void setup()
{
  Serial.begin(9600);  // Serial monitor used for testing
  rtc.begin();  //previous version of code: rtc.start(); created an error (conflict with line 413 of RTClib) 
  // Micro switch code
  pinMode (SWITCH_PIN, INPUT_PULLUP);
  Serial.println("Micro switch project");
  pinMode(10, OUTPUT);
 // line 54 moved from here in previous version
  if (!SD.begin(10)) {  // check if card is installed
    Serial.println("No SD Card present in module");
    return;
  }

  Serial.println("SD Card Ready");
}

void loop() 
{
  // set date time callback function
 SdFile::dateTimeCallback(dateTime);
    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");

  // Micro switch code
  int microSwitch = digitalRead(SWITCH_PIN);
  if (microSwitch == LOW)
  {
    strcpy(shutterStatus, "Shutters Open");
  }
  else
  {
    strcpy(shutterStatus, "Shutters Closed");
  }
  // deleted as per error at 80:12
  {
    year4digit = now.year();  // 4 digit year variable (previous version of code – now.year() + 1970; - caused year to print 1970 years higher than current year 

    // Format Time & Date string in timedatebuf
    sprintf(timedatebuf, "%s Time: %02d:%02d:%02d   Date:  %02d/%02d/%02d", shutterStatus, now.hour(), now.minute(), now.second(), now.day(), now.month(), year4digit);
    
    File dataFile = SD.open("SHUTTERS.txt", FILE_WRITE);  // Open or Create file
    if (dataFile)   // Check if file exist on SD Card
    {
      dataFile.println(timedatebuf);
      dataFile.close();  // Close file
      Serial.println(timedatebuf);
    }
    else
    {
      Serial.println("error opening shutters.txt"); // if file not on SD Card
    }
  }
  delay(1000);   // altered to 1 sec. to capture rapid open-close
}

I found that altering the line of code rtc.start; to rtc.begin; (in the setup loop) overcame an error attributed to line 413 of the RTClib file (see attachment below) and I also deleted the + 1970 component of the 4 digit year variable in the loop, as it was otherwise returning a date 1970 years hence.

At this stage, I’m happy with the way this sketch is working on the Adafruit Data Logger Shield. I appreciate your persistence in helping me with this refinement of my previous sketch because, in my application (under an EV bonnet), the compactness of the UNO board and attached Shield is of great benefit.
RTClib_line_413.pdf (99.1 KB)