GPS-Clock and RTC Teensy 4.1 synchronized

Hello everyone,

I receive the time via a GPS receiver (neo6mv2 /TinyGPS++.h).
The time (UTC) is then converted with the library (Timezone.h) and rules (CET & CE) to my local time (in Germany).
I would also like to use the internal RTC if the GPS fails or after a power outage.

I've tried a few things (the whole procedure with setSyncProvider(getTeensy3Time) etc. but nothing works.
The RTC always shows the UTC time.

I think it's because of the time_t that's already in use.

What else can I do to ensure that the RTC is synchronized with the correct time (local)?
Can you please help me ?

I almost suspect that I need another RTC because of the TimeLib.h .

CU Martin



Code:
EXTMEM char bigBuffer[1000000];
#include <Timezone.h>
#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <TimeLib.h>

const int offset = +2;   // Central European Time

SoftwareSerial ss();

#define ss Serial1

#include <circular_buffer.h>
#include <isotp.h>
#include <kinetis_flexcan.h>
#include <isotp_server.h>
#include <FlexCAN_T4.h>
#include <imxrt_flexcan.h>

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> can1;

static CAN_message_t msg;

#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>

// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);

//define GPS variable
TinyGPSPlus gps;

// Change these two rules corresponding to your timezone, see https://github.com/JChristensen/Timezone
//Central European Time (Frankfurt, Paris)  120 = +2 hours in daylight saving time (summer).
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};
//Central European Time (Frankfurt, Paris)  60  = +1 hour in normal time (winter)
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};
Timezone CE(CEST, CET);

// time variables
time_t local, utc, prev_set;


int timesetinterval = 30; //set microcontroller time every 60 seconds

void setup() {


  Serial.begin(9600);

  ss.begin(9600); //This opens up communications to the GPS
  
  can1.begin();
  can1.setBaudRate(125000);

  Serial.print("GPS Test");
  display.begin();
  display.setContrast(75);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0, 15);
  display.print("Teensy GPS Uhr");


  display.display();

  smartDelay(1000);
  while (!gps.time.isValid()) // wait a valid GPS UTC time (valid is defined in the TinyGPS++ library)
  {
    smartDelay(1000);
  }
  setthetime();
  prev_set = now();

}

void loop()
{
   
  if (now() - prev_set > timesetinterval && gps.time.isValid())  // set the microcontroller time every interval, only if there is a valid GPS time
  {
    setthetime();
    prev_set = now();

    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 15);
    display.println("time is set");

    display.display();

    smartDelay(1000);

    can1.write(msg);

  }
  displaythetime();

  smartDelay(1000);     // update the time every second

  digitalClockDisplay();

  
} //end loop

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


}

void printDigits(int digits) {
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}



static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do
  {
    // If data has come in from the GPS module
    while (ss.available())
      gps.encode(ss.read()); // Send it to the encode function
    // tinyGPS.encode(char) continues to "load" the tinGPS object with new
    // data coming in from the GPS module. As full NMEA strings begin to come in
    // the tinyGPS library will be able to start parsing them for pertinent info
  } while (millis() - start < ms);
}

void setthetime(void)
{
  int Year = gps.date.year();
  byte Month = gps.date.month();
  byte Day = gps.date.day();
  byte Hour = gps.time.hour();
  byte Minute = gps.time.minute();
  byte Second = gps.time.second();

  // Set Time from GPS data string
  setTime(Hour, Minute, Second, Day, Month, Year);  // set the time of the microcontroller to the UTC time from the GPS

}

