Built-in project · Arduino Uno R3

Twenty by four on an Arduino Uno

Two 20x4 character LCDs, one on six wires and one on its I2C backpack. A long line runs off row 0 and carries on in row 2. The whole build, an Arduino Uno and 2 more parts, 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 20 minutes to build and run. Runs in your browser. Free, and no account needed.

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

The circuit runs here once the page has loaded.

What you'll learn

  • How a 20x4 LCD maps rows to controller memory
  • Driving an LCD on six wires versus two with I2C
  • Placing text with a cursor column and row
  • Why one long print wraps in an odd order
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: Arduino Uno R3
  • Parts: 4

How it works

An Uno drives two 20x4 character LCDs. The first is wired directly: RS on pin 2, E on pin 3 and D4 to D7 on pins 4 to 7. The second has an I2C backpack and needs only SDA on A4 and SCL on A5.

A 20x4 panel uses the same controller as a 16x2, which only has two lines of memory. The rows start at addresses 0x00, 0x40, 0x14 and 0x54. So when the sketch prints forty characters in a row on the first panel, the second twenty land in row 2, not row 1.

The second panel places each row by hand with setCursor and shows a seconds counter that updates in row 2.

Parts list

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

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, 20x4 pin D7Arduino Uno R3 pin 7
Character LCD, 20x4 pin D6Arduino Uno R3 pin 6
Character LCD, 20x4 pin D5Arduino Uno R3 pin 5
Character LCD, 20x4 pin D4Arduino Uno R3 pin 4
Character LCD, 20x4 pin EArduino Uno R3 pin 3
Character LCD, 20x4 pin RSArduino Uno R3 pin 2
Character LCD, 20x4 pin VDDArduino Uno R3 pin 5V
Character LCD, 20x4, with I2C backpack pin VCCArduino Uno R3 pin 5V
Character LCD, 20x4 pin VSSArduino Uno R3 pin GND
Character LCD, 20x4 pin V0Arduino Uno R3 pin GND
Character LCD, 20x4 pin RWArduino Uno R3 pin GND
Character LCD, 20x4, with I2C backpack pin GNDArduino Uno R3 pin GND
Character LCD, 20x4, with I2C backpack pin SDAArduino Uno R3 pin A4
Character LCD, 20x4, with I2C backpack pin SCLArduino Uno R3 pin A5

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// Twenty by four: two 20x4 character LCDs, one on six wires and one on two.
//
//   bare 20x4   RS -> 2   E -> 3   D4..D7 -> 4, 5, 6, 7
//               VSS, V0, RW -> GND    VDD -> 5 V
//   I2C 20x4    SDA -> A4   SCL -> A5   VCC -> 5 V   GND -> GND
//
// A 20x4 is the same HD44780 controller as a 16x2, and the controller only has
// two lines of memory, forty characters each. The panel shows each line twice
// over: row 0 is the first twenty characters of line 1 and row 2 the next
// twenty, rows 1 and 3 the same of line 2. So the rows start at 0x00, 0x40,
// 0x14 and 0x54, and setCursor() knows that.
//
// The bare panel shows what happens without it: one long print runs off the
// end of row 0 and carries on in row 2, not row 1. The I2C panel counts, with
// each row placed by setCursor().

#include "mokxi_lcd1602.h"
#include "mokxi_lcd1602_i2c.h"

Lcd1602 bare(2, 3, 4, 5, 6, 7);
Lcd1602I2c backpack(A5, A4);

void setup() {
  bare.begin();
  // Forty characters in one go: the second twenty land in row 2.
  bare.print("Row 0 runs off here ");
  bare.print("and on into row 2...");
  bare.setCursor(0, 1);
  bare.print("Row 1 is line 2");

  backpack.begin();
  backpack.setCursor(0, 0);
  backpack.print("Twenty by four");
  backpack.setCursor(0, 1);
  backpack.print("rows at 00 40 14 54");
  backpack.setCursor(0, 2);
  backpack.print("seconds:");
}

void loop() {
  backpack.setCursor(9, 2);
  backpack.print((long)(millis() / 1000));
  backpack.setCursor(0, 3);
  backpack.print("row 3 is 0x54");
  delay(250);
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Starts both screens with begin. On the bare panel it prints two 20-character strings in a row, which fill row 0 and then run on into row 2, then moves to row 1 for a separate line. On the I2C panel it uses setCursor to put a title, an address note and the word seconds on rows 0, 1 and 2.

void setup() {
  bare.begin();
  // Forty characters in one go: the second twenty land in row 2.
  bare.print("Row 0 runs off here ");
  bare.print("and on into row 2...");
  bare.setCursor(0, 1);
  bare.print("Row 1 is line 2");

  backpack.begin();
  backpack.setCursor(0, 0);
  backpack.print("Twenty by four");
  backpack.setCursor(0, 1);
  backpack.print("rows at 00 40 14 54");
  backpack.setCursor(0, 2);
  backpack.print("seconds:");
}

loop sketch.ino

Every 250 milliseconds it moves the I2C cursor to column 9 of row 2 and prints millis() / 1000, so the seconds count updates in place. It also prints a label on row 3.

void loop() {
  backpack.setCursor(9, 2);
  backpack.print((long)(millis() / 1000));
  backpack.setCursor(0, 3);
  backpack.print("row 3 is 0x54");
  delay(250);
}

Troubleshooting

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

  • Row 1 stays empty after a long print on the bare panel This project
    That is expected. The text continues in row 2. Use setCursor to place text on each row yourself.
  • The I2C panel stays blank This project
    Check SDA on A4 and SCL on A5, plus 5 V and ground on the backpack.
  • The LCD shows blocks or nothing. Character LCD
    Adjust the contrast with the potentiometer on V0. Check the wiring, power at 5 V, and that you set 20 columns and 4 rows in the LiquidCrystal begin call.
  • Text appears on the wrong line or breaks across rows. Character LCD
    Row 2 continues row 0 and row 3 continues row 1. Use setCursor(col, row) for each line, and clear old text. For an I2C backpack, check the address, often 0x27 or 0x3F.
  • The screen lights up but shows nothing, or only blocks. Character LCD
    Turn the small contrast trimmer on the backpack. Make sure the backlight jumper is on. The backpack needs 5 V power, not 3.3 V.
  • The sketch hangs or the display stays blank. Character LCD
    The PCF8574 backpack is at 0x27, but PCF8574A versions are at 0x3F. Run an I2C scanner. SDA goes to A4 and SCL to A5 on an Uno. Use LiquidCrystal_I2C with 20 columns and 4 rows.
  • Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
    Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected.
  • Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
    Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.

Change it and keep it

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