STANNUM
Open the simulator

Tutorials / Output

Character LCD with I²C backpack

The classic 16×2 text display cut down to two wires by a small I²C board — text, custom characters and a backlight.

Parts in the example:
  • I²C LCD
  • LM35
The I²C LCD block as it appears on the canvas.

What it is

The HD44780 LCD shows characters, not pixels: 16 columns by 2 rows in the usual module (20×4 also exists). It normally needs six data wires; the I²C backpack — a PCF8574 expander soldered to the back — turns that into SDA and SCL. The price is an address you must get right: 0x27 on most backpacks, 0x3F on the PCF8574A variant.

In the simulator

The block shows the glass with each character drawn dot by dot, in blue-with-white or green-with-black glass, with the cursor and the backlight the code controls. Settings: the address, the size (16×2, 20×4, 16×4) and the glass color. If the display lights up but stays blank, it is the address — the same symptom as on the bench.

Pins

PinWhat it is
SDAI²C data — to a GPIO
SCLI²C clock — to a GPIO
VCCpower — to a 3V3 symbol
GNDground — to a GND symbol

Wiring

The circuit below is the example Thermometer on the I²C LCD from the lab — open it with Project → Open example… and it comes ready to run.

The wired circuit, as the lab draws it.
I²C LCD pinGoes to
SDAD21 on the board (GPIO 21)
SCLD22 on the board (GPIO 22)
VCCa 3V3 symbol
GNDa GND symbol

Settings

Double-click the block's title bar to open its card, then the Settings tab. Changes apply to the running simulation right away.

The Settings tab of the I²C LCD.
SettingAcceptsDefaultNotes
I²C address0x27 (PCF8574) · 0x3F (PCF8574A)0x27 (PCF8574)It depends on the backpack chip. If the display lights up but stays blank, it is almost always the wrong address — run an I²C scanner to find yours.
Size16 columns × 2 rows · 20 columns × 4 rows · 16 columns × 4 rows16 columns × 2 rows
Glass colorBlue with white text · Green with black textBlue with white text

Code

sketch.cpp
#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// address, columns, rows. If it lights up but writes nothing, it is almost
// always the address: modules ship as 0x27 (PCF8574) or 0x3F (PCF8574A).
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int LM35 = 32;

// The HD44780 lets you draw 8 characters of your own: 8 rows of 5 dots
// each. This one is the degree symbol.
byte grau[8] = {0b01100,0b10010,0b10010,0b01100,0b00000,0b00000,0b00000,0b00000};

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);        // SDA, SCL — any GPIOs work on the ESP32
  lcd.init();
  lcd.backlight();
  lcd.createChar(0, grau);
  lcd.setCursor(0, 0);
  lcd.print("Thermometer");
  delay(1200);
  lcd.clear();
}

void loop() {
  float t = analogRead(LM35) * 330.0 / 4095;   // 10 mV per degree

  lcd.setCursor(0, 0);
  lcd.print("Temp: ");
  lcd.print(t, 1);
  lcd.write((byte)0);        // the degree symbol I drew
  lcd.print("C   ");

  // 16-block level bar: 0 to 50 degrees
  int blocos = constrain(map((int)t, 0, 50, 0, 16), 0, 16);
  lcd.setCursor(0, 1);
  for (int i = 0; i < 16; i++) lcd.write(i < blocos ? (char)255 : ' ');

  Serial.printf("%.1f C -> %d blocks\n", t, blocos);
  delay(500);
}

The LiquidCrystal I2C library: init(), backlight(), setCursor(col, row), print(). The example also draws its own degree symbol with createChar() — the controller stores up to 8 custom 5×8 characters — and fills the second row with block characters as a level bar for the LM35 reading.

Run it

Press Build and run. The first build of a project takes a while; after that, only what changed is rebuilt.

The simulation running: the canvas reacts and the serial monitor shows what the code prints.

The first row shows the temperature with the custom degree symbol; the second row is a bar that grows as you drag the LM35's slider. Call noBacklight() and the glass darkens but the text stays — an LCD does not emit light, it only blocks it.

How the simulation models it

Try this

  1. Scroll a long message across the first row with scrollDisplayLeft() every 300 ms.
  2. Switch the size to 20×4 in Settings and in the constructor, and use the four rows for four sensors.

See also