Project 3.3: "1602 LCD Display"
🚀 Project 3.3 – 1602 LCD Display
🎯 What you’ll learn
- ✅ Goal 1: Initialize a 1602 LCD over I2C and show text
- ✅ Goal 2: Move messages across the LCD and format data
- ✅ Goal 3: Build a tiny multi‑screen interface with clear status messages
Key ideas
- Short definition: An LCD shows characters in rows and columns using I2C.
- Real‑world link: Robots use LCDs to show status like battery, mode, and sensor data.
🧱 Blocks glossary (used in this project)
- I2C setup: Connect two pins (SCL, SDA) to talk to displays and sensors.
- LCD setup: Create an LCD object to control text.
- LCD show: Print text at column/row (0–15 columns, 0–1 rows on 1602).
- Delay: Wait a short time to make changes readable.
- Variable: Store data to print (like numbers or words).
- Serial print: Log actions to your computer for debugging.
🧰 What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| LCD 1602 I2C | 1 | SCL → Pin 26, SDA → Pin 5 (I2C) |
🔌 Wiring tip: Connect LCD VCC to 5V and GND to GND. Use SCL=26 and SDA=5 as in the official reference for LCD.
📍 Pin map snapshot: Pins 26 (SCL) and 5 (SDA) are used for I2C. Keep other pins free for later projects.
✅ Before you start
- Plug in the USB and open the serial monitor.
- Test print shows:
print("Ready!") # Confirm serial is working
🎮 Microprojects (5 mini missions)
🎮 Microproject 3.3.1 – LCD initialization
Goal: Create the I2C bus and the LCD object, then show “Clu‑Bots”.
Blocks used:
- I2C setup: Make i2c_extend with SCL=26, SDA=5
- LCD setup: Create lcd with width 16, address 0x27
- LCD show: Print at row 0, centered
Block sequence:
- Setup I2C bus (SCL=26, SDA=5, 100000 Hz)
- Create LCD (width=16, addr=0x27)
- Show “Clu‑Bots” centered at row 0
MicroPython code:
# Microproject 3.3.1 – Initialize LCD and show text
import machine # Load hardware/I2C library
import i2clcd # Load LCD library (I2C 1602)
i2c_extend = machine.SoftI2C( # Create software I2C bus
scl = machine.Pin(26), # Use Pin 26 for SCL (clock)
sda = machine.Pin(5), # Use Pin 5 for SDA (data)
freq = 100000 # Set I2C speed to 100 kHz
)
lcd = i2clcd.LCD( # Create LCD controller object
i2c_extend, # Pass the I2C bus we just made
lcd_width = 16, # LCD has 16 columns per row
i2c_addr = 0x27 # Typical I2C address for 1602 adapters
)
print("[Init] LCD ready at 0x27, width=16")# Log successful setup
lcd.shows('Clu-Bots', # Show a message on the LCD
column = 0, # Start at column 0
line = 0, # Use top row (row 0)
center = True) # Center text horizontally
print("[LCD] Displayed: Clu-Bots") # Confirm on serial
Reflection: You made your robot speak on a screen—nice first step!
Challenge:
- Easy: Change the message to your name.
- Harder: Show text on row 1 (line=1) with center=True.
🎮 Microproject 3.3.2 – Static message
Goal: Display two lines: title on row 0 and status on row 1.
Blocks used:
- LCD show: Print strings at specific row/column
- Delay: Pause to read the screen
Block sequence:
- Show “Status:” at row 0, col 0
- Show “Ready” at row 1, col 0
- Wait for viewing
MicroPython code:
# Microproject 3.3.2 – Show a static 2-line message
import machine # Load hardware/I2C library
import i2clcd # Load LCD library
import time # Load time library for delays
i2c_extend = machine.SoftI2C( # Create I2C bus for LCD
scl = machine.Pin(26), # SCL on Pin 26
sda = machine.Pin(5), # SDA on Pin 5
freq = 100000 # I2C speed 100 kHz
)
lcd = i2clcd.LCD( # Create LCD object
i2c_extend, # Use our I2C bus
lcd_width = 16, # 16 columns LCD
i2c_addr = 0x27 # I2C address of LCD
)
lcd.shows('Status:', # Print title text
column = 0, # Start at first column
line = 0, # Top row
center = False) # No centering, left aligned
print("[LCD] Row0: Status:") # Serial log
lcd.shows('Ready', # Print status text
column = 0, # Start at first column
line = 1, # Second row
center = False) # Left aligned
print("[LCD] Row1: Ready") # Serial log
time.sleep_ms(1500) # Pause for a moment to read
Reflection: Two clear lines—title and status—make your UI readable.
Challenge:
- Easy: Change “Ready” to “Running…”.
- Harder: Center the title and keep the status left aligned.
🎮 Microproject 3.3.3 – Message displacement (scroll effect)
Goal: Simulate movement: clear, then print shifted messages.
Blocks used:
- LCD clear: Clean the display
- LCD show: Print at different columns
- Delay: Create animation timing
Block sequence:
- Clear screen
- Print “Hello” moving from col 0 to col 10 on row 0
- Delay between steps
MicroPython code:
# Microproject 3.3.3 – Displace text across the LCD
import machine # Load hardware/I2C library
import i2clcd # Load LCD library
import time # Load time library for delays
i2c_extend = machine.SoftI2C( # Create I2C bus
scl = machine.Pin(26), # SCL pin
sda = machine.Pin(5), # SDA pin
freq = 100000 # I2C speed
)
lcd = i2clcd.LCD( # Create LCD object
i2c_extend, # I2C bus
lcd_width = 16, # 16 columns
i2c_addr = 0x27 # LCD I2C address
)
for col in range(0, 11): # Loop through columns 0..10
lcd.clear() # Clear the entire screen
print("[LCD] Clear display") # Serial log: cleared
lcd.shows('Hello', # Show text to move
column = col, # Shifted column position
line = 0, # Top row
center = False) # Left aligned at chosen col
print("[LCD] Row0 col", col, ": Hello") # Serial log current position
time.sleep_ms(200) # Short delay for animation feel
Reflection: You created motion with simple steps—like a basic marquee.
Challenge:
- Easy: Move on row 1 instead.
- Harder: Bounce back (move right, then left).
🎮 Microproject 3.3.4 – Display variable values
Goal: Show a changing counter on the LCD with formatting.
Blocks used:
- Variable: Count up
- LCD show: Print dynamic content
- Delay: Control update speed
Block sequence:
- Start counter at 0
- Show “Count: N” on row 1
- Increase N and repeat
MicroPython code:
# Microproject 3.3.4 – Print a changing counter on LCD
import machine # Load hardware/I2C library
import i2clcd # Load LCD library
import time # Load time library
i2c_extend = machine.SoftI2C( # Create I2C bus
scl = machine.Pin(26), # SCL pin
sda = machine.Pin(5), # SDA pin
freq = 100000 # I2C speed
)
lcd = i2clcd.LCD( # Create LCD object
i2c_extend, # I2C bus
lcd_width = 16, # 16 columns width
i2c_addr = 0x27 # LCD I2C address
)
count = 0 # Initialize counter variable
print("[Init] Counter=0") # Serial log: start value
while True: # Continuous update loop
lcd.clear() # Clear screen to avoid overlap
print("[LCD] Clear") # Serial log: cleared
lcd.shows('Count:', # Show label on row 0
column = 0, # Column 0
line = 0, # Top row
center = False) # Left aligned
lcd.shows(str(count), # Show counter value (as text)
column = 7, # After "Count: "
line = 0, # Same row for one-line display
center = False) # Left aligned
print("[LCD] Count:", count) # Serial log: current value
count = count + 1 # Increase counter by 1
time.sleep_ms(500) # Update twice per second
Reflection: Numbers on screen help you monitor robot state in real time.
Challenge:
- Easy: Show the count on row 1 instead.
- Harder: Reset the counter at 100 with a friendly message.
🎮 Microproject 3.3.5 – Multi‑screen interface
Goal: Alternate between two screens: TITLE and DATA.
Blocks used:
- LCD show: Print different screens
- Delay: Time between screens
- Variable: Simple page toggle
Block sequence:
- Show Screen A (title)
- Wait, then show Screen B (data)
- Repeat
MicroPython code:
# Microproject 3.3.5 – Alternate between two LCD screens
import machine # Load hardware/I2C library
import i2clcd # Load LCD library
import time # Load time library
i2c_extend = machine.SoftI2C( # Create I2C bus
scl = machine.Pin(26), # SCL pin
sda = machine.Pin(5), # SDA pin
freq = 100000 # I2C speed
)
lcd = i2clcd.LCD( # Create LCD object
i2c_extend, # I2C bus
lcd_width = 16, # 16 columns width
i2c_addr = 0x27 # LCD I2C address
)
page = 0 # Toggle variable for pages
value = 42 # Example data value to display
print("[Init] Multi-screen ready") # Serial log: ready
while True: # Loop to alternate screens
lcd.clear() # Clear before drawing a new page
print("[LCD] Clear") # Serial log: cleared
if page == 0: # If we are on page 0
lcd.shows('Clu-Bots', # Show title text
column = 0, # Column 0
line = 0, # Top row
center = True) # Centered for a title look
lcd.shows('LCD Station', # Subtitle text
column = 0, # Column 0
line = 1, # Second row
center = True) # Center for symmetry
print("[LCD] Page 0: Title") # Serial log: title page
else: # Otherwise page 1 (data page)
lcd.shows('Value:', # Label text
column = 0, # Column 0
line = 0, # Top row
center = False) # Left aligned
lcd.shows(str(value), # Show data value
column = 7, # After "Value: "
line = 0, # Same row
center = False) # Left aligned
lcd.shows('OK', # Status indicator
column = 0, # Column 0
line = 1, # Second row
center = False) # Left aligned
print("[LCD] Page 1: Data", value) # Serial log: data page
time.sleep_ms(1000) # Hold page for 1 second
page = 1 - page # Toggle page (0→1, 1→0)
Reflection: Two pages give your robot a simple dashboard—clean and tidy.
Challenge:
- Easy: Change the hold time to 500 ms for faster switching.
- Harder: Add a third page showing “Mode: TEST”.
✨ Main project – Complete display station
🔧 Blocks steps (with glossary)
- I2C setup: Make i2c_extend with SCL=26 and SDA=5
- LCD setup: Create lcd at address 0x27, width 16
- LCD show: Print multiple lines and pages
- Variable: Toggle screens and update values
- Delay: Control readability
Block sequence:
- Setup I2C and LCD (width=16, addr=0x27)
- Show title on page 0; show data on page 1
- Toggle pages each second
- Update a counter value on page 1
🐍 MicroPython code (mirroring blocks)
# Project 3.3 – Complete Display Station
import machine # Load hardware/I2C library
import i2clcd # Load LCD library
import time # Load time library
i2c_extend = machine.SoftI2C( # Create I2C bus with pins
scl = machine.Pin(26), # SCL pin on 26 (per reference)
sda = machine.Pin(5), # SDA pin on 5 (per reference)
freq = 100000 # I2C frequency 100 kHz
)
lcd = i2clcd.LCD( # Create the LCD controller
i2c_extend, # Pass our I2C bus
lcd_width = 16, # LCD width is 16 columns
i2c_addr = 0x27 # Typical I2C address for 1602
)
print("[Init] Display station ready") # Serial log: setup success
counter = 0 # Variable to show changing data
page = 0 # Variable to toggle pages
while True: # Main loop for the display station
lcd.clear() # Clear before drawing a page
print("[LCD] Clear") # Serial log: cleared
if page == 0: # Page 0: Title/branding
lcd.shows('Clu-Bots', # Title text
column = 0, # Column 0
line = 0, # Top row
center = True) # Center the title
lcd.shows('Display v1', # Subtitle
column = 0, # Column 0
line = 1, # Bottom row
center = True) # Center subtitle
print("[LCD] Page 0 ready") # Serial log: title page
else: # Page 1: Data page
lcd.shows('Count:', # Label on row 0
column = 0, # Column 0
line = 0, # Top row
center = False) # Left aligned label
lcd.shows(str(counter), # Show counter value
column = 7, # After label
line = 0, # Same row
center = False) # Left aligned value
lcd.shows('OK', # Status indicator on row 1
column = 0, # Column 0
line = 1, # Second row
center = False) # Left aligned
print("[LCD] Page 1: counter =", counter) # Serial log: data page
counter = counter + 1 # Increase counter value
time.sleep_ms(1000) # Keep page visible for 1 second
page = 1 - page # Toggle page for next loop
📖 External explanation
- What it teaches: How to initialize an LCD, show text, animate movement, and build multi‑page displays.
- Why it works: I2C lets the board talk to the LCD with two wires; the LCD library places text at column/row.
- Key concept: Clear screen + show at column/row = simple and readable user interfaces.
✨ Story time
Your robot just got a face—this LCD is its dashboard. It can greet, report, and guide your missions like a tiny control panel.
🕵️ Debugging (2)
🐞 Debugging 3.3.A – LCD does not initialize
Problem: Nothing shows on the LCD.
Clues: Black rectangles or no text; serial shows only init messages.
Broken code:
lcd = i2clcd.LCD(i2c_extend, lcd_width=16, i2c_addr=0x3F) # Wrong address for this module
Fixed code:
lcd = i2clcd.LCD(i2c_extend, lcd_width=16, i2c_addr=0x27) # Use 0x27 per reference
Why it works: The LCD backpack must match its I2C address.
Avoid next time: Check the label or try 0x27 first (common).
🐞 Debugging 3.3.B – Corrupt characters
Problem: Random symbols or broken text appear.
Clues: Fast updates without clear or overlapping prints.
Broken code:
lcd.shows('Clu-Bots', column=10, line=0, center=False) # Prints off the screen range
Fixed code:
lcd.clear() # Clear before reprinting
lcd.shows('Clu-Bots', column=0, line=0, center=True) # Keep inside 0–15 columns
Why it works: Clearing and using valid positions prevents overlaps and overflow.
Avoid next time: Respect 0–15 columns, 0–1 rows; clear before new layouts.
✅ Final checklist
- I saw the LCD show a centered “Clu‑Bots”
- I displayed two lines clearly
- I moved text across the screen
- I showed a changing value
- I built a two‑page mini interface
📚 Extras
- 🧠 Student tip: Short messages fit best—use clear labels like “Mode” or “Count”.
- 🧑🏫 Instructor tip: Have students verify I2C pins (SCL=26, SDA=5) and address (0x27) before coding.
- 📖 Glossary:
- I2C: Two‑wire communication for displays/sensors (SCL clock, SDA data).
- Column/row: Position on the LCD (columns 0–15, rows 0–1).
- Address: The I2C number that identifies the device (e.g., 0x27).
- 💡 Mini tips:
- Clear before large layout changes.
- Center titles; left align data.
- Keep delays around 300–1000 ms for readable updates.ing, stop and f