Multi SSD1306 OLED, on Raspberry Pi Pico 2/MicroPython
My exercise on Raspberry Pi Pico 2 (RP2350)
running MicroPython v1.24.0-preview.321, to display on two 128x64 SSD1315 I2C OLED (SSD1306 compatible), and one 128x64 SSD1306 SPI OLED. The 3rd, 4th and 5th exercises display on three OLEDs as one framebuf. In
the 4th and 5th exercises, groupedOLED_class is implemented, make it easier to
append OLEDs at runtime.
Two I2C SSD1306 OLED share the same I2C
bus with different address.
Connection:
mpyPico2_tri_oled_hello.py, a simple hello world display on 3 OLED separately.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on three OLED, 0.96" 128x64 SSD1306 OLED
- two I2C OLED share one I2C with different I2C address
- and one SPI OLED.
To install MicroPython ssd1306 driver on Pico 2. read:
https://coxxect.blogspot.com/2024/09/i2c-oled-ssd1306ssd1315-screen-with.html
Two I2C OLEDs are connected to the same I2C(0) with different I2C address
The I2C address of the left SSD1306 I2C OLED = 0x3d
The I2C address of the mid SSD1306 I2C OLED = 0x3c (default)
SCL - GP5
SDA - GP4
SPI OLED in the right 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_i2c = machine.I2C(0)
print("oled_i2c:", oled_i2c)
oled_spi = machine.SPI(1)
print("oled_spi:", oled_spi)
print()
oled_ssd1306_i2c_0 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c, 0x3d)
oled_ssd1306_i2c = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
oled_ssd1306_spi = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
oled_ssd1306_i2c_0.fill(1)
oled_ssd1306_i2c.fill(1)
oled_ssd1306_spi.fill(1)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(1)
oled_ssd1306_i2c_0.fill(0)
oled_ssd1306_i2c.fill(0)
oled_ssd1306_spi.fill(0)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(1)
oled_ssd1306_i2c_0.rect(1, 1, DISP_WIDTH-2, 25, 1)
oled_ssd1306_i2c.rect(1, 1, DISP_WIDTH-2, 25, 1)
oled_ssd1306_spi.rect(1, 1, DISP_WIDTH-2, 25, 1)
oled_ssd1306_i2c_0.text('Hello, World!', 5, 5, 1)
oled_ssd1306_i2c_0.text(oled_ssd1306_i2c.__qualname__, 5, 15, 1)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.text('Hello, World!', 5, 5, 1)
oled_ssd1306_i2c.text(oled_ssd1306_i2c.__qualname__, 5, 15, 1)
oled_ssd1306_i2c.show()
oled_ssd1306_spi.text('Hello, World!', 5, 5, 1)
oled_ssd1306_spi.text(oled_ssd1306_spi.__qualname__, 5, 15, 1)
oled_ssd1306_spi.show()
while True:
for y in range(9):
oled_ssd1306_i2c_0.scroll(0, y)
oled_ssd1306_i2c.scroll(0, y)
oled_ssd1306_spi.scroll(0, y)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(0.2)
for y in range(9):
oled_ssd1306_i2c_0.scroll(0, -y)
oled_ssd1306_i2c.scroll(0, -y)
oled_ssd1306_spi.scroll(0, -y)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(0.2)
print("~ bye ~")
mpyPico2_tri_oled_polygon3.py, display stars (polygon) on 3 OLED separately.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on three OLED, 0.96" 128x64 SSD1306 OLED
- two I2C OLED share one I2C with different I2C address
- and one SPI OLED.
Try different num_of_ends on all three OLED.
To install MicroPython ssd1306 driver on Pico 2. read:
https://coxxect.blogspot.com/2024/09/i2c-oled-ssd1306ssd1315-screen-with.html
Two I2C OLEDs are connected to the same I2C(0) with different I2C address
The I2C address of the left SSD1306 I2C OLED = 0x3d
The I2C address of the mid SSD1306 I2C OLED = 0x3c (default)
SCL - GP5
SDA - GP4
SPI OLED in the right 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_i2c = machine.I2C(0)
print("oled_i2c:", oled_i2c)
oled_spi = machine.SPI(1)
print("oled_spi:", oled_spi)
print()
oled_ssd1306_i2c_0 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c, 0x3d)
oled_ssd1306_i2c = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
oled_ssd1306_spi = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
oled_ssd1306_i2c_0.fill(1)
oled_ssd1306_i2c.fill(1)
oled_ssd1306_spi.fill(1)
oled_ssd1306_i2c_0.text(oled_ssd1306_i2c_0.__qualname__, 5, 5, 0)
oled_ssd1306_i2c.text(oled_ssd1306_i2c.__qualname__, 5, 5, 0)
oled_ssd1306_spi.text(oled_ssd1306_spi.__qualname__, 5, 5, 0)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(3)
oled_ssd1306_i2c_0.fill(0)
oled_ssd1306_i2c.fill(0)
oled_ssd1306_spi.fill(0)
oled_ssd1306_i2c_0.text(oled_ssd1306_i2c_0.__qualname__, 5, 5, 1)
oled_ssd1306_i2c.text(oled_ssd1306_i2c.__qualname__, 5, 5, 1)
oled_ssd1306_spi.text(oled_ssd1306_spi.__qualname__, 5, 5, 1)
oled_ssd1306_i2c_0.show()
oled_ssd1306_i2c.show()
oled_ssd1306_spi.show()
time.sleep(3)
# === myEquPolygonClass class ===
class myEquPolygonClass:
def __init__(self, num_of_ends, r, angel=0):
self.num_of_ends = num_of_ends
self.r = r
self.angel = angel
self.coords_list = []
self.reCal_coords_list()
def update_coords_list(self, num_of_ends, r, angel=0):
self.num_of_ends = num_of_ends
self.r = r
self.angel = angel
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) + self.angel)
#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 ===
# === myStarClass class ===
class myStarClass:
def __init__(self, num_of_ends, r1, r2, angel=0):
self.num_of_ends = num_of_ends
self.r1 = r1
self.r2 = r2
self.angel = angel
self.coords_list = []
self.reCal_coords_list()
def update_coords_list(self, num_of_ends, r1, r2, angel=0):
self.num_of_ends = num_of_ends
self.r1 = r1
self.r2 = r2
self.angel = angel
self.coords_list.clear()
self.reCal_coords_list()
def reCal_coords_list(self):
# if self.num_of_ends ==0, this code will raise error of:
# ZeroDivisionError: divide by zero
half_angel_per_end = (360/self.num_of_ends)/2
for end in range(self.num_of_ends):
angle_in_radians = math.radians((360 * end/self.num_of_ends) + self.angel)
self.coords_list.append(int(self.r1 * math.sin(angle_in_radians)))
self.coords_list.append(int(self.r1 * math.cos(angle_in_radians)))
angle_in_radians = math.radians((360 * end/self.num_of_ends) + half_angel_per_end + self.angel)
self.coords_list.append(int(self.r2 * math.sin(angle_in_radians)))
self.coords_list.append(int(self.r2 * 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 ===
center_x_i2c_0 = int(oled_ssd1306_i2c_0.width/2)
center_y_i2c_0 = int(oled_ssd1306_i2c_0.height/2)
center_x_i2c = int(oled_ssd1306_i2c.width/2)
center_y_i2c = int(oled_ssd1306_i2c.height/2)
center_x_spi = int(oled_ssd1306_spi.width/2)
center_y_spi = int(oled_ssd1306_spi.height/2)
init_num_of_ends = 3
init_angel = 0
init_r1 = 30
init_r2 = 10
myStar1 = myStarClass(num_of_ends = init_num_of_ends,
r1 = init_r1, r2 = init_r2,
angel = init_angel)
myStar1.show(oled_ssd1306_i2c_0, center_x_i2c_0, center_y_i2c_0, 0)
myStar1.show(oled_ssd1306_i2c, center_x_i2c, center_y_i2c, 0)
myStar1.show(oled_ssd1306_spi, center_x_spi, center_y_spi, 0)
angel = init_angel
num_of_ends = init_num_of_ends
r1 = init_r1
r2 = init_r2
r2_min = 5
r2_max = 30
r2_dir = 1
while True:
myStar1.update_coords_list(num_of_ends, r1, r2, angel)
oled_ssd1306_i2c_0.fill(0)
oled_ssd1306_i2c.fill(0)
oled_ssd1306_spi.fill(0)
myStar1.show(oled_ssd1306_i2c_0, center_x_i2c_0, center_y_i2c_0)
myStar1.show(oled_ssd1306_i2c, center_x_i2c, center_y_i2c)
myStar1.show(oled_ssd1306_spi, center_x_spi, center_y_spi)
num_of_ends = num_of_ends + 1
if num_of_ends > 10:
num_of_ends = 1
time.sleep(0.1)
print("~ bye ~")
mpyPico2_triOLED_one_framebuf.py, three OLEDs as one FrameBuffer.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on three OLED, 0.96" 128x64 SSD1306 OLED
- two I2C OLED share one I2C with different I2C address
- and one SPI OLED.
Three OLEDs as one FrameBuffer
To install MicroPython ssd1306 driver on Pico 2. read:
https://coxxect.blogspot.com/2024/09/i2c-oled-ssd1306ssd1315-screen-with.html
Two I2C OLEDs are connected to the same I2C(0) with different I2C address
The I2C address of the left SSD1306 I2C OLED = 0x3d
The I2C address of the mid SSD1306 I2C OLED = 0x3c (default)
SCL - GP5
SDA - GP4
SPI OLED in the right 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
# 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
sys_msg_1 = sys.implementation[0] + " " + os.uname()[3]
sys_msg_2 = "run on " + os.uname()[4]
print("====================================")
print(sys_msg_1)
print(sys_msg_2)
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_i2c = machine.I2C(0)
print("oled_i2c:", oled_i2c)
oled_spi = machine.SPI(1)
print("oled_spi:", oled_spi)
oled_0 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c, 0x3d)
oled_1 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
oled_2 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
print("oled_0 I2C address:",
oled_0.addr, "/", hex(oled_0.addr))
print("oled_1 I2C address:",
oled_1.addr, "/", hex(oled_1.addr))
"""
fb structure:
|<--------------- fb_width=128 x 3 = 384 --------------->|
-------------- +==================+==================+==================+
^ | 128x64 | 128x64 | 128x64 |
fb_height=64 | | | |
v--------------+==================+==================+==================+
^ ^ ^
fb0_x_offset=0 fb1_x_offset=128 fb2_x_offset=256
I2C SSD1306 I2C SSD1306 SPI SSD1306
addr = 0x3d addr = 0x3c
(default)
"""
# Grouped FrameBuffer = 384x64
fb_width=384
fb_height=64
fb0_x_offset=0
fb1_x_offset=128
fb2_x_offset=256
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():
oled_0.blit(fb, fb0_x_offset, 0)
oled_1.blit(fb, -fb1_x_offset, 0)
oled_2.blit(fb, -fb2_x_offset, 0)
oled_0.show()
oled_1.show()
oled_2.show()
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)
fb.text(sys_msg_1, 3, 10, 1)
fb.text(sys_msg_2, 3, 20, 1)
fb.text(package_info, 3, 30, 1)
update_display()
time.sleep(3)
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
step_x = 2
step_y = 1
while True:
fb.blit(pi_fb, blitx, blity)
update_display()
#prepare next round
blitx = blitx + (xdir*step_x)
if blitx < 0:
blitx=1
xdir=1
if blitx >= xlim:
blitx=xlim-1
xdir=-1
blity = blity + (ydir*step_y)
if blity < 0:
blity=1
ydir=1
if blity >= ylim:
blity=ylim-1
ydir=-1
time.sleep(0.01)
print("~ bye ~")
mpyPico2_multiOLED_one_framebuf.py, groupedOLED_class is implemented, easier to append OLEDs at runtime.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on three OLED, 0.96" 128x64 SSD1306 OLED
- two I2C OLED share one I2C with different I2C address
- and one SPI OLED.
Three OLEDs as one FrameBuffer
groupedOLED_class is implemented, easier to append OLEDs at runtime.
To install MicroPython ssd1306 driver on Pico 2. read:
https://coxxect.blogspot.com/2024/09/i2c-oled-ssd1306ssd1315-screen-with.html
Two I2C OLEDs are connected to the same I2C(0) with different I2C address
The I2C address of the left SSD1306 I2C OLED = 0x3d
The I2C address of the mid SSD1306 I2C OLED = 0x3c (default)
SCL - GP5
SDA - GP4
SPI OLED in the right 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
# 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
sys_msg_1 = sys.implementation[0] + " " + os.uname()[3]
sys_msg_2 = "run on " + os.uname()[4]
print("====================================")
print(sys_msg_1)
print(sys_msg_2)
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_i2c = machine.I2C(0)
print("oled_i2c:", oled_i2c)
oled_spi = machine.SPI(1)
print("oled_spi:", oled_spi)
oled_0 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c, 0x3d)
oled_1 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
oled_2 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
print("oled_0 I2C address:",
oled_0.addr, "/", hex(oled_0.addr))
print("oled_1 I2C address:",
oled_1.addr, "/", hex(oled_1.addr))
"""
fb structure:
|<--------------- fb_width=128 x 3 = 384 --------------->|
-------------- +==================+==================+==================+
^ | 128x64 | 128x64 | 128x64 |
fb_height=64 | | | |
v--------------+==================+==================+==================+
^ ^ ^
fb0_x_offset=0 fb1_x_offset=128 fb2_x_offset=256
I2C SSD1306 I2C SSD1306 SPI SSD1306
addr = 0x3d addr = 0x3c
(default)
"""
class groupedOLED_class():
def __init__(self):
self.OLED_group = []
self.reCreateFrameBuffer()
def reCreateFrameBuffer(self):
if len(self.OLED_group) == 0:
self.width = 0
self.height = 0
self.fb = None;
print("OLED_group is empty")
else:
self.height = self.OLED_group[0].height
fb_width = 0
for oled in self.OLED_group:
fb_width = fb_width + oled.width
self.width = fb_width
self.fb = framebuf.FrameBuffer(bytearray(self.width*self.height),
self.width,
self.height,
framebuf.MONO_HLSB)
print("new FrameBuffer created:", self.width, "x", self.height)
def append(self, new_oled):
self.OLED_group.append(new_oled)
self.reCreateFrameBuffer()
def update_display(self):
next_x_offset = 0
for oled in self.OLED_group:
oled.blit(self.fb, -next_x_offset, 0)
next_x_offset = next_x_offset + oled.width
for oled in self.OLED_group:
oled.show()
# 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)
groupedOLED = groupedOLED_class()
groupedOLED.append(oled_0)
groupedOLED.append(oled_1)
groupedOLED.append(oled_2)
print("groupedOLED: ", groupedOLED.width, "x", groupedOLED.height)
groupedOLED.fb.fill(1)
groupedOLED.update_display()
time.sleep(0.5)
groupedOLED.fb.fill(0)
groupedOLED.update_display()
time.sleep(0.5)
def test_msg():
groupedOLED.fb.fill(0)
groupedOLED.fb.rect(0, 0, groupedOLED.width, groupedOLED.height, 1)
groupedOLED.fb.text(sys_msg_1, 3, 10, 1)
groupedOLED.fb.text(sys_msg_2, 3, 20, 1)
groupedOLED.fb.text(package_info, 3, 30, 1)
groupedOLED.update_display()
time.sleep(2)
test_msg()
blitx = 1
blity = 1
xdir = 1
xlim = groupedOLED.width-pi_fb_width
ydir = 1
ylim = groupedOLED.height-pi_fb_height
step_x = 2
step_y = 1
while True:
groupedOLED.fb.blit(pi_fb, blitx, blity)
groupedOLED.update_display()
#prepare next round
blitx = blitx + (xdir*step_x)
if blitx <= 0:
blitx=1
xdir=1
if blitx >= xlim:
blitx=xlim-1
xdir=-1
blity = blity + (ydir*step_y)
if blity <= 0:
blity=1
ydir=1
if blity >= ylim:
blity=ylim-1
ydir=-1
time.sleep(0.01)
print("~ bye ~")
mpyPico2_multiOLED_test.py, append OLEDs at runtime, with various test: hline, vline, line, rect ellipse, circle.
"""
MicroPython/Raspberry Pi Pico 2 exercise
display on three OLED, 0.96" 128x64 SSD1306 OLED
- two I2C OLED share one I2C with different I2C address
- and one SPI OLED.
Three OLEDs as one FrameBuffer,
with various test: hline, vline, line, rect, ellipse, circle
groupedOLED_class is implemented, easier to append OLEDs at runtime.
To install MicroPython ssd1306 driver on Pico 2. read:
https://coxxect.blogspot.com/2024/09/i2c-oled-ssd1306ssd1315-screen-with.html
Two I2C OLEDs are connected to the same I2C(0) with different I2C address
The I2C address of the left SSD1306 I2C OLED = 0x3d
The I2C address of the mid SSD1306 I2C OLED = 0x3c (default)
SCL - GP5
SDA - GP4
SPI OLED in the right is connected to SPI(1)
SCK - GP10
MOSI - GP11
MISO - GP8 (Not use)
ref. MicroPython framebuf:
https://docs.micropython.org/en/latest/library/framebuf.html
"""
import sys
import os
import time
import ssd1306
import framebuf
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
sys_msg_1 = sys.implementation[0] + " " + os.uname()[3]
sys_msg_2 = "run on " + os.uname()[4]
print("====================================")
print(sys_msg_1)
print(sys_msg_2)
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_i2c = machine.I2C(0)
print("oled_i2c:", oled_i2c)
oled_spi = machine.SPI(1)
print("oled_spi:", oled_spi)
oled_0 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c, 0x3d)
oled_1 = ssd1306.SSD1306_I2C(DISP_WIDTH, DISP_HEIGHT, oled_i2c)
oled_2 = ssd1306.SSD1306_SPI(DISP_WIDTH, DISP_HEIGHT, oled_spi, dc, res, cs)
print("oled_0 I2C address:",
oled_0.addr, "/", hex(oled_0.addr))
print("oled_1 I2C address:",
oled_1.addr, "/", hex(oled_1.addr))
"""
fb structure:
|<--------------- fb_width=128 x 3 = 384 --------------->|
-------------- +==================+==================+==================+
^ | 128x64 | 128x64 | 128x64 |
fb_height=64 | | | |
v--------------+==================+==================+==================+
^ ^ ^
fb0_x_offset=0 fb1_x_offset=128 fb2_x_offset=256
I2C SSD1306 I2C SSD1306 SPI SSD1306
addr = 0x3d addr = 0x3c
(default)
"""
class groupedOLED_class():
def __init__(self):
self.OLED_group = []
self.reCreateFrameBuffer()
def reCreateFrameBuffer(self):
if len(self.OLED_group) == 0:
self.width = 0
self.height = 0
self.fb = None;
print("OLED_group is empty")
else:
self.height = self.OLED_group[0].height
fb_width = 0
for oled in self.OLED_group:
fb_width = fb_width + oled.width
self.width = fb_width
self.fb = framebuf.FrameBuffer(bytearray(self.width*self.height),
self.width,
self.height,
framebuf.MONO_HLSB)
print("new FrameBuffer created:", self.width, "x", self.height)
def append(self, new_oled):
self.OLED_group.append(new_oled)
self.reCreateFrameBuffer()
def update_display(self):
next_x_offset = 0
for oled in self.OLED_group:
oled.blit(self.fb, -next_x_offset, 0)
next_x_offset = next_x_offset + oled.width
for oled in self.OLED_group:
oled.show()
# 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 clear_screen():
groupedOLED.fb.fill(0)
groupedOLED.update_display()
def test_hline(steps=10):
clear_screen()
time.sleep(0.2)
x = 10
y = 0
w = 0
for i in range(steps):
groupedOLED.fb.hline(x,
int(groupedOLED.height * i/steps),
int(groupedOLED.width * i/steps),
1)
groupedOLED.update_display()
time.sleep(0.1)
def test_vline(steps=10):
clear_screen()
time.sleep(0.2)
x = 0
y = 5
w = 0
for i in range(steps):
groupedOLED.fb.vline(int(groupedOLED.width * i/steps),
y,
int(groupedOLED.height * i/steps),
1)
groupedOLED.update_display()
time.sleep(0.1)
def test_line(steps=10):
clear_screen()
time.sleep(0.2)
for i in range(steps):
groupedOLED.fb.line(int(groupedOLED.width * i/steps),
0,
groupedOLED.width-1,
int(groupedOLED.height * i/steps),
1)
groupedOLED.update_display()
time.sleep(0.1)
def test_rect(steps=10):
clear_screen()
time.sleep(0.2)
for i in range(steps):
groupedOLED.fb.rect(int(groupedOLED.width * i/steps),
int(groupedOLED.height * i/steps),
int(groupedOLED.width * (steps - 2*i)/steps),
int(groupedOLED.height * (steps - 2*i)/steps),
1)
groupedOLED.update_display()
time.sleep(0.1)
def test_ellipse(steps=10):
clear_screen()
time.sleep(0.2)
for i in range(steps):
x = int(groupedOLED.width/2)
y = int(groupedOLED.height/2)
xr = int(groupedOLED.width/2 * i/steps)
yr = int(groupedOLED.height/2 * i/steps)
# in my test, xr, yr cannot be 0
xr = 1 if xr == 0 else xr
yr = 1 if xr == 0 else yr
groupedOLED.fb.ellipse(x, y, xr, yr, 1)
groupedOLED.update_display()
time.sleep(0.1)
def test_circle(steps=10):
clear_screen()
time.sleep(0.2)
for i in range(steps):
x = int(groupedOLED.width * i/steps)
y = int(groupedOLED.height/2)
r = int(groupedOLED.height/2 * i/steps)
# in my test, r (xr, yr in ellipse) cannot be 0
r = 1 if r == 0 else r
groupedOLED.fb.ellipse(x, y, r, r, 1)
groupedOLED.update_display()
time.sleep(0.1)
def grouped_test():
clear_screen()
time.sleep(0.5)
test_hline()
time.sleep(0.5)
test_vline(30)
time.sleep(0.5)
test_line(15)
time.sleep(0.5)
test_rect(15)
time.sleep(0.5)
test_ellipse(8)
time.sleep(0.5)
test_circle(20)
groupedOLED = groupedOLED_class()
groupedOLED.append(oled_0)
print("groupedOLED: ", groupedOLED.width, "x", groupedOLED.height)
grouped_test()
groupedOLED.append(oled_1)
print("groupedOLED: ", groupedOLED.width, "x", groupedOLED.height)
grouped_test()
groupedOLED.append(oled_2)
print("groupedOLED: ", groupedOLED.width, "x", groupedOLED.height)
grouped_test()
print("~ bye ~")
Comments
Post a Comment