How to Use a 16x2 I2C LCD with Arduino

A 16x2 I2C LCD is the classic 16-column, 2-row character display with a small adapter already mounted on the back. Its main advantage is simple wiring: instead of many connections, you normally only need GND, VCC, SDA and SCL. This lesson explains how it works, the four pins and then the Arduino library.

Required components:

  • 1x Arduino UNO
  • 1x 16x2 I2C LCD display
  • Jumper wires (and an optional breadboard)

Display Image

I2C LCD display Photo

How a 16x2 I2C LCD works

The LCD itself normally uses an HD44780-compatible controller. The small board on the back, known as an I2C backpack, often contains an I/O expander such as the PCF8574. Arduino sends information over I2C, and the backpack converts it into the parallel signals required by the LCD.

I2C uses two lines: SDA for data and SCL for the clock. Every device also has an I2C address. 0x27 is very common on 16x2 LCD backpacks, but other addresses such as 0x3F are also used.

What the 4 I2C pins do

GND

Module ground: connect it to Arduino GND.

VCC

Powers the LCD and backpack. Common Arduino UNO modules are normally used at 5 V.

SDA - Serial Data

I2C data line. On Arduino UNO and classic Nano it corresponds to A4/SDA. Boards with a dedicated SDA pin can use that pin directly.

SCL - Serial Clock

I2C clock line. On Arduino UNO and classic Nano it corresponds to A5/SCL. Other boards may use different pins.

Contrast trimmer, backlight and address

The small trimmer on the backpack adjusts contrast. If the backlight turns on but no characters are visible, rotate it slowly. Many modules also include a backlight jumper and A0, A1 and A2 pads that can modify the I2C address.

Wire an I2C LCD to Arduino UNO

Wiring for I2C LCD display and Arduino UNO
  • GND → GND
  • VCC → 5V
  • SDA → SDA / A4
  • SCL → SCL / A5

On the UNO R3, the dedicated SDA/SCL pins carry the same signals as A4/A5, so you do not need to connect both pairs.

Arduino LiquidCrystal_I2C library

These modules are commonly controlled with a LiquidCrystal_I2C library. If it is not installed, open the Arduino IDE Library Manager and install a library compatible with the LiquidCrystal_I2C.h header. Implementations can differ slightly, but the following API is widely used.

LiquidCrystal_I2C lcd(0x27, 16, 2);

0x27 is the I2C address, 16 is the number of columns and 2 is the number of rows.

First sketch: write on the display

lcd_16x2_i2c.ino
// https://nemiatools.com
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

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

  lcd.setCursor(0, 0);
  lcd.print("Hello Arduino!");

  lcd.setCursor(0, 1);
  lcd.print("NEMIA TOOLS");
}}

void loop() {{
}}

lcd.init() initializes the module and lcd.backlight() turns on the backlight. After that, print() and setCursor() work much like the parallel version.

Most useful LiquidCrystal_I2C commands

  • lcd.init() initializes the LCD.
  • lcd.backlight() and lcd.noBacklight() control the backlight.
  • lcd.print() prints text or numbers.
  • lcd.setCursor() selects the writing position.
  • lcd.clear() clears the LCD.
  • lcd.home() returns the cursor to the beginning.
  • lcd.cursor(), lcd.noCursor(), lcd.blink() and lcd.noBlink() control the cursor.

Example: display a changing value

lcd_i2c_counter.ino
// https://nemiatools.com
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {{
  lcd.init();
  lcd.backlight();
  lcd.print("Power on:");
}}

void loop() {{
  lcd.setCursor(0, 1);
  lcd.print(millis() / 1000);
  lcd.print(" s    ");
  delay(200);
}}

If 0x27 does not work: I2C scanner

If the display is wired correctly but does not respond, find its address with an I2C scanner:

i2c_scanner.ino
// https://nemiatools.com
#include <Wire.h>

void setup() {{
  Wire.begin();
  Serial.begin(9600);
}}

void loop() {{
  for (byte address = 1; address < 127; address++) {{
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {{
      Serial.print("I2C device: 0x");
      if (address < 16) Serial.print("0");
      Serial.println(address, HEX);
    }}
  }}
  delay(3000);
}}

If the Serial Monitor reports 0x3F, for example, use LiquidCrystal_I2C lcd(0x3F, 16, 2);.

Why choose I2C?

The I2C version only uses two communication lines, leaving many Arduino pins available for sensors, buttons and actuators. Multiple I2C devices can also share SDA and SCL as long as their addresses are compatible.

Common problems

Backlight on but no text: adjust the trimmer and verify the address. Scanner finds nothing: check power, GND, SDA and SCL. Sketch compiles but LCD stays blank: make sure the installed library uses the same API as the example.