ESP32 + OLED SSD1306 (I2C): Basic Sensor Data Display
Beginner1/8/2026- Author: IoTSpark Maker

ESP32 + OLED SSD1306 (I2C): Basic Sensor Data Display

Learn to connect a 0.96" I2C SSD1306 OLED display to an ESP32 DevKit and show real-time sensor data (temperature, humidity) using the Adafruit_SSD1306 library.

ESP32OLEDSSD1306I2CDisplay
0 steps2 components

The 0.96" 128x64 I2C OLED display (SSD1306 driver) is the most popular choice for showing sensor data on ESP32 projects thanks to its low price, low power draw (~20mA), and needing only 2 signal wires (SDA/SCL) besides power.

This article shows how to wire an ESP32 DevKit to an SSD1306 OLED over I2C and write a program that reads sensor data and displays it on-screen using the Adafruit_SSD1306 + Adafruit_GFX libraries.

Detailed guide

Connect an ESP32 DevKit to a 0.96" SSD1306 OLED over I2C, reading sensor data and displaying it in real time with Adafruit_SSD1306. Code compiles cleanly with arduino-cli.

1. Introduction

The 0.96" 128x64 I2C OLED display (SSD1306 driver) is the most popular choice for showing sensor data on ESP32 projects thanks to its low price, low power draw (~20mA), and needing only 2 signal wires (SDA/SCL) besides power. This article shows how to wire an ESP32 DevKit to an SSD1306 OLED over I2C and write a program that reads sensor data and displays it on-screen using the Adafruit_SSD1306 + Adafruit_GFX libraries.

An OLED needs no backlight (every pixel emits its own light), giving it very high contrast — well suited for showing temperature/humidity readings and WiFi/MQTT connection status on compact IoT devices.

2. Components Needed

ComponentQtyReference Price
ESP32 DevKit V4 (30/38 pin)1~75,000 - 120,000₫
OLED 0.96" 128x64 I2C (SSD1306)1~35,000 - 60,000₫
Female-Female Jumper Wires (4 wires)1 set~10,000₫

3. Wiring Diagram

Just 4 wires: 2 power + 2 I2C signal. The SSD1306 OLED works fine at both 3.3V and 5V, but it's best to power it directly from the ESP32's 3.3V so the I2C bus stays at a consistent 3.3V logic level — completely safe for the ESP32's GPIO (which can't tolerate 5V on input pins).

OLED SSD1306ESP32 DevKitNote
VCC3V33.3V supply
GNDGNDCommon ground
SCLGPIO22I2C Clock (ESP32 default)
SDAGPIO21I2C Data (ESP32 default)

Voltage safety: the SSD1306 OLED module used here has an onboard 3.3V-5V driver, but we power it directly with 3V3 to avoid any logic-level risk — no level shifter needed.

4. Full Sample Code

The program initializes I2C on the correct GPIO21/GPIO22 pins, checks whether the OLED responds at address 0x3C (with a clear log message if not found), then every 2 seconds reads sensor data (simulated here — swap in a real sensor for deployment) and redraws it on the display.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C

#define I2C_SDA 21
#define I2C_SCL 22

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

unsigned long lastRead = 0;
const unsigned long READ_INTERVAL_MS = 2000;
bool sensorOk = false;

float readTemperatureC() {
  // Khong co cam bien vat ly trong demo nay: gia lap gia tri hop le.
  // Thay bang dht.readTemperature() / bme.readTemperature() khi co cam bien that.
  float simulated = 26.5f + (float)((millis() / 2000) % 5);
  return simulated;
}

float readHumidityPct() {
  float simulated = 55.0f + (float)((millis() / 3000) % 10);
  return simulated;
}

void setup() {
  Serial.begin(115200);
  delay(200);
  Serial.println(F("[BOOT] ESP32 OLED SSD1306 demo starting..."));

  Wire.begin(I2C_SDA, I2C_SCL);

  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("[ERROR] SSD1306 khong tim thay tren I2C bus (0x3C). Kiem tra day SDA/SCL."));
    sensorOk = false;
  } else {
    sensorOk = true;
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println(F("IoTLabs Maker"));
    display.println(F("OLED SSD1306 Demo"));
    display.display();
    Serial.println(F("[OK] OLED SSD1306 khoi tao thanh cong."));
  }
}

void loop() {
  unsigned long now = millis();
  if (now - lastRead >= READ_INTERVAL_MS) {
    lastRead = now;
    float t = readTemperatureC();
    float h = readHumidityPct();

    Serial.printf("[DATA] temp=%.1fC hum=%.1f%%\n", t, h);

    if (sensorOk) {
      display.clearDisplay();
      display.setTextSize(1);
      display.setCursor(0, 0);
      display.println(F("Cam bien moi truong"));
      display.drawLine(0, 10, 128, 10, SSD1306_WHITE);

      display.setTextSize(2);
      display.setCursor(0, 16);
      display.printf("T:%.1fC", t);

      display.setCursor(0, 40);
      display.printf("H:%.0f%%", h);
      display.display();
    } else {
      Serial.println(F("[WARN] Display khong san sang, chi in Serial."));
    }
  }
}

Compile result: Sketch uses 339368 bytes (25%) of program storage space. Global variables use 23652 bytes (7%) of dynamic memory. FQBN: esp32:esp32:esp32.

5. Detailed Code Walkthrough

  • Wire.begin(I2C_SDA, I2C_SCL) — the ESP32 lets you freely reassign I2C pins (unlike fixed-pin AVR boards); here it picks the exact GPIO21/GPIO22 pair that's wired up.

  • display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS) returns false if there's no ACK response from address 0x3C on the bus — used to catch a wiring/address error right at boot instead of silently crashing later.

  • readTemperatureC()/readHumidityPct() simulate sensor data with a function that varies based on millis() — for a real deployment, replace these with real sensor-library calls (DHT22, BME280...).

  • The loop() only redraws the screen every READ_INTERVAL_MS (2 seconds) instead of every iteration — avoiding screen flicker and reducing I2C bus load.

6. Real Applications / Extensions

You can extend this project by: (1) replacing the simulated read functions with a real DHT22/BME280 sensor via the DHT sensor library or Adafruit_BMP280; (2) adding a WiFi/MQTT connection-status icon with drawBitmap(); (3) using U8g2 instead of Adafruit_SSD1306 if you need more fonts with Vietnamese diacritics.

7. Common Issues

IssueCauseFix
The display stays off, Serial prints "[ERROR] SSD1306 khong tim thay"Wrong SDA/SCL wiring or a faulty moduleRecheck all 4 wires, use an I2C scanner to confirm address 0x3C
Compile error "Adafruit_SSD1306.h: No such file"The library isn't installedarduino-cli lib install "Adafruit SSD1306"
The display shows noise/a white checkerboardWrong I2C address (0x3D instead of 0x3C), or a missing display.display() callChange SCREEN_ADDRESS to 0x3D, or check that display.display() is called after drawing

8. Summary

With just 4 wires and the Adafruit_SSD1306 library, an ESP32 can display real-time sensor data on a 0.96" I2C OLED — a foundation for most compact IoT display projects: environmental monitoring stations, smart clocks, mini dashboards.