Unreliable time keeping

Working on a sketch where I will want to timestamp logs of an event. The project also displays information on a 4 x 20 LCD (on the I2C bus at the default address of 0x27) hopefully including the current date and time. First I tried the RTC simulation running via RTCZero library. I got the epoch from the NTP server ok, and have a little routine to display the time on a 4x20 LCD display. That all worked.

However, it’s wildly inaccurate, losing minutes per hour. Or gaining minutes per hour, depending on the specific Nano 33 IOT it was running on. So I decided to try a DS1307 RTC module. It is also on the I2C bus with an address of 0x86.

Running a simple test script from the RTClib.h examples library it seems to work fine. Serial.print shows the updated time reliably every second.

The problems crop up when I try to display the date/time on the LCD. There are glitches – intermittent spurious values displayed. And those values seem to be coming from the RTC, as they are duplicated in the serial monitor. The timing is random – it may take several minutes or just a few seconds to start glitching. Once it starts, it seems to become more frequent and eventually the display freezes at some random values, although the serial monitor shows it’s still running and producing garbage.

To clarify then: the LCD alone works fine. The RTC alone works fine. The problem seems to be related to having both on the same I2C bus. I have tried all combinations of two separate RTC modules, two separate LCD displays and two separate Nano 33 IOTs. This does not appear to be a hardware issue.

Here is the sketch (as modified):

/*
 * Created by ArduinoGetStarted.com
 *
 * This example code is in the public domain
 *
 * Tutorial page: https://arduinogetstarted.com/tutorials/arduino-lcd-clock
 */

#include <LiquidCrystal_I2C.h>
#include <RTClib.h>

// modified the following to suit 4x20 display:
LiquidCrystal_I2C lcd(0x27, 20, 4);  // I2C address 0x27 (from DIYables LCD), 16 column and 2 rows
RTC_DS1307 rtc;

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

  lcd.init();       // initialize the lcd
  lcd.backlight();  // open the backlight

  // SETUP RTC MODULE
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    while (true)
      ;
  }

  // automatically sets the RTC to the date & time on PC this sketch was compiled
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}

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

  int year = now.year();
  int month = now.month();
  int day = now.day();
  int hour = now.hour();
  int minute = now.minute();
  int second = now.second();

  lcd.clear();
  lcd.setCursor(0, 0);  // start to print at the first row
  lcd.print("Date: ");
  lcd.print(year);
  lcd.print("/");
  lcd.print(month);
  lcd.print("/");
  lcd.print(day);

  lcd.setCursor(0, 1);  // start to print at the second row
  lcd.print("Time: ");
  
  // added if statement to print leading zero as required. Same with minutes/seconds  
  if(hour < 10){
    lcd.print("0");
  }
  lcd.print(hour);
  lcd.print(":");
  if(minute < 10){
    lcd.print("0");
  }
  lcd.print(minute);
  lcd.print(":");
  if(second < 10){
    lcd.print("0");
  }
  lcd.print(second);

// I added these Serial.print lines to see what's going on:
  Serial.print("Date: ");
  Serial.print(year);
  Serial.print("/");
  Serial.print(month);
  Serial.print("/");
  Serial.print(day);

  Serial.print(" Time: ");
  if(hour < 10){
    Serial.print("0");
  }
  Serial.print(hour);
  Serial.print(":");
  if(minute < 10){
    Serial.print("0");
  }
  Serial.print(minute);
  Serial.print(":");
  if(second < 10){
    Serial.print("0");
  }
  Serial.println(second);

  delay(1000);  // Update every second
}

Sample serial monitor output:


08:45:03.815 -> Date: 2025/12/14 Time: 08:44:17
08:45:04.862 -> Date: 2025/12/14 Time: 08:44:18
08:45:05.943 -> Date: 2000/61/109 Time: 104:158:00
08:45:06.989 -> Date: 2025/12/14 Time: 08:44:20
08:45:08.059 -> Date: 2025/12/14 Time: 08:44:21
08:45:09.057 -> Date: 2000/61/109 Time: 134:151:00
08:45:10.191 -> Date: 2025/12/14 Time: 08:44:23
08:45:11.188 -> Date: 2025/12/14 Time: 08:44:24
08:45:12.218 -> Date: 2025/12/14 Time: 08:44:25

Sample of serial monitor output after LCD freezes:

08:46:33.883 -> Date: 2025/12/14 Time: 08:45:47
08:46:34.920 -> Date: 2025/12/14 Time: 08:45:48
08:46:35.962 -> Date: 2000/61/109 Time: 162:153:00
08:46:36.965 -> Date: 2000/61/109 Time: 12:51:00
08:46:38.011 -> Date: 2000/61/109 Time: 21:112:00
08:46:38.962 -> Date: 2000/61/109 Time: 31:14:00
08:46:39.966 -> Date: 2000/61/109 Time: 40:76:00
08:46:40.956 -> Date: 2000/61/109 Time: 49:137:00
08:46:41.996 -> Date: 2000/61/109 Time: 59:40:00
08:46:42.992 -> Date: 2000/61/109 Time: 68:101:00
08:46:44.005 -> Date: 2000/61/109 Time: 77:163:00
08:46:44.989 -> Date: 2000/61/109 Time: 87:65:00
08:46:45.997 -> Date: 2000/61/109 Time: 96:121:00
08:46:47.034 -> Date: 2000/61/109 Time: 106:22:00
08:46:48.025 -> Date: 2000/61/109 Time: 115:85:00

