Skip to content

feat: add MAX7219 LED matrix display support (4-panel and 8-panel)#240

Merged
PierreGode merged 11 commits intoPierreGode:mainfrom
dbzx6r:pr/max7219-upstream
Mar 18, 2026
Merged

feat: add MAX7219 LED matrix display support (4-panel and 8-panel)#240
PierreGode merged 11 commits intoPierreGode:mainfrom
dbzx6r:pr/max7219-upstream

Conversation

@dbzx6r
Copy link
Contributor

@dbzx6r dbzx6r commented Mar 14, 2026

Summary

Adds support for cascaded MAX7219 8×8 LED matrix panel arrays as a new display option for Ragnar on Raspberry Pi.

Hardware Support

  • 4-panel (32×8 pixels) — max7219_4panel
  • 8-panel (64×8 pixels) — max7219_8panel
  • SPI connection: DIN→GPIO10 (Pin 19), CS→GPIO8 (Pin 24), CLK→GPIO11 (Pin 23)

Features

  • Scrolling display showing SSID, IP address, target count, and Ragnar status
  • Configurable display brightness (0–15)
  • Configurable SPI port/device and block orientation
  • Startup animation clears all pixels to prevent stuck LEDs
  • Automatic luma.core installation via install script

Install Script

Options added to \install_ragnar.sh:

  • Option 11: MAX7219 8-panel (64×8) LED matrix
  • Option 12: MAX7219 4-panel (32×8) LED matrix

Web UI Config

New settings exposed in the Display section of the web config page when a MAX7219 type is selected:

  • SPI port / SPI device
  • Block orientation
  • Display brightness

Files Changed

  • \display.py\ — new _run_max7219()\ display loop
  • \shared.py\ — MAX7219 display profiles, config defaults, EPD init skip, wipe_epd skip

  • esources/waveshare_epd/max7219.py\ — MAX7219 SPI driver using luma.core
  • \install_ragnar.sh\ — install options 11 and 12
  • \web/scripts/ragnar_modern.js\ — display config UI additions
  • \README.md\ — feature entry added

Compatibility Notes

This PR and the open LCD1602 PR (#239) both touch shared infrastructure:

  • \shared.py\ \initialize_epd_display(): early-return for non-EPD displays
  • \shared.py\ \get_default_config(): \display_brightness\ key
  • \display.py\ \Display.init: \�pd_helper\ None guard

If both PRs are merged, apply LCD1602 (#239) first, then this PR — or merge them together to avoid conflicts.

dbzx6r and others added 10 commits March 13, 2026 20:50
Adds SPI-driven MAX7219 cascaded LED matrix support for Ragnar:
- resources/waveshare_epd/max7219.py: luma.led_matrix-based driver
- display.py: _run_max7219() scrolling status renderer
- shared.py: max7219_spi_port, max7219_spi_device config keys + display profiles
- epd_helper.py: registers max7219_4panel and max7219_8panel types
- install_ragnar.sh: MAX7219 added as options 11/12 in setup wizard
- ragnar_modern.js: configMetadata entries for MAX7219 settings
- requirements.txt: luma.led_matrix + luma.core dependencies
- README.md: MAX7219 added to fork features table

Wiring: VCC→5V, GND→GND, DIN→GPIO10(MOSI), CS→GPIO8(CE0), CLK→GPIO11(SCLK)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MAX7219 is SPI-driven and managed entirely by display.py.
Previously, SharedData.initialize_epd_display() would try
EPDHelper('max7219_8panel'), fail, trigger auto-detect, find
the physical e-paper, and overwrite epd_type in the config.

Add early-return before the EPDHelper block for max7219_4panel
and max7219_8panel, mirroring the existing pager-mode skip.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When epd_type is max7219_4panel/max7219_8panel, shared_data.epd_helper
is None (by design — MAX7219 is managed entirely by _run_max7219).
The unconditional init_partial_update() call in __init__ raised
AttributeError on None, crashing the display thread before run().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- wipe_epd.py: skip MAX7219/SSD1306/GC9A01 — not e-paper, no wipe needed
- install_ragnar.sh: use sudo pip3 so luma.led_matrix is available system-wide
  (Ragnar service runs as root, user-local installs are not visible to it)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- block_orientation default changed from -90 to 0 (correct for
  horizontal panel arrays; -90 is for portrait-mounted modules)
- Add explicit device.clear() after init — MAX7219 powers on with
  all LEDs lit, this forces a blank canvas immediately
- Add max7219_block_orientation config key (default 0) so users
  can tune rotation from the Settings UI without code changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…strips

Standard cascaded horizontal LED matrix modules require block_orientation=-90
with luma.led_matrix. The previous default of 0 caused text to appear rotated
90 degrees and only ~2 characters to be visible (pixel mapping collapsed the
effective width to 8px instead of 64px).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix block_orientation default to -90 for horizontal LED matrix strips
- Fix .get() fallback in _run_max7219 to use -90 instead of 0
- Replace generic T:/C: counter with detailed labelled messages:
  TARGETS FOUND, CREDENTIALS STOLEN, VULNERABILITIES, WIFI, IP, STATUS
- All messages are uppercase for better LED matrix readability

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch Arial.ttf → DejaVuSansMono.ttf for even monospace character spacing
  and proper word gaps (Arial proportional spacing caused uneven kerning and
  words running together at 8px)
- Bump font size 8 → 9 to use 7 of 8 pixel rows
- Draw at y=-bbox_top instead of y=0 to shift glyphs up and use all 8 LED rows

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add MAX7219 options to the web UI config page:
- Display type dropdown: MAX7219 8-panel (64x8) and 4-panel (32x8)
- Display config section: max7219_spi_port, max7219_spi_device,
  max7219_block_orientation, display_brightness
- Custom input rows with IDs for show/hide (same pattern as ssd1306)
- syncDisplayRows() shows MAX7219 settings only when a MAX7219
  type is selected; brightness row visible for MAX7219/SSD1306/GC9A01

Install script already had MAX7219 options (options 11/12).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dbzx6r
Copy link
Contributor Author

dbzx6r commented Mar 15, 2026

image

@PierreGode PierreGode merged commit f382809 into PierreGode:main Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants