The Serial Monitor needs a laptop. A 16×2 LCD lets a project show information on its own. With an I2C backpack soldered on the back, it needs only four wires.
What is I2C?
I2C is a two-wire bus: SDA carries data, SCL carries the clock. Many devices can share the same two wires, because each has an address — a number from 0x08 to 0x77. The board calls an address and only that device answers.
On the Uno, SDA is A4 and SCL is A5 (they’re also repeated on the two pins next to AREF).
Wiring
| LCD backpack | Uno |
|---|---|
| GND | GND |
| VCC | 5V |
| SDA | A4 |
| SCL | A5 |
Install the library
Tools → Manage Libraries… → search LiquidCrystal I2C by Frank de Brabander → Install.
How the sketch finds the LCD
Most backpacks answer at 0x27, some (with a PCF8574A chip) at 0x3F. The sketch knocks on both doors with Wire.beginTransmission() and uses whichever answers — then prints the address so you know it for later projects.
If neither answers, the I2C scanner checks every address.
Custom characters
Each character cell is 5 dots wide and 8 tall. createChar(0, smiley) stores your own drawing in slot 0 (there are 8 slots); write(byte(0)) prints it. Each B01010 row is one line of dots, 1 = on.
Blank screen or a row of boxes?
That’s almost always contrast. Turn the small blue trimmer on the backpack slowly with a screwdriver while the sketch runs. The LCD blank or blocks fix covers the rest.
Try it
- Show day 10’s temperature on the LCD — day 15 builds exactly this, so try it yourself first.
- Use
(char)223to print a ° sign — it’s built into most of these LCDs. - Turn the backlight off after 30 s with
noBacklight()to save power.
You’re done when…
- The Serial Monitor (9600 baud) prints
LCD found at 0x27(or0x3F) — note it down for later projects. - The LCD shows
Namaste!and a smiley on line 1, andUp 00:00:07counting up on line 2. - After turning the contrast trimmer, the characters are crisp, with no row of solid boxes.
Today's code
// Day 12 — I2C and the 16x2 LCD (Arduino Uno / Nano)
// LCD with a PCF8574 I2C backpack: GND → GND, VCC → 5V, SDA → A4, SCL → A5.
// The sketch first asks "who is on the bus?" at the two usual addresses, then uses the one that answers.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C* lcd = nullptr;
// A custom character: 8 rows of 5 dots each. 1 = dot on.
byte smiley[8] = {
B00000,
B01010,
B01010,
B00000,
B10001,
B01110,
B00000,
B00000,
};
bool answers(uint8_t address) {
Wire.beginTransmission(address);
return Wire.endTransmission() == 0; // 0 = a device acknowledged
}
void setup() {
Serial.begin(9600);
Wire.begin();
uint8_t address = 0;
if (answers(0x27)) address = 0x27; // most PCF8574T backpacks
else if (answers(0x3F)) address = 0x3F; // PCF8574AT backpacks
if (address == 0) {
Serial.println(F("No LCD at 0x27 or 0x3F - check SDA/SCL and power"));
while (true) delay(1000);
}
Serial.print(F("LCD found at 0x"));
Serial.println(address, HEX);
lcd = new LiquidCrystal_I2C(address, 16, 2);
lcd->init();
lcd->backlight();
lcd->createChar(0, smiley); // store the drawing in slot 0
lcd->setCursor(0, 0);
lcd->print(F("Namaste! "));
lcd->write(byte(0)); // print slot 0
lcd->setCursor(0, 1);
lcd->print(F("Up "));
}
void loop() {
unsigned long s = millis() / 1000;
char line[14];
snprintf(line, sizeof line, "%02lu:%02lu:%02lu", s / 3600, (s / 60) % 60, s % 60);
lcd->setCursor(3, 1);
lcd->print(line); // same length every time, so no leftover digits
delay(250);
}// Day 12 — I2C and the 16x2 LCD (Arduino Uno / Nano)
// LCD with a PCF8574 I2C backpack: GND → GND, VCC → 5V, SDA → A4, SCL → A5.
// The sketch first asks "who is on the bus?" at the two usual addresses, then uses the one that answers.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C* lcd = nullptr;
// A custom character: 8 rows of 5 dots each. 1 = dot on.
byte smiley[8] = {
B00000,
B01010,
B01010,
B00000,
B10001,
B01110,
B00000,
B00000,
};
bool answers(uint8_t address) {
Wire.beginTransmission(address);
return Wire.endTransmission() == 0; // 0 = a device acknowledged
}
void setup() {
Serial.begin(9600);
Wire.begin();
uint8_t address = 0;
if (answers(0x27)) address = 0x27; // most PCF8574T backpacks
else if (answers(0x3F)) address = 0x3F; // PCF8574AT backpacks
if (address == 0) {
Serial.println(F("No LCD at 0x27 or 0x3F - check SDA/SCL and power"));
while (true) delay(1000);
}
Serial.print(F("LCD found at 0x"));
Serial.println(address, HEX);
lcd = new LiquidCrystal_I2C(address, 16, 2);
lcd->init();
lcd->backlight();
lcd->createChar(0, smiley); // store the drawing in slot 0
lcd->setCursor(0, 0);
lcd->print(F("Namaste! "));
lcd->write(byte(0)); // print slot 0
lcd->setCursor(0, 1);
lcd->print(F("Up "));
}
void loop() {
unsigned long s = millis() / 1000;
char line[14];
snprintf(line, sizeof line, "%02lu:%02lu:%02lu", s / 3600, (s / 60) % 60, s % 60);
lcd->setCursor(3, 1);
lcd->print(line); // same length every time, so no leftover digits
delay(250);
}