micropython_ssd1306 on the Raspberry Pi Pico 2 to control SSD1306 OLED
Installing micropython_ssd1306 on the Raspberry Pi Pico 2 with Thonny
SSD1306 OLED demos for Raspberry Pi Pico 2 / MicroPython
Next:
~ Raspberry Pi Pico 2/MicroPython to handle two SSD1306 OLEDs, in main thread and background thread.
This video explains how to install the micropython_ssd1306 library (the
SSD1306 OLED driver for MicroPython) on the Raspberry Pi Pico 2 using the
Thonny Package Manager.
- Connect your Raspberry Pi Pico 2 (with MicroPython firmware) to your computer.
- Open Thonny and configure the interpreter for the Raspberry Pi Pico 2 (MicroPython).
- In the top menu, go to Tools > Manage packages...
- Search for micropython-ssd1306 and click Install.
Exercise code:
# mpy_pico2_ssd1306.py
# MicroPython exercise running on Raspberry Pi Pico 2 with RP2350
# to control 128x64 SSD1306 OLED.
#
# https://coxxect.blogspot.com/2026/10/installing-micropythonssd1306-on.html
#
import time
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
from framebuf import FrameBuffer
i2c = I2C(0)
print(i2c)
print(i2c.scan())
oled = SSD1306_I2C(128, 64, i2c)
print(oled.width, "x", oled.height)
print("type(oled):", type(oled))
# whether oled is an instance of framebuf.FrameBuffer.
print("isinstance(oled, FrameBuffer):", isinstance(oled, FrameBuffer))
print()
# valid attributes, methods, or variables for oled
print("dir(oled):\n", dir(oled))
oled.fill(0)
oled.show()
time.sleep(0.5)
oled.fill(1)
oled.show()
time.sleep(0.5)
oled.fill(0)
oled.show()
time.sleep(0.5)
SSD1306 OLED demos for Raspberry Pi Pico 2 / MicroPython
# mpy_pico2_ssd1306_ex.py
# SSD1306 OLED demos for Raspberry Pi Pico 2 / MicroPython
# 128x64 I2C OLED
#
# https://coxxect.blogspot.com/2026/10/installing-micropythonssd1306-on.html
import time
import math
import random
import framebuf
from machine import Pin, I2C
from ssd1306 import SSD1306_I2C
WIDTH = 128
HEIGHT = 64
# Your original init line:
i2c = I2C(0)
# If I2C(0) does not work on your wiring, comment the line above
# and use explicit pins. Example:
# i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400_000)
# Optional: check I2C address.
# Usually [60] means 0x3C.
# print("I2C devices:", i2c.scan())
# Most SSD1306 OLEDs use address 0x3C.
# If yours is different, try 0x3D.
oled = SSD1306_I2C(WIDTH, HEIGHT, i2c, addr=0x3C)
# ------------------------------------------------------------
# Basic helpers
# ------------------------------------------------------------
def clear(oled, show=True):
"""Clear the display buffer."""
oled.fill(0)
if show:
oled.show()
def text_center(oled, s, y, color=1):
"""Center built-in 8x8 font text horizontally."""
x = (WIDTH - len(s) * 8) // 2
if x < 0:
x = 0
oled.text(s, x, y, color)
def scaled_text(oled, s, x, y, scale=2, color=1):
"""
Draw larger text by rendering the built-in 8x8 font
into a small framebuffer and scaling it up.
This is not a real font engine, but it is useful for
simple larger text without adding external font files.
"""
if not s:
return
w = len(s) * 8
h = 8
buf = bytearray((w * h + 7) // 8)
fb = framebuf.FrameBuffer(buf, w, h, framebuf.MONO_HLSB)
fb.fill(0)
fb.text(s, 0, 0, 1)
for yy in range(h):
for xx in range(w):
if fb.pixel(xx, yy):
oled.fill_rect(
x + xx * scale,
y + yy * scale,
scale,
scale,
color
)
# ------------------------------------------------------------
# Shape helpers
# ------------------------------------------------------------
def draw_circle(oled, cx, cy, r, color=1, fill=False):
"""
Draw circle outline or filled circle.
"""
if r < 0:
return
if r == 0:
oled.pixel(cx, cy, color)
return
if fill:
# Simple filled circle using horizontal lines.
r2 = r * r
for dy in range(-r, r + 1):
dx = int(math.sqrt(r2 - dy * dy))
oled.hline(cx - dx, cy + dy, 2 * dx + 1, color)
else:
# Midpoint circle algorithm for outline.
x = r
y = 0
err = 0
while x >= y:
oled.pixel(cx + x, cy + y, color)
oled.pixel(cx + y, cy + x, color)
oled.pixel(cx - y, cy + x, color)
oled.pixel(cx - x, cy + y, color)
oled.pixel(cx - x, cy - y, color)
oled.pixel(cx - y, cy - x, color)
oled.pixel(cx + y, cy - x, color)
oled.pixel(cx + x, cy - y, color)
y += 1
err += 1 + 2 * y
if 2 * (err - x) + 1 > 0:
x -= 1
err += 1 - 2 * x
def draw_poly(oled, points, color=1, closed=True):
"""
Draw a polygon from a list of (x, y) tuples.
"""
for i in range(len(points) - 1):
x0, y0 = points[i]
x1, y1 = points[i + 1]
oled.line(x0, y0, x1, y1, color)
if closed and len(points) >= 3:
x0, y0 = points[-1]
x1, y1 = points[0]
oled.line(x0, y0, x1, y1, color)
# ------------------------------------------------------------
# Demo 1: text and simple fonts
# ------------------------------------------------------------
def demo_text(oled):
clear(oled, False)
text_center(oled, "SSD1306", 0)
text_center(oled, "MicroPython", 12)
oled.hline(0, 22, WIDTH, 1)
oled.text("8x8 font", 0, 26)
# Inverse text: draw filled rectangle, then draw text with color 0.
oled.fill_rect(0, 36, WIDTH, 10, 1)
text_center(oled, "Inverse", 37, 0)
oled.show()
time.sleep_ms(2000)
# Scaled text examples.
for scale in (2, 3):
clear(oled, False)
text_center(oled, "Scaled", 0)
scaled_text(oled, "Pico", 8, 20, scale)
oled.show()
time.sleep_ms(1500)
# ------------------------------------------------------------
# Demo 2: scrolling text
# ------------------------------------------------------------
def demo_scroll(oled, loops=1):
# Normal 8x8 font horizontal scroll.
msg = "Scrolling demo"
text_w = len(msg) * 8
for _ in range(loops):
for x in range(WIDTH, -text_w, -1):
# Clear only the text line area.
oled.fill_rect(0, 24, WIDTH, 8, 0)
oled.text(msg, x, 24, 1)
oled.show()
time.sleep_ms(25)
# Bigger scrolled text.
# This is slower because scaled_text() re-renders every frame.
msg2 = "Pico 2"
scale = 3
tw = len(msg2) * 8 * scale
th = 8 * scale
y = (HEIGHT - th) // 2
# Step by 2 to make it faster.
for x in range(WIDTH, -tw, -2):
oled.fill_rect(0, y, WIDTH, th, 0)
scaled_text(oled, msg2, x, y, scale)
oled.show()
time.sleep_ms(35)
# ------------------------------------------------------------
# Demo 3: geometric shapes
# ------------------------------------------------------------
def demo_shapes(oled):
clear(oled, False)
text_center(oled, "Shapes", 0)
# Rectangle outline.
oled.rect(8, 14, 30, 20, 1)
# Filled rectangle.
oled.fill_rect(48, 14, 30, 20, 1)
# Line.
oled.line(88, 14, 118, 34, 1)
# Horizontal and vertical lines.
oled.hline(8, 40, 112, 1)
oled.vline(64, 38, 24, 1)
# Circle outline.
draw_circle(oled, 24, 52, 8, 1, False)
# Filled circle.
draw_circle(oled, 56, 52, 8, 1, True)
# Triangle.
draw_poly(
oled,
[
(88, 44),
(104, 60),
(72, 60)
],
1,
True
)
oled.show()
time.sleep_ms(2000)
# Growing circle animation.
for r in range(2, 26, 2):
clear(oled, False)
draw_circle(oled, WIDTH // 2, HEIGHT // 2, r, 1, False)
oled.show()
time.sleep_ms(80)
# ------------------------------------------------------------
# Demo 4: bouncing ball animation
# ------------------------------------------------------------
def demo_bounce(oled, frames=180):
x = WIDTH // 2
y = HEIGHT // 2
dx = 3
dy = 2
r = 5
for _ in range(frames):
oled.fill(0)
# Border.
oled.rect(0, 0, WIDTH, HEIGHT, 1)
# Ball.
draw_circle(oled, x, y, r, 1, True)
oled.show()
x += dx
y += dy
# Bounce horizontally.
if x - r <= 1:
x = r + 1
dx = abs(dx)
elif x + r >= WIDTH - 2:
x = WIDTH - r - 2
dx = -abs(dx)
# Bounce vertically.
if y - r <= 1:
y = r + 1
dy = abs(dy)
elif y + r >= HEIGHT - 2:
y = HEIGHT - r - 2
dy = -abs(dy)
time.sleep_ms(20)
# ------------------------------------------------------------
# Demo 5: radar / rotating line animation
# ------------------------------------------------------------
def demo_radar(oled, frames=180):
cx = WIDTH // 2
cy = HEIGHT // 2
r = 28
blips = [
(cx + 10, cy - 12),
(cx - 18, cy + 8),
(cx + 2, cy + 20)
]
for i in range(frames):
oled.fill(0)
# Radar rings.
draw_circle(oled, cx, cy, r, 1, False)
draw_circle(oled, cx, cy, r - 10, 1, False)
# Center point.
oled.pixel(cx, cy, 1)
# Rotating line.
angle = i * 0.12
x = int(cx + (r - 2) * math.cos(angle))
y = int(cy + (r - 2) * math.sin(angle))
oled.line(cx, cy, x, y, 1)
# Blips.
for bx, by in blips:
oled.pixel(bx, by, 1)
oled.show()
time.sleep_ms(25)
# ------------------------------------------------------------
# Demo 6: starfield animation
# ------------------------------------------------------------
def demo_starfield(oled, frames=220):
stars = []
for _ in range(40):
stars.append([
random.randint(0, WIDTH - 1),
random.randint(0, HEIGHT - 1),
random.randint(1, 3)
])
for _ in range(frames):
oled.fill(0)
for s in stars:
# Move star left. Bigger speed value = faster star.
s[0] -= s[2]
# If star leaves screen, respawn on right side.
if s[0] < 0:
s[0] = WIDTH - 1
s[1] = random.randint(0, HEIGHT - 1)
s[2] = random.randint(1, 3)
oled.pixel(s[0], s[1], 1)
oled.show()
time.sleep_ms(25)
# ------------------------------------------------------------
# Demo 7: sprite animation using FrameBuffer and blit()
# ------------------------------------------------------------
def make_sprite(step):
"""
Create a small 16x16 sprite.
The sprite is a filled circle with a small animated cut.
"""
buf = bytearray(16 * 16 // 8)
spr = framebuf.FrameBuffer(buf, 16, 16, framebuf.MONO_HLSB)
spr.fill(0)
# Body.
draw_circle(spr, 8, 8, 6, 1, True)
# Eye hole.
spr.pixel(10, 6, 0)
# Simple wing / motion cut.
if step == 0:
spr.line(3, 12, 8, 8, 0)
else:
spr.line(3, 4, 8, 8, 0)
return spr
def demo_sprite(oled, loops=2):
sprites = [
make_sprite(0),
make_sprite(1)
]
for _ in range(loops):
x = 0
frame = 0
while x < WIDTH - 16:
oled.fill(0)
# Draw current sprite frame.
#
# If your MicroPython version supports the key argument,
# you can try:
#
# oled.blit(sprites[frame % 2], x, 24, 0)
#
# where 0 is treated as transparent.
oled.blit(sprites[frame % 2], x, 24)
oled.show()
x += 3
frame += 1
time.sleep_ms(30)
# ------------------------------------------------------------
# Run all demos
# ------------------------------------------------------------
def run_all_demos(oled):
try:
while True:
demo_text(oled)
demo_scroll(oled, loops=1)
demo_shapes(oled)
demo_bounce(oled, 120)
demo_radar(oled, 120)
demo_starfield(oled, 120)
demo_sprite(oled, 1)
except KeyboardInterrupt:
clear(oled, False)
text_center(oled, "Stopped", 28)
oled.show()
# Run everything.
run_all_demos(oled)
# Or comment the line above and call only one demo:
# demo_text(oled)
# demo_scroll(oled)
# demo_shapes(oled)
# demo_bounce(oled)
# demo_radar(oled)
# demo_starfield(oled)
# demo_sprite(oled)
Next:
~ Raspberry Pi Pico 2/MicroPython to handle two SSD1306 OLEDs, in main thread and background thread.

Comments
Post a Comment