4.1 IIC - OLED
The ESP32 chips have a GPIO matrix that allows you to route most peripherals (like I2C) to any available GPIO pin. In other words: pick two convenient GPIO pins, make sure they're not otherwise occupied (flash connected to them, bootstrap pins etc), and you can use them as your I2C pins.
In this tutorial we’ll take a look at the I2C communication protocol with OLED Display.
Introduction
1. IIC or I2C Protocol
IIC or I2C means Inter Integrated Circuit (it’s pronounced I-squared-C), and it is a synchronous, multi-master, multi-slave communication protocol. You can connect multiple slaves to one master or multiple masters controlling the same slave. I2C stands out for its simplicity and it is an ideal choice for short distance communications.
The ESP32 supports I2C communication that can serve as I2C master or slave. We use this protocol with the ESP32 to communicate with external devices like sensors and displays. In these cases, the ESP32 is the master chip and the OLED display is the slave.

2. IIC Wiring
I2C communication protocol uses two wires to share information: the Serial Clock Line (SCL) and the Serial Data Line (SDA). Note that in many boards, the SDA line may also be labeled as SDI and the SCL line as SCK.
The SDA and SCL lines are active low, so they should be pulled up with resistors. Typical values are 4.7k Ohm for 5V devices and 2.4k Ohm for 3.3V devices. Most sensors and displays are already have the resistors built-in. If not, you need to connect pull-up resistors by yourself.
Connecting an I2C device to an ESP32 is normally as simple as follows.
SDA or SDI
SDA (can use any GPIO pins not occupied)
SCL or SCK
SCL (can use any GPIO pins not occupied)
GND
GND
VCC
usually 3.3V or 5V
3. IIC Device Address
With I2C communication, each slave on the bus has its own address, a hexadecimal number that allows the ESP32 to communicate with each device.
The I2C address can be usually found on the component or its datasheet. However, if it is difficult to find out, you may need to run an I2C scanner sketch to find out the I2C address.
4. OLED Display
OLED means Organic Light-Emitting Diode, and OLED display doesn’t require backlight, which results in a very nice contrast in dark environments. Additionally, its pixels consume energy only when they are on, so the OLED display consumes less power when compared to other displays.
The OLED display that we’ll use in this tutorial is the 0.96 inch 128x64 Pixel OLED Display with SSD1306 Driver and I2C protocol.
The model we’re using has four pins and communicates with any microcontroller using I2C communication protocol.
VCC: This is the power supply pin for 3.3V or 5V power to 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.
The SSD1306 controller supports both SPI and I2C communication protocols.There are models that come with an extra RESET pin or using SPI communication protocol.

Hardware Circuit
1. Parts Required
Here’s a list of the parts to you need to build the circuit:
ESP32-S3-N16R8
OLED Display with SSD1306 Driver and I2C protocol
Pull-up resistors
Breadboard
Jumper wires
2. Wiring
As the OLED display uses I2C communication protocol, wiring is very simple. Here we use GPIO 6 as SDA and GPIO 5 as SCL as shown below:

Coding and Testing
1. Available Libraries
For Arduino IDE, use the Wire.h
library to communicate with devices using I2C protocol. However, you can use third-party libraries. There are several libraries available for ESP32 to control the OLED display driven by SSD1306:
No matter what OLED screen and library, it can be abstracted into a pixel matrix. If you want to display something, just light up the pixels at a specific location. For example, SSD1306 is a 128X64 pixel matrix. This matrix has its own set of coordinate systems. In the coordinate system, the upper left corner is the origin, the right is the X axis, and the bottom is the Y axis.

2. Use Adafruit libraries
Firstly, Install the two libraries in the PlatformIO IDE: Go to Libraries, search for Adafruit GFX
and Adafruit_SSD1306
and add them to your project.

