LCD1602 mit Arduino ansteuern (I2C): Text, Sonderzeichen & Live-Sensoranzeige

Das LCD1602 ist der Klassiker unter den Arduino-Displays: zwei Zeilen, je 16 Zeichen, gut ablesbar und spottbillig. Der einzige Haken am nackten Modul sind die 16 Anschluesse, die dir jede Menge Pins wegfressen. Die elegante Loesung ist der I2C-Backpack: eine kleine Platine auf der Rueckseite, die aus 16 Pins ganze vier macht. In diesem Tutorial verkabelst du das Display mit nur vier Draehten, findest die I2C-Adresse, gibst deinen ersten Text aus, zeigst Live-Sensorwerte an, bastelst eigene Sonderzeichen wie das Grad-Symbol und laesst Text ueber das Display scrollen.

Warum LCD1602 – und warum per I2C?

Ein LCD1602 zeigt 16 Zeichen in 2 Zeilen – daher der Name. Es ist kein Grafikdisplay, sondern ein reines Zeichendisplay: perfekt fuer Messwerte, Statusmeldungen, Menuetexte oder eine kleine Uhr. Gegenueber einem OLED-Display punktet das LCD1602 mit groesseren, aus mehreren Metern ablesbaren Zeichen und einem sehr guenstigen Preis – dafuer kann es keine Grafiken und keine frei positionierten Pixel.

Das nackte HD44780-Display braucht im Parallelbetrieb bis zu zehn Datenleitungen. Genau hier setzt der I2C-Backpack an: Der aufgeloetete PCF8574-Chip wandelt den I2C-Bus in die parallelen Steuersignale um. Fuer dich heisst das: nur noch vier Kabel (VCC, GND, SDA, SCL) statt sechzehn – und die beiden Datenpins SDA/SCL kannst du dir sogar mit anderen I2C-Sensoren teilen.

ℹ️ I2C ist ein Bus: Am selben SDA/SCL-Paar haengen bei Bedarf mehrere Teilnehmer, jeder mit eigener Adresse. Wie das funktioniert und wie du Adresskonflikte loest, erklaert der I2C-Bus-Guide. Genau dieses Prinzip nutzen wir gleich, um Display und Sensor an denselben zwei Kabeln zu betreiben.

Das brauchst du

LCD1602 Display mit I2C-Schnittstelle – das 16x2-Display mit bereits aufgeloetetem I2C-Backpack, blaue Hintergrundbeleuchtung
UNO R3 Entwicklungsboard (USB-C) – der ideale Einsteiger-Controller mit 5-V-Logik, passt perfekt zum LCD1602
Optional: Nano V3 Board (USB-C) – die kompakte Alternative fuer feste Aufbauten
DHT22 Temperatur- & Feuchtesensor – liefert die Messwerte fuer die Live-Anzeige
Breadboard mit 400 Kontakten – fuer den fliegenden Aufbau ohne Loeten

1Verkabelung: vier Kabel und fertig

Der I2C-Backpack hat genau vier Anschluesse. Am UNO liegen die I2C-Pins fest: SDA auf A4 und SCL auf A5 (beim UNO gibt es zusaetzlich die beschrifteten SDA/SCL-Pins neben AREF – sie sind intern mit A4/A5 verbunden).

LCD1602 (I2C) Arduino UNO Hinweis
GND GND gemeinsame Masse
VCC 5V das LCD braucht 5 V fuer volle Helligkeit
SDA A4 (SDA) Datenleitung
SCL A5 (SCL) Taktleitung

⚠️ 5 V, nicht 3,3 V: Das LCD1602 mit Backpack ist ein 5-V-Modul. An einem 3,3-V-Board wie dem ESP32 bleibt die Anzeige oft blass oder leer. Betreibe VCC dann an 5 V (falls vorhanden) – die 3,3-V-I2C-Signale erkennt der PCF8574 in der Praxis meist trotzdem. Am sichersten faehrst du mit einem 5-V-Board wie dem UNO oder Nano.

Kontrast einstellen

Auf der Rueckseite des Backpacks sitzt ein kleines blaues Potentiometer. Zeigt das Display nach dem Hochladen nur schwarze Kaestchen oder gar nichts, drehe dieses Poti langsam mit einem Schraubendreher – irgendwo dazwischen wird der Text scharf und gut lesbar. Das ist der haeufigste "Fehler", der gar keiner ist.

2Bibliothek installieren und I2C-Adresse finden

Fuer die I2C-Ansteuerung nutzen wir die verbreitete Bibliothek LiquidCrystal_I2C. Oeffne in der Arduino IDE den Bibliotheksverwalter (Werkzeuge → Bibliotheken verwalten), suche nach LiquidCrystal I2C (z. B. von Frank de Brabander) und installiere sie.

