A small OLED is the easiest way to give a NodeMCU a screen, and it only needs two signal wires. Wire SCL to D1 and SDA to D2, power it from 3V3, and the default Wire.begin() finds it with no pin numbers in the code.
The ESP8266 does I2C in software, so the core can put it on almost any pin — but D1/D2 are the defaults, they don’t affect booting, and every OLED example for this board assumes them.
Wiring
| NodeMCU ESP8266 | SSD1306 OLED 0.96″ (128×64, I2C) | Wire | Notes |
|---|---|---|---|
| 3V3 | VCC | red | Power from 3V3 so the module’s I2C pull-ups go to 3.3 V, not 5 V. |
| GND | GND | black | |
| D1 | SCL | yellow | D1 is GPIO5 — the ESP8266 core’s default SCL. |
| D2 | SDA | blue | D2 is GPIO4 — the default SDA. |
3.3–5 V (on-board regulator) part, 3.3 V board. Most modules have a regulator and run from 3.3 V or 5 V. Their I2C pull-ups go to VCC, so on ESP32 and Pico power the OLED from 3.3 V to keep SDA/SCL at a safe level.
Board quirks
- On the ESP8266
Wire.begin()means SDA = D2 (GPIO4), SCL = D1 (GPIO5). It is easy to swap them because D1 comes first on the header but carries SCL. - The ESP8266 has no I2C hardware — the core bit-bangs it. Any two pins can be I2C with
Wire.begin(sda, scl), except D0 (GPIO16), which can’t do open-drain I2C. Keep off D3, D4 and D8 too: the OLED’s pull-ups would hold D8 (GPIO15) high at reset and the board would not boot. - The library allocates a 1 KB frame buffer at
begin(). That is fine on the ESP8266 (about 50 KB of free heap with Wi-Fi off), and the sketch prints the free heap so you can see it. - The “0x78” printed on the back is the 8-bit form of address 0x3C. If nothing is found, try 0x3D, which some modules use when the address resistor is moved.
Code
// 0.96" SSD1306 OLED (128x64, I2C) on a NodeMCU (ESP8266).
// VCC → 3V3, GND → GND, SCL → D1 (GPIO5), SDA → D2 (GPIO4).
// Wire.begin() with no arguments uses exactly these pins on the ESP8266 core.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const uint8_t OLED_ADDR = 0x3C; // the "0x78" printed on the back is the same address shifted left
Adafruit_SSD1306 display(128, 64, &Wire, -1); // -1: the 4-pin module has no reset pin
void setup() {
Serial.begin(115200);
Serial.println();
Wire.begin(); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("SSD1306 not found at 0x3C - check SDA/SCL and try 0x3D");
while (true) delay(100); // delay() feeds the ESP8266 watchdog
}
Serial.println("SSD1306 found at 0x3C");
Serial.printf("Free heap after display buffer: %u bytes\n", ESP.getFreeHeap());
display.setTextColor(SSD1306_WHITE);
}
void loop() {
unsigned long s = millis() / 1000;
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("NodeMCU ESP8266");
display.println("SDA D2 SCL D1");
display.setTextSize(2);
display.setCursor(0, 32);
display.printf("%lus", s);
display.display();
Serial.printf("Uptime %lus\n", s);
delay(1000);
}// 0.96" SSD1306 OLED (128x64, I2C) on a NodeMCU (ESP8266).
// VCC → 3V3, GND → GND, SCL → D1 (GPIO5), SDA → D2 (GPIO4).
// Wire.begin() with no arguments uses exactly these pins on the ESP8266 core.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const uint8_t OLED_ADDR = 0x3C; // the "0x78" printed on the back is the same address shifted left
Adafruit_SSD1306 display(128, 64, &Wire, -1); // -1: the 4-pin module has no reset pin
void setup() {
Serial.begin(115200);
Serial.println();
Wire.begin(); // SDA = D2 (GPIO4), SCL = D1 (GPIO5)
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("SSD1306 not found at 0x3C - check SDA/SCL and try 0x3D");
while (true) delay(100); // delay() feeds the ESP8266 watchdog
}
Serial.println("SSD1306 found at 0x3C");
Serial.printf("Free heap after display buffer: %u bytes\n", ESP.getFreeHeap());
display.setTextColor(SSD1306_WHITE);
}
void loop() {
unsigned long s = millis() / 1000;
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("NodeMCU ESP8266");
display.println("SDA D2 SCL D1");
display.setTextSize(2);
display.setCursor(0, 32);
display.printf("%lus", s);
display.display();
Serial.printf("Uptime %lus\n", s);
delay(1000);
}Library: Adafruit SSD1306 (Adafruit (also install Adafruit GFX Library)) — compiled with Adafruit SSD1306 2.5.17.
Expected serial output
SSD1306 found at 0x3C
Free heap after display buffer: 49712 bytes
Uptime 0s
Uptime 1s
Uptime 2sSSD1306 found at 0x3C Free heap after display buffer: 49712 bytes Uptime 0s Uptime 1s Uptime 2s
Common errors
“SSD1306 not found at 0x3C”
SDA and SCL swapped (SDA is D2, SCL is D1), a loose jumper, or a 0x3D module. Run the I2C scanner to see what answers.
Screen shows random noise or only the top part
You have a 128×32 module. Change Adafruit_SSD1306 display(128, 64, …) to (128, 32, …).
Board resets in a loop after wiring the OLED
The display is wired to D3, D4 or D8. Its pull-ups change the boot levels — move SDA/SCL back to D2/D1.