Skip to content
Draft
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,10 +802,11 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
_hasRgb = true;
_hasWhite = false;
virtualDisp = nullptr; // todo: this should be solved properly, can cause memory leak (if omitted here, nothing seems to work)
_isVirtual = false;
// aliases for easier reading
uint8_t panelWidth = bc.pins[0];
uint8_t panelHeight = bc.pins[1];
uint8_t chainLength = bc.pins[2];
unsigned panelWidth = bc.pins[0];
unsigned panelHeight = bc.pins[1];
unsigned chainLength = bc.pins[2];
_rows = bc.pins[3];
_cols = bc.pins[4];

Expand All @@ -822,7 +823,7 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.

mxconfig.clkphase = bc.reversed;
// allow chain length up to 4, limit to prevent bad data from preventing boot due to low memory
mxconfig.chain_length = max((uint8_t) 1, min(chainLength, (uint8_t) 4));
mxconfig.chain_length = max(1U, min(chainLength, 4U));

if (mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
#if defined(BOARD_HAS_PSRAM) // limitation to one panel only applies to boards without PSRAM
Expand All @@ -835,12 +836,12 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
}

if (bc.type == TYPE_HUB75MATRIX_HS) {
mxconfig.mx_width = min((uint8_t) 64, panelWidth); // TODO: UI limit is 128, this limits to 64
mxconfig.mx_height = min((uint8_t) 64, panelHeight);
mxconfig.mx_width = min(128U, panelWidth); // UI limit is 128
mxconfig.mx_height = min(64U, panelHeight);
} else if (bc.type == TYPE_HUB75MATRIX_QS) {
_isVirtual = true;
mxconfig.mx_width = min((uint8_t) 64, panelWidth) * 2;
mxconfig.mx_height = min((uint8_t) 64, panelHeight) / 2;
mxconfig.mx_width = min(128U, panelWidth) * 2;
mxconfig.mx_height = min(64U, panelHeight) / 2;
mxconfig.driver = HUB75_I2S_CFG::FM6124; // use FM6124 for "outdoor" 4-scan panels - workaround until we can make the driver user-configurable
} else {
DEBUGBUS_PRINTLN("Unknown type");
Expand Down Expand Up @@ -887,6 +888,12 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
// 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 😅

DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA - S3 devKit-C with PSRAM, SEENGREAT_V2 pinout");
mxconfig.gpio = { 18, 8, 17, // R1_PIN, G1_PIN, B1_PIN,
16, 1, 15, // R2_PIN, G2_PIN, B2_PIN,
7, 48, 6, 47, 2, // A_PIN, B_PIN, C_PIN, D_PIN, E_PIN,
21, 4, 5 }; //LAT_PIN, OE_PIN,CLK_PIN
#else
DEBUGBUS_PRINTLN("MatrixPanel_I2S_DMA - S3 with PSRAM");
// 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};
Expand Down Expand Up @@ -1011,15 +1018,15 @@ BusHub75Matrix::BusHub75Matrix(const BusConfig &bc) : Bus(bc.type, bc.start, bc.
// chained panels with cols and rows define need the virtual display driver, so do quarter-scan panels
if (chainLength > 1 && (_rows > 1 || _cols > 1) || bc.type == TYPE_HUB75MATRIX_QS) {
_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

@softhack007 softhack007 Jun 4, 2026

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.

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.

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);
}
else {
_isVirtual = false;
}

if (_isVirtual) {
virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width, mxconfig.mx_height, chainType);
virtualDisp = new VirtualMatrixPanel((*display), _rows, _cols, mxconfig.mx_width/2, mxconfig.mx_height*2, chainType);
virtualDisp->setRotation(0);
if (bc.type == TYPE_HUB75MATRIX_QS) {
switch(panelHeight) {
Expand Down Expand Up @@ -1090,7 +1097,7 @@ void IRAM_ATTR BusHub75Matrix::setPixelColor(unsigned pix, uint32_t c) {
uint32_t BusHub75Matrix::getPixelColor(unsigned pix) const {
if (!_valid) return IS_BLACK; // note: no need to check pix >= _len as that is checked in containsPixel()
if (_ledBuffer)
return uint32_t(_ledBuffer[pix]);
return uint32_t(_ledBuffer[pix]) & 0x00FFFFFF; // FastLED 32bit is RGBA, we need RGBW
else
return getBitFromArray(_ledsDirty, pix) ? IS_DARKGREY: IS_BLACK; // just a hack - we only know if the pixel is black or not
}
Expand Down Expand Up @@ -1155,8 +1162,8 @@ std::vector<LEDType> BusHub75Matrix::getLEDTypes() {

size_t BusHub75Matrix::getPins(uint8_t* pinArray) const {
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

pinArray[1] = _isVirtual ? mxconfig.mx_height *2 : mxconfig.mx_height;
pinArray[2] = mxconfig.chain_length;
pinArray[3] = _rows;
pinArray[4] = _cols;
Expand Down
Loading