MicroPython: OLED Display with ESP32 and ESP8266
In this guide, you’ll learn how to use the 0.96 inch SSD1306 OLED display with an ESP32 or ESP8266 using MicroPython firmware. As an example, we’ll show you how to display a simple ‘Hello, World!’ message. Later, we’ll also show you how to use other useful functions to interact with the OLED display.

You might also like reading our dedicated guides for ESP with OLED display using Arduino IDE:
Prerequisites
To follow this tutorial you need MicroPython firmware installed in your ESP32 or ESP8266 boards. You also need an IDE to write and upload the code to your board. We suggest using Thonny IDE or uPyCraft IDE:
- Thonny IDE:
- uPyCraft IDE:
- Getting Started with uPyCraft IDE
- Install uPyCraft IDE (Windows, Mac OS X, Linux)
- Flash/Upload MicroPython Firmware to ESP32 and ESP8266
Learn more about MicroPython: MicroPython Programming with ESP32 and ESP8266 eBook
Introducing the OLED display
In this guide we’ll use the0.96 inch SSD1306 OLED display that is 128×64 pixels and uses I2C communication protocol.

I2C communication
For the I2C OLED display, these are the connections you need to make:
| OLED | ESP32 | ESP8266 |
| Vin | 3.3V | 3.3V |
| GND | GND | GND |
| SCL | GPIO 22 | GPIO 5 (D1) |
| SDA | GPIO 21 | GPIO 4 (D2) |
Parts Required
Here’s a list of parts you need for this project:
You can use the preceding links or go directly toMakerAdvisor.com/tools to find all the parts for your projects at the best price!
Schematic – ESP32
Follow the next schematic diagram if you’re using an ESP32 board:
Recommended reading:ESP32 Pinout Reference Guide
Schematic – ESP8266
Follow the next schematic diagram if you’re using an ESP8266 board:
Recommended reading:ESP8266 Pinout Reference Guide
SSD1306 OLED Library
The library to write to the OLED display isn’t part of the standard MicroPython library by default. So, you need to upload the library to your ESP32/ESP8266 board.
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces created by Adafruitimport timeimport framebuf# register definitionsSET_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 # Note the subclass must initialize self.framebuf to a framebuffer. # This is necessary because the underlying data buffer is different # between I2C and SPI implementations (I2C needs an extra byte). self.poweron() self.init_display() def init_display(self): for cmd in ( SET_DISP | 0x00, # off # address setting SET_MEM_ADDR, 0x00, # horizontal # resolution and layout SET_DISP_START_LINE | 0x00, 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_DISP_OFFSET, 0x00, SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12, # timing and driving scheme SET_DISP_CLK_DIV, 0x80, 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 self.write_cmd(cmd) self.fill(0) self.show() def poweroff(self): self.write_cmd(SET_DISP | 0x00) def contrast(self, contrast): self.write_cmd(SET_CONTRAST) self.write_cmd(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(SET_COL_ADDR) self.write_cmd(x0) self.write_cmd(x1) self.write_cmd(SET_PAGE_ADDR) self.write_cmd(0) self.write_cmd(self.pages - 1) self.write_framebuf() def fill(self, col): self.framebuf.fill(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)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) # Add an extra byte to the data buffer to hold an I2C data/command byte # to use hardware-compatible I2C transactions. A memoryview of the # buffer is used to mask this byte from the framebuffer operations # (without a major memory hit as memoryview doesn't copy to a separate # buffer). self.buffer = bytearray(((height // 8) * width) + 1) self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1 self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.temp[0] = 0x80 # Co=1, D/C#=0 self.temp[1] = cmd self.i2c.writeto(self.addr, self.temp) def write_framebuf(self): # Blast out the frame buffer using a single I2C transaction to support # hardware I2C interfaces. self.i2c.writeto(self.addr, self.buffer) def poweron(self): passclass 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 self.buffer = bytearray((height // 8) * width) self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs.high() self.dc.low() self.cs.low() self.spi.write(bytearray([cmd])) self.cs.high() def write_framebuf(self): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs.high() self.dc.high() self.cs.low() self.spi.write(self.buffer) self.cs.high() def poweron(self): self.res.high() time.sleep_ms(1) self.res.low() time.sleep_ms(10) self.res.high()Follow the next set of instructions for the IDE you’re using:
- A. Upload OLED library withuPyCraft IDE
- B. Upload OLED library withThonny IDE
A. Upload OLED library with uPyCraft IDE
This section shows how to upload a library using uPyCraft IDE. If you’re using Thonny IDE, read the next section.
1. Create a new file by pressing theNew File button.

2. Copy the OLED library code into that file. TheOLED library code can be found here.
Note: the SSD1306 OLED display library was built by Adafruit and will no longer
be updated. At the moment, it works fine. However, we’ll update this guide if we
find a similar library that works as well as this one.
3. After copying the code, save the file by pressing theSavebutton.

4. Call this new file “ssd1306.py” and pressok.

5. Click theDownload and Run button.

The file should be saved on the device folder with the name “ssd1306.py” as
highlighted in the following figure.

Now, you can use the library functionalities in your code by importing the library.
B. Upload OLED library with Thonny IDE
If you’re using Thonny IDE, follow the next steps:
1. Create a new file in Thonny IDE and copy the library code. TheOLED library code can be found here.
2. Go toFile>Saveas and selectMicroPython device.
3. Name the filessd1306.py and clickOKto save the file on the ESP Filesystem.
And that’s it. The library was uploaded to your board. Now, you can use the library functionalities in your code by importing the library.
Code
After uploading the library to the ESP32 or ESP8266, copy the following code to the
main.py file. It simply prints the ‘Hello, World!‘ message three times in the display.
# Complete project details at https://RandomNerdTutorials.com/micropython-programming-with-esp32-and-esp8266/from machine import Pin, SoftI2Cimport ssd1306from time import sleep# ESP32 Pin assignment i2c = SoftI2C(scl=Pin(22), sda=Pin(21))# ESP8266 Pin assignment#i2c = SoftI2C(scl=Pin(5), sda=Pin(4))oled_width = 128oled_height = 64oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)oled.text('Hello, World 1!', 0, 0)oled.text('Hello, World 2!', 0, 10)oled.text('Hello, World 3!', 0, 20) oled.show()How the Code Works
Start by importing the necessary modules to interact with the GPIOs and send data to the OLED via I2C communication. You need to import thePin andSoftI2C classes from themachine module.
from machine import Pin, SoftI2CYou also need to import the OLED library that you previously uploaded to the board asssd1306.py file.
import ssd1306The ESP32 default I2C pins areGPIO 22 (SCL) andGPIO 21 (SDA). The ESP8266 default I2C pins areGPIO 5 (SLC) andGPIO 4 (SDA).
Use the following line if you’re using an ESP32 board:
# ESP32 Pin assignment i2c = SoftI2C(scl=Pin(22), sda=Pin(21))Comment the previous line and uncomment the following if you’re using an ESP8266 board:
#ESP8266 Pin assignmenti2c = SoftI2C(scl=Pin(5), sda=Pin(4))Define the OLED width and height on the following variables:
oled_width = 128oled_height = 64After that, create anSSD1306_I2C object calledoled. This object accepts the OLED width, height, and the I2C pins you’ve defined earlier.
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)After initializing the OLED display, you just need to use thetext() function on theoled object to write text. After thetext() function, you need to call theshow() method to update the OLED.
oled.text('Hello, World 1!', 0, 0)oled.text('Hello, World 2!', 0, 10)oled.text('Hello, World 3!', 0, 20)oled.show()Thetext() method accepts the following arguments (in this order):
- Message: must be of type String
- X position: where the text starts
- Y position: where the text is displayed vertically
- Text color: it can be either black or white. The default color is white and this parameter is optional.
- 0 = black
- 1 = white
For example, the following line writes the ‘Hello, World 1!’ message in white color. The text starts on x = 0 and y = 0.
oled.text('Hello, World 1!', 0, 0)The next line of code writes the text on the next line (y =10).
oled.text('Hello, World 2!', 0, 10)Finally, for the changes to take effect, use theshow() method on theoled object.
oled.show()Demonstration
Upload the code to your board. Your OLED display should look as follows:

Other OLED functions
The library provides other methods to interact with the OLED display.
Fill the screen
To fill the entire screen with white, use thefill() function as follows:
oled.fill(1)oled.show()
To clear the screen use thefill() method as pass0 as argument. (Sets all pixels to black):
oled.fill(0)oled.show()Draw a pixel
To draw a pixel, use thepixel() method, followed by theshow() method. Thepixel() method accepts the following arguments:
- X coordinate: pixel location horizontally
- Y coordinate: pixel location vertically
- Pixel color: can be black or white
- 0 = black
- 1 = white
For example, to draw a white pixel on the top left corner:
oled.pixel(0, 0, 1)oled.show()
Invert colors
You can also invert the OLED colors: white with black and vice versa, using theinvert() method:
oled.invert(True)
To get back to the original colors, use:
oled.invert(False)Displaying data from sensors
Thetext() function only accepts variables of type String as a message. Sensor readings are usually stored in int or float variables.
If you want to display sensor readings and they are stored in int or float variables, they should be converted to a String. To convert the data to a string you can use thestr() function:
temperature = 12.34temperature_string = str(temperature)Then, you can display thetemperature_string variable on the OLED using thetext() andshow() methods:
oled.text(temperature_string, 0, 0)oled.show()Wrapping Up
This quick guide showed you how to use the OLED basic functionalities: write text and draw pixels with the ESP32 and ESP8266 using MicroPython. Now, you can use the OLED in your own projects to display messages, or sensor readings.
We have other MicroPython tutorials that you might also like:
- MicroPython: ESP32/ESP8266 with DHT11/DHT22
- MicroPython: ESP32/ESP8266 with DHT11/DHT22 Web Server
- MicroPython: WS2812B Addressable RGB LEDs with ESP32 and ESP8266
If you want to learn more about programming the ESP32 and ESP8266 boards with MicroPython, take a look our eBook: MicroPython Programming with ESP32 and ESP8266.

Recommended Resources
Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.
Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.
Arduino Step-by-Step Projects »Build 25 Arduino projects with our course, even with no prior experience!
What to Read Next…
Enjoyed this project? Stay updated by subscribing our newsletter!
50 thoughts on “MicroPython: OLED Display with ESP32 and ESP8266”
Thanks a lot for the great tutorial. Everything worked for me right away!
I still have a question:
Is it possible to use a different font with the given driver “ssd1306.py”?
ReplyThanks for the answer.
I have found an alternative solution that works quite well.A Python 3 utility to convert fonts to Python source capable of being frozen as bytecode:
=>https://github.com/peterhinch/micropython-font-to-py
Regards,
Reply
AndréI followed this tutorial. I am using a wemos D1 mini. I am using esp8266-20190529-v1.11.bin firmware. I have no problem with the led blink code. When I tried using with the OLED, i get this error – OSError: [Errno 110] ETIMEDOUT. I have no idea how to solve it. With the same OLED and code( just modify the scl and sda pins), I tried on an esp32, it is working. Anyone facing the same problem with wemos D1 mini?
Replywith lot s of effort ,i m finished this project,thank you Sara and Rui .
Reply
all the best from 9a3xz-Mikele-CroatiaThe initialization from this guide didn’t work for me. I used directly from micropython reference for ESP8266 and it work fine:
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)Can u tell me how to clear screen before updating it with new data in loop ? I’m reading the temperature and I want to update it but firstly I have to clear screen because after few updates the it’s unreadable. Some tip ?
ReplyHi there,
when I uploade the code to my ESP 32 it shows the following error message:
download ok
ex& J<moduleZ”ssd1306.py”, line 123, in write_cmd
OSError: [Errno 110] ETIMEDOUTThere seems to be a problem with the initialization. Any Ideas?
Reply
Best regards
MartinComment resolved, used the wrong i2c pins.
ReplyI’m doing this today and hopefully I’ll have smooth tinkering.
Reply
Feedback to you later.Thank you for writing this tutorial! It was a great way to introduce myself to the ESP32 and using it with an OLED display.
I ended up abandoning the adafruit driver in favor of the micropython supported driver (https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py). They are substantially similar, but I found the latter, with its simple ability to use fill_rect() and rect() much easier to work with (I never figured out how to update a single value on the screen without having to use fill(0) in the adafruit driver, and wasn’t a fan of the flashing it caused).
Other than the change-up of the driver, this tutorial really helped me get started with the ESP32. Thanks again!
ReplyHi Tim.
Reply
Thanks for sharing.
I’ll experiment with that driver for the OLED.
Regards,
SaraGreat! I’d love to hear your thoughts on it.
The code you wrote above should work with only minor changes: in terms of function calls for text(), you just need add a parameter for color at the end of each call (binary of 0 or 1), but otherwise all of the commands you already have listed will work.
Also worthy of note, it would seem that since this tutorial was written, the I2C library has been deprecated in favor of SoftI2C. The code you wrote will absolutely work as is, but the debugger will issue a warning about the deprecation.
Again, this only requires minor changes: importing the SoftI2C from machine instead of I2C, and dropping the -1 parameter from the class initialization.
I hope I’m not coming across as critical, I found this tutorial extremely useful, and just trying to share some of my discoveries while playing with what you taught me.
Reply
hello, in thonny i havent device and upload button so I cant upload to rasp pi pico.
Replybut i still, cant use library for rasp pico because it is not library for pico.
Reply
Traceback (most recent call last):
File “”, line 13, in
File “ssd1306.py”, line 116, ininit
File “ssd1306.py”, line 36, ininit
File “ssd1306.py”, line 61, in init_display
File “ssd1306.py”, line 121, in write_cmdI am very much interested in Micropython, though far from it except Blink. I have inspired and did many projects from your newsletter in Arduino and achieved success. should I use SSD1307 instead of SSD1306? If not what should be the right code. If possible plz. help me out.
ReplyExcellent tutorial! I got a “Micropython starter kit” which had the exact same boards: an ESP32, an SDD1306, and a DHT11. I was afraid that at least the OLED module didn’t work since it didn’t seem to power up whether I connected it to the ESP32, or direct to the breadboard. I think it was uploading ssd1306.py to the board that brought it to life, and I got “Hello World” displayed on it.
I also appended your code for the DHT11 to main.py, modifying it to send output to the OLED module instead. After a little futzing with the parameters to oled.text, the temperature and humidity show up below the Hello World strings.
Next up: a nicer format instead of the bare numbers, and installing larger fonts so the numbers take up more of the screen. The “Hello World” strings can go
ReplyYou might want to have a look at the ssd1306 driver which is part of micropython:
https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py
This derives directly from framebuf and thus directly inherits all drawing functions and is therefore a little simpler and more straight forward to use.
Replynon funzio questi sono gli errori
Traceback (most recent call last):
File “”, line 15, in
File “ssd1306.py”, line 116, ininit
File “ssd1306.py”, line 36, ininit
File “ssd1306.py”, line 61, in init_display
File “ssd1306.py”, line 121, in write_cmd
OSError: [Errno 19] ENODEV
Reply>
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
Traceback (most recent call last):
Reply
File “”, line 10, in
AttributeError: ‘module’ object has no attribute ‘SSD1306_I2C’Hi Sara,
Reply
Thanks for the above.
Can I use this connection and OLED device to display an image on the OLED screen?
We would like to display QR code on the screen which we will have as a BMP file available for the ESP.
Thanks,
EyalThanks for the tutorial.
I connut find the “device” menu in the new release of the Thonny IDE. Any idea ?
regards
Reply
CyrilI erased the flash, installed the latest Micropython (1.20.0) using Thonny, and copied your code from this web page. I get the “OSError: [Errno 110] ETIMEDOUT” error.
This exact same hardware works with Arduino IDE code.
I’m using a D1 Mini 8266 and SSD1306 display.
I would appreciate your help. Thanks…
ReplyHi.
Double-check that the OLED display is properly connected to your board.
Also, make sure that you’ve uncommented the line to use ESP8266 I2C pins:
i2c = SoftI2C(scl=Pin(5), sda=Pin(4))Regards.
Reply
Sara
That is the right address, but it was found by an Arduino scan.
The scan of the same hardware with micropython does not show any I2C devices.
from machine import I2C
i2c.scan()
[]Yes, I uncommented the I2C pins for the 8266 as shown below.
ESP8266 Pin assignment
i2c = SoftI2C(scl=Pin(5), sda=Pin(4))
Thanks.
ReplyHelp pleace !
%Run -c $EDITOR_CONTENT
MPY: soft reboot
Traceback (most recent call last):
File “”, line 13, in
File “ssd1306.py”, line 116, ininit
File “ssd1306.py”, line 36, ininit
File “ssd1306.py”, line 61, in init_display
File “ssd1306.py”, line 121, in write_cmd
OSError: [Errno 19] ENODEV
Reply>
My OLED supports different font sizes, and the default is very small. I know that because I did that on c (ino) code on arduino ide. Is there a way to tell it to use a larger font with micropython?
ReplyHallo Sara,
Reply
vielen Dank für die tollen Tutorials. Habe dadurch sehr viel Spaß mit MicroPython und dem ESP32. Konnte viele Erfahrungen sammeln und manches Problem selbst lösen.
Mit dem OLED Display ssd1306 habe ich ein größeres Problem.
Es wir nun schwarzer Schnee auf hellem Grund angezeigt, statt der Worte “Hello World”.
Ich habe sowohl die hier empfohlene Lib. zu ssd1306, wie auch die auf git.hub ausprobiert, sogar 2 andere Displays, aber es bleibt bei der Anzeige von Schnee statt Schrift. Das “Schneebild” ist sehr konstant. Markante Muster ändern sich nicht. Es scheint, wie eine zerrissene Anzeige der Textausgabe. Kannst Du bitte helfen?
Mit bestem Dank vorab
Manfredhi!!
I have this problem and I can´t solve it. Can you help me?Traceback (most recent call last):
File “”, line 15, in
File “ssd1306.py”, line 114, ininit
File “ssd1306.py”, line 34, ininit
File “ssd1306.py”, line 59, in init_display
File “ssd1306.py”, line 119, in write_cmd
OSError: [Errno 116] ETIMEDOUT
Reply>
Hello Sara,
Thank you very much for the great tutorials. I’ve had a lot of fun with MicroPython and the ESP32 as a result. I’ve gained a lot of experience and have been able to solve many problems on my own.
I’m facing a bigger issue with the OLED display ssd1306. Instead of displaying the words “Hello World,” it’s showing black snow on a light background. I’ve tried both the recommended library for ssd1306 and the one on GitHub, even with two other displays, but it still shows snow instead of text. The “snow image” is very consistent, and distinctive patterns don’t change. It seems like a distorted display of text output. Can you please help?
Thanks in advance.
Best regards,
Reply
ManfredHi.
Reply
Did you upload the code to the ESP32 as main.py?
Regards,
SaraYes, I did .
Thank you for answering. I checked everything carefully.
Many other tutorials worked pretty fine. Just the Display is not working well.
I will double check all settings again.Best Regards
Reply
ManfredIt seems like the display is running the default code and that it is not running the current code.
Reply
Make sure to upload the code as main.py and restart the ESP32 afterwards.
Regards,
SaraHi Sara,
thank you very much.
I saved the library to the ESP32 device and the short code for ‘Hello, World’ to the device.
So there are three files on the device: boot.py, main.py and ssd1306.py
After restart the ESP32 the display showed just a black bar on the top and one at the bottom in adittion to the black snow pixels.
The pixel are not random. the black pixels are on fixed positions.I would send you a few images if possible!?
Best regards
ManfredShare a link to the picture in google drive or imgur
Regards,
SaraHi Sara,
sorry it tooks some time …https://drive.google.com/drive/folders/19lXA2PerWmP4Va7qkyfzl8iiWSuJRzDY?usp=sharing
Traceback (most recent call last):
File “”, line 14, in
File “ssd1306.py”, line 116, ininit
File “ssd1306.py”, line 36, ininit
File “ssd1306.py”, line 61, in init_display
File “ssd1306.py”, line 121, in write_cmd
OSError: [Errno 19] ENODEVMay help me?
ReplyHi.
Reply
Check that you’ve installed the library following the instructions in our guide.
Double-check your display connections.
Regards,
SaraPls do the i2c scan & find out the address of your display. Mostly it will be 0x3C. You have to change the default 0x3D and use this address in driver (line no 104 of driver)
Reply













