Skip to content

Add HUB75 refresh rates by chain length and color depth - #230

Merged
MoonModules merged 1 commit into
MoonModules:mainfrom
AnjinMeili:hub75-refresh-rates
Nov 2, 2026
Merged

MoonModules merged 1 commit into
MoonModules:mainfrom
AnjinMeili:hub75-refresh-rates

Conversation

@AnjinMeili

Copy link
Copy Markdown
Contributor

A reference page for sizing a HUB75 chain before wiring it: the refresh rate a chain reaches at 8, 11 and 16 bits per color, worked out from the shift clock, the bit planes and each plane's on-time, for panels driven from GPIO and for ColorLight 5A-75 receiving cards.

Docs

  • hub75-refresh.md: the formula with one worked example, how rpi-rgb-led-matrix, the ESP32 HUB75 driver and SmartMatrix each set the LSB, tables for 128x64 panels at 1/32 scan, and the ColorLight cards' specs and refresh ceilings, with sources. The ESP32 driver sits at the formula's ceiling: one shift per plane at its 20 MHz pixel clock, the latch riding the last pixel, which gives the 2,441 Hz its bench figure is measured against. The tables are recomputed from the formula and match.
  • mkdocs.yml: the page under Reference, Hardware, after HUB75 panels.
  • hub75-panels.md: the ESP32 bench figure links the page's ceiling in place of its own line-time formula, and points to the page for longer chains and deeper color.
  • panel-cards.md: links the card ceilings; "specialised" to "specialized"; the parts table names the card models in place of referring to the page itself, and section 8 is titled "When the wall stays dark", in positive form.
  • 06-advanced.yaml: the panel-card step links section 8 by its new heading.
  • check_prose: NegatedHeading 6 to 7 and project_mirror's 7 findings are main's baseline, outside this diff.

The Raspberry Pi rows name the library's own flags. Once the HUB75 Pi driver lands, a follow-up names its controls instead.

What an agent wrote: Claude Code (Opus) wrote this page and this text from my direction; I reviewed every line and vouch for it.

Tested: On this branch on macOS, build_docs --strict builds the site with the page in the nav, check_specs and check_docgen pass, and check_prose finds the added lines clean. I recomputed the GPIO tables from the formula and they match.

Skipped checks:

  • Builds, tests, scenarios and collect_kpi: docs only, no code changed.

🤖 Generated with Claude Code

A reference page for sizing a HUB75 chain before wiring it: the refresh rate a chain reaches at 8, 11 and 16 bits per color, worked out from the shift clock, the bit planes and each plane's on-time, for panels driven from GPIO and for ColorLight 5A-75 receiving cards.

**Docs/CI**
- hub75-refresh.md: the formula with one worked example, how `rpi-rgb-led-matrix`, the ESP32 HUB75 driver and SmartMatrix each set the LSB, tables for 128x64 panels at 1/32 scan, and the ColorLight cards' specs and refresh ceilings, with sources. The ESP32 driver sits at the formula's ceiling: one shift per plane at its 20 MHz pixel clock, the latch riding the last pixel, which gives the 2,441 Hz its bench figure is measured against. The tables are recomputed from the formula and match.
- mkdocs.yml: the page under Reference, Hardware, after HUB75 panels.
- hub75-panels.md: the ESP32 bench figure links the page's ceiling in place of its own line-time formula, and points to the page for longer chains and deeper color.
- panel-cards.md: links the card ceilings; "specialised" to "specialized"; the parts table names the card models in place of referring to the page itself, and section 8 is titled "When the wall stays dark", in positive form.
- 06-advanced.yaml: the panel-card step links section 8 by its new heading.
- check_prose: NegatedHeading 6 to 7 and project_mirror's 7 findings are main's baseline, outside this diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 2, 2026 11:18
@coderabbitai

coderabbitai Bot commented Nov 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The guide contains unresolved inaccuracies in Pi PWM timing conversion, refresh-rate ceilings, and SmartMatrix’s achievable refresh rate and effective color depth.

