MicroPython I2C LCD1602: hello and a counter, on an ESP32-DevKitC V4
MicroPython on an ESP32: an LCD1602 on an I2C backpack, SDA on GPIO 13 and SCL on 14, says hello and counts the seconds. The whole build, an ESP32-DevKitC V4 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.
Intermediate About 15 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $14. On Mokxi they're free.
What you'll learn
- Scanning an I2C bus to find a device's address
- How an I2C backpack drives an LCD in 4-bit mode
- Splitting a script into a main file and a helper module
- Formatting numbers into a string with %d
- Difficulty: Intermediate
- Time: About 15 minutes
- Board: ESP32-DevKitC V4
- Parts: 3
How it works
The LCD has a small I2C backpack, so it needs only four wires: power, ground, SDA on GPIO 13 and SCL on GPIO 14. The script opens the I2C bus at 400 kHz and scans it to find the display's address, usually 0x27.
A helper file, lcd1602.py, talks to the backpack. It sends every byte to the display as two 4-bit halves, because the backpack wires only four data lines.
The main loop writes Hello, world! on the top line and Counter: followed by a number on the bottom line. The number goes up by one each second and also prints to the console.
Parts list
3 parts, plus the jumper wires. They retail for about $14; on Mokxi they're free.
- 1 × ESP32-DevKitC V4
- 1 × Character LCD with I2C backpackIn Claw Machine Kit, Vending Machine Kit, Rhythm Game Kit.
- 1 × Power, 5 V
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 pin | Connects to |
|---|---|
| Character LCD with I2C backpack pin SCL | ESP32-DevKitC V4 pin 14 |
| Character LCD with I2C backpack pin SDA | ESP32-DevKitC V4 pin 13 |
| Character LCD with I2C backpack pin GND | Ground |
| Character LCD with I2C backpack pin VCC | 5 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.
# LCD1602 on I2C: SDA on GPIO 13, SCL on GPIO 14. Find the display's
# address, say hello on the top line and count on the bottom one.
from machine import I2C, Pin
from lcd1602 import LCD1602
import time
i2c = I2C(0, sda=Pin(13), scl=Pin(14), freq=400000)
found = i2c.scan()
print("I2C:", [hex(a) for a in found]) # 0x27 here; some modules are 0x3f
lcd = LCD1602(i2c, found[0])
count = 0
while True:
lcd.show("Hello, world!", "Counter: %d" % count)
print("counter", count)
count += 1
time.sleep(1)
# 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 script stops with an error right after the scan. This project
The scan found nothing. Check SDA on GPIO 13, SCL on GPIO 14, and the power and ground wires. - The LCD lights up but shows boxes or nothing. This project
Some modules sit at 0x3f instead of 0x27. The script uses the first address the scan finds, so check the contrast dial on the backpack too. - SDA and SCL swapped. This project
SDA goes to GPIO 13 and SCL to GPIO 14. - 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. - The board is not found, or upload fails with a connecting message. ESP32-DevKitC V4
Use a USB cable that carries data, not charge only. Pick the ESP32 Dev Module board. If it still fails, hold BOOT while uploading starts, then release. - Some pins never read right, or the board will not boot with a wire attached. ESP32-DevKitC V4
GPIO34 to 39 are input only and have no pull-ups. GPIO6 to 11 run the flash, so leave them alone. GPIO0, 2 and 12 are boot pins; avoid pulling them the wrong way at reset.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.