BysMax

Arduino LCD 16x2 with I2C — Wiring, Code & Display Text (2025)

14 min

Connecting a 16x2 LCD without I2C requires 6+ wires and 4 GPIO pins. With an I2C adapter (PCF8574), you only need 2 wires (SDA + SCL) and use the I2C bus instead of GPIO pins. This guide covers both types.

Time to complete: 15 minutes
Skill level: Beginner

What You Need

ComponentNotes
Arduino Uno or NanoAny 5V Arduino
LCD 16x2 (or 20x4)Standard HD44780-compatible
I2C adapter modulePCF8574 backpack (usually comes pre-soldered)
4 jumper wiresMale-to-male

Most LCD 16x2 modules sold today come with an I2C adapter pre-soldered to the back. If yours doesn't have the adapter, buy the PCF8574 I2C backpack separately and solder it on.

Wiring — LCD with I2C Adapter

I2C AdapterArduino UnoArduino Nano
GNDGNDGND
VCC5V5V
SDAA4A4
SCLA5A5

That's it — 4 wires instead of 6+. The I2C bus is shared, so you can add other I2C devices (sensors, OLED displays) on the same SDA/SCL pins.

I2C pins on other boards: ESP32: SDA=GPIO21, SCL=GPIO22. Mega: SDA=20, SCL=21. Pro Mini: SDA=A4, SCL=A5 (same as Uno).

Find Your I2C Address

LCD I2C adapters come with two possible addresses: 0x27 or 0x3F. You need to know yours before uploading code.

Method 1 — I2C Scanner sketch (fastest):

#include <Wire.h>

void setup() {
  Wire.begin();
  Serial.begin(9600);
  Serial.println("Scanning I2C bus...");

  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("Found device at 0x");
      if (addr < 16) Serial.print("0");
      Serial.println(addr, HEX);
    }
  }
  Serial.println("Scan complete.");
}

void loop() {}

Upload this, open Serial Monitor at 9600 baud. It prints the address of every I2C device on the bus. Your LCD will appear as 0x27 or 0x3F.

Method 2 — Try both: If you'd rather skip scanning, try 0x27 first. If nothing appears, change to 0x3F.

Install the Library

  1. Open Arduino IDE
  2. Go to Sketch → Include Library → Manage Libraries
  3. Search for LiquidCrystal I2C by Frank de Brabander
  4. Click Install

Code — Display Text on LCD

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

// Change 0x27 to 0x3F if nothing appears on screen
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();
  lcd.backlight();  // Turn on backlight

  // First line (row 0)
  lcd.setCursor(0, 0);
  lcd.print("Hello, World!");

  // Second line (row 1)
  lcd.setCursor(0, 1);
  lcd.print("Arduino LCD I2C");
}

void loop() {
  // Nothing here — text stays on screen
}

Key functions:

  • lcd.init() — initialize the display
  • lcd.backlight() — turn on backlight (use lcd.noBacklight() to turn off)
  • lcd.setCursor(col, row) — position the cursor (0,0 = top-left; max = 15,1 for 16x2)
  • lcd.print("text") — print text at current cursor position
  • lcd.clear() — clear screen and reset cursor to (0,0)

Code — Scrolling Text

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();
  lcd.backlight();
  lcd.print("Scroll Demo");
  delay(1500);
  lcd.clear();
}

void loop() {
  String message = "This text scrolls across the display...";

  // Pad so text enters from right side
  for (int pos = 0; pos < (int)message.length() + 16; pos++) {
    lcd.clear();
    lcd.setCursor(0, 0);
    
    int startChar = max(0, (int)pos - 16);
    int cursorPos = max(0, 16 - (int)pos);
    
    lcd.setCursor(cursorPos, 0);
    lcd.print(message.substring(startChar, startChar + 16));
    delay(300);
  }
}

Code — Display Sensor Data (DHT11)

Show live temperature and humidity from a DHT11:

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

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

  lcd.setCursor(0, 0);
  lcd.print("  Temp & Humid  ");
  lcd.setCursor(0, 1);
  lcd.print("  Initializing  ");
  delay(2000);
}

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

  if (!isnan(temp) && !isnan(humidity)) {
    lcd.clear();

    lcd.setCursor(0, 0);
    lcd.print("Temp: ");
    lcd.print(temp, 1);
    lcd.print(" C");

    lcd.setCursor(0, 1);
    lcd.print("Hum:  ");
    lcd.print(humidity, 1);
    lcd.print(" %");
  }

  delay(2000);  // DHT11 needs at least 1s between readings
}

