Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Teensy 4.0 and (S)RTC

  1. #1

    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/60317...eensy+4.0+srtc
    https://forum.pjrc.com/threads/57176...eensy+4.0+srtc
    https://forum.pjrc.com/threads/58249...eensy+4.0+srtc
    https://forum.pjrc.com/threads/58191...eensy+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

  2. #2
    Senior Member
    Join Date
    Oct 2019
    Location
    Calgary
    Posts
    103
    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.

  3. #3
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,753
    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.

  4. #4
    @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.

  5. #5
    @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?

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,753
    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.

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,753
    Doing other stuff after machine update and restart - but this post may point to the example to follow: pjrc.com/threads/63928-Teensy-TimeLib-question

  8. #8
    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.

  9. #9
    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')?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •