tm.Wday returns the wrong day of the week

Today is Sunday, Aug 13. 2023. tm.Wday should return 0 for sunday.

I programmed my watering system to start an hour later on weekends, but noticed it still came on at 0700.

I added Serial.prints (line 153-169) to see it what is going on here. The Serial prints return:
13:8:2023:4 <-- So tm has the correct date, but wrong weekday.
13:8:2053:4 <-- this other way of returning time does time travel. It is also wrong though as 13.Aug 2053 will be a wednesday.

What am I doing wrong here?

/* 
=======================
Watering system control
=======================
- 5 watering zones garden
- 24VAC solenoid valves
- generic 8 channel relay board
- watering suppressed when raining.
- Manual switch to increase watering during heatwave
*/

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>

#define i2c_Address 0x3c  //initialize with the I2C addr 0x3C Typically eBay OLED's
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels
#define OLED_RESET -1     //   QT-PY / XIAO
Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);



bool HeatWave = LOW;                      // initialise for normal weather
const int PlannedHour = 7 * 60;           //initialise with normal start time 7 AM
const int BonusStartHour = 20 * 60;       // If needed a second watering in the evening at 20:00
const int nZones = 5;                     // five zone watering system.
const unsigned long ZoneTime = 600000ul;  // default watering time for each zone (= 10 min)
String words;
int StartHour = PlannedHour;
int currentTime;
unsigned long ZoneDuration = ZoneTime;
int PumpPin = 7;
int RainPin = 9;
int HeatWavePin = 10;
int ZonePins[] = {
  2,
  3,
  4,
  5,
  6,
};

/* 
Need to initialise the serial interface, for use with the Serial monitor for trouble shooting, and also to interface with the RTC.
Otherwise just set up the pins
*/
void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;  // wait for serial
  delay(200);
  display.begin(i2c_Address, true);  // Address 0x3C default
  //display.setContrast (0); // dim display
  display.display();
  delay(2000);
  // Clear the buffer.
  display.clearDisplay();
  // text display tests
  display.setTextSize(2);
  display.setTextColor(SH110X_WHITE);
  display.setCursor(0, 0);
  words = "Set up";
  twoSecondText(words);
  Serial.println("Setup Okay");


  for (int i = 0; i < nZones; i++) {
    pinMode(ZonePins[i], OUTPUT);
    digitalWrite(ZonePins[i], HIGH);
  }
  pinMode(RainPin, INPUT);
  pinMode(HeatWavePin, INPUT);
  pinMode(PumpPin, OUTPUT);
  digitalWrite(PumpPin, HIGH);
  words = "Exit Setup";
  twoSecondText(words);
}

/*
Once the start time for watering is called, based on Heat, decide whether to perform longer duration watering.
The Pump has to be turned on for the full duration, and the Zones get cycled through.
*/
void StartWatering() {
  display.clearDisplay();
  display.display();
  words = "Start Watering";
  twoSecondText(words);
  if (digitalRead(HeatWavePin) == HIGH) {
    ZoneDuration = ZoneTime * 1.5;
  } else {
    ZoneDuration = ZoneTime;
  }
  digitalWrite(PumpPin, LOW);
  for (int i = 0; i < nZones; i++) {
    digitalWrite(ZonePins[i], LOW);
    display.clearDisplay();
    display.display();
    display.setCursor(0, 0);
    display.print("Zone:");
    display.println(i + 1);  // Easier for others to read if the zones are called 1-5 rather than 0-4
    display.println("On at: ");
    digitalClockDisplay();
    display.print("For: ");
    display.print(ZoneDuration / 100000);
    display.println(" min");
    display.display();
    delay(ZoneDuration);
  }
  digitalWrite(PumpPin, HIGH);
  display.clearDisplay();
  display.display();
  words = "Finished  watering";
  twoSecondText(words);
}

void twoSecondText(String words) {
  display.setCursor(0, 0);
  display.println(words);
  display.display();
  delay(2000);
  display.clearDisplay();
  display.display();
}

// just there to make the clock outputs look nice.
void digitalClockDisplay() {
  // digital clock display of the time

  display.print(hour());
  printDigits(minute());
  printDigits(second());
  display.println();
}

