software-RTC adapting the AVR-swRTC to SAMD21

Hi everybody,

I want to use a Seeeduino XIAO-board which has a SAMD21-µC to create one-time-passwords ( OTP )

There is a demo-code for OTPs that uses the library swRTC which is written for AVR-boards.
The TOTP-library needs the time in this format

  long GMT = rtc.getTimestamp();

This is the complete demo-code

// GetCode.ino
// 
// Basic example for the TOTP library
//
// This example uses the opensource SwRTC library as a software real-time clock
// you can download from https://github.com/leomil72/swRTC
// for real implementation it's suggested the use of an hardware RTC

#include "sha1.h"
#include "TOTP.h"
#include "swRTC.h"

// The shared secret is MyLegoDoor
uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};

TOTP totp = TOTP(hmacKey, 10);
swRTC rtc;
char code[7];

void setup() {  
  Serial.begin(9600);
  rtc.stopRTC();
  
  // Adjust the following values to match the current date and time
  // and power on Arduino at the time set (use GMT timezone!)
  rtc.setDate(27, 8, 2013);
  rtc.setTime(21, 25, 00);
  
  rtc.startRTC();
}

void loop() {

  long GMT = rtc.getTimestamp();
  char* newCode = totp.getCode(GMT);
  if(strcmp(code, newCode) != 0) {
    strcpy(code, newCode);
    Serial.println(code);
  }  
}

The swRTC-function getTimestamp() is defined like this.
The comment

//returns a timestamp giving the number of seconds since a part year

is not really clear to me

//returns a timestamp giving the number of seconds since a part year (default=1970)
unsigned long swRTC::getTimestamp(int yearT){
	unsigned long time=0;

	//check the epoch
	if (yearT == 0) {
		yearT = 1970;
	} else if (yearT < 1900) {
		yearT = 1900;
	} else if (yearT > 1970) {
		yearT = 1970;
	} else if ((yearT != 1900) && (yearT != 1970)) {
		yearT = 1970;
	}

	//One revolution of the Earth is not 365 days but accurately 365.2422 days.
	//It is leap year that adjusts this decimal fraction. But...
	time += (getYear() - yearT) * 365.2422;
	for (byte i = 0; i < getMonth() - 1; i++){
		time += daysPerMonth[i]; //find day from month
	}
	time = (time + getDay()) * 24UL; //find hours from day
	time = (time + getHours()) * 60UL; //find minutes from hours
	time = (time + getMinutes()) * 60UL; //find seconds from minute
	time += getSeconds(); // add seconds
	if (time > 951847199UL) { time += 86400UL; } //year 2000 is a special leap year, so 1 day must be added if date is greater than 29/02/2000
	//the code below checks if, in case of a leap year, the date is before or past the 29th of februray:
	//if no, the leap day hasn't been yet reached so we have to subtract a day
	if (isLeapYear(getYear())) {
	    if (getMonth() <= 2 ) {
	        time -= 86400UL;
	    }
	}
	return (time - 86400UL); //because years start at day 0.0, not day 1.
}

I'm not familiar with unix-time-things like epoc etc.
looks like number of seconds since 1970??

The software RTC for the SAMD21
has different functions so this is no drop-in replacement.
what is the name of this timeformat that the function

swRTC::getTimestamp(int yearT)

returns?

Which libraries / functions do I have to use to transform the time-format delivered by the RTC_SAMD21.h-library to get the swRTC-time-format?

or as a different approach:
How could I transform the swRTC.h with all its functions into a hardware-independent version that counts up time based on the universal available function millis()?

I want to explain a bit more on that:
I mean not an independant running RTC that is able to work in the backround. This is what makes it hardware-dependant through the use if timer-interrupts.

I mean a version where I the user is responsible to code in a way to call a tick-function often enough that the RTC-variables keep track of time on a precisionlevel of seconds.

For creating one time passwords a precision of a second is enough.

best regards Stefan

SAMD21 has RTC peripheral. see the RTCZero library

