Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cab2d42
Move everything to a package structure
jepler Feb 16, 2025
97d5a44
Allow passing in the matrix map directly to a Geometry object
jepler Feb 18, 2025
47226cf
Move the click options into the piomatter package
jepler Mar 6, 2025
4ffb57c
Reorganize includes per ruff's preferences
jepler Mar 6, 2025
d26bba8
Update ruff config
jepler Mar 6, 2025
d008684
Add active3 pinout (only first connector works)
jepler Mar 6, 2025
cfec606
WIP support more RGB out pins
jepler Feb 10, 2025
274adf1
Improve error reporting when address lines wrong
jepler Mar 6, 2025
296047a
Allow using fewer than all the lanes
jepler Mar 6, 2025
65b8b6f
A variant spiral demo now works on active3!
jepler Mar 7, 2025
4fba6a6
Merge remote-tracking branch 'origin/main' into make-package
jepler Mar 7, 2025
25c324d
run pre-commit and commit formatting change
jepler Mar 7, 2025
39bc663
introduce pixel schedules
jepler Mar 9, 2025
68c24fc
allow multiple schedules for temporal dithering
jepler Mar 9, 2025
7c75692
plumb temporal dither most of the way through
jepler Mar 10, 2025
a5f5052
tidy some build errors
jepler Mar 10, 2025
4f548a3
the rest of the plumbing. now it should be possible to use temporal d…
jepler Mar 10, 2025
e2c5bc3
tweak comment & no-temporal check
jepler Mar 10, 2025
2442bee
Make temporal dithering schedules actually work
jepler Mar 10, 2025
9bd7813
Add rudimentary multi-connector support to fbmirror as well
jepler Mar 10, 2025
1d9d652
update piomatter_click import
FoamyGuy Mar 10, 2025
8ae0fea
move simple_multilane_mapper into package
jepler Mar 11, 2025
372a158
Make n_temporal_planes, n_lanes "standard" argmuents
jepler Mar 11, 2025
908766e
remove deprecated aliases
jepler Mar 11, 2025
f7a247e
this is a private variable
jepler Mar 11, 2025
3fb93d3
Finish removing deprecated aliases
jepler Mar 11, 2025
34d8cd1
Update copyright year
jepler Mar 11, 2025
b26f8dc
Another file to rename
jepler Mar 11, 2025
6d3e236
This improves the doc situation
jepler Mar 11, 2025
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/*.pio.h
protodemo
/build
*.egg-info
8 changes: 8 additions & 0 deletions docs/adafruit_blinka_raspberry_pi5_piomatter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
HUB75 matrix driver for Raspberry Pi 5 using PIO
------------------------------------------------

.. autosummary::
:toctree: _generate
:recursive:

adafruit_blinka_raspberry_pi5_piomatter
1 change: 0 additions & 1 deletion docs/adafruit_raspberry_pi5_piomatter.rst

This file was deleted.

20 changes: 17 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,27 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"autoapi.extension",
"sphinx.ext.intersphinx",
"sphinx.ext.autosummary",
"sphinx.ext.napoleon",
]

autosummary_generate = True
autoapi_keep_files = True
autoapi_dirs = ["../src/adafruit_blinka_raspberry_pi5_piomatter"]
autoapi_add_toctree_entry = True
autoapi_options = [
"members",
"undoc-members",
"show-inheritance",
"special-members",
"show-module-summary",
]

autoapi_python_class_content = "both"
autoapi_python_use_implicit_namespaces = True
autoapi_template_dir = "autoapi/templates"
autoapi_root = "api"

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
Expand All @@ -50,7 +64,7 @@

# General information about the project.
project = "adafruit-blinka-pi5-piomatter"
copyright = "2023 Jeff Epler"
copyright = "2025 Jeff Epler"
author = "Jeff Epler"

# The version info for the project you're documenting, acts as replacement for
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
# SPDX-License-Identifier: Unlicense

sphinx
sphinx-autoapi
sphinx-rtd-theme
sphinxcontrib-jquery
29 changes: 25 additions & 4 deletions examples/fbmirror.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
"""


import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import click
import numpy as np
import piomatter_click

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper

with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]
Expand All @@ -33,12 +35,31 @@

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

def make_pixelmap_multilane(width, height, n_addr_lines, n_lanes):
calc_height = n_lanes << n_addr_lines
if height != calc_height:
raise RuntimeError(f"Calculated height {calc_height} does not match requested height {height}")
n_addr = 1 << n_addr_lines

m = []
for addr in range(n_addr):
for x in range(width):
for lane in range(n_lanes):
y = addr + lane * n_addr
m.append(x + width * y)
print(m)
return m

@click.command
@click.option("--x-offset", "xoffset", type=int, help="The x offset of top left corner of the region to mirror", default=0)
@click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0)
@piomatter_click.standard_options
def main(xoffset, yoffset, width, height, serpentine, rotation, pinout, n_planes, n_addr_lines):
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation)
def main(xoffset, yoffset, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes):
if n_lanes != 2:
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, rotation=rotation)
framebuffer = np.zeros(shape=(geometry.height, geometry.width), dtype=dtype)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB565, pinout=pinout, framebuffer=framebuffer, geometry=geometry)

Expand Down
14 changes: 10 additions & 4 deletions examples/fbmirror_scaled.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@
`... video=HDMI-A-1:640x480M@60D`.
"""

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import click
import numpy as np
import PIL.Image as Image
import piomatter_click

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper

with open("/sys/class/graphics/fb0/virtual_size") as f:
screenx, screeny = [int(word) for word in f.read().split(",")]
Expand All @@ -65,8 +67,12 @@
@click.option("--y-offset", "yoffset", type=int, help="The y offset of top left corner of the region to mirror", default=0)
@click.option("--scale", "scale", type=int, help="The scale factor to reduce the display down by.", default=3)
@piomatter_click.standard_options
def main(xoffset, yoffset, scale, width, height, serpentine, rotation, pinout, n_planes, n_addr_lines):
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, rotation=rotation)
def main(xoffset, yoffset, scale, width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes):
if n_lanes != 2:
pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_addr_lines=n_addr_lines, n_temporal_planes=n_temporal_planes, n_lanes=n_lanes, map=pixelmap)
else:
geometry = piomatter.Geometry(width=width, height=height, n_planes=n_planes, n_temporal_planes=n_temporal_planes, n_addr_lines=n_addr_lines, rotation=rotation)
matrix_framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8)
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=matrix_framebuffer, geometry=geometry)

Expand Down
3 changes: 2 additions & 1 deletion examples/play_gif.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

import time

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import PIL.Image as Image

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

width = 64
height = 32

Expand Down
3 changes: 2 additions & 1 deletion examples/playframes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
import sys
import time

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import PIL.Image as Image

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

images = sorted(glob.glob(sys.argv[1]))

geometry = piomatter.Geometry(width=64, height=32, n_addr_lines=4, rotation=piomatter.Orientation.Normal)
Expand Down
3 changes: 2 additions & 1 deletion examples/quote_scroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@

"""

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import requests
from PIL import Image, ImageDraw, ImageFont

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

# 128px for 2x1 matrices. Change to 64 if you're using a single matrix.
total_width = 128
total_height = 32
Expand Down
3 changes: 2 additions & 1 deletion examples/rainbow_spiral.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
$ python rainbow_spiral.py

"""
import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import rainbowio
from PIL import Image, ImageDraw

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

width = 64
height = 32
pen_radius = 1
Expand Down
114 changes: 114 additions & 0 deletions examples/rainbow_spiral_active3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/python3
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Display a spiral around the display drawn with a rainbow color.

Run like this:

$ python rainbow_spiral.py

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

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
from adafruit_blinka_raspberry_pi5_piomatter.pixelmappers import simple_multilane_mapper

width = 64
n_lanes = 6
n_addr_lines = 5
height = n_lanes << n_addr_lines
pen_radius = 1

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

pixelmap = simple_multilane_mapper(width, height, n_addr_lines, n_lanes)
geometry = piomatter.Geometry(width=width, height=height, n_addr_lines=n_addr_lines, n_planes=10, n_temporal_planes=4, map=pixelmap, n_lanes=n_lanes)
framebuffer = np.asarray(canvas) + 0 # Make a mutable copy
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed,
pinout=piomatter.Pinout.Active3,
framebuffer=framebuffer,
geometry=geometry)

color_index = 0

update_interval = 3
update_counter = 0
def update_matrix():
global update_counter
if (update_counter := update_counter + 1) >= update_interval:
framebuffer[:] = np.asarray(canvas)
matrix.show()
update_counter = 0

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

print(matrix.fps)
clearing = not clearing

except KeyboardInterrupt:
print("Exiting")
3 changes: 2 additions & 1 deletion examples/simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

import pathlib

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import PIL.Image as Image

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

geometry = piomatter.Geometry(width=64, height=64, n_addr_lines=4, rotation=piomatter.Orientation.Normal)
framebuffer = np.asarray(Image.open(pathlib.Path(__file__).parent / "blinka64x64.png"))
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed,
Expand Down
3 changes: 2 additions & 1 deletion examples/simpletest_addre_bgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@

import pathlib

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
import PIL.Image as Image

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

geometry = piomatter.Geometry(width=64, height=64, n_addr_lines=5, rotation=piomatter.Orientation.Normal, n_planes=8)
framebuffer = np.asarray(Image.open(pathlib.Path(__file__).parent / "blinka64x64.png"))
matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=piomatter.Pinout.AdafruitMatrixBonnetBGR, framebuffer=framebuffer, geometry=geometry)
Expand Down
3 changes: 2 additions & 1 deletion examples/single_panel_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

"""

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import numpy as np
from PIL import Image, ImageDraw

import adafruit_blinka_raspberry_pi5_piomatter as piomatter

width = 64
height = 32

Expand Down
5 changes: 3 additions & 2 deletions examples/virtualdisplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@
import shlex
from subprocess import Popen

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import click
import numpy as np
import piomatter_click
from pyvirtualdisplay.smartdisplay import SmartDisplay

import adafruit_blinka_raspberry_pi5_piomatter as piomatter
import adafruit_blinka_raspberry_pi5_piomatter.click as piomatter_click


@click.command
@click.option("--scale", type=float, help="The scale factor, larger numbers mean more virtual pixels", default=1)
Expand Down
Loading