Fun With OLED Display and Arduino




About: I'm a software developer interested on robotics and electronics project as a hobby. I really enjoy sharing what I've learned.

I’m pretty sure you’ve definitely heard about OLED display technology. It’s relatively new and offers a better quality than old LCD technology. In this tutorial we want to review the steps required to display data on one of the most common single color OLED display modules available on market. I'll try to explain functionalities provided by corresponding Adafruit library to display data on this module.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: What OLED Modules Are We Going to Use?

OLED modules are available in wide variety of sizes and features. The one we’re going to use in this tutorial is a mono color 128x64 OLED module. This type of module is available in following sizes (In order that you see on the pictures):

  • 128x64
  • 128x32
  • 96x16
  • 64x48
  • 64x32

As all these modules support I2C protocol as a mean for communication, the code and wiring of all of them is exact same. The only difference is that you have to consider the size of the display on your code so that the contents that you’re going to display, fit properly on it.

Step 2: I2C in a Nutshell

The inter-integrated circuit (IIC) which is normally called I2C (I squared C) developed by Philips on 80s as a data exchange bus used to transfer data between the central processing unit (CPU) or microcontroller unit (MCU) of a device and peripheral chips. It was basically targeted for TV application. Due to its simplicity, it became so popular that after awhile it became one of the primary mechanisms of data transfer for CPUs and MCUs and peripheral devices that are not necessary part of the same PCB board and are connected to it via wire (e.g. sensors, display modules, etc.).

I2C consists of a communication bus made of two wire that supports bidirectional data transfer between a master and several slave devices. Typically the master node is in charge of controlling the bus – which is actually done by generating a synchronization signal on the serial clock line (SCL) . It’s a signal that would be sent continuously by master during the transfer and all other nodes connected to the bus will use it to sync their communication and detect the speed of the bus. Data is transferred between the master and slave through a serial data (SDA) line. The transmission speed can be up to 3.4 Mbps. All devices that want to transfer data via I2C should have a unique address and can operate as either transmitter or receiver depending on the function of the device. For example an OLED display module is a receiver which accepts some data and displays them, while a temperature sensor is a transceiver that sends captured temperature via I2C bus. Normally a master device is the device that initiates a data transfer on the bus and generates the clock signals to permit the transfer. During that transfer, any device addressed by this master is considered a slave and reads that data.

When a node wants to send some data, the very first byte of the data should be the address of the receiver and then actual data comes afterwards. This means that in order to send a data to an output device using I2C (e.g. I2C OLED display module) we should first find its I2C address and this is what we’ll do first on next steps.

If you are interested to know more about the details and theories about I2C bus, you can use following references:

Step 3: Required Modules and Components

Here you can find the list of components that you would need to complete this tutorial:

eBay links: links: links:

Step 4: Wiring OLED Display Module to Arduino

An important note about I2C enabled devices is that the way you should connect them to Arduino are all the same. This is because Arduino runs its I2C communication only on specific pins. In this tutorial I’m using Arduino Uno. The Arduino Uno uses pin A5 as SCK and A4 as SDA. So we can connect the OLED display module to Arduino Uno as shown in the schematic view. As you may notice in the picture I’ve taken from my OLED display module, the connector for VCC and GND are different than the schematic view. Remember to check the labels of the pins on your modules to make sure you’re connecting it in a right way.

We need only 4 pins that should be connected as below:

Arduino VCC -> OLED Module VCC

Arduino GND -> OLED Module GND

Arduino 4 -> OLED Module SDA

Arduino 5 -> OLED Module SCK

Step 5: Finding the Address of the Display Module

As a first step on connecting to an I2C enabled device, you need to have the address of the module. In order to do so, after wiring up the module to your Arduino, you should just upload the code attached, onto your Arduino. This code incorporates the Wire library which is a library included with Arduino IDE that handles I2C communication. It tries to scan connected I2C devices and sends their address via serial port to your computer. So you can access its output via Serial Monitor tool in Arduino IDE. The original version is available at Arduino Playground). Also you can view it in a more readable way in my online Arduino Editor. Don't expect anything to be displayed on the screen while this code is running.

