ESP32 MicroPython SSD1309 OLED Display
The ESP32 is a powerful, Wi-Fi-enabled microcontroller widely used in IoT and embedded projects. Paired with MicroPython and the DIYables OLED SSD1309 library, you can easily add a crisp monochrome OLED display to your project.
This tutorial walks you through:
- Connecting the OLED SSD1309 to an ESP32 — only 2 data pins needed (I2C).
- Displaying text and numbers.
- Drawing shapes: pixels, lines, rectangles, and bitmaps.
- Using hardware scrolling.
- Adjusting contrast and dimming the display.

Components Needed
Or you can buy the following kits:
| 1 | × | DIYables ESP32 Starter Kit (ESP32 included) | |
| 1 | × | DIYables ESP32 S3 Starter Kit (ESP32 S3 included) | |
| 1 | × | DIYables Sensor Kit (18 sensors/displays) |
Additionally, some of these links are for products from our own brand, DIYables .
The OLED SSD1309
The SSD1309 is a single-chip CMOS OLED/PLED driver with controller for organic/polymer light emitting diode dot-matrix graphic display systems. It connects to a microcontroller using the I2C (two-wire) protocol, making wiring minimal — only SDA and SCL lines are required in addition to power.
The display is self-luminous (no backlight needed), offers high contrast, a wide viewing angle, and is readable in both bright and dim environments. It is commonly available in 128×64 and 128×32 pixel variants.
Key features:
- I2C interface (default address 0x3C)
- 128×64 pixel resolution (or 128×32, 96×16, 64×48, 64×32)
- Built-in hardware scrolling in horizontal and diagonal directions
- Adjustable contrast (0–255)
- Extends framebuf.FrameBuffer — all MicroPython drawing primitives work out of the box
Pin Description
| Pin | Purpose | ESP32 Connection |
|---|---|---|
| SDA | I2C data line | Connect to GPIO21 |
| SCL | I2C clock line | Connect to GPIO22 |
| VCC | 3.3 V to 5 V power input | Connect to 3.3V |
| GND | Ground | Connect to GND |

Wiring
The ESP32 operates at 3.3 V logic, which is compatible with the SSD1309 display.
| OLED SSD1309 | ESP32 | Notes |
|---|---|---|
| SDA | GPIO21 | I2C data |
| SCL | GPIO22 | I2C clock |
| VCC | 3.3V | Power |
| GND | GND | Ground |
- How to connect ESP32 to SSD1309 OLED 128x64 display using breadboard

This image is created using Fritzing. Click to enlarge image
No breadboard? The same circuit can be wired without one, as shown below:

This image is created using Fritzing. Click to enlarge image
- How to connect ESP32 to SSD1309 OLED 128x64 display using screw terminal block breakout board

This image is created using Fritzing. Click to enlarge image
Tip: You can use any available I2C-capable GPIO pins on the ESP32. Just update the SCL_PIN and SDA_PIN values in the code.
Detailed Instructions
Here's instructions on how to set up and run your MicroPython code on the ESP32 using Thonny IDE:
- Make sure Thonny IDE is installed on your computer.
- Confirm that MicroPython firmware is loaded on your ESP32 board.
- If this is your first time using an ESP32 with MicroPython, check out the ESP32 MicroPython Getting Started guide for step-by-step instructions.
- Connect the ESP32 board to the OLED SSD1309 according to the provided diagram.
- Connect the ESP32 board to your computer with a USB cable.
- Open Thonny IDE on your computer.
- In Thonny IDE, go to Tools Options.
- Under the Interpreter tab, choose MicroPython (ESP32) from the dropdown menu.
- Make sure the correct port is selected. Thonny IDE usually detects it automatically, but you might need to select it manually (like COM3 on Windows or /dev/ttyUSB0 on Linux).
- Navigate to the Tools Manage packages on the Thonny IDE.
- Search "DIYables-MicroPython-OLED-SSD1309", then find the OLED SSD1309 library created by DIYables.
- Click on DIYables-MicroPython-OLED-SSD1309, then click Install button to install the library.

