RTC seconds jumps

I’ve built a RTC that outputs on a 128x96 OLED.

It works well except the seconds counter jumps. it will count 1sec and 2 sec it the right time then move to 3 sec faster then it slows down again for 4 and 5 then faster to 6.

I’ve also noticed it counts to 60 seconds then the seconds units counter column moves to the seconds 10’s column and counts to 9 before moving back to the units column. This must be the clue, its like the seconds units is writing to the wrong part of the screen or is in the wrong format but I cant see where?

 #include <Adafruit_GFX.h>
 #include <Adafruit_SSD1351.h>
 #include "RTClib.h"
 #include <SPI.h>

 // These are OLED hardware spi
#define sclk 52
#define mosi 51
#define dc   15 
#define cs   53//OCS on  big display
#define rst  2

// Color definitions
#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0  
#define WHITE           0xFFFF 
 
RTC_DS3231 rtc;
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs, dc, mosi, sclk, rst);  

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup() 
{
Serial.begin(9600);
tft.begin();
tft.fillScreen(BLACK);
if (! rtc.begin()) {
 Serial.println("Couldn't find RTC");
 while (1);
 }

rtc.adjust(DateTime(__DATE__, __TIME__));
tft.setTextColor(WHITE);
tft.setTextSize(2);
tft.setCursor(0,5);
tft.print("  Clock ");
delay(3000);
tft.fillScreen(BLACK);
}

void loop()
 {
 DateTime now = rtc.now();

 tft.setTextSize(2);
  tft.setCursor(78,0);
  tft.setTextColor(MAGENTA,BLACK);
  tft.println(now.second(), DEC);

tft.setTextSize(2);
 tft.setCursor(25,0);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(":");

tft.setTextSize(2);
 tft.setCursor(65,0);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(":");

tft.setTextSize(2);
 tft.setCursor(40,0);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(now.minute(), DEC);

tft.setTextSize(2);
 tft.setCursor(0,0);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(now.hour(), DEC);

tft.setTextSize(1);
 tft.setCursor(0,17);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(now.day(), DEC);
 tft.setTextColor(MAGENTA,BLACK);
 tft.print(daysOfTheWeek[now.dayOfTheWeek()]);

tft.setTextSize(1);
 tft.setCursor(25,17);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println("-");

tft.setTextSize(1);
 tft.setCursor(40,17);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(now.month(), DEC);

tft.setTextSize(1);
 tft.setCursor(55,17);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println("-");

tft.setTextSize(1);
 tft.setCursor(70,17);
 tft.setTextColor(MAGENTA,BLACK);
 tft.println(now.year(), DEC);

 }

Code please

I've also noticed it counts to 60 seconds then the seconds units counter column moves to the seconds 10's column and counts to 9 before moving back to the units column.

Are you clearing the previous digits before printing the new ones ?

Yes I am using tft.setTextColor(MAGENTA,BLACK); to clear digits.

Its like the units seconds is moving to the tens seconds column?

So it counts ....

.....58,59, 09, 19,29,39,49,59,69.79.89,99,10,12.....etc.

I think its because I am not writing the units column (farthest right when 60 seconds) to black when it starts at 1 second again, as the 1 moves to the left when the next minute starts counting?

How do I do that easily?

kpg:
So it counts …

…58,59, 09, 19,29,39,49,59,69.79.89,99,10,12…etc.

That’s because you are not displaying a leading 0 to make the seconds take both columns. Typically people will do something like this:

  if (seconds < 10)
     lcd.print("0");
  lcd.print(seconds);

You will have to do the same for minutes and hours. Often the hours uses a leading space instead of zero.

Thinking about what you said I tried this as well...

