Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Installing from pip:
System setup
------------

If `ls -l /dev/pio0` reports that the file is not found, you may need to update your Pi 5 firmware to one with PIO support and make sure that you are running a suitably recent kernel. If `ls -l /dev/pio0` reports that the file is owned by root and group root, you should add the following to /etc/udev/rules/99-com.rules:
If `ls -l /dev/pio0` reports that the file is not found, you may need to update your Pi 5 firmware to one with PIO support and make sure that you are running a suitably recent kernel. If `ls -l /dev/pio0` reports that the file is owned by root and group root, you should add the following to /etc/udev/rules.d/99-com.rules:

```
SUBSYSTEM=="*-pio", GROUP="gpio", MODE="0660"
Expand Down
Binary file added examples/LindenHill-webfont.ttf
Binary file not shown.
3 changes: 3 additions & 0 deletions examples/LindenHill-webfont.ttf.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Copyright (c) 2010, Barry Schwartz <site-chieftain@crudfactory.com>, with Reserved Font Name OFL "Linden Hill"

# SPDX-License-Identifier: OFL-1.1-RFN
44 changes: 41 additions & 3 deletions examples/fbmirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,57 @@

The upper left corner of the framebuffer is displayed until the user hits ctrl-c.

Control matrix size, and orientation with command line arguments.

python fbmirror_scaled.py [width] [height] [orientation]

width int: Total width of matrices in pixels. Default is 64.
height int: Total height of matrices in pixels. Default is 32.
orientation int: Orientation in degrees, must be 0, 90, 180, or 270.
Default is 0 or Normal orientation.

The `/dev/fb0` special file will exist if a monitor is plugged in at boot time,
or if `/boot/firmware/cmdline.txt` specifies a resolution such as
`... video=HDMI-A-1:640x480M@60D`.
"""

import sys

import adafruit_raspberry_pi5_piomatter
import numpy as np

width = 64
height = 32

yoffset = 0
xoffset = 0


if len(sys.argv) >= 2:
width = int(sys.argv[1])
else:
width = 64

if len(sys.argv) >= 3:
height = int(sys.argv[2])
else:
height = 32

if len(sys.argv) >= 4:
rotation = int(sys.argv[3])
if rotation == 90:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.CW
elif rotation == 180:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.R180
elif rotation == 270:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.CCW
elif rotation == 0:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal
else:
raise ValueError("Invalid rotation. Must be 0, 90, 180, or 270.")
else:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal

with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]

Expand All @@ -32,9 +71,8 @@

linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // bytes_per_pixel), dtype=dtype)

width = 64
height = 32
geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal)

geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=rotation)
matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width), dtype=dtype)
matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB565(matrix_framebuffer, geometry)

Expand Down
49 changes: 43 additions & 6 deletions examples/fbmirror_scaled.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,59 @@
#!/usr/bin/python3
"""
Mirror a scaled copy of the framebuffer to a 64x32 matrix
Mirror a scaled copy of the framebuffer to 64x32 matrices,

The upper left corner of the framebuffer is displayed until the user hits ctrl-c.

Control scale, matrix size, and orientation with command line arguments.

python fbmirror_scaled.py [scale] [width] [height] [orientation]

scale int: How many times to scale down the display framebuffer. Default is 3.
width int: Total width of matrices in pixels. Default is 64.
height int: Total height of matrices in pixels. Default is 32.
orientation int: Orientation in degrees, must be 0, 90, 180, or 270.
Default is 0 or Normal orientation.

The `/dev/fb0` special file will exist if a monitor is plugged in at boot time,
or if `/boot/firmware/cmdline.txt` specifies a resolution such as
`... video=HDMI-A-1:640x480M@60D`.
"""

import sys

import adafruit_raspberry_pi5_piomatter
import numpy as np
import PIL.Image as Image

if len(sys.argv) >= 2:
scale = int(sys.argv[1])
else:
scale = 3

if len(sys.argv) >= 3:
width = int(sys.argv[2])
else:
width = 64

if len(sys.argv) >= 4:
height = int(sys.argv[3])
else:
height = 32

if len(sys.argv) >= 5:
rotation = int(sys.argv[4])
if rotation == 90:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.CW
elif rotation == 180:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.R180
elif rotation == 270:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.CCW
elif rotation == 0:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal
else:
raise ValueError("Invalid rotation. Must be 0, 90, 180, or 270.")
else:
rotation = adafruit_raspberry_pi5_piomatter.Orientation.Normal

with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]

Expand All @@ -32,11 +72,8 @@

xoffset = 0
yoffset = 0
width = 64
height = 32
scale = 3

geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal)
geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=rotation)
matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8)
matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(matrix_framebuffer, geometry)

Expand Down
Binary file added examples/nyan.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions examples/play_gif.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/python3
"""
Display an animated gif

Run like this:

$ python play_gif.py

The animated gif is played repeatedly until interrupted with ctrl-c.
"""

import time

import adafruit_raspberry_pi5_piomatter
import numpy as np
import PIL.Image as Image

width = 64
height = 32

gif_file = "nyan.gif"

canvas = Image.new('RGB', (width, height), (0, 0, 0))
geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4, rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal)
framebuffer = np.asarray(canvas) + 0 # Make a mutable copy
matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(framebuffer, geometry)

with Image.open(gif_file) as img:
print(f"frames: {img.n_frames}")
while True:
for i in range(img.n_frames):
img.seek(i)
canvas.paste(img, (0,0))
framebuffer[:] = np.asarray(canvas)
matrix.show()
time.sleep(0.1)
66 changes: 66 additions & 0 deletions examples/quote_scroller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/python3
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Display quote from the Adafruit quotes API as text scrolling across the
matrices.

Requires the requests library to be installed.

Run like this:

$ python quote_scroller.py

"""

import adafruit_raspberry_pi5_piomatter
import numpy as np
import requests
from PIL import Image, ImageDraw, ImageFont

# 128px for 2x1 matrices. Change to 64 if you're using a single matrix.
total_width = 128
total_height = 32

bottom_half_shift_compensation = 1

font_color = (0, 128, 128)

# Load the font
font = ImageFont.truetype("LindenHill-webfont.ttf", 26)

quote_resp = requests.get("https://www.adafruit.com/api/quotes.php").json()

text = f'{quote_resp[0]["text"]} - {quote_resp[0]["author"]}'
#text = "Sometimes you just want to use hardcoded strings. - Unknown"

x, y, text_width, text_height = font.getbbox(text)

full_txt_img = Image.new("RGB", (int(text_width) + 6, int(text_height) + 6), (0, 0, 0))
draw = ImageDraw.Draw(full_txt_img)
draw.text((3, 3), text, font=font, fill=font_color)
full_txt_img.save("quote.png")

single_frame_img = Image.new("RGB", (total_width, total_height), (0, 0, 0))

geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=total_width, height=total_height, n_addr_lines=4, rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal)
framebuffer = np.asarray(single_frame_img) + 0 # Make a mutable copy

matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(framebuffer, geometry)

print("Ctrl-C to exit")
while True:
for x_pixel in range(-total_width-1,full_txt_img.width):
if bottom_half_shift_compensation == 0:
# full paste
single_frame_img.paste(full_txt_img.crop((x_pixel, 0, x_pixel + total_width, total_height)), (0, 0))

else:
# top half
single_frame_img.paste(full_txt_img.crop((x_pixel, 0, x_pixel + total_width, total_height//2)), (0, 0))
# bottom half shift compensation
single_frame_img.paste(full_txt_img.crop((x_pixel, total_height//2, x_pixel + total_width, total_height)), (bottom_half_shift_compensation, total_height//2))

framebuffer[:] = np.asarray(single_frame_img)
matrix.show()
102 changes: 102 additions & 0 deletions examples/rainbow_spiral.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/python3
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Display a simple test pattern of 3 shapes on a single 64x32 matrix panel.

Run like this:

$ python simpletest.py

"""
import adafruit_raspberry_pi5_piomatter
import numpy as np
import rainbowio
from PIL import Image, ImageDraw

width = 64
height = 32
pen_radius = 1


canvas = Image.new('RGB', (width, height), (0, 0, 0))
draw = ImageDraw.Draw(canvas)

geometry = adafruit_raspberry_pi5_piomatter.Geometry(width=width, height=height, n_addr_lines=4,
rotation=adafruit_raspberry_pi5_piomatter.Orientation.Normal)
framebuffer = np.asarray(canvas) + 0 # Make a mutable copy
matrix = adafruit_raspberry_pi5_piomatter.AdafruitMatrixBonnetRGB888Packed(framebuffer, geometry)

color_index = 0

def update_matrix():
framebuffer[:] = np.asarray(canvas)
matrix.show()

def darken_color(hex_color, darkness_factor):
# Convert hex color number to RGB
r = (hex_color >> 16) & 0xFF
g = (hex_color >> 8) & 0xFF
b = hex_color & 0xFF

# Apply darkness factor
r = int(r * (1 - darkness_factor))
g = int(g * (1 - darkness_factor))
b = int(b * (1 - darkness_factor))

# Ensure values are within the valid range
r = max(0, min(255, r))
g = max(0, min(255, g))
b = max(0, min(255, b))

# Convert RGB back to hex number
darkened_hex_color = (r << 16) + (g << 8) + b

return darkened_hex_color

step_count = 4
darkness_factor = 0.5

clearing = False

try:
# step_down_size = pen_radius * 2 + 2

while True:
for step in range(step_count):
step_down_size = step * (pen_radius* 2) + (2 * step)
for x in range(pen_radius + step_down_size, width - pen_radius - step_down_size - 1):
color_index = (color_index + 2) % 256
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
draw.circle((x, pen_radius + step_down_size), pen_radius, color)
update_matrix()
for y in range(pen_radius + step_down_size, height - pen_radius - step_down_size - 1):
color_index = (color_index + 2) % 256
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
draw.circle((width - pen_radius - step_down_size -1, y), pen_radius, color)
update_matrix()
for x in range(width - pen_radius - step_down_size - 1, pen_radius + step_down_size, -1):
color_index = (color_index + 2) % 256
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
draw.circle((x, height - pen_radius - step_down_size - 1), pen_radius, color)
update_matrix()
for y in range(height - pen_radius - step_down_size - 1, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1))) -1, -1):
color_index = (color_index + 2) % 256
color = darken_color(rainbowio.colorwheel(color_index), darkness_factor) if not clearing else 0x000000
draw.circle((pen_radius + step_down_size, y), pen_radius, color)
update_matrix()

if step != step_count-1:
# connect to next iter
for x in range(pen_radius + step_down_size, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1)))):
color_index = (color_index + 2) % 256
color = darken_color(rainbowio.colorwheel(color_index),
darkness_factor) if not clearing else 0x000000
draw.circle((x, pen_radius + ((step+1) * (pen_radius* 2) + (2 * (step+1)))), pen_radius, color)
update_matrix()

clearing = not clearing

except KeyboardInterrupt:
print("Exiting")
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
Adafruit-Blinka
adafruit-circuitpython-pioasm
numpy
pillow