-1

The RTC module can output the hours as 165, and the minutes as 165. This is some random number that is different every time. Everything works fine, but at random times I get this. Maybe once every half a minute, or more often. It happens randomly. On the next time update after this I get the correct time values.

I am using the RTClib library from Adafruit and a homemade board on ATmega328 programmed into the Arduino IDE. The DS1307 RTC module connection is copied from a purchased module. On the IIC lines there are pull-up resistors with a nominal value of 4.7K near the RTC chip. The pull-up resistors are removed from the OLED screen board, since they are already present near the RTC chip. But if I solder the resistors back to the OLED board, the behavior does not change.

The values of hours and minutes are displayed in the port monitor and on the SSD1306 OLED display. And here's what the problem looks like:

Serial monitor output

OLED screen output

I also add my code:

// Бібліотеки
#include <Wire.h>
#include <Adafruit_GFX.h>         // https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_SSD1306.h>     // https://github.com/adafruit/Adafruit_SSD1306
#include "RTClib.h"               // https://github.com/adafruit/RTClib
#include <Fonts/DSEG7_Classic_Mini_Bold_32.h>

// Ініціалізація
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
RTC_DS1307 rtc;

// Системні змінні
unsigned long timing = 0;   // Відлік оновлення циферблату
boolean dd = 1;             // Режим показу двокрапки ":"
int dh, dm;              

void setup() {
  Serial.begin(9600);
  rtc.begin();
  display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
  display.setFont(&DSEG7_Classic_Mini_Bold_32);
}

void loop() {
  if (millis() - timing >= 500) {
    timing = millis();
    display.clearDisplay();
    display.setCursor(0,32);
    
    DateTime now = rtc.now();
    dh = now.hour(), DEC;
    dm = now.minute(), DEC;

    Serial.print(dh);
    Serial.print(" : ");
    Serial.println(dm);
    display.setTextColor(SSD1306_WHITE);
    if (dh < 10) {
      display.print(0);
    }
    display.print(dh);
    if (!dd) {
      display.setTextColor(SSD1306_BLACK);
    }
    display.print(":");
    display.setTextColor(SSD1306_WHITE);
    if (dm < 10) {
      display.print(0);
    }
    display.print(dm);
    display.display();
    dd = !dd;
  }
}

Maybe someone had such a problem. I already checked my board, everything is fine with it. The code also seems to be fine. I hope you read to the end.

I checked the connection for correctness. I looked visually. Then I checked the connection with a multimeter. Where it is needed, it is there. There are no short circuits on the board. All power supplies are present. The battery has a voltage of 3.1V. The voltage on the RTC and microcontroller is 5V. I took another RTC module and an Arduino Nano. I assembled the circuit on a breadboard and used the same code. After waiting for 10 minutes, I found no bugs. Maybe my RTC chip in DIY pcb is damaged?

Now I have used a crutch in the code. Since most time queries are output correctly, I check if the new response to the query is no more than one unit relative to the old one. Code without oled display. It is not connected.

#include <Wire.h>
#include "RTClib.h"               // https://github.com/adafruit/RTClib
RTC_DS1307 rtc;
unsigned long timing = 0;
boolean dd = 1;
int dh, dm, dh_old, dm_old;             

void setup() {
  Serial.begin(9600);
  rtc.begin();
}

void loop() {
  if (millis() - timing >= 500) {
    timing = millis();
    dh_old = dh;
    dm_old = dm;
    DateTime now = rtc.now();
    dh = now.hour();
    dm = now.minute();
    if (dd) {
      if (dh - dh_old <=1 && dm - dm_old <=1) {
        if (dh < 10) {
            Serial.print(0);
        }
          Serial.print(dh);
          Serial.print(" : ");
        if (dm < 10) {
            Serial.print(0);
        }
          Serial.println(dm);
      }
      else {
        Serial.print("[ERROR] ");
        Serial.print(dh);
        Serial.print(" : ");
        Serial.print(dm);
        Serial.println(" [ERROR]");
      }
    }
    dd = !dd;
  }
}

This is what I got in the serial monitor:
12 : 00
12 : 00
[ERROR] 15 : 0 [ERROR]
[ERROR] 15 : 0 [ERROR]
12 : 00
12 : 00

Half the solution for my problem:

The isValid() check function did not help me. I get the correct data within 0-23 hours, 0-59 minutes and further. But the time is incorrect. For example, the current time is 15:15, and after updating I can get 23:13. isValid() says that the data is correct, although it is not. So I made my own function to check. Since I update the data every half a second, the hours and minutes cannot exceed one minute (hour) or more. Now I just need to make sure that the received time is correct when the device is turned on, and the function checks the correctness relative to the update moment when the device is turned on. My verification function:

bool testRTC(DateTime now, DateTime prev) {
  long deltaMinutes = (now.unixtime() - prev.unixtime()) / 60;
  return (deltaMinutes >= 0 && deltaMinutes <= 1);
}

Example usage in code. You just need to store the correct time and new time in some variables. I use mynow and prevnow of type DateTime.

#include "RTClib.h"               // https://github.com/adafruit/RTClib
RTC_DS1307 rtc;
DateTime mynow, prevnow;
unsigned long timing1 = 0;
int hh, mm;