Jetzt der Knackpunkt: Der Konstruktor braucht die I2C-Adresse des Backpacks. Ueblich sind 0x27 oder 0x3F – aber verlass dich nicht auf Raten, sondern scanne den Bus. Dieser Mini-Sketch verraet dir jede angeschlossene Adresse:

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("I2C-Scanner startet...");
}

void loop() {
  byte gefunden = 0;
  for (byte adr = 1; adr < 127; adr++) {
    Wire.beginTransmission(adr);
    if (Wire.endTransmission() == 0) {
      Serial.print("Geraet gefunden auf 0x");
      if (adr < 16) Serial.print("0");
      Serial.println(adr, HEX);
      gefunden++;
    }
  }
  if (gefunden == 0) Serial.println("Nichts gefunden - Verkabelung pruefen!");
  delay(3000);
}

Der Serielle Monitor zeigt dir z. B. Geraet gefunden auf 0x27. Merke dir diese Adresse – sie kommt gleich in den Konstruktor. Ausfuehrlicher ist der Scanner im I2C-Bus-Tutorial erklaert.

Hallo Welt auf dem LCD

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

// Adresse aus dem Scanner, 16 Spalten, 2 Zeilen
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();          // Display initialisieren
  lcd.backlight();     // Hintergrundbeleuchtung an

  lcd.setCursor(0, 0); // Spalte 0, Zeile 0
  lcd.print("Hallo MAKEROO!");

  lcd.setCursor(0, 1); // Spalte 0, Zeile 1
  lcd.print("LCD1602 laeuft");
}

void loop() {
  // nichts noetig - Text bleibt stehen
}

Wichtig ist die Zaehlweise: setCursor(spalte, zeile) beginnt bei 0. Die erste Zeile ist also Zeile 0, das erste Zeichen Spalte 0. Steht dein Text an der falschen Stelle, ist fast immer die Reihenfolge der Argumente vertauscht.

3Live-Sensorwerte anzeigen (DHT22)

Ein Display wird erst spannend, wenn sich etwas bewegt. Wir haengen den DHT22 an Pin D2 und zeigen Temperatur und Luftfeuchte an. Installiere dafuer die Bibliotheken DHT sensor library und Adafruit Unified Sensor.

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

#define DHTPIN 2
#define DHTTYPE DHT22

LiquidCrystal_I2C lcd(0x27, 16, 2);
DHT dht(DHTPIN, DHTTYPE);

void setup() {
  lcd.init();
  lcd.backlight();
  dht.begin();

  lcd.setCursor(0, 0);
  lcd.print("Wetterstation");
  delay(1500);
  lcd.clear();
}

void loop() {
  float temp = dht.readTemperature();
  float feuchte = dht.readHumidity();

  if (isnan(temp) || isnan(feuchte)) {
    lcd.setCursor(0, 0);
    lcd.print("Sensor-Fehler   ");
    delay(2000);
    return;
  }

  lcd.setCursor(0, 0);
  lcd.print("Temp:  ");
  lcd.print(temp, 1);      // eine Nachkommastelle
  lcd.print(" C   ");      // Leerzeichen ueberschreiben Reste

  lcd.setCursor(0, 1);
  lcd.print("Feuchte: ");
  lcd.print(feuchte, 0);
  lcd.print(" %  ");

  delay(2000);
}

💡 Der Leerzeichen-Trick: Ein LCD loescht alte Zeichen nicht von selbst. Wird aus 25.3 spaeter 9.1, bliebe das alte 3 stehen (9.13). Deshalb schreiben wir ein paar Leerzeichen hinter jeden Wert – die ueberschreiben Reste sauber. Alternativ lcd.clear(), das flackert aber sichtbar.

Dieses Grundgeruest laesst sich mit jedem Sensor kombinieren – etwa dem BMP280 fuer den Luftdruck oder dem wasserdichten DS18B20. Weil das LCD am I2C-Bus haengt, kannst du einen weiteren I2C-Sensor einfach parallel an dieselben SDA/SCL-Pins klemmen.

4Eigene Sonderzeichen: Grad-Symbol & Icons

Der Zeichensatz des HD44780 kennt kein sauberes Grad-Zeichen und keine Umlaute. Aber du darfst bis zu acht eigene Zeichen selbst definieren – als 5x8-Pixel-Muster. Jedes Zeichen ist ein Array aus acht Bytes, wobei die unteren fuenf Bits jeweils eine Pixelzeile bilden.

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Grad-Symbol als 5x8-Muster (1 = Pixel an)
byte grad[8] = {
  0b00110,
  0b01001,
  0b01001,
  0b00110,
  0b00000,
  0b00000,
  0b00000,
  0b00000
};

// kleines Herz
byte herz[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000,
  0b00000
};

