A 16x2 LCD with an I2C backpack needs only two signal wires, which suits the NodeMCU’s small number of free pins. The complication is voltage: the LCD wants 5 V for good contrast, and the ESP8266 is a 3.3 V chip.
So the LCD is powered from VIN, and a small BSS138 level shifter sits between D2/D1 and the backpack’s SDA/SCL. The sketch prints an uptime clock and a custom degree symbol, ready for a temperature display.
Wiring
| NodeMCU ESP8266 | 16×2 LCD + I2C backpack | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | 5 V from USB — the LCD needs it for contrast. |
| GND | GND | black | |
| D2 | SDA | blue | Via level shifter — D2 is GPIO4 (SDA). |
| D1 | SCL | yellow | Via level shifter — D1 is GPIO5 (SCL). Shifter LV = 3V3, HV = VIN (5 V), GND shared. |
5 V (3.3 V versions exist) part, 3.3 V board. Most of these LCDs need 5 V for good contrast, and the backpack pulls SDA/SCL up to its own VCC. On ESP32 or Pico, put a BSS138 level shifter between the board and the backpack instead of wiring SDA/SCL directly.
Board quirks
- The PCF8574 backpack pulls SDA and SCL up to its own VCC — 5 V here. ESP8266 pins are not specified for 5 V, so the level shifter keeps them at 3.3 V. (Wiring straight across often “works”; it is still outside the chip’s ratings.)
- Run at 3.3 V instead and most 5 V LCDs show faint or no characters even with the contrast pot turned fully. Buy a 3.3 V LCD if you want to skip the shifter.
lcd.init()callsWire.begin()for you, which on the ESP8266 means SDA = D2 and SCL = D1. To use other pins, callWire.begin(sda, scl)beforelcd.init()— but not D0, which can’t do I2C, and not D8, which the pull-ups would hold high at boot.- Arduino IDE warns that LiquidCrystal I2C “claims to run on avr architecture”. It is only a warning; the library works on the ESP8266.
Code
// 16x2 LCD with PCF8574 I2C backpack on a NodeMCU (ESP8266).
// VCC → VIN (5 V), GND → GND, SDA → level shifter → D2 (GPIO4), SCL → level shifter → D1 (GPIO5).
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // 0x3F on backpacks with the PCF8574A chip
byte degree[8] = {0b00110, 0b01001, 0b01001, 0b00110, 0b00000, 0b00000, 0b00000, 0b00000};
void setup() {
Serial.begin(115200);
Serial.println();
lcd.init(); // calls Wire.begin(): SDA = D2, SCL = D1 on the ESP8266
lcd.backlight();
lcd.createChar(0, degree);
lcd.setCursor(0, 0);
lcd.print("NodeMCU + LCD");
Serial.println("LCD at 0x27 - if the screen stays blank, turn the blue contrast pot");
}
void loop() {
unsigned long s = millis() / 1000;
char line[17];
snprintf(line, sizeof line, "Up %02lu:%02lu:%02lu", s / 3600, (s / 60) % 60, s % 60);
lcd.setCursor(0, 1);
lcd.print(line);
lcd.print(' ');
lcd.write(byte(0)); // custom degree symbol, just to show createChar works
Serial.println(line);
delay(1000);
}// 16x2 LCD with PCF8574 I2C backpack on a NodeMCU (ESP8266).
// VCC → VIN (5 V), GND → GND, SDA → level shifter → D2 (GPIO4), SCL → level shifter → D1 (GPIO5).
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // 0x3F on backpacks with the PCF8574A chip
byte degree[8] = {0b00110, 0b01001, 0b01001, 0b00110, 0b00000, 0b00000, 0b00000, 0b00000};
void setup() {
Serial.begin(115200);
Serial.println();
lcd.init(); // calls Wire.begin(): SDA = D2, SCL = D1 on the ESP8266
lcd.backlight();
lcd.createChar(0, degree);
lcd.setCursor(0, 0);
lcd.print("NodeMCU + LCD");
Serial.println("LCD at 0x27 - if the screen stays blank, turn the blue contrast pot");
}
void loop() {
unsigned long s = millis() / 1000;
char line[17];
snprintf(line, sizeof line, "Up %02lu:%02lu:%02lu", s / 3600, (s / 60) % 60, s % 60);
lcd.setCursor(0, 1);
lcd.print(line);
lcd.print(' ');
lcd.write(byte(0)); // custom degree symbol, just to show createChar works
Serial.println(line);
delay(1000);
}Library: LiquidCrystal I2C (Frank de Brabander) — compiled with LiquidCrystal I2C 1.1.2.
Expected serial output
LCD at 0x27 - if the screen stays blank, turn the blue contrast pot
Up 00:00:00
Up 00:00:01
Up 00:00:02LCD at 0x27 - if the screen stays blank, turn the blue contrast pot Up 00:00:00 Up 00:00:01 Up 00:00:02
Common errors
Backlight on, but only blocks or nothing
Turn the blue contrast pot on the backpack. If that doesn’t help, the address is 0x3F, not 0x27. Full fix.
Only the first letter of each word appears
A library version mismatch. Full fix.
“library claims to run on avr architecture” warning
Harmless. Why it appears.