MicroPython: Interfacing 0.96″ OLED Display with ESP32

Organic Light-Emitting Diode (OLED) is a technology that employs LEDs to produce light that is produced by organic molecules. These organic LEDs are utilized to make what is widely regarded as the best display panels in the world.

This tutorial will show you how to use a 0.96′′ or 1.3′′ OLED display with an ESP32 and MicroPython code. With the ESP32 Module, we’ll use an SSD1306 or SSD1315 I2C OLED Display. We’ll use the uPyCraft IDE to develop Micropython code and upload the firmware to the ESP32 board. On the OLED Screen, we will display messages such as “Hello World” or any Simple Text.

Before going with this one, you should read Getting Started with MicroPython on ESP32 Using PyCraft IDE.

Hardware Required

  • ESP32 Board-ESP32 ESP-32S Development Board (ESP-WROOM-32)
  • 0.96″ I2C OLED Display
  • Jumper Wires
  • Breadboard

Alternatively, you can get the MakePython ESP32 Board from Makerfabs for a lower price, as seen in the image below. MicroPython is supported by the board, which has an ESP32 chip and a 1.3′′ I2C OLED display.

To learn more about this board and purchase it online, you can visit this link: MakePython ESP32 Board.

What is an OLED?

OLED stands for Organic Light Emitting Diode, and it is made up of individual image elements called pixels comprises of organic chemical compounds that emit light when an electric current is supplied between their anode and cathode. Light is emitted through the recombination of electrons and holes from the cathode and anode in this process, which is similar to that of a normal LED.


SSD1306 I2C OLED Display

The OLED module depicted in the above image is one of the most widely used on the market. There are several various versions of this module on the market, with differing resolutions, connection protocols, and pixel colors.

SSD1306 IC, a driver IC for 128×64 Dot Matrix OLED segments, is used to power these OLED modules. The SSD1306 has its own controller and can communicate via both SPI and I2C protocols. As a result, there are a variety of OLED modules on the market, some of which only support SPI communication, some that only support I2C communication, and still others that support both I2C and SPI communication. (Each module has a different number of pins.)

Since the driver, IC provides 128×64 resolution, other variations, such as 128×32, have a lower resolution. Colors such as blue, yellow, and white are supported by different modules. Some modules also allow for the use of numerous colors. To find out which colors are supported by your display module, look at the specifications.

We’re utilizing a 128×64 OLED module with 4-pin I2C connectivity, identical to the one in the image above.

 Pin Description

OLED I2C Module Pin Description
  • VCC: This is the power pin for the module. A supply of 3.3V or 5V can be provided to this pin to power the display.
  • GND: This is the ground pin for the module.
  • SCL and SDA: These are the serial clock and serial data pins for I2C communication.

Circuit Diagram

If you are using “MakePython ESP32 Board“, there is no connection needed at all. The ESP32 Board’s GPIO5(SCL) and GPIO4(SDA) are connected to the 1.3′′ I2C OLED.

If you’re using an Espressif ESP32 Board with an external OLED Display, connect them as shown in the image below. The OLED display’s SDA and SCL pins are wired to ESP32 GPIO21 and GPIO22, respectively.

MicroPython Code for Interfacing OLED Display with ESP32

The MicroPython Code for Interfacing OLED Display with ESP32 has two-part.

1. SSD1306 Library

2. Main Code

The library required to write to the OLED display is not included in the standard MicroPython library. To begin, we must first download the library to the ESP32 board.

SSD1306/SSD1315 OLED Library

# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
import time
import framebuf
import sys

import pyb
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)
class SSD1306:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
self.framebuf = framebuf.FrameBuffer(self.buffer, self.width, self.height, framebuf.MVLSB)
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height – 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# timing and driving scheme
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def contrast(self, contrast):
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width – 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(self.pages – 1)
def fill(self, col):
def pixel(self, x, y, col):
self.framebuf.pixel(x, y, col)
def scroll(self, dx, dy):
self.framebuf.scroll(dx, dy)
def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col)
def hline(self, x, y, w, col):
self.framebuf.hline(x, y, w, col)
def vline(self, x, y, h, col):
self.framebuf.vline(x, y, h, col)
def line(self, x1, y1, x2, y2, col):
self.framebuf.line(x1, y1, x2, y2, col)
def rect(self, x, y, w, h, col):
self.framebuf.rect(x, y, w, h, col)
def fill_rect(self, x, y, w, h, col):
self.framebuf.fill_rect(x, y, w, h, col)
def blit(self, fbuf, x, y):
self.framebuf.blit(fbuf, x, y)

class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
self.i2c.writeto(self.addr, self.temp)
elif currentBoard==”pyboard”:

def write_data(self, buf):
self.temp[0] = self.addr << 1
self.temp[1] = 0x40 # Co=0, D/C#=1
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
elif currentBoard==”pyboard”:
def poweron(self):

class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
elif currentBoard==”pyboard”:
self.spi.init(mode = pyb.SPI.MASTER,baudrate=self.rate, polarity=0, phase=0)
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
elif currentBoard==”pyboard”:
def write_data(self, buf):
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
elif currentBoard==”pyboard”:
self.spi.init(mode = pyb.SPI.MASTER,baudrate=self.rate, polarity=0, phase=0)
global currentBoard
if currentBoard==”esp8266″ or currentBoard==”esp32″:
elif currentBoard==”pyboard”:
def poweron(self):
  1. Create a new file in the uPyCraft IDE.
  1.  Paste the code above into the IDE window and save it as
  1. Choose the “Download & Run” option. The file will be uploaded to the ESP32 board.

4. The following message should appear in the uPyCraft terminal window.

You can now utilize the SSD1306 Library on the Code Part after importing it.

Main Code

You now upload the main code after downloading the file to the ESP32 board.

from machine import Pin, I2C
import ssd1306
from time import sleep

# ESP32 Pin assignment
i2c = I2C(-1, scl=Pin(5), sda=Pin(4))

oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

oled.text(‘Welcome’, 0, 0)
oled.text(‘OLED Display’, 0, 10)
oled.text(‘how2electronics’, 0, 20)
oled.text(‘Makerfabs’, 0, 30)

1. Open the uPyCraft IDE and create a new tab called

Copy the code from the previous section and upload it to the ESP32 board using the “Download & Run” button.

After Console windows show the above message, you can now check the OLED Display screen for the message stated in the code.

Code Explanation

from machine import Pin, I2C

This line imports the Pin and I2C classes from the machine module, allowing data to be delivered to the OLED Display using I2C communication.

import ssd1306

This imports the OLED Library that was previously uploaded to the Board.

i2c = I2C(-1, scl=Pin(5), sda=Pin(4))

The I2C Pins of the ESP32 Board are defined here. GPIO22 (SCL) and GPIO21 (SDA) are the ESP32’s default I2C pins (SDA). The GPIO Pins on the MakePython ESP32 Board are GPIO5 (SCL) and GPIO4 (SDA)

oled_width = 128oled_height = 64

This line defines the OLED height and width

oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)


Then we create the OLED SSD1306 I2C object. This object accepts the OLED width, height, and I2C pins that were previously defined.

oled.text(‘Welcome’, 0, 0)oled.text(‘OLED Display’, 0, 10)

We initialize the OLED display with OLED and write text with the text() function.

Using this line we call the show() method to update the OLED.


I hope you have understood how to interface 0.96″ OLED with ESP32 MicroPython. We MATHA ELECTRONICS will be back soon with more informative blogs.

Leave a Reply

Your email address will not be published.