void setup() {
  lcd.init();
  lcd.backlight();

  lcd.createChar(0, grad);   // Speicherplatz 0-7
  lcd.createChar(1, herz);

  lcd.setCursor(0, 0);
  lcd.print("Temp: 25.3");
  lcd.write(byte(0));        // eigenes Zeichen 0 = Grad
  lcd.print("C");

  lcd.setCursor(0, 1);
  lcd.print("MAKEROO ");
  lcd.write(byte(1));        // Herz
}

void loop() {}

Mit lcd.createChar(nummer, muster) laedst du das Zeichen in den Speicher (0 bis 7), mit lcd.write(byte(nummer)) gibst du es aus. So baust du dir Batterie-Symbole, Pfeile fuer ein Menue, WLAN-Icons oder Balkenanzeigen. Am schnellsten entwirfst du die Muster mit einem der kostenlosen "LCD Custom Character Generator" im Web – die spucken die fertigen Byte-Arrays aus.

ℹ️ Nur acht Slots: Mehr als acht eigene Zeichen gleichzeitig gehen nicht – der Zeichenspeicher ist voll. Du kannst die Slots aber jederzeit im Programm mit createChar() neu belegen, etwa fuer eine Animation.

5Laufschrift: Text laenger als 16 Zeichen

Sechzehn Zeichen sind schnell voll. Fuer laengere Meldungen laesst du den Text durchlaufen. Am flexibelsten geht das nicht mit den eingebauten scrollDisplayLeft()-Funktionen (die schieben beide Zeilen zugleich), sondern mit einem eigenen Ausschnitt-Fenster ueber einen langen String – und dank millis() ganz ohne blockierendes delay():

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

String laufText = "MAKEROO - alles fuer deine Maker-Projekte! ";
int fenster = 16;          // sichtbare Breite
int position = 0;
unsigned long letzterSchritt = 0;
const int tempo = 350;     // ms pro Schritt

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Laufschrift:");
}

void loop() {
  if (millis() - letzterSchritt >= tempo) {
    letzterSchritt = millis();

    // Ausschnitt der Laenge 'fenster' ab 'position'
    String ausschnitt = laufText.substring(position, position + fenster);
    lcd.setCursor(0, 1);
    lcd.print(ausschnitt);

    position++;
    if (position > (int)laufText.length() - fenster) {
      position = 0;        // von vorne
    }
  }
}

Weil die Anzeige ueber millis() statt delay() laeuft, bleibt der Rest deines Programms ansprechbar – du koenntest parallel Sensoren abfragen oder Tasten auswerten. Das Prinzip dahinter erklaert der Guide millis() statt delay().

💡 Menue statt Laufschrift? Wenn du mit dem LCD ein bedienbares Menue bauen willst, kombiniere es mit einem Rotary Encoder. Das Konzept ist beim OLED-Menuesystem beschrieben und laesst sich 1:1 aufs LCD1602 uebertragen.

Troubleshooting

⚠️ Display zeigt nur schwarze Kaestchen: Das ist der Normalzustand vor dem lcd.init() und bei falsch eingestelltem Kontrast. Poti auf der Rueckseite drehen und pruefen, ob dein Sketch wirklich laeuft.

⚠️ Hintergrundbeleuchtung an, aber kein Text: Fast immer die falsche I2C-Adresse im Konstruktor. Erst den Scanner laufen lassen, dann 0x27 bzw. 0x3F eintragen.

⚠️ Gar nichts leuchtet: VCC/GND vertauscht oder das Display haengt an 3,3 V statt 5 V. Auch ein Wackler auf dem Breadboard ist ein Klassiker.

⚠️ Alte Zeichen bleiben stehen: Das LCD ueberschreibt nicht automatisch – mit Leerzeichen auffuellen oder gezielt lcd.clear() nutzen.

⚠️ Umlaute erscheinen als Muell: Der HD44780-Zeichensatz kennt keine deutschen Umlaute. Nutze ae/oe/ue oder definiere eigene Zeichen mit createChar().

🚀 Dein LCD-Starterpaket

Alles fuer dieses Tutorial gibt es im Shop:

Fazit

Das LCD1602 mit I2C-Backpack ist das unkomplizierteste Display fuer den Einstieg: vier Kabel, eine Bibliothek, die richtige Adresse aus dem Scanner – und du hast eine gut ablesbare Textanzeige. Du hast deinen ersten Text ausgegeben, Live-Werte eines DHT22 angezeigt, mit createChar() eigene Symbole wie das Grad-Zeichen gebaut und eine nicht-blockierende Laufschrift programmiert.

Von hier aus ist alles offen: eine Uhr mit dem DS3231, ein Menue mit Rotary Encoder, eine Statusanzeige fuer dein Smart-Home-Projekt. Weil das Display am I2C-Bus haengt, kostet es dich fast keine Pins und laesst sich mit weiteren I2C-Sensoren an denselben zwei Leitungen kombinieren. Viel Spass beim Anzeigen!

Zurück zum Blog