Skip to content

HUB75 bugfixes for 4-scan and chained panels + new "Seengreat" pinout#5662

Draft
softhack007 wants to merge 8 commits into
mainfrom
hub75_4scan_bugfixes
Draft

HUB75 bugfixes for 4-scan and chained panels + new "Seengreat" pinout#5662
softhack007 wants to merge 8 commits into
mainfrom
hub75_4scan_bugfixes

Conversation

@softhack007
Copy link
Copy Markdown
Member

@softhack007 softhack007 commented Jun 3, 2026

Improvements:

  • new -S3 pin-out for Seengreat RGB Matrix Adapter Board (https://seengreat.com/wiki/186)
  • allow up to 128 pixels wide panels, prevent uint8 overflow for 128px panels

Bugfixes for 4-scan (aka QS) panels

  • prevent panels going flatter each time that cfg.json is saved
  • correct VirtualMatrixPanel setup: need to use real panel dimensions, not modified mxconfig dimensions
  • only set chainType when chain length > 1
  • use a chaintype that does not flip the display upside-down

Summary by CodeRabbit

  • New Features

    • Added support for an ESP32-S3 PSRAM pinout configuration and related debug output.
  • Bug Fixes

    • Enforced tighter matrix size limits across scan modes.
    • Improved virtual-matrix chaining and geometry so displays scale, orient and report dimensions correctly when virtual chaining is used.
    • Fixed pixel color reads to return RGB-only values so reported colors match the display.

align with settings page that also allows up to 64x128 per panel
* prevent panels going flatter each time that cfg.json is saved
* correct VirtualMatrixPanel setup: provide real panel dimensions
* only set chainType when chain length > 1
* use a chaintype that does not flip the display upside-down
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Important

Review skipped

Draft detected.

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

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5c6769b-fc20-429f-81c4-e48634699fc3

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:

  • ✅ Review completed - (🔄 Check again to review again)

Walkthrough

Constructor initializes a virtual-display flag and adjusts matrix sizing for half- and quarter-scan HUB75 types, adds an ESP32‑S3 PSRAM pinout variant, selects a chaining orientation for virtual displays, constructs the virtual panel with scaled dimensions, and updates getPins() to report scaled dimensions when virtual.

Changes

HUB75 Virtual Display Configuration and Chaining

Layer / File(s) Summary
Virtual display initialization and matrix type configuration
wled00/bus_manager.cpp
Constructor initializes _isVirtual = false, clamps mxconfig.chain_length, applies half-scan caps (mx_width ≤128, mx_height ≤64), and for quarter-scan sets _isVirtual and scales mx_width/mx_height.
Hardware pinout and virtual chaining setup
wled00/bus_manager.cpp
Adds ESP32‑S3 PSRAM pinout branch (SEENGREAT_V2_PINOUT), sets chainType = CHAIN_TOP_RIGHT_DOWN for virtual chaining, logs scaled dimensions, and constructs VirtualMatrixPanel with scaled width/height.
Virtual mode dimension reporting in getPins()
wled00/bus_manager.cpp
getPins() conditionally returns mxconfig.mx_width/2 and mxconfig.mx_height*2 when _isVirtual is true, otherwise returns unscaled mxconfig dimensions.

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • wled/WLED#5026: Modifies _isVirtual and virtual-display handling in HUB75 matrix initialization and getPins() dimension reporting for virtual vs. physical modes.
  • wled/WLED#4950: Updates virtual-display initialization and width/height logic in the HUB75 matrix constructor path.
  • wled/WLED#5647: Modifies BusHub75Matrix constructor logic for quarter-scan (TYPE_HUB75MATRIX_QS) panel type configuration.

Suggested labels

enhancement, hardware, Awaiting testing

Suggested reviewers

  • netmindz
  • DedeHai
  • willmmiles
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: HUB75 bugfixes for 4-scan and chained panels, plus a new Seengreat pinout, which directly aligns with the PR's stated objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added enhancement hardware (Likely) Setup-specific issue, e.g. flickering LEDs Awaiting testing labels Jun 3, 2026
change local variables to unsigned (=32bit)
coderabbitai[bot]

This comment was marked as low quality.

@softhack007 softhack007 marked this pull request as draft June 3, 2026 20:35
@coderabbitai coderabbitai Bot added the good job label Jun 3, 2026
Comment thread wled00/bus_manager.cpp
_isVirtual = true;
chainType = CHAIN_BOTTOM_LEFT_UP; // TODO: is there any need to support other chaining types?
DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, mxconfig.mx_width, mxconfig.mx_height);
if (chainLength > 1 && (_rows > 1 || _cols > 1)) chainType = CHAIN_TOP_RIGHT_DOWN; // we need to use a _DOWN chainType, otherwise the display is upside-down
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the condition is a duplicate, better to change the initial condition bc.type == TYPE_HUB75MATRIX_QS

also what effect does chainType have? CHAIN_BOTTOM_LEFT_UP works fine on my chained panel.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The chain type is typically used for multiple physical panels and is a common technique of physically instal panels upside down to keep cables shorter, see the driver docs for details.

