Raspberry Pi Pico 2/MicroPython display on SPI SSD1306 OLED

Last post show how to install MicroPython ssd1306 driver on Raspberry Pi Pico 2 in Thonny, and display on 0.96 inch 128x64 SSD1315 I2C OLED (SSD1306 compatible). Here is how to using the ssd1306 driver, display on 0.96 inch 128x64 SSD1306 SPI OLED.


Connection:


SPI SSD1306 OLED    Raspberry Pi Pico 2
========================================
- D/C		    GP13
- RST		    GP12
- SDA		    GP11 (mosi)
- SCL		    GP10 (sck)
- VCC		    3V3
- GND		    GND
		    GP9 (dummy cs) - No connection
		    GP8 (miso) - No connection
Notice that my SPI SSD1306 OLED is 6 pins version, no cs on board. But it's required in ssd1306 driver, so I have to assign dummy GP9 for it. Also GP8 is the SPI MISO, not use in this case.

Exercise Code:

mpyPico2_SSD1306_spi_hello.py, a simple exercise to say Hello.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on 0.96" 128x64 SSD1306/SSD1306 SPI OLED.
(It should work on both Pico/Pico 2)

Install ssd1306 @ micropython-lib SSD1306 OLED driver (ver: 0.1.0)
in Thonny > Tools > Manager packages...

OLED is connected to SPI(1)
SCK  - GP10
MOSI - GP11
MISO - GP8  (Not use)

"""
import sys
import os
import time
import ssd1306

DISP_WIDTH=128
DISP_HEIGHT=64

# notice that:
# parameter cs have to be provided to ssd1306.SSD1306_SPI(),
# so I assign a GP8 as dummt cs, no need connect it.
dc  = machine.Pin(13)
res = machine.Pin(12)
cs  = machine.Pin(9)   #dummy (any un-used pin), no connection

print("====================================")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("====================================")

package_info = ssd1306.__name__
try:
    package_info = package_info + " ver:" + ssd1306.__version__
except AttributeError as exc:
    print("AttributeError!", exc)

print("ssd1306 package: ", package_info)
print("====================================")
oled_spi = machine.SPI(1)
print("Using SPI:", oled_spi)
print()

try:
    oled_ssd1306 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
    oled_ssd1306.fill(1)
    oled_ssd1306.show()
    time.sleep(1)
    oled_ssd1306.fill(0)
    oled_ssd1306.show()
    time.sleep(1)
    
    #oled_ssd1306.rect(0, 0, DISP_WIDTH, DISP_HEIGHT, 1)
    oled_ssd1306.rect(1, 1, DISP_WIDTH-2, 25, 1)
    
    oled_ssd1306.text('Hello, World!', 5, 5, 1)
    oled_ssd1306.text(oled_ssd1306.__qualname__, 5, 15, 1)
    oled_ssd1306.show()
    
    """
    scroll() shift the contents of the FrameBuffer by the given vector.
    This may leave a footprint of the previous colors in the FrameBuffer.
    https://docs.micropython.org/en/latest/library/framebuf.html#framebuf.FrameBuffer.scroll
    """

    while True:
        for y in range(9):
            oled_ssd1306.scroll(0, y)
            oled_ssd1306.show()
            time.sleep(0.2)
        for y in range(9):
            oled_ssd1306.scroll(0, -y)
            oled_ssd1306.show()
            time.sleep(0.2)       

except OSError as exc:
    print("OSError!", exc)
    if exc.errno == errno.ENODEV:
        print("No such device")
        
print("~ bye ~")


mpyPico2_SSD1306_spi_hello_fb.py, display a bitmap in framebuffer to move around.
"""
MicroPython/Raspberry Pi Pico exercise
display on 0.96" 128x64 SSD1306/SSD1306 SPI OLED, using FrameBuffer.
(It should work on both Pico/Pico 2)

Install ssd1306 @ micropython-lib SSD1306 OLED driver (ver: 0.1.0)
in Thonny > Tools > Manager packages...

OLED is connected to SPI(1)
SCK  - GP10
MOSI - GP11
MISO - GP8  (Not use)