Then, you can check the example code:
In your PlatformIO IDE, go to .pio > libdeps > Adafruit SSD1306 > Examples and select the example
ssd1306_128x64_i2c.ino
If your OLED doesn’t have a RESET pin, you should set the OLED_RESET variable to -1 as shown below:
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Using the Adafruit_SSD1306 library is divided into three steps:
Initialize OLED, call the constructor, and call the begin method;
After initialization, call the drawing class function, and you can set the color, font, etc.
After drawing, call the display class function.
Finally, upload the code to your ESP32 board.
Don’t forget to select the right board and COM port.
If your OLED display is not showing anything:
Check that the OLED display is properly wired to the ESP32
Double-check the OLED display I2C address
Double check if your display has built-in pull-up resistor
3. OLED Display Text
The Adafruit library for the OLED display comes with several functions to write text. In this section, you’ll learn how to write and scroll text using the library functions.
Static text display The following code displays Hello, world! message in the OLED display. (If you use different Pins with ESP32, change it first.)
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
// Display static text
display.println("Hello, world!");
display.display();
}
void loop() {
}
- Scrolling Text
The Adafruit OLED library provides useful methods to easily scroll text.
startscrollright(0x00, 0x0F)
: scroll text from left to rightstartscrollleft(0x00, 0x0F)
: scroll text from right to leftstartscrolldiagright(0x00, 0x07)
: scroll text from left bottom to right upperstartscrolldiagleft(0x00, 0x07)
: scroll text from right bottom to left upper
The following code implements those methods.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
// Display static text
display.println("Scrolling Hello");
display.display();
delay(100);
}
void loop() {
// Scroll in various directions, pausing in-between:
display.startscrollright(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrollleft(0x00, 0x0F);
delay(2000);
display.stopscroll();
delay(1000);
display.startscrolldiagright(0x00, 0x07);
delay(2000);
display.startscrolldiagleft(0x00, 0x07);
delay(2000);
display.stopscroll();
delay(1000);
}
The Adafruit GFX library allows us to use some other fonts besides the built-in fonts.
To use one of those fonts, first you need to include it in your sketch, for example:
#include <Fonts/FreeSerif12pt7b.h>
Next, you just need to use the setFont() method and pass as argument, the specified font:
display.setFont(&FreeSerif12pt7b);
After specifying the font, all methods to write text will use that font.
To get back to use the original font, you just need to call the setFont() method with no arguments:
display.setFont();
4. OLED Display Shapes
The Adafruit OLED library provides useful methods to draw pixels, lines and shapes.
Draw a pixel
To draw a pixel in the OLED display, you can use the drawPixel(x, y, color) method that accepts as arguments the x and y coordinates where the pixel appears, and color. For example:
display.drawPixel(64, 32, WHITE);
- Draw a rectangle
The drawRect(x, y, width, height, color) provides an easy way to draw a rectangle. The (x, y) coordinates indicate the top left corner of the rectangle. Then, you need to specify the width, height and color:
display.drawRect(10, 10, 50, 30, WHITE);
You can use the fillRect(x, y, width, height, color) to draw a filled rectangle. This method accepts the same arguments as drawRect().
display.fillRect(10, 10, 50, 30, WHITE);
- Draw a circle
To draw a circle use the drawCircle(x, y, radius, color) method. The (x,y) coordinates indicate the center of the circle. You should also pass the radius as an argument. For example:
display.drawCircle(64, 32, 10, WHITE);
In the same way, to build a filled circle, use the fillCircle() method with the same arguments:
display.fillCircle(64, 32, 10, WHITE);
- Draw a triangle
Use the the drawTriangle(x1, y1, x2, y2, x3, y3, color) method to build a triangle. This method accepts as arguments the coordinates of each corner and the color.
display.drawTriangle(10, 10, 55, 20, 5, 40, WHITE);
Use the fillTriangle() method to draw a filled triangle.
display.fillTriangle(10, 10, 55, 20, 5, 40, WHITE);
Code for Draw Shapes: Upload the following code that implements each snippet of code we’ve covered previously and goes through all the shapes.
#include <Arduino.h> // Some code
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000); // Pause for 2 seconds
// Clear the buffer
display.clearDisplay();
// Draw a single pixel in white
display.drawPixel(64, 32, WHITE);
display.display();
delay(3000);
// Draw line
display.clearDisplay();
display.drawLine(0, 0, 127, 20, WHITE);
display.display();
delay(3000);
// Draw rectangle
display.clearDisplay();
display.drawRect(30, 10, 50, 30, WHITE);
display.display();
delay(3000);
// Fill rectangle
display.fillRect(30, 10, 50, 30, WHITE);
display.display();
delay(3000);
// Draw round rectangle
display.clearDisplay();
display.drawRoundRect(10, 10, 30, 50, 2, WHITE);
display.display();
delay(3000);
// Fill round rectangle
display.clearDisplay();
display.fillRoundRect(10, 10, 30, 50, 2, WHITE);
display.display();
delay(3000);
// Draw circle
display.clearDisplay();
display.drawCircle(64, 32, 10, WHITE);
display.display();
delay(3000);
// Fill circle
display.fillCircle(64, 32, 10, WHITE);
display.display();
delay(3000);
// Draw triangle
display.clearDisplay();
display.drawTriangle(10, 10, 55, 20, 5, 40, WHITE);
display.display();
delay(3000);
// Fill triangle
display.fillTriangle(10, 10, 55, 20, 5, 40, WHITE);
display.display();
delay(3000);
// Invert and restore display, pausing in-between
display.invertDisplay(true);
delay(3000);
display.invertDisplay(false);
delay(3000);
}
void loop() {
}
5. Display Bitmap Images
You can display 128×64 bitmap monocolor images on the OLED display.
First, use an imaging program to resize a photo or picture and save it as monochrome bitmap. If you’re on a Windows PC, you can use Paint.
Then, use a Image to C Array converter to convert the image into an array. I’ve used LCD Image Converter. Run the program and start with a new image. Go to Image > Import and select the bitmap image you’ve created earlier.