void setup() {
  Serial.begin(9600);
  rtc.begin();
  // Trying to get the time right
  // So far I don't know how to make sure that the first 
  // time retrieval from the RTC will be successful 
  // and the time will be correct.
  if (rtc.isrunning()) {
    do
      mynow = rtc.now();
    while (!mynow.isValid());
    prevnow = rtc.now(); // Updating the last valid time variable
  }
}

void loop() {
  if (millis() - timing1 > 500 && rtc.isrunning()) { // Update every half a second
    mynow = rtc.now(); // Getting a new time
    if (testRTC(mynow, prevnow)) { // Calling the validation function
      prevnow = mynow;  // If the time is correct, write the new time into the last correct time variable.
      hh = mynow.hour(); // Just write the value of the hours into a variable
      mm = mynow.minute(); // Just write the value of the minutes into a variable
      Serial.print("[ OK] ");
      Serial.print(prevnow.hour());
      Serial.print(":");
      Serial.println(prevnow.minute());
    }
    else { // If the time differs by one minute
      Serial.print("[ERR] ");
      Serial.print(mynow.hour());
      Serial.print(":");
      Serial.println(mynow.minute());
    }
  }
}

// Time validation
bool testRTC(DateTime now, DateTime prev) {
  long deltaMinutes = (now.unixtime() - prev.unixtime()) / 60;
  return (deltaMinutes >= 0 && deltaMinutes <= 1);
}

The other half of the solution is to make sure that the microprocessor gets the correct time when it is first turned on. I am on the way to doing this and am using the isValid() function, but it has proven unreliable. Thank you all very much!

10
  • 1. How much free memory (flash and RAM) is reported when you upload the sketch? 2. Does the program work as expected if you remove all the code related to the OLED display? Commented Jul 23 at 14:08
  • How do you powered the RTC module? There are some tutorials on the internet connecting the RTC to 3.3V which is wrong. As per DS1307 datasheet "The nominal power-fail trip point (VPF) voltage at which access to the RTC and user RAM is denied is set by the internal circuitry as 1.25 x VBAT nominal", what this is saying is that if you have a coin cell backup battery of 3V, the VCC need to be at least 1.25x3.0 =3.75V or higher. Otherwise, the RTC might think there is a power failure and going into backup mode, causing the glitches that you saw. Also check the backup battery voltage.
    – hcheung
    Commented Jul 23 at 14:28
  • The sketch uses 21126 bytes (68%) of the program storage. Global variables use 539 bytes (26%) of the dynamic memory. I already know about the nuance with the supply voltage. In another project with a voltage of 3.3V I used DS1338Z-33 that works with a voltage of 3.3V. However, here the supply voltage is 5V for the DS1307
    – Rodney
    Commented Jul 23 at 16:45
  • 1
    Everything works fine ... no, it does not
    – jsotola
    Commented Jul 23 at 20:19
  • I already checked my board, everything is fine with it.... how did you check it?
    – jsotola
    Commented Jul 23 at 20:32

1 Answer 1

0

Given this:

I took another RTC module and an Arduino Nano. I assembled the circuit on a breadboard and used the same code. After waiting for 10 minutes, I found no bugs.

it definitely looks like a hardware problem.

Now I have used a crutch in the code. Since most time queries are output correctly, I check if the new response to the query is no more than one unit relative to the old one.

There may be a simpler solution. The DateTime class of RTClib has an isValid() method that tells you whether all the fields make sense both individually (e.g. hour<24) and when taken together (the day number should be no greater than the length of the month). You may simply query the RTC until you get a valid DateTime.

I would suggest also another simplification: the font you are using is designed specifically to make it easy to add and remove the colon between the hours and the minutes: this glyph has the exact same width as the space. So you don't need to display a black colon in order to erase it. Also, DateTime has a formatting function that you can use, and automatically takes care of leading zeros.

Putting all together:

void loop() {
    // Update only once every 500 ms.
    if (millis() - timing < 500)
        return;
    timing += 500;

    // Query the RTC until we get a valid time.
    DateTime now;
    do
        now = rtc.now();
    while (!now.isValid());

    // Format the time as a string.
    char formatted_time[6];
    strcpy(formatted_time, showColon ? "hh:mm" : "hh mm");
    now.toString(formatted_time);
    showColon = !showColon;

    // Print it.
    Serial.println(formatted_time);
    display.clearDisplay();
    display.setCursor(0, 32);
    display.print(formatted_time);
}

Note that I renamed dd (which is not a very meaningful name) as showColon.

3
  • If I use the isValid() check... Does this mean that when the correct time is 15:15, the RTC will give the result 16:16 and that will be valid? showColon is a good variable name. I couldn't think of a normal name)
    – Rodney
    Commented Jul 26 at 2:28
  • @Rodney: 16:16 would be valid if the other fields returned by the RTC (seconds, day, month) are valid. If the communication returns crap, getting a fully valid DateTime seems unlikely. Commented Jul 26 at 8:54
  • 1
    Haha) I am using your code and doing data validation using now.isValid(). It seems that this does not work in my case, as my watch shows the wrong time periodically. Thank you all for solving my problem! You gave me useful code and added food for thought. Next I will return the time check with an increase of one and try to replace the RTC chip. Maybe my experience will also be useful to someone else
    – Rodney
    Commented Jul 28 at 18:54

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.