Why I Started Using This Tool

By Part 11 my proximity alarm could measure distance and shout about it through a buzzer. But every time I wanted to know the actual number, I had to open a laptop, SSH in and read the console. For a demo on a table at a STEM event, that's no good. People want to see the number.

The LCD1602 is the obvious choice: cheap, readable, 2 lines of 16 characters. The catch is that the bare screen needs around 16 wires: power, contrast, backlight, three control lines and eight data lines. Mine came with an I2C backpack already soldered on, which brings that down to four. And it was sold as a 3.3 V module, which turned out to matter more than I expected.

Same rules as the buzzer and sensor wiring posts: no C code today. We wire it, we make sure it's electrically safe for the Pi, and we prove it works from the console before writing a single line.

Missed the buzzer task? Start with Part 11: My Raspberry Pi Learned to Beep Like a Parking Sensor: Hardware PWM From C (video).

Here's the video walkthrough of this post, if you'd rather watch it step by step:

Watch on YouTube

What It Does

The backpack is a PCF8574 I/O expander. It sits on the I2C bus at address 0x27 (0x3F on some boards) and turns every byte it receives into 8 output pins: RS, RW, E, the backlight (bit 3) and the LCD's four data lines. The LCD runs in 4-bit mode, so each character is sent as two half-bytes.

The wiring:

  • GND โ†’ pin 6, VCC โ†’ 3.3 V (pin 1)
  • SDA โ†’ GPIO2 (pin 3), SCL โ†’ GPIO3 (pin 5)

Why 3.3 V and not 5 V like most tutorials? I2C lines are pulled up by resistors to the supply voltage. The Pi has its own 1.8 kฮฉ pull-ups to 3.3 V, but the backpack has its own too, to its VCC. On 5 V, the idle bus sits around 3.8 V, which is more than the Pi's GPIO pins are rated for. A 3.3 V module on 3.3 V keeps the whole bus at 3.3 V, no level shifter required.

Then I tested it without writing any code:

  1. Power on: backlight on, and after turning the blue contrast pot, a row of 16 solid blocks.
  2. I turned on the kernel's I2C driver for one session with sudo dtparam i2c_arm=on and sudo modprobe i2c-dev, then ran i2cdetect -y 1. There it was in the grid: 27.
  3. i2cset -y 1 0x27 0x00 turned the backlight off, and 0x08 turned it back on.
  4. sudo reboot cleared the driver. pinctrl get 2,3 showed both pins as plain inputs reading hi, so the pull-ups were working and nothing owned the bus.

That third step felt great. One byte from the console, and a light on a separate chip switched off. And the reboot matters because my LCD code will drive the Pi's I2C controller directly through /dev/mem, like my buzzer and sensor code, and I don't want the kernel driver fighting it for the same pins.

Step by Step

1. Recap: which pins?

Back in Part 3 we made a pin table for the whole project. The LCD rows were:

FunctionBCM GPIOPhysical pinNotes
LCD1602 I2C data (SDA)GPIO23I2C bus 1
LCD1602 I2C clock (SCL)GPIO35I2C bus 1
3.3 V powerโ€”1, 17For our 3.3 V LCD module
Groundโ€”6, 9, 14, 20, โ€ฆAny GND pin works

Handy detail: pins 1, 3 and 5 are the first three pins of the inner row (the odd-numbered row, closer to the middle of the board), at the end of the header farthest from the USB ports. Pin 1 is the one with the square solder pad. Pin 9 (GND) is just two steps further along the same row, and pin 6 (GND) is right across from pin 5. Four wires, all within one thumb's width.

Why GPIO2 and GPIO3? Because inside the BCM2837 chip, those two pins are connected to a hardware I2C controller (bus 1). The chip does all the clocking and timing for us; we just hand it bytes.

2. What's actually on this board?

Flip the module over. There are really two boards here:

PartWhat it does
The LCD16022 lines ร— 16 characters, each character a 5 ร— 8 dot grid. Driven by an HD44780-compatible controller chip (the black blob under the screen). The HD44780 is a design from the 1980s, and it's still everywhere because it just works.
The I2C backpackA small board with a PCF8574 chip: an "I/O expander". It listens on I2C and turns each byte it receives into 8 output pins, which are wired to the LCD's control and data pins.
Blue potentiometerThe little screw on the backpack. Sets the contrast. You will need to turn it.
Backlight jumperTwo pins with a jumper cap. Remove it and the backlight can never turn on. Keep it on.
A0, A1, A2 padsSolder pads that change the I2C address, so you can put several screens on one bus. Leave them alone.

The pins at the end of the backpack are labelled GND, VCC, SDA, SCL. That's all we connect.

3. I2C in plain words

I2C (say "I-squared-C" or "I-two-C"; this module calls it "IIC", same thing) is a way for chips to talk over just two wires:

  • SDA: Serial DAta. The bits travel here, one at a time.
  • SCL: Serial CLock. The Pi ticks this line, and on each tick one bit on SDA is read.

One device is the controller (our Pi). It starts every conversation. All the others are targets, and each one has its own 7-bit address, like a house number on a street. The Pi starts with "hey, number 0x27!", and only the chip with that address answers. That's how one pair of wires can serve a screen, a temperature sensor and a clock all at once.

How fast? The Pi's default is 100 kHz: 100,000 bits per second. Slow for a computer, but plenty for 32 characters on a screen.

The clever electrical trick: nobody ever drives the line high. Every chip on the bus can only pull the line down to 0 V (this is called open-drain). When nobody pulls, a pull-up resistor gently lifts the line back up to the supply voltage. That way two chips can never fight, one pushing high while the other pulls low, and burn each other out.

The Raspberry Pi already has 1.8 kฮฉ pull-up resistors to 3.3 V on GPIO2 and GPIO3, soldered on the board. Remember those; they're the key to section 5.

4. How 8 bits drive a whole screen

The PCF8574 has 8 outputs, P0 to P7. On nearly every LCD backpack they're wired like this:

PCF8574 bitLCD pinJob
P0RSRegister Select: 0 = command, 1 = character
P1RWRead/Write: we always keep it 0 (write)
P2EEnable: a short high pulse tells the LCD "read the data now"
P3Backlight1 = backlight on (through a transistor on the backpack)
P4โ€“P7D4โ€“D7The 4 data lines

Notice there are only 4 data lines, not 8. The HD44780 has a 4-bit mode: every byte is sent as two halves (nibbles), high half first, each one latched by a pulse on E. Slower, but nobody can read a screen faster than it updates anyway.

So one character costs us a few I2C bytes: "nibble + E high", "nibble + E low", twice. That's the dance we'll write in C in the next part. For today, just remember one number: bit 3 = 0x08 = backlight. We'll use it when we test the screen.

5. The 3.3 V question

Here's where the rule from Part 3 and Part 9 comes back: the Pi's GPIO pins are 3.3 V only. GPIO2 and GPIO3 are no exception.

Most LCD1602 modules are 5 V parts, and most tutorials just wire VCC to the Pi's 5 V pin. Look at what happens on the bus when you do that:

  • The backpack usually has its own pull-up resistors (often 4.7 kฮฉ) from SDA and SCL to its VCC, which is now 5 V.
  • The Pi's 1.8 kฮฉ pull-ups go to 3.3 V.
  • With both connected, an idle line settles somewhere in between:
Vidle = (5 V ร— 1.8 kฮฉ + 3.3 V ร— 4.7 kฮฉ) / (1.8 kฮฉ + 4.7 kฮฉ)
      = (9 + 15.5) / 6.5
      โ‰ˆ 3.8 V          โœ˜ above the Pi's 3.3 V

Plenty of people run like that and "it works". But it's out of spec, it's pushing current into the Pi's protection diodes the whole time the bus is idle, and we already learned in Part 9 that "it seems fine" is not the same as "it's safe".

Our module is the 3.3 V version (the "Blue Screen 3.3V" in its name). So we do the simple, clean thing: power it from the Pi's 3.3 V pin (pin 1). Now every pull-up on the bus goes to 3.3 V, and SDA and SCL can never go higher than 3.3 V. No level shifter, no math, no risk.

Got the 5 V version instead? Signs: it says 5 V on the listing, and on 3.3 V the characters are very faint or invisible even with the contrast pot turned all the way. Your safe options: (a) power it from 5 V and put a cheap I2C level-shifter board between it and the Pi, or (b) power it from 5 V and remove the backpack's SDA/SCL pull-up resistors, so only the Pi's 3.3 V pull-ups remain. (The PCF8574 still reads 3.3 V as "high" from a 5 V supply, just with less margin.) Option (a) is the beginner-friendly one.

Is the 3.3 V pin strong enough? The backlight LED is the hungry part, typically around 20 mA, plus a milliamp or two for the chips. The Pi's 3.3 V rail can spare a few hundred mA. Easy.

6. The circuit

Backpack pinConnects toWhy
GNDPi GND (pin 6)Common ground
VCCPi 3.3 V (pin 1)3.3 V module, keeps the I2C bus at 3.3 V
SDAGPIO2 (pin 3)I2C data
SCLGPIO3 (pin 5)I2C clock
          Raspberry Pi header (top-left corner)

          pin 1  (3.3 V)  โ—  โ—  pin 2  (5 V)    โ† NOT this one
          pin 3  (GPIO2)  โ—  โ—  pin 4  (5 V)    โ† NOT this one
          pin 5  (GPIO3)  โ—  โ—  pin 6  (GND)

   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
   โ”‚  LCD1602 + I2C backpack                 โ”‚
   โ”‚                                         โ”‚
   โ”‚   GND โ”€โ”€โ”€โ”€โ”€โ”€ Pi pin 6  (GND)            โ”‚
   โ”‚   VCC โ”€โ”€โ”€โ”€โ”€โ”€ Pi pin 1  (3.3 V)          โ”‚
   โ”‚   SDA โ”€โ”€โ”€โ”€โ”€โ”€ Pi pin 3  (GPIO2, SDA1)    โ”‚
   โ”‚   SCL โ”€โ”€โ”€โ”€โ”€โ”€ Pi pin 5  (GPIO3, SCL1)    โ”‚
   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Look at the row right next to our pins: pins 2 and 4 are 5 V. They're one slip of a jumper wire away. Double-check VCC lands on pin 1, the square pad.

No breadboard, no resistors, no transistor. The backpack has everything on board. Four female-to-female jumper wires go straight from the backpack to the Pi header.

7. Build it

Parts:

PartQtyNotes
LCD1602 with I2C backpack (3.3 V version)1PCF8574 backpack already soldered on
Jumper wires (female-to-female)4Use 4 different colours: black GND, red VCC, and two others for SDA/SCL

Power the Pi off first. Then:

  1. GND on the backpack โ†’ pin 6.
  2. VCC โ†’ pin 1 (3.3 V, square pad). Not pin 2, not pin 4.
  3. SDA โ†’ pin 3 (GPIO2).
  4. SCL โ†’ pin 5 (GPIO3).

Trace it with your finger: black to GND, red to 3.3 V, and SDA to SDA, SCL to SCL. I2C is not like a UART, where TX crosses to RX. Swap SDA and SCL and nothing breaks, but nothing answers either.

If your buzzer and sensor are still wired from Parts 8 and 9, leave them. The LCD uses completely different pins, and all the grounds are already common through the Pi.

8. Test it from the console, before any code

Same philosophy as every wiring post: prove the hardware with simple tools, so any bug later is in the code.

Test 1: power and contrast (no software at all).

Power the Pi on. Two things should happen straight away:

  • The backlight turns on. At power-up, the PCF8574's outputs all start high, and bit 3 is the backlight.
  • The top row shows 16 solid blocks, or nothing at all. When the HD44780 powers up without being told what to do, it shows one line of "full" characters. Whether you can see them depends on the contrast.

Take a small screwdriver and slowly turn the blue potentiometer on the back until the top row shows 16 clear rectangles on a blue background and the bottom row stays empty. On a 3.3 V module the sweet spot is often near one end of the pot's travel, so keep turning.

You see the blocks? Power, ground and contrast are all good. Turn it too far and the whole screen goes faint or every dot fills in; back off a little.

Test 2: find the chip.

To talk to the chip from the console, we'll borrow the kernel's I2C driver and the standard I2C tools. SSH in and turn the driver on, for this session only:

ssh au@au.local
sudo dtparam i2c_arm=on
sudo modprobe i2c-dev
sudo apt install i2c-tools

Now scan the bus. This knocks on every address and prints the ones that answer:

i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

There it is: 27, the most common address, for backpacks with the PCF8574T chip. If yours shows 3f instead, you have the PCF8574AT version; both are fine. Write your number down, it goes into the C code.

Test 3: blink the backlight.

Now send a real byte. Remember bit 3 = backlight:

i2cset -y 1 0x27 0x00          # all PCF8574 outputs low: backlight OFF
i2cset -y 1 0x27 0x08          # bit 3 high:              backlight ON

