A 20×4 LCD gives a NodeMCU four full lines — enough for a tank level, two temperatures and the Wi-Fi status at once. It is a 5 V part, though, and the ESP8266 is not.
The clean way: power the LCD from VIN (5 V) and pass SDA (D2) and SCL (D1) through a small BSS138 level shifter, so the 5 V pull-ups on the backpack never reach the NodeMCU’s pins.
Wiring
| NodeMCU ESP8266 | 20×4 LCD + I2C backpack | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | VIN is USB 5 V. The LCD needs 5 V for contrast; the backlight draws 20–60 mA. |
| GND | GND | black | |
| D2 | SDA | blue | Via level shifter — Shifter LV = 3V3, HV = VIN (5 V), GND shared. |
| D1 | SCL | yellow | Via level shifter |
5 V part, 3.3 V board. The LCD needs 5 V for proper contrast, and the backpack pulls SDA/SCL up to 5 V. On ESP32 or Pico, put a BSS138 level shifter between the board and the backpack instead of wiring SDA/SCL straight to 3.3 V pins.
Board quirks
- The PCF8574 backpack has 4.7 kΩ pull-ups to its own VCC, so wired straight in it pulls the NodeMCU’s SDA/SCL to 5 V. The ESP8266 isn’t specified for 5 V on its pins; the shifter keeps the board side at 3.3 V.
- Running the LCD from 3V3 instead avoids the shifter, but a standard 5 V LCD then shows faint or no characters. Only “3.3 V” LCD modules work that way.
VINis 5 V only while the NodeMCU runs from USB (or a 5 V supply on VIN). The board’s 3.3 V regulator has no spare for the backlight — don’t power the LCD from 3V3.- I2C stays on the default D2 (SDA) and D1 (SCL). The backpack’s pull-ups must never end up on D8 (GPIO15 must be LOW at boot) or the board won’t start.
- Writing to an I2C LCD is slow (each character is several I2C transfers). Update only the line that changes, as the sketch does, rather than clearing the whole screen every second.
Code
// 20x4 character LCD with a PCF8574 I2C backpack on a NodeMCU (ESP8266).
// VCC → VIN (5 V), GND → GND, SDA → D2 and SCL → D1 through a BSS138 level shifter
// (LV = 3V3, HV = VIN). Address 0x27 (some backpacks: 0x3F).
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
Serial.begin(115200);
Serial.println();
Wire.begin(); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("FlashReady");
lcd.setCursor(0, 1);
lcd.print("NodeMCU ESP8266");
lcd.setCursor(0, 2);
lcd.print("Chip ID ");
lcd.print(ESP.getChipId(), HEX);
Serial.println("LCD ready - if it stays blank, turn the contrast trimmer");
}
void loop() {
char line[21];
snprintf(line, sizeof line, "Up %5lus Heap %5u", millis() / 1000, ESP.getFreeHeap());
lcd.setCursor(0, 3);
lcd.print(line);
Serial.println(line);
delay(1000);
}// 20x4 character LCD with a PCF8574 I2C backpack on a NodeMCU (ESP8266).
// VCC → VIN (5 V), GND → GND, SDA → D2 and SCL → D1 through a BSS138 level shifter
// (LV = 3V3, HV = VIN). Address 0x27 (some backpacks: 0x3F).
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
void setup() {
Serial.begin(115200);
Serial.println();
Wire.begin(); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("FlashReady");
lcd.setCursor(0, 1);
lcd.print("NodeMCU ESP8266");
lcd.setCursor(0, 2);
lcd.print("Chip ID ");
lcd.print(ESP.getChipId(), HEX);
Serial.println("LCD ready - if it stays blank, turn the contrast trimmer");
}
void loop() {
char line[21];
snprintf(line, sizeof line, "Up %5lus Heap %5u", millis() / 1000, ESP.getFreeHeap());
lcd.setCursor(0, 3);
lcd.print(line);
Serial.println(line);
delay(1000);
}Library: LiquidCrystal I2C (Frank de Brabander) — compiled with LiquidCrystal I2C 1.1.2.
Expected serial output
LCD ready - if it stays blank, turn the contrast trimmer
Up 0s Heap 51288
Up 1s Heap 51288
Up 2s Heap 51288LCD ready - if it stays blank, turn the contrast trimmer Up 0s Heap 51288 Up 1s Heap 51288 Up 2s Heap 51288
Common errors
Backlight on but no characters
Turn the blue contrast trimmer on the backpack. If that doesn’t help, the address may be 0x3F — see LCD shows blocks or blank.
Only the first character of each print appears
A library mismatch. See LiquidCrystal I2C prints only the first character.
Nothing at all on the bus
Check the shifter has both LV (3V3) and HV (VIN) connected, then run the I2C scanner.