LCD on I2C disrupting RTC

Hello Helpful Community!
As a small part of a fully automated hydroponic system I want to switch a relay (LOW level trigger, to power on and off an LED driver) based on time from an RTC DS 1370.
All works fine... until the LCD gets connected to the I2C bus. Then, before the time comes to switch ON the relay, all serial monitor readings look good, but after the initial switch ON (which works) the RTC either gives 165:165 or random numbers or stops at all or the relay gets switched OFF and ON at short random intervals. This very simple system freezes.
I already spent days working thru this forum or other corners of the internet, but nothing seems to help in this case, so I decided to open a new topic hoping to find expert advise.
Your help is highly appreciated! Thank you very much!

My hardware setup:

  • ARD-NanoV4-MC (ATmega328PB, fully Arduino Nano compatible)

  • DS1307 I2C RTC Modul - Tiny RTC (on verified address 0x68)

  • 20x04 I2C LCD Modul with HD44780 (on verified address 0x27)

  • Solid State Relay Card, with OMRON 1565E G3MB 202P, Optocouplers on in and out

My stripped down test sketch:

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

// Initialize the RTC
RTC_DS1307 rtc;

// Initialize the LCD with the I2C address
LiquidCrystal_I2C lcd(0x27, 20, 4);

// Relay pin
const int relayPin = 9;

// Sunrise and sunset times (24-hour format)
const int sunriseHour = 6;  // Set sunrise hour here
const int sunriseMinute = 0; // Set sunrise minute here
const int sunsetHour = 22;  // Set sunset hour here
const int sunsetMinute = 0; // Set sunset minute here

bool sunriseTriggered = false;
bool sunsetTriggered = false;

void setup() {
  // Start the I2C interface
  Wire.begin();
    // Initialize the LCD
  lcd.init();
  lcd.backlight();
  // Initialize the Serial Monitor
  Serial.begin(9600);
  // Initialize the RTC
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  // Check if the RTC is running, otherwise set the date and time
  if (!rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // Uncomment to set the date and time
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  // Initialize relay pin
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH); // Ensure relay is off initially
}

void loop() {

  // Get the current date and time
  DateTime now = rtc.now();

  // Display the date and time on the Serial Monitor
  Serial.print("Date: ");
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(" Time: ");
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();

  // Display the date
  lcd.setCursor(0, 0);
  lcd.print("Date: ");
  lcd.print(now.year(), DEC);
  lcd.print('/');
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.day(), DEC);

  // Display the time
  lcd.setCursor(0, 1);
  lcd.print("Time: ");
  lcd.print(now.hour(), DEC);
  lcd.print(':');
  lcd.print(now.minute(), DEC);
  lcd.print(':');
  lcd.print(now.second(), DEC);

  // Check if it's time to switch the relay
  checkRelaySwitch(now);

  // Update every second
  delay(1000);
}

void checkRelaySwitch(DateTime now) {
  // Calculate current time in minutes
  int currentMinutes = now.hour() * 60 + now.minute();
  int sunriseMinutes = sunriseHour * 60 + sunriseMinute;
  int sunsetMinutes = sunsetHour * 60 + sunsetMinute;

  // Control the relay based on the current time
  if (currentMinutes == sunriseMinutes && !sunriseTriggered) {
    // It's the exact time of sunrise, turn relay on
    digitalWrite(relayPin, LOW);
    Serial.println("Relay turned ON at sunrise.");
    sunriseTriggered = true;
    sunsetTriggered = false; // Reset sunset trigger for the next day
  } else if (currentMinutes == sunsetMinutes && !sunsetTriggered) {
    // It's the exact time of sunset, turn relay off
    digitalWrite(relayPin, HIGH);
    Serial.println("Relay turned OFF at sunset.");
    sunsetTriggered = true;
    sunriseTriggered = false; // Reset sunrise trigger for the next day
  }
}

Kick up the baud rate on the serial, at least 38400.

Edit: Is there a ground connection from the relay input to the Nano? Should be.

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27, 20, 4);
const byte relayPin = 9;

// Sunrise and sunset times (24-hour format)
const byte sunriseHour = 6;  // Set sunrise hour here
const byte sunsetHour = 22;  // Set sunset hour here

bool isDay = false;

void setup() {
  digitalWrite(relayPin, HIGH); // Ensure relay is off initially
  lcd.init();
  lcd.backlight();
  Serial.begin(9600);
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  // Check if the RTC is running, otherwise set the date and time
  if (!rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
  }
  pinMode(relayPin, OUTPUT);
}

void loop() {
  DateTime now = rtc.now();  // Get the current date and time
  char DateText[21];
  sprintf(DateText, "Date: %u/%02u/%02u ", now.year(), now.month(), now.day());
  char TimeText[21];
  sprintf(TimeText, "Time: %02u:%02u:%02u", now.hour(), now.minute(), now.second());

  Serial.print(DateText);
  Serial.println(TimeText);

  lcd.setCursor(0, 0);
  lcd.print(DateText);
  lcd.setCursor(0, 1);
  lcd.print(TimeText);

  if (now.minute() == 0) {
    bool isTime = (now.hour() >= sunriseHour) && (now.hour() < sunsetHour);
    if (isDay != isTime) {
      isDay = isTime;
      digitalWrite(relayPin, !isDay);
      if (isDay)Serial.println("Relay turned ON at sunrise.");
      else Serial.println("Relay turned OFF at sunset.");
    }
  }
  delay(1000);  // Update every second
}

