Built-in project · Raspberry Pi Pico W

Pico W I2C LCD1602 on GP6 and GP7

MicroPython on a Pico W: an I2C LCD1602 on I2C1 (SDA GP6, SCL GP7) at 0x27 shows two messages in turn, driven by a small lcd1602.py. The whole build, a Raspberry Pi Pico W and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Beginner About 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $10. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Wiring an I2C device to a Pico W's SDA and SCL pins
  • Scanning the I2C bus to see which addresses answer
  • Putting display code in a helper module
  • Writing two lines of text to a 16x2 display
  • Difficulty: Beginner
  • Time: About 15 minutes
  • Board: Raspberry Pi Pico W
  • Parts: 3

How it works

The LCD's I2C backpack needs only four wires: power, ground, SDA on GP6 and SCL on GP7. These two pins are the Pico W's second I2C bus, I2C1. The script opens it at 400 kHz and prints the addresses it finds.

A helper file, lcd1602.py, does the talking. It starts the display at the default address 0x27 and sends every byte as two 4-bit halves.

The main loop shows Hello, Kepler! over Pico W is here, waits 2 seconds, then shows MicroPython over on an LCD1602, waits 2 more and repeats.

Parts list

3 parts, plus the jumper wires. They retail for about $10; on Mokxi they're free.

Kits that can build it

These 2 starter kits have every part in the circuit. Each kit's page lists what else it can build.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Character LCD with I2C backpack pin SDARaspberry Pi Pico W pin GP6
Character LCD with I2C backpack pin SCLRaspberry Pi Pico W pin GP7
Character LCD with I2C backpack pin GNDGround
Character LCD with I2C backpack pin VCC5 V

The code

The MicroPython code exactly as the editor opens it. Change a line there and press Run: the board runs it on the official MicroPython firmware, with nothing to compile.

main.py · Raspberry Pi Pico W
# Liquid crystal display: an LCD1602 on I2C1, SDA on GP6 and SCL on GP7.
# lcd1602.py, in its own tab, does the talking.
from machine import I2C, Pin
from lcd1602 import LCD1602
import time

i2c = I2C(1, sda=Pin(6), scl=Pin(7), freq=400000)
print("I2C:", [hex(a) for a in i2c.scan()])
lcd = LCD1602(i2c)

while True:
    lcd.show("Hello, Kepler!", "Pico W is here")
    print("message 1")
    time.sleep(2)
    lcd.show("MicroPython", "on an LCD1602")
    print("message 2")
    time.sleep(2)
lcd1602.py · Raspberry Pi Pico W
# lcd1602.py: a 16x2 character LCD on a PCF8574 I2C backpack.
# The backpack's P0 is RS, P2 is E, P3 is the backlight and P4 to P7 are
# the display's D4 to D7, so every byte is sent as two 4-bit halves.
import time

class LCD1602:
    def __init__(self, i2c, addr=0x27):
        self.i2c = i2c
        self.addr = addr
        self.light = 0x08
        time.sleep_ms(50)
        for half in (0x30, 0x30, 0x30, 0x20):   # wake up, then 4-bit mode
            self._half(half, 0)
            time.sleep_ms(5)
        self.command(0x28)                      # two lines, 5x8 dots
        self.command(0x0C)                      # display on, no cursor
        self.command(0x06)                      # move right after a character
        self.clear()

    def _send(self, bits):
        self.i2c.writeto(self.addr, bytes([bits | self.light]))

    def _half(self, bits, rs):
        self._send(bits | rs | 0x04)            # E high...
        self._send(bits | rs)                   # ...and low: the LCD reads it

    def _byte(self, value, rs):
        self._half(value & 0xF0, rs)
        self._half((value << 4) & 0xF0, rs)

    def command(self, value):
        self._byte(value, 0)
        if value < 4:
            time.sleep_ms(2)                    # clear and home are slow

    def clear(self):
        self.command(0x01)

    def move_to(self, col, row):
        self.command(0x80 | (col + 0x40 * row))

    def write(self, text):
        for ch in text:
            self._byte(ord(ch), 1)

    def show(self, line1, line2=""):
        self.move_to(0, 0)
        self.write("%-16s" % line1[:16])
        self.move_to(0, 1)
        self.write("%-16s" % line2[:16])

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The screen is lit but empty. This project
    Turn the contrast dial on the backpack. Also check that SDA is on GP6 and SCL on GP7.
  • The scan prints an empty list. This project
    Check power and ground, and make sure SDA and SCL are not swapped.
  • Your module has a different address. This project
    The script uses 0x27. Some backpacks use 0x3f, so pass that address to LCD1602.
  • The screen stays blank or shows only a row of blocks. Character LCD with I2C backpack
    The I2C address is usually 0x27 or 0x3F. Run an I2C scanner to find yours. Then turn the contrast trimmer on the backpack slowly until text appears.
  • The backlight is on but there is no text, or text is garbled. Character LCD with I2C backpack
    Wire SDA and SCL to the right pins (A4 and A5 on an Uno), power it from 5 V, and call lcd.init() and the backlight call in setup.
  • Pins are powered from 5 V, or a 5 V signal goes into a GPIO. The RP2040 pins are 3.3 V only and are not 5 V tolerant. Raspberry Pi Pico W
    Power the board from VBUS or VSYS at 5 V, but keep GPIO signals at 3.3 V. Use a level shifter or a divider for 5 V parts.
  • WiFi code fails to connect, or the on-board LED code does nothing. The LED on the Pico W is not on GP25 like on the plain Pico. Raspberry Pi Pico W
    Use the board's LED_BUILTIN name so the library drives it through the WiFi chip. Join a 2.4 GHz network only, with the right SSID and password, and wait for the link before sending.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.