The SSD1306 is a tiny $0.96''$ OLED display with $128 \times 64$ pixels. Despite its small size, it produces sharp, high-contrast text and graphics because each pixel emits its own light -- there is no backlight. It communicates over I2C using just two data pins, making it one of the easiest displays to get started with. Once you learn to use it, you can display sensor readings, status messages, simple graphs, and more on a screen that draws very little power.
This guide uses the ESP32-WROOM-32 DevKit. Pin labels and GPIO numbers may differ on your board -- always check your board's pinout diagram.
Alternatives: For large, readable text in bright environments, an I2C LCD may be a better fit. See our display comparison guide for help choosing.
🔗Key Specs
| Parameter | Value |
|---|---|
| Display size | $0.96''$ diagonal |
| Resolution | $128 \times 64$ pixels |
| Interface | I2C (also available in SPI versions) |
| I2C address | 0x3C (some modules use 0x3D) |
| Supply voltage | $3.3\,\text{V}$ (some modules accept $5\,\text{V}$ via onboard regulator) |
| Current draw | $\approx 20\,\text{mA}$ (depends on pixels lit) |
| Colors | Monochrome (white, blue, or yellow/blue split) |
🔗What You'll Need
| Component | Qty | Notes | Buy |
|---|---|---|---|
| ESP32 dev board | 1 | AliExpress | Amazon.de .co.uk .com | |
| SSD1306 OLED display (0.96") | 1 | AliExpress | Amazon.de .co.uk .com | |
| Breadboard | 1 | AliExpress | Amazon.de .co.uk .com | |
| Jumper wires | 4 | AliExpress | Amazon.de .co.uk .com |
Links marked Amazon/AliExpress are affiliate links. We may earn a small commission at no extra cost to you.
🔗Wiring
The I2C version of the SSD1306 has four pins.
| SSD1306 Pin | ESP32 Pin | Notes |
|---|---|---|
| VCC | 3.3V | Check your module -- some need 5V |
| GND | GND | |
| SDA | GPIO 21 | Default I2C data line on ESP32 |
| SCL | GPIO 22 | Default I2C clock line on ESP32 |
Tip: The ESP32's default I2C pins are GPIO 21 (SDA) and GPIO 22 (SCL). Most tutorials and libraries assume these pins. You can remap I2C to other pins, but the defaults are recommended for beginners.
🔗Required Libraries
Install these two libraries through the Arduino Library Manager:
- Adafruit SSD1306 -- display driver for the SSD1306 chip
- Adafruit GFX Library -- graphics primitives (text, shapes, bitmaps)
To install:
- Go to Sketch > Include Library > Manage Libraries...
- Search for Adafruit SSD1306 and install it
- When prompted, also install Adafruit GFX Library and its dependencies
🔗Code Example: Display Text
This sketch displays a greeting and a counter on the OLED.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // No reset pin on most modules
#define OLED_ADDRESS 0x3C // Common I2C address
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int counter = 0;
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("SSD1306 allocation failed!");
while (true); // Halt if display not found
}
display.clearDisplay();
display.setTextSize(1); // Normal size (6x8 pixels per character)
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("IoT With ESP");
display.println("OLED Display Ready");
display.display(); // Push buffer to screen
delay(2000);
}
void loop() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("IoT With ESP");
display.setTextSize(2); // Double size for emphasis
display.setCursor(0, 20);
display.print("Count: ");
display.println(counter);
display.display();
counter++;
delay(500);
}🔗Code Example: Draw Shapes
The Adafruit GFX library provides functions for drawing basic shapes.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("SSD1306 allocation failed!");
while (true);
}
display.clearDisplay();
// Draw a rectangle outline
display.drawRect(0, 0, 128, 64, SSD1306_WHITE);
// Draw a filled circle
display.fillCircle(64, 32, 15, SSD1306_WHITE);
// Draw a line
display.drawLine(0, 0, 128, 64, SSD1306_WHITE);
// Draw a triangle
display.drawTriangle(100, 10, 90, 50, 120, 50, SSD1306_WHITE);
display.display();
}
void loop() {
// Nothing to update
}🔗Code Example: Display Sensor Data
A practical example that reads a value and displays it. This simulates a temperature reading, but you could easily replace it with a real sensor.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println("SSD1306 allocation failed!");
while (true);
}
}
void displayReading(float temperature, float humidity) {
display.clearDisplay();
// Header
display.setTextSize(1);
display.setCursor(0, 0);
display.println("--- Sensor Data ---");
// Temperature (large text)
display.setTextSize(2);
display.setCursor(0, 16);
display.print(temperature, 1);
display.setTextSize(1);
display.print(" ");
display.println("C");
// Humidity
display.setTextSize(2);
display.setCursor(0, 40);
display.print(humidity, 1);
display.setTextSize(1);
display.print(" ");
display.println("%RH");
display.display();
}
void loop() {
// Replace these with actual sensor readings
float temp = 23.5 + random(-10, 10) / 10.0;
float hum = 55.0 + random(-20, 20) / 10.0;
displayReading(temp, hum);
delay(2000);
}🔗How It Works
The SSD1306 OLED uses I2C (Inter-Integrated Circuit) to communicate with the ESP32. I2C is a two-wire protocol:
- SDA (Serial Data) carries the data
- SCL (Serial Clock) provides the timing
The ESP32 acts as the I2C master and sends commands and pixel data to the display. The Adafruit SSD1306 library maintains a frame buffer in the ESP32's memory -- a copy of every pixel on the screen. When you call drawing functions like println() or drawCircle(), they modify this buffer in RAM. The pixels only update on the physical display when you call display.display(), which transfers the entire buffer over I2C.
This buffer-based approach means you can compose a full frame (clear, draw text, draw shapes) and then send it all at once, avoiding flicker.
🔗Display Memory
The $128 \times 64$ pixel display requires:
$$\frac{128 \times 64}{8} = 1024\,\text{bytes}$$
of buffer RAM (one bit per pixel). The Adafruit library allocates this buffer automatically.
🔗Troubleshooting
| Problem | Possible Cause | Solution |
|---|---|---|
| Blank screen | Wrong I2C address | Try 0x3D instead of 0x3C. Run an I2C scanner sketch (see below). |
| Blank screen | SDA/SCL swapped | Double-check that SDA is on GPIO 21 and SCL on GPIO 22 |
| Display shows garbage | Wrong screen dimensions in code | Verify SCREEN_WIDTH and SCREEN_HEIGHT match your module ($128 \times 64$ or $128 \times 32$) |
| "SSD1306 allocation failed" | Not enough memory or wrong address | Check wiring and I2C address |
| Display is very dim | Most pixels are off (by design) | OLED brightness is per-pixel. More lit pixels = brighter appearance. This is normal. |
| Text appears cut off | Cursor position out of bounds | Keep coordinates within $0$--$127$ (x) and $0$--$63$ (y) |
🔗I2C Scanner
If you are unsure of your display's address, upload this sketch. It scans the I2C bus and reports all detected devices.
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("Scanning I2C bus...");
for (byte address = 1; address < 127; address++) {
Wire.beginTransmission(address);
if (Wire.endTransmission() == 0) {
Serial.print("Device found at 0x");
Serial.println(address, HEX);
}
}
Serial.println("Scan complete.");
}
void loop() {}