I2C LCD 16x2 Library for Proteus V1.0: two Arduino UNOs with a blue and a yellow-green I2C LCD and their PCF8574 backpacks, the I2C LCD 16x2 Advance pop-up Display page with a bar graph, and the TEP Serial Monitor printing SCAN and STATUS

I2C LCD 16x2 Library for Proteus V1.0 (PCF8574 + Arduino)

2.5K Views
40
700
60
25
60
PCBWay

Hello friends, I hope you all are doing great. Today, I am going to share the new I2C LCD 16x2 Library for Proteus V1.0. The I2C LCD 16x2 is the same blue or yellow-green 1602 character LCD you know, with a small black I2C backpack soldered to its back. Instead of six or more wires, it needs only four: GND, VCC, SDA and SCL. That is why it is the LCD most of us use with an Arduino today. With this I2C LCD 16x2 Library for Proteus, your sketch talks to an LCD that behaves like the real module: the I2C address, the contrast trimmer on the backpack, the backlight jumper, and the real HD44780 behind it.

This is the second library of our LCD series, after the LCD 16x2 Library for Proteus V3.0, and it is our own model of the whole module: the PCF8574 I2C port expander of the backpack and the HD44780 controller of the LCD. You get four devices: a blue and a yellow-green LCD, each as I2C LCD 16x2 Simple, with a live STATUS panel on the schematic, and I2C LCD 16x2 Advance, with a pop-up window, a bus log and four LCD test tools, among them a new I2C bus tool. The demo runs the popular LiquidCrystal_I2C library on two Arduino UNOs, one LCD at 0x27 and one at 0x3F, with the Simple interface of our TEP Serial Monitor.

NOTICE: This library is very special to our team. We did not just draw a backpack on an LCD: we wrote the PCF8574 I2C chip ourselves, wired it to our HD44780 model exactly like the real backpack, and made the trimmer, the jumper and the address pads clickable. It took our team a lot of hard work and many test runs. Your feedback is the fuel that keeps us going, so please tell us what you think in the comments below or in the LCD Library board of our forum. And if our free libraries help you, you can buy us a coffee. So, let's get started with the I2C LCD 16x2 Library for Proteus V1.0:

I2C LCD 16x2 Library for Proteus V1.0: two Arduino UNOs with a blue and a yellow-green I2C LCD and their PCF8574 backpacks, the I2C LCD 16x2 Advance pop-up Display page with a bar graph, and the TEP Serial Monitor printing SCAN and STATUS
Figure: The I2C LCD 16x2 demo in Proteus 8.5: NODE 1 (blue, Simple, 0x27) and NODE 2 (yellow-green, Advance, 0x3F), LCD2's Display page with "Hello from 0x3F" over a 75 % bar graph, and U2 answering SCAN, BAR 75 and STATUS.

What is an I2C LCD 16x2?

An I2C LCD 16x2 is a normal 16x2 character LCD (2 lines of 16 characters, each 5x8 dots, driven by the HD44780 controller) plus an I2C backpack: a small board of about 41 x 19 mm soldered to the LCD's 16-pin header. On the backpack sit a PCF8574T chip, a blue contrast trimmer, a LED jumper for the backlight, three address pads A0, A1 and A2, and a 4-pin header: GND, VCC, SDA, SCL. For the LCD itself, read our Introduction to 16x2 LCD Module.

The PCF8574 is an I2C port expander: it has 8 output pins, and the Arduino sets all 8 at once by sending one byte over I2C. The backpack wires those 8 pins to the LCD's RS, RW, E, the backlight and D4 to D7. So the LCD still runs on its 4-bit bus, but the bus now comes from the PCF8574 instead of six Arduino pins.

The I2C LCD 16x2 at a Glance

The I2C LCD 16x2 in numbers
FeatureValue
Display2 lines x 16 characters, each 5x8 dots (HD44780 controller)
InterfaceI2C: SDA and SCL (A4 and A5 on an Arduino UNO), plus GND and VCC
Chip on the backpackPCF8574T (addresses 0x20 - 0x27) or PCF8574AT (0x38 - 0x3F)
Default address0x27 (PCF8574T) or 0x3F (PCF8574AT): all three pads open
Supply5 V on VCC
ContrastThe blue trimmer on the backpack
BacklightSwitched by the PCF8574 (P3) through a transistor; the LED jumper must be fitted
SpeedAbout 1.5 ms per character at 100 kHz with LiquidCrystal_I2C
Arduino libraryLiquidCrystal_I2C (Library Manager) and Wire

Terms You Will See in This Article

I2C LCD terms
TermMeaning
I2CA two-wire bus: SDA carries the data, SCL the clock; many devices share it, each with its own address
AddressThe 7-bit number a device answers to, written in hex (0x27)
ACK / NACKAfter each byte the receiver pulls SDA low (ACK) or not (NACK); nobody at an address means NACK
PCF8574The I2C chip on the backpack: one byte in, 8 output pins (P0 - P7)
Port byteThe byte the Arduino writes to the PCF8574; each bit is one pin
Pull-upA resistor that holds SDA and SCL high; I2C devices only pull the lines low
TrimmerThe small blue pot on the backpack that sets the contrast (V0)
DDRAM / CGRAMThe LCD's character memory / its 8 own characters

How the PCF8574 Backpack Works

One Byte, Eight Pins

The backpack connects the PCF8574's pins like this: P0 = RS, P1 = RW, P2 = E, P3 = the backlight and P4 to P7 = D4 to D7. The LCD's D0 to D3 stay unconnected, so the LCD always runs in 4-bit mode. When the Arduino writes the byte 0x4D, for example, the pins become RS 1, RW 0, E 1, backlight 1 and D7 to D4 = 0x4. LiquidCrystal_I2C uses exactly this wiring, which is why it works with almost every blue backpack.

