Arduino hangs whit I2C-LCD when NOT printing Serial messages

Good afternoon.

You may have read this topic Lightweight program restarts, about my struggling to get some model cars running on a modell railway. That topic is now resolved thanks to very helpful people,
and this new topic, as far as I believe, is only slightly related. At least it is the same car system on the same model railway.

The problem
The Arduino Nano prints status messages on an LCD through an I2C-LCD-card.
Link to LCD with I2C

The serial monitor outputs messages for use during development but with commenting out “define debug” it prints nothing. In the cases below I have Serial active and it has worked flawless with LCD.

Serial messages printed - OK
When the serial monitor prints messages the program runs a bit slower. This for example results in the need for a button push to be a little longer before the program register the pushed button.
The Arduino “never” freeze and the LCD-screens stays lit giving correct information.

Serial messages not printed - problem
When the serial monitor doesn’t print any messages the program reacts quick and normal.
The status messages are printed on the LCD. However after while the program freeze. About 1-5 minutes. First the LCD freeze, but the program seems to continue. 30-60 sec later the LCD screen becomes black and the program hangs. Need for hard reset.

Nothing else is changed.

Running on external power or USB doesn’t matter.
I am not powering LCD from Arduino 5v, but they share common ground.
When everything is working I measure gnd-SCL/SCA = 2,54v. When it freezes I did measure 5v.

So my question is - maybe the program is to quick for the I2C?

Code: Ldh_BB1_v1.ino (54.8 KB)

You could leave the debug on - the Arduino doesn’t care whether anything is listening to Serial. To avoid slowness, increase the baud rate.

Of course, it would be better to figure out what is wrong.

If I increase the baud rate, won’t the program runs fast anyway?

Yes I think it would be very nice to understand this.

I wouldn’t expect the LCD to care about how often it is communicating over I2C, but usually, people keep the refresh rate low that people can read it. Constant refresh tends to cause flickering. Perhaps have your lcd control function use millis to see if some interval has passed before printing to it.

I think though, I would write a simpler test program and prove out whether the LCD can manage being updated at maximum speed.

You mean a test program writing info to LCD without any sensors or relays controlled by the program?

Just writing to LCD and serial of?

Yes. I’m curious about whether the display is really a problem at high speed.

So I did write this program:
With this code the number is now 29278 and it stills count up quick.

Did I understand you right that this was the kind of program you mentioned?

/**
    Felsökning

    Här kan du aktivera felsökning. OBS Felsökningen gör programmet betydligt långsammare!
    Om du aktiverar detta printas viss information och variabler ut i Serial Monitor.

    Där du vill felsöka kan du lägga in följande i koden:
    DEBUG_PRINT("Text som ska skrivas ut");
    DEBUG_PRINT(variabelSomSkaSkrivasUt);
*/

// Ta bort kommentaren på nedanstående rad för att aktivera felsökning.
//#define DEBUG


// Om DEBUG är definierat (genom att ta bort kommentar ovan), printas text eller variabel ut, annars blank rad.
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.println (x)
#define DEBUG_PRINT_VAR(x) Serial.println (x)
#define DEBUG_PRINT_F(x) Serial.println (F(x))
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINT_VAR(x)
#define DEBUG_PRINT_F(x)
#endif



/**
    Bibliotek.

    Bibliotek som används i programmet.
*/
#include <Wire.h>

// LCD-skärm
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Adress, antal tecken, antal rader

long testnumber = 0; // Increasing number


/**
    Initiering av programmet.

    Körs bara när Arduino startas.
*/
void setup()
{
//  #ifdef DEBUG
  Serial.begin(9600);
 // #endif

  Wire.setClock(10000);
  DEBUG_PRINT(" ");
  DEBUG_PRINT(" ");
  DEBUG_PRINT(" ");
  DEBUG_PRINT_F("LCD_hastighetstest.ino");
  DEBUG_PRINT_F("F E L SĂ– K N I N G      Aktiv");


  // LCD-skärm
  lcd.init();
  lcd.backlight();
}


/**
    Funktion: loop

    Aktivera de olika funktionerna varje gĂĄng som Arduinon loopar.
*/
void loop()
{
  DEBUG_PRINT(" ");
  DEBUG_PRINT(" ");
  DEBUG_PRINT_F("F U N K T I ON: loop");
  DEBUG_PRINT(" ");
  //currentMillis = millis();
  DEBUG_PRINT_F(" ");
  DEBUG_PRINT_F("Nummer: ");
  DEBUG_PRINT_VAR(testnumber);
  testnumber = testnumber+1;
  lcd.setCursor(2, 0);
  lcd.print(F("Nummer: "));
  lcd.setCursor(2, 1);
  lcd.print(testnumber);
}

Yes, that’s the kind of thing I had in mind.

Edit: It’s probably worth making it do a bit more work though - the existing function sends rather more data to the display.

The I2C bus with 10kHz clock speed does not work with the Nano, that is a bug.
I think you can select 50 kHz … 400 kHz. Other values cause a miscalculation and the resulting bus speed can be anything.

Since Arduino IDE 1.8.13, there is a low-level timeout in the Wire library, but you have to turn it on yourself.

1 Like

May you have any good idea or example of more work?

I was going to suggest taking the code from your existing program, but it looks like Koepel has the answer you need.

I did fins the Wire.clockspeed on a you tube movie that solved a problem with strange letters on the LCD. So this should be 50 kHz?

Do I understand you right: when printing Serial messages this bug is not an issue but it shows up when not printing serial messages.

(When it works, no hangs, but the programs runs “slow” I do print Serial Messages and LCD messages at the same time).

Thank you both for help so far!

I suspect that the Serial debug was taking up time and the strain on I2C timing was less. I’d try 50K as suggested and see what happens.

