Teensy3Time.ino updates wrong time ...

Status
Not open for further replies.

MogaRaghu

Well-known member
My Teensy3.5 seems to love the time 21.05.00 ... I first ran the code around that time yesterday and then disconnected. No battery back up is there for the Teensy3.5. But today I ran the same code around noon to update the time and it seems to be stuck on the time around 21.05 hours. I have two queries :

1. Why is it not updating as expected ? ( My PC Time is perfect )
2. In case i need to use the on board RTC in other code (after i provide a battery back up), which part of this code below should i port to the new code ? Meaning just read time without trying to set it.

Code:
#include <TimeLib.h>

void setup()
{
  setSyncProvider(getTeensy3Time);         // Set the Time library to use Teensy 3.0's RTC to keep time
  Serial.begin(115200);
  while (!Serial);                         // Wait for Arduino Serial Monitor to open
  delay(100);
  if (timeStatus() != timeSet)
  {
    Serial.println("Unable to sync with the RTC");
  } else {
    Serial.println("RTC has set the system time");
  }
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void loop()
{
  if (Serial.available())
  {
    time_t t = processSyncMessage();
    if (t != 0)
    {
      Teensy3Clock.set(t);                 // Set the RTC
      setTime(t);
    }
  }
  
  digitalClockDisplay();
  delay(1000);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void digitalClockDisplay()                 // Digital clock display of the time
{
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

//*************************************

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

//*************************************

// Code to process time sync messages from the serial port

#define TIME_HEADER  "T"                   // Header tag for serial time sync message
unsigned long processSyncMessage()
{
  unsigned long pctime = 0L;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013

  if (Serial.find(TIME_HEADER))
  {
    pctime = Serial.parseInt();
    return pctime;
    if ( pctime < DEFAULT_TIME)          // Check the value is a valid time (greater than Jan 1 2013)
    {
      pctime = 0L;                       // Return 0 to indicate that the time is not valid
    }
  }
  return pctime;
}

//*************************************

//Utility function for clock display: prints preceding colon and leading 0
void printDigits(int digits)
{
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}
 
Without a battery to maintain a running clock - the Teensy is loaded with the 'time of compile'. There is example code that shows setting the clock - and other notes on the forum.
 
Without a battery to maintain a running clock - the Teensy is loaded with the 'time of compile'. There is example code that shows setting the clock - and other notes on the forum.
Got it . Thanks.

So sat down and coded a simple blink that also can show the time in Serial. Though I made it work, I still don't follow how the function time_t getTeensy3Time() works without being explicitly called. Maybe once started it runs in the background...

I am giving the code that I put together below .... now I can use this for any project requiring RTC support.

Code:
/* Blink without delay() and Display Date / Time on serial.
    FOr getting correct time display the Teensy should have had
    its RTC synched with PC time using TimeTeensy3.ino
    and the set time held by a battery connected to it.  If not the
    Date Time display will start from Jan 01, 1970.

    22 Nov 2017. OK. 

*/
#include <TimeLib.h>


const int ledPin =  13;             // The number of the LED pin
int ledState = LOW;                 // LedState used to set the LED
long previousMillis = 0;            // Will store last time LED was updated
long interval = 1000;               // Interval at which to blink (milliseconds)

void setup()
{
  setSyncProvider(getTeensy3Time);  // Set the Time library to use Teensy 3.0's RTC to keep time. 
// If the above line is commented, time starts from Jan 1 ,1970
  Serial.begin(115200);
  while (!Serial);
  delay(100);
  pinMode(ledPin, OUTPUT);
}

//##########################################

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis > interval)
  {
    previousMillis = currentMillis;
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(ledPin, ledState);         // Update the LED status ...
    digitalClockDisplay();                  // Update the date and time....
  }
}

//##########################################

void digitalClockDisplay()                 // Digital clock display of the time
{
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year());
  Serial.println();
}

//*************************************

time_t getTeensy3Time()
{
  return Teensy3Clock.get();
}

//*************************************

//Prints preceding colon and leading 0 for min and sec