void printDigits(int digits) {
  display.print(":");
  if (digits < 10)
    display.print('0');
  display.print(digits);
}
/*
As the watering system will run for years, the RTC doesn't only get interogated one time on setup, but drift in the Aduino's oscillator is avoided by resyncing to the RTC constantly.
The during heatwaves, the system should perform a second watering in the evening.
The morning start time is shifted back an hour on the weekend.
The if on the raim sensor is there to make the program go into "Do nothing" mode when the rain sensor is active.
*/
void loop() {
  tmElements_t tm;
  RTC.read(tm);
  setTime(tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month, tm.Year);
  Serial.print(tm.Day);
  Serial.print(":");
  Serial.print(tm.Month);
  Serial.print(":");
  Serial.print(tmYearToCalendar(tm.Year));
  Serial.print(":");
  Serial.print(tm.Wday);
  Serial.println();
  Serial.print(day());
  Serial.print(":");
  Serial.print(month());
  Serial.print(":");
  Serial.print(year());
  Serial.print(":");
  Serial.print(weekday());
  Serial.println();

  display.clearDisplay();
  display.display();
  display.setCursor(0, 0);
  display.print("Rain: ");
  display.println(digitalRead(RainPin));
  display.print("Heat: ");
  display.println(digitalRead(HeatWavePin));
  display.println();
  digitalClockDisplay();
  display.display();
  delay(5000);
  display.clearDisplay();
  display.display();
  delay(10000);
  if (digitalRead(RainPin) == LOW) {
    if (tm.Wday == 0 || tm.Wday == 6) {
      Serial.println("Weekend");
      StartHour = PlannedHour + 60;
    } else {
      StartHour = PlannedHour;
    }
    int currentHour = hour();
    int currentMinute = minute();
    // Calculate the current time in minutes since midnight
    currentTime = currentHour * 60 + currentMinute;
    if (currentTime == StartHour || (currentTime == BonusStartHour && digitalRead(HeatWavePin) == HIGH)) {
      StartWatering();
    }
  }
}

The DS1307 doesn't know how to compute the day of week from the date. You have to correctly initialize the DS1307 day of week register.

There are many ds1307 libraries, so try another one.

Alternatively, you could calculate the day of week from the date. Code can be found on the web for that.

Whenever you have a question with a library, first look for its documentation or review its code.

From TimeLib (look the comment at the end of the line)

#define dayOfWeek(_time_) ((((_time_) / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday

Sunday is 1, not 0.

Regards

Part of your problem is that there are differences in the time format between directly accessing the DS1307, and using it through the Time library.

Also, the year format is different. The DS1307 library stores the year as the number of years since 2000, the Time library uses 1970, that is where the 30 year difference comes from.

If you set the DS1307 as the sync source for the Time library, and use the functions from that library to set/access the time, then it should work properly. This also greatly reduces the use of the I2C buss, because the RTC is only accessed at intervals to synchronize the clock.

Yep Sunday is day 1 , same as you can’t have the 0th January

If you had six eggs in a box and starting counting at zero you’d only count up to 5 lol

No, there is no difference (at least with dayofweek).

From DS1307 datasheet:

Thanks for looking into this.
"If you set the DS1307 as the sync source for the Time library, and use the functions from that library to set/access the time, then it should work properly. "
I thought that is what I did with:

RTC.read(tm);

I am confused?

So is the DS1307 has Sunday as day 1, why is tm returning 4, although the date is right?

Because

Maybe.
Reading the RTC chip just reads the RTC chip and does not affect the time being tracked by the Time/TimeLib library.
You can tell the Time/TimeLib libary to automatically sync itself by periodically calling a function.
You need a function that returns a proper/compatible time_t value.
You set the function to call using the Time/TimeLib API call setSyncProvider()
This should be done in setup() as you only do it once.
See the Time/TimeLib example TimeRTC

If using the TimeLib library, you should not have to ever call the RTC library API functions or reference the RTC object after you set up the SyncProvider as you should only be using the Time/TimeLib API functions after that.
(well except maybe to set the time inside the RTC)

--- bill

Thanks Bill,

I think it have it working now.
Mark

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.