Compare commits

...

12 commits

Author SHA1 Message Date
foamyguy
00101f6668
Merge pull request #9 from FoamyGuy/various_touchups
Some checks failed
Pip / build (ubuntu-24.04-arm, 3.11) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.12) (push) Has been cancelled
Pip / build (ubuntu-24.04-arm, 3.13) (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Wheels / Build SDist (push) Has been cancelled
Wheels / Wheels on ubuntu-24.04-arm (push) Has been cancelled
Wheels / Upload release (push) Has been cancelled
Various touchups
2025-02-04 17:17:11 -06:00
foamyguy
5452337f38 fbmirror.py args fix 2025-02-04 12:10:53 -06:00
foamyguy
502beb6cda animated gif example 2025-02-04 10:08:27 -06:00
foamyguy
33b8041ef6 font color var, normal rotation, fix margin 2025-02-04 08:50:20 -06:00
foamyguy
c3675b74bd fix rotation, use smaller size pen 2025-02-04 08:38:36 -06:00
foamyguy
b19bfce407 int radius 2025-02-04 08:33:04 -06:00
foamyguy
6caa68ec13 new examples, parameterize fbmirrors, add pillow to req, add font license 2025-02-04 08:27:12 -06:00
foamyguy
73ad9de612 Merge branch 'refs/heads/main' into various_touchups 2025-02-04 08:21:37 -06:00
foamyguy
cc110b3ce5 parameterize quote scroller 2025-02-03 17:53:32 -06:00
foamyguy
53e580e404 quote scroller 2025-01-30 20:22:15 -06:00
foamyguy
d9790082c3 adding requirements.txt 2025-01-30 09:50:31 -06:00
foamyguy
ba64d13463 udev rules.d directory 2025-01-30 09:10:32 -06:00
10 changed files with 299 additions and 10 deletions

View file

@ -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"

BIN
examples/LindenHill-webfont.ttf Executable file

Binary file not shown.

View file

@ -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

View file

@ -4,18 +4,57 @@ Mirror a scaled copy of the framebuffer to a 64x32 matrix
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(",")]
@ -32,9 +71,8 @@ with open("/sys/class/graphics/fb0/stride") as f:
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)

View file

@ -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(",")]
@ -32,11 +72,8 @@ linux_framebuffer = np.memmap('/dev/fb0',mode='r', shape=(screeny, stride // byt
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)

BIN
examples/nyan.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

36
examples/play_gif.py Normal file
View file

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

View file

@ -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
examples/rainbow_spiral.py Normal file
View file

@ -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
requirements.txt Normal file
View file

@ -0,0 +1,7 @@
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense
Adafruit-Blinka
adafruit-circuitpython-pioasm
numpy
pillow