Last Updated: August 13, 2021
Originally Published: August 10, 2018
Skill Level: Intermediate
Table Of Contents
Introduction
In this tutorial, you will learn how to connect an SSD1306 based OLED display module to an Arduino Uno using the I2C bus interface and display text and graphics on the module’s screen. A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform. If you are new to the Arduino platform or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno tutorial before proceeding with this one. Detailed versions of all source code used in this tutorial are available on GitHub for your reference.
What Is Needed
- Linux, macOS, Or Windows Based Computer With A USB Port
- Arduino IDE
- Arduino Uno (available on Arduino and SparkFun) With Compatible USB Cable
- SSD1306 OLED Display Module With I2C Interface (available on Amazon and Adafruit)
Background Information
Organic light emitting diode (OLED) displays typically are thinner, more efficient, and provide a better viewing experience in comparison to other types of displays. They are increasingly being used for TVs, mobile phones, and other consumer electronics.
An SSD1306 display module consists of a 128×64 OLED display connected to the SSD1306 OLED Display Driver IC by Solomon Systech. The SSD1306 driver is capable of communicating with microcontrollers through I2C, SPI, and 6800/8000 parallel bus interfaces.
My development board is the Arduino Uno R3 and my display module is the SunFounder 0.96″ Inch Blue I2C IIC Serial 128×64 OLED LCD LED SSD1306 Module (which includes the I2C based 4 pin wiring connector). If you are using different Arduino or display models, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.
Building The Circuit
Before connecting the display module to your Arduino board, disconnect the Arduino from power and your computer in order to avoid accidental damage during wiring.
Some displays have their VCC and GND pin placements reversed, so make sure you connect them properly. In addition, the 4 pin I2C connector that came with my display has GND and VCC connected to the brown and white wires, not black and red as expected.
Connect the VCC and GND pins of the display module to the 5V and GND power pins respectively on the Arduino board.
For the I2C serial bus connection, connect the SDA and SCL pins of the display module to the SDA and SCL pins respectively on the Arduino. Be aware that not all Arduino boards have the analog pins A4 and A5 connected to the I2C port. I am using the SDA and SCL pins on the Digital row for this reason. Your particular display may require additional connections as well. See your display’s documentation for further information.
You can now connect your Arduino to your computer with the USB cable.
Writing The Software
I researched various libraries that can be used with SSD1306 display modules and settled on the U8g2 graphics library by olikraus because of its popularity and it continues to be maintained by the author. It has the capability of drawing graphics, e.g. lines, boxes, circles, triangles, individual pixels, etc. along with drawing text using many different fonts and sizes.
The U8g2 library also comes with the U8x8 text library as well that uses a much smaller memory footprint as long as you do not need to support graphics or different font sizes with your display. It is only capable of displaying text using an 8×8 fixed sized font, but you can select among different font styles. When using the U8x8 library, the screen is divided into a grid of 16 columns by 8 rows reflecting the fixed font size.
Install the U8g2 library within the Library Manager of the Arduino IDE.
We will first create a simple sketch using the U8x8 text library to display text on the screen. Create and save a new sketch named DisplayText with the code shown below.
#include <Arduino.h> #include <U8x8lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif #define DISPLAY_I2C_ADDRESS 0x3C // U8x8 text library constructor for SunFounder OLED SSD1306 Display Module. // Use the constructor compatible with your display module. // See https://github.com/olikraus/u8g2/wiki/u8x8setupcpp for constructor list. // 128x64 display with hardware I2C and no reset pin U8X8_SSD1306_128X64_NONAME_HW_I2C display(/* reset = */ U8X8_PIN_NONE); void setup() { display.setI2CAddress(DISPLAY_I2C_ADDRESS * 2); // required if display does not use default address of 0x3C display.begin(); // initialize U8x8 text library for selected display module } void loop() { display.setFont(u8x8_font_victoriabold8_r); // set text font (8x8 pixels) display.drawString(0, 0, "Hello, Arduino!"); // print text to screen (column 0, row 0) delay(2000); // update screen every 2 seconds }
Line 2 loads the U8x8 text library.
Lines 3-8 load the SPI and I2C hardware libraries if needed. Please note that although we are specifically using an I2C based display module here, I included SPI just in case someone is using an SPI based display module instead.
Line 16 sets up the U8x8 library for use with our particular display using a constructor which is defined by the library to delineate among the various display types. The display
identifier is the name that we will use throughout our program when referring to the U8x8 library operations we are applying to our display. This can be a different name if you choose, but make sure to change all of the references to it throughout the rest of the code if you do change it. It is very important to use a constructor that is compatible with your particular display module. Please select your constructor from the U8x8 constructor list provided by the library author. The U8X8_SSD1306_128X64_NONAME_HW_I2C
library constructor I chose is for a generic SSD1306 display module with a size of 128×64 pixels using a hardware based I2C bus interface. I also used the U8X8_PIN_NONE
identifier for the reset pin argument as my display does not have a separate reset pin.
Line 19 sets the library to use a specific I2C address, defined by DISPLAY_I2C_ADDRESS
on line 10. The library’s reference manual states that the setI2CAddress()
method’s argument is the display’s address multiplied by two. I don’t know why the factor of 2 is required. This line is only needed if your display does not use the default address of 0x3C.
Line 20 initializes the U8x8 library for use with your display as chosen by the constructor.
Line 24 sets the font to use for our display text.
Line 25 draws the text to the screen beginning in column 0 and row 0.
Line 26 delays each run of the loop by 2 seconds (arbitrary) since we are only using static text and do not need quick updates.
Upload the sketch to the Arduino and you should see Hello, Arduino!
on your display as shown below.
Play around with changing the text and its location on the screen. The reference manual shows all of the functions available for use with the U8x8 text library.
Next, let’s add some graphics. This sketch will use the U8g2 graphics library to display text and graphics on the screen. Create and save a new sketch named DisplayGraphics with the code shown below.
#include <Arduino.h> #include <U8g2lib.h> #ifdef U8X8_HAVE_HW_SPI #include <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include <Wire.h> #endif #define DISPLAY_I2C_ADDRESS 0x3C // U8g2 graphics library constructor for SunFounder OLED SSD1306 Display Module // Use the constructor compatible with your display module. // See https://github.com/olikraus/u8g2/wiki/u8g2setupcpp for constructor list. // 128x64 display with hardware I2C, no mirroring or rotation, and no reset pin U8G2_SSD1306_128X64_NONAME_1_HW_I2C display(/* rotation/mirror = */ U8G2_R0); // Draws LED schematic symbol with size of 60x24 void drawLED(int centerX, int centerY) { display.drawTriangle(centerX - 10, centerY + 12, centerX + 10, centerY + 2, centerX - 10, centerY - 8); display.drawLine(centerX + 10, centerY - 8, centerX + 10, centerY + 12); display.drawLine(centerX - 30, centerY + 2, centerX - 10, centerY + 2); display.drawLine(centerX + 10, centerY + 2, centerX + 30, centerY + 2); display.drawLine(centerX, centerY - 6, centerX + 3, centerY - 10); display.drawTriangle(centerX + 6, centerY - 12, centerX + 6, centerY - 8, centerX + 2, centerY - 12); display.drawLine(centerX - 22, centerY + 8, centerX - 18, centerY + 8); display.drawLine(centerX - 20, centerY + 6, centerX - 20, centerY + 10); display.drawLine(centerX + 18, centerY + 8, centerX + 22, centerY + 8); } void setup() { display.setI2CAddress(DISPLAY_I2C_ADDRESS * 2); // required if display does not use default address of 0x3C display.begin(); // initialize U8g2 graphics library for selected display module } void loop() { int displayWidth = display.getDisplayWidth(); // width of display int displayHeight = display.getDisplayHeight(); // height of display display.firstPage(); // enable display rendering using page buffering do { // Draw text display.setFont(u8g2_font_ncenB10_tr); // set text font (11 pixel height) display.drawStr((displayWidth - display.getStrWidth("Woolsey")) / 2, 11, "Woolsey"); // center text at top of screen display.drawStr((displayWidth - display.getStrWidth("Workshop")) / 2, 61, "Workshop"); // center text at bottom of screen // Draw LED symbol drawLED(displayWidth / 2, (displayHeight / 2) - 2); // place symbol in middle of screen (between text) } while (display.nextPage()); // render current page delay(2000); // update screen every 2 seconds }
Line 2 loads the U8g2 graphics library.
Line 16 sets up the U8g2 library in a similar way to the U8x8 based sketch we used previously, but we will need a different constructor this time. Select the appropriate constructor from the U8g2 constructor list. The U8G2_SSD1306_128X64_NONAME_1_HW_I2C
library constructor I chose is for a generic SSD1306 display module with a size of 128×64 pixels, a 1 page display memory buffer, and using a hardware based I2C bus interface. The U8G2_R0 argument specifies no rotation or mirroring.
Lines 37-38 get the display’s width and height for use in centering items in the display.
Lines 39 and 48 enable page buffering to render the display.
Lines 42-44 sets the font and displays the Woolsey
and Workshop
text at the top and bottom of the screen respectively.
Line 47 draws an LED schematic symbol in the middle of the screen. The operations required to draw the symbol are defined in the drawLED()
function defined in lines 19-29. This function draws lines and triangles to assemble the LED graphic.
Upload and run the sketch on the Arduino and you should see the text along with an LED schematic symbol being displayed on your screen as shown below.
Again, play around and draw some of your own shapes to the screen. The reference manual shows all of the functions available for use with the U8g2 graphics library.
Summary
In this tutorial, we learned how to connect an SSD1306 based OLED display module to an Arduino Uno development board using the I2C bus interface and draw text and graphics on the display using the U8x8 (text only) and U8g2 (graphics) libraries.
The final source code for this tutorial is located on GitHub.
Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.
Hi,
In my case it’s not working… I saw, you didn’t declare an I2C address, is there no need for that? When I use the Adafruit library, with the correct I2C address, then it’s working. But I don’t want to use the Adafruit library.
Thanks!
Good catch. Most SSD1306 based displays use 0x3C as their I2C address. The U8g2 library assumes this is the case. I did some research and found that the library’s I2C address can be changed by placing the following statement within the sketch’s setup() function before the display’s begin method().
display.setI2CAddress(DISPLAY_I2C_ADDRESS * 2);
Replace DISPLAY_I2C_ADDRESS with the actual I2C address of your display, e.g. 0x3D. This statement is required for working with displays that do not use the default address value of 0x3C. The library’s reference manual states that the method’s argument is the display’s I2C address multiplied by two. I don’t know why.
I assume your display does not use the default address of 0x3C. Give the new statement above a try to see if it resolves your issue. I just tested it on my display (using 0x3C) and it worked.
I will try to update the tutorial with this new information. Thank you for bringing it to my attention.
@John Woolsey
Thanks a lot, it worked! I also did some research and found the
display.setI2CAddress(DISPLAY_I2C_ADDRESS);
But without the factor two. This *2 important, that’s all I needed.
Thanks!
You are very welcome! Glad it worked for you.