- Copy the provided MicroPython code and paste it into Thonny's editor.
- Save the code to your ESP32 by:
- Clicking the Save button or pressing Ctrl+S.
- In the save dialog, choose MicroPython device.
- Name the file main.py.
- Click the green Run button (or press F5) to execute the script.
- Observe the result — text and graphics should appear on the OLED display.
Starter Code Template
ESP32 Code — Hello World
Try It
- Wire the OLED SSD1309 to the ESP32 according to the diagram above.
- Upload the code using Thonny IDE.
- You should see "Hello, World!", "DIYables", and "SSD1309 OLED 128 x 64" displayed on screen.
Text Drawing Reference
| Method Call | What It Does | Notes |
|---|---|---|
| oled.fill(0) | Clear the entire screen (all pixels off) | Always call before drawing a fresh frame |
| oled.text("Hello!", 0, 0) | Draw text at column 0, row 0 | Built-in 8×8 pixel font; 16 chars per row, 8 rows on 64px display |
| oled.text("Line 2", 0, 16) | Draw text on the second line | Rows are 8 px tall; next line is y+8 or y+16 for spacing |
| oled.show() | Push framebuffer to the physical display | Nothing is visible until show() is called |
ESP32 Code — Draw Shapes
Try It
- Upload the code using Thonny IDE.
- The display cycles through demos: diagonal pixels, line fans, grid lines, rectangle outlines, and filled rectangles.
Drawing Methods Reference
| Method Call | What It Draws | Notes |
|---|---|---|
| oled.pixel(x, y, 1) | Single pixel at (x, y) | 0 = off, 1 = on |
| oled.line(x1, y1, x2, y2, 1) | Line between two points | Bresenham algorithm |
| oled.hline(x, y, w, 1) | Horizontal line of width w | Faster than line() for horizontal |
| oled.vline(x, y, h, 1) | Vertical line of height h | Faster than line() for vertical |
| oled.rect(x, y, w, h, 1) | Rectangle outline | Top-left corner at (x, y) |
| oled.fill_rect(x, y, w, h, 1) | Filled rectangle | Same parameters as rect() |
ESP32 Code — Scroll Text
Try It
- Upload the code using Thonny IDE.
- The display scrolls right, then left, then diagonally right, then diagonally left, and repeats.
Hardware Scroll Reference
| Method Call | Scroll Direction | Notes |
|---|---|---|
| oled.scroll_right(0x00, 0x07) | Right | Pages 0–7 = all rows on a 64 px display |
| oled.scroll_left(0x00, 0x07) | Left | Pages 0–7 covers the full screen height |
| oled.scroll_diag_right(0x00, 0x07) | Diagonal right (right + down) | Combines horizontal and vertical scrolling |
| oled.scroll_diag_left(0x00, 0x07) | Diagonal left (left + down) | Combines horizontal and vertical scrolling |
| oled.stop_scroll() | Stop scrolling | Call before drawing new content |
Note: Modify the framebuffer (e.g. call oled.fill() + oled.show()) before starting a new scroll to update the scrolling content. Always call stop_scroll() before sending new draw commands.
ESP32 Code — Contrast & Dim
Try It
- Upload the code using Thonny IDE.
- Watch the display brightness ramp up and down, then toggle between dimmed and full brightness.
Contrast & Dim Reference
| Method Call | Effect | Notes |
|---|---|---|
| oled.set_contrast(0) | Minimum brightness | Value range 0–255 |
| oled.set_contrast(128) | Medium brightness | Value is saved internally for dim/restore |
| oled.set_contrast(255) | Maximum brightness | Default after init is 0xCF (207) |
| oled.dim(True) | Drop contrast to 0 (dim) | Does not modify the saved contrast value |
| oled.dim(False) | Restore saved contrast | Restores the value set by set_contrast() |
| oled.invert(True) | Invert all pixels at hardware level | No framebuffer change |
| oled.invert(False) | Restore normal pixel polarity | — |
ESP32 Code — Bitmap
Try It
- Upload the code using Thonny IDE.
- A 16×16 heart icon appears centred on the display. It pulses by alternating normal and inverted frames.
Bitmap Drawing Reference
| Method Call | Description | Notes |
|---|---|---|
| framebuf.FrameBuffer(data, w, h, framebuf.MONO_HMSB) | Wrap raw bytes as a bitmap FrameBuffer | Use MONO_HMSB for row-major MSB-first data |
| oled.blit(icon_fb, x, y) | Copy bitmap onto display framebuffer at (x, y) | All pixels are copied (no transparency) |
| oled.blit(icon_fb, x, y, 1) | Blit with key=1 — skip pixels with value 1 | Useful for XOR-style effects on filled backgrounds |
How bitmap data is stored: Each row is stored left-to-right, most-significant bit first (MONO_HMSB). The byte count per row is ceil(width / 8). For a 16-wide image that is 2 bytes per row.
Library Reference
See DIYables MicroPython OLED SSD1309 Library Reference for the complete API documentation including all constructors, methods, and constants.
Next Steps
- Combine with a DHT11 or DHT22 sensor to display real-time temperature and humidity readings.
- Add a button to cycle between multiple display screens.
- Use framebuf.FrameBuffer.blit() to build an animated sprite display.