From b02b302823d104f9aac9f94cf11ce2084a30968e Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Thu, 17 Sep 2026 08:59:47 -0400 Subject: [PATCH] docs(display): library limits, template defaults and Pi 5 slowdown - rows 8-64, chain_length 1-255, parallel limited by the mapping's outputs, classic/classic-pi1 mappings and orientation 90/270 documented. - Defaults are the config.template.json values: config migration adds missing keys from the template, so the listed "code defaults" never applied. - One Raspberry Pi 5 gpio_slowdown recommendation: 1-3 in PIO mode, starting at 1. - Troubleshooting describes the refused-settings fallback, and CHANGELOG corrects the Unreleased "no upper limit" entry. Audit findings F19, F20, F21. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 37 ++++++++++++++++++++++++++++++++++--- README.md | 27 ++++++++++++++++----------- docs/CONFIG_REFERENCE.md | 33 +++++++++++++++++++-------------- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea497352..6f5c1c28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,9 +68,9 @@ Web interface: declared, e.g. countdown's row `id` and weather's `api_key` / `radar_zoom`. See `docs/widget-guide.md`. - Display settings no longer silently cut values on save: columns were capped - at 128, chain length at 24 and PWM LSB nanoseconds at 500. Rows, columns and - chain length now have no upper limit (the current rgbmatrix library still - rejects more than 64 rows per panel); rows must be even and at least 8, + at 128, chain length at 24 and PWM LSB nanoseconds at 500. Columns have no + upper limit, chain length is 1–255 and rows must be even and 8–64 (see + "Display hardware settings the library refuses" below); parallel is 1–3 and PWM dither bits 0–2, matching the library. A stored GPIO slowdown, PWM dither bits or refresh-rate cap of 0 no longer shows (and re-saves) as 3, 1 or 120, and the refresh cap accepts 0 (no cap). The config @@ -112,6 +112,37 @@ Web interface: re-sent with backoff instead of being counted as failed and skipped — that is how a disabled plugin with an update waiting was silently left out. +Display hardware settings the library refuses: + +- The rgbmatrix library answers several settings with no matrix or `abort()` + rather than an error, on every board, so the display service crash-looped + instead of falling back: rows above 64, `chain_length` above 255 (the Python + binding stores it in one byte; this was documented as "no upper limit"), a + misspelled `hardware_mapping`, and `parallel` 2–3 on a mapping with one output + (`adafruit-hat`, `adafruit-hat-pwm`, `regular-pi1`, `classic-pi1`) — the last + one reachable from the Display form on the default mapping. The config API + now refuses them with a 400 naming the setting, and `DisplayManager` refuses + a hand-edited one before creating the matrix: logged, fallback mode, reported + by `/api/v3/hardware/status`. The rules, including the Pi 5 ones, live in + `src/matrix_support.py` and must be re-checked when the submodule is bumped. +- `/api/v3/hardware/status` adds `cause`: `"settings"` when LEDMatrix refused + the config, `"library"` when the library failed. The Display tab banner and + the fallback log line give the Pi 5 rebuild hint only for a library failure; + they used to follow every failure with it and with GPIO slowdown advice. +- The Display form offers the `classic` and `classic-pi1` mappings and the + `90` / `270` orientations, and renders any other stored mapping selected with + a warning. With no option selected the browser posted the first one, so one + unrelated save rewrote those settings. The API accepts orientation `90` and + `270`, which `DisplayManager` already applied. +- The display size the web preview, Starlark magnify default and + `scripts/dev/vegas_audit.py` compute (`src/display_geometry.py`) now applies + `orientation` and `pixel_mapper_config` as the library does: `Rotate:90` + swaps width and height, `U-mapper` folds the chain. +- One Raspberry Pi 5 GPIO slowdown recommendation everywhere: 1–3 in PIO mode, + starting at 1. README and the config reference now describe the template + values as the defaults; the "code default" values they listed never apply, + because config migration fills missing keys from the template. + Plugin system: - A plugin no longer starts with a schema warning and a degraded flag because diff --git a/README.md b/README.md index 0a77ce1f..162003b9 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ The system supports live, recent, and upcoming game information for multiple spo ```bash sudo RPI_RGB_FORCE_REBUILD=1 ./first_time_install.sh ``` - - Pi 5 config: leave `rp1_rio` at `0` (PIO mode, default) and set `gpio_slowdown` to `1` or `2`. + - Pi 5 config: leave `rp1_rio` at `0` (PIO mode, default) and start `gpio_slowdown` at `1`, raising it a step at a time if the image flickers or shows garbage (see `gpio_slowdown` under Display Settings). - **1GB models (Pi 3B / 3B+) and other low-memory boards**: supported, but the `rpi-rgb-led-matrix` C++ build needs more memory than the Pi has. The installer detects this automatically, compiles with fewer parallel jobs, and adds a temporary swapfile for the build which it removes afterwards. Expect that step to take 15-25 minutes instead of 2-5, and leave at least **3GB free** on the SD card. If you manage swap yourself, opt out with `--skip-swap`. To pin the compiler down further, use `--build-jobs 1`. @@ -485,6 +485,10 @@ If you are copying my exact setup, you can likely leave the defaults alone. Howe The display settings are located in `config/config.json` under the `"display"` key and are organized into three main sections: `hardware`, `runtime`, and `display_durations`. +The defaults below are the values in `config/config.template.json`. They are what applies when you haven't set a key: on every load, LEDMatrix adds any key your `config.json` lacks from the template, so `DisplayManager`'s own fallbacks are never reached on a normal install. + +The web UI and the config API refuse values the rgbmatrix library can't start with. If one is written into `config.json` by hand anyway, the display logs which setting it is (`Failed to initialize RGB Matrix` in `sudo journalctl -u ledmatrix`), runs in fallback mode, and the Display tab shows the message. + ### Hardware Configuration (`display.hardware`) These settings control the physical hardware configuration and how the matrix is driven. @@ -494,9 +498,7 @@ These settings control the physical hardware configuration and how the matrix is - **`rows`** (integer, default: 32) - Number of LED rows (vertical pixels) in each panel - Common values: 16, 32, 48, 64 - - Must be an even number, at least 8. LEDMatrix sets no upper limit, but the - current rgbmatrix library rejects more than 64 rows per panel — the display - then won't start (see Troubleshooting Display Settings below) + - An even number from 8 to 64, the most the rgbmatrix library drives per panel - Must match your physical panel configuration - **`cols`** (integer, default: 64) @@ -507,7 +509,7 @@ These settings control the physical hardware configuration and how the matrix is - **`chain_length`** (integer, default: 2) - Number of LED panels chained together horizontally - - At least 1, with no upper limit; longer chains lower the refresh rate + - 1 to 255 (the library's Python binding stores it in one byte); longer chains lower the refresh rate - If you have 2 panels side-by-side, set to 2 - If you have 4 panels in a row, set to 4 - Total display width = `cols × chain_length` @@ -516,7 +518,7 @@ These settings control the physical hardware configuration and how the matrix is - Number of parallel chains (panels stacked vertically) - Use 1 for a single row of panels - Use 2 if you have panels stacked in two rows - - 1–3 on a Raspberry Pi, and the HAT needs that many outputs + - 1–3, and no more than your `hardware_mapping` has outputs: `regular` and `classic` have 3 (e.g. the Adafruit Triple LED Matrix Bonnet); `adafruit-hat`, `adafruit-hat-pwm`, `regular-pi1` and `classic-pi1` have 1. The library stops the display service outright on a mismatch, so it is refused - Total display height = `rows × parallel` #### Brightness and Visual Settings @@ -529,12 +531,14 @@ These settings control the physical hardware configuration and how the matrix is #### Hardware Mapping -- **`hardware_mapping`** (string, default: "adafruit-hat-pwm") +- **`hardware_mapping`** (string, default: "adafruit-hat") - Specifies which GPIO pin mapping to use for your hardware - **`"adafruit-hat-pwm"`**: Use this for Adafruit RGB Matrix Bonnet/HAT WITH the jumper mod (PWM enabled). This is the recommended setting for Adafruit hardware with the PWM jumper soldered. - **`"adafruit-hat"`**: Use this for Adafruit RGB Matrix Bonnet/HAT WITHOUT the jumper mod (no PWM). Remove `-pwm` from the value if you did not solder the jumper. - **`"regular"`**: Standard GPIO pin mapping for direct GPIO connections (Generic). Also the right choice for the Adafruit Triple LED Matrix Bonnet - **`"regular-pi1"`**: Standard GPIO pin mapping for Raspberry Pi 1 (older hardware or non-standard hat mapping) + - **`"classic"`** / **`"classic-pi1"`**: the library's original pin-outs, for old adapter boards wired to them. Not used by current HATs + - Any other name is refused. `compute-module` is only compiled in when the library is built with `ENABLE_WIDE_GPIO_COMPUTE_MODULE`, which the installer doesn't do. On a Raspberry Pi 5, `classic-pi1` isn't supported - Choose the option that matches your specific hardware setup, if aren't sure try them all. - Hardware pulsing (see `disable_hardware_pulsing`) needs the panel's OE line on GPIO 18, which `adafruit-hat-pwm` and `regular` provide and `adafruit-hat` does not @@ -571,7 +575,6 @@ These settings affect color fidelity and smoothness of color transitions: - Caps the panel refresh rate in Hz; `0` = no cap - A steady cap reduces flicker caused by other activity on the Pi, and in camera recordings - Scroll speeds are worked out against this value (against 100 Hz when it is `0`), so a cap the panel can actually hold keeps scrolling even - - If the key is missing from the config, `DisplayManager` uses 90 - Recommended: 80-120. `sudo python3 scripts/scroll_speeds.py --measure` reports the rate your panel really achieves - **`disable_hardware_pulsing`** (boolean, default: false) @@ -613,8 +616,10 @@ These settings are typically only needed for non-standard panels or custom confi - Set to `"180"` (or use the "Upside Down" option in the web UI's Display settings) if the panel is mounted upside down — useful for optimizing where the Raspberry Pi and wiring sit relative to the mounting location + - `"90"` and `"270"` are for a panel mounted on its side; they swap the + display's width and height - Applied independently of `pixel_mapper_config` (appended as a trailing - `Rotate:180` mapper), so custom mapper configs keep working alongside it + `Rotate:` mapper), so custom mapper configs keep working alongside it - **`row_address_type`** (integer, default: 0) - How rows are addressed on the panel @@ -663,7 +668,7 @@ These settings control runtime behavior and GPIO timing: - **Raspberry Pi Zero/1**: 0-1 - **Raspberry Pi 2/3**: 1-3 - **Raspberry Pi 4**: 2-4 (the config template ships 3) - - **Raspberry Pi 5**: 1–3 in PIO mode (`rp1_rio: 0`, the default); start with `1` and increase if you see flickering + - **Raspberry Pi 5**: 1–3 in PIO mode (`rp1_rio: 0`, the default). Start at `1` (the library treats `0` as `1` there) and raise it a step at a time if the image flickers or shows garbage — chained panels are the likeliest to need it - Panels on `row_address_type` 5 (SM5368 row drivers) can need 6-8 on a Pi 4 - Too low: garbage, flicker or rows jumping. Too high: a lower refresh rate - If you experience issues, try adjusting this value up or down by 1 @@ -752,7 +757,7 @@ Controls how long each installed plugin stays visible in seconds before switchin - Verify `hardware_mapping` matches your HAT/connection type - Try adjusting `gpio_slowdown` - Ensure your display doesn't need the E-Addressable line -- If it went blank right after a settings change, check `sudo journalctl -u ledmatrix` for `Failed to initialize RGB Matrix`: the rgbmatrix library refused a value (for example more than 64 `rows`, or `pwm_dither_bits` above 2) and the display fell back to no output. The library's own message nearby names the setting; on a Raspberry Pi 5, LEDMatrix's message names any unsupported `row_address_type`, `parallel` or `hardware_mapping` +- If it went blank right after a settings change, the Display tab shows a "simulation mode" banner, and `sudo journalctl -u ledmatrix` shows `Failed to initialize RGB Matrix` followed by the reason. When LEDMatrix refused the settings (for example more than 64 `rows`, `parallel` 2 on an `adafruit-hat` mapping, a misspelled `hardware_mapping`, or on a Raspberry Pi 5 a `row_address_type` other than 0 or 2), the message names each one: change them, save, and restart the display service. Otherwise the library itself failed, and its own message just before names the problem - A repeating scramble points at `row_address_type` or `multiplexing`; a panel that stays dark, at `panel_type` **Rows jump up and down, or the bottom row repeats other rows:** diff --git a/docs/CONFIG_REFERENCE.md b/docs/CONFIG_REFERENCE.md index f4d991ca..834d1f4b 100644 --- a/docs/CONFIG_REFERENCE.md +++ b/docs/CONFIG_REFERENCE.md @@ -47,38 +47,43 @@ saved via `POST /api/v3/config/dim-schedule`). The display returns to ## `display.hardware` — matrix panel hardware All keys map to the corresponding `rpi-rgb-led-matrix` options and are read -in `DisplayManager` (`src/display_manager.py`, ~lines 270–295). +in `DisplayManager._setup_matrix` (`src/display_manager.py`). Defaults are the +`config/config.template.json` values: `ConfigManager` adds any key missing from +`config.json` from the template on load, so `DisplayManager`'s own fallbacks +don't apply on a normal install. + +The ranges are what the pinned rgbmatrix library and its Python binding accept +(`src/matrix_support.py`). The config API refuses anything else; a value +hand-edited into `config.json` makes the display log the setting and run in +fallback mode instead of starting the matrix. | Key | Type / default | |---|---| -| `rows` / `cols` | int, `32` / `64` — rows: even, at least 8, no upper limit here (the current rgbmatrix library rejects more than 64); cols: at least 16, no upper limit | -| `chain_length` | int, `2` — at least 1, no upper limit | -| `parallel` | int, `1` — 1–3 | +| `rows` / `cols` | int, `32` / `64` — rows: even, 8–64; cols: at least 16 | +| `chain_length` | int, `2` — 1–255 (the Python binding stores it in one byte) | +| `parallel` | int, `1` — 1–3, and no more than `hardware_mapping` has outputs (`regular`, `classic`: 3; the others: 1) | | `brightness` | int, `90` — 1–100 | -| `hardware_mapping` | string, `"adafruit-hat"` (code default `"adafruit-hat-pwm"`) | +| `hardware_mapping` | string, `"adafruit-hat"` — `"adafruit-hat-pwm"`, `"adafruit-hat"`, `"regular"`, `"regular-pi1"`, `"classic"` or `"classic-pi1"` (case-insensitive; `compute-module` isn't in the installed build). A Pi 5 doesn't support `"classic-pi1"` | | `scan_mode` | int, `0` — `0` progressive, `1` interlaced | -| `pwm_bits` | int, `9` (code default 10) — 1–11 | +| `pwm_bits` | int, `9` — 1–11 | | `pwm_dither_bits` | int, `1` — 0–2 | -| `pwm_lsb_nanoseconds` | int, `130` (code default 150) — 50–3000 | +| `pwm_lsb_nanoseconds` | int, `130` — 50–3000 | | `disable_hardware_pulsing` | bool, `false` — `true` times brightness pulses in software (less exact); hardware pulsing needs the OE line on GPIO 18 and the Pi's onboard sound driver off | | `inverse_colors` | bool, `false` | | `show_refresh_rate` | bool, `false` — prints the refresh rate to stdout; draws nothing on the panel | | `led_rgb_sequence` | string, `"RGB"` — `"RGB"`, `"RBG"`, `"GRB"`, `"GBR"`, `"BRG"` or `"BGR"` | -| `limit_refresh_rate_hz` | int, `100` (code default 90) — `0` = no cap; scroll timing assumes 100 Hz when `0` | -| `pixel_mapper_config` | string, `""` — e.g. `"U-mapper"` / `"Rotate:90"` | -| `orientation` | string, `"normal"` — `"180"` rotates the rendered image 180° for panels physically mounted upside down (e.g. to move the Pi/wiring to a more convenient side); composed onto `pixel_mapper_config` as a trailing `Rotate:180` mapper, so it stays independent of any custom `pixel_mapper_config` value | +| `limit_refresh_rate_hz` | int, `100` — `0` = no cap; scroll timing assumes 100 Hz when `0` | +| `pixel_mapper_config` | string, `""` — e.g. `"U-mapper"` / `"Rotate:90"`; mappers that rotate or fold the chain change the display size plugins and the web preview see | +| `orientation` | string, `"normal"` — `"180"` rotates the rendered image 180° for panels physically mounted upside down (e.g. to move the Pi/wiring to a more convenient side); `"90"` / `"270"` for a panel on its side, swapping width and height; composed onto `pixel_mapper_config` as a trailing `Rotate:` mapper, so it stays independent of any custom `pixel_mapper_config` value | | `row_address_type` | int, `0` — non-standard panel row addressing: `1` AB, `2` direct row select, `3` ABC, `4` ABC shift + DE direct, `5` SM5368 / B707 row shift register (e.g. Waveshare 96x48 V2, with `led_rgb_sequence` `"BGR"`). On a Pi 5 the library supports only `0` and `2`, and LEDMatrix enforces that (`src/pi5_matrix_support.py`) | | `multiplexing` | int, `0` — 0–22, pixel wiring scheme for outdoor/specialty panels (names listed in the README) | | `panel_type` | string, `""` — set to `"FM6126A"` or `"FM6127"` for panels needing init; FM6124 / FM6124D / FM6124DJ panels need none, so leave it `""` | -Where "code default" differs from the template value, the code default only -applies if the key is missing entirely from your config. - ## `display.runtime` | Key | Type / default | Meaning | |---|---|---| -| `gpio_slowdown` | int, `3` | GPIO timing slowdown for faster Pis (0–10). Panels on `row_address_type` `5` (SM5368 row drivers) can need 6–8 on a Pi 4 — lower values make rows jump | +| `gpio_slowdown` | int, `3` | GPIO timing slowdown for faster Pis (0–10). On a Pi 5 in PIO mode start at `1` (`0` acts as `1`) and raise it if the image flickers or shows garbage. Panels on `row_address_type` `5` (SM5368 row drivers) can need 6–8 on a Pi 4 — lower values make rows jump | | `rp1_rio` | int, `0` | Pi 5 only: `0` = PIO (less CPU), `1` = RIO (higher refresh; `gpio_slowdown` effect inverted). Applied only if the installed matrix library supports it | ## `display.double_sided`