As you can see on the picture, my module is binded to address 0x3C. Normally all the devices in a specific product line (for example all 128x64 OLED modules)have same address.

The addresse of I2C devices are limited from 1 to 126. This code simply tries to connect to each device in order (without transmitting any data) and then check if there was any error reported by the underlying library on connecting to the provided address. If there is no error, then prints the address as an available module to connect. Also it should be noted that the first 15 addresses are reserved, so it jumps over them and just prints those above this range. Remember that the address of these I2C modules are hard-coded on the device and it can not be changed. So it would be a good idea to write it down somewhere or put a label on the module when you’re going to put it back on your lab shelf so that the next time, running the scanner code would not be necessary. However it’s not a complicated procedure ;)

Step 6: Installing Libraries Required to Display Data on OLED Module

The Wire library can handle low level communication with I2C devices. When you want to connect to a specific device in order to read/write data from/to it, normally you would use a library provided by the company who has originally built that module. This library handles all I2C communication details with given module and let us concentrate more on our business which in this case is displaying the data in the way we want.

Adafruit, the company who manufactures the original version of such display modules, provides a libraries called Adafruit SSD1306 to display data on these monochrome displays. So before start coding, we’ve to install this library via Library Manager (accessible via Sketch > Include Library > Manage Libraries... menu) in Arduino IDE. There is also another library called Adafruit GFX Library which handles more low level graphical stuff and is used internally by Adafruit SSD1306. You need to have both of them installed on your Arduino IDE as you can see on the pictures.

Step 7: Initializing the Display Module

Drawing on display module is wrapped in a class named Adafruit_SSD1306. The definition of this class is on Adafruit library, so we need to first include that library. Then we have to instantiate an instance of this class first. The constructor of this class takes the port number at which the display could be reset which is pin 4 (connected to SCK). This part of the code should be located at the beginning of the file (out of setup() and loop() functions).

#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(4);

Now inside the setup() function we should call the begin function of the display object by passing our I2C address as below (the SSD1306_SWITCHCAPVCC is a constant value specifying the type of power source to the library):

