Adding a character LCD to your Raspberry Pi Pico is a popular hardware project. With the I²C interface, the setup is clean and compact, allowing you to display anything from system messages and sensor readings to menus or fun animations. This guide covers everything—from basic wiring to advanced techniques like custom characters and scroll zones.
1. What You’ll NeedRaspberry Pi Pico or Pico W running MicroPython firmware
- Raspberry Pi Pico or Pico W running MicroPython firmware
16×2 (or larger) HD44780-compatible LCD with I²C backpack (PCF8574)
- 16×2 (or larger) HD44780-compatible LCD with I²C backpack (PCF8574)
Breadboard, jumper wires
- Breadboard, jumper wires
(Optional) Logic-level shifter for 3.3 V to 5 V data compatibility
- (Optional) Logic-level shifter for 3.3 V to 5 V data compatibility
Software: Thonny IDE, library modules lcd_api.py
and pico_i2c_lcd.py
- Software: Thonny IDE, library modules
lcd_api.py
andpico_i2c_lcd.py
A standard 16×2 LCD requires 10 GPIO lines and manual enable/data toggles. The I²C "backpack" reduces it to just four wires—VCC, GND, SDA, SCL—making wiring much simpler and conserving Pico pins
3. Wiring the Hardware 🛠️Here's the typical wiring setup:
LCD Pin
Pico Pin
Notes
VCC
5 V (VBUS)
Many modules are 5 V-powered
GND
GND
Shared ground ▲
SDA
GP0 (or GP4)
Data line
SCL
GP1 (or GP5)
Clock line
Important note: Most modules need 5 V VCC. The Pico GPIO pins are 3.3 V–only. To prevent damage, use a logic-level shifter or ensure the module tolerates 3.3 V levels for SDA/SCL
4. Identifying Your I²C AddressRun a simple scan in Thonny to find your LCD’s address:
python
CopyEdit
import machine
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)
print(i2c.scan())
python
CopyEdit
import machine
i2c = machine.I2C(0, sda=machine.Pin(0), scl=machine.Pin(1), freq=400000)
print(i2c.scan())
You'll typically see 0x27
or 0x3F
appear—record this for later use
5. Installing MicroPython Driver Libraries
To simplify control, use these scripts in Pico’s flash:
lcd_api.py Contains core HD44780 command routines (e.g., clear, move cursor). Save this to your Pico’s drive.
- lcd_api.py Contains core HD44780 command routines (e.g., clear, move cursor). Save this to your Pico’s drive.
pico_i2c_lcd.py (or i2c_lcd.py) Implements the hardware interface over I²C using PCF8574 I/O expander
- pico_i2c_lcd.py (or i2c_lcd.py) Implements the hardware interface over I²C using PCF8574 I/O expander
Once saved, your Pico is fully prepared to drive the display.
6. Displaying Static TextHere's a basic “Hello World” MicroPython script:
python
CopyEdit
from machine import Pin, SoftI2C
from pico_i2c_lcd import I2cLcd
from time import sleep
I2C_ADDR = 0x27
ROWS, COLS = 2, 16
i2c = SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, ROWS, COLS)
lcd.clear()
lcd.putstr("Hello, Pico!\nLCD Demo")
sleep(5)
python
CopyEdit
from machine import Pin, SoftI2C
from pico_i2c_lcd import I2cLcd
from time import sleep
I2C_ADDR = 0x27
ROWS, COLS = 2, 16
i2c = SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000)
lcd = I2cLcd(i2c, I2C_ADDR, ROWS, COLS)
lcd.clear()
lcd.putstr("Hello, Pico!\nLCD Demo")
sleep(5)
Or, use loops to alternate text:
python
CopyEdit
while True:
lcd.clear()
lcd.putstr("Row 1 Text")
sleep(2)
lcd.clear()
lcd.move_to(0,1)
lcd.putstr("Row 2 Text")
sleep(2)
python
CopyEdit
while True:
lcd.clear()
lcd.putstr("Row 1 Text")
sleep(2)
lcd.clear()
lcd.move_to(0,1)
lcd.putstr("Row 2 Text")
sleep(2)
This mirrors the Techatronic example
7. Scrolling MessagesThe I²C LCD driver lacks built-in scroll functions, but you can implement your own:
python
CopyEdit
msg = "This message is longer than 16 chars!"
for i in range(len(msg) - COLS + 1):
lcd.clear()
lcd.putstr(msg[i:i+COLS])
sleep(0.5)
python
CopyEdit
msg = "This message is longer than 16 chars!"
for i in range(len(msg) - COLS + 1):
lcd.clear()
lcd.putstr(msg[i:i+COLS])
sleep(0.5)
This slide updates the display to show each substring in sequence—perfect for stock tickers or news feeds.
8. Custom CharactersThe HD44780 supports 8 custom characters (5×8 pixels each). Here's how to define and show them:
python
CopyEdit
heart = bytearray([0x00,0x0A,0x1F,0x1F,0x1F,0x0E,0x04,0x00])
lcd.custom_char(0, heart)
lcd.clear()
lcd.putstr("I ")
lcd.putchar(chr(0))
lcd.putstr(" Pico")
python
CopyEdit
heart = bytearray([0x00,0x0A,0x1F,0x1F,0x1F,0x0E,0x04,0x00])
lcd.custom_char(0, heart)
lcd.clear()
lcd.putstr("I ")
lcd.putchar(chr(0))
lcd.putstr(" Pico")
You can create icons, arrows, progress bars, and more
9. Practical ApplicationsWith basic text and visuals sorted, you can elevate your project:
Sensor Dashboards: Use LCD to display real‑time readings (temperature/humidity via DHT22, DS18B20)
- Sensor Dashboards: Use LCD to display real‑time readings (temperature/humidity via DHT22, DS18B20)
Menus & Navigation: Map buttons to navigate menus—selecting options, toggling settings.
- Menus & Navigation: Map buttons to navigate menus—selecting options, toggling settings.
Mini Info-Panel: Show live data like time, IP address (via Pico W), CPU stats.
- Mini Info-Panel: Show live data like time, IP address (via Pico W), CPU stats.
Status & Alerts: Indicate events or errors clearly when Pico is headless.
- Status & Alerts: Indicate events or errors clearly when Pico is headless.
Contrast is key – Adjust the onboard potentiometer for clear visibility
- Contrast is key – Adjust the onboard potentiometer for clear visibility
Check the I²C address – Address mismatches lead to unresponsive displays. Always scan first
- Check the I²C address – Address mismatches lead to unresponsive displays. Always scan first
Voltage matters – Use 5 V for VCC, and level shifting for logic lines to avoid damaging the Pico
- Voltage matters – Use 5 V for VCC, and level shifting for logic lines to avoid damaging the Pico
Backlight & power – Some modules allow control of the backlight via lcd.backlight_off()
and lcd.display_off()
commands
- Backlight & power – Some modules allow control of the backlight via
lcd.backlight_off()
andlcd.display_off()
commands
Multi-line Displays – Larger screens (20×4, 40×2) work if you set row/column parameters correctly when initializing I2cLcd(i2c, addr, rows, cols)
Multi-line Displays – Larger screens (20×4, 40×2) work if you set row/column parameters correctly when initializing I2cLcd(i2c, addr, rows, cols)
Here’s a robust script incorporating multiple techniques:
python
CopyEdit
from machine import Pin, SoftI2C
from pico_i2c_lcd import I2cLcd
from time import sleep
I2C_ADDR = SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000).scan()[0]
lcd = I2cLcd(SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000), I2C_ADDR, 2, 16)
heart = bytearray([0x00,0x0A,0x1F,0x1F,0x1F,0x0E,0x04,0x00])
lcd.custom_char(0, heart)
while True:
lcd.clear()
lcd.putstr("Temp: 23°C ")
lcd.putchar(chr(0)) # Heart at pos 15
sleep(3)
scroll_msg = "Press Btn to Start..."
for i in range(len(scroll_msg) - 15):
lcd.clear()
lcd.putstr(scroll_msg[i:i+16])
sleep(0.2)
python
CopyEdit
from machine import Pin, SoftI2C
from pico_i2c_lcd import I2cLcd
from time import sleep
I2C_ADDR = SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000).scan()[0]
lcd = I2cLcd(SoftI2C(sda=Pin(4), scl=Pin(5), freq=400000), I2C_ADDR, 2, 16)
heart = bytearray([0x00,0x0A,0x1F,0x1F,0x1F,0x0E,0x04,0x00])
lcd.custom_char(0, heart)
while True:
lcd.clear()
lcd.putstr("Temp: 23°C ")
lcd.putchar(chr(0)) # Heart at pos 15
sleep(3)
scroll_msg = "Press Btn to Start..."
for i in range(len(scroll_msg) - 15):
lcd.clear()
lcd.putstr(scroll_msg[i:i+16])
sleep(0.2)
12. Going BeyondOnce you're comfortable with basics:
Integrate time and connectivity with Pico W for IoT dashboards.
- Integrate time and connectivity with Pico W for IoT dashboards.
Add buttons or rotary encoders for interactive menus.
- Add buttons or rotary encoders for interactive menus.
Embed graphics via custom fonts, creating logos or gauges.
- Embed graphics via custom fonts, creating logos or gauges.
Explore larger displays or terminal-style interfaces for more complexity.
- Explore larger displays or terminal-style interfaces for more complexity.
Connecting a 16×2 I²C LCD to the Raspberry Pi Pico is easy—and versatile. Wiring requires just four pins; libraries abstract away the complex command set. From basic text display to custom characters, scrolling, and interactive projects, the LCD backpack unlocks countless possibilities. Be mindful of voltage compatibility, scan correctly, and adjust contrast. With creativity, you can transform the Pico into dashboards, controllers, educational tools, and more—all powered through MicroPython and a little character display.
Happy coding—and enjoy bringing your Pico to life on the LCD!
Comments