"""
import sys
import os
import time
import ssd1306
import framebuf

DISP_WIDTH=128
DISP_HEIGHT=64

dc  = machine.Pin(13)
res = machine.Pin(12)
cs  = machine.Pin(9)   #dummy (any un-used pin), no connection

print("====================================")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("====================================")

package_info = ssd1306.__name__
try:
    package_info = package_info + " ver:" + ssd1306.__version__
except AttributeError as exc:
    print("AttributeError!", exc)

print("ssd1306 package: ", package_info)
print("====================================")
oled_spi = machine.SPI(1)
print("Using SPI:", oled_spi)
print()

# Raspberry Pi logo as 32x32 bytearray
fb_width=32
buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x008\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")

# Load the raspberry pi logo into the framebuffer (the image is 32x32)
fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB)

"""
If you install another lib micropython-ssd1306 @ PyPI ssd1306 module for MicroPython (ver 0.3),
it's lack of rotate() function.
This function (rotate_screen()) is special handle it for information only, just skip rotate if:
AttributeError! 'SSD1306_I2C' object has no attribute 'rotate'
"""
def rotate_screen(rot=False):
    try:
        oled_ssd1306.rotate(rot)
    except AttributeError as exc:
        print("AttributeError!", exc)

def demo():

    oled_ssd1306.fill(0)
    oled_ssd1306.rect(0, 0, DISP_WIDTH, DISP_HEIGHT, 1)
    oled_ssd1306.text('Hello', 5, 5, 1)
    oled_ssd1306.text('coXXect', 5, 15, 1)
    oled_ssd1306.show()
    time.sleep(2)

    oled_ssd1306.blit(fb, DISP_WIDTH-fb_width, 0)
    oled_ssd1306.show()
    time.sleep(1)
    
    for i in range(DISP_WIDTH-fb_width, 0-1, -1):
        oled_ssd1306.blit(fb, i, 0)
        oled_ssd1306.show()
        time.sleep(0.1)

try:
    oled_ssd1306 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
    
    rotate_screen(False)  #oled_ssd1306.rotate(False)
    oled_ssd1306.invert(0)
    oled_ssd1306.fill(1)
    oled_ssd1306.show()
    time.sleep(1)
    oled_ssd1306.fill(0)
    oled_ssd1306.show()
    time.sleep(1)
    
    demo()
    rotate_screen(True)  #oled_ssd1306.rotate(True)
    oled_ssd1306.invert(1)
    demo()
    time.sleep(1)
    
    oled_ssd1306.invert(0)
    time.sleep(1)
    
    rotate_screen()  #oled_ssd1306.rotate(False)
    oled_ssd1306.invert(0)
    oled_ssd1306.fill(0)
    
    step_width = fb_width+2
    for i in range(0, DISP_WIDTH, step_width):
        oled_ssd1306.blit(fb, i, 0)
        oled_ssd1306.show()
        time.sleep(0.5)

except OSError as exc:
    print("OSError!", exc)   # may be no connected to I2C OLED


print("~ bye ~")


mpyPico2_SSD1306_spi_myEquPolygon.py, exercise to display Equilateral Polygon.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on 0.96" 128x64 SSD1306/SSD1306 SPI OLED.
(It should work on both Pico/Pico 2)

To draw a Equilateral Polygon

Install ssd1306 @ micropython-lib SSD1306 OLED driver (ver: 0.1.0)
in Thonny > Tools > Manager packages...

OLED is connected to SPI(1)
SCK  - GP10
MOSI - GP11
MISO - GP8  (Not use)

"""
import sys
import os
import time
import ssd1306
from array import array
import math

DISP_WIDTH=128
DISP_HEIGHT=64

# notice that:
# parameter cs have to be provided to ssd1306.SSD1306_SPI(),
# so I assign a GP8 as dummt cs, no need connect it.
dc  = machine.Pin(13)
res = machine.Pin(12)
cs  = machine.Pin(9)   #dummy (any un-used pin), no connection

print("====================================")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("====================================")

package_info = ssd1306.__name__
try:
    package_info = package_info + " ver:" + ssd1306.__version__
except AttributeError as exc:
    print("AttributeError!", exc)

print("ssd1306 package: ", package_info)
print("====================================")
oled_spi = machine.SPI(1)
print("Using SPI:", oled_spi)
print()

# === myEquPolygonClass class ===
class myEquPolygonClass:
    
    def __init__(self, num_of_ends, r):
        self.num_of_ends = num_of_ends
        self.r = r
        
        self.coords_list = []
        
        for end in range(num_of_ends):
            angle_in_radians = math.radians(360 * end/num_of_ends)
            print("degree of end", end, angle_in_radians)
            print(angle_in_radians, ":", math.sin(angle_in_radians), math.cos(angle_in_radians))
            self.coords_list.append(int(r * math.sin(angle_in_radians)))
            self.coords_list.append(int(r * math.cos(angle_in_radians)))
        
        
    def show(self, screen, offset_x=0, offset_y=0, fill=0):
        coords_array = array('h', self.coords_list)
        
        screen.poly(offset_x, offset_y,
                    coords_array,
                    1,        # color
                    fill)     # optional fill: True - fill, False - not fill
        screen.show()
# === End of myFixedPolygonClass ===

try:
    oled_ssd1306 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
    oled_ssd1306.fill(1)
    oled_ssd1306.show()
    time.sleep(1)
    oled_ssd1306.fill(0)
    oled_ssd1306.show()
    time.sleep(1)
    
    center_x = int(oled_ssd1306.width/2)
    center_y = int(oled_ssd1306.height/2)
    myEquPolygon = myEquPolygonClass(6, 30)
    
    fill = False
    while True:
        oled_ssd1306.fill(0)
        oled_ssd1306.text('num_of_ends ' + str(myEquPolygon.num_of_ends), 5, 5, 1)
        myEquPolygon.show(oled_ssd1306, center_x, center_y, fill)
        fill = not fill
        time.sleep(1)

except OSError as exc:
    print("OSError!", exc)
    if exc.errno == errno.ENODEV:
        print("No such device")
        
print("~ bye ~")


mpyPico2_SSD1306_spi_myEquPolygon2.py and mpyPico2_SSD1306_spi_myEquPolygon3.py do the same thing, to change the polygon at runtime, different in how to recreate/update myEquPolygonClass object.

mpyPico2_SSD1306_spi_myEquPolygon2.py
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on 0.96" 128x64 SSD1306/SSD1306 SPI OLED.
(It should work on both Pico/Pico 2)

To draw a Equilateral Polygon, change num_of_ends at runtime.
In this version, myEquPolygonClass object have to be re-created everytimes wanted to change parameters (num_of_ends, r).
To improve it, refer to mpyPico2_SSD1306_spi_myEquPolygon3.py.

Install ssd1306 @ micropython-lib SSD1306 OLED driver (ver: 0.1.0)
in Thonny > Tools > Manager packages...

OLED is connected to SPI(1)
SCK  - GP10
MOSI - GP11
MISO - GP8  (Not use)

"""
import sys
import os
import time
import ssd1306
from array import array
import math

DISP_WIDTH=128
DISP_HEIGHT=64

# notice that:
# parameter cs have to be provided to ssd1306.SSD1306_SPI(),
# so I assign a GP8 as dummt cs, no need connect it.
dc  = machine.Pin(13)
res = machine.Pin(12)
cs  = machine.Pin(9)   #dummy (any un-used pin), no connection

print("====================================")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("====================================")

package_info = ssd1306.__name__
try:
    package_info = package_info + " ver:" + ssd1306.__version__
except AttributeError as exc:
    print("AttributeError!", exc)

print("ssd1306 package: ", package_info)
print("====================================")
oled_spi = machine.SPI(1)
print("Using SPI:", oled_spi)
print()

# === myEquPolygonClass class ===
class myEquPolygonClass:
    
    def __init__(self, num_of_ends, r):
        self.num_of_ends = num_of_ends
        self.r = r
        
        self.coords_list = []
        
        for end in range(num_of_ends):
            angle_in_radians = math.radians(360 * end/num_of_ends)
            print("degree of end", end, angle_in_radians)
            print(angle_in_radians, ":", math.sin(angle_in_radians), math.cos(angle_in_radians))
            self.coords_list.append(int(r * math.sin(angle_in_radians)))
            self.coords_list.append(int(r * math.cos(angle_in_radians)))
        
    def show(self, screen, offset_x=0, offset_y=0, fill=0):
        coords_array = array('h', self.coords_list)
        
        screen.poly(offset_x, offset_y,
                    coords_array,
                    1,        # color
                    fill)     # optional fill: True - fill, False - not fill
        screen.show()
# === End of myFixedPolygonClass ===

try:
    oled_ssd1306 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
    oled_ssd1306.fill(1)
    oled_ssd1306.show()
    time.sleep(1)
    oled_ssd1306.fill(0)
    oled_ssd1306.show()
    time.sleep(1)
    
    center_x = int(oled_ssd1306.width/2)
    center_y = int(oled_ssd1306.height/2)
    
    fill = False
    my_num_of_ends = 0
    while True:
        myEquPolygon = myEquPolygonClass(my_num_of_ends, 30)
        oled_ssd1306.fill(0)
        oled_ssd1306.text('num_of_ends ' + str(myEquPolygon.num_of_ends), 5, 5, 1)
        myEquPolygon.show(oled_ssd1306, center_x, center_y, fill)
        fill = not fill
        time.sleep(1)
        
        if fill == False:
            if my_num_of_ends == 10:
                my_num_of_ends = 0
            else:
                my_num_of_ends = my_num_of_ends + 1

except OSError as exc:
    print("OSError!", exc)
    if exc.errno == errno.ENODEV:
        print("No such device")
        
print("~ bye ~")


mpyPico2_SSD1306_spi_myEquPolygon3.py
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on 0.96" 128x64 SSD1306/SSD1306 SPI OLED.
(It should work on both Pico/Pico 2)

To draw a Equilateral Polygon, change num_of_ends at runtime.
Implement update_coords_list() in myEquPolygonClass class,
such that no need to re-create myEquPolygonClass object everytime wanted to change parameters.

Install ssd1306 @ micropython-lib SSD1306 OLED driver (ver: 0.1.0)
in Thonny > Tools > Manager packages...

OLED is connected to SPI(1)
SCK  - GP10
MOSI - GP11
MISO - GP8  (Not use)

"""
import sys
import os
import time
import ssd1306
from array import array
import math

