Arduino Opta, RTC

On my new Arduino Opta I need the RTC. Setting it with setTime(), using the Unix time stamp, works so far. If I set a certain value and read the values RTC->TR and RTC->DR, all TR values are reasonable, from RTC-DR the day also.
But month and year I can't understand:
month: expecting 9, it's 0x89; expecting 10, it's 0xB0 or 0xD0
year: expecting 29, it's 0x61; expecting 30, it's 62
What is the fault in my interpretation? How can I find out the year and the month out of RTC->DR?

// Hardware: Arduino Opta Lite (Finder)
// IDE: Arduino, Version 2.3.4 (on OS Linux Mint)

#include <TimeLib.h> // Library Time by Michael Margolis
#include <mbed.h>

// Variables
int actU; 

void setup() {
  // setup Serial
  Serial.begin(9600);
  while (!Serial);
  Serial.println();
  Serial.println("Serial working");

  // set RTC (intern)
  actU = 1887117330; // 2029-10-19 15:15:30
  set_time(actU);
  Serial.print("RTC set to Unix Time "); Serial.print(actU); Serial.println(" (2029-10-19 15:15:30)");

  // print raw register values
  Serial.print("RTC->TR (HEX): 0x");
  Serial.println(RTC->TR, HEX);
  Serial.print("RTC->TR (BIN): ");
  Serial.println(RTC->TR, BIN);
  Serial.print("RTC->DR (HEX): 0x");
  Serial.println(RTC->DR, HEX);
  Serial.print("RTC->DR (BIN): ");
  Serial.println(RTC->DR, BIN);
}

void loop() {
  delay(1000);
}

Output (3 examples):
Serial working
RTC set to Unix Time 1918653330 (2030-10-19 15:15:30)
RTC->TR (HEX): 0x151530
RTC->TR (BIN): 101010001010100110000
RTC->DR (HEX): 0x62D019
RTC->DR (BIN): 11000101101000000011001

Serial working
RTC set to Unix Time 1916061330 (2030-09-19 15:15:30)
RTC->TR (HEX): 0x151530
RTC->TR (BIN): 101010001010100110000
RTC->DR (HEX): 0x628919
RTC->DR (BIN): 11000101000100100011001

Serial working
RTC set to Unix Time 1887117330 (2029-10-19 15:15:30)
RTC->TR (HEX): 0x151530
RTC->TR (BIN): 101010001010100110000
RTC->DR (HEX): 0x61B019
RTC->DR (BIN): 11000011011000000011001

Hi @Apus . There are reserved bits inside the register and an addition to year, subtraction from month plus weekday, all in bcd. Check this code that reads the reg to better understand the structure.

Thank you! Coding of the year I understood, coding of the ones of the month number I found out, see code below.
But the relation between the decs of the month and the corresponding Hex number is not clear to me:
month HEX
1 81
12 72
10 B0
11 D1
12 32
1 41
2 62
3 83
9 E9 or 89
10 30 or D0
What's the meccano?

// Hardware: Arduino Opta Lite (Finder)
// IDE: Arduino, Version 2.3.4 (on OS Linux Mint)

#include <TimeLib.h> // Library Time by Michael Margolis
#include <mbed.h>

// Variables
int actU; 

void setup() {
  // setup Serial
  Serial.begin(9600);
  while (!Serial);
  Serial.println();
  Serial.println("Serial working");

  // set RTC 
  actU = 1885507170; // (see line 20)
  set_time(actU);
  Serial.print("RTC set to Unix Time "); Serial.print(actU); Serial.println(" (30 September 2029 23:59:30)");
}