I thought maybe it is a voltage issue, since both I2C devices are 5VCC and the Nano 33 IOT is 3.3VCC, so I inserted level shifters (Adafruit 4 Channels IIC I2C Bi-directional Logic Level Converter) with the device side of the bus at 5v (IE: SCL of RTC and LCD connected together at 5v side of one level shifter with 33 IOT SCL pin to the corresponding 3.3v side; SDA of RTC and LCD connected to 5v side of a second level shifter with 33 IOT SDA pin connected to corresponding 3.3v side of the second level shifter).

That did not make any difference.

Power to the Nano 33 IOT is supplied from a regulated 12v supply. Power to the 5v devices is via an LM7805* regulator. Power to the 3.3v side of the level shifters is via an AMS1117-3.3 step-down buck module. Power at all points seems stable when examined on an oscilloscope.

The fallback will be to revert to the RTCzero simulated RTC routine and just update the time via NTP every hour. But that relies on an internet connection and I really rather get a real RTC working. Any suggestions?

*Edited LM3305 typo to LM7805

isn't that 0x68 ?

The Nano 33 IoT is a 3.3V board. This means it uses 3.3V logic levels, and if you're connecting it to peripherals like the DS1307 RTC or an I2C LCD, which are often designed for 5V logic, you could run into issues.

Do you have a voltage adapter?

Please post a wiring diagram. Hand drawn is preferred, with pins, parts and connections clearly labeled.

Also post a link to the LCD. Many, if not most are 5V parts and will not operate reliably from a 3.3V processor without level shifters.

By connecting and operating the 5V and 3.3V parts without using logic level shifters, you may already have done some damage to them.

One thing to check is whether each I2C device has its own pullup resistors. If they do, then the equivalent parallel pullup resistance may be too low, and the processor is having trouble bringing the lines low. If each device works ok by itself, but when both are connected it gets flaky, that could be the reason. I don't know if the level shifters you tried would eliminate this problem. They probably have their own pullup resistors.

Then on top of that, you may have pullup resistors to 3.3V on the Nano 33. So pullups to 5V and pullups to 3.3V. Hard to guess what may be going on.

Adafruit also offers an I2C multiplexer. It puts each I2C device on its own circuit, so you can mix and mingle different voltages.

Nick Gammon has an I2C scanner tool on his website.

A further look at the schematic for the 33 IOT v2 shows the I2C lines are connected to three devices on the main board - some position sensor, the radio chip, and the crypto chip. And there are 4.7K resistors on those lines on the board, to 3.3V. If your display also has 4.7K pullups, to 5V, and the same for the DS1307, then you could have a real mess. However, if you use the bidirectional shifters as you described, that should help since then you would only have one additional pullup, to 3.3V, on the lines. So I think it's odd that adding them doesn't help, unless their pullups are really low, like 1K.

I don't see anything in the software that would mess up reading from the DS1307. You do that at the beginning of each loop() iteration, and I don't see anything that would mess that up.

With the power off, but everything connected, including the bidirectional shifters, measure the resistance between the A4 and 3.3V pins of the Nano, then the same for A5. Then measure them again with the meter leads reversed. If there's any difference, the higher value should be the net parallel pullup resistance. Edit: Then try the same thing on the device side, measuring the resistance between their SDA and SCL pins and their 5V supply.

Edit: It looks like the Adafruit shifters use 10K pullups. That shouldn't be a problem. So it's a mystery.

(Note: I have tried both level shifting as shown above with both 5v devices connected before the level shift and also with each device shifted separately, combining the signals on the 3.3v side. No difference. Currently configured as the latter.)

I am using this LCD display:

The I2C daughter board has 4.7K pull-up resistors. The RTC module has 3.3K resistors. The level shifter is 10K on each side. And yes, that was a typo: the RTC address is indeed 0x68 not 0x86.

The Nano 33 IOT “some position sensor” (aka “IMU”) is at address 0x6A, so it shouldn’t be interfering. Similarly, the crypto chip is at 0x60.

I couldn’t find the radio address, but running the aforementioned scanner program produces:

4:13:43.770 -> I2C scanner. Scanning ...
14:13:43.770 -> Found address: 39 (0x27)
14:13:43.770 -> Found address: 80 (0x50)
14:13:43.770 -> Found address: 96 (0x60)
14:13:43.770 -> Found address: 104 (0x68)
14:13:43.770 -> Found address: 106 (0x6A)
14:13:43.770 -> Done.
14:13:43.770 -> Found 5 device(s).