if (now.second () == 59 ){
   tft.setCursor(78,0);
  tft.setTextColor(BLACK,BLACK);
  tft.println(now.second(), DEC);

It almost works in that it gets to 58 and as it transitions to 59 it blanks out, momentarily shows 59 then does the 1-9 correctly after. Maybe a 1 second delay would clean it up?

Also the timer still seems to change its second pulse. It is ok for a few seconds then pauses a little then the next second is quicker? Is this just a quirk of this RTC?

kpg:
Thinking about what you said I tried this as well…

if (now.second () == 59 ){

tft.setCursor(78,0);
 tft.setTextColor(BLACK,BLACK);
 tft.println(now.second(), DEC);




It almost works in that it gets to 58 and as it transitions to 59 it blanks out, momentarily shows 59 then does the 1-9 correctly after. Maybe a 1 second delay would clean it up?

That is not the way,
how about :

tft.setTextSize(2);
  tft.setCursor(78,0);
  tft.setTextColor(MAGENTA,BLACK);
  if (now.second()<10) tft.print("0");
  tft.println(now.second(), DEC);

kpg:
Also the timer still seems to change its second pulse. It is ok for a few seconds then pauses a little then the next second is quicker? Is this just a quirk of this RTC?

No this is because you are printing all sorts of things that are not needed every time around.
Try keeping track of the last now.second() value, if it has changed print the seconds again,
and if also the seconds==0 print the minutes and if also the minutes==0 print the hours. (all of them with the leading zero if required goes almost without saying)
and of course the first time around. Print everything !

Also i think you can set the internal clock of the Arduino to the RTC time and call that instead, the RTC call will take a bit more time than the call to the internal clock.

It seems like you are doing a ton of extra positioning on the display which does take time. rather than use println(), just use print() and the next statements will follow in line without having to reposition the cursor

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "RTClib.h"
#include <SPI.h>

// These are OLED hardware spi
#define sclk 52
#define mosi 51
#define dc   15
#define cs   53//OCS on  big display
#define rst  2

// Color definitions
#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0
#define WHITE           0xFFFF

RTC_DS3231 rtc;
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs, dc, mosi, sclk, rst);

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup()
{
  Serial.begin(9600);
  tft.begin();
  tft.fillScreen(BLACK);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  rtc.adjust(DateTime(__DATE__, __TIME__));
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.setCursor(0, 5);
  tft.print("  Clock ");
  delay(3000);
  tft.fillScreen(BLACK);
}

void loop()
{
  DateTime now = rtc.now();

  tft.setTextSize(2);
  tft.setTextColor(MAGENTA, BLACK);

  tft.setCursor(0, 0);
  if ( now.hour()<10 ) tft.print(" ");
  tft.print(now.hour(), DEC);
  tft.print(":");
  if ( now.minute()<10 ) tft.print("0");
  tft.print(now.minute(), DEC);
  tft.print(":");
  if ( now.second()<10 ) tft.print("0");
  tft.print(now.second(), DEC);

  tft.setTextSize(1);
  tft.setCursor(0, 17);

  tft.print(now.day(), DEC);
  tft.print(daysOfTheWeek[now.dayOfTheWeek()]);
  tft.print("-");
  tft.print(now.month(), DEC);
  tft.print("-");
  tft.println(now.year(), DEC);
  tft.print("   "); // erase any previous date string that was longer
}

Deva_Rishi:
Also i think you can set the internal clock of the Arduino to the RTC time and call that instead, the RTC call will take a bit more time than the call to the internal clock.

There is no internal clock for any of the atmel based Arduino boards. They only know about elapsed time.

I tried the code from blh64's message and it is much better (thanks) but there is still a small delay between some of the seconds countings?

kpg:
I tried the code from blh64's message and it is much better (thanks) but there is still a small delay between some of the seconds countings?

because still the whole lot gets printed every time around whether or not anything has changed.

There is no internal clock for any of the atmel based Arduino boards. They only know about elapsed time.

There is the timelib.h which allows for the elapsed time to be linked to a set time and the time being called referencing that. Not so important since most time gets wasted printing the whole thing and that can take up to a few 1/10 seconds which if it gets done just before it changes to the next second causes the delay for the next second being printed. Keep track of the seconds as such:uint8_t secs=0; and within loop()

void loop()
{
  DateTime now = rtc.now();
  if (secs==now.second() return;
  secs=now.second();
  // etc.

It is still wasteful to print the hours and the minutes etc, but at least you won't see any skips (eg if you want to do other stuff other then printing the time the if statement should change.)

It is still wasteful to print the hours and the minutes

Only print any part of the time when it changes, not only the seconds.

UKHeliBob:
Only print any part of the time when it changes, not only the seconds.

That is what i suggested in #6

Just for completeness, This is where I am now…

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "RTClib.h"
#include <SPI.h>

// These are OLED hardware spi
#define sclk 52
#define mosi 51
#define dc   15
#define cs   53//OCS on  big display
#define rst  2

// Color definitions
#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0
#define WHITE           0xFFFF

RTC_DS3231 rtc;
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs, dc, mosi, sclk, rst);

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
uint8_t secs = 0;

void setup()
{
  Serial.begin(9600);
  tft.begin();
  tft.fillScreen(BLACK);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  rtc.adjust(DateTime(__DATE__, __TIME__));
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.setCursor(0, 5);
  tft.print("  Clock ");
  delay(3000);
  tft.fillScreen(BLACK);
}

void loop()
{
  DateTime now = rtc.now();
  if (secs == now.second())
  {
    return;
    secs = now.second();
  }
  tft.setTextSize(2);
  tft.setTextColor(MAGENTA, BLACK);
  tft.setCursor(0, 0);
  if ( now.hour()<10 ) tft.print(" ");
  tft.print(now.hour(), DEC);
  tft.print(":");
  if ( now.minute()<10 ) tft.print("0");
  tft.print(now.minute(), DEC);
  tft.print(":");
  if ( now.second()<10 ) tft.print("0");
  tft.print(now.second(), DEC);

  tft.setTextSize(1);
  tft.setCursor(0, 17);

  tft.print(now.day(), DEC);
  tft.print(daysOfTheWeek[now.dayOfTheWeek()]);
  tft.print("-");
  tft.print(now.month(), DEC);
  tft.print("-");
  tft.println(now.year(), DEC);
  tft.print("   "); // erase any previous date string that was longer
}

Hopefully I have understood the last messages but I still seem to get the seconds ‘hesitating’ at some points.

  if (secs == now.second())
  {
    return;
    secs = now.second();
  }

I am not sure if it is related to the problem but the second line of code in the braces will never be executed

UKHeliBob:

  if (secs == now.second())

{
    return;
    secs = now.second();
  }



I am not sure if it is related to the problem but the second line of code in the braces will never be executed

It is for sure related ! i didn't put those braces. If you want to put braces (for which there is no need) put the second line below the closing brace. (sorry if it is just one line of code that needs to be conditionally executed, i omit the braces, some people find that confusing (i am betting those people did not start out coding using Basic)

Solved…Thanks a lot guys.

My bad on the misplaced bracket!!

For anyone else interested this is the final code.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "RTClib.h"
#include <SPI.h>

// These are OLED hardware spi
#define sclk 52
#define mosi 51
#define dc   15
#define cs   53//OCS on  big display
#define rst  2

// Color definitions
#define  BLACK           0x0000
#define BLUE            0x001F
#define RED             0xF800
#define GREEN           0x07E0
#define CYAN            0x07FF
#define MAGENTA         0xF81F
#define YELLOW          0xFFE0
#define WHITE           0xFFFF

RTC_DS3231 rtc;
Adafruit_SSD1351 tft = Adafruit_SSD1351(cs, dc, mosi, sclk, rst);

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
uint8_t secs = 0;

void setup()
{
  Serial.begin(9600);
  tft.begin();
  tft.fillScreen(BLACK);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  rtc.adjust(DateTime(__DATE__, __TIME__));
  tft.setTextColor(WHITE);
  tft.setTextSize(2);
  tft.setCursor(0, 5);
  tft.print("  Clock ");
  delay(3000);
  tft.fillScreen(BLACK);
}

void loop()
{
  DateTime now = rtc.now();
  
  tft.setTextSize(2);
  tft.setTextColor(MAGENTA, BLACK);
  tft.setCursor(0, 0);
  if ( now.hour()<10 ) tft.print(" ");
  tft.print(now.hour(), DEC);
  tft.print(":");
  if ( now.minute()<10 ) tft.print("0");
  tft.print(now.minute(), DEC);
  tft.print(":");
 if (secs == now.second())//do more of these IF's to save time on days and hours etc. not printing
  {
    return;
   
  }
   secs = now.second();
  if ( now.second()<10 ) tft.print("0");
  tft.print(now.second(), DEC);

  tft.setTextSize(1);
  tft.setCursor(0, 18);
  tft.print(now.day(), DEC);
  tft.print("-");
  tft.print(now.month(), DEC);
  tft.print("-");
  tft.print(now.year(), DEC);
  tft.setCursor(0, 30);
  tft.print(daysOfTheWeek[now.dayOfTheWeek()]);
  tft.print("   "); // erase any previous date string that was longer
}

Well i think you would be better of moving the test for a change in the seconds to right after you’ve read the time. Now you are still printing the hours and the minutes every time around.

void loop()
{
  DateTime now = rtc.now();

  if (secs == now.second())
  {
    return;
   
  }
   secs = now.second();
  
  tft.setTextSize(2);
  tft.setTextColor(MAGENTA, BLACK);
  tft.setCursor(0, 0);
  if ( now.hour()<10 ) tft.print(" ");
  tft.print(now.hour(), DEC);
  tft.print(":");
  if ( now.minute()<10 ) tft.print("0");
  tft.print(now.minute(), DEC);
  tft.print(":");
 
  if ( now.second()<10 ) tft.print("0");
  tft.print(now.second(), DEC);

  tft.setTextSize(1);
  tft.setCursor(0, 18);
  tft.print(now.day(), DEC);
  tft.print("-");
  tft.print(now.month(), DEC);
  tft.print("-");
  tft.print(now.year(), DEC);
  tft.setCursor(0, 30);
  tft.print(daysOfTheWeek[now.dayOfTheWeek()]);
  tft.print("   "); // erase any previous date string that was longer
}

Your right it does make a small improvement. Thanks again for all the help. Really appreciate it.

This chunk of code slots in nicely to the larger project now. Basically I am taking an old 1970's motorcycle and computerising the whole thing........

Various sensors
Menu pages on OLED
Resources monitoring and predictions
Phone link (Bluetooth)

All good fun!