Code — Custom Characters

LCD displays support up to 8 custom characters (5×8 pixel each):

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Heart symbol (5x8 pixels)
byte heart[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000,
  0b00000
};

// Thermometer symbol
byte thermometer[8] = {
  0b00100,
  0b01010,
  0b01010,
  0b01110,
  0b01110,
  0b11111,
  0b11111,
  0b01110
};

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

  lcd.createChar(0, heart);       // Store in slot 0
  lcd.createChar(1, thermometer); // Store in slot 1

  lcd.setCursor(0, 0);
  lcd.print("I ");
  lcd.write(0);  // Print heart
  lcd.print(" Arduino!");

  lcd.setCursor(0, 1);
  lcd.write(1);  // Print thermometer
  lcd.print(" 23.5 C");
}

void loop() {}

Adjusting LCD Contrast

If characters appear too dark, too light, or invisible:

  1. Look for the small blue potentiometer on the I2C adapter
  2. Turn it slowly with a screwdriver
  3. Characters should appear clearly — dark letters on light blue background

If there's no potentiometer, the contrast is fixed. Some modules require the backlight on (lcd.backlight()) before contrast adjustment is visible.

Simulate on Wokwi

Wokwi supports LCD 16x2 with I2C:

  1. Go to wokwi.com → New Project → Arduino Uno
  2. Add LCD1602 component
  3. In component settings, enable I2C mode
  4. Connect SDA → A4, SCL → A5, VCC → 5V, GND → GND
  5. Paste the display text code and click Play

See the Wokwi guide for full setup.

Troubleshooting

Nothing appears on the LCD:

  • Try the I2C scanner sketch to confirm the address
  • Change the address in LiquidCrystal_I2C lcd(0x27...) to 0x3F
  • Adjust the contrast potentiometer on the I2C adapter
  • Check that lcd.backlight() is called in setup()

LCD shows blocks (■■■■) but no text:

  • Wrong I2C address. Run the scanner sketch to find the correct one
  • Or the contrast is set too high — turn the potentiometer counterclockwise

I2C scanner finds nothing:

  • Verify SDA is on A4 and SCL is on A5 (for Uno/Nano)
  • Check VCC is 5V (not 3.3V)
  • Confirm the I2C adapter is soldered properly to the LCD

Text disappears after a few seconds:

  • You're calling lcd.clear() in loop(). Clear only when updating, not every loop iteration.

Characters look scrambled:

  • Your LCD might be 20x4, not 16x2. Change to LiquidCrystal_I2C lcd(0x27, 20, 4);

Frequently Asked Questions (FAQ)

What does 16x2 mean? 16 columns × 2 rows = 32 character positions total. Each character is a 5×8 dot matrix cell.

Can I use a 20x4 LCD with the same code? Yes. Change LiquidCrystal_I2C lcd(0x27, 16, 2) to LiquidCrystal_I2C lcd(0x27, 20, 4). The functions and I2C wiring are identical.

Can I use LCD with ESP32? Yes. Connect SDA to GPIO21, SCL to GPIO22. Use the same LiquidCrystal_I2C library. Code is identical.

Why use I2C instead of direct wiring? Direct wiring uses 6 GPIO pins (RS, EN, D4–D7) plus 2 for power. I2C uses only 2 pins (SDA, SCL) which are shared with all other I2C devices. Saves 4 GPIO pins.

How many I2C devices can I connect at once? Up to 127 (in theory). In practice, up to 8-16 with short wires. Each device must have a unique address. Most PCF8574 adapters let you set the address with 3 solder jumpers (A0/A1/A2).

How do I display numbers that change? Use lcd.setCursor() to position the cursor at a fixed location, then lcd.print() to overwrite. Add padding spaces to clear leftover digits: lcd.print(value, 1); lcd.print(" ");

Comentarios (0) /de/blog/arduino-lcd-i2c-display