mirror of
https://github.com/wled/WLED.git
synced 2026-01-11 01:37:56 +00:00
Compare commits
32 Commits
main
...
copilot/ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1988199ce0 | ||
|
|
8da802a569 | ||
|
|
0c0a4d0c7a | ||
|
|
423e1f51d3 | ||
|
|
a1487bb3ec | ||
|
|
acc32f2ef3 | ||
|
|
4f59e53f30 | ||
|
|
da0e985160 | ||
|
|
51a6868468 | ||
|
|
28db9cbbd4 | ||
|
|
4fe3e9fcdc | ||
|
|
7fb0dca286 | ||
|
|
9c7cc22022 | ||
|
|
27c04c8743 | ||
|
|
b7170bbbaa | ||
|
|
ea9d53c22a | ||
|
|
4733e4f708 | ||
|
|
04088655e7 | ||
|
|
7a8874fff9 | ||
|
|
96710630ce | ||
|
|
5589bfd371 | ||
|
|
07c4305217 | ||
|
|
efec2b4c38 | ||
|
|
8f6f24a665 | ||
|
|
54edff85c6 | ||
|
|
87fe97b5f6 | ||
|
|
6acf3ef003 | ||
|
|
ffd29de610 | ||
|
|
0e34591eea | ||
|
|
afdd0e3f4b | ||
|
|
2726a91aa6 | ||
|
|
a69e04f7ef |
@@ -1162,24 +1162,39 @@ void WS2812FX::finalizeInit() {
|
||||
|
||||
unsigned digitalCount = 0;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
|
||||
// Determine if I2S/LCD should be used and whether parallel mode is possible
|
||||
unsigned maxLedsOnBus = 0;
|
||||
unsigned busType = 0;
|
||||
bool mixedBusTypes = false;
|
||||
for (const auto &bus : busConfigs) {
|
||||
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
|
||||
digitalCount++;
|
||||
if (busType == 0) busType = bus.type; // remember first bus type
|
||||
if (busType != bus.type) {
|
||||
DEBUG_PRINTF_P(PSTR("Mixed digital bus types detected! Forcing single I2S output.\n"));
|
||||
useParallelI2S = false; // mixed bus types, no parallel I2S
|
||||
mixedBusTypes = true;
|
||||
}
|
||||
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
|
||||
// we may remove 600 LEDs per bus limit when NeoPixelBus is updated beyond 2.8.3
|
||||
if (maxLedsOnBus <= 600 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
|
||||
else useParallelI2S = false; // enforce single I2S
|
||||
|
||||
// Determine parallel vs single I2S usage
|
||||
bool useParallelI2S = false;
|
||||
if (useI2S) {
|
||||
// Parallel I2S only possible if: no mixed bus types, LEDs per bus <= 600, and enabled by user
|
||||
if (!mixedBusTypes && maxLedsOnBus <= 600) {
|
||||
useParallelI2S = true;
|
||||
DEBUG_PRINTF_P(PSTR("Using parallel I2S/LCD output.\n"));
|
||||
} else {
|
||||
DEBUG_PRINTF_P(PSTR("Using single I2S output (mixed types or >600 LEDs/bus).\n"));
|
||||
}
|
||||
}
|
||||
|
||||
// Set the flags in PolyBus via BusManager
|
||||
BusManager::useI2SOutput(useI2S);
|
||||
if (useParallelI2S) {
|
||||
BusManager::useParallelOutput(); // This sets parallel I2S flag - must call before creating buses
|
||||
}
|
||||
digitalCount = 0;
|
||||
#endif
|
||||
|
||||
@@ -1190,12 +1205,25 @@ void WS2812FX::finalizeInit() {
|
||||
for (const auto &bus : busConfigs) {
|
||||
unsigned memB = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // does not include DMA/RMT buffer
|
||||
mem += memB;
|
||||
// estimate maximum I2S memory usage (only relevant for digital non-2pin busses)
|
||||
// estimate maximum I2S memory usage (only relevant for digital non-2pin busses when I2S is enabled)
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
const bool usesI2S = ((useParallelI2S && digitalCount <= 8) || (!useParallelI2S && digitalCount == 1));
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
const bool usesI2S = (useParallelI2S && digitalCount <= 8);
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
bool usesI2S = false;
|
||||
if (BusManager::hasI2SOutput()) {
|
||||
if (BusManager::hasParallelOutput()) {
|
||||
// Parallel I2S: first 8 buses use I2S
|
||||
usesI2S = (digitalCount <= 8);
|
||||
} else {
|
||||
// Single I2S: only the last bus uses I2S
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
usesI2S = (digitalCount == 9); // bus 8 (9th bus, 0-indexed)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
usesI2S = (digitalCount == 5); // bus 4 (5th bus, 0-indexed)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
usesI2S = false; // S3 doesn't support single I2S
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
const bool usesI2S = false;
|
||||
#endif
|
||||
@@ -1206,6 +1234,8 @@ void WS2812FX::finalizeInit() {
|
||||
constexpr unsigned stepFactor = 3; // 3 step cadence (3 bits per pixel bit)
|
||||
#endif
|
||||
unsigned i2sCommonSize = stepFactor * bus.count * (3*Bus::hasRGB(bus.type)+Bus::hasWhite(bus.type)+Bus::hasCCT(bus.type)) * (Bus::is16bit(bus.type)+1);
|
||||
// Parallel I2S uses 8 channels, requiring 8x the DMA buffer size
|
||||
if (BusManager::hasParallelOutput()) i2sCommonSize *= 8;
|
||||
if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
extern char cmDNS[];
|
||||
extern bool cctICused;
|
||||
extern bool useParallelI2S;
|
||||
extern bool useI2S;
|
||||
|
||||
// functions to get/set bits in an array - based on functions created by Brandon for GOL
|
||||
// toDo : make this a class that's completely defined in a header file
|
||||
@@ -123,6 +123,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
|
||||
, _colorOrder(bc.colorOrder)
|
||||
, _milliAmpsPerLed(bc.milliAmpsPerLed)
|
||||
, _milliAmpsMax(bc.milliAmpsMax)
|
||||
, _driverType(bc.driverType) // Store driver preference (0=RMT, 1=I2S)
|
||||
{
|
||||
DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
|
||||
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
|
||||
@@ -139,7 +140,9 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
|
||||
_pins[1] = bc.pins[1];
|
||||
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
|
||||
}
|
||||
_iType = PolyBus::getI(bc.type, _pins, nr);
|
||||
// Reuse the iType that was determined during memory estimation (memUsage)
|
||||
// This avoids calling getI() twice which would double-count channels
|
||||
_iType = bc.iType;
|
||||
if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
@@ -1111,7 +1114,10 @@ size_t BusConfig::memUsage(unsigned nr) const {
|
||||
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
|
||||
} else if (Bus::isDigital(type)) {
|
||||
// if any of digital buses uses I2S, there is additional common I2S DMA buffer not accounted for here
|
||||
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr));
|
||||
// Call getI() to determine bus type and allocate channel (this is the single call)
|
||||
// Store the result in iType for later reuse during bus creation
|
||||
const_cast<BusConfig*>(this)->iType = PolyBus::getI(type, pins, nr, driverType);
|
||||
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, iType);
|
||||
} else if (Bus::isOnOff(type)) {
|
||||
return sizeof(BusOnOff);
|
||||
} else {
|
||||
@@ -1213,6 +1219,14 @@ bool BusManager::hasParallelOutput() {
|
||||
return PolyBus::isParallelI2S1Output();
|
||||
}
|
||||
|
||||
void BusManager::useI2SOutput(bool enable) {
|
||||
PolyBus::setI2SOutput(enable);
|
||||
}
|
||||
|
||||
bool BusManager::hasI2SOutput() {
|
||||
return PolyBus::isI2SOutput();
|
||||
}
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void BusManager::removeAll() {
|
||||
DEBUGBUS_PRINTLN(F("Removing all."));
|
||||
@@ -1220,6 +1234,8 @@ void BusManager::removeAll() {
|
||||
while (!canAllShow()) yield();
|
||||
busses.clear();
|
||||
PolyBus::setParallelI2S1Output(false);
|
||||
// Reset channel tracking for fresh allocation
|
||||
PolyBus::resetChannelTracking();
|
||||
}
|
||||
|
||||
#ifdef ESP32_DATA_IDLE_HIGH
|
||||
@@ -1423,6 +1439,7 @@ ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; }
|
||||
|
||||
|
||||
bool PolyBus::_useParallelI2S = false;
|
||||
bool PolyBus::_useI2S = false;
|
||||
|
||||
// Bus static member definition
|
||||
int16_t Bus::_cct = -1;
|
||||
@@ -1431,6 +1448,10 @@ uint8_t Bus::_gAWM = 255;
|
||||
|
||||
uint16_t BusDigital::_milliAmpsTotal = 0;
|
||||
|
||||
// PolyBus channel tracking for dynamic allocation
|
||||
uint8_t PolyBus::_rmtChannelsUsed = 0;
|
||||
uint8_t PolyBus::_i2sChannelsUsed = 0;
|
||||
|
||||
std::vector<std::unique_ptr<Bus>> BusManager::busses;
|
||||
uint16_t BusManager::_gMilliAmpsUsed = 0;
|
||||
uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
|
||||
@@ -140,6 +140,7 @@ class Bus {
|
||||
virtual uint16_t getLEDCurrent() const { return 0; }
|
||||
virtual uint16_t getUsedCurrent() const { return 0; }
|
||||
virtual uint16_t getMaxCurrent() const { return 0; }
|
||||
virtual uint8_t getDriverType() const { return 0; } // Default to RMT (0) for non-digital buses
|
||||
virtual size_t getBusSize() const { return sizeof(Bus); }
|
||||
virtual const String getCustomText() const { return String(); }
|
||||
|
||||
@@ -258,6 +259,7 @@ class BusDigital : public Bus {
|
||||
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
|
||||
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
|
||||
uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
|
||||
uint8_t getDriverType() const override { return _driverType; }
|
||||
void setCurrentLimit(uint16_t milliAmps) { _milliAmpsLimit = milliAmps; }
|
||||
void estimateCurrent(); // estimate used current from summed colors
|
||||
void applyBriLimit(uint8_t newBri);
|
||||
@@ -272,6 +274,7 @@ class BusDigital : public Bus {
|
||||
uint8_t _colorOrder;
|
||||
uint8_t _pins[2];
|
||||
uint8_t _iType;
|
||||
uint8_t _driverType; // 0=RMT (default), 1=I2S
|
||||
uint16_t _frequencykHz;
|
||||
uint16_t _milliAmpsMax;
|
||||
uint8_t _milliAmpsPerLed;
|
||||
@@ -421,9 +424,11 @@ struct BusConfig {
|
||||
uint16_t frequency;
|
||||
uint8_t milliAmpsPerLed;
|
||||
uint16_t milliAmpsMax;
|
||||
uint8_t driverType; // 0=RMT (default), 1=I2S
|
||||
uint8_t iType; // internal bus type (I_*) determined during memory estimation, used for bus creation
|
||||
String text;
|
||||
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT, String sometext = "")
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT, uint8_t driver=0, String sometext = "")
|
||||
: count(std::max(len,(uint16_t)1))
|
||||
, start(pstart)
|
||||
, colorOrder(pcolorOrder)
|
||||
@@ -433,13 +438,15 @@ struct BusConfig {
|
||||
, frequency(clock_kHz)
|
||||
, milliAmpsPerLed(maPerLed)
|
||||
, milliAmpsMax(maMax)
|
||||
, driverType(driver)
|
||||
, iType(I_NONE) // Initialize to I_NONE, will be set during memory estimation
|
||||
, text(sometext)
|
||||
{
|
||||
refreshReq = (bool) GET_BIT(busType,7);
|
||||
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
|
||||
size_t nPins = Bus::getNumberOfPins(type);
|
||||
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d, driver:%d)\n"),
|
||||
(int)start, (int)(start+len),
|
||||
(int)type,
|
||||
(int)colorOrder,
|
||||
@@ -447,7 +454,8 @@ struct BusConfig {
|
||||
(int)skipAmount,
|
||||
(int)autoWhite,
|
||||
(int)frequency,
|
||||
(int)milliAmpsPerLed, (int)milliAmpsMax
|
||||
(int)milliAmpsPerLed, (int)milliAmpsMax,
|
||||
(int)driverType
|
||||
);
|
||||
}
|
||||
|
||||
@@ -504,6 +512,8 @@ namespace BusManager {
|
||||
|
||||
void useParallelOutput(); // workaround for inaccessible PolyBus
|
||||
bool hasParallelOutput(); // workaround for inaccessible PolyBus
|
||||
void useI2SOutput(bool enable); // set I2S/LCD usage flag
|
||||
bool hasI2SOutput(); // check I2S/LCD usage flag
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void removeAll();
|
||||
|
||||
@@ -339,11 +339,14 @@
|
||||
//handles pointer type conversion for all possible bus types
|
||||
class PolyBus {
|
||||
private:
|
||||
static bool _useParallelI2S;
|
||||
static bool _useParallelI2S; // use parallel I2S/LCD (8 channels)
|
||||
static bool _useI2S; // use I2S/LCD at all (could be parallel or single)
|
||||
|
||||
public:
|
||||
static inline void setParallelI2S1Output(bool b = true) { _useParallelI2S = b; }
|
||||
static inline bool isParallelI2S1Output(void) { return _useParallelI2S; }
|
||||
static inline void setI2SOutput(bool b = true) { _useI2S = b; }
|
||||
static inline bool isI2SOutput(void) { return _useI2S; }
|
||||
|
||||
// initialize SPI bus speed for DotStar methods
|
||||
template <class T>
|
||||
@@ -481,16 +484,11 @@ class PolyBus {
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (_useParallelI2S && (channel >= 8)) {
|
||||
// Parallel I2S channels are to be used first, so subtract 8 to get the RMT channel number
|
||||
// I2S/LCD channels are to be used first, so subtract 8 to get the RMT channel number
|
||||
channel -= 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
|
||||
if (!_useParallelI2S && channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
|
||||
#endif
|
||||
|
||||
void* busPtr = nullptr;
|
||||
switch (busType) {
|
||||
case I_NONE: break;
|
||||
@@ -1283,8 +1281,19 @@ class PolyBus {
|
||||
return size;
|
||||
}
|
||||
|
||||
// Channel tracking for dynamic allocation
|
||||
static uint8_t _rmtChannelsUsed;
|
||||
static uint8_t _i2sChannelsUsed;
|
||||
|
||||
// Reset channel tracking (call before adding buses)
|
||||
static void resetChannelTracking() {
|
||||
_rmtChannelsUsed = 0;
|
||||
_i2sChannelsUsed = 0;
|
||||
}
|
||||
|
||||
//gives back the internal type index (I_XX_XXX_X above) for the input
|
||||
static uint8_t getI(uint8_t busType, const uint8_t* pins, uint8_t num = 0) {
|
||||
// driverPreference: 0=RMT (default), 1=I2S/LCD
|
||||
static uint8_t getI(uint8_t busType, const uint8_t* pins, uint8_t num = 0, uint8_t driverPreference = 0) {
|
||||
if (!Bus::isDigital(busType)) return I_NONE;
|
||||
if (Bus::is2Pin(busType)) { //SPI LED chips
|
||||
bool isHSPI = false;
|
||||
@@ -1340,39 +1349,43 @@ class PolyBus {
|
||||
return I_8266_U0_SM16825_5 + offset;
|
||||
}
|
||||
#else //ESP32
|
||||
uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S1 [I2S0 is used by Audioreactive]
|
||||
// Dynamic channel allocation based on driver preference
|
||||
// Get platform-specific max channels
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// ESP32-S2 only has 4 RMT channels
|
||||
if (_useParallelI2S) {
|
||||
if (num > 11) return I_NONE;
|
||||
if (num < 8) offset = 1; // use x8 parallel I2S0 channels followed by RMT
|
||||
// Note: conflicts with AudioReactive if enabled
|
||||
} else {
|
||||
if (num > 4) return I_NONE;
|
||||
if (num > 3) offset = 1; // only one I2S0 (use last to allow Audioreactive)
|
||||
}
|
||||
const uint8_t maxRMT = 4;
|
||||
const uint8_t maxI2S = _useI2S ? (_useParallelI2S ? 8 : 1) : 0;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// On ESP32-C3 only the first 2 RMT channels are usable for transmitting
|
||||
if (num > 1) return I_NONE;
|
||||
//if (num > 1) offset = 1; // I2S not supported yet (only 1 I2S)
|
||||
const uint8_t maxRMT = 2;
|
||||
const uint8_t maxI2S = 0; // I2S not supported on C3
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// On ESP32-S3 only the first 4 RMT channels are usable for transmitting
|
||||
if (_useParallelI2S) {
|
||||
if (num > 11) return I_NONE;
|
||||
if (num < 8) offset = 1; // use x8 parallel I2S LCD channels, followed by RMT
|
||||
} else {
|
||||
if (num > 3) return I_NONE; // do not use single I2S (as it is not supported)
|
||||
}
|
||||
const uint8_t maxRMT = 4;
|
||||
const uint8_t maxI2S = _useI2S ? (_useParallelI2S ? 8 : 0) : 0; // LCD only, no single I2S
|
||||
#else
|
||||
// standard ESP32 has 8 RMT and x1/x8 I2S1 channels
|
||||
if (_useParallelI2S) {
|
||||
if (num > 15) return I_NONE;
|
||||
if (num < 8) offset = 1; // 8 I2S followed by 8 RMT
|
||||
} else {
|
||||
if (num > 9) return I_NONE;
|
||||
if (num == 0) offset = 1; // prefer I2S1 for 1st bus (less flickering but more RAM needed)
|
||||
}
|
||||
// Standard ESP32
|
||||
const uint8_t maxRMT = 8;
|
||||
const uint8_t maxI2S = _useI2S ? (_useParallelI2S ? 8 : 1) : 0;
|
||||
#endif
|
||||
|
||||
// Determine which driver to use based on preference and availability
|
||||
uint8_t offset = 0; // 0 = RMT, 1 = I2S/LCD
|
||||
|
||||
if (driverPreference == 1 && _i2sChannelsUsed < maxI2S) {
|
||||
// User wants I2S and we have I2S channels available
|
||||
offset = 1;
|
||||
_i2sChannelsUsed++;
|
||||
} else if (_rmtChannelsUsed < maxRMT) {
|
||||
// Use RMT (either user wants RMT, or I2S unavailable, or fallback)
|
||||
_rmtChannelsUsed++;
|
||||
} else if (_i2sChannelsUsed < maxI2S) {
|
||||
// RMT full, fallback to I2S if available
|
||||
offset = 1;
|
||||
_i2sChannelsUsed++;
|
||||
} else {
|
||||
// No channels available
|
||||
return I_NONE;
|
||||
}
|
||||
|
||||
// Now determine actual bus type with the chosen offset
|
||||
switch (busType) {
|
||||
case TYPE_WS2812_1CH_X3:
|
||||
case TYPE_WS2812_2CH_X3:
|
||||
@@ -1406,5 +1419,10 @@ class PolyBus {
|
||||
}
|
||||
return I_NONE;
|
||||
}
|
||||
|
||||
static void resetChannelTracking() {
|
||||
_rmtChannelsUsed = 0;
|
||||
_i2sChannelsUsed = 0;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -166,7 +166,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
CJSON(useParallelI2S, hw_led[F("prl")]);
|
||||
CJSON(useI2S, hw_led[F("prl")]);
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -234,9 +234,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
maMax = 0;
|
||||
}
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
uint8_t driverType = elm[F("drv")] | 0; // 0=RMT (default), 1=I2S
|
||||
|
||||
String host = elm[F("text")] | String();
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax, host);
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax, driverType, host);
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
|
||||
}
|
||||
@@ -319,7 +320,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
unsigned start = 0;
|
||||
// analog always has length 1
|
||||
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
|
||||
busConfigs.emplace_back(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0);
|
||||
busConfigs.emplace_back(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, LED_MILLIAMPS_DEFAULT, ABL_MILLIAMPS_DEFAULT, 0); // driver=0 (RMT default)
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
}
|
||||
}
|
||||
@@ -984,6 +985,10 @@ void serializeConfig(JsonObject root) {
|
||||
ins[F("freq")] = bus->getFrequency();
|
||||
ins[F("maxpwr")] = bus->getMaxCurrent();
|
||||
ins[F("ledma")] = bus->getLEDCurrent();
|
||||
// Save driver preference from busConfigs if available
|
||||
if (s < busConfigs.size()) {
|
||||
ins[F("drv")] = busConfigs[s].driverType;
|
||||
}
|
||||
ins[F("text")] = bus->getCustomText();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,21 +63,22 @@ constexpr size_t FIXED_PALETTE_COUNT = DYNAMIC_PALETTE_COUNT + FASTLED_PALETTE_C
|
||||
#endif
|
||||
#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
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 2
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 2 // x2 RMT only (I2S not supported by NPB)
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 6
|
||||
#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
|
||||
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0
|
||||
// I2S is only used when explicitly enabled by user (Enable I2S checkbox)
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT (default), or x4 RMT + x8 I2S0 (when I2S enabled)
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#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 supports parallel x8 LCD on I2S1
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
|
||||
// LCD driver is only used when explicitly enabled by user (Enable I2S checkbox)
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT (default), or x4 RMT + x8 LCD (when I2S enabled)
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT
|
||||
// RMT is used by default; I2S is only used when explicitly enabled by user (Enable I2S checkbox)
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 16 // x8 RMT (default), or x8 RMT + x8 I2S1 (when I2S enabled)
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 16
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#endif
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
}); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
if (loc) d.Sf.action = getURL('/settings/leds');
|
||||
}
|
||||
function bLimits(b,v,p,m,l,o=5,d=2,a=6,n=4) {
|
||||
function bLimits(b,v,p,m,l,o=5,d=2,a=6,n=4,rmt=0,i2s=0) {
|
||||
maxB = b; // maxB - max physical (analog + digital) buses: 32 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
|
||||
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
|
||||
@@ -62,6 +62,8 @@
|
||||
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
|
||||
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
|
||||
maxBT = n; // maxBT - max buttons
|
||||
maxRMT = rmt; // maxRMT - max RMT channels: 8 - ESP32, 4 - S2/S3, 2 - C3, 0 - 8266
|
||||
maxI2S = i2s; // maxI2S - max I2S/LCD channels: 8 - ESP32/S2/S3, 0 - C3/8266
|
||||
}
|
||||
function is8266() { return maxA == 5 && maxD == 3; } // NOTE: see const.h
|
||||
function is32() { return maxA == 16 && maxD == 16; } // NOTE: see const.h
|
||||
@@ -233,13 +235,13 @@
|
||||
if (is8266() && d.Sf["L0"+n].value == 3) { //8266 DMA uses 5x the mem
|
||||
mul = 5;
|
||||
}
|
||||
let parallelI2S = d.Sf.PR.checked && (is32() || isS2() || isS3()) && !isD2P(t);
|
||||
if (isC3() || (isS3() && !parallelI2S)) {
|
||||
mul = 2; // ESP32 RMT uses double buffer
|
||||
} else if ((is32() || isS2() || isS3()) && toNum(n) > (parallelI2S ? 7 : 0)) {
|
||||
mul = 2; // ESP32 RMT uses double buffer
|
||||
} else if ((parallelI2S && toNum(n) < 8) || (n == 0 && is32())) { // I2S uses extra DMA buffer
|
||||
dbl = len * ch * 3; // DMA buffer for parallel I2S (TODO: ony the bus with largst LED count should be used)
|
||||
let enabledI2S = d.Sf.PR.checked && (is32() || isS2() || isS3()) && !isD2P(t); // I2S enabled and not 2-pin LED
|
||||
if (isC3() || !enabledI2S) {
|
||||
mul = 2; // RMT uses double buffer
|
||||
} else if (enabledI2S && toNum(n) < 8) { // I2S/LCD uses extra DMA buffer
|
||||
dbl = len * ch * 3; // DMA buffer for I2S/LCD (TODO: only the bus with largest LED count should be used)
|
||||
// Parallel I2S uses 8 channels, requiring 8x the DMA buffer size
|
||||
if (d.Sf.PI && d.Sf.PI.checked) dbl *= 8;
|
||||
}
|
||||
}
|
||||
return len * ch * mul + dbl + pbfr;
|
||||
@@ -294,14 +296,12 @@
|
||||
|
||||
// enable/disable LED fields
|
||||
updateTypeDropdowns(); // restrict bus types in dropdowns to max allowed digital/analog buses
|
||||
let dC = 0; // count of digital buses (for parallel I2S)
|
||||
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
|
||||
LTs.forEach((s,i)=>{
|
||||
// is the field a LED type?
|
||||
var n = s.name.substring(2,3); // bus number (0-Z)
|
||||
var t = parseInt(s.value);
|
||||
memu += getMem(t, n); // calc memory
|
||||
dC += (isDig(t) && !isD2P(t));
|
||||
setPinConfig(n,t);
|
||||
gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings
|
||||
if (change) { // did we change LED type?
|
||||
@@ -340,9 +340,8 @@
|
||||
let nm = LC.name.substring(0,2); // field name : /L./
|
||||
let n = LC.name.substring(2,3); // bus number (0-Z)
|
||||
let t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
if (isDig(t)) {
|
||||
if (isDig(t) && !isD2P(t)) {
|
||||
if (sameType == 0) sameType = t; // first bus type
|
||||
else if (sameType != t) sameType = -1; // different bus type
|
||||
}
|
||||
// do we have a led count field
|
||||
if (nm=="LC") {
|
||||
@@ -421,12 +420,114 @@
|
||||
} else LC.style.color = "#fff";
|
||||
});
|
||||
if (is32() || isS2() || isS3()) {
|
||||
if (maxLC > 600 || dC < 2 || sameType <= 0) {
|
||||
d.Sf["PR"].checked = false;
|
||||
gId("prl").classList.add("hide");
|
||||
} else
|
||||
gId("prl").classList.remove("hide");
|
||||
} else d.Sf["PR"].checked = false;
|
||||
// Always show show I2S checkbox on ESP32/S2/S3
|
||||
gId("prl").classList.remove("hide");
|
||||
} else {
|
||||
d.Sf["PR"].checked = false;
|
||||
}
|
||||
// Calculate max single-pin digital bus count based on configuration:
|
||||
// - If I2S is enabled and ≤600 LEDs/bus: parallel I2S (8 I2S + RMT)
|
||||
// - If I2S is enabled but >600 LEDs/bus: single I2S (RMT + 1 I2S)
|
||||
// - If I2S is disabled: RMT only
|
||||
let maxDigitalBusCount;
|
||||
if (d.Sf["PR"].checked) {
|
||||
if (maxLC <= 600) {
|
||||
// Parallel I2S mode possible
|
||||
maxDigitalBusCount = maxD; // ESP32: 16, S2: 12, S3: 12
|
||||
} else {
|
||||
// Single I2S mode
|
||||
// Uses maxD-7 to match bus_wrapper.h logic (ESP32: 16-7=9, S2: 12-7=5)
|
||||
if (isS3()) maxDigitalBusCount = 4; // S3 doesn't support single I2S, only RMT
|
||||
else maxDigitalBusCount = maxD - 7;
|
||||
}
|
||||
} else {
|
||||
// RMT only
|
||||
maxDigitalBusCount = (isC3() ? 2 : (isS2() || isS3()) ? 4 : is32() ? 8 : maxD);
|
||||
}
|
||||
|
||||
// Mark invalid buses and update driver info + track channel usage
|
||||
let invalidBusCount = 0;
|
||||
let digitalBusIndex = 0;
|
||||
let rmtUsed = 0, i2sUsed = 0; // track channel usage
|
||||
let maxRMT = (is32() ? 8 : (isS2() || isS3()) ? 4 : isC3() ? 2 : 0);
|
||||
let maxI2S = (is32() ? 8 : (isS2() || isS3()) ? 8 : isS3() ? 8 : 0);
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
|
||||
let n = s.name.substring(2,3);
|
||||
let t = parseInt(s.value);
|
||||
let drv = gId("drv"+n); // bus driver info (I2S/RMT)
|
||||
let drvsel = gId("drvsel"+n); // driver selection dropdown
|
||||
if (drv) drv.textContent = ""; // reset
|
||||
if (drvsel) drvsel.style.display = "none"; // hide by default
|
||||
s.style.color = "#fff"; // reset
|
||||
if (isDig(t) && !isD2P(t)) {
|
||||
digitalBusIndex++;
|
||||
// Count channel usage based on user's driver selection
|
||||
let driverPref = d.Sf["LD"+n] ? parseInt(d.Sf["LD"+n].value || 0) : 0;
|
||||
if (d.Sf.PR.checked && driverPref === 1 && i2sUsed < maxI2S) {
|
||||
i2sUsed++;
|
||||
} else if (rmtUsed < maxRMT) {
|
||||
rmtUsed++;
|
||||
} else if (d.Sf.PR.checked && i2sUsed < maxI2S) {
|
||||
i2sUsed++;
|
||||
}
|
||||
// Mark buses that exceed the limit in red
|
||||
if (digitalBusIndex > maxDigitalBusCount) {
|
||||
s.style.color = "red";
|
||||
invalidBusCount++;
|
||||
}
|
||||
// Update I2S/RMT driver info/dropdown for ESP32 digital buses
|
||||
if (!is8266()) {
|
||||
let useI2S = d.Sf.PR.checked;
|
||||
d.Sf.PR.disabled = false; // reset
|
||||
|
||||
// Show driver selection dropdown when I2S is enabled
|
||||
if (useI2S && drvsel) {
|
||||
drvsel.style.display = "inline";
|
||||
// Set default value if not already set (backward compatibility)
|
||||
if (!d.Sf["LD"+n].value) {
|
||||
d.Sf["LD"+n].value = "0"; // default to RMT
|
||||
}
|
||||
} else if (drv) {
|
||||
// Show info text when I2S is disabled
|
||||
drv.textContent = " (RMT)";
|
||||
}
|
||||
|
||||
// Lock PR checkbox if disabling would result in invalid config
|
||||
if (useI2S) {
|
||||
let rmtCount = is32() ? 8 : (isS2() || isS3()) ? 4 : 0;
|
||||
if (maxLC <= 600) {
|
||||
// Parallel I2S mode
|
||||
if (digitalBusIndex > rmtCount) d.Sf.PR.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
updateTypeDropdowns(); // update type dropdowns to disable unavailable digital/analog types (I2S/RMT bus count may have changed)
|
||||
|
||||
// Show channel usage warning
|
||||
let channelWarning = gId('channelwarning');
|
||||
let channelMsg = gId('channelmsg');
|
||||
if (channelWarning && channelMsg && !is8266()) {
|
||||
if (rmtUsed > maxRMT || i2sUsed > maxI2S) {
|
||||
channelWarning.style.display = 'inline';
|
||||
channelWarning.style.color = 'red';
|
||||
let msg = '';
|
||||
if (rmtUsed > maxRMT) msg += `RMT: ${rmtUsed}/${maxRMT}`;
|
||||
if (i2sUsed > maxI2S) {
|
||||
if (msg) msg += ', ';
|
||||
msg += `I2S: ${i2sUsed}/${maxI2S}`;
|
||||
}
|
||||
channelMsg.textContent = 'Channel limit exceeded! ' + msg;
|
||||
} else if (d.Sf.PR.checked && (rmtUsed >= maxRMT - 1 || i2sUsed >= maxI2S - 1)) {
|
||||
channelWarning.style.display = 'inline';
|
||||
channelWarning.style.color = 'orange';
|
||||
channelMsg.textContent = `Channel usage: RMT ${rmtUsed}/${maxRMT}, I2S ${i2sUsed}/${maxI2S}`;
|
||||
} else {
|
||||
channelWarning.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// distribute ABL current if not using PPL
|
||||
enPPL(sDI);
|
||||
|
||||
@@ -462,6 +563,7 @@
|
||||
gId('fpsWarn').style.display = (d.Sf.FR.value == 0) || (d.Sf.FR.value >= 80) ? 'block':'none';
|
||||
gId('fpsHigh').style.display = (d.Sf.FR.value >= 80) ? 'block':'none';
|
||||
}
|
||||
|
||||
function lastEnd(i) {
|
||||
if (i-- < 1) return 0;
|
||||
var s = chrID(i);
|
||||
@@ -470,6 +572,7 @@
|
||||
if (isPWM(t)) v = 1; //PWM busses
|
||||
return isNaN(v) ? 0 : v;
|
||||
}
|
||||
|
||||
function addLEDs(n,init=true)
|
||||
{
|
||||
var o = gEBCN("iST");
|
||||
@@ -518,6 +621,13 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
|
||||
<span id="p2d${s}"></span><input type="number" name="L2${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p3d${s}"></span><input type="number" name="L3${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="p4d${s}"></span><input type="number" name="L4${s}" class="s" onchange="UI();pinUpd(this);"/>
|
||||
<span id="drv${s}" style="color:#999"></span>
|
||||
<div id="drvsel${s}" style="display:none">
|
||||
Driver: <select name="LD${s}" onchange="UI()">
|
||||
<option value="0">RMT</option>
|
||||
<option value="1">I2S</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="net${s}h" class="hide">Host: <input type="text" name="HS${s}" maxlength="32" pattern="[a-zA-Z0-9_\\-]*" onchange="UI()"/>.local</div>
|
||||
<div id="dig${s}r" style="display:inline"><br><span id="rev${s}">Reversed</span>: <input type="checkbox" name="CV${s}"></div>
|
||||
<div id="dig${s}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${s}" min="0" max="255" value="0" oninput="UI()"></div>
|
||||
@@ -845,12 +955,17 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
}
|
||||
// dynamically enforce bus type availability based on current usage
|
||||
function updateTypeDropdowns() {
|
||||
const i2sLocked = d.Sf.PR.disabled; // checkbox was locked because config needs I2S
|
||||
let I2SdigitalType = null;
|
||||
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
|
||||
let digitalB = 0, analogB = 0, twopinB = 0, virtB = 0;
|
||||
// count currently used buses
|
||||
LTs.forEach(sel => {
|
||||
let t = parseInt(sel.value);
|
||||
if (isDig(t) && !isD2P(t)) digitalB++;
|
||||
if (isDig(t) && !isD2P(t)) {
|
||||
digitalB++;
|
||||
if (i2sLocked && I2SdigitalType === null) I2SdigitalType = t; // use first digital type for I2S lock
|
||||
}
|
||||
if (isPWM(t)) analogB += numPins(t);
|
||||
if (isD2P(t)) twopinB++;
|
||||
if (isVir(t)) virtB++;
|
||||
@@ -861,11 +976,17 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
const disable = (q) => sel.querySelectorAll(q).forEach(o => o.disabled = true);
|
||||
const enable = (q) => sel.querySelectorAll(q).forEach(o => o.disabled = false);
|
||||
enable('option'); // reset all first
|
||||
// max digital buses: ESP32 & S2 support mono I2S as well as parallel so we need to take that into account; S3 only supports parallel
|
||||
// supported outputs using parallel I2S/mono I2S: S2: 12/5, S3: 12/4, ESP32: 16/9
|
||||
let maxDB = maxD - ((is32() || isS2() || isS3()) ? (!d.Sf["PR"].checked) * 8 - (!isS3()) : 0); // adjust max digital buses if parallel I2S is not used
|
||||
// max digital buses when using RMT only: ESP32: 8, S2/S3: 4, C3: 2
|
||||
// max digital buses when I2S enabled: ESP32: 16 (8 I2S + 8 RMT), S2: 12 (8 I2S + 4 RMT), S3: 12 (8 LCD + 4 RMT)
|
||||
let maxDB = d.Sf["PR"].checked ? maxD : (isC3() ? 2 : (isS2() || isS3()) ? 4 : is32() ? 8 : maxD);
|
||||
// disallow adding more of a type that has reached its limit but allow changing the current type
|
||||
if (digitalB >= maxDB && !(isDig(curType) && !isD2P(curType))) disable('option[data-type="D"]');
|
||||
// If I2S is on and a single digital type is in use, hide other single-pin digital types
|
||||
if (I2SdigitalType !== null) {
|
||||
sel.querySelectorAll('option[data-type="D"]').forEach(o => {
|
||||
if (parseInt(o.value) !== I2SdigitalType) o.disabled = true;
|
||||
});
|
||||
}
|
||||
if (twopinB >= 2 && !isD2P(curType)) disable('option[data-type="2P"]');
|
||||
// Disable PWM types that need more pins than available (accounting for current type's pins if PWM)
|
||||
disable(`option[data-type^="${'A'.repeat(maxA - analogB + (isPWM(curType)?numPins(curType):0) + 1)}"]`);
|
||||
@@ -915,8 +1036,11 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
⚠ You might run into stability or lag issues.<br>
|
||||
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
|
||||
</div>
|
||||
<div id="channelwarning" class="warn" style="display: none; color: orange;">
|
||||
⚠ <span id="channelmsg"></span><br>
|
||||
</div>
|
||||
<hr class="sml">
|
||||
<div id="prl" class="hide">Use parallel I2S: <input type="checkbox" name="PR"><br></div>
|
||||
<div id="prl" class="hide">Enable I2S: <input type="checkbox" name="PR" onchange="UI()"><br></div>
|
||||
Make a segment for each output: <input type="checkbox" name="MS"><br>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"><br>
|
||||
<hr class="sml">
|
||||
|
||||
@@ -138,7 +138,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned colorOrder, type, skip, awmode, channelSwap, maPerLed;
|
||||
unsigned colorOrder, type, skip, awmode, channelSwap, maPerLed, driverType;
|
||||
unsigned length, start, maMax;
|
||||
uint8_t pins[OUTPUT_MAX_PINS] = {255, 255, 255, 255, 255};
|
||||
String text;
|
||||
@@ -156,7 +156,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
|
||||
strip.setTargetFps(request->arg(F("FR")).toInt());
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
useParallelI2S = request->hasArg(F("PR"));
|
||||
useI2S = request->hasArg(F("PR"));
|
||||
#endif
|
||||
|
||||
bool busesChanged = false;
|
||||
@@ -175,6 +175,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
|
||||
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 ld[4] = "LD"; ld[2] = offset+s; ld[3] = 0; //driver type (RMT=0, I2S=1)
|
||||
char hs[4] = "HS"; hs[2] = offset+s; hs[3] = 0; //hostname (for network types, custom text for others)
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINTF_P(PSTR("# of buses: %d\n"), s+1);
|
||||
@@ -226,10 +227,11 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
maMax = request->arg(ma).toInt() * request->hasArg(F("PPL")); // if PP-ABL is disabled maMax (per bus) must be 0
|
||||
}
|
||||
type |= request->hasArg(rf) << 7; // off refresh override
|
||||
driverType = request->arg(ld).toInt(); // 0=RMT (default), 1=I2S
|
||||
text = request->arg(hs).substring(0,31);
|
||||
// actual finalization is done in WLED::loop() (removing old busses and adding new)
|
||||
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
|
||||
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, maPerLed, maMax, text);
|
||||
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, maPerLed, maMax, driverType, text);
|
||||
busesChanged = true;
|
||||
}
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
|
||||
@@ -401,7 +401,7 @@ WLED_GLOBAL bool useGlobalLedBuffer _INIT(false); // double buffering disabled o
|
||||
#else
|
||||
WLED_GLOBAL bool useGlobalLedBuffer _INIT(true); // double buffering enabled on ESP32
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
WLED_GLOBAL bool useParallelI2S _INIT(false); // parallel I2S for ESP32
|
||||
WLED_GLOBAL bool useI2S _INIT(false); // I2S/LCD for ESP32 (parallel or single)
|
||||
#endif
|
||||
#endif
|
||||
#ifdef WLED_USE_IC_CCT
|
||||
|
||||
@@ -291,7 +291,23 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
settingsScript.printf_P(PSTR("d.ledTypes=%s;"), BusManager::getLEDTypesJSONString().c_str());
|
||||
|
||||
// set limits
|
||||
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d,%d);"),
|
||||
// Calculate max RMT and I2S channels based on platform
|
||||
uint8_t maxRMT = 0, maxI2S = 0;
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
maxRMT = 8; // ESP32 has 8 RMT channels
|
||||
maxI2S = 8; // Can use 8 parallel I2S or 1 single I2S
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
maxRMT = 4; // ESP32-S2 has 4 RMT channels
|
||||
maxI2S = 8; // Can use 8 parallel I2S or 1 single I2S
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
maxRMT = 4; // ESP32-S3 has 4 RMT channels
|
||||
maxI2S = 8; // Can use 8 parallel LCD (no single I2S support)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
maxRMT = 2; // ESP32-C3 has 2 RMT channels
|
||||
maxI2S = 0; // No I2S support
|
||||
#endif
|
||||
|
||||
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d);"),
|
||||
WLED_MAX_BUSSES,
|
||||
WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
|
||||
MAX_LEDS_PER_BUS,
|
||||
@@ -300,7 +316,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
WLED_MAX_COLOR_ORDER_MAPPINGS,
|
||||
WLED_MAX_DIGITAL_CHANNELS,
|
||||
WLED_MAX_ANALOG_CHANNELS,
|
||||
WLED_MAX_BUTTONS
|
||||
WLED_MAX_BUTTONS,
|
||||
maxRMT,
|
||||
maxI2S
|
||||
);
|
||||
|
||||
printSetFormCheckbox(settingsScript,PSTR("MS"),strip.autoSegments);
|
||||
@@ -330,6 +348,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
char sp[4] = "SP"; sp[2] = offset+s; sp[3] = 0; //bus clock speed
|
||||
char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED current
|
||||
char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max per-port PSU current
|
||||
char ld[4] = "LD"; ld[2] = offset+s; ld[3] = 0; //driver type (RMT=0, I2S=1)
|
||||
char hs[4] = "HS"; hs[2] = offset+s; hs[3] = 0; //hostname (for network types, custom text for others)
|
||||
settingsScript.print(F("addLEDs(1);"));
|
||||
uint8_t pins[OUTPUT_MAX_PINS];
|
||||
@@ -370,6 +389,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
printSetFormValue(settingsScript,sp,speed);
|
||||
printSetFormValue(settingsScript,la,bus->getLEDCurrent());
|
||||
printSetFormValue(settingsScript,ma,bus->getMaxCurrent());
|
||||
printSetFormValue(settingsScript,ld,bus->getDriverType());
|
||||
printSetFormValue(settingsScript,hs,bus->getCustomText().c_str());
|
||||
sumMa += bus->getMaxCurrent();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user