Display Configuration
This example will mirror a display running under the X Window System to the matrix panel. Raspberry Pi OS now defaults to the Wayland window system instead of X. In order to use this example, you must configure the Pi 5 to use X for its window system display.
Launch raspi-config with sudo raspi-config
.
- Scroll down to Advanced Options and press enter
- Scroll down to Wayland and press enter
- Select the X11 option and press enter
- Press enter for Ok on the dialog confirming the configuration of X11.
- Scroll over to Finish and press enter
- When prompted select Yes to reboot and press enter.
To ensure that the system and any applications running believe that a display exists whether or not an HDMI display is plugged in, there will be a modification to the file /boot/firmware/cmdline.txt
that adds a video configuration with the smallest allowed display size.
sudo nano /boot/firmware/cmdline.txt
Scroll to the end of the line by pressing end or holding right arrow for a while.
Press space bar to insert a space at the end of the existing line.
Add the following after the space at the end of the existing line.
video=HDMI-A-1:640x480M@60D
Press Ctrl-S to save the file.
Press Ctrl-X to exit.
Then reboot the Pi for it to take effect
sudo reboot
Run X Display Mirror Example
As always, you need to activate the virtual environment before running the example. If you haven't already, then activate it like this.
source ~/venvs/blinka_venv/bin/activate
Next you can run the xdisplay_mirror.py script. The script accepts several arguments to configure the panels. The first 2 are specific to the xdisplay_mirror.py example.
-
--mirror-region
- The region of the display to mirror. x,y,w,h values seperated by commas. If omitted it will mirror the entire screen by default. Example:--mirror-region 0,0,128,128
to mirror a 128x128 pixel square of the display at real size on the matrices. -
--x-display
- The X display to use. The default is:0
. Only needs to be specified if you aren't mirroring the default display. -
--resample-method
- The PIL resample method to use when resizing the image to be sent to the panel. Valid values are:nearest
,bilinear
,lanczos
, andbicubic
. The default isnearest
which works best for pixel art style graphics that have been scaled up to fit a larger display. Other methods may look better for different graphics.
The remaining arguments are of the configuration options described on the Initialization Config page. The first two are required.
-
--width
- the number of pixels wide the total panel is. 128px is for a 2x2 matrix panel. -
--height
- the number of pixels tall the total panel is. 64px is for a 2x2 matrix panel.
The rest are optional if you're using default the configuration for everything.
-
--orientation
- the rotation angle, must be one ofNormal
,CW
,ÂCCW
,R180
. -
--pinout
- the pinout to use for the panel(s) e.g.AdafruitMatrixBonnet
-
--num-address-lines
- the number of address lines. The 64x64 panels we stock useÂ5
, all smaller panels use4
-
--num-planes
- the color depth, defaults to10
. Lower values can improve refresh rate speed. -
--num-temporal-planes
- the temporal dithering control. Valid values are0
,2
, or4
. -
--serpentine
- the organization of multiple panels, serpentine is default. Use--no-serpentine
if your panels are wired in the non-serpentine configuration.
The values for a 2x2 matrix panel made fro 64x64 matrices rotated 180 are shown below, adjust them for your panel(s).
python xdisplay_mirror.py --pinout AdafruitMatrixHatBGR --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 8
Once the display is mirrored simply open another terminal or SSH session and launch whatever app you want to mirror.
The code for xdisplay_mirror.py is embedded below. You can download it, or copy/paste the text into a file.
#!/usr/bin/python3 """ Display a (possibly scaled) X display to a matrix The display runs until this script exits. The display doesn't get a keyboard or mouse, so you have to use a program that will get its input in some other way, such as from a gamepad. For help with commandline arguments, run `python xdisplay_mirror.py --help` This example command will mirror the entire display scaled onto a 2x2 grid of 64px panels, total matrix size 128x128. $ python xdisplay_mirror.py --pinout AdafruitMatrixHatBGR --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 8 This example command will mirror a 128x128 pixel square from the top left of the display at real size on the same matrix panels $ python xdisplay_mirror.py --pinout AdafruitMatrixHatBGR --width 128 --height 128 --serpentine --num-address-lines 5 --num-planes 8 --mirror-region 0,0,128,128 """ import click import numpy as np from PIL import Image, ImageEnhance, ImageGrab 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 RESAMPLE_MAP = { "nearest": Image.NEAREST, "bilinear": Image.BILINEAR, "lanczos": Image.LANCZOS, "bicubic": Image.BICUBIC } @click.command @click.option("--mirror-region", help="Region of X display to mirror. Comma seperated x,y,w,h. " "Default will mirror entire display.", default="") @click.option("--x-display", help="The X display to mirror. Default is :0", default=":0") @click.option("--brightness", help="The brightness factor of the image output to the matrix", default=1.0, type=click.FloatRange(min=0.1, max=1.0)) @click.option("--resample-method", type=click.Choice(RESAMPLE_MAP), default="nearest", help="The resample method for PIL to use when resizing the screen image. Default is nearest") @piomatter_click.standard_options(n_lanes=2, n_temporal_planes=0) def main(width, height, serpentine, rotation, pinout, n_planes, n_temporal_planes, n_addr_lines, n_lanes, mirror_region, x_display, resample_method, brightness): 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, serpentine=serpentine) framebuffer = np.zeros(shape=(geometry.height, geometry.width, 3), dtype=np.uint8) matrix = piomatter.PioMatter(colorspace=piomatter.Colorspace.RGB888Packed, pinout=pinout, framebuffer=framebuffer, geometry=geometry) if mirror_region: mirror_region = tuple(int(_) for _ in mirror_region.split(',')) else: mirror_region = None size_measure = ImageGrab.grab(xdisplay=":0") print(f"Mirroring full display: {size_measure.width}, {size_measure.height}") while True: img = ImageGrab.grab(xdisplay=x_display) if mirror_region is not None: img = img.crop((mirror_region[0], mirror_region[1], # left,top mirror_region[0] + mirror_region[2], # right mirror_region[1] + mirror_region[3])) # bottom if brightness != 1.0: darkener = ImageEnhance.Brightness(img) img = darkener.enhance(brightness) img = img.resize((width, height), RESAMPLE_MAP[resample_method]) framebuffer[:, :] = np.array(img) matrix.show() if __name__ == '__main__': main()
Page last edited March 12, 2025
Text editor powered by tinymce.