4 open issues
What changed in this PR

Adds a HUB75 refresh-rate reference to help size panel chains by color depth, covering GPIO drivers and ColorLight receiving cards.

Changes:

  • Documents timing formulas, a worked example, and refresh-rate and brightness tables.
  • Adds the reference to site navigation and links it from existing hardware guides.
  • Renames the receiving-card troubleshooting heading and updates its process link.
File Description
moonwalk/​06-advanced.yaml Updates the troubleshooting link to the renamed heading.
mkdocs.yml Adds the refresh-rate page under Reference → Hardware.
docs/​reference/​hardware/​panel-cards.md Links card ceilings and refines wording and the troubleshooting heading.
docs/​reference/​hardware/​hub75-refresh.md Adds timing models, driver comparisons, sizing tables, and sources.
docs/​reference/​hardware/​hub75-panels.md Links the bench comparison and chain-sizing guidance to the new reference.

Review effort: Balanced

| t_latch | Latch and set-up time per shifted row | 0.5 µs |
| L | LSB time: how long the least significant plane stays lit | 130 ns, the `rpi-rgb-led-matrix` default, or 50 ns |

A real chain sits between the two bounds: its low planes are shift-bound and its high planes are on-time-bound. The ceiling falls as the chain grows. The on-time bound halves with each extra bit and stays the same at any chain length.

| Driver | What it holds fixed | What follows | Its highest rate |
|---|---|---|---|
| `rpi-rgb-led-matrix` | The LSB time, `--led-pwm-lsb-nanoseconds` | The refresh rate, from the formula | The formula's f, at up to 11 bit planes, `--led-pwm-bits` |
| 6 | 768 | 49,152 | 83 Hz | 366.4 ns | 25% | 83 Hz | 9% | 83 Hz | 3% |
| 8 | 1024 | 65,536 | 63 Hz | 487.2 ns | 25% | 63 Hz | 7% | 63 Hz | 3% |

At 8 bits the shift clock sets the rate, and a shorter LSB only dims the panel.
| 6 | 768 | 49,152 | 42 Hz | 1.4 ns | 12% | 3.5 Hz | 96% | 8.5 Hz | 89% |
| 8 | 1024 | 65,536 | 31 Hz | 1.9 ns | 12% | 3.5 Hz | 95% | 8.1 Hz | 85% |

A fixed LSB at 16 planes and 1/32 scan flickers at single-digit rates. SmartMatrix's 48-bit mode reaches 240 Hz on one panel with an LSB of 0.3 ns, well inside one timer tick, so its lowest planes share one on-time.
@MoonModules
MoonModules merged commit 6aa1db5 into MoonModules:main Nov 2, 2026
10 checks passed
@ewowi

ewowi commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Merged, thanks James! A careful page: the formula, a worked example and sourced tables, and the ESP32 bench figure now has a ceiling to be measured against.

ewowi added a commit that referenced this pull request Nov 2, 2026
NdiDriver.h takes #227's per-light read by role. panel-cards.md and 06-advanced.yaml keep this branch's numbering, where "8. Set the card up in LEDVision" puts "When the wall stays dark" at 9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ewowi added a commit that referenced this pull request Nov 2, 2026
…th, a sanitizer-clean seqlock

The ESP32-P4's HDMI output no longer allocates on the render tick and frees its frame only once no write is in flight, as the desktop does; a refused output is opened again by itself. CI's thread-sanitizer job builds again, and the branch's merge losses and doc gaps are restored.

KPI: 256lights | Desktop:2282KB | tick:7/10/8/2/8/7/270/7/7/11us(FPS:142857/100000/125000/500000/125000/142857/3703/142857/142857/90909) | src:353(91049) | test:266(62360) | functions-over:345
Commit: src +46/-19 lines | test +3/-11 lines | duplicated lines 3217 -> 3217 (+0)

