SSD1306 OLED over i2c: wiring, code, fixes
The SSD1306 is everyone's first display: four wires, a mature library on every platform, and three famous ways to get a blank screen. Here's the working path and every fix.
Wiring
SSD1306 wiring: four wires and one scan
VCC, GND, SDA, SCL — that's it. Most modules run at 3.3 V or 5 V (check yours), and the i2c version needs no reset pin. Before any code, scan the bus: you should see 0x3C (sometimes 0x3D). If the silkscreen on the back says 0x78, don't panic — that's the same address in 8-bit notation, and your library still wants 0x3C.
Arduino
SSD1306 Arduino code: a minimal working example
Install Adafruit SSD1306 (it pulls in Adafruit GFX) from the Library Manager, then:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1); // width, height, bus, no reset pin
void setup() {
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for (;;); // begin() failed — wrong address or wiring
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Hello I2C");
display.display(); // nothing appears without this line
}
void loop() {} The rule that catches everyone: all drawing goes into a buffer — text, lines, bitmaps — and only display.display() pushes it to the glass. If your sketch "runs but shows nothing," it's this. For animation, add Wire.setClock(400000); after display.begin() — a full frame drops from ~100 ms to ~25 ms.
MicroPython
SSD1306 on the Pico: the MicroPython version
Copy ssd1306.py (from the micropython-lib drivers) onto the board, then:
from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=400_000)
oled = SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("Hello I2C", 0, 0)
oled.text("0x3C reporting", 0, 16)
oled.show() # same rule: buffer until show() Same buffered model, same fix — oled.show() is the MicroPython display(). (Bus setup details in the MicroPython tutorial.)
When it misbehaves
Three classic SSD1306 failures
Blank screen: missing display()/show() call, wrong address (try 0x3D), or power. A scan plus one grep for display.display() solves nearly all of them.
Half the screen / doubled lines: height mismatch — you declared 64 on a 128×32 panel or vice versa. The constructor numbers must match the glass.
Shifted image with a noise column: your board is an SH1106 clone sold as SSD1306 — its RAM is 132 px wide, so an SSD1306 driver reads it 2–4 pixels off. Switch to an SH1106 driver or u8g2, which auto-handles both. Extremely common on the cheapest modules.
Bus-level weirdness beyond those — intermittent init, corruption when other devices talk — is ordinary i2c debugging: the checklist and pull-up guide apply to displays exactly as to sensors, and the SSD1306 coexists happily with them on non-conflicting addresses.
Get one
Questions
SSD1306 FAQ
Is my OLED's address 0x3C or 0x78?
Both — they're the same address in two notations. The silkscreen on many modules says 0x78, which is the 8-bit write form; libraries want the 7-bit form, 0x3C (0x78 shifted right once). If the back says 0x7A, use 0x3D. When in doubt, a bus scan settles it.
My display initialises but shows garbage or is shifted sideways. Why?
Two classic causes. If the top or bottom half is missing or doubled, you declared the wrong height (128×32 vs 128×64). If everything is shifted with a column of noise at one edge, your 'SSD1306' is actually an SH1106 clone — its RAM is 132 pixels wide. Use an SH1106 driver or the u8g2 library, which supports both.
Why is the screen blank even though the code runs?
Nine times out of ten: nothing called display.display() (Arduino) or oled.show() (MicroPython). Both libraries draw into a RAM buffer and only push it to the panel on that call. The remaining cases are the wrong address (0x3C vs 0x3D) or a power issue — verify with a scanner first.
Can I run the SSD1306 from 5 V?
The controller itself is a 3.3 V part, but most breakout modules include a regulator and are happy with 3.3–5 V on VCC — check your specific module. Logic levels from a 5 V Arduino generally work in practice on these modules, but a 3.3 V host is the by-the-book pairing.
How fast can it refresh over i2c?
A full 128×64 frame is 1 KB; at 400 kHz Fast-mode that's roughly 25–30 ms per push — about 30 fps if you redraw everything, faster if you don't. Set Wire.setClock(400000) for smooth animation; at the default 100 kHz a full-frame update takes ~100 ms.