Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HUB75 support #3777

Open
wants to merge 35 commits into
base: 0_15
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7ef84cf
Add HUB75 support
netmindz Feb 26, 2024
7603b5a
Remove getMaxPixels
netmindz Feb 26, 2024
755f91f
Remove referece to MM
netmindz Feb 26, 2024
2bd1e81
Default to mrfaptastic pinout
netmindz Feb 26, 2024
07a1588
Cleanup comments
netmindz Feb 26, 2024
74f77a7
Merge branch 'bus-config' into HUB75-AC
netmindz Sep 7, 2024
ecd46f2
Swap to new way to have dynamic LED types list
netmindz Sep 7, 2024
aae9446
Add "old-style" changes to settings_led for hub75
netmindz Sep 7, 2024
e94943d
Assign proper type ID for Hub75
netmindz Sep 8, 2024
e066b50
hub75 - remove hard coded panel sizes
netmindz Sep 8, 2024
78fb9dc
Cleanup mxconfig.chain_length
netmindz Sep 8, 2024
e185f2e
Hub75 compact pin defintion
netmindz Sep 8, 2024
f96acd6
Hub75 - Tweaks to webui
netmindz Sep 8, 2024
e0d78d5
Porting latest BusHub75Matrix from MoonModules - Mostly authored by F…
netmindz Sep 8, 2024
21c582e
Porting latest BusHub75Matrix from MoonModules - Mostly authored by F…
netmindz Sep 8, 2024
ad402ad
Hub75 - Misc fixes - WiP
netmindz Sep 8, 2024
23e578b
Swap BusHub75Matrix to use allocateMultiplePins
netmindz Sep 22, 2024
382d7e8
Remove stray whitespace from xml.cpp
netmindz Sep 22, 2024
fc07397
cleanup hub75 comments
netmindz Sep 22, 2024
713cbb8
Merge branch '0_15' into HUB75-AC
netmindz Sep 22, 2024
b7aba15
Always copy all the pin data
netmindz Sep 22, 2024
e111b6e
Hub75 - PIN_COUNT const
netmindz Sep 22, 2024
8632a0a
Hub75 - use actual panel config values
netmindz Sep 22, 2024
0a8d86c
Always copy all the pin data
netmindz Sep 22, 2024
9a9c65a
Whitespace
netmindz Sep 22, 2024
fbeead0
Exclude hub75 from pin validdation for xml.cpp
netmindz Sep 22, 2024
e74eb7d
Move examples envs for hub75
netmindz Sep 22, 2024
6ce6b95
Merge branch '0_15' into HUB75-AC
netmindz Oct 4, 2024
5b86c67
Error for ESP8266 and hub75
netmindz Oct 4, 2024
4276671
HUB75 - Remove hot from show
netmindz Oct 4, 2024
f7b8828
HUB75 - code formatting
netmindz Oct 4, 2024
c356846
HUB75 - fix hasRGB and missing override
netmindz Oct 4, 2024
6f03854
HUB75 - add comments to example env
netmindz Oct 4, 2024
f1b9952
HUB75 - Support BGR color order
netmindz Oct 4, 2024
de8a366
HUB75 - lower color depth for larger panels
netmindz Oct 10, 2024
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
17 changes: 17 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,20 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
${esp32.AR_build_flags}
lib_deps = ${esp32s2.lib_deps}
${esp32.AR_lib_deps}

;; TODO - move to sample ini
[env:esp32dev_hub75]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I will require this to be moved into sample file. I want platformio.ini clean and only used for default official builds.

board = esp32dev
upload_speed = 921600
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags}
-D WLED_RELEASE_NAME=ESP32_hub75
-D WLED_ENABLE_HUB75MATRIX -D NO_GFX
-D WLED_DEBUG
lib_deps = ${esp32_idf_V4.lib_deps}
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA.git#3.0.11

monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.default_partitions}
202 changes: 202 additions & 0 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,200 @@ void BusNetwork::cleanup(void) {
freeData();
}

// ***************************************************************************

#ifdef WLED_ENABLE_HUB75MATRIX
blazoncek marked this conversation as resolved.
Show resolved Hide resolved

BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {

mxconfig.double_buff = true; // <------------- Turn on double buffer

switch(bc.type) {
case TYPE_HUB75MATRIX_HS:
mxconfig.mx_width = bc.pins[0];
mxconfig.mx_height = bc.pins[1];
break;
}

mxconfig.chain_length = max((u_int8_t) 1, min(bc.pins[2], (u_int8_t) 4)); // prevent bad data preventing boot due to low memory

if(mxconfig.mx_height >= 64 && (mxconfig.chain_length > 1)) {
DEBUG_PRINTLN("WARNING, only single panel can be used of 64 pixel boards due to memory");
mxconfig.chain_length = 1;
}

// mxconfig.driver = HUB75_I2S_CFG::SHIFTREG;
mxconfig.clkphase = bc.reversed;

#if defined(ARDUINO_ADAFRUIT_MATRIXPORTAL_ESP32S3) // MatrixPortal ESP32-S3
blazoncek marked this conversation as resolved.
Show resolved Hide resolved

// https://www.adafruit.com/product/5778

DEBUG_PRINTLN("MatrixPanel_I2S_DMA - Matrix Portal S3 config");

mxconfig.gpio.r1 = 42;
mxconfig.gpio.g1 = 41;
mxconfig.gpio.b1 = 40;
mxconfig.gpio.r2 = 38;
mxconfig.gpio.g2 = 39;
mxconfig.gpio.b2 = 37;

mxconfig.gpio.lat = 47;
mxconfig.gpio.oe = 14;
mxconfig.gpio.clk = 2;

mxconfig.gpio.a = 45;
mxconfig.gpio.b = 36;
mxconfig.gpio.c = 48;
mxconfig.gpio.d = 35;
mxconfig.gpio.e = 21;

#elif defined(ESP32_FORUM_PINOUT) // Common format for boards designed for SmartMatrix

DEBUG_PRINTLN("MatrixPanel_I2S_DMA - ESP32_FORUM_PINOUT");

/*
ESP32 with SmartMatrix's default pinout - ESP32_FORUM_PINOUT

https://github.com/pixelmatix/SmartMatrix/blob/teensylc/src/MatrixHardware_ESP32_V0.h

Can use a board like https://github.com/rorosaurus/esp32-hub75-driver
*/

mxconfig.gpio.r1 = 2;
mxconfig.gpio.g1 = 15;
mxconfig.gpio.b1 = 4;
mxconfig.gpio.r2 = 16;
mxconfig.gpio.g2 = 27;
mxconfig.gpio.b2 = 17;

mxconfig.gpio.lat = 26;
mxconfig.gpio.oe = 25;
mxconfig.gpio.clk = 22;

mxconfig.gpio.a = 5;
mxconfig.gpio.b = 18;
mxconfig.gpio.c = 19;
mxconfig.gpio.d = 21;
mxconfig.gpio.e = 12;

#else
DEBUG_PRINTLN("MatrixPanel_I2S_DMA - Default pins");
/*
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-DMA?tab=readme-ov-file

Boards

https://esp32trinity.com/
https://www.electrodragon.com/product/rgb-matrix-panel-drive-interface-board-for-esp32-dma/

*/
mxconfig.gpio.r1 = 25;
mxconfig.gpio.g1 = 26;
mxconfig.gpio.b1 = 27;
mxconfig.gpio.r2 = 14;
mxconfig.gpio.g2 = 12;
mxconfig.gpio.b2 = 13;

mxconfig.gpio.lat = 4;
mxconfig.gpio.oe = 15;
mxconfig.gpio.clk = 16;

mxconfig.gpio.a = 23;
mxconfig.gpio.b = 19;
mxconfig.gpio.c = 5;
mxconfig.gpio.d = 17;
mxconfig.gpio.e = 18;

#endif


DEBUG_PRINTF("MatrixPanel_I2S_DMA config - %ux%u length: %u\n", mxconfig.mx_width, mxconfig.mx_height, mxconfig.chain_length);

// OK, now we can create our matrix object
display = new MatrixPanel_I2S_DMA(mxconfig);

this->_len = (display->width() * display->height());

pinManager.allocatePin(mxconfig.gpio.r1, true, PinOwner::HUB75);
blazoncek marked this conversation as resolved.
Show resolved Hide resolved
pinManager.allocatePin(mxconfig.gpio.g1, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.b1, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.r2, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.g2, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.b2, true, PinOwner::HUB75);

pinManager.allocatePin(mxconfig.gpio.lat, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.oe, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.clk, true, PinOwner::HUB75);

pinManager.allocatePin(mxconfig.gpio.a, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.b, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.c, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.d, true, PinOwner::HUB75);
pinManager.allocatePin(mxconfig.gpio.e, true, PinOwner::HUB75);

// display->setLatBlanking(4);

DEBUG_PRINTLN("MatrixPanel_I2S_DMA created");
// let's adjust default brightness
display->setBrightness8(25); // range is 0-255, 0 - 0%, 255 - 100%

// Allocate memory and start DMA display
if( not display->begin() ) {
DEBUG_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
return;
}
else {
_valid = true;
}

DEBUG_PRINTLN("MatrixPanel_I2S_DMA started");
}

void BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {
r = R(c);
g = G(c);
b = B(c);
x = pix % display->width();
y = floor(pix / display->width());
display->drawPixelRGB888(x, y, r, g, b);
}

void BusHub75Matrix::setBrightness(uint8_t b, bool immediate) {
this->display->setBrightness(b);
}

void BusHub75Matrix::deallocatePins() {

pinManager.deallocatePin(mxconfig.gpio.r1, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.g1, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.b1, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.r2, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.g2, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.b2, PinOwner::HUB75);

pinManager.deallocatePin(mxconfig.gpio.lat, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.oe, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.clk, PinOwner::HUB75);

pinManager.deallocatePin(mxconfig.gpio.a, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.b, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.c, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.d, PinOwner::HUB75);
pinManager.deallocatePin(mxconfig.gpio.e, PinOwner::HUB75);

}

std::vector<LEDType> BusHub75Matrix::getLEDTypes() {
return {
{TYPE_HUB75MATRIX_HS, "H", PSTR("HUB75 (Half Scan)")},
// {TYPE_HUB75MATRIX_QS, "H", PSTR("HUB75 (Quarter Scan)")},
};
}


#endif
// ***************************************************************************

//utility to get the approx. memory usage of a given BusConfig
uint32_t BusManager::memUsage(BusConfig &bc) {
Expand Down Expand Up @@ -815,6 +1009,10 @@ int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (Bus::isVirtual(bc.type)) {
busses[numBusses] = new BusNetwork(bc);
#ifdef WLED_ENABLE_HUB75MATRIX
} else if (Bus::isHub75(bc.type)) {
busses[numBusses] = new BusHub75Matrix(bc);
#endif
} else if (Bus::isDigital(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
} else if (Bus::isOnOff(bc.type)) {
Expand Down Expand Up @@ -846,6 +1044,10 @@ String BusManager::getLEDTypesJSONString(void) {
json += LEDTypesToJson(BusPwm::getLEDTypes());
json += LEDTypesToJson(BusNetwork::getLEDTypes());
//json += LEDTypesToJson(BusVirtual::getLEDTypes());
#ifdef WLED_ENABLE_HUB75MATRIX
json += LEDTypesToJson(BusHub75Matrix::getLEDTypes());
#endif

json.setCharAt(json.length()-1, ']'); // replace last comma with bracket
return json;
}
Expand Down
49 changes: 49 additions & 0 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#ifndef BusManager_h
#define BusManager_h

#ifdef WLED_ENABLE_HUB75MATRIX
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
#endif
/*
* Class for addressing various light types
*/
Expand Down Expand Up @@ -140,6 +143,7 @@ class Bus {
static constexpr bool isOnOff(uint8_t type) { return (type == TYPE_ONOFF); }
static constexpr bool isPWM(uint8_t type) { return (type >= TYPE_ANALOG_MIN && type <= TYPE_ANALOG_MAX); }
static constexpr bool isVirtual(uint8_t type) { return (type >= TYPE_VIRTUAL_MIN && type <= TYPE_VIRTUAL_MAX); }
static constexpr bool isHub75(uint8_t type) { return (type >= TYPE_HUB75MATRIX_MIN && type <= TYPE_HUB75MATRIX_MAX); }
static constexpr bool is16bit(uint8_t type) { return type == TYPE_UCS8903 || type == TYPE_UCS8904 || type == TYPE_SM16825; }
static constexpr int numPWMPins(uint8_t type) { return (type - 40); }

Expand Down Expand Up @@ -310,6 +314,51 @@ class BusNetwork : public Bus {
bool _broadcastLock;
};

#ifdef WLED_ENABLE_HUB75MATRIX
class BusHub75Matrix : public Bus {
public:
BusHub75Matrix(BusConfig &bc);

bool hasRGB() { return true; }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add override attribute.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You can't actually override as inline, bu that suggestion helped find and fix that bug

bool hasWhite() { return false; }

void setPixelColor(uint16_t pix, uint32_t c);

void show() {
if(mxconfig.double_buff) {
display->flipDMABuffer(); // Show the back buffer, set currently output buffer to the back (i.e. no longer being sent to LED panels)
display->clearScreen(); // Now clear the back-buffer
}
}

void setBrightness(uint8_t b, bool immediate);

uint8_t getPins(uint8_t* pinArray) {
pinArray[0] = mxconfig.chain_length;
return 1;
} // Fake value due to keep finaliseInit happy

void deallocatePins();

void cleanup() {
deallocatePins();
delete display;
_valid = false;
}

~BusHub75Matrix() {
cleanup();
}

static std::vector<LEDType> getLEDTypes(void);

private:
MatrixPanel_I2S_DMA *display = nullptr;
HUB75_I2S_CFG mxconfig;
uint8_t r, g, b, x, y;

};
#endif

//temporary struct for passing bus configuration to bus
struct BusConfig {
Expand Down
6 changes: 6 additions & 0 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@
#define TYPE_P9813 53
#define TYPE_LPD6803 54
#define TYPE_2PIN_MAX 63

#define TYPE_HUB75MATRIX_MIN 64
#define TYPE_HUB75MATRIX_HS 65
#define TYPE_HUB75MATRIX_QS 66
#define TYPE_HUB75MATRIX_MAX 71

//Network types (master broadcast) (80-95)
#define TYPE_VIRTUAL_MIN 80
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
Expand Down
18 changes: 14 additions & 4 deletions wled00/data/settings_leds.htm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
function isD2P(t) { return gT(t).t === "2P"; } // is digital 2 pin type
function isNet(t) { return gT(t).t === "N"; } // is network type
function isVir(t) { return gT(t).t === "V" || isNet(t); } // is virtual type
function isHub75(t){ return gT(t).t === "H"; } // is HUB75 type
function hasRGB(t) { return !!(gT(t).c & 0x01); } // has RGB
function hasW(t) { return !!(gT(t).c & 0x02); } // has white channel
function hasCCT(t) { return !!(gT(t).c & 0x04); } // is white CCT enabled
Expand Down Expand Up @@ -246,12 +247,15 @@
case 'V': // virtual/non-GPIO based
p0d = "Config:"
break;
case 'H': // HUB75
p0d = "Panel size (width x height):"
break;
}
gId("p0d"+n).innerText = p0d;
gId("p1d"+n).innerText = p1d;
gId("off"+n).innerText = off;
// secondary pins show/hide (type string length is equivalent to number of pins used; except for network and on/off)
let pins = Math.max(gT(t).t.length,1) + 3*isNet(t); // fixes network pins to 4
let pins = Math.max(gT(t).t.length,1) + 3*isNet(t) + 1*isHub75(t); // fixes network pins to 4
for (let p=1; p<5; p++) {
var LK = d.Sf["L"+p+n];
if (!LK) continue;
Expand Down Expand Up @@ -279,13 +283,13 @@
}
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
gRGBW |= hasW(t); // RGBW checkbox
gId("co"+n).style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide color order for PWM
gId("co"+n).style.display = (isVir(t) || isAna(t) || isHub75(t)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (isDig(t) && hasW(t)) ? "inline":"none"; // show swap channels dropdown
gId("dig"+n+"w").querySelector("[data-opt=CCT]").disabled = !hasCCT(t); // disable WW/CW swapping
if (!(isDig(t) && hasW(t))) d.Sf["WO"+n].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (isAna(t)) ? "none":"inline"; // hide count for analog
gId("dig"+n+"c").style.display = (isAna(t) || isHub75(t)) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (isVir(t)) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = (isVir(t) || isAna(t)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"s").style.display = (isVir(t) || isAna(t) || isHub75(t)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = (isDig(t) || (isPWM(t) && maxL>2048)) ? "inline":"none"; // hide refresh (PWM hijacks reffresh for dithering on ESP32)
gId("dig"+n+"a").style.display = (hasW(t)) ? "inline":"none"; // auto calculate white
gId("dig"+n+"l").style.display = (isD2P(t) || isPWM(t)) ? "inline":"none"; // bus clock speed / PWM speed (relative) (not On/Off)
Expand Down Expand Up @@ -335,6 +339,12 @@
LC.min = 0;
LC.style.color="#fff";
return; // do not check conflicts
}
else if (isHub75(t)) {
LC.max = 128;
LC.min = 16;
LC.style.color="#fff";
return; // do not check conflicts
} else {
LC.max = d.max_gpio;
LC.min = -1;
Expand Down
1 change: 1 addition & 0 deletions wled00/pin_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum struct PinOwner : uint8_t {
DMX = 0x8A, // 'DMX' == hard-coded to IO2
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32)
HUB75 = 0x8E, // 'Hub75' == Hub75 driver
// Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
Expand Down
1 change: 1 addition & 0 deletions wled00/xml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -784,5 +784,6 @@ void getSettingsJS(byte subPage, char* dest)
#else
oappend(SET_F("gId(\"somp\").remove(1);")); // remove 2D option from dropdown
#endif

}
}