void displaythetime(void)
{
  utc = now();  // read the time in the correct format to change via the TimeChangeRules
  local = CE.toLocal(utc);

  if (gps.location.isValid())
  {
    Serial.print("Latitude: ");
    Serial.println(gps.location.lat(), 6);
    Serial.print("Longitude: ");
    Serial.println(gps.location.lng(), 6);
    Serial.print("Altitude: ");
    Serial.println(gps.altitude.meters());

    Serial.println();
    Serial.print("Satelliten");
    Serial.print(":");
    Serial.print(gps.satellites.value());  // display the number of satellites
    Serial.println();
  }
  else
  {
    Serial.println("Location: Not Available");
  }

  Serial.print("Date: ");
  if (gps.date.isValid())
  {
    Serial.print(gps.date.month());
    Serial.print("/");
    Serial.print(gps.date.day());
    Serial.print("/");
    Serial.println(gps.date.year());
  }
  else
  {
    Serial.println("Not Available");
  }

  Serial.print("Time: ");
  if (gps.time.isValid())
  {
    if (gps.time.hour() < 10) Serial.print(F("0"));
    Serial.print(gps.time.hour() + offset);
    Serial.print(":");
    if (gps.time.minute() < 10) Serial.print(F("0"));
    Serial.print(gps.time.minute());
    Serial.print(":");
    if (gps.time.second() < 10) Serial.print(F("0"));
    Serial.print(gps.time.second());
    Serial.print(".");
    if (gps.time.centisecond() < 10) Serial.print(F("0"));
    Serial.println(gps.time.centisecond());

    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(BLACK);
    display.setCursor(0, 0);
    display.print("Satelliten");
    display.setCursor(64, 0);
    display.print(gps.satellites.value());  // display the number of satellites

    display.setCursor(0, 10);
    display.print("Time:");
    display.setCursor(30, 10);
    if (hour(local) < 10) // add a zero if hour is under 10
      display.print("0");
    //display.print(gps.time.hour() + offset);
    display.print(hour(local));
    display.setCursor(40, 10);
    display.print(":");
    display.setCursor(45, 10);
    if (minute(local) < 10) // add a zero if minute is under 10
      display.print("0");
    display.print(minute(local));
    display.setCursor(55, 10);
    display.print(":");
    display.setCursor(60, 10);
    if (second(local) < 10) // add a zero if minute is under 10
      display.print("0");
    display.print(second(local));

    display.setCursor(0, 20);
    display.print("Date:");
    display.setCursor(30, 20);
    display.print(gps.date.day());
    display.setCursor(40, 20);
    display.print(".");
    display.setCursor(48, 20);
    display.print(gps.date.month());
    display.setCursor(54, 20);
    display.print(".");
    display.setCursor(60, 20);
    display.println(gps.date.year());

    display.setCursor(0, 30);
    display.print("Lat:");
    display.setCursor(25, 30);
    display.print(gps.location.lat(), 6);
    display.setCursor(0, 40);
    display.print("Lon:");
    display.setCursor(25, 40);
    display.print(gps.location.lng(), 6);

    display.display();

    msg.flags.extended = 0;
    msg.id = 0x40F;
    msg.len = 6;
    msg.buf[0] = 0x20;
    msg.buf[1] = (hour(local)); //hour
    msg.buf[2] = (minute(local)); //minute
    msg.buf[3] = (gps.date.day());  //decToBcd(tm.Day); //day
    msg.buf[4] = (gps.date.month());  //decToBcd(tm.Month); //month
    msg.buf[5] = (gps.date.year());  //decToBcd(tm.Year + 1970);//year


  }
  else
  {
    Serial.println("Not Available");
  }

}
 

Attachments

  • GPS-1.png
    GPS-1.png
    21.6 KB · Views: 37
Last edited:
This example seems to replicate the above code - maybe adding the offset from UTC?
>> {local install}\hardware\teensy\avr\libraries\Time\examples\TimeGPS\TimeGPS.ino

But that doesn't appear to set the RTC clock.

This example shows setting the RTC time: {local install}\hardware\teensy\avr\libraries\Time\examples\TimeRTCSet\TimeRTCSet.ino

Maybe there are better ...\libraries\Time\examples" ?

or perhaps that leads to a forum search looking at the posts with ref to: "RTC.set"
 
As already said, I would like to use the internal RTC from the Teensy. Otherwise I need extra hardware (DS3132 or DS.....).
 
As already said, I would like to use the internal RTC from the Teensy. Otherwise I need extra hardware (DS3132 or DS.....).

Indeed, you did - and the somewhere in above "examples" demonstrates setting the RTC ... perhaps not as desired with the one indicated that does refer to DS1307RTC ...

Indicated forum search - or better - should find usable code not at hand just now.

Perhaps better example is: {local install}\hardware\teensy\avr\libraries\Time\examples \TimeTeensy3\TimeTeensy3.ino

Forum search will find threads like: https://forum.pjrc.com/threads/60317-How-to-access-the-internal-RTC-in-a-Teensy-4-0
That discuss usage
 
The setthetime funtion does not adjust the GPS time to local time. It sets the RTC to GPS time (UTC).
When doing arithmetic on time values, such as adjusting the timezone, I find it best to use the maketime and breaktime functions in the TimeLib library.