So I’m guessing the radio is at 0x50.

Thanks to all who replied! But I’m still not sure what’s going on. I changed the clock code to update every minute instead of every second and it hasn’t glitched yet, but that may just be the law of averages at work: 1/60th of the chance each iteration.

Update: Yes it still glitches. And freezes.

When I reboot, it comes back with the correct time. So the RTC is working.

So the parallel pullup resistance on the 5V side of the shifter is 1.86K. I think that's pretty low, but I guess it still should work. That's 2.7mA to bring the line low. But in addition, the IC2 pin on the processor also has to pull down the 10K on its side of the shifter, plus the 4.7K on the board itself, both of those going to 3.3V.

Is it practical to remove the pullup resistors from the DS1307?

I can certainly try that – although I’m away from home for a couple of days so it’ll be a bit before I have the opportunity.

I don’t think that will affect what is seen on the Arduino side of the level shifter though.

I was actually considering removing the pull-ups from both the display and the RTC and bypassing the level shifter. Just let the on-board pull-up resistors handle the whole circuit.

I was thinking that too, but it's not right. The bidirectional level shifter is described in this NXP app note:

https://cdn-shop.adafruit.com/datasheets/AN10441.pdf

When either side pulls the line low, the mosfet turns on, so there is a direct connection between the two sides. The only time they are not connected is when both sides are high.

I think the bottom line is that when the MCU brings a line low, it pulls current through every pullup resistor connected to the line, on both sides, and the ones in the level shifter itself. Moreover, every one of the I2C peripherals also has to do that when it wants to take a line low, such as when returning data on SDA. So their I2C pins also have to be able to sink all that current, or at least SDA does. And actually, if this is the problem, the level shifter just makes it worse - by adding even more pullup resistors.

I think that might work. The only question is whether the 5V peripherals, particularly the display, would detect 3.3V as high. If they don't, you could try powering them through a Schottky diode so their Vcc is 4.7V. That would make 3.3V input more likely to work. However, I don't know whether the pullup resistance has to be "distributed" to both ends of the line. If the lines are long enough, that might become an issue. The engineers can opine on that.

From the Freenove github site, I was able to download this specsheet for the LCD display module.

It states that the LCD 2004 module can be powered from 3.3v to 5v and that both the SDA and CLK can be 3.3v to 5v. I’m not sure if that 3.3vcc figure is for the display itself, the adapter or both. Some manufacturers can play loose and fast with those kinds of numbers.

The Maxim DS1307 datasheet indicates VCC should be min 4.5v, but Logic high is 2.2v to VCC +.8V and logic low is -.03v to +.08v.

So I believe both devices should be compatible with 3.3v on SDA and SCL.

I like your hand drawn and coloured schematic!

One convention that is became less unconventional at some point is the little bunny hops in you signal wires.

These days, if lines need to cross you can just run them right over other signals.

If two crossing signals are meant to be the same, a dot at the intersection signifies an electrical connection.

a7

What can I say? I’m old. My first forays into electrical schematics involved tubes, paper capacitors and cloth-covered wires in dead bug configurations.

Your schematic shows an LM7805 supplying 5 volts. Your text says it's an LM3305. I'm unfamiliar with the latter, but for former requires input and output capacitors to ensure its output remains stable. Your schematic doesn't show any caps.

Your drawing shows 3.3V connected to Nano's VIN pin, shouldn't that be the 3.3V pin?

To me it looks like he has 12V going to Vin, which would be the right pin for 12V input.

Huh. No idea how I typed "LM3305". Good catch, I edited the original to correctly identify it as an LM7805. Input and output caps are not technically required if the circuit paths are short enough, but I did in fact add them. I also mounted it to a heat sink. It was my intent to just show the 5v supply as a block diagram. I did check the output for ripple on a scope and it's negligible. Same with the 3.3v supply.

And yes, I supplied the board with 12v at Vin. I did not use the 3.3v output pin of the Nano to supply the level shifter as I plan to use that for the SD card.

Update: left it running for >24 hours, when I came back the display was a garbled mess of garbage characters.

I've switched the LCD over to 3.3v vcc and SDA/SCL direct to the Nano. It does work powered by 3.3v, although the contrast pot must be cranked up all the way to make characters appear and even then it's a bit muddied. Definitely crisper display with a 5v vcc. So right now it's running with the RTC vcc @ 5v and only the RTC SDA/SCL through the level shifters. We'll see how long that lasts. I'll reinstate the seconds display to accelerate the process.

Next step will be to lift the pull-ups on the RTC and try eliminating the level shifter.

Nope. Still glitching:

11:34:58.229 -> Date: 2025/12/16 Time: 11:34:43
11:34:59.241 -> Date: 2000/60/43 Time: 48:85:00
11:35:00.224 -> Date: 2025/12/16 Time: 11:34:45

I would try updating every 2, 3, 4, or 5 seconds.

Also, what happens if you don’t update the LCD?

I would try that as well.