Allow "unlimited" virtual buses

- added config upload options
- number of buses it limited to 36 (0-9+A-Z identifiers)
- WRNING web server may not support that many variables
This commit is contained in:
Blaž Kristan 2025-01-22 20:33:56 +01:00
parent bf69d37cbe
commit 70042db2de
5 changed files with 75 additions and 53 deletions

View File

@ -192,7 +192,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#endif #endif
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; if (s >= WLED_MAX_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"]; JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue; if (pinArr.size() == 0) continue;
@ -220,21 +220,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
maMax = 0; maMax = 0;
} }
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); //busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax)));
if (useParallel && s < 8) { busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
// if for some unexplained reason the above pre-calculation was wrong, update doInitBusses = true; // finalization done in beginStrip()
unsigned memT = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
} else
mem += BusManager::memUsage(bc); // includes global buffer
if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else {
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
doInitBusses = true; // finalization done in beginStrip()
}
s++;
} }
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem); DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem);
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());

View File

@ -49,31 +49,31 @@
#define WLED_MAX_DIGITAL_CHANNELS 3 #define WLED_MAX_DIGITAL_CHANNELS 3
#define WLED_MAX_ANALOG_CHANNELS 5 #define WLED_MAX_ANALOG_CHANNELS 5
#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB #define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB
#define WLED_MIN_VIRTUAL_BUSSES 2 #define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI
#else #else
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
#define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white #define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white
#define WLED_MAX_DIGITAL_CHANNELS 2 #define WLED_MAX_DIGITAL_CHANNELS 2
//#define WLED_MAX_ANALOG_CHANNELS 6 //#define WLED_MAX_ANALOG_CHANNELS 6
#define WLED_MIN_VIRTUAL_BUSSES 3 #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 5 #define WLED_MAX_DIGITAL_CHANNELS 5
//#define WLED_MAX_ANALOG_CHANNELS 8 //#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 3 #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 4 #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
//#define WLED_MAX_ANALOG_CHANNELS 8 //#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
#else #else
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
#define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB #define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 17 #define WLED_MAX_DIGITAL_CHANNELS 17
//#define WLED_MAX_ANALOG_CHANNELS 16 //#define WLED_MAX_ANALOG_CHANNELS 16
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
#endif #endif
#endif #endif
#else #else
@ -87,7 +87,7 @@
#ifndef WLED_MAX_DIGITAL_CHANNELS #ifndef WLED_MAX_DIGITAL_CHANNELS
#error You must also define WLED_MAX_DIGITAL_CHANNELS. #error You must also define WLED_MAX_DIGITAL_CHANNELS.
#endif #endif
#define WLED_MIN_VIRTUAL_BUSSES (5-WLED_MAX_BUSSES) #define WLED_MIN_VIRTUAL_BUSSES 3
#else #else
#if WLED_MAX_BUSSES > 20 #if WLED_MAX_BUSSES > 20
#error Maximum number of buses is 20. #error Maximum number of buses is 20.
@ -98,7 +98,11 @@
#ifndef WLED_MAX_DIGITAL_CHANNELS #ifndef WLED_MAX_DIGITAL_CHANNELS
#error You must also define WLED_MAX_DIGITAL_CHANNELS. #error You must also define WLED_MAX_DIGITAL_CHANNELS.
#endif #endif
#define WLED_MIN_VIRTUAL_BUSSES (20-WLED_MAX_BUSSES) #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
#define WLED_MIN_VIRTUAL_BUSSES 4
#else
#define WLED_MIN_VIRTUAL_BUSSES 6
#endif
#endif #endif
#endif #endif

View File