BTW, your adjustment using 'offset' doesn't look complete. If, for example, the current hour is 23, you'll adjust it to 25 instead of 01 of the next day.

Pete
 
I've tried a few things (the whole procedure with setSyncProvider(getTeensy3Time) etc. but nothing works.
The RTC always shows the UTC time.
I actually think you are missing one important piece of code for accessing RTC in the Teensy microcontroller in void setthetime(void).
Code:
void setthetime(void)
{
  int Year = gps.date.year();
  byte Month = gps.date.month();
  byte Day = gps.date.day();
  byte Hour = gps.time.hour();
  byte Minute = gps.time.minute();
  byte Second = gps.time.second();

[COLOR="#FF0000"]  // Set Time from GPS data string
  // This sets the system time (TimeLib.h)
  // set your seperated date/time variables out as normal and update system time FIRST
  setTime(Hour, Minute, Second, Day, Month, Year);  // set the time of the microcontroller to the UTC time from the GPS

  // now lets set the RTC in teensy before exiting.
  // now we can use the system time to update the Teensy's RTC bits
  // This sets the Teensy RTC Clock from Arduino Time library(system time) - epoch stylee, just like it wants :)
  Teensy3Clock.set(now());[/COLOR]
}

In setup()
Code:
void setup() {
  [COLOR="#FFFF00"][COLOR="#FF0000"]// set the Time library to use Teensy 3.0's RTC to keep time
  setSyncProvider(getTeensy3Time);[/COLOR][/COLOR]

  Serial.begin(9600);

  ss.begin(9600); //This opens up communications to the GPS

  can1.begin();
  can1.setBaudRate(125000);

  Serial.print("GPS Test");
  display.begin();
  display.setContrast(75);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0, 15);
  display.print("Teensy GPS Uhr");


  display.display();

  smartDelay(1000);
  while (!gps.time.isValid()) // wait a valid GPS UTC time (valid is defined in the TinyGPS++ library)
  {
    smartDelay(1000);
  }
  setthetime();
  prev_set = now();
}
And you will also need this:
time_t getTeensy3Time()
{
return Teensy3Clock.get();
}
 
Thanks for the numerous messages.Yes, I made the sketch with copy and paste without thinking too much.Sorry.
I'll revise the sketch over the next few days. I'll get back to you.
CU Martin
 
hello forum,

I need your help again. If I send the time with the CAN to the PLC, I receive the correct date and month on the PLC
the year then looks like this: 20;2
I've already experimented with +-1970;+-30;t etc.
What am I doing wrong?
I put the TimeSerial sketch on an Arduino UNO with MCP2515 for testing, but with the Flexcan on Teensy 4.1 the result is the same

<TimeLib.h>


Flexcan
----------------------------------------
msg.flags.extended = 0;
msg.id = 0x40F;
msg.len = 6;
msg.buf[0] = 0x20;
msg.buf[1] = (hour()); //hour
msg.buf[2] = (minute()); //minute
msg.buf[3] = (day()); //day
msg.buf[4] = (month()); //month
msg.buf[5] = (year());//year

MCP2515 Shield
-----------------------------------------
canMsg.can_id = 0x40F;
canMsg.can_dlc = 6;
canMsg.data[0] = 0x20;
canMsg.data[1] = (hour()); //hour
canMsg.data[2] = (minute()); //minute
canMsg.data[3] = (day()); //day
canMsg.data[4] = (month()); //month
canMsg.data[5] = (year());//year

Do you have a tip?

Greetings Martin
 
Last edited:
the year then looks like this: 20;2
Your code snippet doesn't include the code which prints "20;2" which is where the problem is almost certain to lie. Post the code which prints this value.

Pete
 
The 20;2 is from my attempts.
I sent the Unix time T1660573535 with the serial monitor in the sketch. The serial monitor shows the time 14:25:35 15 8 2022. When the CAN sends, I receive 14:26 15.08 2070 on the PLC

As already said from an Arduino UNO with MCP2515.
Thanks in advance
Code:
#include <INTERVAL.h>

/*
   TimeSerial.pde
   example code illustrating Time library set through serial port messages.

   Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970)
   you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013
  T1357041600

   A Processing example sketch to automatically send the messages is included in the download
   On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
*/

#include <TimeLib.h>
#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg;
MCP2515 mcp2515(10);