Go to Options > Conversion and in the Prepare tab, select the following options:
Type: Monochrome, Threshold Dither
Main Scan Direction: Top to Bottom
Line Scan Direction: Forward

Go to the Image tab and select the following options:
Split to rows
Block size: 8 bit
Byte order: Little-Endian

Then, click OK. Finally, in the main menu, go to File > Convert. A new file with .c extension should be saved. That file contains the C array for the image.
Open that file with a text editor, and copy your array to the code. Then, to display the array, use the drawBitmap() method that accepts the following arguments (x, y, image array, image width, image height, rotation). The (x, y) coordinates define where the image starts to be displayed.
Copy the code below to display your bitmap image in the OLED.
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
static const uint8_t image_data_Saraarray[480] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0x39, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfe, 0xed, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfc, 0xc4, 0x7f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xfb, 0xc5, 0x3f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf3, 0xcd, 0x9f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xef, 0xf9, 0xdf, 0xff, 0xff, 0xff,
0xff, 0xff, 0xcf, 0xf3, 0xe7, 0xff, 0xff, 0xff,
0xff, 0xff, 0xbf, 0xe7, 0xf3, 0xff, 0xff, 0xff,
0xff, 0xff, 0x3f, 0xd9, 0xfb, 0xff, 0xff, 0xff,
0xff, 0xfe, 0xff, 0x9c, 0xfb, 0xff, 0xff, 0xff,
0xff, 0xfc, 0x3f, 0x7e, 0xfb, 0xff, 0xff, 0xff,
0xff, 0xf9, 0x5e, 0x7f, 0x23, 0xff, 0xff, 0xff,
0xff, 0xf3, 0xcd, 0xff, 0x99, 0xff, 0xff, 0xff,
0xff, 0xe6, 0x61, 0xff, 0xfc, 0xff, 0xff, 0xff,
0xff, 0xec, 0x37, 0xff, 0xfe, 0xff, 0xff, 0xff,
0xff, 0xec, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xec, 0x67, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xef, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x37, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xe0, 0x00, 0x3f, 0xff, 0x00, 0xe1, 0xff,
0xff, 0xe0, 0x00, 0x1f, 0xff, 0x00, 0x43, 0xff,
0xff, 0xe0, 0x00, 0x0f, 0xff, 0x00, 0xc3, 0xff,
0xff, 0xe0, 0x00, 0x07, 0xfe, 0x18, 0xc3, 0xff,
0xff, 0xe0, 0x00, 0x07, 0xfe, 0x10, 0x87, 0xff,
0xff, 0xe0, 0x00, 0x03, 0xfe, 0x01, 0x87, 0xff,
0xff, 0xe0, 0x00, 0x01, 0xfc, 0x03, 0x87, 0xff,
0xff, 0xe0, 0x00, 0x01, 0xfc, 0x03, 0x0f, 0xff,
0xff, 0xe0, 0x00, 0x01, 0xfc, 0x63, 0x0f, 0xff,
0xff, 0xe0, 0x00, 0x00, 0xf8, 0x43, 0x0f, 0xff,
0xff, 0xe0, 0x00, 0x00, 0xf8, 0x42, 0x1f, 0xff,
0xff, 0xe0, 0x00, 0x00, 0xf8, 0x46, 0x1f, 0xff,
0xff, 0xe0, 0x00, 0x00, 0xf0, 0x86, 0x07, 0xff,
0xff, 0xe0, 0x00, 0x00, 0x70, 0x84, 0x07, 0xff,
0xff, 0xe0, 0x00, 0x00, 0xf5, 0xdd, 0x1f, 0xff,
0xff, 0xf5, 0x55, 0x55, 0x7f, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
void setup() {
Serial.begin(115200);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
delay(2000); // Pause for 2 seconds
// Clear the buffer.
display.clearDisplay();
//display.setCursor(20,20);
// Draw bitmap on the screen
display.drawBitmap(20, 0, image_data_Saraarray, 64, 64, 1);
display.display();
}
void loop() {
}
6. Other Libraries
For other libraries, the application is similar. For example, we can install and have a quick test the ThingPulse/esp8266-oled-ssd1306 library as follows:
#include <Arduino.h>
#include <Wire.h>
#include "SSD1306.h"
SSD1306 display(0x3c, 6, 5);
void setup() {
display.init();
display.setFont(ArialMT_Plain_24);
display.drawString(0, 0, "Hello World");
display.display();
}
void loop() {
Serial.println("Hello Robotlabs");
}
Summary
In this tutorial you learned about I2C communication protocol with the ESP32. Also, we talk about how to develop the OLED display with SSD1306 Driver and I2C protocol. Now, you can go further and integrate the OLED display in your own projects.
Finally, if you want to learn more about other controllers, take a look at our Youtube channel:
Last updated