Arduino LCD 16x2 with I2C — Wiring, Code & Display Text (2025)
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
| Component | Notes |
|---|---|
| Arduino Uno or Nano | Any 5V Arduino |
| LCD 16x2 (or 20x4) | Standard HD44780-compatible |
| I2C adapter module | PCF8574 backpack (usually comes pre-soldered) |
| 4 jumper wires | Male-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 Adapter | Arduino Uno | Arduino Nano |
|---|---|---|
| GND | GND | GND |
| VCC | 5V | 5V |
| SDA | A4 | A4 |
| SCL | A5 | A5 |
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
- Open Arduino IDE
- Go to Sketch → Include Library → Manage Libraries
- Search for LiquidCrystal I2C by Frank de Brabander
- 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 displaylcd.backlight()— turn on backlight (uselcd.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 positionlcd.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:
- Look for the small blue potentiometer on the I2C adapter
- Turn it slowly with a screwdriver
- 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:
- Go to wokwi.com → New Project → Arduino Uno
- Add LCD1602 component
- In component settings, enable I2C mode
- Connect SDA → A4, SCL → A5, VCC → 5V, GND → GND
- 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...)to0x3F - Adjust the contrast potentiometer on the I2C adapter
- Check that
lcd.backlight()is called insetup()
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()inloop(). 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(" ");
Related Guides
- DHT11 Temperature Sensor — Show temperature on this LCD
- Wokwi Simulator Guide — Test LCD circuits online
- HC-SR04 Ultrasonic Sensor — Show distance on LCD
- Ohm's Law Calculator — Calculate resistor values