#define TIME_HEADER  "T"   // Header tag for serial time sync message
#define TIME_REQUEST  7    // ASCII bell character requests a time sync message 

void setup()  {
  Serial.begin(9600);
  //while (!Serial) ; // Needed for Leonardo only
  //pinMode(13, OUTPUT);
  setSyncProvider( requestSync);  //set function to call when sync required
  Serial.println("Waiting for sync message");

  mcp2515.reset();
  mcp2515.setBitrate(CAN_125KBPS, MCP_8MHZ);
  mcp2515.setNormalMode();
}

void loop() {
  if (Serial.available()) {
    processSyncMessage();
  }
  if (timeStatus() != timeNotSet) {
    digitalClockDisplay();
  }
  if (timeStatus() == timeSet) {
    //digitalWrite(13, HIGH); // LED on if synced
  }
  delay(1000);


  INTERVAL(10000UL)  // millisekunden
  {
    mcp2515.sendMessage(&canMsg);
    Serial.println("Message sent");
  }
}

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();
  
  canMsg.can_id  = 0x40F;
  canMsg.can_dlc = 6;
  canMsg.data[0] = 0x20;
  canMsg.data[1] = (hour()); //hour
  canMsg.data[2] = (minute()); //minute
  canMsg.data[3] = (day()); //day
  canMsg.data[4] = (month()); //month
  canMsg.data[5] = (year());  /////0x16 <<<<was 2022 in HEX

}

