The blue 16x2 I2C LCD is a 5 V part; the SuperMini is a 3.3 V board. Powering the LCD from 3V3 gives a dim, low-contrast screen, and powering it from 5V puts 5 V on the I2C lines through the backpack’s pull-ups.
The clean fix is a cheap BSS138 level shifter between them: the LCD runs at 5 V, the C3 sees 3.3 V on GPIO5 (SDA) and GPIO6 (SCL). The sketch also shows the one software trap on this board — calling Wire.begin(5, 6) before lcd.init().
Wiring
| ESP32-C3 SuperMini | 16×2 LCD + I2C backpack | Wire | Notes |
|---|---|---|---|
| 5V | VCC | red | The LCD and backlight need 5 V for good contrast. |
| GND | GND | black | |
| 5 | SDA | blue | Via level shifter — BSS138 module: LV → 3V3, HV → 5V, both GNDs to GND. |
| 6 | SCL | yellow | Via level shifter — Second channel of the same shifter. |
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 has pull-up resistors to its own VCC. At 5 V that puts 5 V on SDA/SCL, and the C3’s pins are not 5 V tolerant — hence the level shifter.
lcd.init()callsWire.begin()with no pins, which on a generic C3 means GPIO8/9 (the LED and BOOT strapping pins). CallingWire.begin(5, 6)first keeps the bus on GPIO5/6 — the core just keeps the bus that is already running.- If the screen is lit but blank, turn the blue contrast pot on the backpack — at 5 V it is usually set right, at 3.3 V it rarely is.
- The backpack address is 0x27 (PCF8574T) or 0x3F (PCF8574AT). The sketch checks 0x27 and tells you if nothing answers.
Code
// 16x2 I2C LCD (PCF8574 backpack) on an ESP32-C3 SuperMini.
// VCC → 5V, GND → GND, SDA → GPIO5 and SCL → GPIO6 through a BSS138 level shifter
// (LV side 3V3, HV side 5V). The backpack pulls SDA/SCL up to 5 V; the C3's pins are 3.3 V only.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
constexpr int SDA_PIN = 5;
constexpr int SCL_PIN = 6;
LiquidCrystal_I2C lcd(0x27, 16, 2); // many backpacks are 0x3F instead
void setup() {
Serial.begin(115200);
delay(1500); // let the native USB serial port enumerate
// Start Wire on our pins FIRST. lcd.init() calls Wire.begin() again with no pins;
// the core keeps the bus that is already running, so GPIO5/6 stay in use.
Wire.begin(SDA_PIN, SCL_PIN);
Wire.beginTransmission(0x27);
if (Wire.endTransmission() != 0) Serial.println("Nothing at 0x27 — try 0x3F in the sketch");
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("ESP32-C3 Mini");
Serial.println("LCD ready");
}
void loop() {
lcd.setCursor(0, 1);
lcd.printf("Up %5lu s", millis() / 1000);
delay(1000);
}// 16x2 I2C LCD (PCF8574 backpack) on an ESP32-C3 SuperMini.
// VCC → 5V, GND → GND, SDA → GPIO5 and SCL → GPIO6 through a BSS138 level shifter
// (LV side 3V3, HV side 5V). The backpack pulls SDA/SCL up to 5 V; the C3's pins are 3.3 V only.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
constexpr int SDA_PIN = 5;
constexpr int SCL_PIN = 6;
LiquidCrystal_I2C lcd(0x27, 16, 2); // many backpacks are 0x3F instead
void setup() {
Serial.begin(115200);
delay(1500); // let the native USB serial port enumerate
// Start Wire on our pins FIRST. lcd.init() calls Wire.begin() again with no pins;
// the core keeps the bus that is already running, so GPIO5/6 stay in use.
Wire.begin(SDA_PIN, SCL_PIN);
Wire.beginTransmission(0x27);
if (Wire.endTransmission() != 0) Serial.println("Nothing at 0x27 — try 0x3F in the sketch");
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("ESP32-C3 Mini");
Serial.println("LCD ready");
}
void loop() {
lcd.setCursor(0, 1);
lcd.printf("Up %5lu s", millis() / 1000);
delay(1000);
}Library: LiquidCrystal I2C (Frank de Brabander) — compiled with LiquidCrystal I2C 1.1.2.
Expected serial output
LCD readyLCD ready