void loop() {
  // print RTC-DR register to find out, how the month is codes  
  Serial.print("RTC->DR (HEX): 0x");
  Serial.println(RTC->DR, HEX);

  uint32_t dr = RTC->DR;
  uint32_t tr = RTC->TR;
  uint8_t myYearOnes = dr >> 16 & 0x0F;
  uint8_t myYearDecs = dr >> 20;
  uint16_t myYear = myYearOnes + 10 * myYearDecs + 1968;
  uint8_t myMonthOnes = dr >> 8 & 0x0F;
  uint8_t myMonthDecs = 0; // tbd!!!
  uint8_t myMonth = myMonthOnes + 10 * myMonthDecs;
  uint8_t myDayOnes = dr & 0x0F;
  uint8_t myDayDecs = dr >> 4 & 0x0F;
  uint8_t myDay = myDayOnes + 10 * myDayDecs;
  uint8_t myHourOnes = tr >> 16 & 0x0F;
  uint8_t myHourDecs = tr >> 20;
  uint8_t myHour = myHourOnes + 10 * myHourDecs;
  uint8_t myMinuteOnes = tr >> 8 & 0x0F;
  uint8_t myMinuteDecs = tr >> 12 & 0x0F;
  uint8_t myMinute = myMinuteOnes + 10 * myMinuteDecs;
  uint8_t mySecOnes = tr & 0x0F;
  uint8_t mySecDecs = tr >> 4 & 0x0F;
  uint8_t mySec = mySecOnes + 10 * mySecDecs;
  Serial.print("RTC->DR and ->TR converted: "); Serial.print(myYear); Serial.print("-");
  Serial.print(myMonth); Serial.print("-"); Serial.print(myDay); Serial.print(" ");
  Serial.print(myHour); Serial.print(":"); Serial.print(myMinute); Serial.print(":"); 
  Serial.println(mySec); Serial.println();
  delay(10000);
}

For September, bit 12 = 0, bits 11:8 = 1001. Answer 09
For October, bit 12 = 1, bits 11:8 = 0000. Answer 10

I think that's how it is held. I don't use the registers directly.

Thank you, Steve9, now it works perfectly!
My fault was taking bits 15:12 for Month tens in BCD instead of taking only bit 12.
So I could append also the weekday (see following code).

// IDE: Arduino, Version 2.3.4 (on OS Linux Mint)

#include <TimeLib.h> // Library Time by Michael Margolis
#include <mbed.h>

// Variables
int actU; 

void setup() {
  // setup Serial
  Serial.begin(9600);
  while (!Serial);
  Serial.println();
  Serial.println("Serial working");

  // set RTC 
  actU = 1893455980; // (i.e. from https://www.epochconverter.com/)
  set_time(actU);
  Serial.print("RTC set to Unix Time "); Serial.print(actU); Serial.println(" (31 December 2029 23:59:40)");
}

void loop() {
  // print RTC-DR register to find out, how the month is codes  
  Serial.print("RTC->DR (HEX): 0x");
  Serial.println(RTC->DR, HEX);

  uint32_t dr = RTC->DR;
  uint32_t tr = RTC->TR;
  uint8_t myYearUnits = dr >> 16 & 0x0F;
  uint8_t myYearTens = dr >> 20;
  uint16_t myYear = myYearUnits + 10 * myYearTens + 1968;
  uint8_t myMonthUnits = dr >> 8 & 0x0F;
  uint8_t myMonthTens = dr >> 12 & 0x1;
  uint8_t myMonth = myMonthUnits + 10 * myMonthTens;
  uint8_t myDayUnits = dr & 0x0F;
  uint8_t myDayTens = dr >> 4 & 0x03;
  uint8_t myDay = myDayUnits + 10 * myDayTens;
  uint8_t myWeekday = dr >> 13 & 0x7;
  uint8_t myHourUnits = tr >> 16 & 0x0F;
  uint8_t myHourTens = tr >> 20;
  uint8_t myHour = myHourUnits + 10 * myHourTens;
  uint8_t myMinuteUnits = tr >> 8 & 0x0F;
  uint8_t myMinuteTens = tr >> 12 & 0x0F;
  uint8_t myMinute = myMinuteUnits + 10 * myMinuteTens;
  uint8_t mySecUnits = tr & 0x0F;
  uint8_t mySecTens = tr >> 4 & 0x0F;
  uint8_t mySec = mySecUnits + 10 * mySecTens;
  Serial.print("RTC->DR and ->TR converted: "); Serial.print(myYear); Serial.print("-");
  Serial.print(myMonth); Serial.print("-"); Serial.print(myDay); Serial.print(" ");
  Serial.print(myHour); Serial.print(":"); Serial.print(myMinute); Serial.print(":"); 
  Serial.print(mySec); Serial.print(", WD "); Serial.println(myWeekday); Serial.println();
  delay(10000);
}

The output for example for 2029-12-31 23:59:40 and following is:

Serial working

RTC set to Unix Time 1893455980 (31 December 2029 23:59:40)

RTC->DR (HEX): 0x613231

RTC->DR and ->TR converted: 2029-12-31 23:59:40, WD 1

RTC->DR (HEX): 0x613231

RTC->DR and ->TR converted: 2029-12-31 23:59:50, WD 1

RTC->DR (HEX): 0x624101

RTC->DR and ->TR converted: 2030-1-1 0:0:0, WD 2

RTC->DR (HEX): 0x624101

RTC->DR and ->TR converted: 2030-1-1 0:0:10, WD 2

So you solved my problem and I can continue with the Arduino Opta. Tank you!

Here is my final version.

// Hardware: Arduino Opta Lite (Finder)
// Coded by Rolf Gutzwiller and chatgpt.com

#include <TimeLib.h> // Library Time by Michael Margolis
#include <mbed.h>

// Function Prototypes
bool isLeapYear(uint16_t year);
uint32_t unixTimestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); 

// Variables
const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint32_t actU;

void setup() {
  // setup Serial
  Serial.begin(9600);
  while (!Serial);
  Serial.println();
  Serial.println("Serial working");

  // set RTC 
  actU = unixTimestamp(2025, 1, 23, 9, 53, 0);
  Serial.print("actU from unixTimestamp(...) = "); Serial.println(actU);
  set_time(actU);

  // Debug: Print raw register values
  /*Serial.print("Raw RTC->DR (BIN): ");
  Serial.println(RTC->DR, BIN);
  Serial.print("Raw RTC->DR: 0x");
  Serial.println(RTC->DR, HEX);*/
}

void loop() {
  // take RTC register and calculate date and time
  uint32_t dr = RTC->DR;
  uint32_t tr = RTC->TR;
  uint8_t myYearUnits = dr >> 16 & 0x0F;
  uint8_t myYearTens = dr >> 20;
  uint16_t myYear = myYearUnits + 10 * myYearTens + 1968;
  uint8_t myMonthUnits = dr >> 8 & 0x0F;
  uint8_t myMonthTens = dr >> 12 & 0x1;
  uint8_t myMonth = myMonthUnits + 10 * myMonthTens;
  uint8_t myDayUnits = dr & 0x0F;
  uint8_t myDayTens = dr >> 4 & 0x03;
  uint8_t myDay = myDayUnits + 10 * myDayTens;
  uint8_t myWeekday = dr >> 13 & 0x7;
  uint8_t myHourUnits = tr >> 16 & 0x0F;
  uint8_t myHourTens = tr >> 20;
  uint8_t myHour = myHourUnits + 10 * myHourTens;
  uint8_t myMinuteUnits = tr >> 8 & 0x0F;
  uint8_t myMinuteTens = tr >> 12 & 0x0F;
  uint8_t myMinute = myMinuteUnits + 10 * myMinuteTens;
  uint8_t mySecUnits = tr & 0x0F;
  uint8_t mySecTens = tr >> 4 & 0x0F;
  uint8_t mySec = mySecUnits + 10 * mySecTens;
  
  // Print date and time
  Serial.print("from RTC-registers DR and TR calculated: "); Serial.print(myYear); Serial.print("-");
  Serial.print(myMonth); Serial.print("-"); Serial.print(myDay); Serial.print(" ");
  Serial.print(myHour); Serial.print(":"); Serial.print(myMinute); Serial.print(":"); 
  Serial.print(mySec); Serial.print(", WD "); Serial.println(myWeekday); Serial.println();
  
  delay(10000);
}

// Functions

// Calculate Unix timestamp from human date
uint32_t unixTimestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
{
  Serial.print("Input to unixTimestamp(...): "); 
  Serial.print(year); Serial.print("-");
  Serial.print(month); Serial.print("-"); Serial.print(day); Serial.print(" ");
  Serial.print(hour); Serial.print(":"); Serial.print(minute); Serial.print(":"); 
  Serial.println(second);

  // Intern variables
  int totalDays = 0; // number of days since 1970-01-01 00:00:00
  int totalSeconds = 0; // number of seconds since 1970-01-01
  uint16_t y;
  uint8_t m;
  
  // Add days for full years
  for (y = 1970; y < year; y++) {
    if (isLeapYear(y)) {
      totalDays += 366;
    }
    else {
      totalDays += 365;
    }    
  }

  // Add days for full months in the current year
  for (m = 1; m < month; m++) {
    totalDays += daysInMonth[m - 1];
    if (m == 2 && isLeapYear(year)) { // February in a leap year
      totalDays += 1;
    }
  }

  // Add days for the current month
  totalDays += (day - 1);

  // Convert total days to seconds
  totalSeconds = totalDays * 86400;

  // Add seconds for the current day
  totalSeconds += (hour * 3600) + (minute * 60) + second;

  return totalSeconds;
}

// Check if a year is a leap year
bool isLeapYear(uint16_t y) {
  if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) {
    return true;
  }
  return false;
}

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