void printDigits(int digits) {
  // utility function for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if (digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

void processSyncMessage() {
  unsigned long pctime;
  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013

  if (Serial.find(TIME_HEADER)) {
    pctime = Serial.parseInt();
    if ( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
      setTime(pctime); // Sync Arduino clock to the time received on the serial port
    }
  }
}

time_t requestSync()
{
  Serial.write(TIME_REQUEST);
  return 0; // the time will be sent later in response to serial mesg
}

Teensy-Time-CAN-1.jpg
DSC06980_Kopie.jpg

With this Sketch works the CAN(Message) perfect !
Code:
#include <SPI.h>
#include <mcp2515.h>
struct can_frame canMsg;
int led = 5;
int pushB = 7;
int f = 0;
MCP2515 mcp2515(10);

#define DCFPIN 8
#define INVERTEDSIGNAL false

int hour, minute, day, month, year;

#define PULSE_ERRORLIMIT 40  // 40 ms seems to be a suitable value
#define PULSE_SHORTSECOND (1000-PULSE_ERRORLIMIT)
#define PULSE_LONGSECOND  (1000+PULSE_ERRORLIMIT)
#define PULSE_MINUTEMARK  (2000-PULSE_ERRORLIMIT)

// 8 Bytes = 64 Bits to store all 59 DCF bits in a minute
uint8_t dcfBits[8];  
byte dcfBitcount;

boolean parityOK(byte startbit, byte paritybit)
{ // test parity bit in the range startbit ... paritybit-1
  byte p=0;
  for (int i=startbit; i<=paritybit;i++) p+= bitRead(dcfBits[i/8],i%8);
  if (p%2 == 0) return true; // even parity detected
  else return false;
}

byte dcfDecodeBCD(byte startbit, byte numbits)
{ // return BCD encoded byte-value beginning at startbit
  byte b=0;
  for (int i=startbit;i<startbit+numbits;i++) bitWrite(b,i-startbit, bitRead(dcfBits[i/8],i%8));
  return b;
}

boolean dcfDecodeTime()
{
  //int hour, minute, day, month, year;
  byte resultCode, parityError=0;
  // print bits of time code to serial
  //for (int i=0;i<dcfBitcount;i++) Serial.print(bitRead(dcfBits[i/8],i%8));
  //Serial.print('\t');Serial.println(dcfBitcount);
  if(!parityOK(21,28)) bitSet(parityError,0); // parity error minute
  if(!parityOK(29,35)) bitSet(parityError,1); // parity error hour
  if(!parityOK(36,58)) bitSet(parityError,2); // parity error date
  hour   = 10*dcfDecodeBCD(33,2);  // tens of hours, 2 bit 
  hour  += dcfDecodeBCD(29,4);     // single hours, 4 bit 
  minute = 10*dcfDecodeBCD(25,3);  // tens of minutes, 3 bit
  minute += dcfDecodeBCD(21,4);    // single minutes, 4 bit

  day = 10*dcfDecodeBCD(40,2);     // day of month (tens), 2 bit
  day += dcfDecodeBCD(36,4);       // day of month (single), 4 bit
  month = 10*dcfDecodeBCD(49,1);   // tens of month, 1 bit
  month += dcfDecodeBCD(45,4);     // single digit of month, 4 bit
  year = 10*dcfDecodeBCD(54,4);    // tens of year, 4 bit
  year += dcfDecodeBCD(50,4);      // single digit of year, 4 bit
  if (dcfBitcount!=59)
  {
    //Serial.println("Error: Time code must contain 59 bits!");
  }
  else if (parityError)
  {
    //Serial.println("Error: Wrong parity bit detected!");
  }
  else
  {
    Serial.print(hour);Serial.print(':');
    Serial.print(minute);Serial.print(":00  ");
    Serial.print(day);Serial.print('.');
    Serial.print(month);Serial.print('.');
    Serial.println(year);
    dcfBitcount=0;
    return true;
  }
  dcfBitcount=0;
  return false;
}


boolean dcfHandleBit(uint16_t hiTime, uint16_t pulseTime)
{
  if (dcfBitcount<60)
  {
    if (hiTime<150) bitClear(dcfBits[dcfBitcount/8],dcfBitcount%8);
    else  bitSet(dcfBits[dcfBitcount/8],dcfBitcount%8);
    dcfBitcount++;  
  }
  if (pulseTime>=PULSE_MINUTEMARK) return dcfDecodeTime();
  return false;
}

boolean dcfUpdate()
{
  static boolean lastState;
  static unsigned long lastChangeTime;
  static uint16_t hiMillis,loMillis;
  static byte cnt;
  boolean state= digitalRead(DCFPIN);
  if (INVERTEDSIGNAL) state=!state;
  if (state!=lastState)
  {
    long timeDiff=millis()-lastChangeTime;
    lastState=state;
    lastChangeTime+= timeDiff;
    if (state)
    {
      cnt++;
      loMillis+= timeDiff;
      uint16_t pulsetime=hiMillis+loMillis;
      if ((pulsetime>=PULSE_SHORTSECOND && pulsetime<=PULSE_LONGSECOND)|| pulsetime>=PULSE_MINUTEMARK) 
      {
        /*
        Serial.print(cnt);Serial.print('\t');
        Serial.print(hiMillis);Serial.print('\t');
        Serial.print(loMillis);Serial.print('\t');
        Serial.print(pulsetime);Serial.print('\t');
        Serial.println();
        */
        
        boolean result=dcfHandleBit(hiMillis, pulsetime);
        hiMillis=0;
        loMillis=0;
        cnt=0;
        return result;
      }
    }
    else hiMillis+= timeDiff;
  }
  return false;
}




void setup() {
  Serial.begin(9600);
  Serial.println("DCF77 -> easyNET");
  pinMode(DCFPIN, INPUT);
  pinMode(pushB, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  digitalWrite(led,HIGH);
  mcp2515.reset();
  mcp2515.setBitrate(CAN_125KBPS,MCP_8MHZ);
  mcp2515.setNormalMode();
}

void loop() {
  if (dcfUpdate()){
    Serial.println("Erfolgreich Synchronisiert!");
    canMsg.can_id  = 0x40F;
    canMsg.can_dlc = 6;
    canMsg.data[0] = 0x20;
    canMsg.data[1] = hour; //hour
    canMsg.data[2] = minute; //minute
    canMsg.data[3] = day; //day
    canMsg.data[4] = month; //month
    canMsg.data[5] = year; //year
    digitalWrite(led,LOW);
    f=1;
  }else{
  if(digitalRead(pushB)==LOW && f==1){
    mcp2515.sendMessage(&canMsg);
    delay(50);
    mcp2515.sendMessage(&canMsg);
    digitalWrite(led,HIGH);
    f=0;
  }  
    
  }
}
 
Last edited:
For example

40F -> 20 10 22 15 05 16 = 16:34 21.05.2022

Description
0x40F = Adress
0x20 = Function Set time
0x10 = 16 (Hour)
0x22 = 34 (Minute)
0x15 = 21 (Day)
0x05 = 05 (Month)
0x16 = 22 (Year)
 
After many hours of searching and trying, I think I've found the solution to my problem. You have to subtract 2000 from Year.
Also: canMsg1.data[5] = (now.year() - 2000);
 
Back
Top