You’ve likely heard about SSD1306 or SSD1315 organic light-emitting diode (OLED) displays. These monochrome screens are typically in a similar price range as character displays, providing a more aesthetic appeal. More significantly, they provide a truly graphical interface for an embedded device.
The OLED displays are available in ready-to-use breakout boards that can easily push into nearly any PCB. Rather than simple characters and numbers, OLEDs can display complex graphics where every individual pixel on the screen can be user-programmed.
The SSD1306 or SSD1315 are the names of OLED driver chips. The official MicroPython repository does not have a driver library for SSD1306. Although there are forks of the same library by other developers that claim to work well with SSD1315, these claims are currently unconfirmed.
In fact, even the SSD1306 driver available under the official code repository has bugs in the I2C implementation. For our purposes, we’ll only be covering the official SSD1306 driver library with a demonstration of its SPI implementation, which has proven to work.
SSD1306 is a single-chip CMOS OLED/PLED driver. It can manage a 128×64 dot-matrix graphic display. It’s designed to control common-cathode OLED panels. The chip comes with several built-in features, such as 256-step brightness control, display RAM, oscillator, and contrast control. The built-in features of the SSD1306 chip mean there are no additional requirements for a controller or a block to interface with the OLED screen in a breakout board.
The SSD1306 driver chip is used to control monochromatic OLED screens with a resolution of 128×64 (0.96″), 128×32 (0.91″), or smaller OLED screens. The OLED driver uses I2C and SPI interfaces to connect with a microcontroller or microcomputer. Some breakout boards only use the I2C interface, while others offer both interfaces.
The I2C implementation of MicroPython SSD1306 OLED driver library has runtime errors, so it’s worth getting an SSD1306 OLED display module with an onboard SPI interface. The SSD1306 OLED display module can display highly complex graphics, including text, bitmap images, and animations.
These OLED displays are popular for designing wearables and IoT devices.
The SSD1306 OLED display
OLED displays for embedded devices come in two standard sizes:
- 0.91″ with 128×32 screen resolution
- 0.96″ with 128×64 screen resolution.
The displays are monochrome blue, monochrome white, or yellow-blue. The OLED display module can have a 3-pin/4-pin port, which is only for the I2C interface, or a 7-pin interface for either the 3-wire SPI, 4-wire SPI, or I2C interface.
The SSD1306 module that we’ll test in the tutorial is a 0.96″ screen with a resolution of 128×64. It is monochromatic yellow-blue in color.
The module here has a 7-pin interface, which allows interfacing with any microcontroller or SBC via a 3-wire SPI, 4-wire SPI, and I2C interface.
The 7-pin OLED display has this pin configuration:
Interfacing SSD1306 with ESP8266
The SSD1306 OLED module can be interfaced with ESP8266 using an I2C or SPI bus. Both the SPI hardware and software can be used.
To interface with the I2C bus, connect the SCL and SDA pins on the SSD1306 OLED with ESP8266’s GPIO5 and GPIO4, respectively. The power supply and ground to the OLED module can be supplied through 3V out and ESP8266’s GND, respectively.
Currently, the I2C implementation of the SSD1306 OLED driver library has runtime errors for the ETIMEOUT. Therefore, it’s recommended to use SPI for interfacing the OLED module.
As mentioned, the hardware and software SPI can be used. The SPI software can use any output-capable pins for the SCK, MOSI, RST, DC, and CS. These circuit connections can be used for interfacing SSD1306 using the SPI hardware and software.
Interfacing SSD1306 with ESP32
The SSD1306 OLED display can be interfaced with ESP32 using the I2C and the SPI hardware and software.
To interface via the I2C, connect the SCL and SDA pins on the SSD1306 OLED with ESP32’s GPIO22 and GPIO21, respectively. The power supply and ground to the OLED module can be supplied via 3V out and ESP32’s GND, respectively.
Again, the I2C implementation of the SSD1306 OLED driver library has the runtime error, ETIMEOUT. So, it’s recommended to use the SPI for interfacing the OLED module. The SPI hardware and software can be used. The software SPI can use any output-capable pins for the SCK, MOSI, RST, DC and CS.
The circuit connections can be used for interfacing SSD1306 using both the SPI hardware and software.
MicroPython SSD1306 driver
The SSD1306 OLED driver library is now part of the standard MicroPython. The library can be found here.
Here’s the source code for this library:
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces
from micropython import const
# 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_IREF_SELECT = const(0xAD)
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)
# Subclassing FrameBuffer provides support for graphics primitives
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)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
for cmd in (
SET_DISP, # display off
# address setting
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE, # start at line 0
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
self.height – 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
0x02 if self.width > 2 * self.height else 0x12,
# timing and driving scheme
0x22 if self.external_vcc else 0xF1,
0x30, # 0.83*Vcc
0xFF, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
0x30, # enable internal IREF during display on
# charge pump
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01, # display on
): # on
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def rotate(self, rotate):
self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
self.write_cmd(SET_SEG_REMAP | (rotate & 1))
x0 = 0
x1 = self.width – 1
if self.width != 128:
# narrow displays use centred columns
col_offset = (128 – self.width) // 2
x0 += col_offset
x1 += col_offset
self.write_cmd(self.pages – 1)
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b”\x40″, None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp = 0x80 # Co=1, D/C#=0
self.temp = cmd
def write_data(self, buf):
self.write_list = buf
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
The SSD1306 driver code
The SSD1306 OLED driver begins by importing the const class from the MicroPython module and framebuf module. The const class is used for defining the class member constants. The framebuf is a module designed for manipulating the frame buffer, like the bitmap images.
This is followed by a declaration of the constants for the SSD1306 registers. The constants are assigned the hexadecimal addresses of the respective SSD1306 registers.
Next, the SSD1306 class is defined, which takes the frame buffer as the only parameter. The constructor function of the SSD1306 class takes the width of the display in pixels, the height of the display in pixels, and the external supply pin as parameters. These arguments are used to determine the number of pages over the screen, creating a buffer object that can store the frame buffer for each page.
The SSD1306 class includes these methods…
- init_display(): initializes the display, setting a number of parameters such as the horizontal resolution, column address, display clock, contrast, and RAM settings.
- poweroff(): turns off the display.
- poweron(): turns on the display.
- contrast(): sets the contrast of the display.
- invert(): inverts the color of the text.
- rotate(): rotates the display.
- show(): shows the display screen — as this method is called, the content written to the OLED RAM is displayed on the OLED screen.
The driver uses various methods from the FrameBuffer class of the framebuf module to write the text or draw shapes on the OLED screen.
The following methods of the FrameBuffer class are useful in manipulating the text and shapes on the SSD1306 OLED.
- FrameBuffer.fill(c): fills the entire frame buffer with a specified color. The SSD1306 OLED driver uses this method to fill the OLED screen with a monochromatic color.
- FrameBuffer.pixel(x, y[, c]): gets or sets the color of a specified pixel. The position of pixel is passed as arguments ‘x’ and ‘y.’ If used to set the color of the pixel, ‘c’ is the color that’s passed as an argument.
- FrameBuffer.hline(x, y, w, c): draws a horizontal line of width ‘w,’ starting from the pixel positions ‘x’ and ‘y.’ The horizontal line is filled with the color ‘c.’
- FrameBuffer.vline(x, y, h, c): draws a vertical line with the height, ‘h,’ starting from the pixel positions, ‘x’ and ‘y.’ The vertical line is filled with the color ‘c.’
- FrameBuffer.line(x1, y1, x2, y2, c): draws a line between the pixels positioned at the (x1, y1) and (x2, y2). The line is filled with the color, ‘c.’
- FrameBuffer.rect(x, y, w, h, c): draws a rectangular of the width ‘w’ and the height ‘h,’ starting from the pixel positions, ‘x’ and ‘y.’ The borders of the rectangle are filled with the color ‘c.’
- FrameBuffer.fill_rect(x, y, w, h, c): draws a rectangular box with the width ‘w’ and the height ‘h,’ starting from the pixel positions, ‘x’ and ‘y.’ The rectangular box is filled with the color ‘c.’
The monochromatic OLED display can only have two colors for any pixel. It’s either filled with the monochrome color of the OLED or the inverted color. Therefore, the value of the argument c can be only ‘0’ or ‘1.’
The ‘0’ indicates the actual monochrome color, and the ‘1’ indicates the inverted color. The pixel position can be specified as 0~128, along the x-axis and 0~64 along the y-axis in the case of a 128×64 OLED resolution.
- FrameBuffer.text(s, x, y[, c]): writes a string of text on the OLED display. The text starts printing on the screen from the pixel positions, ‘x’ and ‘y.’ The color of the text can be set to ‘c.’
The class definition of the SSD1306 is followed by the class definition for another class, the SSD1306_I2C(). This class has a constructor function and two methods — write_cmd to write the OLED command and write_data to write the display data.
Currently, the execution of this class is giving a runtime error.
The class, SSD1306_SPI(), is defined by implementing the SPI bus for the SSD1306 OLED. This class has a constructor function and two methods — write_cmd to write the OLED command and write_data to write the display data.
Uploading the OLED driver with uPyCraft IDE
To upload the OLED driver to ESP8266 or ESP32 using uPyCraft IDE, connect the board with a computer using a USB cable. Ensure the MicroPython firmware to ESP8266/ESP32 is uploaded.
Select the COM port ESP8266/ESP32 to ensure it’s connected by navigating to the Tools-> Serial and choose your MicroPython board by navigating to the Tools->board. Next, connect the board to uPyCraft IDE by clicking the connect button.
Once the board is connected, you’re able to browse the boot.py from the device folder. To add the SSD1306 OLED driver, create a new file by navigating to the File->New or cby licking the New button.
Copy the SSD1306 OLED driver library code and save the file as ssd1306.py. Finally, click the Download and Run button to upload the library file into ESP8266/ESP32.
Once the file is uploaded, it can be imported into the main script and used to interface the OLED display.
Uploading the OLED driver with Thonny IDE
If you are using Thonny IDE, create a new file by navigating to File->New. Copy the SSD1306 OLED driver library code here and save the file as ssd1306.py. To upload the file to ESP8266/ESP32, navigate to the Device -> Upload current script with the current name.
To check if the file is successfully uploaded to ESP8266/ESP32, type this command in the shell:
It will return a list of files uploaded to the board. The list should include ssd1306.py.
Displaying sensor data on SSD1306
The SSD1306 OLED display can be used with ESP8266/ESP32 to display sensor data, show network parameters, or display insights that are received from a remote IoT server.
The text() method of the FrameBuffer class requires a string argument. The values returned by the sensors, the values of network parameters, or IoT insights are often integers or floating-point values. These values must first be converted to a string using str() function of cPython/MicroPython.
This is a valid example of converting an integer or a floating-point value from a sensor to a string.
temp = 25
temp_string = str(temp)
The string object can be directly passed as an argument to the text() method to display on the OLED.
oled.text(temp_string, 0, 0)
Displaying text on SSD1306 with ESP8266
Now let’s display text messages on the SSD1306 OLED. In this tutorial, the SSD1306 is interfaced with ESP8266.
1. ESP8266 x1
2. SSD1306 OLED with the SPI interface x1
3. Connecting wires/Jumper wires
We have connected the SSD1306 OLED with ESP8266 using the SPI software. The OLED’s DC, RST, and CS pins are connected to GPIO4, GPIO5, and GPIO15, respectively. The OLED module’s D0 and D1 pins are connected to GPIO14 and GPIO13. The OLED module’s VCC and GND pins are connected to ESP8266’s 3V out and GND.
The MicroPython script
from machine import Pin, SoftSPI
from time import sleep
spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
dc = Pin(4) # data/command
rst = Pin(5) # reset
cs = Pin(15) # chip select, some modules do not have a pin for this
oled = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs)
oled.text(‘Welcome to’, 0, 0)
oled.text(‘ENGINEERSGARAGE’, 0, 16)
oled.text(‘Have fun with’, 0, 35)
oled.text(‘MicroPython’, 0, 45)
How it works
The OLED display module is interfaced with ESP8266 using the SPI software. The methods of the ssd1306() class are used to control the OLED, while methods from the framebuf module are used to print text messages to the OLED.
Save the above code as main.py and upload the file to ESP8266 by clicking Download and Run button. The code begins by importing the Pin and SoftSPI class from the machine module. The ssd1306 module is imported, which must first be uploaded to ESP8266. The sleep class from the time module is also imported.
An object, spi, is created from the SoftSPI class, which is instantiated with a baud rate of 500000 Hz, polarity 1, phase 0, as well as the SCK pin to the GPIO14 pin and the MOSI pin to GPIO13.
The MISO pin is not used with the OLED module. The GPIO12 is assigned the MISO for the SPI software. The DC, RST, and CS pins are set to GPIO4, GPIO5, and GPIO15, respectively. These pin constants are used for creating an object of the SSD1306_SPI class oled. The class object is instantiated to a screen width of 128 pixels and a height of 64 pixels. The text is printed to the SSD1306 display by calling the oled.text() method. It’s shown on the screen when the oled.show() method is called.
In this MicroPython script, two text messages are displayed on the OLED screen at a two-second interval. The sleep() method is used to provide the delay between the two messages.
Filed Under: Tech Articles