1 Like

Hi Juraj,

thank you for pointing me to this library
https://www.arduino.cc/en/Reference/RTC
a lot of functions but none of them is similar to

swRTC::getTimestamp(int yearT)

So the question how do I convert seconds, minutes, hours etc. to the time-format of

swRTC::getTimestamp(int yearT)

remains
best regards Stefan

Well I guess I can boil down my question to

does the swRTC.h-library -function

swRTC::getTimestamp(int yearT)

return the exact same timeformat as

the RTCZero.h-library-function

uint32_t RTCZero::getEpoch()

if not:
what conversion(s) do I have to do to transform the return-value of
uint32_t RTCZero::getEpoch()
into the same time-format like the swRTC-library-function swRTC.h ?

best regards Stefan

Why do you need a function that gives you the time stamp for a specific epoch (1900 or 1970)? Why can't you just used the default epoch of the the RTCZero library? If all calculations are done using timestamps based on the same epoch, won't it just work?

So finally after testing it I can confirm

the AVR-board swRTC-library function

swRTC::getTimestamp()

used like in this demo sketch

/* This file is part of swRTC library.
   Please check the README file and the notes
   inside the swRTC.h file to get more info
   
   This example will send different infos every second
   to the computer through the serial port.
   
   Written by Leonardo Miliani <leonardo AT leonardomiliani DOT com>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation; either
    version 3.0 of the License, or (at your option) any later version.
*/

#include <swRTC.h>
swRTC rtc; //create a new istance of the lib

void setup() {
	rtc.stopRTC(); //stop the RTC
	rtc.setTime(0,0,0); //set the time here
	rtc.setDate(1,1,2016); //set the date here
	rtc.startRTC(); //start the RTC
	Serial.begin(115200); //choose the serial speed here
	//delay(2000); //delay to let the user opens the serial monitor
}

void loop() {
    Serial.print("Timestamp: ");
    Serial.println(rtc.getTimestamp(), DEC);
    delay(1000);
}

adjusted to 1.1.2016 prints

21:10:56.706 -> Timestamp: 1451606400
21:10:57.708 -> Timestamp: 1451606401
21:10:58.710 -> Timestamp: 1451606402
21:10:59.692 -> Timestamp: 1451606403
21:11:00.714 -> Timestamp: 1451606404
21:11:01.694 -> Timestamp: 1451606405
21:11:02.688 -> Timestamp: 1451606406

which are unix-time = epoc-time = seconds since 1.1.1970 0:00 am

is the same as in the demofile

/*
  Epoch time example for Arduino Zero and MKR1000

  Demonstrates how to set time using epoch for the Arduino Zero and MKR1000

  This example code is in the public domain

  created by Sandeep Mistry <s.mistry@arduino.cc>
  31 Dec 2015
  modified
  18 Feb 2016
*/

#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

void setup() {
  Serial.begin(115200);

  rtc.begin(); // initialize RTC

  rtc.setEpoch(1451606400); // Jan 1, 2016
}

void loop() {
  Serial.print("Unix time = ");
  Serial.println(rtc.getEpoch());

  delay(1000);
}

which needs two seconds until the COM-port is established after reset

21:19:18.149 -> Unix time = 1451606402
21:19:19.174 -> Unix time = 1451606403
21:19:20.154 -> Unix time = 1451606404
21:19:21.172 -> Unix time = 1451606405
21:19:22.158 -> Unix time = 1451606406
21:19:23.161 -> Unix time = 1451606407
21:19:24.163 -> Unix time = 1451606408
21:19:25.210 -> Unix time = 1451606409

question solved. Thank you juraj for pointing me to the RTCZero.h-library
best regards Stefan

To answer your question:
I'm simply not familiar with all the epoc-timing. So a question like yours with no additional explanation of the different words like "epoc" "unix-time" and different zero-time-basing like 1900 or 1970 and where is used which one does not help because your question assumes more knowledge about it than I have
best regards Stefan