I've yet to see a true 128 wide panel myself, the ones I've seen have been two 64x64 mounted side by side, so you need a chain length of 2 per panel, but that could get tricky once you then try and use multiple physical panels to get both halves of the panel the right way up

Copy link
Copy Markdown
Member Author

@softhack007 softhack007 Jun 4, 2026

Choose a reason for hiding this comment

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

also what effect does chainType have? CHAIN_BOTTOM_LEFT_UP works fine on my chained panel.

This should - normally - only be relevant when you have chained panels. Similar our 2D setup, chained panels can be connected from top down, left to right or even "sepentine" to save wire length.

For my 4-scan panel, CHAIN_BOTTOM_LEFT_UP always results in 180° rotated display where everything is upside-down, even without chaining panels. TOP_RIGHT_DOWN is the only one where I still had the "correct" orientation. I've compared to CHAIN_NONE and to 2-scan mode, just to be sure I'm not holding the panel wrongly.

Comment thread wled00/bus_manager.cpp
if (pinArray) {
pinArray[0] = mxconfig.mx_width;
pinArray[1] = mxconfig.mx_height;
pinArray[0] = _isVirtual ? mxconfig.mx_width /2 : mxconfig.mx_width;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Originally I was tracking QS virtual panels separately from regular use of virtual panels, we possibly need to use separate flag not the isVirtual

Comment thread wled00/bus_manager.cpp
// HUB75_I2S_CFG::i2s_pins _pins={R1_PIN, G1_PIN, B1_PIN, R2_PIN, G2_PIN, B2_PIN, A_PIN, B_PIN, C_PIN, D_PIN, E_PIN, LAT_PIN, OE_PIN, CLK_PIN};
mxconfig.gpio = {4, 5, 6, 7, 15, 16, 18, 8, 3, 42, 9, 40, 2, 41};

#elif defined(SEENGREAT_V2_PINOUT)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Probably worth adding the S3 check to that define

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

More to come - the board has a second pinout for esp32 classic, and it even comes in "V1" and "V2" pinout variants. 4 new printouts in total 😅

ensure that BusHub75Matrix::getPixelColor() returns RGBW format, not RGBA.

Currently busses.getPixelColor() is not used by the WLED core.
@coderabbitai coderabbitai Bot added the bug label Jun 4, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wled00/bus_manager.cpp (1)

828-845: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move the 64px chain-memory clamp below the real height assignment.

This guard now reads mxconfig.mx_height before the constructor copies panelHeight into it, so the single-panel fallback is based on stale/default state instead of the current bus config. That can let 64px chained panels bypass the safeguard, or clamp smaller panels unnecessarily depending on the library default.

Suggested fix
-  if (mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
-  `#if` defined(BOARD_HAS_PSRAM)                    // limitation to one panel only applies to boards without PSRAM
-    if (!psramFound() || ESP.getPsramSize() == 0) // PSRAM sanity check
-  `#endif`
-    {
-      DEBUGBUS_PRINTLN(F("WARNING, only single panel can be used of 64 pixel boards due to memory"));
-      mxconfig.chain_length = 1;
-    }
-  }
-
-  if (bc.type == TYPE_HUB75MATRIX_HS) {
-      mxconfig.mx_width = min(128U, panelWidth); // UI limit is 128
-      mxconfig.mx_height = min(64U, panelHeight);
+  const unsigned clampedPanelWidth  = min(128U, panelWidth);
+  const unsigned clampedPanelHeight = min(64U, panelHeight);
+
+  if (bc.type == TYPE_HUB75MATRIX_HS) {
+      mxconfig.mx_width = clampedPanelWidth; // UI limit is 128
+      mxconfig.mx_height = clampedPanelHeight;
   } else if (bc.type == TYPE_HUB75MATRIX_QS) {
       _isVirtual = true;
-      mxconfig.mx_width = min(128U, panelWidth) * 2;
-      mxconfig.mx_height = min(64U, panelHeight) / 2;
+      mxconfig.mx_width = clampedPanelWidth * 2;
+      mxconfig.mx_height = clampedPanelHeight / 2;
       mxconfig.driver = HUB75_I2S_CFG::FM6124;
   } else {
     DEBUGBUS_PRINTLN("Unknown type");
     return;
   }
+
+  if (clampedPanelHeight >= 64 && mxconfig.chain_length > 1) {
+  `#if` defined(BOARD_HAS_PSRAM)
+    if (!psramFound() || ESP.getPsramSize() == 0)
+  `#endif`
+    {
+      DEBUGBUS_PRINTLN(F("WARNING, only single panel can be used of 64 pixel boards due to memory"));
+      mxconfig.chain_length = 1;
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_manager.cpp` around lines 828 - 845, The 64-pixel chain-memory
clamp is checking mxconfig.mx_height before panelHeight is copied in, so move
the entire guard that inspects mxconfig.mx_height and possibly forces
mxconfig.chain_length = 1 to after the code that assigns mxconfig.mx_height from
panelHeight (the branches that handle bc.type == TYPE_HUB75MATRIX_HS and
TYPE_HUB75MATRIX_QS where mxconfig.mx_height is set); keep the PSRAM conditional
and its psramFound()/ESP.getPsramSize() check intact but run it after the
mx_height assignment so the clamp uses the real panel height.
♻️ Duplicate comments (1)
wled00/bus_manager.cpp (1)

1019-1029: ⚠️ Potential issue | 🟠 Major

Split “uses virtual panel” from “quarter-scan geometry was rescaled.”

This is the same _isVirtual conflation called out earlier: regular chained panels now enter the virtual path too, but VirtualMatrixPanel(...) and getPins() always undo QS scaling with /2 and *2. For non-QS chains that corrupts both runtime mapping and the dimensions written back to config.

Suggested direction
-  if (chainLength > 1 && (_rows > 1 || _cols > 1) || bc.type == TYPE_HUB75MATRIX_QS) {
-    _isVirtual = true;
+  const bool isQuarterScan = (bc.type == TYPE_HUB75MATRIX_QS);
+  const bool usesVirtualPanel = (chainLength > 1 && (_rows > 1 || _cols > 1)) || isQuarterScan;
+  if (usesVirtualPanel) {
+    _isVirtual = true;
     if (chainLength > 1 && (_rows > 1 || _cols > 1)) chainType = CHAIN_TOP_RIGHT_DOWN;
-    DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, mxconfig.mx_width/2, mxconfig.mx_height*2);
+    const unsigned physicalWidth  = isQuarterScan ? (mxconfig.mx_width / 2)  : mxconfig.mx_width;
+    const unsigned physicalHeight = isQuarterScan ? (mxconfig.mx_height * 2) : mxconfig.mx_height;
+    DEBUGBUS_PRINTF_P(PSTR("Using virtual matrix: %ux%u panels of %ux%u pixels\n"), _cols, _rows, physicalWidth, physicalHeight);
   }
...
-    virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width/2, mxconfig.mx_height*2, chainType);
+    virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, physicalWidth, physicalHeight, chainType);

That same “quarter-scan only” flag should also drive getPins().

Also applies to: 1165-1166

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/bus_manager.cpp` around lines 1019 - 1029, The code conflates "virtual
chain" with "quarter-scan geometry" causing non-QS chains to be rescaled;
introduce a separate boolean (e.g., _isQuarterScan or _isQS) set from (bc.type
== TYPE_HUB75MATRIX_QS) and keep _isVirtual for chained panels, then change the
VirtualMatrixPanel(...) call and any getPins() usage to use conditional scaling
only when _isQS (use mxconfig.mx_width / ( _isQS ? 2 : 1 ) and
mxconfig.mx_height * ( _isQS ? 2 : 1 )), and ensure the chainType assignment
(chainType = CHAIN_TOP_RIGHT_DOWN) remains based on chainLength and rows/cols
only; apply the same split/conditional scaling at the other
VirtualMatrixPanel/getPins call sites mentioned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@wled00/bus_manager.cpp`:
- Around line 828-845: The 64-pixel chain-memory clamp is checking
mxconfig.mx_height before panelHeight is copied in, so move the entire guard
that inspects mxconfig.mx_height and possibly forces mxconfig.chain_length = 1
to after the code that assigns mxconfig.mx_height from panelHeight (the branches
that handle bc.type == TYPE_HUB75MATRIX_HS and TYPE_HUB75MATRIX_QS where
mxconfig.mx_height is set); keep the PSRAM conditional and its
psramFound()/ESP.getPsramSize() check intact but run it after the mx_height
assignment so the clamp uses the real panel height.

---

Duplicate comments:
In `@wled00/bus_manager.cpp`:
- Around line 1019-1029: The code conflates "virtual chain" with "quarter-scan
geometry" causing non-QS chains to be rescaled; introduce a separate boolean
(e.g., _isQuarterScan or _isQS) set from (bc.type == TYPE_HUB75MATRIX_QS) and
keep _isVirtual for chained panels, then change the VirtualMatrixPanel(...) call
and any getPins() usage to use conditional scaling only when _isQS (use
mxconfig.mx_width / ( _isQS ? 2 : 1 ) and mxconfig.mx_height * ( _isQS ? 2 : 1
)), and ensure the chainType assignment (chainType = CHAIN_TOP_RIGHT_DOWN)
remains based on chainLength and rows/cols only; apply the same
split/conditional scaling at the other VirtualMatrixPanel/getPins call sites
mentioned.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e96884a1-51d6-4c4a-9caa-e51f368ebf4c

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee4c61 and 53cbb15.

📒 Files selected for processing (1)
  • wled00/bus_manager.cpp

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Jun 4, 2026

@softhack007 offtopic: what is up with the rabbit adding random labels?

@softhack007
Copy link
Copy Markdown
Member Author

@softhack007 offtopic: what is up with the rabbit adding random labels?

That was an experiment, I've allowed it to directly add the labels it suggests. Initially it looked meaningful, but it's true we see to many "random" labels. I'll switch that feature off.

@softhack007 softhack007 removed hardware (Likely) Setup-specific issue, e.g. flickering LEDs Awaiting testing labels Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants