Pyboard/MicroPython display on SSD1315 I2C OLED using SSD1306 driver

This video show how Pyboard PYBv1.1 (flashed with MicroPython v1.19.1) display on 0.96 inch 128x64 SSD1315 I2C OLED using micropython-ssd1306 driver.

The post "Raspberry Pi Pico/MicroPython exercise using SSD1306 I2C OLED" show how to manual install ssd1306 driver to MicroPython device. This video show another approach in Thonny to install micropython-ssd1306 driver. This is a fork of the driver for SSD1306 displays which is hosted in the Micropython package. The purpose of this fork is to make the driver available on PyPi and thus installable via the upip package manager. But it haven't implemented rotate() function.


Exercise code

mpyPYB_i2c_scanner.py

Show I2C info and scan I2C devices
import sys
import os
import machine

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

def i2c_scan(i2c_toscan):
    print('=== I2C Scanner ===')
    print(i2c_toscan)
    devices = i2c_toscan.scan()

    if len(devices) == 0:
        print("No I2C device found!")
    else:
        print('I2C devices found:',len(devices))
        for device in devices:
            print(device, ":", hex(device))
    
    
# How many hardware I2C supported
# and show the I2C info
print("number of I2C supported:")
print("========================")
i=1
while True:
    try:
        i2c=machine.I2C(i)
        i2c_scan(i2c)
    except ValueError as e:
        #print(e)
        break
    i += 1
print()

mpyPYB_ssd1306.py
A simple test code for ssd1306
import sys
import os
import machine
import time
import ssd1306

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

oled_i2c = machine.I2C(2)
print("Default I2C(2):", oled_i2c)

DISP_WIDTH=128
DISP_HEIGHT=64

oled = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
print("Default SSD1306 I2C address:",
      oled.addr, "/", hex(oled.addr))
oled.fill(1)
oled.show()
time.sleep(2)
oled.fill(0)
oled.show()
time.sleep(2)
oled.rect(0, 0, DISP_WIDTH, DISP_HEIGHT, 1)
oled.text('Hello, World!', 5, 5, 1)
oled.show()


mpyPYB_ssd1315_hello_fb.py, port from the post "Raspberry Pi Pico/MicroPython exercise using SSD1306 I2C OLED".
"""
MicroPython/Pyboard exercise
display on 0.96" 128x64 SSD1315 I2C OLED.

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

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

oled_i2c = machine.I2C(2)
print("Default I2C(2):", oled_i2c)

DISP_WIDTH=128
DISP_HEIGHT=64

# 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)

def demo():

    oled.fill(0)
    oled.rect(0, 0, DISP_WIDTH, DISP_HEIGHT, 1)
    oled.text('Hello coXXect', 5, 5, 1)
    oled.text('SSD1315 OLED', 5, 15, 1)
    oled.text('comp.to SSD1306', 5, 25, 1)
    oled.show()
    time.sleep(2)

    oled.blit(fb, DISP_WIDTH-fb_width, 32)
    oled.show()
    time.sleep(1)
    
    for i in range(DISP_WIDTH-fb_width, 0-1, -1):
        oled.blit(fb, i, 32)
        oled.show()
        time.sleep(0.05)

oled = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
print("Default SSD1306 I2C address:",
      oled.addr, "/", hex(oled.addr))
    
#oled.rotate(False)
oled.invert(0)
oled.fill(1)
oled.show()
time.sleep(1)
oled.fill(0)
oled.show()
time.sleep(1)
    
demo()
#oled.rotate(True)
oled.invert(1)
demo()
time.sleep(1)
    
oled.invert(0)
time.sleep(1)
    
#oled.rotate(False)
oled.invert(0)
oled.fill(0)
    
step_width = fb_width+2
for i in range(0, DISP_WIDTH, step_width):
    oled.blit(fb, i, 32)
    oled.show()
    time.sleep(0.5)

print("~ bye ~")


The following code basically ported from the post "Pico/MicroPython display on two OLED as one FrameBuffer".

mpyPYB_dualdisplay_one_framebuf.py, display on two OLEDs as one FrameBuffer.
"""
MicroPython/Pyboard exercise
Two display as one FrameBuffer

Two 0.96" 128x64 SSD1315(SSD1306) I2C OLED
oledl - I2C(1): scl=B6
                sda=87
oledr - I2C(2): scl=B10
                sda=B11
"""
import sys
import os
import time
import ssd1306
import framebuf

SSD1315_WIDTH=128
SSD1315_HEIGHT=64

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

oledl_I2C = machine.I2C(1)
print("Default I2C(1) for OLED on left:\t", oledl_I2C)
oledr_I2C = machine.I2C(2)
print("Default I2C(2) for OLED on right:\t", oledr_I2C)

"""
fb structure:
                 |<---------- fb_width=256 ---------->|
    
 -------------- +==================+==================+
 ^              | 128x64           | 128x64           |
 fb_height=64   |                  |                  |
 v--------------+==================+==================+
                ^                  ^
                fb1_hoffset=0      fb2_hoffset=128
    

"""
# Raspberry Pi logo as 32x32 bytearray
fb_width=128+128
fb_height=64

fb1_hoffset=0
fb2_hoffset=128
buffer = bytearray(fb_width*fb_height)
fb = framebuf.FrameBuffer(buffer, fb_width, fb_height, framebuf.MONO_HLSB)

# Raspberry Pi logo as 32x32 bytearray
pi_fb_width=32
pi_fb_height=32
pi_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)
pi_fb = framebuf.FrameBuffer(pi_buffer, 32, 32, framebuf.MONO_HLSB)

def update_display():
    oledL.blit(fb, fb1_hoffset, 0)
    oledR.blit(fb, -fb2_hoffset, 0)
    oledL.show()
    oledR.show()

oledL = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledl_I2C)
print("OLED on Left I2C address:",
      oledL.addr, "/", hex(oledL.addr))

oledR = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledr_I2C)
print("OLED on Right I2C address:",
      oledR.addr, "/", hex(oledR.addr))

fb.fill(1)
update_display()
time.sleep(0.5)
fb.fill(0)
update_display()
time.sleep(0.5)
    
fb.rect(0, 0, fb_width, fb_height, 1)
update_display()
time.sleep(0.5)

update_display()
time.sleep(1)
fb.fill(0)
time.sleep(0.5)

blitx = 0
blity = 0
xdir = 1
xlim = fb_width-pi_fb_width
ydir = 1
ylim = fb_height-pi_fb_height

while True:
    fb.blit(pi_fb, blitx, blity)
    update_display()
    
    #prepare next round
    blitx = blitx+xdir
    if blitx == -1:
        blitx=1
        xdir=1
    if blitx == xlim:
        blitx=xlim-1
        xdir=-1
        
    blity = blity+ydir
    if blity == -1:
        blity=1
        ydir=1
    if blity == ylim:
        blity=ylim-1
        ydir=-1
        

print("~ bye ~")


mpyPYB_dualdisplay_one_framebuf_scroll.py, scroll text across two OLEDs.
"""
MicroPython/Pyboard exercise
Two display as one FrameBuffer
Scroll text across two displays

Two 0.96" 128x64 SSD1315(SSD1306) I2C OLED
oledl - I2C(1): scl=B6
                sda=87
oledr - I2C(2): scl=B10
                sda=B11
"""
import sys
import os
import time
import ssd1306
import framebuf

SSD1315_WIDTH=128
SSD1315_HEIGHT=64

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

oledl_I2C = machine.I2C(1)
print("Default I2C(1) for OLED on left:\t", oledl_I2C)
oledr_I2C = machine.I2C(2)
print("Default I2C(2) for OLED on right:\t", oledr_I2C)

"""
fb structure: 
                 |<---------- fb_width=256 ---------->|
    
 -------------- +==================+==================+
 ^              | 128x64           | 128x64           |
 fb_height=64   |                  |                  |
 v--------------+==================+==================+
                ^                  ^
                fb1_hoffset=0      fb2_hoffset=128
    

"""
# Raspberry Pi logo as 32x32 bytearray
fb_width=128+128
fb_height=64

fb1_hoffset=0
fb2_hoffset=128
buffer = bytearray(fb_width*fb_height)
fb = framebuf.FrameBuffer(buffer, fb_width, fb_height, framebuf.MONO_HLSB)

# Raspberry Pi logo as 32x32 bytearray
pi_fb_width=32
pi_fb_height=32
pi_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)
pi_fb = framebuf.FrameBuffer(pi_buffer, 32, 32, framebuf.MONO_HLSB)

def update_display():
    oledL.blit(fb, fb1_hoffset, 0)
    oledR.blit(fb, -fb2_hoffset, 0)
    oledL.show()
    oledR.show()

oledL = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledl_I2C)
print("OLED on Left I2C address:",
      oledL.addr, "/", hex(oledL.addr))

oledR = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledr_I2C)
print("OLED on Right I2C address:",
      oledR.addr, "/", hex(oledR.addr))

fb.fill(1)
update_display()
time.sleep(0.5)
fb.fill(0)
update_display()
time.sleep(0.5)

fb.text("Hello",50,5)
fb.text("coXXect",50,15)
update_display()

for i in range(80):
    fb.scroll(1, 0)
    update_display()
time.sleep(0.5)

for i in range(40):
    fb.scroll(-1, 1)
    update_display()
time.sleep(0.5)

for i in range(40):
    fb.scroll(0, -1)
    update_display()
time.sleep(0.5)

for i in range(40):
    fb.scroll(+1, +1)
    update_display()
time.sleep(0.5)

print("~ bye ~")


mpyPYB_dualdisplay_one_framebuf_sq.py, draw squares across OLEDs.
"""
MicroPython/Pyboard exercise
Two display as one FrameBuffer
Draw squares across two displays

Two 0.96" 128x64 SSD1315(SSD1306) I2C OLED
oledl - I2C(1): scl=B6
                sda=87
oledr - I2C(2): scl=B10
                sda=B11
"""
import sys
import os
import time
import ssd1306
import framebuf

SSD1315_WIDTH=128
SSD1315_HEIGHT=64

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

oledl_I2C = machine.I2C(1)
print("Default I2C(1) for OLED on left:\t", oledl_I2C)
oledr_I2C = machine.I2C(2)
print("Default I2C(2) for OLED on right:\t", oledr_I2C)

"""
fb structure: 
                 |<---------- fb_width=256 ---------->|
    
 -------------- +==================+==================+
 ^              | 128x64           | 128x64           |
 fb_height=64   |                  |                  |
 v--------------+==================+==================+
                ^                  ^
                fb1_hoffset=0      fb2_hoffset=128
    

"""
# Raspberry Pi logo as 32x32 bytearray
fb_width=128+128
fb_height=64

fb1_hoffset=0
fb2_hoffset=128
buffer = bytearray(fb_width*fb_height)
fb = framebuf.FrameBuffer(buffer, fb_width, fb_height, framebuf.MONO_HLSB)

# Raspberry Pi logo as 32x32 bytearray
pi_fb_width=32
pi_fb_height=32
pi_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)
pi_fb = framebuf.FrameBuffer(pi_buffer, 32, 32, framebuf.MONO_HLSB)

def update_display():
    oledL.blit(fb, fb1_hoffset, 0)
    oledR.blit(fb, -fb2_hoffset, 0)
    oledL.show()
    oledR.show()

oledL = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledl_I2C)
print("OLED on Left I2C address:",
      oledL.addr, "/", hex(oledL.addr))

oledR = ssd1306.SSD1306_I2C(SSD1315_WIDTH, SSD1315_HEIGHT, oledr_I2C)
print("OLED on Right I2C address:",
      oledR.addr, "/", hex(oledR.addr))

fb.fill(1)
update_display()
time.sleep(0.5)
fb.fill(0)
update_display()
time.sleep(0.5)

# draw squares
pt0=[0, 0]
pt1=[fb_width-1, 0]
pt2=[fb_width-1, fb_height-1]
pt3=[0, fb_height-1]
num_of_step = 12
step_x = fb_width//num_of_step
step_y = fb_height//num_of_step

def draw_sq():
    fb.line(pt0[0], pt0[1], pt1[0], pt1[1], 1)
    fb.line(pt1[0], pt1[1], pt2[0], pt2[1], 1)
    fb.line(pt2[0], pt2[1], pt3[0], pt3[1], 1)
    fb.line(pt3[0], pt3[1], pt0[0], pt0[1], 1)

n=0
while n<num_of_step:
    draw_sq()
    update_display()
    pt0[0]+=step_x
    pt1[1]+=step_y
    pt2[0]-=step_x
    pt3[1]-=step_y
    time.sleep(0.5)
    n+=1
print("~ bye ~")


Comments

Popular posts from this blog

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

CameraWebServe: ESP32-S3 (arduino-esp32) + OV5640 camera module