Teensy 4.0 and (S)RTC

Hi!

Hello everyone, this my first posting in here.

Alright, I have been trying to use the Teensy 4.0's SRTC feature in a clock project. The Teensy 4.0 (https://www.adafruit.com/product/4323) is the core module, driving an OLED display (https://www.adafruit.com/product/2674) and a WiFi module (https://www.adafruit.com/product/2999) via SPI. The Teensy has a (proven working) 3V button cell connected to Vbat and GND. What it does is simple, upon first power up (no (S)RTC is set) it connects to the web, fetches the time via NTP, sets the time and the (S)RTC. Next time, when it is powered up, it should check with the SRTC (hopefully), tell the battery backed up time is okay and there is no need to go out on the web and start right away with the saved time. Every day at 1.55am (chosen arbitrarily) it syncs the time from the web (later this is gonna be set to sync once per month or so).

Basically, it works but the time from reading the (S)RTC always returns the compile time of the sketch (can be verified by changing the host computer's system time), along with a "timeSet".

I have searched this forum and have found these:
https://forum.pjrc.com/threads/6031...RTC-in-a-Teensy-4-0?highlight=teensy+4.0+srtc
https://forum.pjrc.com/threads/57176-teensy_loader_cli-and-Teensy-4-0?highlight=teensy+4.0+srtc
https://forum.pjrc.com/threads/58249-T4-rtc-setting-with-time-change?highlight=teensy+4.0+srtc
https://forum.pjrc.com/threads/5819...s-after-power-cycle?highlight=teensy+4.0+srtc

... but all the hints there haven't helped solving my problem so far, also

Code:
void rtc_set(unsigned long t)
{
   // stop the RTC
   SNVS_HPCR &= ~(SNVS_HPCR_RTC_EN | SNVS_HPCR_HP_TS);
  while (SNVS_HPCR & SNVS_HPCR_RTC_EN) ; // wait
   // stop the SRTC
  SNVS_LPCR &= ~SNVS_LPCR_SRTC_ENV;
  while (SNVS_LPCR & SNVS_LPCR_SRTC_ENV); // wait
   // set the SRTC
   SNVS_LPSRTCLR = t << 15;
   SNVS_LPSRTCMR = t >> 17;
   // start the SRTC
  SNVS_LPCR |= SNVS_LPCR_SRTC_ENV;
  while (!(SNVS_LPCR & SNVS_LPCR_SRTC_ENV)); // wait
   // start the RTC and sync it to the SRTC
  SNVS_HPCR |= SNVS_HPCR_RTC_EN | SNVS_HPCR_HP_TS;
}

already seems to be part of the installed library that came with the Teensyduino-download that I am using. Nevertheless, I added it as a separate function to my code to make sure.

Here is my code, might seem mangled as it is the result of all attempts to get this working:

Code:
/*
  OLED Display clock
 */
#include <TimeLib.h>
#include <Timezone.h>
#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1325.h>

// If using software SPI, define CLK and MOSI
#define OLED_CLK 13
#define OLED_MOSI 11

// These are neede for both hardware & softare SPI
#define OLED_CS 10
#define OLED_RESET 9
#define OLED_DC 8

// WiFi control pin
#define WIFI_EN 18

int status = WL_IDLE_STATUS;
char ssid[] = "*******";  // network SSID
char pass[] = "********";  // network password
int keyIndex = 0;  // your network key Index number (needed only for WEP)

// NTP
IPAddress timeServer(129, 6, 15, 28);  // time.nist.gov NTP server
const int NTP_PACKET_SIZE = 48;  // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE];  // buffer to hold incoming and outgoing packets

// A UDP instance to let us send and receive packets over UDP
unsigned int localPort = 2390;  // local port to listen for UDP packets
WiFiUDP Udp;

// hardware SPI
Adafruit_SSD1325 display(OLED_DC, OLED_RESET, OLED_CS);

// the setup routine runs once when you press reset:
void setup() {
  // time source
  setSyncProvider(getTeensyTime);
  
  // sane wifi
  pinMode(WIFI_EN, OUTPUT);
  enableWifi();
  WiFi.setPins(19, 17, 16);
  
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  display.begin();  // init
  display.clearDisplay();  // clears the screen and buffer

  if (timeStatus() != timeSet) {
    fetchTime();
  }
  else {
    Serial.println("RTC is good, no need to fetch time from network.");
  }
}

// the loop routine runs over and over again forever:
void loop() {
  char tstr[10] = {0};
  // central European Time (Berlin, Prague)
  TimeChangeRule myDST = {"CEST", Last, Sun, Mar, 2, 120};  // Daylight time = UTC + 2 hours
  TimeChangeRule mySTD = {"CET", Last, Sun, Oct, 2, 60};    // Standard time = UTC + 1 hour
  Timezone myTZ(myDST, mySTD);
  TimeChangeRule *tcr;  //pointer to the time change rule, use to get TZ abbrev /**/

  time_t utc = now();
  time_t local = myTZ.toLocal(utc, &tcr);/**/
  
  snprintf(tstr, sizeof(tstr)-1, "%02d:%02d:%02d", hour(local), minute(local), second(local));

  display.clearDisplay();
  display.setCursor(17, 25);  
  printScn((const char*)(tstr), WHITE, 2);

  // every day at 01:55 (am) fetch time from internet to sync things up
  if ((hour(local) == 1) && (minute(local) == 55) && (second(local) == 0)) {
    fetchTime();
  }
  
  delay(450);  // make sure we update the display more than twice per second to see every update
}

void printScnXY(const char* text, const int& x, const int& y, const int& col, const int& txtsz) {
  display.setTextSize(txtsz);
  display.setTextColor(col);
  display.setCursor(x, y);
  display.println(text);
  display.display();
}

void printScn(const char* text, const int& col, const int& txtsz) {
  display.setTextSize(txtsz);
  display.setTextColor(col);
  display.print(text);
  display.display();
}

void printlnScn(const char* text, const int& col, const int& txtsz) {
  display.setTextSize(txtsz);
  display.setTextColor(col);
  display.println(text);
  display.display();
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address) {
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}


void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  printScn("IP: ", WHITE, 1);
  char ipprnbuf[17] = {0};
  snprintf(ipprnbuf, sizeof(ipprnbuf)-1, "%03d.%03d.%03d.%03d", ip[0], ip[1], ip[2], ip[3]);
  printlnScn((const char*)(ipprnbuf), WHITE, 1);
  
  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void fetchTime() {
  connectToWifi();
  getNtpTime();
}

void enableWifi() {
  digitalWrite(WIFI_EN, HIGH);  // turn on WiFi module
  delay(1000);
}

void disableWifi() {
  digitalWrite(WIFI_EN, LOW);  // turn off WiFi module
  delay(1000);
}

void connectToWifi() {
  // check for the presence of the shield:
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    printScnXY("No WiFi device found!", 0, 0, WHITE, 1);
    // don't continue:
    while (true);
  }

  display.clearDisplay();
  display.setCursor(0, 0);
  
  // attempt to connect to WiFi network:
  status = WL_IDLE_STATUS;
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    printlnScn("Connecting to WiFi...", WHITE, 1);
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }

  Serial.println("Connected to wifi");
  printScn("Link: ", WHITE, 1);
  printlnScn(ssid, WHITE, 1);
  printWiFiStatus();

  Serial.println("\nStarting connection to server...");
  Udp.begin(localPort);
}