DISP_WIDTH=128
DISP_HEIGHT=64

# notice that:
# parameter cs have to be provided to ssd1306.SSD1306_SPI(),
# so I assign a GP8 as dummt cs, no need connect it.
dc  = machine.Pin(13)
res = machine.Pin(12)
cs  = machine.Pin(9)   #dummy (any un-used pin), no connection

print("====================================")
print(sys.implementation[0], os.uname()[3],
      "\nrun on", os.uname()[4])
print("====================================")

package_info = ssd1306.__name__
try:
    package_info = package_info + " ver:" + ssd1306.__version__
except AttributeError as exc:
    print("AttributeError!", exc)

print("ssd1306 package: ", package_info)
print("====================================")
oled_spi = machine.SPI(1)
print("Using SPI:", oled_spi)
print()

# === myEquPolygonClass class ===
class myEquPolygonClass:
    
    def __init__(self, num_of_ends, r):
        self.num_of_ends = num_of_ends
        self.r = r
        
        self.coords_list = []
        self.reCal_coords_list()
        
    def update_coords_list(self, num_of_ends, r):
        self.num_of_ends = num_of_ends
        self.r = r
        
        self.coords_list.clear()
        self.reCal_coords_list()
        
    def reCal_coords_list(self):
        for end in range(self.num_of_ends):
            angle_in_radians = math.radians(360 * end/self.num_of_ends)
            print("degree of end", end, angle_in_radians)
            print(angle_in_radians, ":", math.sin(angle_in_radians), math.cos(angle_in_radians))
            self.coords_list.append(int(self.r * math.sin(angle_in_radians)))
            self.coords_list.append(int(self.r * math.cos(angle_in_radians)))
        
    def show(self, screen, offset_x=0, offset_y=0, fill=0):
        coords_array = array('h', self.coords_list)
        
        screen.poly(offset_x, offset_y,
                    coords_array,
                    1,        # color
                    fill)     # optional fill: True - fill, False - not fill
        screen.show()
# === End of myFixedPolygonClass ===

try:
    oled_ssd1306 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
    oled_ssd1306.fill(1)
    oled_ssd1306.show()
    time.sleep(1)
    oled_ssd1306.fill(0)
    oled_ssd1306.show()
    time.sleep(1)
    
    center_x = int(oled_ssd1306.width/2)
    center_y = int(oled_ssd1306.height/2)
    
    fill = False
    my_num_of_ends = 0
    myEquPolygon = myEquPolygonClass(my_num_of_ends, 30)
    
    while True:
        myEquPolygon.update_coords_list(my_num_of_ends, 30)
        oled_ssd1306.fill(0)
        oled_ssd1306.text('num_of_ends ' + str(myEquPolygon.num_of_ends), 5, 5, 1)
        myEquPolygon.show(oled_ssd1306, center_x, center_y, fill)
        fill = not fill
        time.sleep(1)
        
        if fill == False:
            if my_num_of_ends == 10:
                my_num_of_ends = 0
            else:
                my_num_of_ends = my_num_of_ends + 1

except OSError as exc:
    print("OSError!", exc)
    if exc.errno == errno.ENODEV:
        print("No such device")
        
print("~ bye ~")



Comments

Popular posts from this blog

480x320 TFT/ILI9488 SPI wih EP32C3 (arduino-esp32) using Arduino_GFX Library

MicroPython/ESP32-C3 + 1.8" 128x160 TFT ST7735 SPI, using boochow/MicroPython-ST7735 library.