OLED Troubleshooting: Scrambled, Mirrored, Shifted or Blank Displays
Monochrome OLED displays like the 0.96" SSD1306 and 1.3" SH1106 are inexpensive and widely used, but byte order mismatches, chip misidentification, and RAM exhaustion cause common display failures. Use the diagnostic table below to find your symptom and apply the tested fix.
OLED Diagnostic Table (Symptom → Cause → Fix)
| Symptom | Likely Cause | Fix |
|---|---|---|
| Image mirrored left-right | Array is XBM (LSB-first) used with Adafruit drawBitmap (MSB-first), or vice versa. |
Re-convert for Adafruit in OLED Bitmap Converter, or draw using u8g2.drawXBMP(). |
| Image scrambled / diagonal noise | Wrong canvas size specified in code, or vertical page byte order instead of horizontal raster. | Set width/height in drawBitmap(x, y, data, W, H, color) to match array exactly; export horizontal MSB-first. |
| Image shifted 2 pixels, right/left edge noisy | Display has an SH1106 driver, not SSD1306. SH1106 uses a 132x64 RAM controller with 2px padding on each side. | Switch display selection to 1.3" SH1106; use the Adafruit_SH110X library or U8g2 constructor U8G2_SH1106_128X64_NONAME_F_HW_I2C. |
| Screen stays blank (backlight off) | Incorrect I2C address, missing display.display(), or floating I2C lines without pull-ups. |
Run an I2C scanner sketch. Most modules are 0x3C, but some boards default to 0x3D. Ensure display.display() is called. |
| "SSD1306 allocation failed" / Crashes on boot | Arduino Uno/Nano SRAM exhaustion (1,024 bytes buffer out of 2,048 bytes total RAM). | Switch to U8g2 page mode (_1_ or _2_ buffer), or upgrade to an ESP32 or ESP8266 with ample RAM. |
1. Mirrored Image: Adafruit MSB-First vs U8g2 LSB-First (XBM)
The most common question makers ask is: "Why is my bitmap drawing backwards or mirrored?"
Both libraries represent pixels where 1 = lit (white) and 0 = off (black). However, they unpack the 8 bits of each byte in opposite directions:
- Adafruit GFX
drawBitmap()expects MSB-first (Most Significant Bit first). Bit 7 is the leftmost pixel, bit 0 is the rightmost pixel. - U8g2
drawXBMP()expects LSB-first (Least Significant Bit first), following the standard X11 XBM bitmap format. Bit 0 is the leftmost pixel, bit 7 is the rightmost pixel.
// Example: Adafruit drawBitmap expects horizontal MSB-first:
// Left pixel is 0x80 (bit 7)
display.drawBitmap(0, 0, my_bitmap, 128, 64, SSD1306_WHITE);
display.display();
// Example: U8g2 drawXBMP expects horizontal LSB-first (XBM):
// Left pixel is 0x01 (bit 0)
u8g2.drawXBMP(0, 0, 128, 64, my_xbm_bitmap);
If you paste an array from an older tool like image2cpp configured for Adafruit into U8g2, the picture will render horizontally reversed. OLED Bitmap Converter provides a 1-click preset for both Adafruit and U8g2 so the bit order is mathematically generated to match your target library.
2. Scrambled Static or Diagonal Skew: Byte Padding & Dimensions
When an image appears as broken diagonal streaks or fuzzy white noise, verify your row byte calculation.
Monochrome graphics libraries pad every single row to full whole bytes. The formula for bytes per row is:
bytes_per_row = (width + 7) / 8;
total_bytes = bytes_per_row * height;
For a standard 128x64 display: (128 + 7) / 8 = 16 bytes per row × 64 rows = 1,024 bytes. But if your icon is 20 pixels wide, (20 + 7) / 8 = 3 bytes per row (24 bits, with 4 unused bits padding the right side of each line). If you tell drawBitmap that the image is 24 pixels wide instead of 20, the entire coordinate grid shifts by 4 bits each row, creating a sheared diagonal distortion.
3. The 2-Pixel Shift: SH1106 vs SSD1306 Misidentification
Many low-cost 1.3-inch OLED displays sold online as "SSD1306" actually contain the SH1106 controller.
While the physical glass has 128x64 pixels, the internal controller in the SH1106 has a RAM buffer of 132x64 pixels. The 128 visible pixels are centered starting at column 2 (columns 2 through 129).
If you run standard SSD1306 code on an SH1106:
- The entire screen image is shifted 2 pixels to the left.
- A 2-pixel vertical band of random flickering noise appears on the right edge.
The Solution: If using Adafruit libraries, install and use Adafruit_SH110X. If using U8g2, simply change your constructor from U8G2_SSD1306 to U8G2_SH1106_128X64_NONAME_F_HW_I2C.
4. Screen Stays Blank: I2C Scanner & Initialization
If the display remains completely dark when powering on:
- Verify I2C Address: Most 0.96" modules use address
0x3C. However, some variants tie the address pin high to default to0x3D. Run a simple Arduino I2C scanner sketch to confirm your board acknowledges the display. - Check SDA / SCL Pins:
- Arduino Uno / Nano: SDA is
A4, SCL isA5. - ESP32 (default): SDA is
GPIO 21, SCL isGPIO 22. - ESP8266 (Wemos D1 / NodeMCU): SDA is
D2 (GPIO 4), SCL isD1 (GPIO 5). - Raspberry Pi Pico (RP2040): Default Wire is SDA
GP4, SCLGP5.
- Arduino Uno / Nano: SDA is
- Did you call
display.display()? In Adafruit GFX, drawing commands only modify the local RAM buffer. Nothing appears on the physical glass untildisplay.display()sends the buffer over I2C!
5. "SSD1306 allocation failed" on Arduino Uno / Nano
The ATmega328P microcontroller has only 2,048 bytes of SRAM. The Adafruit SSD1306 library allocates a full 128 × 64 / 8 = 1,024-byte framebuffer in RAM when you call display.begin().
This consumes 50% of your total RAM before your sketch executes a single line of user code. If you have string literals, sensor libraries, or serial buffers, malloc() fails silently and returns false.
The Fix: Switch to the U8g2 library in page-buffer mode (_1_ or _2_):
// U8g2 Page-Buffer Mode uses only 128 or 256 bytes of RAM!
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
void setup() {
u8g2.begin();
}
void loop() {
u8g2.firstPage();
do {
u8g2.drawXBMP(0, 0, 128, 64, logo_bitmap);
} while (u8g2.nextPage());
delay(1000);
}
Frequently Asked Questions
How do I test if my display is SSD1306 or SH1106?
Upload a sketch with Adafruit SSD1306 that fills a border around the screen (display.drawRect(0, 0, 128, 64, SSD1306_WHITE)). If the left border is clipped and the right edge shows 2 columns of sparkling static, your module is an SH1106.
Why does my animation drop frames or run sluggishly?
Default Arduino I2C communication runs at standard 100 kHz. Calling Wire.setClock(400000); in your setup() increases the I2C bus clock to 400 kHz Fast-Mode, cutting frame transmission time from ~100ms down to ~25ms.
Ready to generate clean, verified code?
Convert your images and GIFs with zero guess-work. Our converter automatically selects the exact byte order, padding, and constructor for your board and display.