void setup() {
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

void loop() {} // loop can be empty for now

Now the display object is ready and we can call its functions (e.g. display.write(), display.drawLine, etc.). The important note is that whenever we draw something on by calling on our display object, we need to call the display.display() function to make the actual drawing happen on the hardware level. This is mainly due to the fact that the drawing functions that we call, just update the "in memory" representation of the display for performance reasons. It actually caches the changes in memory. So we should always remember to call the display() function when we finished drawing something on the screen.

display.write(...);    // keeps updating in memory
display.drawLine(...); // keeps updating in memory
display.display();     // flushes all changes to the display hardware

If you try to upload your code in this step, you'll notice that the Adafruit Industries logo would be displayed. You may wonder who has asked it to draw that! Actually this is what the Adafruit library does. It initializes the memory of the module (the in memory representation of the display hardware) with the logo of this company. If you don't want to see that during the initialization, you can try to call display.clearDisplay() function right before calling display.display() in your setup function. This function, as its name suggests, clears the display completely.

#include <Adafruit_SSD1306.h>

Adafruit_SSD1306 display(4);

void setup() {
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

void loop() {

Based on documentation of Adafruit_SSD1306 library, you can use different functions provided by this class to draw on display or directly manipulate the pixels on it. In next sections we'll try to present an example for each one of them so that you can have an idea about the way it works. Most of these examples will display just a simple static contents, so we can just put them inside our setup() function (after the initialization code). By doing so it would be run only once and remains there.

Step 8: Display a Simple Text

To display a text, we can use the simple display.println() function of the library. It accepts the text as an string and tries to display it. It's important to know that we have to tell the library where on the display we're going to present the text. Every pixel on the display has a coordinate that is specified with a X and Y. The X increases from left to right and Y increases from top to the bottom. The upper left corner of the screen is (X=0, Y=0) and the lower right corner is (X=127, Y=63). I noted the coordinates of the corners on the first picture. We can use the display.setCursor() function to specify where on the display we're going to display the text.

Another property of the text is its color. We can specify the color using display.setTextColor() as displayed on following sample.


display.println("Hello World!");

We can also use the display.write() function to display a single character. It accepts a character code as an uint8_t type and displays the character corresponding to that code on the string. As an example, if we want to display the same string using this function, we can use the following snippet:




It's also possible to draw texts in black color with a white background. In order to do so, you have to call the display.setTextColor function as below:


// Sets the color to black with a white background
display.setTextColor(BLACK, WHITE);
display.println("Inverted text!");

You also have the option of setting the size of the text using display.setTextSize() function. It accepts an integer number as a size. The greater the number, the bigger the text would be. Smallest size is 1 which is the default size of texts. Following code tries to write the letter "A" in 6 different sizes:



Step 9: Drawing Basic Shapes

Drawing basic shapes like rectangle, circle, triangle, line or point are very easy and there is a dedicated function for each one.

Drawing line

To draw a line you can call display.drawLine(startX, startY, endX, endY, color). For example following code draws a diagonal lines in the screen so that they shape a big X:


display.drawLine(0,0,display.width() - 1, display.height() - 1, WHITE);
display.drawLine(display.width() - 1,0,0, display.height() - 1, WHITE);


You can access the width and height of the display using display.width() and display.height() functions. By doing so your code would be independent from the screen size.

Drawing rectangle

The function to draw a rectangle is display.drawRect(upperLeftX, upperLeftY, width, height, color). Here is the code that draws three rectangle on some random places:


display.drawRect(100, 10, 20, 20, WHITE);
display.fillRect(10, 10, 45, 15, WHITE);
display.drawRoundRect(60, 20, 35, 35, 8, WHITE);


By calling display.fillRect(upperLeftX, upperLeftY, width, height, WHITE) you can draw a rectangle filled by specified color. Also the third function in this example is display.drawRoundRect(upperLeftX, upperLeftY, width, height, cornerRadius, color) that as you can see in the picture is used to draw a rectangle with round corners. It accepts an extra parameter before color that is an integer number indicating the corner radius. The bigger the value the rounder the corner. It also has a corresponding fill function named display.drawFillRoundRect that I think you can guess what it does.

Drawing circle

The function is display.drawCircle(centerX, centerY, radius, color). Here is an example which draws a smiley-like shape:

display.drawCircle(60, 30, 30, WHITE);
display.fillCircle(50, 20, 5, WHITE);
display.fillCircle(70, 20, 5, WHITE);

Like rectangles, you can use the display.fillCircle function to draw a circle filled with the given color.

Drawing triangle

Ahh, again a function called display.drawTriangle(poin1X, point1Y, point2X, point2Y, point3X, point3Y, color) and corresponding display.fillTriangle that draw a filled triangle.

display.drawTriangle(24, 1, 3, 55, 45, 55, WHITE);
display.fillTriangle(104, 62, 125, 9, 83, 9, WHITE);

Draw a point

You can also color a specific point (which is called pixel) on the screen via display.drawPixel(pixelX, pixelY, color) function.

display.drawPixel(20, 35, WHITE);
display.drawPixel(45, 12, WHITE);
display.drawPixel(120, 59, WHITE);
display.drawPixel(97, 20, WHITE);
display.drawPixel(35, 36, WHITE);
display.drawPixel(72, 19, WHITE);
display.drawPixel(90, 7, WHITE);
display.drawPixel(11, 29, WHITE);
display.drawPixel(57, 42, WHITE);
display.drawPixel(69, 34,  WHITE);
display.drawPixel(108, 12, WHITE);

Step 10: Drawing Image

Drawing an image is different and a bit complicated. As the display module is monocolour, we need to first convert our image to a format called mono color bitmap (also called black and white). In such a format, each pixel of the image is presented with either 0 or 1. The 1s represents the existence of the color and 0s means an empty space. You can see an example of the Arduino logo in this format on top of this section. The function to draw a bitmap image is display.drawBitmap(topLeftX, topLeftY, imageData, width, height, color). The imageData parameter is an array of numbers in bytes. Each byte has 8 bits, so each byte contains the data of 8 pixels of the image. By specifying the width and height of the image, the drawBitmap function will know from which bit the next row of pixels begins.

The solution that I chose to convert my image to this format was to first use one of online "image to ASCII converters" (e.g. to convert my picture to a set of ASCII characters and then replace the characters used for empty space by 0 and other ones by 1. That's what you see below. You can think of each 0 and 1 as a pixel on the display. So the size of the picture should not exceed our display size which is 128x64.

Note: Using this ASCII technique is not a recommended approach because due to the aspect ratio of characters your image will be deformed (characters are not a square). I tried this technique just because it make it easier to convert the image to required format. Otherwise it would be possible to achieve the best result via some programming or using some utility applications which are completely out of the scope of this text.

0000000000000000011111111111111111111111111111100000000000000000 0000000000000111111111111111111111111111111111111110000000000000 0000000000011111111111111111111111111111111111111111100000000000 0000000001111111111111111111111111111111111111111111111000000000 0000000111111111111111111111111111111111111111111111111110000000 0000011111111111111111111111111111111111111111111111111111100000 0000111111111111111111111111111111111111111111111111111111110000 0001111111111111111111111111111111111111111111111111111111111000 0011111111111111111111111111111111111111111111111111111111111100 0111111111111111000000011111111111111111100000001111111111111110 0111111111110000000000000001111111111000000000000000111111111110 1111111111000000001111000000001111000000001111000000001111111111 1111111110000011111111111100000110000011111111111100000111111111 1111111100000111111111111111000000001111111001111110000011111111 1111111100001111100000011111100000011111100000011111000011111111 1111111100001111100000011111100000011111100000011111000011111111 1111111100000111111111111111000000001111111001111110000011111111 1111111110000011111111111100000110000011111111111100000111111111 1111111111000000001111000000001111000000001111100000001111111111 0111111111110000000000000000111111110000000000000000111111111110 0111111111111111000000001111111111111111000000001111111111111110 0011111111111111111111111111111111111111111111111111111111111100 0001111111111111111111111111111111111111111111111111111111111000 0000111111111111111111111111111111111111111111111111111111110000 0000011111111111111111111111111111111111111111111111111111100000 0000000111111111111111111111111111111111111111111111111110000000 0000000011111111111111111111111111111111111111111111111100000000 0000000000011111111111111111111111111111111111111111100000000000 0000000000000111111111111111111111111111111111111110000000000000 0000000000000000111111111111111111111111111111110000000000000000 0000000000000000000001111111111111111111111000000000000000000000

Now we should divide each line by 8, representing a byte and store them in an array as below:

static const unsigned char PROGMEM arduino_logo[] ={
    B00000000, B00000000, B00000111, B11111111,
    B11111111, B11100000, B00000000, B00000000,
    B00000000, B00000000, B01111111, B11111111, 
    B11111111, B11111110, B00000000, B00000000,
    ... // continue up to the end of the picture

Then we can draw it on display by calling the drawBitmap function.

display.drawBitmap(32,16,arduino_logo, 64, 32, WHITE);

Step 11: Troubleshooting

This was a long tutorial and so it's highly probable that something goes wrong. Here is the list of some common errors that you may encounter while setting up OLED display module for your project (some of them happened for me while preparing this tutorial).

Nothing is being displayed at all

This could happen for many reasons so I suggest to check following list which is in order that may occur in your project:

  • I2C address is probably wrong

Make sure that you've set the address you got in i2c-scanner code in display.begin() function when setting up your display object.

  • SCL and SDA are connected in a wrong way

This actually happened for me. If you're using Arduino Uno then you have to check your connections again to make sure they're connected same as mine. If you're using another Arduino edition (e.g. Mega, Leonardo, etc.), you have to know that they may have their I2C set to other pins. You can check it at documentation of Wire library.

  • You’re drawing something out of the visible area

This is a software issue. It's very common when using a drawing functions to miscalculate some coordinates and so your drawing would be deformed or in worst scenario it may be completely out of the scene. Review your calculations and try to do a step by step drawing to see what's going on.

Text are not being displayed at all

  • You forgot to set the color of text or you've set it to a wrong value

You need to call setTextColor before drawing texts. Otherwise you got no errors, but you'll see nothing on display. Also you may have set the text color same as the background color.

  • You’re using a very large font

If you set the text size to a very large value, it could be possible that characters get completely out of the visible area.

There is a compile error about display size

This also happened for me and I think it would happen for most of you. It's because of the display size constant values that are defined inside the header file Adafruit_SSD1306.h that we include on top of our script. This file is located at {your-project-folder}\libraries\Adafruit_SSD1306\Adafruit_SSD1306.h. If you open this file you would notice that there is a comment section as below in which it describes that you need to uncomment only the constant that represents your OLED display module size. For a 128x64 display modules, the line #define SSD1306_128_64 should be uncommented.


    SSD1306 Displays
    The driver is used in multiple displays (128x64, 128x32, etc.).
    Select the appropriate display below to create an appropriately
    sized framebuffer, etc.

    SSD1306_128_64  128x64 pixel display
    SSD1306_128_32  128x32 pixel display

   #define SSD1306_128_64
//   #define SSD1306_128_32
//   #define SSD1306_96_16

Step 12: What to Do Next?

The OLED display as an output module can give you a great opportunity to provide a professional looking interface to your hobby projects. You can try following ideas as an starting point to display a meaningful data on it or help user to know what's going on or if he/she needed to do something. It would be much clearer for a user to read a message on a display than to interpret the state of a project/device via some LEDs.

What you can do as a starting point could be:

  • Read a temperature sensor value and display it on OLED module. You can add pressure or humidity sensor to it and create a fully functional weather station project.
  • Try to draw something on the display module using a joystick module as an input device.
  • Try to draw an animation on the display by a sequence of drawing/delaying function calls or Arduino interrupt
  • Display your custom logo on your system startup (instead of Adafruit Logo)

Don't forget to tell me on comments, what would you do (or you've already done) using OLED display module.

Be the First to Share


    • Instrument Contest

      Instrument Contest
    • Make it Glow Contest

      Make it Glow Contest
    • STEM Contest

      STEM Contest

    19 Discussions


    Question 7 months ago

    any chance of showing an eye?
    was thinking circle but blink would be eliptical then line. also centre would have to be another colour for "pupil"
    the pupil would move around to simulate thinking and bored etc with the iris changing colour for happy, sad, angry etc.
    making for my daughter.


    8 months ago

    Nicely explained.


    1 year ago

    thank you, helped me a lot. how about programming multiple displays?

    1 reply

    Reply 11 months ago

    Hi FerenS3
    As far as I know, these OLED modules have a fixed address and so you can't use more than one of them in a single I2C bus. However I think it should be possible to use two other pins of the arduino as another I2C bus and connect the second display to that bus. I've never tested that but it worth to try. You can take a look at SoftwareI2C library ( It claims that it can setup I2C on any port.
    Also another solution could be to find and buy an OLED display that allows you to set its I2C address via a jumper or soldering path. It's a common technique used for such situations. Actually I've not found any OLED display with an adjustable address :(


    1 year ago

    I read a lot of tutorials, and this is by far the best, most comprehensive article about the incorporation of an OLED display into an Arduino project I have read to date. It will be bookmarked for future reference. My only regret was that I didn't read this article first. It would have saved me several hours of chunking up the example sketch, trial and error, troubleshooting etc. It's by no means a difficult challenge, its just hard to find all the right info, at the right time, in the same article.

    The basic, fundamental, good to know background info on the displays, I2C standard, perfect, digital wiring diagram, CURRENT info on updating libraries with new library updater, is all very useful. If you know it already - fine, thats what the scroll wheel is for, but I certainly found some good fundamental tidbits in many sections that filled in some gaps.

    It would have been a fantastic article if it ended right there, but no. From the perfect setup hardware explanation, it then merges seamlessly into the soft, code side of things.

    The author explains it thoroughly, with function syntax and structure information from the initial device addressing, through the display.display(); requirement, which I accidentally deleted once and then couldn't figure out why my display code wasn't working properly...and on and on and on. I experienced challenges at almost every step, that would have been easily understood had I read this article. It also goes through the most common functions and how to set them up. Really excellent work.

    I don't write reviews often, but this article is truly praiseworthy. I'm going to read more articles by this author if possible. Brother, you have serious talent - if you have other Instructables, and they are as well written as this one, you will have my sincere gratitude and admiration.

    1 reply

    Question 1 year ago on Step 5

    where can i find that code which is used to check "the module address"that prints on serial monitor

    1 answer
    Ashraf Minhaj

    1 year ago

    BEST EVER TUTORIAL i have EVER READ ! Thanks a lot.

    I'm gonna use this to make the Facial expression of my ROBOT.

    1 reply

    1 year ago

    Can you explain how to adapt a code from an sd1103 oled that uses U8g library and is an I2C type to my SPI SSD1306 oled that uses Adafruit 1306 library please? It seems to be way more complicated than i imagined. Tnx

    1 reply

    Reply 1 year ago

    Hi OviB

    If you're going to use the libraries you mentioned in your comment, then the code doesn't need to be changed that much. Methods I used are all defined in Adafruit library and they have provided these methods for both versions of the display (I2C as well as SPI) on top of GFX library. The only changes that I can imagine are:

    1 - Wiring (obviously)

    2 - Headers included on top of the code (SPI, GFX, 1306)

    3 - (Probably) some constant needed to be defined in the code

    I suggest you to take a look at the following code and libraries that are included (and their order) on top of the code:

    You can see that it included both SPI and Wire library so my guess is that it should work in both cases. Wire is the underlying library that would be used for I2C communication and the SPI is the one used for communicating over SPI port. I don't have experiences with U8g, but if the sample code in the above link works for you, then you don't need that library at all.


    1 year ago

    Hi, I've got the SSD1306 SPI version of it. Can i use the same methods as described and/or what do i need to change apart from uncommenting the right type of the display. Im trying to build a temp and humidity weather station with the DHT22 sensor by copy someone else's code that has I2c SD1103 screen and uses U8g library but nothing seems to work. I do not know how am i suppose to look @that code and cpy all the methods and adapte it to my SPI SSD1306 screen that uses the Adafruit 1306 library. Really difficult. Ive been searching high and low for almost 3 months now. But nothing seems to work. However my arduino uno rev. 3, my ssd1306 oled and the DHT22 sensor. All work fine since ive tested them all by using example codes shown in the Arduino IDE.
    Programming in c seems too much to learn and too many things to take care of @ once


    2 years ago

    I would like to know that if this particular display has an over-voltage protection.

    I applied 5V instead of 3.3V

    1 reply

    2 years ago

    I've been playing with these displays for months, but have never taken the opportunity to play with all of the features. Thanks so much for this Instructable!

    1 reply