Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

20×4 I2C LCD with NodeMCU ESP8266 — level-shifted wiring

Run a 5 V 20×4 I2C LCD from a NodeMCU ESP8266: power from VIN, SDA/SCL on D2/D1 through a BSS138 level shifter, and print with LiquidCrystal I2C.

Arduino C++Verified — core esp8266 3.1.2, LiquidCrystal I2C 1.1.2, 3 Nov 2026Compile-only

Community draft — report a mistake

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 ESP8266Only the pins used are shown · 3.3 V logic20×4 LCD + I2C backpackLV⇄HVLV⇄HVGNDVIND1D2GNDVCCSDASCL
Also needed: 4-channel BSS138 level shifter (LV = 3V3, HV = VIN 5 V, common GND).
Every connection
NodeMCU ESP826620×4 LCD + I2C backpackWireNotes
VINVCCredVIN is USB 5 V. The LCD needs 5 V for contrast; the backlight draws 20–60 mA.
GNDGNDblack
D2SDAblueVia level shifter — Shifter LV = 3V3, HV = VIN (5 V), GND shared.
D1SCLyellowVia 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.
  • VIN is 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

lcd-20x4-nodemcu.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// 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

Serial Monitor · 115200Output
LCD 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
Nothing at all on the bus

Check the shifter has both LV (3V3) and HV (VIN) connected, then run the I2C scanner.

↑↓ to move · Enter to open