Project Overview & Difficulty Rating
Interfacing an LCD liquid crystal display with an Arduino is a rite of passage for embedded builders. The classic 16x2 or 20x4 HD44780-based LCDs remain the most cost-effective way to output local telemetry, debug states, and user menus without relying on a serial monitor. While older tutorials wire these displays in 4-bit parallel mode (consuming 6 GPIO pins), modern builds almost exclusively use an I2C backpack. This reduces the wiring to just four pins (VCC, GND, SDA, SCL) and frees up your microcontroller for sensors and actuators.
This guide targets the Arduino Uno R3 and Arduino Uno R4 Minima board variants. We will cover the exact hardware needed, provide a robust, error-handling code base, and dive deep into the specific I2C bus failures that leave beginners staring at a blank, backlit screen.
Parts List & Spec Sheet
Before wiring, verify your I2C backpack silicon. The market is flooded with two variants of the I/O expander chip, and using the wrong address in your code is the number one cause of failure.
| Component | Exact Variant / Model | Estimated Cost | Notes |
|---|---|---|---|
| Microcontroller | Arduino Uno R3 or R4 Minima | $12 (Clone) - $22 (Official) | Target board for this code. 5V logic. |
| Display Module | 16x2 LCD with HD44780 Controller | $3 - $5 | Blue backlight with white text is standard. |
| I2C Backpack | PCF8574 or PCF8574A I/O Expander | $1 - $2 (usually pre-soldered) | PCF8574 = 0x27. PCF8574A = 0x3F. |
| Wiring | Female-to-Male Jumper Wires | $3 | 4 wires required for I2C connection. |
Pin Mapping Table (Arduino Uno R3 / R4)
| LCD I2C Backpack Pin | Arduino Uno R3 / R4 Pin | Wire Color (Recommended) | Function |
|---|---|---|---|
| GND | GND | Black | Common ground reference. |
| VCC | 5V | Red | Logic and backlight power (requires ~80mA). |
| SDA | A4 (or dedicated SDA header) | Blue | I2C Serial Data line. |
| SCL | A5 (or dedicated SCL header) | Yellow | I2C Serial Clock line. |
Wiring Steps & Complete Compilable Code
Follow these numbered steps to physically connect the display, then upload the provided code.
- Power Down: Disconnect the Arduino from USB or external power before wiring.
- Connect Power: Route the 5V pin to the VCC pin on the backpack, and GND to GND. Do not use the 3.3V pin; the HD44780 controller and backlight LEDs require 5V to operate correctly.
- Connect I2C Data: Connect SDA to A4 and SCL to A5. If you are using an Arduino Mega 2560 instead of the Uno, these map to pins 20 (SDA) and 21 (SCL).
- Adjust Contrast: Locate the small blue trimpot (variable resistor) on the back of the I2C backpack. We will adjust this after powering on.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// TARGET BOARD: Arduino Uno R3 / R4 Minima
// PIN DEFINITIONS: SDA to A4, SCL to A5
// PCF8574 address is typically 0x27. If using PCF8574A, change to 0x3F.
const int lcdAddress = 0x27;
const int lcdCols = 16;
const int lcdRows = 2;
// Initialize the library with the I2C address and LCD dimensions
LiquidCrystal_I2C lcd(lcdAddress, lcdCols, lcdRows);
void setup() {
Serial.begin(115200);
Wire.begin();
// ERROR HANDLING: Verify I2C device presence before initializing
Wire.beginTransmission(lcdAddress);
byte error = Wire.endTransmission();
if (error != 0) {
Serial.println("FATAL: LCD not found on I2C bus. Check wiring and address.");
// Blink onboard LED to indicate hardware fault without hanging the main loop
pinMode(LED_BUILTIN, OUTPUT);
while(1) {
digitalWrite(LED_BUILTIN, HIGH); delay(250);
digitalWrite(LED_BUILTIN, LOW); delay(250);
}
}
// Initialize LCD and turn on backlight
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("ElectricalFlux");
lcd.setCursor(0, 1);
lcd.print("LCD I2C Ready!");
}
void loop() {
// Static display for this baseline test.
// Add your sensor reading or menu logic here.
}
Debugging: Blank Screens & I2C Bus Errors
When an LCD liquid crystal display fails to show text, the issue is almost always physical or address-related, not a code logic error. If your serial monitor outputs FATAL: LCD not found on I2C bus or if you run an I2C scanner and see the exact error string no I2C devices found, work through these ranked causes.
The First Three Things to Check When It Fails
- Verify the I2C Address (0x27 vs 0x3F): Look closely at the black chip on the backpack. If it reads
PCF8574A, the base address shifts. You must changeconst int lcdAddress = 0x27;to0x3Fin the code. If you aren't sure, run the standard Arduino Wire Scanner Sketch to poll the bus. - Adjust the Contrast Trimpot: If the backlight is on but you only see solid white blocks on the top row (or nothing at all), the contrast voltage (V0) is wrong. Take a small Phillips screwdriver and turn the blue trimpot on the backpack. Turn it slowly counter-clockwise until the text appears and the blocks fade.
- Confirm SDA/SCL Pin Mapping: A swapped SDA/SCL pair will not damage the board, but the I2C bus will fail to handshake. Double-check that SDA is on A4 and SCL is on A5. On some clone Uno boards, the dedicated SDA/SCL headers near the USB port are internally disconnected; always use the A4/A5 pins to be safe.
Ranked Causes for "no I2C devices found"
- Cause 1: Missing Pull-up Resistors. The PCF8574 backpack usually includes 10k pull-up resistors on the SDA and SCL lines. If you are using a custom bare PCB or a damaged module, the I2C bus will float. Measure the resistance between SDA and 5V; it should read ~10kΩ.
- Cause 2: Insufficient 5V Current. The backlight LEDs draw 60-80mA. If your Arduino is powered via a weak USB hub, the 5V rail may brownout, causing the I/O expander to reset and drop off the bus. Power the Arduino via the barrel jack with a 7V-9V supply for stability.
- Cause 3: Cold Solder Joints on the Backpack. Many cheap modules have the 16-pin header poorly soldered to the LCD glass. If pins 15 and 16 (Anode/Cathode for the backlight) or pin 4 (RS) have cold joints, the display will malfunction. Reflow the header pins with a soldering iron set to 350°C.
Extending and Simplifying the Build
Once you have the 16x2 display running, you will likely want to scale the project. Here is how to adapt the hardware and code.
Scaling to a 20x4 LCD
The 20x4 LCD uses the exact same HD44780 controller and I2C backpack. You do not need to rewire anything. Simply change the initialization variables in the code:
const int lcdCols = 20;
const int lcdRows = 4;
You can now use lcd.setCursor(0, 2) and lcd.setCursor(0, 3) to write to the third and fourth rows. Note that the memory mapping on 20x4 displays is non-linear; row 3 starts at address 0x10, not 0x28. The LiquidCrystal_I2C library handles this translation automatically.
Simplifying: Dropping I2C for 4-Bit Parallel
If your project requires high-speed I2C sensors (like a BNO055 IMU or multiple VL53L0X time-of-flight sensors) and you are experiencing I2C bus congestion or timing jitter, you can simplify the bus by removing the I2C backpack entirely. Wire the LCD directly in 4-bit mode using pins D4, D5, D6, D7 for data, and D8, D9 for RS and Enable. This consumes 6 GPIO pins but completely removes the LCD from the I2C bus, freeing up bandwidth and eliminating address conflicts. You will switch to the standard built-in LiquidCrystal library instead of the I2C variant.
Daisy-Chaining Multiple Displays
You can connect up to 8 LCDs on a single I2C bus by altering the address pads on the PCF8574 backpack. Look for three small jumper pads labeled A0, A1, and A2. By default, they are pulled high (address 0x27). If you scrape away the solder bridging A0 to ground, the address changes. This allows you to instantiate multiple objects in your code: LiquidCrystal_I2C lcd1(0x27, 16, 2); and LiquidCrystal_I2C lcd2(0x26, 16, 2);.
Frequently Asked Questions
How to find the I2C address for an LCD liquid crystal display on Arduino?
The most reliable method is to use an I2C scanner sketch. Upload the default File > Examples > Wire > i2c_scanner sketch from the Arduino IDE. Open the Serial Monitor at 115200 baud. The scanner will poll all 127 possible addresses and print the exact hex address (usually 0x27 or 0x3F) of any responding device. If it returns nothing, your wiring is incorrect or the backpack is dead.
Why is my LCD liquid crystal display showing white blocks on the first row?
Solid white blocks on the top row (with the bottom row blank) indicate that the LCD is receiving power and the backlight is functioning, but the HD44780 controller has not been successfully initialized by the microcontroller. This is almost always caused by an incorrect I2C address in your code, or a swapped SDA/SCL wire. The controller defaults to showing the uninitialized memory state (blocks) until it receives the proper initialization byte sequence over the I2C bus.
Can I connect multiple LCD liquid crystal displays to one Arduino?
Yes, but not by simply plugging them into the same pins without modification. Because they share the same default I2C address (0x27), they will conflict and display mirrored or garbled text. To connect multiple displays, you must modify the A0, A1, and A2 address jumper pads on the back of each PCF8574 backpack to give every screen a unique I2C address, then declare separate objects in your Arduino code for each address.
What is the difference between the PCF8574 and PCF8574A backpack chips?
According to the NXP PCF8574 datasheet, the difference lies purely in the base I2C address. The PCF8574 has a base address of 0x20, while the PCF8574A has a base address of 0x38. When the A0, A1, and A2 pins are pulled high (the default state on most cheap backpacks), the PCF8574 resolves to 0x27, and the PCF8574A resolves to 0x3F. Always check the laser etching on the chip to know which constant to use in your code.






