Missing letters on lcd

Hello everyone.

I have a problem with my lcd display. Its 20x4 i2c lcd. I used it in my project to show information and different parameters. But when switching between motor modes from manual to auto, the first 4 letters in row 2 disapear.

My setup:

(its a long list for example 2 driver motors , 2 stepper mottors , 2 potenciometers etc.

hoverer I disconected all of them to soleve my problem nut it didnt work )

ESP32 S3

Library LiquidCrystal_I2C.h

power suply 24V converted to 5V ( and the esp and lcd are conected to that)

4 buttons pins 1,3,21,47

lcd i2c 20x4 pins 9,10

To fix it i tried:

1 check if its a "dead pixels"
by displaying on the whole lcd difrent characters I confirmed it works fine

2 Clear any interferance

I disconnected anything thats not lcd and cleared code to only controling lcd and buttons .

My code:



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

// ==========================================================================================================================================
// SEKCJA: KONFIGURACJA PINÓW (Tylko 4 przyciski do nawigacji po menu)
// ==========================================================================================================================================
#define I2C_SDA 9            
#define I2C_SCL 10           

#define p_TRYB 3             // Przełącznik trybów (Manual / Auto)
#define p_OS 21              // Wybór osi (V / H)
#define p_KIERUNEK 47        // Wybór kierunku
#define p_SPAWANIE 1         // Przycisk aktywacji spawania

// ==========================================================================================================================================
// STAN SYSTEMU I ZMIENNE GLOBALNE
// ==========================================================================================================================================
LiquidCrystal_I2C lcd(0x27, 20, 4); 

// Zmienne do kontroli odświeżania ekranu AUTO
int lastAutoMMS = -1; 
bool lastVisualSpawanieState = false; 
bool lastAutoOsH = false;
bool lastAutoKierunekDodatni = false;
bool wymusOdswiezenieAuto = true; 

enum TrybPracy { MANUALNY, AUTOMATYCZNY };
TrybPracy aktualnyTryb = MANUALNY;

bool autoOsH = true;         
bool autoKierunekDodatni = true; 
bool spawanieAktywne = false;

// Flagi do obsługi pojedynczego kliknięcia (debouncing)
bool lastTrybState = HIGH;
bool lastOsState = HIGH;
bool lastKierunekState = HIGH;
bool lastSpawanieState = HIGH;

// ==========================================================================================================================================
// FUNKCJA: SETUP
// ==========================================================================================================================================
void setup() { 
  Wire.begin(I2C_SDA, I2C_SCL); 

  lcd.init(); 
  lcd.backlight(); 
  lcd.clear();

  // Ustawienie pinów przycisków menu
  pinMode(p_TRYB, INPUT_PULLUP);
  pinMode(p_OS, INPUT_PULLUP);
  pinMode(p_KIERUNEK, INPUT_PULLUP);
  pinMode(p_SPAWANIE, INPUT_PULLUP); 

  // Wymuszenie pierwszego odrysowania ekranu
  lastAutoOsH = !autoOsH;
  lastAutoKierunekDodatni = !autoKierunekDodatni;
  wymusOdswiezenieAuto = true;
}

// ==========================================================================================================================================
// GŁÓWNA PĘTLA PROGRAMU: LOOP
// ==========================================================================================================================================
void loop() { 

  // 1. Obsługa przycisku TRYB (Manualny / Automatyczny)
  bool readingTryb = digitalRead(p_TRYB);
  if (readingTryb == LOW && lastTrybState == HIGH) { 
    aktualnyTryb = (aktualnyTryb == MANUALNY) ? AUTOMATYCZNY : MANUALNY;
    lcd.clear(); // Czyścimy ekran przy pełnej zmianie ekranu głównego
    wymusOdswiezenieAuto = true; // Wymuszamy odrysowanie nowego menu
    delay(200); 
  }
  lastTrybState = readingTryb;

  // 2. Obsługa przycisku SPAWANIE
  bool readingSpawanie = digitalRead(p_SPAWANIE);
  if (readingSpawanie == LOW && lastSpawanieState == HIGH) {
    spawanieAktywne = !spawanieAktywne;
    delay(200);
  }
  lastSpawanieState = readingSpawanie;

  // ----------------------------------------------------------------------------------------------------------------------------------------
  // MENU DLA TRYBU MANUALNEGO
  // ----------------------------------------------------------------------------------------------------------------------------------------
  if (aktualnyTryb == MANUALNY) {
    lcd.setCursor(0, 0); 
    lcd.print("TRYB: MANUALNY ");
    lcd.setCursor(0, 1); 
    lcd.print("STATUS: INTERF"); 
    lcd.setCursor(0, 2); 
    lcd.print("V Przod/Tyl: 12 mm/s"); // Wpisane na sztywno do testu
    lcd.setCursor(0, 3); 
    lcd.print("V Gora/Dol:  15 mm/s"); // Wpisane na sztywno do testu
  }

  // ----------------------------------------------------------------------------------------------------------------------------------------
  // MENU DLA TRYBU AUTOMATYCZNEGO 
  // ----------------------------------------------------------------------------------------------------------------------------------------
  else if (aktualnyTryb == AUTOMATYCZNY) {
    
    // Obsługa przycisku OSI (H / V)
    bool readingOs = digitalRead(p_OS);
    if (readingOs == LOW && lastOsState == HIGH) {
      autoOsH = !autoOsH;
      delay(200);
    }
    lastOsState = readingOs;

    // Obsługa przycisku KIERUNEK
    bool readingKierunek = digitalRead(p_KIERUNEK);
    if (readingKierunek == LOW && lastKierunekState == HIGH) {
      autoKierunekDodatni = !autoKierunekDodatni;
      delay(200);
    }
    lastKierunekState = readingKierunek;

    // LINIA 1, 2 i 3: Rysowane TYLKO wtedy, gdy zmieni się konfiguracja (brak migania)
    if (wymusOdswiezenieAuto || autoOsH != lastAutoOsH || autoKierunekDodatni != lastAutoKierunekDodatni) {
      lcd.setCursor(0, 0);
      lcd.print("TRYB: AUTOMATYCZNY");
      
      lcd.setCursor(0, 1);
      lcd.printf("Aktywna os: [%s]", autoOsH ? "H" : "V");
      
      lcd.setCursor(0, 2);
      if (autoOsH) {
        lcd.printf("Kierunek: [%s]       ", autoKierunekDodatni ? "PRZOD" : "TYL  ");
      } else {
        lcd.printf("Kierunek: [%s]       ", autoKierunekDodatni ? "GORA " : "DOL  ");
      }
      
      lastAutoOsH = autoOsH;
      lastAutoKierunekDodatni = autoKierunekDodatni;
      wymusOdswiezenieAuto = false;
    }

    // LINIA 4: Prędkość (ustawiona na sztywno na 10) i status spawania
    if (spawanieAktywne != lastVisualSpawanieState) {
      lcd.setCursor(0, 3);
      lcd.printf("Spaw:[%s] sV:10mm/s", spawanieAktywne ? "ON " : "OFF");
      lastVisualSpawanieState = spawanieAktywne;
    }
  }

  delay(20); 
}

Pics that show the problem :

I dont have any more ideas :(

Swap to the hd44780 library (by Bill Perry, hd44780_I2Cexp class).
It is generally far more robust with cheap PCF8574 backpacks.

It is almost a Drop-in replacement, minimal code changes needed

Check also the pullups

Problem is probably printing too many spaces in line 130, which prints 24 characters.

      if (autoOsH) {
        lcd.printf("Kierunek: [%s]       ", autoKierunekDodatni ? "PRZOD" : "TYL  ");

Count them

"Kierunek: [PRZOD]       "

As the display lines are not consecutive under the hood, printing on line 2 overflows to line 1.

replacing the above snippet with makes the problem explicitly visible.

      if (autoOsH) {
        lcd.printf("Kierunek: [%s]xxxxxxxx", autoKierunekDodatni ? "PRZOD" : "TYL  ");

@J-M-L
Do you know if the HD44780 library cut off printing beyond position 19?

FYI,
my I2C_LCD library (which has its limitations) prevents writing beyond the number of columns. (See write() line 460-470 cpp file)

@robtillaart thank you.

The problem was exactly that. After removing spaces everything works fine.

And also thank u guys , i will definitely check those libraries.

Have a great day.

Removing the spaces helped in this specific case, but I would be careful not to treat that as a robust solution.

The underlying issue is still the maximum number of characters the display can show per line. A slightly different text could cause the same problem again.

So I would either use the library suggested by @robtillaart, or explicitly limit the text length before printing it, for example:

const uint8_t maxChars = 20;

if (text.length() > maxChars) {
text = text.substring(0, maxChars);
}

lcd.print(text);

That way the limit is intentional instead of accidental.

@InquisitiveMind

Limiting the text length works if one includes the print start position too in the math.

When the library truncates the print, it is easier for the user (my assumption).

Would scrolling help?