SSD1306 Raspberry Pi Pico

Hello Hobbyist, Today I’m going to demonstrate SSD1306 with Raspberry Pi Pico. Pico is recently launched and it’s libraries are not that developed. But today we are going to perform connecting this Oled to pico via SPI communication. All code and libraries are given so don’t worry. Let’s Begin then


Raspberry Pi Pico is development board which runs basically on MicroPython. It is similar to other microcontrollers like Arduino, NodeMCU and various other. But it has some more features as compared to them. It is built out of RP2040. It has 264KB of RAM and 2MB of FLASH space. You can also program PICO in C/C++ using arduino IDE. For that just include this link in your Arduino IDE preferences options. Then go to Boards manager and search Raspberry pi pico and download this Boards.  

Using this you can use all type of communication like SPI(2), I2C(2),UART, Serial. So is you are well good in python then you just try this board for sure. The library which you have to use for intalling firmware onto board can easily be done. By using Thonny IDE.

For updating the firmware or installing just press the button(BOOTSEL) on PICO and while pressing connect usb to pico. Once your board is detected in Thonny IDE you can release the button. Now click ‘Install or Upgrade Firmware” option available on bottom right.

Material Required:

  • raspberry Pi Pico
  • SSD1306 SPI OLED
  • Jumper Wires
  • Breadboard
  • Thonny IDE installed on system

Circuit Design:


Open Thonny IDE and then Paste this code in a new file:

 # MicroPython SSD1306 OLED driver, I2C and SPI interfaces  
 from micropython import const  
 import framebuf  
 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)  
 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)  
   def init_display(self):  
     for cmd in (  
       SET_DISP | 0x00, # off  
       # address setting  
       0x00, # horizontal  
       # resolution and layout  
       SET_DISP_START_LINE | 0x00,  
       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  
       # display  
       0xFF, # maximum  
       SET_ENTIRE_ON, # output follows RAM contents  
       SET_NORM_INV, # not inverted  
       # charge pump  
       0x10 if self.external_vcc else 0x14,  
       SET_DISP | 0x01,  
     ): # on  
   def poweroff(self):  
     self.write_cmd(SET_DISP | 0x00)  
   def poweron(self):  
     self.write_cmd(SET_DISP | 0x01)  
   def contrast(self, 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(self.pages - 1)  
 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  
     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)  

Save this file in your pico with name ‘’. This is not the main code but it helps up to run the main code or you can say that it is library which is used.

Now the main code work like this. First we import SPI and Pin functions from machine module. Then we import SSD1306_SPI function from library saved above (Use SSD1306_I2C for I2C communication). After it we import module which contains all processing that we gonna do to display things on oled. Alsowe import some time modules for delay in program.

from machine import Pin, SPI  
 from ssd1306 import SSD1306_SPI  
 import framebuf  
from time import sleep
from utime import sleep_ms

Now we define to which¬† SPI port we gonna hook our OLED i.e., SPI(0). PIns for SPI are 18,19,16,17; SCK, MOSI, MISO, CS respectively. Pins for dc and rst can be defined manually in SSD1306 OLED initialization. Also, we create an object ‘oled’ which stores the initialization of our OLED

spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
oled = SSD1306_SPI(128, 64, spi, Pin(17),Pin(20), Pin(16)) 

after this in the while loop we apply to try and except method to make the loop continue until a keyboard interrupt is encountered. So display will show text ‘HELLO WORLD’ scrolling down and right at the same time until we give keyboard interrupt.


Here is the complete code. Just have a look and remember to save it with the name ‘’ in pico so that it runs whenever pico gets restarted or powered on.

from machine import Pin, SPI
from ssd1306 import SSD1306_SPI
import framebuf
from time import sleep
from utime import sleep_ms

spi = SPI(0, 100000, mosi=Pin(19), sck=Pin(18))
oled = SSD1306_SPI(128, 64, spi, Pin(17),Pin(20), Pin(16))
#oled = SSD1306_SPI(WIDTH, HEIGHT, spi, dc,rst, cs) use GPIO PIN NUMBERS
while True:
        for i in range(40):
            for j in range(56):
                oled.text("HELLO WORLD",i,j)
    except KeyboardInterrupt:

With this, we have successfully completed our tutorial on SSD1306 raspberry Pi Pico using SPI communication. I hope you liked it. If you come through and problem then let me know in the comment down below.