(Use 0x3f if that's what the scan found.)

The backlight goes off and comes back on. You just sent your first I2C bytes to a chip on a screen. The PCF8574 recognised its address, accepted the byte, set its pins, and the transistor on the backpack switched the light. Wiring, pull-ups and address: all proven.

We won't write characters this way. Initializing the HD44780 in 4-bit mode takes a dozen carefully timed nibbles, and that's a job for C. That's the next part.

Test 4: give the bus back.

sudo reboot

Log back in and look at the two pins:

pinctrl get 2,3
 2: ip    pu | hi // GPIO2 = input
 3: ip    pu | hi // GPIO3 = input

ip means plain inputs, so the driver is gone. We never saved it to config.txt, so the reboot cleared it. That matters because our C code will talk to the I2C controller directly through /dev/mem, and two owners would fight over the same pins. hi means both lines read high: the Pi's 1.8 kฮฉ pull-ups doing their job. A multimeter on pin 3 or pin 5 (black probe on GND) shows ~3.3 V, and never more.

9. When things go wrong

ProblemWhat it meansFix
No backlight, no blocksNo powerCheck VCC on pin 1 and GND on pin 6; check the backlight jumper is on
Backlight on, but no blocks at any settingContrast, or a 5 V-only moduleTurn the pot through its whole range slowly; if still nothing, it's probably a 5 V module (see section 5)
Blocks very faint, even at the best contrast5 V module running on 3.3 VUse the 3.3 V version, or 5 V + level shifter
pinctrl get 2,3 shows a0, not ipThe kernel I2C driver is on. Expected before the reboot; a problem only if it's still there afterRemove dtparam=i2c_arm=on from /boot/firmware/config.txt (or sudo raspi-config nonint do_i2c 1), reboot
pinctrl get 2,3 shows lo while idleA line is held at 0 VSDA or SCL shorted to GND: power off and check your wires
i2cdetect shows an empty gridNobody answeredSDA and SCL swapped, loose wire, or no power to the backpack
Could not open file /dev/i2c-1The driver isn't loadedRun sudo dtparam i2c_arm=on and sudo modprobe i2c-dev again; they don't survive a reboot
Permission denied from i2cdetect or i2csetYour user can't access the busPut sudo in front of the command
Works once, then failsLoose jumper on SDA/SCLPush the female connectors firmly onto the header pins

10. What's next

The screen is wired, safe, and answering at 0x27. Next we write the LCD task in C: a new thread that maps the BCM2837's I2C controller (BSC1, at physical address 0x3F804000) through /dev/mem, switches GPIO2 and GPIO3 to ALT0 so the controller drives them, sends the HD44780 its 4-bit wake-up sequence, and then copies lcdLine1 and lcdLine2 from our shared frame onto the screen. Those two 16-character fields have been sitting in frame.h since Part 6, waiting for exactly this moment.

Key Takeaways

  1. The I2C backpack turns 16 wires into 4: GND โ†’ pin 6, VCC โ†’ pin 1, SDA โ†’ GPIO2 (pin 3), SCL โ†’ GPIO3 (pin 5). SDA to SDA, SCL to SCL, no crossing.
  2. I2C is two shared wires plus an address. The Pi is the controller; the LCD backpack answers at 0x27 (or 0x3F). Lines are only ever pulled down; pull-up resistors lift them back up.
  3. Power the 3.3 V module from 3.3 V (pin 1) so every pull-up on the bus goes to 3.3 V. A 5 V module on 5 V pulls SDA/SCL to ~3.8 V: out of spec for the Pi without a level shifter.
  4. The PCF8574 turns one byte into 8 pins: RS, RW, E, backlight (bit 3 = 0x08) and D4โ€“D7. The LCD runs in 4-bit mode, so every byte is sent as two nibbles.
  5. Test before code: backlight + 16 blocks after turning the contrast pot, i2cdetect -y 1 finds 0x27, and i2cset -y 1 0x27 0x00 / 0x08 blinks the backlight. Then reboot to clear the kernel's I2C driver, because our C code will own the controller via /dev/mem: pinctrl get 2,3 should read ip โ€ฆ hi.

Final Verdict

The I2C LCD1602 is one of the best beginner displays there is: four wires, no breadboard, and a bus that can share those same two pins with other sensors later. Just pay attention to the voltage, because the most common way to wire it puts more than 3.3 V on the Pi's pins.

Gotchas I hit, so you don't have to:

  • Pins 2 and 4 are 5 V and sit right next to pins 1 and 3. Put VCC on pin 1, the square pad.
  • Turn the contrast pot. A blank screen with the backlight on is almost always contrast. On 3.3 V the sweet spot can be near the end of its range.
  • Reboot to clear the kernel's I2C driver if your code uses /dev/mem. Two owners for one controller is a fight. pinctrl get 2,3 should show ip, not a0.
  • SDA to SDA, SCL to SCL. It's not a UART, nothing crosses.
  • Check your address. 0x27 and 0x3F are both normal; use whichever one i2cdetect shows.
  • A 5 V-only module looks faint on 3.3 V. If that's what you have, use a level shifter rather than wiring the bus to 5 V.

Next up: the C thread that drives the I2C controller through /dev/mem, wakes up the HD44780 in 4-bit mode, and finally puts the live distance on the screen, in Part 13: My Raspberry Pi Finally Has a Face: Driving an LCD1602 From C With No Libraries.

Related Videos