Can you show a photo of the project and a photo of the power supply.
Do you have a multimeter ? To measure all the voltages.

If your display is not nearby the Nano board, and is connected via a flat bed cable, then this will happen.
If the power supply is very noisy (for example a 5V Power Supply that is only for leds), then this can also happen.
If your project is on a breadboard, then breadboards can have bad contacts and jumper wires can be broken.

Hi Surfer Tim, thanks for your input!

That doesn´t address the issue. No change in behaviour.

On the control side we have Signal, Vcc and GND. GND is connected to the common GND.
On the switch side there is only 230V N going to the LED driver.

Hi @kolaha,
Thanks for this code, but it doesn´t change anything, though I really like your elegant time code.
Here is the serial reading of the switch moment:
serial different code

How long are the wires you are using to the I2C peripherals ? If these are long try either shortening them, use stronger I2C pull up resistors or slow down the I2C bus clock rate.

Hi @Koepel ,
thanks for your input!
The LCD is behind 1 meter of 0.75 mm^2 twisted copper strand.
My slow simple multi-meter is reading:

  • at the LCD Vcc=4,96V stable , SDA=4,95V max , SCL=4,95V max (without pullup resistors SDA and SCL =4,94V max)
  • at the RTC Vcc=4,97V stable , SDA=4,96V max , SCL=4,96V max (without pullup resistors SDA and SCL =4,95V max)

Fiddling around with the wobbly jumper cables doesn´t show any difference, so it seems that all connections on the breadboard are stable.



Connections at LCD

The moment of truth

Thank you for the photos.
You have build to it in a better way, because this is not possible.

Good:

  1. A 12V Mean Well power supply to a voltage regulator and powering the Nano via the 5V pin is okay.
  2. The measured voltages are okay.
  3. The solid state relays are better than the mechanical ones. The module might not pass the rules for using the main voltage. In most cases those relays are still okay.

Bad:

  1. A final project with a breadboard is not okay. The contacts of a breadboard are not reliable.
  2. The I2C bus in that cable is not okay. The I2C bus can not deal with crosstalk between SDA and SCL. If you do it wrong, then the official standard gives you 10 cm for the maximum length of a I2C bus. See page 54, paragraph 7.5 in the official standard for the I2C bus: https://www.nxp.com/docs/en/user-guide/UM10204.pdf
  3. Your I2C bus can probably be made stronger. Do you know how much pullup there is on the RTC module ? The maximum sink current for SDA and SCL is 3mA. That is the current that is needed to pull SDA (or SCL) low.

Can you do a test ?
Remove the gray cable to the display. Use 4 separate wires, and don't tie them together.
What other cables do you have. How short can the cable to the display be ?

The I2C bus is not designed to go into a cable. It is not that kind of bus. Keep SDA away from SCL.

relay better to power from different source with arduino and I2C devices, very suspicious that error appear only after relay activated. i suppose you powers left side(of breadboard) with 5v from USB and right side from LDO PCB.
can you check if breadboard power lines have conductivity on whole length?

Hi @6v6gt ,
if slowing down the clock rate to 12k5 kHz is nothing but

void setup() { 
Wire.begin ();
  TWBR = 158;  
  TWSR |= bit (TWPS0);

then it didn´t help.
So far it looks like it´s the lenght of the wires (>1m).

Thanx for your input!

Thanx for your comments and suggestions!
I just hooked up the LCD directly to the breadboard with jumper wires and it worked :grinning:

But as I want to have the LCD quite far away from the central electronics I will start experimenting with different cable lengths until it all breaks down again haha. Separate cables, shielded cables to avoid crosstalk...

Your third point, making the I2C bus stronger... I don´t know about the pullups on the RTC module.

My "Arduino" has a second I2C bus. Maybe that´s an option.
But even after reading tons of webpages and getting Chat GPT to modify the wire library .h and .cpp files (I´m not a coder, just learning little by little), I wasn´t able to figure out how to address the second I2C bus for the LCD or the RTC. If you have a hint for me, I´d be very happy to test this, too.

I totally agree on this very suspicious timing of the error.
The relays are powered from the same 5V source as everything else.
In my setup all power (left and right of the breadboard) comes from the one external power supply and is perfectly distributed all the way from top to bottom.

To leave no stone unturned I hooked up the relay on a different power source (5V from a USB charger cable) and to my very surprise the relay didn´t switch at all.

Can you try to stay out of trouble:

  1. Don't use ChatGPT. If you have a detailed specific question, then ChatGPT will send you in the wrong direction and you have to "unlearn" everything that ChatGPT told you. You are wasting your and our time with that.
  2. Don't write to the TWBR register. There is a function for the speed: https://www.arduino.cc/reference/en/language/functions/communication/wire/setclock/
  3. Don't try 12k5 kHz. About 50kHz is the lowest possible speed with the Wire.setClock().
  4. "quite far" is not a length. How much length do you need ? Did you read the official standard about 10 cm ?
  5. Why would the second I2C bus be better ? I don't know if there is Arduino support for it. The RTC has little influence on the I2C bus. It is about the cable, not the RTC.
  6. Don't try to "improve" the Wire library.