mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-13 02:26:37 +00:00
feat: refuse matrix settings a Raspberry Pi 5 can't drive
On a Pi 5 the pinned rgbmatrix library drives the panel through the RP1 chip, and that path supports only row address types 0 and 2, parallel 1-3 and the regular / regular-pi1 / classic / adafruit-hat(-pwm) mappings (Rp1PioConfigSupported in lib/rp1/rp1_pio_backend.cc). For anything else CreateFromOptions returns NULL; the Python binding doesn't check, so the display process crashed on its first call into the matrix and systemd restarted it into the same crash every 10 seconds. - src/pi5_matrix_support.py: the rule and Pi 5 detection, matching the library's /proc/device-tree/model check - DisplayManager raises before creating the matrix, so it is a logged init failure (reported by /api/v3/hardware/status) and fallback mode - the config API rejects those settings on a Pi 5 when a request sets row_address_type, parallel or hardware_mapping - the Display form offers only row address types 0 and 2 on a Pi 5, and warns when a stored value can't be used - CLAUDE.md: re-check the rule whenever the submodule is bumped Review feedback on #586. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -279,3 +279,58 @@ class TestDisplayManagerOrientation:
|
||||
suppress_test_pattern=True)
|
||||
options = mock_rgb_matrix['options_class'].return_value
|
||||
assert options.pixel_mapper_config == 'U-mapper;Rotate:180'
|
||||
|
||||
|
||||
class TestDisplayManagerPi5Guard:
|
||||
"""On a Pi 5 the library returns no matrix for settings its RP1 path can't
|
||||
drive, and the binding doesn't check, so the process would crash on its next
|
||||
call. DisplayManager has to refuse before creating the matrix and fall back."""
|
||||
|
||||
def _config(self, **hardware_overrides):
|
||||
config = {
|
||||
'display': {
|
||||
'hardware': {
|
||||
'rows': 48, 'cols': 96, 'chain_length': 1, 'parallel': 1,
|
||||
'hardware_mapping': 'regular', 'brightness': 90,
|
||||
},
|
||||
'runtime': {'gpio_slowdown': 2},
|
||||
},
|
||||
'timezone': 'UTC',
|
||||
'plugin_system': {'plugins_directory': 'plugins'},
|
||||
}
|
||||
config['display']['hardware'].update(hardware_overrides)
|
||||
return config
|
||||
|
||||
@pytest.fixture
|
||||
def board(self, tmp_path, monkeypatch):
|
||||
from src import pi5_matrix_support
|
||||
|
||||
def set_model(model):
|
||||
path = tmp_path / 'model'
|
||||
path.write_bytes(model.encode() + b'\x00')
|
||||
monkeypatch.setattr(pi5_matrix_support, 'MODEL_PATH', str(path))
|
||||
return set_model
|
||||
|
||||
def test_unsupported_setting_on_pi5_never_creates_the_matrix(self, mock_rgb_matrix, board):
|
||||
board('Raspberry Pi 5 Model B Rev 1.0')
|
||||
DisplayManager._instance = None
|
||||
with patch.dict('os.environ', {'EMULATOR': 'false'}):
|
||||
dm = DisplayManager(self._config(row_address_type=5), suppress_test_pattern=True)
|
||||
mock_rgb_matrix['matrix_class'].assert_not_called()
|
||||
assert dm.matrix is None
|
||||
|
||||
def test_supported_setting_on_pi5_creates_the_matrix(self, mock_rgb_matrix, board):
|
||||
board('Raspberry Pi 5 Model B Rev 1.0')
|
||||
DisplayManager._instance = None
|
||||
with patch.dict('os.environ', {'EMULATOR': 'false'}):
|
||||
dm = DisplayManager(self._config(row_address_type=2), suppress_test_pattern=True)
|
||||
mock_rgb_matrix['matrix_class'].assert_called_once()
|
||||
assert dm.matrix is not None
|
||||
|
||||
def test_other_boards_are_left_to_the_library(self, mock_rgb_matrix, board):
|
||||
board('Raspberry Pi 4 Model B Rev 1.5')
|
||||
DisplayManager._instance = None
|
||||
with patch.dict('os.environ', {'EMULATOR': 'false'}):
|
||||
dm = DisplayManager(self._config(row_address_type=5), suppress_test_pattern=True)
|
||||
mock_rgb_matrix['matrix_class'].assert_called_once()
|
||||
assert dm.matrix is not None
|
||||
|
||||
Reference in New Issue
Block a user