Three I2C Writes for Every Half Byte

The LCD takes a half byte (a nibble) when E falls. With a backpack, the sketch cannot pulse E directly, so LiquidCrystal_I2C sends three port bytes for each nibble: the data, the data with E high, and the data with E low again. A character is two nibbles, so it costs six I2C transactions. At the standard 100 kHz, that is about 1.5 ms per character, roughly five times slower than an LCD wired with six pins. Our Speed tool measures exactly this, and our bus log shows every port byte.

The I2C Address

A PCF8574T answers at 0x20 to 0x27, a PCF8574AT at 0x38 to 0x3F. The three pads A0, A1 and A2 choose the address: an open pad is a 1 and a bridged pad (a blob of solder across it) a 0. A new backpack has all pads open, so it is 0x27 or 0x3F, depending on the chip. If your sketch uses 0x27 but your backpack is 0x3F, the LCD just stays blank. That is the second most common I2C LCD problem, and our model shows it clearly.

The Contrast Trimmer and the LED Jumper

The blue trimmer is a 10k pot between VCC and GND, its wiper on the LCD's V0 pin. Turned one way, the characters fade out; turned the other way, the empty dots turn dark too. A new module can come with any setting, which is why the most common I2C LCD problem is "nothing on the screen": the trimmer. The backlight is switched by P3 through a transistor, and the two-pin LED jumper must be fitted, or the backlight stays off whatever the sketch does.

What's New in I2C LCD 16x2 Library for Proteus V1.0

Here is what the first version brings:

  • Our own PCF8574 model inside TEPLCD.DLL: the I2C slave with its address, ACK, port writes and reads, wired to our HD44780 model exactly like the real backpack.
  • Clickable backpack parts: the contrast trimmer, the LED jumper and the A0 - A2 address pads work while the simulation runs.
  • A WRONG ADDRESS warning: when the sketch writes to another address, the panel says so in red.
  • Four devices in TEPI2CLCD16X2.LIB: blue and yellow-green, each Simple and Advance.
  • Four LCD test tools on the Advance: Init check, I2C bus, Speed and Characters.
  • A two-UNO demo with LiquidCrystal_I2C: one sketch that finds the address itself, one LCD at 0x27 and one at 0x3F, and Serial commands such as SCAN and LIGHT ON / OFF.
  • A compact package: about 1.42 MB, without the C++ source code.

The model file TEPLCD.DLL is shared with our LCD 16x2 V3.0 library and runs its devices too. If you have both, keep this newer DLL.

I2C LCD 16x2 Simple vs I2C LCD 16x2 Advance

All four devices share one model and the same module. They differ in the glass colour and in where you see the extras:

The four devices (search "I2CLCD16X2" in Pick Devices; category Optoelectronics > Alphanumeric LCDs)
DeviceName in Pick DevicesGlassExtras
I2CLCD16X2BLUETEPI2C LCD 16x2 V1.0 Blue - SimpleBlue, white charactersSTATUS panel on the sheet
I2CLCD16X2BLUEADVTEPI2C LCD 16x2 V1.0 Blue - AdvanceBlue, white charactersPop-up window and tools
I2CLCD16X2GREENTEPI2C LCD 16x2 V1.0 Yellow-green - SimpleYellow-green, dark charactersSTATUS panel on the sheet
I2CLCD16X2GREENADVTEPI2C LCD 16x2 V1.0 Yellow-green - AdvanceYellow-green, dark charactersPop-up window and tools

Pick a Simple LCD when you want to see the LCD's state and its I2C address right on the schematic. Pick an Advance LCD when the schematic is crowded, or when you want the bus log and the I2C bus tool. The backpack's trimmer, jumper and pads work on all four.

Download I2C LCD 16x2 Library for Proteus

Click the button below to download I2C-LCD-16x2-Library-for-Proteus-v1.0.zip (about 1.42 MB, without the C++ source code):

I2C LCD 16x2 Library for Proteus V1.0
  • README.txt: a detailed guide to the files, the wiring, the backpack, the test tools and real hardware.
  • Proteus Library Files: TEPI2CLCD16X2.LIB (all four devices), TEPSERIALMON.LIB and our Arduino UNO library ArduinoV3TEP.LIB / ArduinoV3TEP.IDX.
  • Proteus Model Files: TEPLCD.DLL and TEPSERIALMON.DLL.
  • Proteus Simulation: I2C-LCD-16x2-ArduinoUnoV3.pdsprj, I2C_LCD_16x2_Demo.hex (both UNOs run it) and copies of both DLLs.
  • Arduino Code: I2C_LCD_16x2_Demo.ino, the demo sketch. It needs the "LiquidCrystal I2C" library by Frank de Brabander from the Library Manager.

Want all four LCDs? This LCD is also part of our LCD Library for Proteus V3.0: the LCD 16x2, LCD 20x4, I2C LCD 16x2 and I2C LCD 20x4 in one library (16 parts, one DLL), with a demo in which one Arduino UNO drives all four.

No Proteus yet? Read How to Download and Install Proteus 7 and 8. New to libraries? Follow How to Install a New Library in Proteus.

