Learn

Draw on an SSD1306 OLED with an Arduino

  • 192parts on the bench
  • 25boards running now
  • 1.00xreal time, on every board
Arduino Uno: SSD1306 OLEDlive0.000 s 0.00x
Click to open it in the editor
The oled example: a 128x64 SSD1306 on A4 and A5, drawing text, shapes and a bouncing ball.

The 0.96 inch SSD1306 OLED is the small screen in every hobby kit: 128 by 64 pixels, four pins, crisp white on black. The circuit above is one on an Arduino Uno, drawing a title, a few shapes, a counter and a ball bouncing in a box. It is running on a simulated Uno, and the display is decoded from the actual I2C traffic on the two wires, bit by bit, rather than being drawn from a shortcut.

Open it in the editor to change the drawing code and run it again. The build takes a second or two, in the browser.

What you need

  • An Arduino Uno
  • A 128x64 SSD1306 OLED module, I2C version
  • Four jumper wires

Wiring

Part and pin
Goes to
Note
OLED SDA
A4
OLED SCL
A5
OLED VCC
5 V
Most modules accept 3.3 V or 5 V
OLED GND
GND

The Adafruit code

The Adafruit SSD1306 tutorial sketch compiles here as it stands, on every board except the ATtiny85. The headers are Mokxi’s own, written on its display driver, and keep the calls tutorials use: clearDisplay(), display(), drawPixel(), lines, rectangles, rounded rectangles, circles and triangles, bitmaps, setTextSize(), setCursor(), print(), setRotation() and invertDisplay().

Two differences from the upstream library are worth knowing before you start. The font is Mokxi’s own 5 by 7, with capitals, digits and punctuation, so lower case prints as capitals. And begin() leaves the panel blank where the upstream library draws its logo.

Hello on an SSD1306, with the Adafruit library calls
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);

void setup() {
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) for (;;);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.println("Hello, world!");
  display.drawRect(0, 20, 40, 20, SSD1306_WHITE);
  display.display();
}

void loop() {}

Why nothing appears until display()

The panel cannot be read back. So a driver that wants to change one pixel has to remember the other 1023 bytes of the frame, and it keeps that copy in the board’s own memory. Every drawing call writes into that copy; display() is what sends it to the panel. Forget display() and you have drawn a perfect picture into RAM that nobody will ever see.

That copy is 1 KB, which is half the Uno’s entire 2 KB of SRAM. It is the real cost of a pixel display on an ATmega328P, and the reason big sketches with an OLED run out of memory. The built-in example also shows a way to go faster: it sends only the 8-column blocks of the screen that changed, so a moving ball is a few dozen bytes on the bus rather than a full kilobyte. Over the Uno’s software I2C a full 1 KB frame takes about a third of a second, which is the difference between two frames a second and the twenty-five this sketch manages.

Try it in the editor

Open the circuit in the editor and select the display. Change its address from 0x3C to 0x3D and run it again. The serial monitor says that no OLED answered, and the panel stays dark, which is the most common first fault with these modules.

Then replace the example with the Adafruit sketch above, add a few more drawing calls, a circle, a filled triangle, a line of large text with setTextSize(2), and run it. Each build takes a moment in the browser and then the panel shows exactly what the code drew.

For a moving picture, redraw inside loop() and call display() once per frame. The example’s bouncing ball shows the trick of erasing only what moved.

Common mistakes

The wrong address. Most modules answer at 0x3C; some are strapped to 0x3D. Nothing appears at the wrong one, and an I2C scanner sketch tells you which is right.

Sending the display-on command without the charge pump. The panel needs about 7.5 volts, made by a pump inside the chip that starts off. A hand-written driver that sends display-on and forgets to start the pump gets a black screen. The simulated panel models the pump, so this fault happens here too. The library handles it for you.

Running out of memory on an Uno. With the frame buffer taking 1 KB, a sketch with big arrays or long strings in RAM can crash in strange ways. Keep constant strings in flash.

Expecting hardware scrolling. The panel’s scroll commands are accepted and ignored by the simulated display, so a scrolling banner stands still here. Scroll by redrawing instead.

Questions

What is the I2C address of an SSD1306 OLED?

Usually 0x3C, sometimes 0x3D, set by a jumper or resistor on the back of the module. The simulated one has the address as a property.

Can I use an SSD1306 OLED with an ESP32?

Yes, with the same library calls on the ESP32’s own I2C pins. There are built-in ESP32 examples with an OLED, including this same text and shapes demo and a paddle game on an ESP32-C3.

Why is my OLED text all capitals?

Because the simulated library uses Mokxi’s 5 by 7 font, which has capitals, digits and punctuation only. On real hardware the Adafruit font has lower case.

Is the display in the simulator a picture or the real protocol?

The real protocol. The part is an I2C device decoded from the pin edges, with the command set the common libraries send and its own 1 KB of display memory.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.