I did try a moment and it looks good, however I will try for a longer time upcoming days. The 10k I put because of rubbish on the screen. But that might have had other reasons and since then I did clean up the code.

I will be back :slight_smile:

50kHz is a good and slow value.

The bug in the source code of the Wire library for the Nano is this line:

TWBR = ((F_CPU / frequency) - 16) / 2;

There is no checking for a minimum or maximum, there is only that calculation and the TWBR register is only 8-bits.

Let’s see: 1/(((255 * 2) + 16)/16000000) = a little more than 30kHz. That means that 30kHz is not reachable, but 40kHz is okay.

The explanation for the timeout is in the source code.

Thank you,

I do not really understand the calculation. Math has never been my subject…

I have now tried with 50kHz and it did work fine for about 20-25 minutes then it freeze.

So now I am going to try 40kHz.

(Now maybe we need to discuss som electrics. I have read that sometimes you shall put resistors between datalines and 5v. As I bought the presoldered card I have not thought about doing “any extra” but connect it to Arduino. The LCD are situated about 2,4 meters from the Arduino. Maybe it doesn’t matter in this case, as I have seen it worked for longer times with Serial messages on.)

EDIT:
I just noticed that I did not comment out Serial. Maybe thats is a problem too?

Now with 40kHz it took about 15 min.

I will now try with 40,
but put my error messages on to see.

EDIT 2021-05-03 21:39:
So, finally I have now been running the program for 1,5 hour with out any problem.
That is when Serial is printing messages to the serial monitor.

Next step, would that be to:

  • move the LCD close to Arduino (now 2,4 meters away)?
  • add 4k7 pull-up resistors at the Arduino? I do not have data sheets of the LCD with I2C-backpack, but I did measure and found the 472 res on I2C, each one giving generating a signal to SCA/SCL. So as far as I understand, there are already resistors. (see picture)
  • take apart the LCD and the I2C interface, and put the I2c very close to Arduino, running 7 cables the 2,4 meters to LCD?

Still it feels so strange that I have been running everything with serial messages but not without.

Thankful for all ideas!

Well, that is actually very normal. All the problems with timing, electric noise, long I2C cables, grounding, and so on, they have such results.

Your loop() is running freely and the baudrate is slow. That means you are printing data to the serial monitor very fast until the TX buffer is full (the software TX buffer inside the Serial library). When the TX buffer is full, the sketch waits until there is a free spot in the buffer. That could slow down your sketch hundred times or more.

Without the debug message you update the display at a very high rate. The liquid crystals are slow, so only a 3 or 4 times per second update should be enough.

The 2.4 meters is too long. Keep it below 50cm. That 50cm is for beginners. You can go up to 6 meters if you do everything right.
What kind of cable is used for the I2C bus ?
Moving the I2C backpack to the Arduino side and have many wires going to the display might introduce the same or other problems. It that is required to make it work, so be it, but I think there are better ways.

A pullup resistor of 4k7 at the display and another 4k7 at the Arduino is okay. A third 4k7 could be too much pullup.

Could you set the timeout (in microseconds) ? A value of 25 milliseconds is sometimes used, but I think that you can try 1 ms (not tested).

Wire.begin();
Wire.setWireTimeout( 1000);  // 1000 microseconds

I have read your other posts in topics about this and I understand even that 20 cm is maximum.
We use 2 cables of standard electric one Cable

Test results:
Yesterday the system ran with LCD
09:34 - 10:06 then it did freeze
10:09 - 10:15 then it did freeze
10:16 - 12:01 then it did freeze
13:50 - 14:29 then it did freeze.

I removed the SDA/SCL-cable from Arduino:
It ran 14:36 - 18:00 then i shut it off.
For me it is obvious that the I2C is the problem.

Today with LCD and 4,4k resistor at the Arduino-side:
10:22 - 11:00

Today with LCD and 4,4k resistor at the Arduino-side
AND the additional set.WireTimeout:
11:46 - 11:52, the LCD freezed but the program is running.
12:10 the screen “shut down” but cars are still running
12:25 the cars are running
13:35 the cars seem to have stopped but unsure if it is just the program that has out them on charging mode.

SO for me it seems that this is not the solution we shall try to solve.

Time to look for other solutions:

  • List item May this be another solution: Another I2c library

  • An “extender” of some way. Does anyone of you have experience in this thing: Extender I2c, 1 or Extender I2c, 2

  • Or maybe this is the only solution, to use another Nano and a Serial bus. But maybe it also looks “easier” that it is in reality: RS485

Yes, you have done enough tests to be sure.

The I2C does not have two wires, it has three: SDA, SCL, GND.
The GND is part of the signal. Without GND, there is no I2C bus. With a bad GND, the I2C bus is even worse.
You also need 5V to power the display.

A shielded Cat cable with RS-485 has been a very good solution for decades and it still is.

Another I2C library will not help, if your wiring is not okay.

The Sparkfun extender uses the PCA9615 which turns the I2C bus into some kind of twisted pair RS-485. However, if you try really hard to create a bad GND, then it might still fail.

The I2C timeout can have an additional parameter to restart the I2C bus. You could try that.
If you also initialize the display again with lcd.init(), then it might work again.
You could initialize the display every minute.

Can you try a shielded Cat cable for I2C ?

  • Connect the shield of the cable to the Arduino GND, and leave it open at the display.
  • Use a twisted pair for 5V + GND for the display. You need the GND from the Arduino, not from somewhere else. At the end of cable, do not use that GND and 5V for other things. At the end of the cable should only be the display and nothing else should be connected to it.
  • Use another twisted pair for SDA + GND, and leave the GND open at the display.
  • Use another twisted pair for SCL + GND, and leave the GND open at the display.

I’m not sure which wires to leave open at the end, but you can start with this.