void printDigits(int digits)
{
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

//*************************************
 
So, I decided to experiment with this using a Teensy 3.6 that I set the time on about 6 months ago using the serial port (It has a CR2032 on it for the RTC).

It shoes that the time was not updated to the compile time, as the reported time from the RTC is an hour off (Time was set during EDT). The Compiler is not setting the time on my 3.6, or the time would be equal to my PC clock. (It is 7:21pm, Teensy is outputting to serial as 8:19pm).

TeensyTimeError.JPG
 
Last edited:
The auto time set process isn’t well documented. Hopefully I can fill in a couple details...

First, understand the Time lib keeps track of time separately from the RTC. This can be confusing. It was designed around the idea of infrequently syncing to time from GPS or the internet. It also does the same for syncing to the RTC. Often I have considered changing the Time lib to distinguish between clocks that are expected to always be present versus intermittent connected time sources. But that’s one of hundreds of low priority project which are unlikely to ever get done. Just remember the Time lib treats the RTC like internet. It keeps track of time itself and occasionally syncs to the RTC.

On pretty much all other Arduino compatible boards, the RTC startup up with some random wrong time. You have to set it. That’s why all the Time lib examples have code to listen for a serial comm message, which happens only when you run a specific program.

For Teensy I tried to make the RTC get set automatically, during the normal course of uploading code. When it works, the result is very convenient. But this process is actually quite difficult to get right.
 
Thanks Paul for that explanation. It makes a lot more sense now.

Do you know what the expected time loss is over time? 2 minutes seems a bit extreme for just a few months of running on the CR2032.
 
My earliest attempt (years ago) seemed like a good idea. The code used a #define to get the compile time. At startup, it would detect if the RTC was not previously programmed with the correct time. If so, it would set the time to the time your code was compiled. Easy, right?

This turned out to have 2 fatal flaws.

First, using a #define was the wrong way to go. The time would get compiled into some .o file, and then Arduino wouldn't need to always recompile that file when you upload. The time in the code could be days, weeks, even months old, if you leave your computer on and keep the Arduino IDE up and running.

Ultimately this problem was solved by having the time baked into the code using a special linker command line argument. This required a hack in Arduino's Java source. Fortunately this is one of the cases where the Arduino devs accepted the code. I'm not aware of any other boards using it yet, but maybe if I write this up in a good blog article it'll help give everyone better documentation on how this is supposed to work?

But the other big problem involved a practical matter of how people actually use the RTC. They almost always (quite wisely) turn off the power to the board before they connect the battery to the RTC. Or in the case of Teensy 3.2, they also have to solder on the 32.768 kHz crystal. Teensy 3.5 and 3.6 have the crystal built in, as do Arduino's newer boards, but you still have to connect wires for the coin cell.

When you turn the power back on, the startup code run and sees the RTC isn't initialized. How could it be? This is the first time you've powered up since the battery started powering the RTC. From here on, every reboot should have the RTC running and not in need of initialization. But this first time with the battery connected, the startup code initializes the RTC using the compile time that was baked into your code by the linker command.

So your RTC gets set to the wrong time. It's off by however much time elapsed between your prior upload and when you complete the battery installation. If you had to order the coin cell holder from Digikey and waited 4 days for the package to arrive, then your RTC will get set to the time 4 days ago when you previously uploaded!

Obviously the startup code needs to be very careful. After you've installed the battery, the absolute last thing you want is for the startup code to incorrectly set the clock back to whenever you originally compiled the code. That would make your project's time keeping horribly unreliable, even if there were a slim chance of this happening by accident.

But the startup code does need to second guess the RTC, because the first initialization after installing the battery will be stale info. Only the first upload that happens after the battery is installed will have a freshly written timestamp, minus a brief moment between the linker command and when the upload completes and your board reboots.

Fortunately the RTC on Teensy 3.x has a 32 byte memory which is also supported by the battery. I solved this problem by dedicating the last 4 bytes to the RTC startup. When the RTC is found to be uninitialized (presumably between power was off without the coin cell battery keeping the RTC alive), those 4 bytes have random bits which are very unlikely to be the special code. So the startup code initializes the RTC with the stale timestamp, and writes the code to those 4 bytes.

Later when you upload code, and Teensy reboots, the startup code looks at those 4 bytes if the RTC is already running. If they have the special code, then the startup code assumes the RTC must have the wrong time. It reinitializes the RTC with the linker's timestamp of the most recent compile.

This system almost always "just works". Usually people familiar with RTCs on other Arduino boards are quite surprised that Teensy somehow has the right time, even though they only recently installed the battery.

But of course it's not perfect. If you remove the battery and power down, the RTC gets set to the old compile time, and doesn't get updated to the correct time unless you upload again. Presumably you're not doing uploads after your project is finished. I just don't know any way this sort of problem could ever be automatically solved. Some people might argue this sort of convenience feature might actually be bad, in a "tough love" sort of way to force people to have to build a time set mechanism into their project from the beginning.

Anyways, hopefully this long-winded description helps. Maybe I'll convert this to a blog post at some point? It's never really been documented well.
 
There was a point that I was looking for a way to have Visual Studio send that time. I had found where the check was done in the teensy base code, and thought it would be a great way to check the RTC for sanity using the PC time (or whatever it's being compiled on), then either set it to the current time, or mark those bits as valid and continue execution.

I'll be using the ODROID-XU4 this week as a build machine for my teensies, so I might have to bake in a check there that just checks the RTC against the ODROID when runtime starts.
 
Ok this has been an interesting thread. Thanks folks ... so time to "show off "my all in PCB using the T3.5 !! Its right now showing the clock that was set about a week back. Going good.
The PCB has headers for : HM10 BT Module / Level shifted I2C headers for LCD and RTC ( this is now spare) / RF24 chip interface / Battery wired / SPI BUS / 8 way header for a matrix keypad / 8 way header for LEDs with on board current limit resistors.
If I may say so the only handicap was the way the VBatt / Reset pins are located. I cannot have mating sockets on PCB for them and hence the add on from top. Other than this I love this T3.5 for its small foot print and blazing speeds in the MCU world !!IMG_2599.jpg
 
Do you know what the expected time loss is over time? 2 minutes seems a bit extreme for just a few months of running on the CR2032.

The 32 khz RTC crystals for T3.5/T3.6 typically drift at less than 20 ppm. I don't know the actual part number. Temperature can effect frequency, see
https://forum.pjrc.com/threads/24628-Interesting-Temperature-Data

Here are some anecdotal measurements of crystal drift https://github.com/manitou48/crystals/blob/master/crystals.txt
 
@PaulStoffregen

That was patient but useful info on how the RTC updates. I am basically a LabVIEW developer and one of the big issues that we have is to handle the First-Run-After-Installation situation. Once the code has run and properly shutdown, there are lots of configuration data that get written to the disk and when the code boots these are read ... so we have a default case when I don't have valid data. I then warn the user . So no issues. But the real bug hits when the code was aborted midway the previous day and next day when the code boots it has "Valid data "to read from disk but that belongs to the last time the code shut down properly. Anyway we now have a flag set for proper shutdowns and so to a large extent the problem is solved.

Coming back to our RTC ... if the time stamp is baked into the startup code... then if the user boots up to find stale RTC data on display, all he has to do is to recompile the code and upload to the Teensy to Reset the RTC to be current. Is my understanding correct ??
 
...
Coming back to our RTC ... if the time stamp is baked into the startup code... then if the user boots up to find stale RTC data on display, all he has to do is to recompile the code and upload to the Teensy to Reset the RTC to be current. Is my understanding correct ??

You can conrim that quickly, but it seems once a battery is installed with a valid time - it will stay running that way. Removing power and the battery to lose the time may be required. Then insert the battery and connect USB while compiling and upload and that will set the compile time to the Teensy?
 
You can conrim that quickly, but it seems once a battery is installed with a valid time - it will stay running that way. Removing power and the battery to lose the time may be required. Then insert the battery and connect USB while compiling and upload and that will set the compile time to the Teensy?

It doesn't work properly. There is a bit that is set based on the RTC (I'll see if I can find it). Unfortunately, the compile-time RTC-set for the teensy does not work and Paul said it's a low priority.

The only way to set the on-board RTC is via code. That is, hard-coded to a time that gets set when the program loads It will reset the time to the hard-coded time every restart), or to set up an event handler to set the RTC based on some serial input.
 