@ -42,10 +42,10 @@
if (loc) d.Sf.action = getURL('/settings/leds'); if (loc) d.Sf.action = getURL('/settings/leds');
} }
function bLimits(b,v,p,m,l,o=5,d=2,a=6) { function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S) oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S): 20 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S) maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 17 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
maxA = a; // maxA - max analog channels maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
maxV = v; // maxV - min virtual buses maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3)
maxPB = p; // maxPB - max LEDs per bus maxPB = p; // maxPB - max LEDs per bus
maxM = m; // maxM - max LED memory maxM = m; // maxM - max LED memory
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32) maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
@ -350,6 +350,18 @@
else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff"; else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff";
} }
}); });
const S2 = (oMaxB == 14) && (maxV == 4);
const S3 = (oMaxB == 14) && (maxV == 6);
if (oMaxB == 19 || S2 || S3) { // TODO: crude ESP32 & S2/S3 detection
if (maxLC > 300 || dC <= 2) {
d.Sf["PR"].checked = false;
gId("prl").classList.add("hide");
} else
gId("prl").classList.remove("hide");
// S2 supports mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
maxD = (S2 || S3 ? 4 : 8) + (d.Sf["PR"].checked ? 8 : S2); // TODO: use bLimits() : 4/8RMT + (x1/x8 parallel) I2S1
maxB = oMaxB - (d.Sf["PR"].checked ? 0 : 7 + S3); // S2 (maxV==4) does support mono I2S
}
// distribute ABL current if not using PPL // distribute ABL current if not using PPL
enPPL(sDI); enPPL(sDI);
@ -409,8 +421,8 @@
if (isVir(t)) virtB++; if (isVir(t)) virtB++;
}); });
if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return; if ((n==1 && i>=36) || (n==-1 && i==0)) return; // used to be i>=maxB+maxV when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
var s = String.fromCharCode((i<10?48:55)+i); var s = chrID(i);
if (n==1) { if (n==1) {
// npm run build has trouble minimizing spaces inside string // npm run build has trouble minimizing spaces inside string
@ -484,7 +496,7 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
o[i].querySelector("[name^=LT]").disabled = false; o[i].querySelector("[name^=LT]").disabled = false;
} }
gId("+").style.display = (i<maxB+maxV-1) ? "inline":"none"; gId("+").style.display = (i<35) ? "inline":"none"; // was maxB+maxV-1 when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
gId("-").style.display = (i>0) ? "inline":"none"; gId("-").style.display = (i>0) ? "inline":"none";
if (!init) { if (!init) {
@ -603,22 +615,32 @@ Swap: <select id="xw${s}" name="XW${s}">
function receivedText(e) { function receivedText(e) {
let lines = e.target.result; let lines = e.target.result;
var c = JSON.parse(lines); let c = JSON.parse(lines);
if (c.hw) { if (c.hw) {
if (c.hw.led) { if (c.hw.led) {
for (var i=0; i<10; i++) addLEDs(-1); // remove all existing outputs
var l = c.hw.led; for (const i=0; i<36; i++) addLEDs(-1); // was i<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let l = c.hw.led;
l.ins.forEach((v,i,a)=>{ l.ins.forEach((v,i,a)=>{
addLEDs(1); addLEDs(1);
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j]; for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
d.getElementsByName("LT"+i)[0].value = v.type; d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start; d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len; d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order; d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
d.getElementsByName("SL"+i)[0].value = v.skip; d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("RF"+i)[0].checked = v.ref; d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev; d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
d.getElementsByName("WO"+i)[0].value = (v.order>>4) & 0x0F;
d.getElementsByName("SP"+i)[0].value = v.freq;
d.getElementsByName("LA"+i)[0].value = v.ledma;
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
}); });
d.getElementsByName("PR")[0].checked = l.prl | 0;
d.getElementsByName("LD")[0].checked = l.ld;
d.getElementsByName("MA")[0].value = l.maxpwr;
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
} }
if(c.hw.com) { if(c.hw.com) {
resetCOM(); resetCOM();
@ -626,22 +648,28 @@ Swap: <select id="xw${s}" name="XW${s}">
addCOM(e.start, e.len, e.order); addCOM(e.start, e.len, e.order);
}); });
} }
if (c.hw.btn) { let b = c.hw.btn;
var b = c.hw.btn; if (b) {
if (Array.isArray(b.ins)) gId("btns").innerHTML = ""; if (Array.isArray(b.ins)) gId("btns").innerHTML = "";
b.ins.forEach((v,i,a)=>{ b.ins.forEach((v,i,a)=>{
addBtn(i,v.pin[0],v.type); addBtn(i,v.pin[0],v.type);
}); });
d.getElementsByName("TT")[0].value = b.tt; d.getElementsByName("TT")[0].value = b.tt;
} }
if (c.hw.ir) { let ir = c.hw.ir;
d.getElementsByName("IR")[0].value = c.hw.ir.pin; if (ir) {
d.getElementsByName("IT")[0].value = c.hw.ir.type; d.getElementsByName("IR")[0].value = ir.pin;
d.getElementsByName("IT")[0].value = ir.type;
} }
if (c.hw.relay) { let rl = c.hw.relay;
d.getElementsByName("RL")[0].value = c.hw.relay.pin; if (rl) {
d.getElementsByName("RM")[0].checked = c.hw.relay.rev; d.getElementsByName("RL")[0].value = rl.pin;
d.getElementsByName("RO")[0].checked = c.hw.relay.odrain; d.getElementsByName("RM")[0].checked = rl.rev;
d.getElementsByName("RO")[0].checked = rl.odrain;
}
let li = c.light;
if (li) {
d.getElementsByName("MS")[0].checked = li.aseg;
} }
UI(); UI();
} }

View File

@ -141,7 +141,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
useGlobalLedBuffer = request->hasArg(F("LD")); useGlobalLedBuffer = request->hasArg(F("LD"));
bool busesChanged = false; bool busesChanged = false;
for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) { for (int s = 0; s < 36; s++) { // theoretical limit is 36 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
int offset = s < 10 ? 48 : 55; int offset = s < 10 ? 48 : 55;
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length
@ -157,7 +157,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA
if (!request->hasArg(lp)) { if (!request->hasArg(lp)) {
DEBUG_PRINTF_P(PSTR("No data for %d\n"), s); DEBUG_PRINTF_P(PSTR("# of buses: %d\n"), s+1);
break; break;
} }
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {

View File

@ -272,7 +272,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
// set limits // set limits
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"), settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
WLED_MAX_BUSSES, WLED_MAX_BUSSES,
WLED_MIN_VIRTUAL_BUSSES, WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
MAX_LEDS_PER_BUS, MAX_LEDS_PER_BUS,
MAX_LED_MEMORY, MAX_LED_MEMORY,
MAX_LEDS, MAX_LEDS,