BLOG / TUTORIALS / I2C LCD Display Arduino: Wiring, Library…
Статья блога

I2C LCD Display Arduino: Wiring, Library, and Custom Characters Guide

Viktor Build ~9 min read

Complete guide to using an I2C LCD display with Arduino. Learn wiring, I2C address scanning, library setup, and how to create custom characters with code examples.

I2C LCD displays are the go-to upgrade for any Arduino project that needs to show text without burning through a dozen GPIO pins. Instead of requiring six or more digital pins like a standard parallel LCD, an I2C backpack reduces the connection to just two wires (SDA and SCL) plus power and ground. This guide covers everything you need to get an I2C LCD display working with Arduino: wiring, library installation, scanning for the correct I2C address, and creating your own custom characters.

Why Use an I2C LCD Display with Arduino?

Standard 16×2 character LCDs are cheap and easy to read, but they eat up pins. You need RS, E, D4–D7 — that's six digital I/O lines. On an Arduino Uno you only have 14 digital pins, so a parallel LCD takes almost half of them. An I2C LCD module adds a small backpack board with a PCF8574 or PCF8574A I/O expander chip. This chip converts the parallel interface into a two-wire I2C bus. The result: more free pins for sensors, buttons, or other peripherals.

Feature Standard Parallel LCD I2C LCD (with backpack)
Pin count 6–10 4 (VCC, GND, SDA, SCL)
Wiring complexity Medium (many jumpers) Low (two data wires)
Library LiquidCrystal LiquidCrystal_I2C
Best for Projects with spare pins Pin-constrained builds

If you’ve already worked with standard LCDs, check out our OLED Display Arduino: Complete Wiring Guide and Code Examples for another display option, or How Capacitors Work: A Beginner's Guide with Practical Arduino Circuits if you want to understand the power conditioning these modules need.

I2C LCD Pinout and Wiring

An I2C LCD module typically has a 4‑pin header (some have 5 with an extra SDA/SCL passthrough). The pinout is standard across most modules:

Pin Label Connect to Arduino
1 VCC 5V
2 GND GND
3 SDA A4 (Uno) / D21 (Mega) / D2 (ESP8266)
4 SCL A5 (Uno) / D20 (Mega) / D1 (ESP8266)

Wiring steps:

  1. Connect VCC to 5V on the Arduino.
  2. Connect GND to GND.
  3. Connect SDA to A4 on Uno (or the dedicated SDA pin on other boards).
  4. Connect SCL to A5 on Uno (or the dedicated SCL pin on other boards).

Most I2C LCD backpacks have built-in pull-up resistors on the SDA and SCL lines, so you don't need to add external ones. If you are using long wires (over 50 cm) or multiple I2C devices, you may need to add 4.7kΩ pull-up resistors to 3.3V or 5V.

For projects that combine an I2C LCD with a pot, see How to Read a Potentiometer with Arduino (Raw + Percent) — you can use it to adjust contrast or brightness dynamically.

Scanning for the I2C Address

Before you can write any code, you need to find the I2C address of your specific LCD module. Most backpacks use address 0x27 or 0x3F, but manufacturers sometimes use different addresses. A quick I2C scanner sketch will tell you exactly which address your module is using.

#include <Wire.h>

void setup() {
  Serial.begin(9600);
  Wire.begin();
  Serial.println("I2C Scanner");
}

void loop() {
  byte error, address;
  int nDevices = 0;

  for (address = 1; address < 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) Serial.print("0");
      Serial.print(address, HEX);
      Serial.println("  !");
      nDevices++;
    }
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  delay(5000);
}

Upload this sketch, open the Serial Monitor (9600 baud), and you'll see the device address printed. Write it down — you'll use it in your LCD initialization.

If you get no devices, check your wiring. Common issues: swapped SDA/SCL, incorrect VCC voltage (some modules want 3.3V), or a loose connection.

Installing the LiquidCrystal_I2C Library

Arduino's built-in LiquidCrystal library only supports parallel LCDs. For I2C modules, you need one of the community libraries. The most widely used is LiquidCrystal_I2C by Marco Schwartz (or Frank de Brabander's fork).

Installation via Library Manager

  1. Open Arduino IDE → Tools → Manage Libraries.
  2. Search for LiquidCrystal_I2C.
  3. Install the version by "Frank de Brabander" (most reliable).

If you prefer manual installation, download the ZIP from GitHub and use Sketch → Include Library → Add .ZIP Library.

Alternative library: NewLiquidCrystal

Some advanced users prefer the NewLiquidCrystal library (by Francisco Malpartida) because it offers more flexibility with I2C, SPI, and parallel backends. For most projects, the standard LiquidCrystal_I2C library is sufficient.

Basic Hello World Sketch

Once the library is installed and you know the I2C address, upload this basic "Hello World" example:

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

// Set the LCD address (0x27 or 0x3F) and dimensions (16 columns, 2 rows)
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  lcd.init();               // Initialize the LCD
  lcd.backlight();          // Turn on the backlight
  lcd.setCursor(0, 0);      // Column 0, Row 0
  lcd.print("Viktor.Build");
  lcd.setCursor(0, 1);      // Column 0, Row 1
  lcd.print("Hello World!");
}

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