How to Install I2C LCD 16x2 Library for Proteus

  1. Close Proteus and extract the whole zip file to a normal folder, for example your Desktop.
  2. Copy the four files from Proteus Library Files into the LIBRARY folder, usually C:\Program Files (x86)\Labcenter Electronics\Proteus 8 Professional\LIBRARY (on some PCs C:\ProgramData\Labcenter Electronics\Proteus 8 Professional\LIBRARY; use the folder that already holds Proteus's own .LIB files).
  3. Copy TEPLCD.DLL and TEPSERIALMON.DLL from Proteus Model Files into the MODELS folder.
  4. Delete TEPI2CLCD16X2.IDX from the LIBRARY folder if there is one; Proteus makes a new index.
  5. Start Proteus, press P and search for I2CLCD16X2. You get the four devices.

Tip: the demo project needs the .hex file and the two DLLs beside it, so keep the Proteus Simulation folder together. The library was tested in Proteus 8.5; Proteus 7 is not supported.

The I2C LCD 16x2 Board in Proteus

On a real module, the backpack hides behind the LCD. In Proteus we drew it folded up above the LCD, like an exploded view, so you can see and click its parts. The LCD board below it is the one of our LCD 16x2 V3.0: the green PCB in the real 1602 size, the 16 pads (here with the backpack's pins soldered in), the metal frame and the glass with all 32 character cells. The backpack's 4-pin header sits on its left end with the pins pointing left, in the order printed on the real board: GND, VCC, SDA, SCL. Here is LCD1, the blue Simple LCD, in the demo:

I2C LCD 16x2 Simple in Proteus: the blue LCD showing Hello from 0x27 and an up-time clock, the black PCF8574T I2C backpack above it with the contrast trimmer, LED jumper and address pads, and the I2C LCD 16x2 STATUS panel with READY
Figure: LCD1 (blue, Simple) at 0x27: "Hello from 0x27", "Up 00:00:20"; READY - I2C 0x27, 4-BIT, 2 LINES, 5x8 DOTS, DISPLAY ON; VCC 5.00 V, CONTRAST GOOD V0 0.75 V, BACKLIGHT ON (P3 HIGH), I2C 0x27 5145 TX, WRITES 859.

Pinout

The 4-pin header of the backpack, top to bottom
PinNameWhat it doesDemo connection
1GNDGroundGND (net label)
2VCCSupply, 5 VVCC (net label, the 5 V rail)
3SDAI2C data (open drain, with the backpack's pull-up)Arduino A4, through the label SDA_1
4SCLI2C clockArduino A5, through the label SCL_1

The Backpack Up Close

PCF8574T I2C backpack of the I2C LCD 16x2 in Proteus: the GND, VCC, SDA and SCL pins with their net labels, the blue contrast trimmer with V0 0.75 V, the LED jumper, the PCF8574T chip marked 0x27, the A0, A1 and A2 pads and the I2C LED
Figure: The backpack of LCD1: the pins with SCL_1, SDA_1, VCC and GND labels, the trimmer (V0 0.75 V), the LED jumper, the PCF8574T marked 0x27, the open A0 - A2 pads and the I2C LED, over the LCD's PWR, CMD and DATA LEDs.

The parts you see are drawn by the model and change while the simulation runs: the trimmer's slot turns and the label above it shows V0, the jumper shows its cap or two bare pins, a bridged pad gets a solder blob, the chip shows its type (PCF8574T or PCF8574AT) and its address, and the small I2C LED flashes blue when the Arduino talks to this address.

LEDs and Indicators

Board indicators (the same on all four devices)
IndicatorWhat it showsOn the real module?
The backlightThe glass glows when P3 is high and the jumper is fittedYes
PWR LED (red)Lit while the module has powerTEP addition
CMD LED (orange)Flashes when the LCD takes a commandTEP addition
DATA LED (green)Flashes when the LCD takes a characterTEP addition
I2C LED (blue)Flashes on every I2C transaction to this addressTEP addition
Chip markingPCF8574T or PCF8574AT and the address, liveTEP addition
SIMPLE / ADVANCE badge, PANEL / OPEN PANELWhich device this is, and the panel buttonTEP addition

Component Properties

I2C LCD properties (double-click the LCD > Edit Properties)
PropertyMeaningDefault
PANELSimple devices only: the STATUS panel at the start, OPEN or CLOSEDOPEN
ADDRESS0x20 - 0x27 (a PCF8574T board) or 0x38 - 0x3F (a PCF8574AT board); the pads follow it0x27
TRIMMERThe contrast trimmer at the start: V0 in % of VCC (0 = dark blocks, 50 = blank)15
LED_JUMPERFITTED or REMOVEDFITTED
BACKLIGHTBACKPACK (P3 and the jumper), ON or OFF (forced)BACKPACK
CONTRASTTRIMMER or AUTO (always good)TRIMMER

Every run starts from these properties; the clicks on the backpack and the panel's buttons change them for that run only. In the demo, LCD2 has ADDRESS = 0x3F: it is a PCF8574AT board.

Click the Backpack: Trimmer, Jumper and Address Pads

Turning the Contrast Trimmer

Click the left half of the blue trimmer (the - side) to turn the contrast down, the right half (the + side) to turn it up; each click moves V0 by 5 % of VCC, 0.25 V. At the start V0 is 0.75 V and the contrast is GOOD. Seven clicks on the - side bring V0 to 2.50 V: the characters fade to faint ghosts, and the panel says TOO LOW:

I2C LCD 16x2 contrast too low in Proteus: the trimmer turned down to V0 2.50 V, faint characters on the blue LCD, and the STATUS panel reading CONTRAST TOO LOW V0 2.50 V
Figure: Seven clicks on -: V0 2.50 V, CONTRAST TOO LOW; the text is barely visible, exactly what a badly set trimmer does.

Now ten clicks on the + side: V0 drops to 0 V, the full 5 V is across the crystal, and even the empty dots turn dark. The panel says TOO HIGH:

I2C LCD 16x2 contrast too high in Proteus: the trimmer at V0 0.00 V, dark blocks behind every character of the blue LCD, and the STATUS panel reading CONTRAST TOO HIGH V0 0.00 V
Figure: Ten clicks on +: V0 0.00 V, CONTRAST TOO HIGH; dark blocks behind the text. Three clicks on - bring it back to GOOD.

This is the classic "my I2C LCD shows nothing" case. On a real module, turn the trimmer with a small screwdriver until the blocks of the top row just show, then a little back.

Pulling the LED Jumper

Click the LED jumper: its cap comes off, two bare pins show, and the backlight goes out, even though the sketch keeps P3 high. The panel says OFF (NO JUMPER):

I2C LCD 16x2 with the LED jumper removed in Proteus: two bare jumper pins on the backpack, the blue LCD without backlight showing grey characters, and the STATUS panel reading BACKLIGHT OFF (NO JUMPER)
Figure: The jumper pulled: BACKLIGHT OFF (NO JUMPER); the glass is dark and the characters grey. The CMD and DATA LEDs flash with the clock update.

Click it again to fit the cap. The sketch can switch the backlight too: lcd.noBacklight() sets P3 low (the panel then says OFF (P3 LOW)), and lcd.backlight() sets it high again.

Bridging an Address Pad: WRONG ADDRESS

Click pad A0: it gets a solder blob, and the backpack's address becomes 0x26 at once. The sketch still writes to 0x27, so nothing reaches the LCD any more: the clock stops. The banner turns red and says WRONG ADDRESS - THE SKETCH WRITES TO 0x27, THIS BACKPACK IS 0x26, and the I2C readout says 0x26 SKETCH: 0x27:

I2C LCD 16x2 wrong address in Proteus: pad A0 bridged, the chip marked 0x26, the LCD clock stopped, and the red banner WRONG ADDRESS - THE SKETCH WRITES TO 0x27, THIS BACKPACK IS 0x26
Figure: Pad A0 bridged: the chip shows 0x26, the clock stops at 00:00:45, the banner says WRONG ADDRESS and the I2C readout 0x26 SKETCH: 0x27.

Click the pad again and everything runs on. On a real module, a wrong address looks just like a bad contrast: a blank LCD. In our model you see the reason at once, and the I2C bus tool of the Advance tells you both addresses.

I2C LCD 16x2 Simple: The STATUS Panel

Under the Simple LCD sits the I2C LCD 16x2 STATUS panel (see the pictures above):

The I2C LCD 16x2 STATUS panel
PartWhat it shows
BannerREADY (with the I2C address, bus, lines and font), WAITING FOR I2C, WRONG ADDRESS, NOT INITIALISED, TOO FAST or NO POWER
DISPLAY, CURSOR, BLINK, ENTRYThe display, cursor, blink and entry mode flags
ADDRESSWhere the LCD's address counter points (row and column, or OFF SCREEN)
VCC, CONTRASTThe supply, and the contrast with V0 (GOOD, WEAK, TOO LOW or TOO HIGH)
BACKLIGHTON (P3 HIGH), OFF (P3 LOW), OFF (NO JUMPER), or forced
I2CThis backpack's address and the transactions so far, or the address the sketch uses instead
WRITESBytes the LCD took (commands and characters)
BusThe last 6 transfers to the LCD, newest first
ButtonsBACKLIGHT (BACKPACK, ON, OFF) and CONTRAST (TRIMMER, AUTO (GOOD)) override the backpack for this run

The red X in the panel's header closes it, and PANEL on the board opens it again.

I2C LCD 16x2 Advance: The Pop-Up Window

The Advance LCD keeps only the module on the schematic. Here is LCD2, the yellow-green Advance LCD; its chip says PCF8574AT and 0x3F:

I2C LCD 16x2 Advance in Proteus: the yellow-green LCD showing Hello from 0x3F and the clock, its PCF8574AT backpack marked 0x3F with the net labels SCL_2, SDA_2, VCC and GND, the ADVANCE badge and the OPEN PANEL button
Figure: LCD2 (yellow-green, Advance) at 0x3F: "Hello from 0x3F", "Up 00:01:06"; the chip reads PCF8574AT 0x3F.

At Run, the "TEP I2C LCD 16x2 Advance" pop-up opens with the look of our TEP Serial Monitor: move it by its header, resize or minimise it, and reopen it with OPEN PANEL. The header has the Display, Bus log and Tests tabs and the theme, Settings and Help icons. Under the header, the BACKLIGHT (Backpack, On, Off) and CONTRAST (Trimmer, Auto) buttons work like the Simple panel's.

The Display Page

I2C LCD 16x2 Advance Display page in Proteus: a big copy of the yellow-green glass with Hello from 0x3F, the BUS, LINES, FONT, DISPLAY, CURSOR, BLINK, ENTRY and ADDRESS tiles, the DDRAM grid, the CGRAM characters, the supply, contrast trimmer and backlight, and the latest transfers
Figure: The Display page: "Hello from 0x3F", "Up 00:01:18"; VCC 5.00 V, Contrast good V0 0.75 V (trimmer 15 %), Backlight ON (P3 high, jumper fitted); the status bar says I2C 0x3F.
  • The glass: a big copy of the LCD, dot by dot.
  • Tiles: BUS 4-BIT, LINES 2, FONT 5x8, DISPLAY ON, CURSOR OFF, BLINK OFF, ENTRY INCREMENT and the address counter.
  • DDRAM: both lines of 40 bytes; the green frame is what the glass shows.
  • CGRAM: the 8 own characters of the demo.
  • Supply, contrast trimmer and backlight: VCC, the contrast with V0 and the trimmer setting, and the backlight with P3 and the jumper.
  • The latest transfers: the bytes the LCD took, about 1.5 ms apart over I2C.

The Bus Log Page

I2C LCD 16x2 Advance bus log in Proteus: the bytes the LCD took behind the backpack with their time, gap, CMD or DATA, meaning and margin, a clock update of line 2 about 1.49 ms per byte, and the filters
Figure: The Bus log: a clock update ("Up 00:01:22") after a pause of 472.86 ms; 1.49 ms between bytes, 1.45 ms of margin.

The Bus log shows what the LCD took from the backpack: one row per byte, newest first, with the time, the gap, CMD or DATA, the byte, its meaning and the margin to the busy time. Read the picture from the bottom up: SET DDRAM ADDRESS 0x40 (line 2), then "U", "p", a space, the digits of the clock, and finally the heart. Every byte comes 1.49 ms after the one before: that is the I2C speed. The controller needs only 37 to 41 us, so there is a margin of 1.45 ms.

The Tests Page: Four LCD Test Tools

The tools read the bus as the model sees it during the running simulation. Each has a bold "Good for:" line and a verdict per row: green is fine, amber means check, red means wrong. All the results below come from LCD2 in Proteus 8.5.

1. Init Check: Is the Start-Up Right?

I2C LCD 16x2 init check in Proteus: every row green - the wait after power-on, the wake-up function sets, the waits, the 4-bit interface, 2 lines, display on, clear, entry mode, no bytes ignored - and the start-up as LiquidCrystal_I2C sent it
Figure: Init check: every row green, verdict "the LCD is initialised like the datasheet's figure (LiquidCrystal_I2C does this)"; the custom characters show as rows of dots.
Init check (LCD2)
CheckResult in the picture
Wait after power-on1121.6 ms (the datasheet asks for 40 ms)
Function set x3 (wake-up)5 function sets seen
Wait after the 1st / 2nd5.27 ms / 5.27 ms (at least 4.1 ms / 100 us)
Interface, lines4-BIT (DB4..DB7), 2 lines (N = 1)
Display on, clear, entry modeYes, yes, increment (left to right)
Bytes ignored (busy)None during the start-up
VerdictThe LCD is initialised like the datasheet's figure

Why 1121.6 ms? LiquidCrystal_I2C's init() waits 50 ms, writes the port once, and then waits a whole second before it starts the LCD. Add the sketch's address search, and the first command arrives 1.12 s after power-on. In the start-up list, the custom characters appear as rows of dots, for example ".#.#." for 0x0A.

2. I2C Bus: Is the Sketch Talking to This Backpack?

I2C LCD 16x2 I2C bus tool in Proteus: this backpack 0x3F PCF8574AT with its pads, the sketch writing to 0x3F, the transactions and bytes, 15 address checks, SCL 100 kHz, the port byte decoded, and the last I2C transactions
Figure: I2C bus: 0x3F, PCF8574AT, pads open; the sketch writes to 0x3F; 1 address check here and 15 to other addresses; SCL 100 kHz; the port 0x19 = RS 1, RW 0, E 0, LED 1, D7-D4 0x1.

Good for: "a blank I2C LCD - the wrong address, SDA / SCL swapped, or a clock too fast." The tool shows:

I2C bus (LCD2)
RowResult in the picture
This backpack0x3F - PCF8574AT, pads A2 A1 A0: open open open
The sketch writes to0x3F - this backpack (19850 writes since power-on)
Measuring for84.966 s of simulation: 19851 transactions to 0x3F, 19850 bytes
Address checks1 to this address, 15 transactions to other addresses (the sketch's address search)
SCL clock100 kHz (the PCF8574 is rated for 100 kHz)
The port now0x19 = RS 1 RW 0 E 0 LED 1 D7-D4 0x1
Bytes lost (too fast)None

The 15 address checks are the demo's address search: it tries 0x20 to 0x27 and 0x38 to 0x3E before it finds 0x3F. Below the rows, the last transactions are listed with every port byte decoded: you can watch the E bit go 1, 0, 1, 0 as LiquidCrystal_I2C pulses each nibble.

Now bridge pad A2 of LCD2 (0x3F becomes 0x3B) while the sketch keeps writing to 0x3F. The tool turns red:

I2C LCD 16x2 I2C bus tool with a wrong address in Proteus: this backpack 0x3B with A2 bridged, a red row saying the sketch writes to 0x3F, NOT this backpack, and every transaction to 0x3F with nobody answering
Figure: Pad A2 bridged: this backpack is 0x3B; red "0x3F - NOT this backpack"; 0 transactions to 0x3B, 570 to other addresses; every row "another address - nobody answered (NACK)".

The counters start again at the pad click: in 2.691 s, 0 transactions reached 0x3B and 570 went to 0x3F, where nobody answers any more. Each of them ends with a NACK after the address byte, so the LCD gets nothing. The status bar says it too: "Sketch writes to 0x3F, not 0x3B".

3. Speed: How Fast Is an I2C LCD?

I2C LCD 16x2 speed test in Proteus: 1489.2 us per byte while writing, a full line in 25.32 ms, a full screen in 50.63 ms, at most 20 screens per second, 671 characters per second, and the gap before each of the last 60 bytes
Figure: Speed: 1489.2 us per byte, a full screen in 50.63 ms (at most 20 screens per second), 671 characters per second; the bus line explains the 3 port writes per nibble.

Good for: "flicker / slow screens - how long a full line and a full screen take on this bus." The tool takes the typical gap between two bytes inside a burst of writes:

Speed (LCD2, the demo sketch with LiquidCrystal_I2C)
ResultValue
Time per byte1489.2 us (the controller needs only 37 to 41 us)
A full line (16 characters + the address)25.32 ms
A full screen (32 characters + 2 addresses)50.63 ms, so at most 20 screens per second
Characters per second671 while writing
BusI2C at 100 kHz: LiquidCrystal_I2C sends 3 port writes per nibble, 6 per byte

Compare it with the same LCD on six pins in our LCD 16x2 V3.0 demo: about 0.3 ms per byte there, 1.5 ms here. For a clock or a menu that is plenty, but a sketch that rewrites the whole screen in a loop will flicker. Update only what changed. The tall blue bars in the chart are the pauses between the clock updates.

4. Characters: Find Any Symbol's Code

Good for: "finding a symbol's code (the degree sign is 0xDF = 223) - click a character." The map shows all 256 codes of the A00 ROM in the datasheet's layout, the characters on the display highlighted. Click one to see its code and the line to print it, for example lcd.write(223);, and, for one of the 8 own characters, its rows as a byte array for createChar(). The LCD 16x2 Library for Proteus V3.0 article shows this tool in a picture.

I2C LCD 16x2 with Arduino in Proteus

Open I2C-LCD-16x2-ArduinoUnoV3.pdsprj from the Proteus Simulation folder, with I2C_LCD_16x2_Demo.hex and both DLLs beside it. Its two Arduino UNOs from our Arduino Library for Proteus V3.0 run the same HEX file:

  • NODE 1 - SIMPLE (on-sheet panel): UNO 1 (ARD1) with LCD1 (blue, Simple, address 0x27) and Serial Monitor U1.
  • NODE 2 - ADVANCE (pop-up panel): UNO 2 (ARD2) with LCD2 (yellow-green, Advance, address 0x3F, a PCF8574AT board) and Serial Monitor U2.
I2C LCD 16x2 Arduino Proteus circuit running: two Arduino UNOs, the blue I2C LCD 16x2 Simple with its STATUS panel showing Hello from 0x27, the yellow-green I2C LCD 16x2 Advance showing Hello from 0x3F, and two TEP Serial Monitors
Figure: The whole circuit running, without the pop-up windows: LCD1 shows "Hello from 0x27", LCD2 "Hello from 0x3F", both "Up 00:00:02".

Wiring

Demo wiring (the same on both UNOs)
Backpack pinArduino UNOWhy
GNDGNDGround
VCC5VSupply
SDAA4 (SDA)I2C data
SCLA5 (SCL)I2C clock
Serial Monitor RXD / TXD / GNDD1 / D0 / groundShows what the Arduino prints, sends what you type

On an Arduino UNO, SDA is A4 and SCL is A5 (see our Introduction to Arduino UNO). In the demo, every pin goes through a short wire to a net label: in Proteus, terminals with the same name are connected. SDA_1 at the backpack of LCD1 meets SDA_1 at A4 of UNO 1, and node 2 has its own SDA_2 and SCL_2, so the two buses stay apart. VCC and GND are Proteus's power rails.

I2C LCD 16x2 wiring in Proteus with net labels: the backpack pins labelled SCL_1, SDA_1, VCC and GND, and the Arduino UNO pins A4 and A5 labelled SDA_1 and SCL_1
Figure: Node 1: the backpack's SCL_1, SDA_1, VCC and GND labels, and SDA_1 / SCL_1 at A4 and A5 of ARD1.

Why labels and not wires? The backpack's pins and the UNO's A4 / A5 both point left, with SDA above SCL on both. Any pair of wires between them must turn around, and turning around swaps their order, so two direct wires would have to cross. Labels keep the schematic clean.

The Arduino Code

The demo sketch, I2C_LCD_16x2_Demo V1.0, uses the LiquidCrystal_I2C library (version 1.1.2, "LiquidCrystal I2C" by Frank de Brabander in the Library Manager) and Arduino's Wire library. It starts with the LCD object; the address in it is only a starting value:

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

LiquidCrystal_I2C lcd(0x27, 16, 2);    // the address is replaced by the one found
byte lcdAddress = 0;                   // 0 = no LCD answered

The sketch finds the address itself. An I2C address check is an empty write: if a device answers with ACK, Wire.endTransmission() returns 0. The search tries the PCF8574T range first, then the PCF8574AT range:

bool answers(byte a) {                 // an address check: does a device ACK?
  Wire.beginTransmission(a);
  return Wire.endTransmission() == 0;
}

byte findLcd() {                       // the first PCF8574 / PCF8574A address that answers
  const byte first[2] = {0x20, 0x38};
  for (byte b = 0; b < 2; b++)
    for (byte a = first[b]; a < first[b] + 8; a++)
      if (answers(a)) return a;
  return 0;
}

setup() uses what it found: a new LiquidCrystal_I2C object with that address, then lcd.init() and lcd.backlight(). That is why the same HEX file works on both UNOs, at 0x27 and at 0x3F:

void setup() {
  Serial.begin(9600);
  Serial.println(F("I2C LCD 16x2 V1.0 demo - The Engineering Projects"));
  Wire.begin();
  lcdAddress = findLcd();
  if (lcdAddress) {
    lcd = LiquidCrystal_I2C(lcdAddress, 16, 2);
    Serial.print(F("LCD found at ")); printHex(lcdAddress); Serial.println();
  } else Serial.println(F("No I2C LCD found - check SDA (A4), SCL (A5), VCC"));
  lcd.init();                  // the HD44780 start-up over I2C (the library waits ~1 s)
  lcd.backlight();

Then come the 8 own characters (a beating heart in two frames, a smiley, a bell and four bar steps), and the start screen with the address in it. As on any HD44780, createChar() leaves the address counter in CGRAM, so the sketch calls setCursor() before it prints:

byte heartBig[8]   = {0x00, 0x0A, 0x1F, 0x1F, 0x1F, 0x0E, 0x04, 0x00};
byte heartSmall[8] = {0x00, 0x00, 0x0A, 0x0E, 0x0E, 0x04, 0x00, 0x00};
  lcd.setCursor(0, 0);         // createChar leaves the address in CGRAM
  snprintf(text1, sizeof text1, "Hello from 0x%02X", lcdAddress ? lcdAddress : 0x27);
  redraw();

SCAN lists every device on the bus, from 0x08 to 0x77, with the same address check. It is the classic I2C scanner, built into the demo:

void scan() {                          // SCAN: every device on the bus
  Serial.print(F("I2C devices:"));
  byte n = 0;
  for (byte a = 8; a < 120; a++)
    if (answers(a)) { Serial.print(' '); printHex(a); n++; }
  if (!n) Serial.print(F(" none - check SDA (A4), SCL (A5), VCC"));
  Serial.println();
}

To print a line, the sketch writes the text and pads it with spaces to 16 characters, so a shorter text never leaves old characters behind:

void printLine(byte row, const char* s) {     // the text, padded to 16
  lcd.setCursor(0, row);
  byte n = 0;
  for (; s[n] && n < 16; n++) lcd.print(s[n]);
  for (; n < 16; n++) lcd.print(' ');
}

The clock on line 2 is the up time from millis(), with the heart in the last cell:

void showClock() {
  unsigned long s = millis() / 1000;
  char buf[17];
  snprintf(buf, sizeof buf, "Up %02lu:%02lu:%02lu", s / 3600 % 100, s / 60 % 60, s % 60);
  printLine(1, buf);
  lcd.setCursor(15, 1);
  lcd.write(byte(beat ? HEART_BIG : HEART_SMALL));
}

The bar graph uses 80 steps, 16 cells of 5 columns: a full cell is the ROM's block 0xFF, a part cell one of the four bar characters:

void showBar() {
  int steps = (long)barPercent * 80 / 100;   // 16 cells x 5 columns
  lcd.setCursor(0, 1);
  for (byte c = 0; c < 16; c++) {
    int left = steps - c * 5;
    if (left >= 5) lcd.write(byte(0xFF));
    else if (left >= 1) lcd.write(byte(4 + left - 1));
    else lcd.print(' ');
  }
}

With a backpack, the backlight is not a PWM pin any more but bit P3 of the PCF8574, so the LIGHT command switches it on or off:

  } else if (!strcasecmp(line, "LIGHT")) {
    if (onOff(arg, lightOn)) {
      if (lightOn) lcd.backlight(); else lcd.noBacklight();
      Serial.print(F("OK - backlight ")); Serial.println(lightOn ? F("on") : F("off"));
    }

The loop reads the Serial Monitor, beats the heart twice a second and runs SCROLL one step every 200 ms, without delay():

  unsigned long now = millis();
  if (now - lastTick >= 500) {       // the heart beats twice a second
    lastTick = now;
    beat = !beat;
    if (line2 == SHOW_CLOCK && scrollSteps == 0) { showClock(); parkCursor(); }
  }

To change the sketch, install the library from the Library Manager, export a new HEX file and load it into both UNOs; see How to get the HEX file from Arduino. New to Arduino? Start with our Arduino Tutorial for Beginners.

Serial Monitor Commands

Commands of the demo sketch, upper or lower case (quick buttons in the full interface: U1 STATUS, TEXT1 Hello, TEP!, LIGHT OFF, LIGHT ON; U2 STATUS, BAR 75, SCAN, BLINK ON)
CommandWhat it does
HELPThe command list
STATUSThe LCD's address, line 1, line 2, the backlight, cursor and blink
SCANEvery I2C device on the bus (0x08 - 0x77)
TEXT1 <text>Line 1 (16 characters; a longer text is cut)
TEXT2 <text>Line 2 (the clock stops)
CLOCKLine 2 shows the up time with the beating heart again
BAR <0-100>Line 2 shows a bar graph (80 steps)
CHARSLine 2 shows the 8 own characters
LIGHT ON / OFFThe backlight (P3 of the backpack)
CURSOR ON / OFF, BLINK ON / OFFThe underline cursor, the blinking block cursor
SCROLLThe display slides 16 steps left and back
CLEARBoth lines empty

How to Run the Demo

  1. Press Run. Both Serial Monitors (9600 baud) and LCD2's pop-up open; move them aside, as they open over the schematic. After about a second the LCDs show "Hello from 0x27" and "Hello from 0x3F".
  2. Click the trimmer halves, the LED jumper and an address pad of LCD1, and watch the panel.
  3. On U1, type LIGHT OFF and LIGHT ON; on U2, SCAN and BAR 75.
  4. On LCD1's panel, try BACKLIGHT OFF and CONTRAST AUTO (GOOD).
  5. On LCD2's pop-up, look at the Bus log and run the four tools; bridge a pad of LCD2 and open the I2C bus tool.

I2C LCD 16x2 Proteus Simulation Results

The Serial Monitor in Its Simple Interface

Both monitors in this demo run in the Simple interface of our TEP Serial Monitor, as in every monitor screenshot here: only the log, the send box and one slim toolbar row stay visible.

  • Switch to it: right-click the log and choose Simple interface, or press Ctrl+Shift+U.
  • Back to the full view: click the corner button, press Esc or Ctrl+Shift+U, or right-click and choose Full interface.
  • Your choice is remembered.

Type a command in the send box and press Enter. Every line the demo prints is 80 characters or less. Read all about the monitor in TEP Serial Monitor Advance for Proteus.

U1: HELP and STATUS

I2C LCD 16x2 Arduino Proteus output in the TEP Serial Monitor Simple interface: the start lines with LCD found at 0x27, the HELP command list and the STATUS answer
Figure: U1 in the Simple interface: "LCD found at 0x27", the HELP list and STATUS.
I2C LCD 16x2 V1.0 demo - The Engineering Projects
LCD found at 0x27
Type HELP for the commands
HELP
Commands:
  TEXT1 <text>   line 1 (16 characters)
  TEXT2 <text>   line 2
  CLOCK          line 2 = the up time
  BAR <0-100>    line 2 = a bar graph
  CHARS          line 2 = the 8 CGRAM characters
  LIGHT ON|OFF   the backlight (the backpack's P3)
  CURSOR ON|OFF  BLINK ON|OFF
  SCROLL  CLEAR  SCAN  STATUS  HELP
STATUS
LCD at 0x27
Line 1: Hello from 0x27
Line 2: clock
Backlight on, cursor off, blink off

The second line is the address search at work: the sketch found the backpack at 0x27 by itself.

U2: SCAN, BAR and STATUS

TEP Serial Monitor Simple interface in Proteus for the I2C LCD 16x2 Advance: LCD found at 0x3F, SCAN listing the I2C devices, BAR 75 and STATUS
Figure: U2 in the Simple interface: "LCD found at 0x3F", SCAN ("I2C devices: 0x3F"), BAR 75 and STATUS.
I2C LCD 16x2 V1.0 demo - The Engineering Projects
LCD found at 0x3F
Type HELP for the commands
SCAN
I2C devices: 0x3F
BAR 75
OK - bar 75%
STATUS
LCD at 0x3F
Line 1: Hello from 0x3F
Line 2: bar 75%
Backlight on, cursor off, blink off

SCAN finds one device, the backpack at 0x3F: each UNO has its own I2C bus in this demo. Put both LCDs on one bus and SCAN lists both addresses; that is how two I2C LCDs share two wires.

Troubleshooting

  • The glass stays dark and the PWR LED is off: no power. Check VCC and GND (the panel says NO POWER with the voltage).
  • WAITING FOR I2C: nothing reached this address. Check SDA to A4 and SCL to A5 (swapped lines look the same: nothing works), and that the sketch runs.
  • WRONG ADDRESS: the sketch writes to another address. Change the address in the sketch, or the ADDRESS property or the pads; the I2C bus tool shows both addresses.
  • A row of blocks on the top line: the LCD was not initialised (lcd.init() or lcd.begin() missing; on the Advance, run the Init check).
  • Faint characters or blocks behind them: the contrast. Click the trimmer, or set CONTRAST AUTO (GOOD).
  • The backlight stays off: the LED jumper, lcd.backlight() in the sketch, or BACKLIGHT OFF on the panel.
  • The part is not simulated, or no monitor window: TEPLCD.DLL or TEPSERIALMON.DLL is missing from MODELS and the project folder; click OPEN MONITOR on a closed monitor.
  • The Advance pop-up does not appear: click OPEN PANEL on the board, or turn "Open the panel at Run" back on in Settings.

Things to Know Before Using a Real I2C LCD

The demo sketch uses only Wire, LiquidCrystal_I2C and Serial, so it runs on a real UNO and I2C LCD too. Keep in mind:

  • Turn the trimmer first: nine times out of ten, a blank I2C LCD is just the contrast. Turn the blue trimmer on the back until the blocks of the top row show, then a little back.
  • Find the address: run an I2C scanner (or the demo's SCAN). A PCF8574T board is 0x27 and a PCF8574AT board 0x3F, unless pads are bridged.
  • The LED jumper must be fitted for the backlight; some sketches also forget lcd.backlight().
  • Pull-ups: the backpack has 4.7k pull-ups on SDA and SCL. For long wires or many devices on the bus, check the total.
  • 100 kHz: the PCF8574 is rated for Wire's standard 100 kHz; 400 kHz usually works but is outside its datasheet.
  • Speed: about 1.5 ms per character, so update only what changed.

Need more room for text? See our Introduction to 20x4 LCD Module.

Limitations of the Simulation

  • Not modelled: the PCF8574's interrupt output (not wired on the backpack), I2C timing violations (any clock the AVR makes is accepted; the I2C bus tool shows when it is above 100 kHz), the 5x10 font on the glass, the A02 ROM, the slow response of the liquid crystal and the module's current draw.
  • The backpack is drawn above the LCD so you can see its parts; on a real module it sits behind the LCD.
  • The model is our own implementation, written from the HD44780 and PCF8574 datasheets. HD44780 is a Hitachi part and PCF8574 an NXP / TI part; this library is not made or endorsed by them or by Labcenter.
  • Licences: the demo sketch uses LiquidCrystal_I2C 1.1.2 and Arduino's Wire library. The HEX file contains them and the Arduino AVR core 1.8.6, which includes LGPL-licensed code; its source code is on GitHub.
  • Tested in Proteus 8.5 in October 2026 (the demo as shipped, both LCDs, the trimmer, the jumper, the pads, the panel, the pop-up, the four tools and every screenshot here; about 0.32 x real time with the pop-up and both monitors open, the same as our LCD 16x2 V3.0 demo on the same PC) and with automatic PC checks, 0 failed: 65 with the real demo sketch and LiquidCrystal_I2C talking bit by bit to the backpack model, 7,202 on the HD44780 controller, and the demo project read back from disk. Proteus 7 is not supported.

So, that was all about the I2C LCD 16x2 Library for Proteus V1.0. I hope the real I2C address, the clickable trimmer, jumper and pads, and the I2C bus tool make the I2C LCD much easier to understand, so your LCD works the first time you wire a real one. If you use the I2C LCD 16x2 Library for Proteus in a project, please share your feedback in the comments or in our forum, and if you have any questions, ask in the comments and I will help you out. Till the next tutorial, take care and have fun!


Comments

0

Join the conversation