The compile time set does work properly, at least in the regular usage cases, as I tried to explain in lengthy detail on those 2 messages.

Long ago (probably mentioned in numerous old threads) it didn't work for various reasons, until the code was added to distinguish between a cold boot with prior compile time versus a fresh Arduino upload with correct timestamp, as I also tried to explain.

Your comments seem as if you've not read or understood anything I've tried to say. :(
 
The compile time set does work properly, at least in the regular usage cases, as I tried to explain in lengthy detail on those 2 messages.

Long ago (probably mentioned in numerous old threads) it didn't work for various reasons, until the code was added to distinguish between a cold boot with prior compile time versus a fresh Arduino upload with correct timestamp, as I also tried to explain.

Your comments seem as if you've not read or understood anything I've tried to say. :(

Guilty Paul. Apologies for that.

The compile time is not working on my 3.6 with a battery on it. No matter what IDE I use. That is where my confusion stems from. The screenshot I posted is from this PC, using Teensyduino 1.4, and Arduino IDE 1.8.5. It did not set the time properly.

Sincere apologies for my misunderstanding.

I think this is what confused me:
For Teensy I tried to make the RTC get set automatically, during the normal course of uploading code. When it works, the result is very convenient. But this process is actually quite difficult to get right.
 
Last edited:
As Defragster explained, you should disconnect power and remove the battery. Remember, there is a capacitor too. Even if you short the battery terminals (which helps), you need to allow time for the stored energy to fully discharge. Give it several minutes. The RTC is capable of continuing to run for a surprising length of time after you remove the battery.

Then reconnect the battery and turn it on again. The startup code will set the RTC to whatever time the previously loaded code was compiled. It will also set the flag (4 bytes) to remember this is from a cold powerup. When you upload & reboot again, the startup code will set the RTC to the new compile time, and clear the flag in battery-backed RAM, so future uploads do not keep changing the RTC.

Hopefully this would be clear based on the understanding of how it works.

Of course, you can and should make a way for your code to receive proper time and set the RTC. All clocks drift. The RTC will eventually need to be sync'd to the actual time to remain correct long-term.

Just remember, the startup code is designed to *NOT* touch the RTC again unless it has completely lost power.
 
Hi Paul,

I have had an opportunity to revisit this, Removed the battery for about a week, and just uploaded a newly compiled build. The output is as follows:
stillNotWorking.JPG

Am I reading from the wrong RTC?
Code:
#include <Time\TimeLib.h>


const int ledPin = 13;             // The number of the LED pin
int ledState = LOW;                 // LedState used to set the LED
long previousMillis = 0;            // Will store last time LED was updated
long interval = 1000;               // Interval at which to blink (milliseconds)

void setup()
{
	setSyncProvider(getTeensy3Time);  // Set the Time library to use Teensy 3.0's RTC to keep time. 
									  // If the above line is commented, time starts from Jan 1 ,1970
	Serial.begin(115200);
	while (!Serial);
	delay(100);
	pinMode(ledPin, OUTPUT);
}

//##########################################

void loop()
{
	unsigned long currentMillis = millis();

	if (currentMillis - previousMillis > interval)
	{
		previousMillis = currentMillis;
		if (ledState == LOW)
			ledState = HIGH;
		else
			ledState = LOW;
		digitalWrite(ledPin, ledState);         // Update the LED status ...
		digitalClockDisplay();                  // Update the date and time....
	}
}

//##########################################

void digitalClockDisplay()                 // Digital clock display of the time
{
	Serial.print(hour());
	printDigits(minute());
	printDigits(second());
	Serial.print(" ");
	Serial.print(day());
	Serial.print(" ");
	Serial.print(month());
	Serial.print(" ");
	Serial.print(year());
	Serial.println();
}

//*************************************

time_t getTeensy3Time()
{
	return Teensy3Clock.get();
}

//*************************************

//Prints preceding colon and leading 0 for min and sec

void printDigits(int digits)
{
	Serial.print(":");
	if (digits < 10)
		Serial.print('0');
	Serial.print(digits);
}

//*************************************
 
Your sketch in post #17 wouldn't compile on my Ubuntu system (1.8.4/1.40). Changed to #include <TimeLib.h> and it compiled and ran just fine on my T3.6 (no battery)
Code:
7:16:03 5 12 2017
7:16:04 5 12 2017
7:16:05 5 12 2017
7:16:06 5 12 2017
7:16:07 5 12 2017
 
Last edited:
Are you using the Arduino IDE? That sure doesn't look like the Arduino Serial Monitor in your screenshot. Likewise, the unusual #include path isn't the way things are normally done when building with Arduino.

Hopefully my lengthy explanation in message #7 should make it clear this RTC auto-set depends on specifics of the Arduino build process.
 
But of course it's not perfect. If you remove the battery and power down, the RTC gets set to the old compile time, and doesn't get updated to the correct time unless you upload again. Presumably you're not doing uploads after your project is finished. I just don't know any way this sort of problem could ever be automatically solved. Some people might argue this sort of convenience feature might actually be bad, in a "tough love" sort of way to force people to have to build a time set mechanism into their project from the beginning.

Anyways, hopefully this long-winded description helps. Maybe I'll convert this to a blog post at some point? It's never really been documented well.
Hmmm, I was just thinking how to set things up to set the RTC time, as I wanted to use timestamps to say when the Teensy started and stopped using a coin cell battery I wasn't aware that the Teensy IDE set the time.

What I want to do is run my prop with a charged battery and see how long the prop runs with that battery. Every 15 minutes or so, I save the current elapsed micros into the Teensy's EEPROM. One of my batteries seems to restart every so often, and I wanted to track multiple boot times and the start timestamp.

Even if I set it once when being programmed, I know the Teensy RTC isn't temperature aware, so it would drift a little bit, and I wanted an automatic way to reset it.

I was thinking to set the time I would need to:
  • Just use __DATE__ and __TIME__ compiled by the compiler and don't worry about the second or two difference in the time;
  • Parsing a date entered by hand in the USB monitor;
  • Adding a USB HID device to inquire about the time;
  • Add an ethernet shield or ESP8266 wifi and use a stripped down NTP to get the time;
  • Temporarily add a GPS to get the time;
  • Use an external DS3231 and program it on a Raspberry Pi that is syned with NTP and move it to the Teensy; or
  • ignore the problem,

All I have to do is unplug the Teensy, take out the coin cell battery, let it sit for a few seconds to be sure, put the battery back in, attach the Teensy, and then re-program it. Easy peasy. I like not having to add additional code and/or hardware.

Note, to other people thinking about using coin cells, in the past when I was playing with Raspberry Pi more, I had a DS3231 RTC on the Pi, and I thought it would be cool to use a rechargeable coin cell battery. I've discovered that these batteries don't hold a charge all that long (a few days, maybe a month), and if you aren't powering your unit for an extended period of time, the battery will be exhausted. It is better to just use the non-rechargeable batteries, and just change the battery every few years.
 
Last edited:
Thanks Paul.

I use Visual studio with a plugin that calls the arduino compiler. It must not use the same linker however.

I'll have to remember that if I need to set an on-board RTC using compile-time, I have to use the Arduino IDE. Thanks for the information there. I appreciate it for sure.


(The plugin I use is a paid plugin called "Visual Micro"). I have been using Visual Studio since 2001, so it's just natural to me. It also gives me intellisense popups and auto-complete, so it makes my life a lot easier.
 
Ok, so you were really using Visual Micro the whole time? Other than a brief mention in message #8, everything in this thread seemed to say you were using Arduino.

Any chance you could run the Arduino IDE, without Visual Studio & Visual Micro, to confirm whether it really works with your board on your computer?
 
When I ran it purely in the Arduino IDE, it updated the time accordingly. I wonder if I used gcc via command line to build, then uploaded another manner if the time would update as well?

At any rate, yeah. With Visual Micro, this will not work. Pure Arduino IDE, and it works like a champ!
 
Status
Not open for further replies.
Back
Top