Key functions explained:

  • lcd.init() — Sends the initialization sequence to the LCD via I2C.
  • lcd.backlight() — Turns the LED backlight on. Use lcd.noBacklight() to turn it off (saves power).
  • lcd.setCursor(col, row) — Sets the cursor position. Row 0 is the top, row 1 is the bottom.
  • lcd.print("text") — Prints text starting at the current cursor position.

If the display shows garbled characters, the most common causes are:

  • Wrong I2C address (re-run the scanner sketch).
  • 5V/3.3V mismatch: most LCDs are 5V, but some backpacks work at 3.3V — check your module's specs.
  • Contrast pot: many backpacks have a small potentiometer (often trimpot) on the backpack. Turn it with a small screwdriver until characters are clear.

Creating Custom Characters

I2C LCDs use the HD44780 controller internally, which supports eight user-defined characters. Each custom character is defined as a 5×8 pixel bitmap — an array of 8 bytes, where each byte represents one row of 5 pixels (bits 0–4, bits 5–7 are ignored).

Steps to create and use a custom character:

  1. Design the character on a 5×8 grid.
  2. Convert each row to a binary byte (1 = pixel on, 0 = off).
  3. Store the bytes in an array.
  4. Use lcd.createChar(location, charArray) to register it (location 0–7).
  5. Use lcd.write(byte(location)) to display it.

Example: Custom Heart and Arrow Characters

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

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

// Right arrow character
byte arrowRight[8] = {
  0b00000,
  0b00100,
  0b00110,
  0b11111,
  0b11111,
  0b00110,
  0b00100,
  0b00000
};

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

  lcd.createChar(0, heart);      // Register at position 0
  lcd.createChar(1, arrowRight); // Register at position 1

  lcd.setCursor(0, 0);
  lcd.write(byte(0));            // Display heart
  lcd.setCursor(2, 0);
  lcd.print("Viktor.Build");
  lcd.setCursor(0, 1);
  lcd.write(byte(1));            // Display arrow
  lcd.setCursor(2, 1);
  lcd.print("Maker Blog");
}

void loop() {
  // Do nothing
}

Tips for designing custom characters:

  • Use an online LCD character generator (search "HD44780 character generator") to draw pixel-by-pixel and get the byte values automatically.
  • Always set the cursor to a new position before writing a custom character, or it will overwrite text at the current cursor location.
  • Custom characters are stored in the LCD's CGRAM (Character Generator RAM). They persist as long as power is applied, but are lost after reset unless you re-create them in setup().

Practical Applications of Custom Characters

  • Progress bars for sensor readings.
  • Battery charge indicators.
  • Small icons (thermometer, Wi‑Fi symbol, lock).
  • Arrow symbols for menu navigation.

If you enjoy building display-based interfaces, check out E-Paper Display with ESP8266 — WeAct 4.2" Wiring & Setup for a low-power alternative.

Advanced: Controlling I2C LCD with ESP8266

The I2C LCD works perfectly with ESP8266 boards like the NodeMCU and Wemos D1 Mini. The main difference is the I2C pins — on ESP8266, SDA defaults to GPIO4 (D2) and SCL defaults to GPIO5 (D1).

ESP8266 wiring:

  • VCC → 3.3V (or 5V if your module is 5V-tolerant — check datasheet)
  • GND → GND
  • SDA → D2 (GPIO4)
  • SCL → D1 (GPIO5)

The code is identical to the Arduino version, just initialize the Wire library after lcd.init() if you need custom pins. Here is a minimal ESP8266 example:

#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("ESP8266 Ready");
  lcd.setCursor(0, 1);
  lcd.print(WiFi.localIP());  // Show IP if connected
}

void loop() {
  // Your IoT logic here
}

For an example that combines an I2C LCD with an ESP8266 and relay, see I Turned My Old Fan Into a Remote Control Fan (ESP8266 + Relay). The LCD shows the fan status while the ESP8266 handles Wi‑Fi control.

Troubleshooting Common Issues

Symptom Likely Cause Fix
No display, no backlight Power issue Check VCC (5V) and GND. Some backpacks need 5V.
Backlight on, no text Contrast too low Adjust the trimpot on the backpack.
Garbled characters Wrong I2C address Run the I2C scanner sketch again.
Flickering display Power supply noise Add a 100µF capacitor across VCC and GND near the module.
Custom characters show wrong symbol Bitmap byte order wrong Check array indexing — first byte is the top row.

Conclusion

The I2C LCD display simplifies Arduino projects by reducing wiring to just two data lines while providing clear, readable output. You now know how to wire it, find its I2C address, install the right library, print text, and create custom bitmap characters for icons and indicators. Whether you are building a weather station, a timer, or a menu-driven gadget, this display gives you a clean output solution without sacrificing GPIO pins.

For more display tutorials, see OLED Display Arduino: Complete Wiring Guide and Code Examples if you need higher resolution or color. And if you want to add user input, How to Read a Potentiometer with Arduino (Raw + Percent) pairs perfectly with an LCD for adjustable settings.

Присоединяйся к сообществу в Discord

Задавай вопросы, делись своими сборками и общайся с другими мейкерами.

Присоединиться к Discord — бесплатно

Понравился туториал?

Поддержи канал на Patreon и получи ранний доступ к проектам и многому другому.

Поддержать на Patreon →