**Core**
- ESP32 HDMI: the output task grows the frame (growFrame), the tick only asks; hdmiClose claims the frame slot before freeing it.
- hdmi_handover.h: the seqlock reader closes with a read-don't-modify-write (Boehm's reader) instead of atomic_thread_fence, which GCC's -fsanitize=thread rejects; reproduced and verified with GCC 14 on the Pi.
- platform.h forward-declares edid::Monitor instead of including the parser into every unit.

**Light domain**
- HdmiDriver: a refused output asks the scheduler for a prepare every 5 s; the scaling Select's values are named (kScalingFit, kScalingStretch).
- NdiDriver: release() chains to DriverBase::release(), so a disabled NDI driver frees its staging.
- EffectBase.h carries <algorithm>, AudioSpectrumEffect drops its second include.

**Tests**
- unit_ModuleContract: NdiDriver's two exemption rows removed, the lists only shrink.
- unit_stop_signal gets its @module tag; test_check_docgen loses the test of the removed appendix filter.
- The HDMI live scenario passes on the Olimex P4-PC (board B) with the ESP32 fixes flashed.

**Scripts/MoonDeck**
- check_docgen: the appendix-total filter goes; the committed baseline no longer carries that row.

**Docs/CI**
- panel-cards.md: three of #230's lines the merge of main had dropped are back (specialized, the 5A-75B or 5A-75E row, the HUB75 refresh rates link).
- The platform page names RTSP and HDMI; the links to #218 say #218.
- The ArtPoll discovery card states the send retries (Project card, mirror regenerated).
- .coderabbit.yaml filters generated files, plans and images, so a PR's review fits CodeRabbit's 100-file limit.
- help.json regenerated; the prose count falls to 2011.
- Performance: Layers_stack_and_blend_live reads 270 us against 163 in main's last KPI (9302c9c); today's main measures the same as this branch (295-297 us on both, three runs each), so main moved in merges that recorded no KPI.

**Reviews**
- Reviewer: the ESP32 hdmiWrite allocates on the tick: done, the task grows it.
- Reviewer: the ESP32 hdmiClose frees the frame without the slot: done, claimUnlessWriting.
- Reviewer: the merge of main lost #230's link on panel-cards.md: done, with two more lost lines found and restored.
- Reviewer: the LEDVision how-to sits on a reference page, not in MoonWalk: accepted, the product owner's decision that MoonWalk describes MoonLight's own processes.
- Reviewer: NdiDriver::release skips the base: done.
- Reviewer: bare 0 and 1 for the scaling Select: done.
- Reviewer: "specialised": done.
- Reviewer: the platform page lacks HDMI: done.
- Reviewer: a refused ESP32 open leaves a sticky wrong error: done, retried every 5 s; untested, the unit bench has no scheduler.
- Reviewer: unit_stop_signal has no @module: done.
- Reviewer: <algorithm> as a second include: done.
- Reviewer: the default mode is positional and kMaxModes equals the desktop's count: skipped, the unit test pins index 2 on both tables and a ninth mode is when kMaxModes grows.
- Reviewer: the bridge control exists where no bridge reads back: skipped, the desktop stand-in tests the readout the ESP32 shows.
- Reviewer: platform.h includes the EDID parser: done; moving mm::edid under mm::platform skipped, taste.
- Reviewer: links to #218 named after items: done.
- Reviewer: a dead appendix filter in check_docgen: done.
- Reviewer: eight topics on one branch: skipped, breadth without coupling on the product owner's integration branch.
- CodeRabbit (#222): the ArtPoll card overstated what a failed send costs: done, card text approved by the product owner.
- CodeRabbit (#222): manual MoonWalk steps described as recorded: skipped, the page already narrows it to script steps.
- CodeRabbit (#222): the Discord reader skips archived threads: skipped, it already pages the archived public threads.
- CI: the thread-sanitizer job fails on the seqlock's fence: done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AnjinMeili
AnjinMeili deleted the hub75-refresh-rates branch November 3, 2026 04:17
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.

4 participants