void getNtpTime() {
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);
  if ( Udp.parsePacket() ) {
    Serial.println("NTP packet received");
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);

    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;
    // print Unix time:
    Serial.println(epoch);
    setTime(epoch);  // sets volatile time
    Teensy3Clock.set(epoch);  // supposed to set the battery backed up RTC
    srtc_set(epoch);  // test, to set the 'real' RTC

    // print the hour, minute and second on serial console:
    char tstr[10] = {0};
    snprintf(tstr, sizeof(tstr)-1, "%02d:%02d:%02d", hour(epoch), minute(epoch), second(epoch));
    Serial.print("The UTC time is ");  // UTC is the time at Greenwich Meridian (GMT)
    Serial.println(tstr);
  }
}

time_t getTeensyTime() {
  Serial.println("calling getTeensyTime() / Teensy3Clock.get()...");
  return Teensy3Clock.get();
}

void srtc_set(unsigned long t) {
  // stop the RTC
  SNVS_HPCR &= ~(SNVS_HPCR_RTC_EN | SNVS_HPCR_HP_TS);
  while (SNVS_HPCR & SNVS_HPCR_RTC_EN) ; // wait
  // stop the SRTC
  SNVS_LPCR &= ~SNVS_LPCR_SRTC_ENV;
  while (SNVS_LPCR & SNVS_LPCR_SRTC_ENV); // wait
  // set the SRTC
  SNVS_LPSRTCLR = t << 15;
  SNVS_LPSRTCMR = t >> 17;
  // start the SRTC
  SNVS_LPCR |= SNVS_LPCR_SRTC_ENV;
  while (!(SNVS_LPCR & SNVS_LPCR_SRTC_ENV)); // wait
  // start the RTC and sync it to the SRTC
  SNVS_HPCR |= SNVS_HPCR_RTC_EN | SNVS_HPCR_HP_TS;
}

Hope I didn't forget to provide any information and that the documentation is sufficient someone can make some sense out of it. Any help is greatly appreciated.


Thanks!

Michael
 
When are you seeing your problem? When the sketch is first downloaded and run? Or when the T4 has been powered off and then powered back on again?

If the former, that's how it's supposed to work; the Teensy Loader sets the SRTC once the sketch has been downloaded. Your sketch shouldn't end up using NTP to set the time because it's already set.

If the latter; do I understand this correctly? You're saying that the RTC hasn't been running, and it simply reporting the time that was set when the sketch was last downloaded? If so, that's very odd.
 
There are included examples of the timelib usage.

See if they show the same trouble - or if there in one that properly syncs the current processor time with the stored RTC unit time. It is the stored RTC time that is used on power up. That is set during upload.

If the processor time is changed and a provider is referenced that won't change the stored running time in the SRTC without explicit addressing that.
 
@Silverlock: I wouldn't have any problem if the Teensy used the sketch time for the first run after it got uploaded, that's actually quite nifty. But it happens after each reset / power cycle. Only when I force it to sync via NTP it's gonna be alright, but only till next power cycle. Anyway, I'll check that again.
 
@defragster: So when I call that little piece of code that has made it into the library it should definitely set the SRTC properly and then that time will be used next time it boots up?
 
Yes, when the proper code is called the SRTC will maintain the provided time going forward.


That's how it works until the SRTC is written to. There is a sample that demos that and I tried it recently when posted about.

If you remove Teensy power and pull the VBat cell ( wait any needed time ) then put the cell back in and repower the Teensy you'll see No Time set.

Until the SRTC time it pushed over it will not have any new time when power is removed from Teensy and the VBat is maintained - on repowering that unset - perhaps updated - time will be stored.
 
After reading it all, it boils down to be as simple as having issues with the button cell connection, the cell was working but not the fixture I was using it in. I checked the voltage across the cell but not directly across the Vbat / GND with the Teensy module itself. Dang, I didn't expect that at all. Thank you for all your help.
 
One question, though: Is there any way one could tell if the time set in the SRTC is 'still' the time that was set from the sketch's compile time by the loader (sort of 'hey, this is the first time the code is run after it was uploaded')?
 
Not sure if this is the correct place to ask my question. I have teensy 4.0. need something that after power cycle the RTC has the real current time. But in my loop I change the RTC time (for other purposes) I was hoping the (S)RTC holds the real current time. (my teensy has BU battery) So if my thinking is correct, I need a command to copy the (S)RTC to RTC.....
 
Back
Top