Interfacing SSD1306 OLED Display with Raspberry Pi Pico
How to interface an SSD1306 OLED display with a Raspberry Pi Pico board
Overview: RPI Pico with OLED Display
In this tutorial, we will learn Interfacing SSD1306 OLED Display with Raspberry Pi Pico microcontroller. OLED displays are known for their attractive appearance and pixel density, making them ideal for displaying small-level graphics.
We will be using the 0.96-inch I2C OLED display. As it only requires two wires for interfacing. The Raspberry Pi Pico, which comes with an RP2040 microcontroller, has two pairs of I2C pins. Any of these I2C pins can be used to interface with the SSD1306 OLED display.
The code for this tutorial will be written in MicroPython and requires the SSD1306 driver code. Once the driver code is written, we can display any text or graphics on the OLED display. In this example, we will display the analog voltage value from a potentiometer on the OLED display. However, before starting this guide, we recommend going through the Raspberry Pi Pico Getting Started Guide and familiarizing yourself with the use of I2C pins.
Components Required
You can easily purchase the required components from the links provided below.
S.N | COMPONENTS NAME | QUANTITY | PURCHASE LINKS |
---|---|---|---|
1 | Raspberry Pi Pico Basic Starter Kit | 1 | Amazon | AliExpress |
2 | Raspberry Pi Pico | 1 | Amazon | AliExpress |
3 | 0.96" I2C OLED Display | 1 | Amazon | AliExpress |
4 | Potentiometer | 1 | Amazon | AliExpress |
5 | Breadboard | 1 | Amazon | AliExpress |
6 | Jumper Wires | 10 | Amazon | AliExpress |
SSD1306 OLED Display
The SSD1306 OLED display is popular for microcontroller projects due to its attractive viewing angle and high pixel density. This specific model is a 0.96/1.3-inch blue OLED display module that can be interfaced with any microcontroller using SPI or I2C protocols. It has a resolution of 128×64 and the package includes a display board, a display, and a 4-pin male header pre-soldered to the board.
OLED technology, short for Organic Light-Emitting Diode, is a self-light-emitting technology that uses a thin, multi-layered organic film placed between an anode and cathode. Unlike LCD technology, OLED does not require a backlight. That makes it a popular choice for displays.
Interfacing SSD1306 OLED Display with Raspberry Pi Pico
To interface the SSD1306 OLED display with the Raspberry Pi Pico microcontroller. We will use the I2C protocol. The Raspberry Pi Pico board has two pairs of I2C pins that can be used for interfacing. To learn more about using I2C pins and applications, refer to our Raspberry Pi Pico I2C guide.
In this example, we will be feeding an analog voltage input from a potentiometer to the GP28 analog pin of the Raspberry Pi Pico. Then displaying the analog voltage on the OLED screen. The circuit is simple and easy to use, as it only requires connecting the SDA and SCL pins of the OLED display to the GP21 and GP20 pins of the Pico, respectively.
OLED Display Raspberry Pi Pico Code
We will be using MicroPython code to interface the OLED display with the Pico board. You can either use Thonny IDE or the uPyCraft IDE for programming. Here I am using Thonny IDE. So, the Thonny IDE requires an SSD1306 driver code. which must be written before displaying any text or graphics on the OLED display.
The programming process is divided into two main parts. writing the SSD1306.py driver code and the main.py code. The SSD1306.py code should be written and uploaded first, and then the main.py code can be uploaded and run.
SSD1306.py
The code for the SSD1306.py file is responsible for setting up the OLED display and configuring it to work with the Raspberry Pi Pico. It includes library imports, initialization commands, and functions for displaying text and graphics on the OLED screen.
So in Thonny IDE, create a new file. Copy the following code & save the file by the name ssd1306.py.
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const import framebuf # 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_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 # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html class SSD1306(framebuf.FrameBuffer): 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) 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.width > 2 * self.height 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 poweron(self): self.write_cmd(SET_DISP | 0x01) 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_data(self.buffer) 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) self.write_list = [b"x40", None] # Co=0, D/C#=1 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_data(self, buf): self.write_list[1] = buf self.i2c.writevto(self.addr, self.write_list) class 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 import time self.res(1) time.sleep_ms(1) self.res(0) time.sleep_ms(10) self.res(1) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(1) self.cs(0) self.spi.write(buf) self.cs(1)
main.py
The main.py code, on the other hand, is where the main logic of the program resides. This code reads the analog voltage value from the potentiometer and displays it on the OLED screen. It also includes commands for controlling the OLED display and updating the displayed information.
Open a new tab again in the Thonny IDE. Copy the following code and paste it on the Thonny IDE new Window. Save the file by the name main.py.
# Display Image & text on I2C driven ssd1306 OLED display from machine import Pin, I2C from ssd1306 import SSD1306_I2C import framebuf import machine import utime sensor_temp = machine.ADC(28) conversion_factor = 3.3 / (65535) WIDTH = 128 # oled display width HEIGHT = 64 # oled display height i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=200000) # Init I2C using pins GP8 & GP9 (default I2C0 pins) print("I2C Address : "+hex(i2c.scan()[0]).upper()) # Display device address print("I2C Configuration: "+str(i2c)) # Display I2C config oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display # Raspberry Pi logo as 32x32 bytearray buffer = bytearray(b"x00x00x00x00x00x00x00x00x00x00x00x00x00|?x00x01x86@x80x01x01x80x80x01x11x88x80x01x05xa0x80x00x83xc1x00x00Cxe3x00x00~xfcx00x00L'x00x00x9cx11x00x00xbfxfdx00x00xe1x87x00x01xc1x83x80x02Ax82@x02Ax82@x02xc1xc2@x02xf6>xc0x01xfc=x80x01x18x18x80x01x88x10x80x00x8c!x00x00x87xf1x00x00x7fxf6x00x008x1cx00x00x0c x00x00x03xc0x00x00x00x00x00x00x00x00x00x00x00x00x00") while True: reading = sensor_temp.read_u16() * conversion_factor # Load the raspberry pi logo into the framebuffer (the image is 32x32) fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB) # Clear the oled display in case it has junk on it. oled.fill(0) # Blit the image from the framebuffer to the oled display oled.blit(fb, 96, 0) # Add some text oled.text("ADC: ",5,8) oled.text(str(round(reading,2)),40,8) # Finally update the oled display so the image & text is displayed oled.show()
Testing
Once the code is uploaded and running. The OLED display will immediately start showing the Raspberry Pi logo along with the analog voltage value from the potentiometer. By rotating the potentiometer knob, you can see the OLED display showing different values. Overall, interfacing an SSD1306 OLED display with a Raspberry Pi Pico is a straightforward process with the help of this MicroPython code.
Together, these two code files work in harmony to create a functioning program that can read an analog voltage value and display it on an OLED screen. This is a simple but powerful example of how the Raspberry Pi Pico can be used to interface with various peripheral devices and sensors.
Conclusion
In conclusion, interfacing the SSD1306 OLED display with the Raspberry Pi Pico is a simple process using MicroPython code. With the OLED display, you can display text, graphics, and sensor data in a clear and attractive way.