Set to \"*\" to "
"connect to any die. Leave Blank to disable. Saving will replace \"*\" with die names.','');"));
#if USING_TFT_DISPLAY
- oappend(SET_F("ddr=addDropdown('DiceTray','rotation');"));
- oappend(SET_F("addOption(ddr,'0 deg',0);"));
- oappend(SET_F("addOption(ddr,'90 deg',1);"));
- oappend(SET_F("addOption(ddr,'180 deg',2);"));
- oappend(SET_F("addOption(ddr,'270 deg',3);"));
- oappend(SET_F(
+ oappend(F("ddr=addDropdown('DiceTray','rotation');"));
+ oappend(F("addOption(ddr,'0 deg',0);"));
+ oappend(F("addOption(ddr,'90 deg',1);"));
+ oappend(F("addOption(ddr,'180 deg',2);"));
+ oappend(F("addOption(ddr,'270 deg',3);"));
+ oappend(F(
"addInfo('DiceTray:rotation',1,' DO NOT CHANGE "
"SPI PINS. CHANGES ARE IGNORED.','');"));
- oappend(SET_F("addInfo('TFT:pin[]',0,'','SPI CS');"));
- oappend(SET_F("addInfo('TFT:pin[]',1,'','SPI DC');"));
- oappend(SET_F("addInfo('TFT:pin[]',2,'','SPI RST');"));
- oappend(SET_F("addInfo('TFT:pin[]',3,'','SPI BL');"));
+ oappend(F("addInfo('TFT:pin[]',0,'','SPI CS');"));
+ oappend(F("addInfo('TFT:pin[]',1,'','SPI DC');"));
+ oappend(F("addInfo('TFT:pin[]',2,'','SPI RST');"));
+ oappend(F("addInfo('TFT:pin[]',3,'','SPI BL');"));
#endif
}
diff --git a/usermods/pwm_outputs/usermod_pwm_outputs.h b/usermods/pwm_outputs/usermod_pwm_outputs.h
index 1880308c45..09232f043a 100644
--- a/usermods/pwm_outputs/usermod_pwm_outputs.h
+++ b/usermods/pwm_outputs/usermod_pwm_outputs.h
@@ -29,13 +29,13 @@ class PwmOutput {
return;
DEBUG_PRINTF("pwm_output[%d]: setup to freq %d\n", pin_, freq_);
- if (!pinManager.allocatePin(pin_, true, PinOwner::UM_PWM_OUTPUTS))
+ if (!PinManager::allocatePin(pin_, true, PinOwner::UM_PWM_OUTPUTS))
return;
- channel_ = pinManager.allocateLedc(1);
+ channel_ = PinManager::allocateLedc(1);
if (channel_ == 255) {
DEBUG_PRINTF("pwm_output[%d]: failed to quire ledc\n", pin_);
- pinManager.deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS);
+ PinManager::deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS);
return;
}
@@ -49,9 +49,9 @@ class PwmOutput {
DEBUG_PRINTF("pwm_output[%d]: close\n", pin_);
if (!enabled_)
return;
- pinManager.deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS);
+ PinManager::deallocatePin(pin_, PinOwner::UM_PWM_OUTPUTS);
if (channel_ != 255)
- pinManager.deallocateLedc(channel_, 1);
+ PinManager::deallocateLedc(channel_, 1);
channel_ = 255;
duty_ = 0.0f;
enabled_ = false;
diff --git a/usermods/quinled-an-penta/quinled-an-penta.h b/usermods/quinled-an-penta/quinled-an-penta.h
index 10b7843344..e446720398 100644
--- a/usermods/quinled-an-penta/quinled-an-penta.h
+++ b/usermods/quinled-an-penta/quinled-an-penta.h
@@ -129,7 +129,7 @@ class QuinLEDAnPentaUsermod : public Usermod
void initOledDisplay()
{
PinManagerPinType pins[5] = { { oledSpiClk, true }, { oledSpiData, true }, { oledSpiCs, true }, { oledSpiDc, true }, { oledSpiRst, true } };
- if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_QuinLEDAnPenta)) {
+ if (!PinManager::allocateMultiplePins(pins, 5, PinOwner::UM_QuinLEDAnPenta)) {
DEBUG_PRINTF("[%s] OLED pin allocation failed!\n", _name);
oledEnabled = oledInitDone = false;
return;
@@ -164,11 +164,11 @@ class QuinLEDAnPentaUsermod : public Usermod
oledDisplay->clear();
}
- pinManager.deallocatePin(oledSpiClk, PinOwner::UM_QuinLEDAnPenta);
- pinManager.deallocatePin(oledSpiData, PinOwner::UM_QuinLEDAnPenta);
- pinManager.deallocatePin(oledSpiCs, PinOwner::UM_QuinLEDAnPenta);
- pinManager.deallocatePin(oledSpiDc, PinOwner::UM_QuinLEDAnPenta);
- pinManager.deallocatePin(oledSpiRst, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(oledSpiClk, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(oledSpiData, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(oledSpiCs, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(oledSpiDc, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(oledSpiRst, PinOwner::UM_QuinLEDAnPenta);
delete oledDisplay;
@@ -184,7 +184,7 @@ class QuinLEDAnPentaUsermod : public Usermod
void initSht30TempHumiditySensor()
{
PinManagerPinType pins[2] = { { shtSda, true }, { shtScl, true } };
- if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_QuinLEDAnPenta)) {
+ if (!PinManager::allocateMultiplePins(pins, 2, PinOwner::UM_QuinLEDAnPenta)) {
DEBUG_PRINTF("[%s] SHT30 pin allocation failed!\n", _name);
shtEnabled = shtInitDone = false;
return;
@@ -212,8 +212,8 @@ class QuinLEDAnPentaUsermod : public Usermod
sht30TempHumidSensor->reset();
}
- pinManager.deallocatePin(shtSda, PinOwner::UM_QuinLEDAnPenta);
- pinManager.deallocatePin(shtScl, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(shtSda, PinOwner::UM_QuinLEDAnPenta);
+ PinManager::deallocatePin(shtScl, PinOwner::UM_QuinLEDAnPenta);
delete sht30TempHumidSensor;
diff --git a/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h b/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h
index e57641bf9b..00fc227252 100644
--- a/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h
+++ b/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h
@@ -40,7 +40,7 @@ class RgbRotaryEncoderUsermod : public Usermod
void initRotaryEncoder()
{
PinManagerPinType pins[2] = { { eaIo, false }, { ebIo, false } };
- if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_RGBRotaryEncoder)) {
+ if (!PinManager::allocateMultiplePins(pins, 2, PinOwner::UM_RGBRotaryEncoder)) {
eaIo = -1;
ebIo = -1;
cleanup();
@@ -108,11 +108,11 @@ class RgbRotaryEncoderUsermod : public Usermod
{
// Only deallocate pins if we allocated them ;)
if (eaIo != -1) {
- pinManager.deallocatePin(eaIo, PinOwner::UM_RGBRotaryEncoder);
+ PinManager::deallocatePin(eaIo, PinOwner::UM_RGBRotaryEncoder);
eaIo = -1;
}
if (ebIo != -1) {
- pinManager.deallocatePin(ebIo, PinOwner::UM_RGBRotaryEncoder);
+ PinManager::deallocatePin(ebIo, PinOwner::UM_RGBRotaryEncoder);
ebIo = -1;
}
@@ -303,8 +303,8 @@ class RgbRotaryEncoderUsermod : public Usermod
}
if (eaIo != oldEaIo || ebIo != oldEbIo || stepsPerClick != oldStepsPerClick || incrementPerClick != oldIncrementPerClick) {
- pinManager.deallocatePin(oldEaIo, PinOwner::UM_RGBRotaryEncoder);
- pinManager.deallocatePin(oldEbIo, PinOwner::UM_RGBRotaryEncoder);
+ PinManager::deallocatePin(oldEaIo, PinOwner::UM_RGBRotaryEncoder);
+ PinManager::deallocatePin(oldEbIo, PinOwner::UM_RGBRotaryEncoder);
delete rotaryEncoder;
initRotaryEncoder();
diff --git a/usermods/sd_card/usermod_sd_card.h b/usermods/sd_card/usermod_sd_card.h
index 5dac79159c..da1999d9b5 100644
--- a/usermods/sd_card/usermod_sd_card.h
+++ b/usermods/sd_card/usermod_sd_card.h
@@ -45,7 +45,7 @@ class UsermodSdCard : public Usermod {
{ configPinPico, true }
};
- if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_SdCard)) {
+ if (!PinManager::allocateMultiplePins(pins, 4, PinOwner::UM_SdCard)) {
DEBUG_PRINTF("[%s] SD (SPI) pin allocation failed!\n", _name);
sdInitDone = false;
return;
@@ -75,10 +75,10 @@ class UsermodSdCard : public Usermod {
SD_ADAPTER.end();
DEBUG_PRINTF("[%s] deallocate pins!\n", _name);
- pinManager.deallocatePin(configPinSourceSelect, PinOwner::UM_SdCard);
- pinManager.deallocatePin(configPinSourceClock, PinOwner::UM_SdCard);
- pinManager.deallocatePin(configPinPoci, PinOwner::UM_SdCard);
- pinManager.deallocatePin(configPinPico, PinOwner::UM_SdCard);
+ PinManager::deallocatePin(configPinSourceSelect, PinOwner::UM_SdCard);
+ PinManager::deallocatePin(configPinSourceClock, PinOwner::UM_SdCard);
+ PinManager::deallocatePin(configPinPoci, PinOwner::UM_SdCard);
+ PinManager::deallocatePin(configPinPico, PinOwner::UM_SdCard);
sdInitDone = false;
}
diff --git a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
index 5c2fac0d4d..1436f8fc4c 100644
--- a/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
+++ b/usermods/seven_segment_display_reloaded/usermod_seven_segment_reloaded.h
@@ -165,7 +165,7 @@ class UsermodSSDR : public Usermod {
void _showElements(String *map, int timevar, bool isColon, bool removeZero
) {
- if (!(*map).equals("") && !(*map) == NULL) {
+ if ((map != nullptr) && (*map != nullptr) && !(*map).equals("")) {
int length = String(timevar).length();
bool addZero = false;
if (length == 1) {
@@ -236,11 +236,13 @@ class UsermodSSDR : public Usermod {
}
void _setLeds(int lednr, int lastSeenLedNr, bool range, int countSegments, int number, bool colon) {
+ if ((lednr < 0) || (lednr >= umSSDRLength)) return; // prevent array bounds violation
+ if (!(colon && umSSDRColonblink) && ((number < 0) || (countSegments < 0))) return;
if ((colon && umSSDRColonblink) || umSSDRNumbers[number][countSegments]) {
if (range) {
- for(int i = lastSeenLedNr; i <= lednr; i++) {
+ for(int i = max(0, lastSeenLedNr); i <= lednr; i++) {
umSSDRMask[i] = true;
}
} else {
@@ -383,7 +385,7 @@ class UsermodSSDR : public Usermod {
_setAllFalse();
#ifdef USERMOD_SN_PHOTORESISTOR
- ptr = (Usermod_SN_Photoresistor*) usermods.lookup(USERMOD_ID_SN_PHOTORESISTOR);
+ ptr = (Usermod_SN_Photoresistor*) UsermodManager::lookup(USERMOD_ID_SN_PHOTORESISTOR);
#endif
DEBUG_PRINTLN(F("Setup done"));
}
diff --git a/usermods/sht/usermod_sht.h b/usermods/sht/usermod_sht.h
index c6e17221be..f10c78a251 100644
--- a/usermods/sht/usermod_sht.h
+++ b/usermods/sht/usermod_sht.h
@@ -310,22 +310,22 @@ void ShtUsermod::onMqttConnect(bool sessionPresent) {
* @return void
*/
void ShtUsermod::appendConfigData() {
- oappend(SET_F("dd=addDropdown('"));
+ oappend(F("dd=addDropdown('"));
oappend(_name);
- oappend(SET_F("','"));
+ oappend(F("','"));
oappend(_shtType);
- oappend(SET_F("');"));
- oappend(SET_F("addOption(dd,'SHT30',0);"));
- oappend(SET_F("addOption(dd,'SHT31',1);"));
- oappend(SET_F("addOption(dd,'SHT35',2);"));
- oappend(SET_F("addOption(dd,'SHT85',3);"));
- oappend(SET_F("dd=addDropdown('"));
+ oappend(F("');"));
+ oappend(F("addOption(dd,'SHT30',0);"));
+ oappend(F("addOption(dd,'SHT31',1);"));
+ oappend(F("addOption(dd,'SHT35',2);"));
+ oappend(F("addOption(dd,'SHT85',3);"));
+ oappend(F("dd=addDropdown('"));
oappend(_name);
- oappend(SET_F("','"));
+ oappend(F("','"));
oappend(_unitOfTemp);
- oappend(SET_F("');"));
- oappend(SET_F("addOption(dd,'Celsius',0);"));
- oappend(SET_F("addOption(dd,'Fahrenheit',1);"));
+ oappend(F("');"));
+ oappend(F("addOption(dd,'Celsius',0);"));
+ oappend(F("addOption(dd,'Fahrenheit',1);"));
}
/**
diff --git a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
index f712316b86..707479df17 100644
--- a/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
+++ b/usermods/stairway_wipe_basic/stairway-wipe-usermod-v2.h
@@ -96,7 +96,7 @@ void setup() {
jsonTransitionOnce = true;
strip.setTransition(0); //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
- resetTimebase(); //make sure wipe starts from beginning
+ strip.resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
Segment& seg = strip.getSegment(0);
diff --git a/usermods/stairway_wipe_basic/wled06_usermod.ino b/usermods/stairway_wipe_basic/wled06_usermod.ino
index c1264ebfb2..dc2159ee9d 100644
--- a/usermods/stairway_wipe_basic/wled06_usermod.ino
+++ b/usermods/stairway_wipe_basic/wled06_usermod.ino
@@ -86,7 +86,7 @@ void startWipe()
bri = briLast; //turn on
transitionDelayTemp = 0; //no transition
effectCurrent = FX_MODE_COLOR_WIPE;
- resetTimebase(); //make sure wipe starts from beginning
+ strip.resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
Segment& seg = strip.getSegment(0);
diff --git a/usermods/usermod_v2_HttpPullLightControl/readme.md b/usermods/usermod_v2_HttpPullLightControl/readme.md
index cf7f971f79..eb56d505d2 100644
--- a/usermods/usermod_v2_HttpPullLightControl/readme.md
+++ b/usermods/usermod_v2_HttpPullLightControl/readme.md
@@ -93,7 +93,7 @@ After getting the URL (it can be a static file like static.json or a mylogic.php
- -D ABL_MILLIAMPS_DEFAULT=450
- -D DEFAULT_LED_COUNT=60 ; For a LED Ring of 60 LEDs
- -D BTNPIN=41 ; The M5Stack Atom S3 Lite has a button on GPIO41
-- -D LEDPIN=2 ; The M5Stack Atom S3 Lite has a Grove connector on the front, we use this GPIO2
+- -D DATA_PINS=2 ; The M5Stack Atom S3 Lite has a Grove connector on the front, we use this GPIO2
- -D STATUSLED=35 ; The M5Stack Atom S3 Lite has a Multi-Color LED on GPIO35, although I didnt managed to control it
- -D IRPIN=4 ; The M5Stack Atom S3 Lite has a IR LED on GPIO4
diff --git a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
index 52ff3cc1db..a257413b42 100644
--- a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
+++ b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
@@ -103,7 +103,7 @@ class AutoSaveUsermod : public Usermod {
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod has enhanced functionality if
// FourLineDisplayUsermod is available.
- display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
+ display = (FourLineDisplayUsermod*) UsermodManager::lookup(USERMOD_ID_FOUR_LINE_DISP);
#endif
initDone = true;
if (enabled && applyAutoSaveOnBoot) applyPreset(autoSavePreset);
diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
index 008647fa7b..684dd86e46 100644
--- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
+++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h
@@ -543,7 +543,7 @@ void FourLineDisplayUsermod::setup() {
type = NONE;
} else {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
- if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; }
+ if (!PinManager::allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; }
}
} else {
if (i2c_scl<0 || i2c_sda<0) { type=NONE; }
@@ -569,7 +569,7 @@ void FourLineDisplayUsermod::setup() {
if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed."));
if (isSPI) {
- pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay);
+ PinManager::deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay);
}
type = NONE;
return;
@@ -1202,21 +1202,21 @@ void FourLineDisplayUsermod::onUpdateBegin(bool init) {
//}
void FourLineDisplayUsermod::appendConfigData() {
- oappend(SET_F("dd=addDropdown('4LineDisplay','type');"));
- oappend(SET_F("addOption(dd,'None',0);"));
- oappend(SET_F("addOption(dd,'SSD1306',1);"));
- oappend(SET_F("addOption(dd,'SH1106',2);"));
- oappend(SET_F("addOption(dd,'SSD1306 128x64',3);"));
- oappend(SET_F("addOption(dd,'SSD1305',4);"));
- oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
- oappend(SET_F("addOption(dd,'SSD1309 128x64',9);"));
- oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
- oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
- oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
- oappend(SET_F("addInfo('4LineDisplay:type',1,' Change may require reboot','');"));
- oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');"));
- oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');"));
- oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'','SPI RST');"));
+ oappend(F("dd=addDropdown('4LineDisplay','type');"));
+ oappend(F("addOption(dd,'None',0);"));
+ oappend(F("addOption(dd,'SSD1306',1);"));
+ oappend(F("addOption(dd,'SH1106',2);"));
+ oappend(F("addOption(dd,'SSD1306 128x64',3);"));
+ oappend(F("addOption(dd,'SSD1305',4);"));
+ oappend(F("addOption(dd,'SSD1305 128x64',5);"));
+ oappend(F("addOption(dd,'SSD1309 128x64',9);"));
+ oappend(F("addOption(dd,'SSD1306 SPI',6);"));
+ oappend(F("addOption(dd,'SSD1306 SPI 128x64',7);"));
+ oappend(F("addOption(dd,'SSD1309 SPI 128x64',8);"));
+ oappend(F("addInfo('4LineDisplay:type',1,' Change may require reboot','');"));
+ oappend(F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');"));
+ oappend(F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');"));
+ oappend(F("addInfo('4LineDisplay:pin[]',2,'','SPI RST');"));
}
/*
@@ -1307,7 +1307,7 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType == SSD1309_SPI64);
if (isSPI) {
- if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay);
+ if (pinsChanged || !newSPI) PinManager::deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay);
if (!newSPI) {
// was SPI but is no longer SPI
if (i2c_scl<0 || i2c_sda<0) { newType=NONE; }
@@ -1315,7 +1315,7 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
// still SPI but pins changed
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
- else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
+ else if (!PinManager::allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
}
} else if (newSPI) {
// was I2C but is now SPI
@@ -1324,7 +1324,7 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
} else {
PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
- else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
+ else if (!PinManager::allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
}
} else {
// just I2C type changed
diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
index 5756fbb695..383c1193eb 100644
--- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
@@ -489,7 +489,7 @@ void RotaryEncoderUIUsermod::setup()
enabled = false;
return;
} else {
- if (pinIRQ >= 0 && pinManager.allocatePin(pinIRQ, false, PinOwner::UM_RotaryEncoderUI)) {
+ if (pinIRQ >= 0 && PinManager::allocatePin(pinIRQ, false, PinOwner::UM_RotaryEncoderUI)) {
pinMode(pinIRQ, INPUT_PULLUP);
attachInterrupt(pinIRQ, i2cReadingISR, FALLING); // RISING, FALLING, CHANGE, ONLOW, ONHIGH
DEBUG_PRINTLN(F("Interrupt attached."));
@@ -502,7 +502,7 @@ void RotaryEncoderUIUsermod::setup()
}
} else {
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
- if (pinA<0 || pinB<0 || !pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
+ if (pinA<0 || pinB<0 || !PinManager::allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
pinA = pinB = pinC = -1;
enabled = false;
return;
@@ -525,7 +525,7 @@ void RotaryEncoderUIUsermod::setup()
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
// But it's optional. But you want it.
- display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
+ display = (FourLineDisplayUsermod*) UsermodManager::lookup(USERMOD_ID_FOUR_LINE_DISP);
if (display != nullptr) {
display->setMarkLine(1, 0);
}
@@ -1090,8 +1090,8 @@ void RotaryEncoderUIUsermod::addToConfig(JsonObject &root) {
}
void RotaryEncoderUIUsermod::appendConfigData() {
- oappend(SET_F("addInfo('Rotary-Encoder:PCF8574-address',1,'(not hex!)');"));
- oappend(SET_F("d.extra.push({'Rotary-Encoder':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
+ oappend(F("addInfo('Rotary-Encoder:PCF8574-address',1,'(not hex!)');"));
+ oappend(F("d.extra.push({'Rotary-Encoder':{pin:[['P0',100],['P1',101],['P2',102],['P3',103],['P4',104],['P5',105],['P6',106],['P7',107]]}});"));
}
/**
@@ -1138,14 +1138,14 @@ bool RotaryEncoderUIUsermod::readFromConfig(JsonObject &root) {
if (oldPcf8574) {
if (pinIRQ >= 0) {
detachInterrupt(pinIRQ);
- pinManager.deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI);
+ PinManager::deallocatePin(pinIRQ, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old IRQ pin."));
}
pinIRQ = newIRQpin<100 ? newIRQpin : -1; // ignore PCF8574 pins
} else {
- pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
- pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
- pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
+ PinManager::deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
+ PinManager::deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
+ PinManager::deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
DEBUG_PRINTLN(F("Deallocated old pins."));
}
pinA = newDTpin;
diff --git a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
index b66be290a5..7ecec08e59 100644
--- a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
+++ b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h
@@ -433,8 +433,8 @@ class WordClockUsermod : public Usermod
void appendConfigData()
{
- oappend(SET_F("addInfo('WordClockUsermod:ledOffset', 1, 'Number of LEDs before the letters');"));
- oappend(SET_F("addInfo('WordClockUsermod:Norddeutsch', 1, 'Viertel vor instead of Dreiviertel');"));
+ oappend(F("addInfo('WordClockUsermod:ledOffset', 1, 'Number of LEDs before the letters');"));
+ oappend(F("addInfo('WordClockUsermod:Norddeutsch', 1, 'Viertel vor instead of Dreiviertel');"));
}
/*
diff --git a/usermods/wireguard/wireguard.h b/usermods/wireguard/wireguard.h
index 8c88d00018..8656a704af 100644
--- a/usermods/wireguard/wireguard.h
+++ b/usermods/wireguard/wireguard.h
@@ -54,13 +54,13 @@ class WireguardUsermod : public Usermod {
}
void appendConfigData() {
- oappend(SET_F("addInfo('WireGuard:host',1,'Server Hostname');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:port',1,'Server Port');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:ip',1,'Device IP');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:psk',1,'Pre Shared Key (optional)');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:pem',1,'Private Key');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:pub',1,'Public Key');")); // 0 is field type, 1 is actual field
- oappend(SET_F("addInfo('WireGuard:tz',1,'POSIX timezone string');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:host',1,'Server Hostname');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:port',1,'Server Port');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:ip',1,'Device IP');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:psk',1,'Pre Shared Key (optional)');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:pem',1,'Private Key');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:pub',1,'Public Key');")); // 0 is field type, 1 is actual field
+ oappend(F("addInfo('WireGuard:tz',1,'POSIX timezone string');")); // 0 is field type, 1 is actual field
}
void addToConfig(JsonObject& root) {
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 807594e430..2f24f745a7 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -2,24 +2,10 @@
WS2812FX.cpp contains all effect methods
Harm Aldick - 2016
www.aldick.org
- LICENSE
- The MIT License (MIT)
+
Copyright (c) 2016 Harm Aldick
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
+ Licensed under the EUPL v. 1.2 or later
+ Adapted from code originally licensed under the MIT license
Modified heavily for WLED
*/
@@ -75,7 +61,7 @@ int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
static um_data_t* getAudioData() {
um_data_t *um_data;
- if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
+ if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio
um_data = simulateSound(SEGMENT.soundSim);
}
@@ -2520,9 +2506,9 @@ static uint16_t ripple_base() {
#endif
{
int left = rippleorigin - propI -1;
- int right = rippleorigin + propI +3;
+ int right = rippleorigin + propI +2;
for (int v = 0; v < 4; v++) {
- unsigned mag = scale8(cubicwave8((propF>>2)+(v-left)*64), amp);
+ unsigned mag = scale8(cubicwave8((propF>>2) + v * 64), amp);
SEGMENT.setPixelColor(left + v, color_blend(SEGMENT.getPixelColor(left + v), col, mag)); // TODO
SEGMENT.setPixelColor(right - v, color_blend(SEGMENT.getPixelColor(right - v), col, mag)); // TODO
}
@@ -4031,7 +4017,7 @@ uint16_t mode_pacifica()
// Increment the four "color index start" counters, one for each wave layer.
// Each is incremented at a different speed, and the speeds vary over time.
- unsigned sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16;
+ unsigned sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step & 0xFFFF, sCIStart4 = (SEGENV.step >> 16);
uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7);
uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7);
strip.now = deltat;
@@ -4046,7 +4032,7 @@ uint16_t mode_pacifica()
sCIStart3 -= (deltams1 * beatsin88(501,5,7));
sCIStart4 -= (deltams2 * beatsin88(257,4,6));
SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2;
- SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3;
+ SEGENV.step = (sCIStart4 << 16) | (sCIStart3 & 0xFFFF);
// Clear out the LED array to a dim background blue-green
//SEGMENT.fill(132618);
@@ -4077,7 +4063,7 @@ uint16_t mode_pacifica()
c.green = scale8(c.green, 200);
c |= CRGB( 2, 5, 7);
- SEGMENT.setPixelColor(i, c.red, c.green, c.blue);
+ SEGMENT.setPixelColor(i, c);
}
strip.now = nowOld;
@@ -4931,8 +4917,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
SEGMENT.fadeToBlackBy(40);
for (size_t i = 0; i < numLines; i++) {
byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1));
- byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1));
- byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24);
+ byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1));
+ byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
@@ -5010,9 +4996,11 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
// draw a gradient line between x and x1
x = x / 2; x1 = x1 / 2;
unsigned steps = abs8(x - x1) + 1;
+ bool positive = (x1 >= x); // direction of drawing
for (size_t k = 1; k <= steps; k++) {
unsigned rate = k * 255 / steps;
- unsigned dx = lerp8by8(x, x1, rate);
+ //unsigned dx = lerp8by8(x, x1, rate);
+ unsigned dx = positive? (x + k-1) : (x - k+1); // behaves the same as "lerp8by8" but does not create holes
//SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate));
SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look
SEGMENT.fadePixelColorXY(dx, i, rate);
@@ -6298,7 +6286,7 @@ static const char _data_FX_MODE_2DPLASMAROTOZOOM[] PROGMEM = "Rotozoomer@!,Scale
uint8_t *fftResult = nullptr;
float *fftBin = nullptr;
um_data_t *um_data;
- if (usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
+ if (UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
volumeSmth = *(float*) um_data->u_data[0];
volumeRaw = *(float*) um_data->u_data[1];
fftResult = (uint8_t*) um_data->u_data[2];
@@ -6483,11 +6471,6 @@ static const char _data_FX_MODE_2DWAVERLY[] PROGMEM = "Waverly@Amplification,Sen
#endif // WLED_DISABLE_2D
-// float version of map()
-static float mapf(float x, float in_min, float in_max, float out_min, float out_max){
- return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
-}
-
// Gravity struct requited for GRAV* effects
typedef struct Gravity {
int topLED;
@@ -6916,7 +6899,7 @@ uint16_t mode_pixels(void) { // Pixels. By Andrew Tuline.
uint8_t *myVals = reinterpret_cast(SEGENV.data); // Used to store a pile of samples because WLED frame rate and WLED sample rate are not synchronized. Frame rate is too low.
um_data_t *um_data;
- if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
+ if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
um_data = simulateSound(SEGMENT.soundSim);
}
float volumeSmth = *(float*) um_data->u_data[0];
@@ -7499,7 +7482,7 @@ uint16_t mode_2DAkemi(void) {
const float normalFactor = 0.4f;
um_data_t *um_data;
- if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
+ if (!UsermodManager::getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
um_data = simulateSound(SEGMENT.soundSim);
}
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
@@ -7533,8 +7516,9 @@ uint16_t mode_2DAkemi(void) {
//add geq left and right
if (um_data && fftResult) {
- for (int x=0; x < cols/8; x++) {
- unsigned band = x * cols/8;
+ int xMax = cols/8;
+ for (int x=0; x < xMax; x++) {
+ unsigned band = map(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands
band = constrain(band, 0, 15);
int barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
CRGB color = CRGB(SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0));
@@ -7751,7 +7735,7 @@ uint16_t mode_2Doctopus() {
const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
- rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI
+ rMap[XY(x, y)].angle = int(40.7436f * atan2f((y - C_Y), (x - C_X))); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu
}
}
diff --git a/wled00/FX.h b/wled00/FX.h
index b1b1fbcb78..5451615464 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -2,24 +2,10 @@
WS2812FX.h - Library for WS2812 LED effects.
Harm Aldick - 2016
www.aldick.org
- LICENSE
- The MIT License (MIT)
+
Copyright (c) 2016 Harm Aldick
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
+ Licensed under the EUPL v. 1.2 or later
+ Adapted from code originally licensed under the MIT license
Modified for WLED
*/
@@ -61,6 +47,15 @@
#define FRAMETIME_FIXED (1000/WLED_FPS)
#define FRAMETIME strip.getFrameTime()
+// FPS calculation (can be defined as compile flag for debugging)
+#ifndef FPS_CALC_AVG
+#define FPS_CALC_AVG 7 // average FPS calculation over this many frames (moving average)
+#endif
+#ifndef FPS_MULTIPLIER
+#define FPS_MULTIPLIER 1 // dev option: multiplier to get sub-frame FPS without floats
+#endif
+#define FPS_CALC_SHIFT 7 // bit shift for fixed point math
+
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
#ifdef ESP8266
@@ -517,68 +512,68 @@ typedef struct Segment {
#endif
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
- inline bool isSelected(void) const { return selected; }
- inline bool isInTransition(void) const { return _t != nullptr; }
- inline bool isActive(void) const { return stop > start; }
- inline bool is2D(void) const { return (width()>1 && height()>1); }
- inline bool hasRGB(void) const { return _isRGB; }
- inline bool hasWhite(void) const { return _hasW; }
- inline bool isCCT(void) const { return _isCCT; }
- inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
- inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1)
- inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
- inline uint16_t groupLength(void) const { return grouping + spacing; }
- inline uint8_t getLightCapabilities(void) const { return _capabilities; }
-
- static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
- static void addUsedSegmentData(int len) { _usedSegmentData += len; }
+ inline bool isSelected() const { return selected; }
+ inline bool isInTransition() const { return _t != nullptr; }
+ inline bool isActive() const { return stop > start; }
+ inline bool is2D() const { return (width()>1 && height()>1); }
+ inline bool hasRGB() const { return _isRGB; }
+ inline bool hasWhite() const { return _hasW; }
+ inline bool isCCT() const { return _isCCT; }
+ inline uint16_t width() const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
+ inline uint16_t height() const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1)
+ inline uint16_t length() const { return width() * height(); } // segment length (count) in physical pixels
+ inline uint16_t groupLength() const { return grouping + spacing; }
+ inline uint8_t getLightCapabilities() const { return _capabilities; }
+
+ inline static uint16_t getUsedSegmentData() { return _usedSegmentData; }
+ inline static void addUsedSegmentData(int len) { _usedSegmentData += len; }
#ifndef WLED_DISABLE_MODE_BLEND
- static void modeBlend(bool blend) { _modeBlend = blend; }
+ inline static void modeBlend(bool blend) { _modeBlend = blend; }
#endif
static void handleRandomPalette();
- inline static const CRGBPalette16 &getCurrentPalette(void) { return Segment::_currentPalette; }
+ inline static const CRGBPalette16 &getCurrentPalette() { return Segment::_currentPalette; }
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
- bool setColor(uint8_t slot, uint32_t c); //returns true if changed
- void setCCT(uint16_t k);
- void setOpacity(uint8_t o);
- void setOption(uint8_t n, bool val);
- void setMode(uint8_t fx, bool loadDefaults = false);
- void setPalette(uint8_t pal);
+ Segment &setColor(uint8_t slot, uint32_t c);
+ Segment &setCCT(uint16_t k);
+ Segment &setOpacity(uint8_t o);
+ Segment &setOption(uint8_t n, bool val);
+ Segment &setMode(uint8_t fx, bool loadDefaults = false);
+ Segment &setPalette(uint8_t pal);
uint8_t differs(Segment& b) const;
- void refreshLightCapabilities(void);
+ void refreshLightCapabilities();
// runtime data functions
- inline uint16_t dataSize(void) const { return _dataLen; }
+ inline uint16_t dataSize() const { return _dataLen; }
bool allocateData(size_t len); // allocates effect data buffer in heap and clears it
- void deallocateData(void); // deallocates (frees) effect data buffer from heap
- void resetIfRequired(void); // sets all SEGENV variables to 0 and clears data buffer
+ void deallocateData(); // deallocates (frees) effect data buffer from heap
+ void resetIfRequired(); // sets all SEGENV variables to 0 and clears data buffer
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
- inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true)
+ inline Segment &markForReset() { reset = true; return *this; } // setOption(SEG_OPTION_RESET, true)
// transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change
- void stopTransition(void); // ends transition mode by destroying transition structure (does nothing if not in transition)
- inline void handleTransition(void) { if (progress() == 0xFFFFU) stopTransition(); }
+ void stopTransition(); // ends transition mode by destroying transition structure (does nothing if not in transition)
+ inline void handleTransition() { if (progress() == 0xFFFFU) stopTransition(); }
#ifndef WLED_DISABLE_MODE_BLEND
void swapSegenv(tmpsegd_t &tmpSegD); // copies segment data into specifed buffer, if buffer is not a transition buffer, segment data is overwritten from transition buffer
void restoreSegenv(tmpsegd_t &tmpSegD); // restores segment data from buffer, if buffer is not transition buffer, changed values are copied to transition buffer
#endif
- uint16_t progress(void) const; // transition progression between 0-65535
- uint8_t currentBri(bool useCct = false) const; // current segment brightness/CCT (blended while in transition)
- uint8_t currentMode(void) const; // currently active effect/mode (while in transition)
- uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition)
+ [[gnu::hot]] uint16_t progress() const; // transition progression between 0-65535
+ [[gnu::hot]] uint8_t currentBri(bool useCct = false) const; // current segment brightness/CCT (blended while in transition)
+ uint8_t currentMode() const; // currently active effect/mode (while in transition)
+ [[gnu::hot]] uint32_t currentColor(uint8_t slot) const; // currently active segment color (blended while in transition)
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
- void setCurrentPalette(void);
+ void setCurrentPalette();
// 1D strip
- uint16_t virtualLength(void) const;
- void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
+ [[gnu::hot]] uint16_t virtualLength() const;
+ [[gnu::hot]] void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); }
@@ -587,7 +582,7 @@ typedef struct Segment {
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
#endif
- uint32_t getPixelColor(int i) const;
+ [[gnu::hot]] uint32_t getPixelColor(int i) const;
// 1D support functions (some implement 2D as well)
void blur(uint8_t, bool smear = false);
void fill(uint32_t c);
@@ -599,8 +594,8 @@ typedef struct Segment {
inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); }
inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); }
inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); }
- uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const;
- uint32_t color_wheel(uint8_t pos) const;
+ [[gnu::hot]] uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255) const;
+ [[gnu::hot]] uint32_t color_wheel(uint8_t pos) const;
// 2D Blur: shortcuts for bluring columns or rows only (50% faster than full 2D blur)
inline void blurCols(fract8 blur_amount, bool smear = false) { // blur all columns
@@ -613,12 +608,18 @@ typedef struct Segment {
}
// 2D matrix
- uint16_t virtualWidth(void) const; // segment width in virtual pixels (accounts for groupping and spacing)
- uint16_t virtualHeight(void) const; // segment height in virtual pixels (accounts for groupping and spacing)
- uint16_t nrOfVStrips(void) const; // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D)
+ [[gnu::hot]] unsigned virtualWidth() const; // segment width in virtual pixels (accounts for groupping and spacing)
+ [[gnu::hot]] unsigned virtualHeight() const; // segment height in virtual pixels (accounts for groupping and spacing)
+ inline unsigned nrOfVStrips() const { // returns number of virtual vertical strips in 2D matrix (used to expand 1D effects into 2D)
+ #ifndef WLED_DISABLE_2D
+ return (is2D() && map1D2D == M12_pBar) ? virtualWidth() : 1;
+ #else
+ return 1;
+ #endif
+ }
#ifndef WLED_DISABLE_2D
- uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
- void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
+ [[gnu::hot]] uint16_t XY(int x, int y); // support function to get relative index within segment
+ [[gnu::hot]] void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
@@ -628,7 +629,7 @@ typedef struct Segment {
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
#endif
- uint32_t getPixelColorXY(int x, int y) const;
+ [[gnu::hot]] uint32_t getPixelColorXY(int x, int y) const;
// 2D support functions
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
@@ -697,8 +698,8 @@ typedef struct Segment {
// main "strip" class
class WS2812FX { // 96 bytes
- typedef uint16_t (*mode_ptr)(void); // pointer to mode function
- typedef void (*show_callback)(void); // pre show callback
+ typedef uint16_t (*mode_ptr)(); // pointer to mode function
+ typedef void (*show_callback)(); // pre show callback
typedef struct ModeData {
uint8_t _id; // mode (effect) id
mode_ptr _fcn; // mode (effect) function
@@ -720,7 +721,11 @@ class WS2812FX { // 96 bytes
#ifndef WLED_DISABLE_2D
panels(1),
#endif
+#ifdef WLED_AUTOSEGMENTS
+ autoSegments(true),
+#else
autoSegments(false),
+#endif
correctWB(false),
cctFromRgb(false),
// semi-private (just obscured) used in effect functions through macros
@@ -733,7 +738,7 @@ class WS2812FX { // 96 bytes
_transitionDur(750),
_targetFps(WLED_FPS),
_frametime(FRAMETIME_FIXED),
- _cumulativeFps(2),
+ _cumulativeFps(50 << FPS_CALC_SHIFT),
_isServicing(false),
_isOffRefreshRequired(false),
_hasWhiteChannel(false),
@@ -764,104 +769,103 @@ class WS2812FX { // 96 bytes
customPalettes.clear();
}
- static WS2812FX* getInstance(void) { return instance; }
+ static WS2812FX* getInstance() { return instance; }
void
#ifdef WLED_DEBUG
printSize(), // prints memory usage for strip components
#endif
finalizeInit(), // initialises strip components
- service(void), // executes effect functions when due and calls strip.show()
+ service(), // executes effect functions when due and calls strip.show()
setMode(uint8_t segid, uint8_t m), // sets effect/mode for given segment (high level API)
setColor(uint8_t slot, uint32_t c), // sets color (in slot) for given segment (high level API)
setCCT(uint16_t k), // sets global CCT (either in relative 0-255 value or in K)
setBrightness(uint8_t b, bool direct = false), // sets strip brightness
setRange(uint16_t i, uint16_t i2, uint32_t col), // used for clock overlay
- purgeSegments(void), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint)
+ purgeSegments(), // removes inactive segments from RAM (may incure penalty and memory fragmentation but reduces vector footprint)
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1),
setMainSegmentId(uint8_t n),
resetSegments(), // marks all segments for reset
makeAutoSegments(bool forceReset = false), // will create segments based on configured outputs
fixInvalidSegments(), // fixes incorrect segment configuration
setPixelColor(unsigned n, uint32_t c), // paints absolute strip pixel with index n and color c
- show(void), // initiates LED output
+ show(), // initiates LED output
setTargetFps(uint8_t fps),
- setupEffectData(void); // add default effects to the list; defined in FX.cpp
+ setupEffectData(); // add default effects to the list; defined in FX.cpp
- inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); }
+ inline void resetTimebase() { timebase = 0UL - millis(); }
+ inline void restartRuntime() { for (Segment &seg : _segments) { seg.markForReset().resetIfRequired(); } }
inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); }
inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
inline void setPixelColor(unsigned n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
inline void setPixelColor(unsigned n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); }
inline void fill(uint32_t c) { for (unsigned i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
- inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
+ inline void trigger() { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; } // sets transition time (in ms)
inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); }
- inline void suspend(void) { _suspend = true; } // will suspend (and canacel) strip.service() execution
- inline void resume(void) { _suspend = false; } // will resume strip.service() execution
+ inline void suspend() { _suspend = true; } // will suspend (and canacel) strip.service() execution
+ inline void resume() { _suspend = false; } // will resume strip.service() execution
bool
paletteFade,
- checkSegmentAlignment(void),
- hasRGBWBus(void) const,
- hasCCTBus(void) const,
- isUpdating(void) const, // return true if the strip is being sent pixel updates
+ checkSegmentAlignment(),
+ hasRGBWBus() const,
+ hasCCTBus() const,
+ isUpdating() const, // return true if the strip is being sent pixel updates
deserializeMap(uint8_t n=0);
- inline bool isServicing(void) const { return _isServicing; } // returns true if strip.service() is executing
- inline bool hasWhiteChannel(void) const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel
- inline bool isOffRefreshRequired(void) const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset)
- inline bool isSuspended(void) const { return _suspend; } // returns true if strip.service() execution is suspended
- inline bool needsUpdate(void) const { return _triggered; } // returns true if strip received a trigger() request
+ inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing
+ inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel
+ inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset)
+ inline bool isSuspended() const { return _suspend; } // returns true if strip.service() execution is suspended
+ inline bool needsUpdate() const { return _triggered; } // returns true if strip received a trigger() request
uint8_t
paletteBlend,
cctBlending,
- getActiveSegmentsNum(void) const,
- getFirstSelectedSegId(void) const,
- getLastActiveSegmentId(void) const,
+ getActiveSegmentsNum() const,
+ getFirstSelectedSegId() const,
+ getLastActiveSegmentId() const,
getActiveSegsLightCapabilities(bool selectedOnly = false) const,
addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp;
- inline uint8_t getBrightness(void) const { return _brightness; } // returns current strip brightness
- inline uint8_t getMaxSegments(void) const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
- inline uint8_t getSegmentsNum(void) const { return _segments.size(); } // returns currently present segments
- inline uint8_t getCurrSegmentId(void) const { return _segment_index; } // returns current segment index (only valid while strip.isServicing())
- inline uint8_t getMainSegmentId(void) const { return _mainSegment; } // returns main segment index
- inline uint8_t getPaletteCount() const { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
- inline uint8_t getTargetFps() const { return _targetFps; } // returns rough FPS value for las 2s interval
- inline uint8_t getModeCount() const { return _modeCount; } // returns number of registered modes/effects
+ inline uint8_t getBrightness() const { return _brightness; } // returns current strip brightness
+ inline uint8_t getMaxSegments() const { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
+ inline uint8_t getSegmentsNum() const { return _segments.size(); } // returns currently present segments
+ inline uint8_t getCurrSegmentId() const { return _segment_index; } // returns current segment index (only valid while strip.isServicing())
+ inline uint8_t getMainSegmentId() const { return _mainSegment; } // returns main segment index
+ inline uint8_t getPaletteCount() const { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
+ inline uint8_t getTargetFps() const { return _targetFps; } // returns rough FPS value for las 2s interval
+ inline uint8_t getModeCount() const { return _modeCount; } // returns number of registered modes/effects
uint16_t
- getLengthPhysical(void) const,
- getLengthTotal(void) const, // will include virtual/nonexistent pixels in matrix
+ getLengthPhysical() const,
+ getLengthTotal() const, // will include virtual/nonexistent pixels in matrix
getFps() const,
getMappedPixelIndex(uint16_t index) const;
- inline uint16_t getFrameTime(void) const { return _frametime; } // returns amount of time a frame should take (in ms)
- inline uint16_t getMinShowDelay(void) const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
- inline uint16_t getLength(void) const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
- inline uint16_t getTransition(void) const { return _transitionDur; } // returns currently set transition time (in ms)
+ inline uint16_t getFrameTime() const { return _frametime; } // returns amount of time a frame should take (in ms)
+ inline uint16_t getMinShowDelay() const { return MIN_SHOW_DELAY; } // returns minimum amount of time strip.service() can be delayed (constant)
+ inline uint16_t getLength() const { return _length; } // returns actual amount of LEDs on a strip (2D matrix may have less LEDs than W*H)
+ inline uint16_t getTransition() const { return _transitionDur; } // returns currently set transition time (in ms)
- uint32_t
- now,
- timebase,
- getPixelColor(uint16_t) const;
+ unsigned long now, timebase;
+ uint32_t getPixelColor(unsigned) const;
- inline uint32_t getLastShow(void) const { return _lastShow; } // returns millis() timestamp of last strip.show() call
+ inline uint32_t getLastShow() const { return _lastShow; } // returns millis() timestamp of last strip.show() call
inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; } // returns currently valid color (for slot i) AKA SEGCOLOR(); may be blended between two colors while in transition
const char *
getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); }
const char **
- getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data
+ getModeDataSrc() { return &(_modeData[0]); } // vectors use arrays for underlying data
Segment& getSegment(uint8_t id);
- inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected"
- inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } // returns reference to main segment
- inline Segment* getSegments(void) { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully)
+ inline Segment& getFirstSelectedSeg() { return _segments[getFirstSelectedSegId()]; } // returns reference to first segment that is "selected"
+ inline Segment& getMainSegment() { return _segments[getMainSegmentId()]; } // returns reference to main segment
+ inline Segment* getSegments() { return &(_segments[0]); } // returns pointer to segment vector structure (warning: use carefully)
// 2D support (panels)
bool
@@ -908,7 +912,7 @@ class WS2812FX { // 96 bytes
// end 2D support
- void loadCustomPalettes(void); // loads custom palettes from JSON
+ void loadCustomPalettes(); // loads custom palettes from JSON
std::vector customPalettes; // TODO: move custom palettes out of WS2812FX class
struct {
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 44919f925c..7c1ae366b7 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -1,24 +1,9 @@
/*
FX_2Dfcn.cpp contains all 2D utility functions
- LICENSE
- The MIT License (MIT)
Copyright (c) 2022 Blaz Kristan (https://blaz.at/home)
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
+ Licensed under the EUPL v. 1.2 or later
+ Adapted from code originally licensed under the MIT license
Parts of the code adapted from WLED Sound Reactive
*/
@@ -161,17 +146,17 @@ void WS2812FX::setUpMatrix() {
#ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
-uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
+uint16_t IRAM_ATTR_YN Segment::XY(int x, int y)
{
unsigned width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
unsigned height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0;
}
-void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
+void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (!isActive()) return; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
+ if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint8_t _bri_t = currentBri();
if (_bri_t < 255) {
@@ -211,7 +196,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
- strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
+ strip.setPixelColorXY(start + width() - xX - 1, startY + height() - yY - 1, tmpCol);
}
}
}
@@ -264,9 +249,9 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
#endif
// returns RGBW values of pixel
-uint32_t IRAM_ATTR Segment::getPixelColorXY(int x, int y) const {
+uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
if (!isActive()) return 0; // not active
- if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
+ if ((unsigned)x >= virtualWidth() || (unsigned)y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
@@ -704,11 +689,14 @@ void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)};
// multiply the intensities by the colour, and saturating-add them to the pixels
for (int i = 0; i < 4; i++) {
- CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1));
+ int wu_x = (x >> 8) + (i & 1); // precalculate x
+ int wu_y = (y >> 8) + ((i >> 1) & 1); // precalculate y
+ CRGB led = getPixelColorXY(wu_x, wu_y);
+ CRGB oldLed = led;
led.r = qadd8(led.r, c.r * wu[i] >> 8);
led.g = qadd8(led.g, c.g * wu[i] >> 8);
led.b = qadd8(led.b, c.b * wu[i] >> 8);
- setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led);
+ if (led != oldLed) setPixelColorXY(wu_x, wu_y, led); // don't repaint if same color
}
}
#undef WU_WEIGHT
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index bc5c8b0519..e706f2b431 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -2,24 +2,10 @@
WS2812FX_fcn.cpp contains all utility functions
Harm Aldick - 2016
www.aldick.org
- LICENSE
- The MIT License (MIT)
+
Copyright (c) 2016 Harm Aldick
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
+ Licensed under the EUPL v. 1.2 or later
+ Adapted from code originally licensed under the MIT license
Modified heavily for WLED
*/
@@ -43,21 +29,16 @@
19, 18, 17, 16, 15, 20, 21, 22, 23, 24, 29, 28, 27, 26, 25]}
*/
-//factory defaults LED setup
-//#define PIXEL_COUNTS 30, 30, 30, 30
-//#define DATA_PINS 16, 1, 3, 4
-//#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
-
#ifndef PIXEL_COUNTS
#define PIXEL_COUNTS DEFAULT_LED_COUNT
#endif
#ifndef DATA_PINS
- #define DATA_PINS LEDPIN
+ #define DATA_PINS DEFAULT_LED_PIN
#endif
-#ifndef DEFAULT_LED_TYPE
- #define DEFAULT_LED_TYPE TYPE_WS2812_RGB
+#ifndef LED_TYPES
+ #define LED_TYPES DEFAULT_LED_TYPE
#endif
#ifndef DEFAULT_LED_COLOR_ORDER
@@ -69,6 +50,18 @@
#error "Max segments must be at least max number of busses!"
#endif
+static constexpr unsigned sumPinsRequired(const unsigned* current, size_t count) {
+ return (count > 0) ? (Bus::getNumberOfPins(*current) + sumPinsRequired(current+1,count-1)) : 0;
+}
+
+static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTypes, unsigned numPins ) {
+ // Pins provided < pins required -> always invalid
+ // Pins provided = pins required -> always valid
+ // Pins provided > pins required -> valid if excess pins are a product of last type pins since it will be repeated
+ return (sumPinsRequired(types, numTypes) > numPins) ? false :
+ (numPins - sumPinsRequired(types, numTypes)) % Bus::getNumberOfPins(types[numTypes-1]) == 0;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Segment class implementation
@@ -146,7 +139,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
}
// allocates effect data buffer on heap and initialises (erases) it
-bool IRAM_ATTR Segment::allocateData(size_t len) {
+bool IRAM_ATTR_YN Segment::allocateData(size_t len) {
if (len == 0) return false; // nothing to do
if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
if (call == 0) memset(data, 0, len); // erase buffer if called during effect initialisation
@@ -170,17 +163,13 @@ bool IRAM_ATTR Segment::allocateData(size_t len) {
return true;
}
-void IRAM_ATTR Segment::deallocateData() {
+void IRAM_ATTR_YN Segment::deallocateData() {
if (!data) { _dataLen = 0; return; }
//DEBUG_PRINTF_P(PSTR("--- Released data (%p): %d/%d -> %p\n"), this, _dataLen, Segment::getUsedSegmentData(), data);
if ((Segment::getUsedSegmentData() > 0) && (_dataLen > 0)) { // check that we don't have a dangling / inconsistent data pointer
free(data);
} else {
- DEBUG_PRINT(F("---- Released data "));
- DEBUG_PRINTF_P(PSTR("(%p): "), this);
- DEBUG_PRINT(F("inconsistent UsedSegmentData "));
- DEBUG_PRINTF_P(PSTR("(%d/%d)"), _dataLen, Segment::getUsedSegmentData());
- DEBUG_PRINTLN(F(", cowardly refusing to free nothing."));
+ DEBUG_PRINTF_P(PSTR("---- Released data (%p): inconsistent UsedSegmentData (%d/%d), cowardly refusing to free nothing.\n"), this, _dataLen, Segment::getUsedSegmentData());
}
data = nullptr;
Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
@@ -202,7 +191,7 @@ void Segment::resetIfRequired() {
reset = false;
}
-CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
+CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // TODO remove strip dependency by moving customPalettes out of strip
//default palette. Differs depending on effect
@@ -417,7 +406,7 @@ uint8_t IRAM_ATTR Segment::currentBri(bool useCct) const {
return (useCct ? cct : (on ? opacity : 0));
}
-uint8_t IRAM_ATTR Segment::currentMode() const {
+uint8_t Segment::currentMode() const {
#ifndef WLED_DISABLE_MODE_BLEND
unsigned prog = progress();
if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
@@ -425,7 +414,7 @@ uint8_t IRAM_ATTR Segment::currentMode() const {
return mode;
}
-uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) const {
+uint32_t IRAM_ATTR_YN Segment::currentColor(uint8_t slot) const {
if (slot >= NUM_COLORS) slot = 0;
#ifndef WLED_DISABLE_MODE_BLEND
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
@@ -520,46 +509,53 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
}
-bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
- if (slot >= NUM_COLORS || c == colors[slot]) return false;
+Segment &Segment::setColor(uint8_t slot, uint32_t c) {
+ if (slot >= NUM_COLORS || c == colors[slot]) return *this;
if (!_isRGB && !_hasW) {
- if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
- if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
+ if (slot == 0 && c == BLACK) return *this; // on/off segment cannot have primary color black
+ if (slot == 1 && c != BLACK) return *this; // on/off segment cannot have secondary color non black
}
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
colors[slot] = c;
stateChanged = true; // send UDP/WS broadcast
- return true;
+ return *this;
}
-void Segment::setCCT(uint16_t k) {
+Segment &Segment::setCCT(uint16_t k) {
if (k > 255) { //kelvin value, convert to 0-255
if (k < 1900) k = 1900;
if (k > 10091) k = 10091;
k = (k - 1900) >> 5;
}
- if (cct == k) return;
- if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
- cct = k;
- stateChanged = true; // send UDP/WS broadcast
+ if (cct != k) {
+ //DEBUGFX_PRINTF_P(PSTR("- Starting CCT transition: %d\n"), k);
+ startTransition(strip.getTransition()); // start transition prior to change
+ cct = k;
+ stateChanged = true; // send UDP/WS broadcast
+ }
+ return *this;
}
-void Segment::setOpacity(uint8_t o) {
- if (opacity == o) return;
- if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
- opacity = o;
- stateChanged = true; // send UDP/WS broadcast
+Segment &Segment::setOpacity(uint8_t o) {
+ if (opacity != o) {
+ //DEBUGFX_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o);
+ startTransition(strip.getTransition()); // start transition prior to change
+ opacity = o;
+ stateChanged = true; // send UDP/WS broadcast
+ }
+ return *this;
}
-void Segment::setOption(uint8_t n, bool val) {
+Segment &Segment::setOption(uint8_t n, bool val) {
bool prevOn = on;
if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
+ return *this;
}
-void Segment::setMode(uint8_t fx, bool loadDefaults) {
+Segment &Segment::setMode(uint8_t fx, bool loadDefaults) {
// skip reserved
while (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4) == 0) fx++;
if (fx >= strip.getModeCount()) fx = 0; // set solid mode
@@ -591,9 +587,10 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
markForReset();
stateChanged = true; // send UDP/WS broadcast
}
+ return *this;
}
-void Segment::setPalette(uint8_t pal) {
+Segment &Segment::setPalette(uint8_t pal) {
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0; // built in palettes
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0; // custom palettes
if (pal != palette) {
@@ -601,37 +598,24 @@ void Segment::setPalette(uint8_t pal) {
palette = pal;
stateChanged = true; // send UDP/WS broadcast
}
+ return *this;
}
// 2D matrix
-uint16_t IRAM_ATTR Segment::virtualWidth() const {
+unsigned IRAM_ATTR Segment::virtualWidth() const {
unsigned groupLen = groupLength();
unsigned vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
-uint16_t IRAM_ATTR Segment::virtualHeight() const {
+unsigned IRAM_ATTR Segment::virtualHeight() const {
unsigned groupLen = groupLength();
unsigned vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight;
}
-uint16_t IRAM_ATTR Segment::nrOfVStrips() const {
- unsigned vLen = 1;
-#ifndef WLED_DISABLE_2D
- if (is2D()) {
- switch (map1D2D) {
- case M12_pBar:
- vLen = virtualWidth();
- break;
- }
- }
-#endif
- return vLen;
-}
-
// Constants for mapping mode "Pinwheel"
#ifndef WLED_DISABLE_2D
constexpr int Pinwheel_Steps_Small = 72; // no holes up to 16x16
@@ -701,7 +685,7 @@ uint16_t IRAM_ATTR Segment::virtualLength() const {
return vLength;
}
-void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
+void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col)
{
if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D
@@ -895,7 +879,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
}
#endif
-uint32_t IRAM_ATTR Segment::getPixelColor(int i) const
+uint32_t IRAM_ATTR_YN Segment::getPixelColor(int i) const
{
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D
@@ -1027,7 +1011,6 @@ void Segment::refreshLightCapabilities() {
if (bus->getStart() >= segStopIdx) continue;
if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
- //uint8_t type = bus->getType();
if (bus->hasRGB() || (strip.cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
if (!strip.cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
if (strip.correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
@@ -1197,12 +1180,9 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
///////////////////////////////////////////////////////////////////////////////
//do not call this method from system context (network callback)
-void WS2812FX::finalizeInit(void) {
+void WS2812FX::finalizeInit() {
//reset segment runtimes
- for (segment &seg : _segments) {
- seg.markForReset();
- seg.resetIfRequired();
- }
+ restartRuntime();
// for the lack of better place enumerate ledmaps here
// if we do it in json.cpp (serializeInfo()) we are getting flashes on LEDs
@@ -1215,28 +1195,82 @@ void WS2812FX::finalizeInit(void) {
//if busses failed to load, add default (fresh install, FS issue, ...)
if (BusManager::getNumBusses() == 0) {
DEBUG_PRINTLN(F("No busses, init default"));
- const unsigned defDataPins[] = {DATA_PINS};
- const unsigned defCounts[] = {PIXEL_COUNTS};
- const unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0]));
- const unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
- // if number of pins is divisible by counts, use number of counts to determine number of buses, otherwise use pins
- const unsigned defNumBusses = defNumPins > defNumCounts && defNumPins%defNumCounts == 0 ? defNumCounts : defNumPins;
- const unsigned pinsPerBus = defNumPins / defNumBusses;
+ constexpr unsigned defDataTypes[] = {LED_TYPES};
+ constexpr unsigned defDataPins[] = {DATA_PINS};
+ constexpr unsigned defCounts[] = {PIXEL_COUNTS};
+ constexpr unsigned defNumTypes = ((sizeof defDataTypes) / (sizeof defDataTypes[0]));
+ constexpr unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0]));
+ constexpr unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
+
+ static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins),
+ "The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES");
+
unsigned prevLen = 0;
- for (unsigned i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
- uint8_t defPin[5]; // max 5 pins
- for (unsigned j = 0; j < pinsPerBus; j++) defPin[j] = defDataPins[i*pinsPerBus + j];
- // when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
- // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), etc
- if (pinManager.isPinAllocated(defPin[0])) {
- defPin[0] = 1; // start with GPIO1 and work upwards
- while (pinManager.isPinAllocated(defPin[0]) && defPin[0] < WLED_NUM_PINS) defPin[0]++;
+ unsigned pinsIndex = 0;
+ for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
+ uint8_t defPin[OUTPUT_MAX_PINS];
+ // if we have less types than requested outputs and they do not align, use last known type to set current type
+ unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1];
+ unsigned busPins = Bus::getNumberOfPins(dataType);
+
+ // if we need more pins than available all outputs have been configured
+ if (pinsIndex + busPins > defNumPins) break;
+
+ // Assign all pins first so we can check for conflicts on this bus
+ for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j];
+
+ for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) {
+ bool validPin = true;
+ // When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
+ // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc.
+ // Pin should not be already allocated, read/only or defined for current bus
+ while (PinManager::isPinAllocated(defPin[j]) || !PinManager::isPinOk(defPin[j],true)) {
+ if (validPin) {
+ DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output."));
+ defPin[j] = 1; // start with GPIO1 and work upwards
+ validPin = false;
+ } else if (defPin[j] < WLED_NUM_PINS) {
+ defPin[j]++;
+ } else {
+ DEBUG_PRINTLN(F("No available pins left! Can't configure output."));
+ return;
+ }
+ // is the newly assigned pin already defined or used previously?
+ // try next in line until there are no clashes or we run out of pins
+ bool clash;
+ do {
+ clash = false;
+ // check for conflicts on current bus
+ for (const auto &pin : defPin) {
+ if (&pin != &defPin[j] && pin == defPin[j]) {
+ clash = true;
+ break;
+ }
+ }
+ // We already have a clash on current bus, no point checking next buses
+ if (!clash) {
+ // check for conflicts in defined pins
+ for (const auto &pin : defDataPins) {
+ if (pin == defPin[j]) {
+ clash = true;
+ break;
+ }
+ }
+ }
+ if (clash) defPin[j]++;
+ if (defPin[j] >= WLED_NUM_PINS) break;
+ } while (clash);
+ }
}
+ pinsIndex += busPins;
+
unsigned start = prevLen;
// if we have less counts than pins and they do not align, use last known count to set current count
unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
+ // analog always has length 1
+ if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
prevLen += count;
- BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
+ BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
if (BusManager::add(defCfg) == -1) break;
}
}
@@ -1249,12 +1283,12 @@ void WS2812FX::finalizeInit(void) {
//RGBW mode is enabled if at least one of the strips is RGBW
_hasWhiteChannel |= bus->hasWhite();
//refresh is required to remain off if at least one of the strips requires the refresh.
- _isOffRefreshRequired |= bus->isOffRefreshRequired();
+ _isOffRefreshRequired |= bus->isOffRefreshRequired() && !bus->isPWM(); // use refresh bit for phase shift with analog
unsigned busEnd = bus->getStart() + bus->getLength();
if (busEnd > _length) _length = busEnd;
#ifdef ESP8266
// why do we need to reinitialise GPIO3???
- //if ((!IS_DIGITAL(bus->getType()) || IS_2PIN(bus->getType()))) continue;
+ //if (!bus->isDigital() || bus->is2Pin()) continue;
//uint8_t pins[5];
//if (!bus->getPins(pins)) continue;
//BusDigital* bd = static_cast(bus);
@@ -1295,7 +1329,7 @@ void WS2812FX::service() {
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
doShow = true;
- unsigned delay = FRAMETIME;
+ unsigned frameDelay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen
int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
@@ -1315,7 +1349,7 @@ void WS2812FX::service() {
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
// would need to be allocated for each effect and then blended together for each pixel.
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
- delay = (*_mode[seg.mode])(); // run new/current mode
+ frameDelay = (*_mode[seg.mode])(); // run new/current mode
#ifndef WLED_DISABLE_MODE_BLEND
if (modeBlending && seg.mode != tmpMode) {
Segment::tmpsegd_t _tmpSegData;
@@ -1324,16 +1358,16 @@ void WS2812FX::service() {
_virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
unsigned d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
- delay = MIN(delay,d2); // use shortest delay
+ frameDelay = min(frameDelay,d2); // use shortest delay
Segment::modeBlend(false); // unset semaphore
}
#endif
seg.call++;
- if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
+ if (seg.isInTransition() && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition
BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
}
- seg.next_time = nowUp + delay;
+ seg.next_time = nowUp + frameDelay;
}
_segment_index++;
}
@@ -1360,13 +1394,13 @@ void IRAM_ATTR WS2812FX::setPixelColor(unsigned i, uint32_t col) {
BusManager::setPixelColor(i, col);
}
-uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) const {
+uint32_t IRAM_ATTR WS2812FX::getPixelColor(unsigned i) const {
i = getMappedPixelIndex(i);
if (i >= _length) return 0;
return BusManager::getPixelColor(i);
}
-void WS2812FX::show(void) {
+void WS2812FX::show() {
// avoid race condition, capture _callback value
show_callback callback = _callback;
if (callback) callback();
@@ -1378,10 +1412,12 @@ void WS2812FX::show(void) {
unsigned long showNow = millis();
size_t diff = showNow - _lastShow;
- size_t fpsCurr = 200;
- if (diff > 0) fpsCurr = 1000 / diff;
- _cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
- _lastShow = showNow;
+
+ if (diff > 0) { // skip calculation if no time has passed
+ size_t fpsCurr = (1000 << FPS_CALC_SHIFT) / diff; // fixed point math
+ _cumulativeFps = (FPS_CALC_AVG * _cumulativeFps + fpsCurr + FPS_CALC_AVG / 2) / (FPS_CALC_AVG + 1); // "+FPS_CALC_AVG/2" for proper rounding
+ _lastShow = showNow;
+ }
}
/**
@@ -1398,7 +1434,7 @@ bool WS2812FX::isUpdating() const {
*/
uint16_t WS2812FX::getFps() const {
if (millis() - _lastShow > 2000) return 0;
- return _cumulativeFps +1;
+ return (FPS_MULTIPLIER * _cumulativeFps) >> FPS_CALC_SHIFT; // _cumulativeFps is stored in fixed point
}
void WS2812FX::setTargetFps(uint8_t fps) {
@@ -1463,7 +1499,7 @@ uint8_t WS2812FX::getActiveSegsLightCapabilities(bool selectedOnly) const {
return totalLC;
}
-uint8_t WS2812FX::getFirstSelectedSegId(void) const {
+uint8_t WS2812FX::getFirstSelectedSegId() const {
size_t i = 0;
for (const segment &seg : _segments) {
if (seg.isActive() && seg.isSelected()) return i;
@@ -1481,14 +1517,14 @@ void WS2812FX::setMainSegmentId(uint8_t n) {
return;
}
-uint8_t WS2812FX::getLastActiveSegmentId(void) const {
+uint8_t WS2812FX::getLastActiveSegmentId() const {
for (size_t i = _segments.size() -1; i > 0; i--) {
if (_segments[i].isActive()) return i;
}
return 0;
}
-uint8_t WS2812FX::getActiveSegmentsNum(void) const {
+uint8_t WS2812FX::getActiveSegmentsNum() const {
uint8_t c = 0;
for (size_t i = 0; i < _segments.size(); i++) {
if (_segments[i].isActive()) c++;
@@ -1496,17 +1532,17 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) const {
return c;
}
-uint16_t WS2812FX::getLengthTotal(void) const {
+uint16_t WS2812FX::getLengthTotal() const {
unsigned len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
return len;
}
-uint16_t WS2812FX::getLengthPhysical(void) const {
+uint16_t WS2812FX::getLengthPhysical() const {
unsigned len = 0;
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
- if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
+ if (bus->isVirtual()) continue; //exclude non-physical network busses
len += bus->getLength();
}
return len;
@@ -1515,7 +1551,7 @@ uint16_t WS2812FX::getLengthPhysical(void) const {
//used for JSON API info.leds.rgbw. Little practical use, deprecate with info.leds.rgbw.
//returns if there is an RGBW bus (supports RGB and White, not only white)
//not influenced by auto-white mode, also true if white slider does not affect output white channel
-bool WS2812FX::hasRGBWBus(void) const {
+bool WS2812FX::hasRGBWBus() const {
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
@@ -1524,7 +1560,7 @@ bool WS2812FX::hasRGBWBus(void) const {
return false;
}
-bool WS2812FX::hasCCTBus(void) const {
+bool WS2812FX::hasCCTBus() const {
if (cctFromRgb && !correctWB) return false;
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index d0e32b2116..5b948b9c41 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -4,6 +4,18 @@
#include
#include
+#ifdef ARDUINO_ARCH_ESP32
+#include "driver/ledc.h"
+#include "soc/ledc_struct.h"
+ #if !(defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3))
+ #define LEDC_MUTEX_LOCK() do {} while (xSemaphoreTake(_ledc_sys_lock, portMAX_DELAY) != pdPASS)
+ #define LEDC_MUTEX_UNLOCK() xSemaphoreGive(_ledc_sys_lock)
+ extern xSemaphoreHandle _ledc_sys_lock;
+ #else
+ #define LEDC_MUTEX_LOCK()
+ #define LEDC_MUTEX_UNLOCK()
+ #endif
+#endif
#include "const.h"
#include "pin_manager.h"
#include "bus_wrapper.h"
@@ -48,38 +60,46 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte
#define W(c) (byte((c) >> 24))
-void ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
- if (_count >= WLED_MAX_COLOR_ORDER_MAPPINGS) {
- return;
- }
- if (len == 0) {
- return;
- }
- // upper nibble contains W swap information
- if ((colorOrder & 0x0F) > COL_ORDER_MAX) {
- return;
- }
- _mappings[_count].start = start;
- _mappings[_count].len = len;
- _mappings[_count].colorOrder = colorOrder;
- _count++;
+bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) {
+ if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information
+ _mappings.push_back({start,len,colorOrder});
+ return true;
}
uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const {
- if (_count > 0) {
- // upper nibble contains W swap information
- // when ColorOrderMap's upper nibble contains value >0 then swap information is used from it, otherwise global swap is used
- for (unsigned i = 0; i < _count; i++) {
- if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
- return _mappings[i].colorOrder | ((_mappings[i].colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0));
- }
+ // upper nibble contains W swap information
+ // when ColorOrderMap's upper nibble contains value >0 then swap information is used from it, otherwise global swap is used
+ for (unsigned i = 0; i < count(); i++) {
+ if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
+ return _mappings[i].colorOrder | ((_mappings[i].colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0));
}
}
return defaultColorOrder;
}
-uint32_t Bus::autoWhiteCalc(uint32_t c) {
+void Bus::calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) {
+ unsigned cct = 0; //0 - full warm white, 255 - full cold white
+ unsigned w = W(c);
+
+ if (_cct > -1) { // using RGB?
+ if (_cct >= 1900) cct = (_cct - 1900) >> 5; // convert K in relative format
+ else if (_cct < 256) cct = _cct; // already relative
+ } else {
+ cct = (approximateKelvinFromRGB(c) - 1900) >> 5; // convert K (from RGB value) to relative format
+ }
+
+ //0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
+ if (cct < _cctBlend) ww = 255;
+ else ww = ((255-cct) * 255) / (255 - _cctBlend);
+ if ((255-cct) < _cctBlend) cw = 255;
+ else cw = (cct * 255) / (255 - _cctBlend);
+
+ ww = (w * ww) / 255; //brightness scaling
+ cw = (w * cw) / 255;
+}
+
+uint32_t Bus::autoWhiteCalc(uint32_t c) const {
unsigned aWM = _autoWhiteMode;
if (_gAWM < AW_GLOBAL_DISABLED) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
@@ -95,7 +115,7 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) {
return RGBW32(r, g, b, w);
}
-uint8_t *Bus::allocData(size_t size) {
+uint8_t *Bus::allocateData(size_t size) {
if (_data) free(_data); // should not happen, but for safety
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
}
@@ -109,12 +129,12 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
, _milliAmpsMax(bc.milliAmpsMax)
, _colorOrderMap(com)
{
- if (!IS_DIGITAL(bc.type) || !bc.count) return;
- if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
+ if (!isDigital(bc.type) || !bc.count) return;
+ if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_frequencykHz = 0U;
_pins[0] = bc.pins[0];
- if (IS_2PIN(bc.type)) {
- if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
+ if (is2Pin(bc.type)) {
+ if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup();
return;
}
@@ -123,13 +143,16 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
}
_iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) return;
- if (bc.doubleBuffer && !allocData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
+ _hasRgb = hasRGB(bc.type);
+ _hasWhite = hasWhite(bc.type);
+ _hasCCT = hasCCT(bc.type);
+ if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) return;
//_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count;
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
_valid = (_busPtr != nullptr);
- DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], IS_2PIN(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
+ DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax);
}
//fine tune power estimation constants for your setup
@@ -263,7 +286,7 @@ void BusDigital::show() {
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, _bri);
}
-bool BusDigital::canShow() {
+bool BusDigital::canShow() const {
if (!_valid) return true;
return PolyBus::canShow(_busPtr, _iType);
}
@@ -319,7 +342,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
-uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
+uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
if (_data) {
size_t offset = pix * getNumberOfChannels();
@@ -349,9 +372,9 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
}
}
-uint8_t BusDigital::getPins(uint8_t* pinArray) {
- unsigned numPins = IS_2PIN(_type) ? 2 : 1;
- for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
+uint8_t BusDigital::getPins(uint8_t* pinArray) const {
+ unsigned numPins = is2Pin(_type) + 1;
+ if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
@@ -361,6 +384,32 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
_colorOrder = colorOrder;
}
+// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
+std::vector BusDigital::getLEDTypes() {
+ return {
+ {TYPE_WS2812_RGB, "D", PSTR("WS281x")},
+ {TYPE_SK6812_RGBW, "D", PSTR("SK6812/WS2814 RGBW")},
+ {TYPE_TM1814, "D", PSTR("TM1814")},
+ {TYPE_WS2811_400KHZ, "D", PSTR("400kHz")},
+ {TYPE_TM1829, "D", PSTR("TM1829")},
+ {TYPE_UCS8903, "D", PSTR("UCS8903")},
+ {TYPE_APA106, "D", PSTR("APA106/PL9823")},
+ {TYPE_TM1914, "D", PSTR("TM1914")},
+ {TYPE_FW1906, "D", PSTR("FW1906 GRBCW")},
+ {TYPE_UCS8904, "D", PSTR("UCS8904 RGBW")},
+ {TYPE_WS2805, "D", PSTR("WS2805 RGBCW")},
+ {TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
+ {TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
+ //{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
+ //{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
+ {TYPE_WS2801, "2P", PSTR("WS2801")},
+ {TYPE_APA102, "2P", PSTR("APA102")},
+ {TYPE_LPD8806, "2P", PSTR("LPD8806")},
+ {TYPE_LPD6803, "2P", PSTR("LPD6803")},
+ {TYPE_P9813, "2P", PSTR("PP9813")},
+ };
+}
+
void BusDigital::reinit() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins);
@@ -373,8 +422,8 @@ void BusDigital::cleanup() {
_valid = false;
_busPtr = nullptr;
if (_data != nullptr) freeData();
- pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
- pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
+ PinManager::deallocatePin(_pins[1], PinOwner::BusDigital);
+ PinManager::deallocatePin(_pins[0], PinOwner::BusDigital);
}
@@ -399,42 +448,54 @@ void BusDigital::cleanup() {
#define MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM
#else
// ESP32: 20 bit (but in reality we would never go beyond 16 bit as the frequency would be to low)
- #define MAX_BIT_WIDTH 20
+ #define MAX_BIT_WIDTH 14
#endif
#endif
BusPwm::BusPwm(BusConfig &bc)
-: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
+: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering
{
- if (!IS_PWM(bc.type)) return;
- unsigned numPins = NUM_PWM_PINS(bc.type);
+ if (!isPWM(bc.type)) return;
+ unsigned numPins = numPWMPins(bc.type);
+ [[maybe_unused]] const bool dithering = _needsRefresh;
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
// duty cycle resolution (_depth) can be extracted from this formula: CLOCK_FREQUENCY > _frequency * 2^_depth
for (_depth = MAX_BIT_WIDTH; _depth > 8; _depth--) if (((CLOCK_FREQUENCY/_frequency) >> _depth) > 0) break;
+ managed_pin_type pins[numPins];
+ for (unsigned i = 0; i < numPins; i++) pins[i] = {(int8_t)bc.pins[i], true};
+ if (!PinManager::allocateMultiplePins(pins, numPins, PinOwner::BusPwm)) return;
+
#ifdef ESP8266
analogWriteRange((1<<_depth)-1);
analogWriteFreq(_frequency);
#else
- _ledcStart = pinManager.allocateLedc(numPins);
+ // for 2 pin PWM CCT strip pinManager will make sure both LEDC channels are in the same speed group and sharing the same timer
+ _ledcStart = PinManager::allocateLedc(numPins);
if (_ledcStart == 255) { //no more free LEDC channels
- deallocatePins(); return;
+ PinManager::deallocateMultiplePins(pins, numPins, PinOwner::BusPwm);
+ return;
}
+ // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
+ if (dithering) _depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering)
#endif
for (unsigned i = 0; i < numPins; i++) {
- uint8_t currentPin = bc.pins[i];
- if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
- deallocatePins(); return;
- }
- _pins[i] = currentPin; //store only after allocatePin() succeeds
+ _pins[i] = bc.pins[i]; // store only after allocateMultiplePins() succeeded
#ifdef ESP8266
pinMode(_pins[i], OUTPUT);
#else
- ledcSetup(_ledcStart + i, _frequency, _depth);
- ledcAttachPin(_pins[i], _ledcStart + i);
+ unsigned channel = _ledcStart + i;
+ ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
+ ledcAttachPin(_pins[i], channel);
+ // LEDC timer reset credit @dedehai
+ uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup()
+ ledc_timer_rst((ledc_mode_t)group, (ledc_timer_t)timer); // reset timer so all timers are almost in sync (for phase shift)
#endif
}
+ _hasRgb = hasRGB(bc.type);
+ _hasWhite = hasWhite(bc.type);
+ _hasCCT = hasCCT(bc.type);
_data = _pwmdata; // avoid malloc() and use stack
_valid = true;
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
@@ -477,7 +538,7 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
}
//does no index check
-uint32_t BusPwm::getPixelColor(uint16_t pix) {
+uint32_t BusPwm::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
// TODO getting the reverse from CCT is involved (a quick approximation when CCT blending is ste to 0 implemented)
switch (_type) {
@@ -497,79 +558,98 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) {
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
}
-#ifndef ESP8266
-static const uint16_t cieLUT[256] = {
- 0, 2, 4, 5, 7, 9, 11, 13, 15, 16,
- 18, 20, 22, 24, 26, 27, 29, 31, 33, 35,
- 34, 36, 37, 39, 41, 43, 45, 47, 49, 52,
- 54, 56, 59, 61, 64, 67, 69, 72, 75, 78,
- 81, 84, 87, 90, 94, 97, 100, 104, 108, 111,
- 115, 119, 123, 127, 131, 136, 140, 144, 149, 154,
- 158, 163, 168, 173, 178, 183, 189, 194, 200, 205,
- 211, 217, 223, 229, 235, 241, 247, 254, 261, 267,
- 274, 281, 288, 295, 302, 310, 317, 325, 333, 341,
- 349, 357, 365, 373, 382, 391, 399, 408, 417, 426,
- 436, 445, 455, 464, 474, 484, 494, 505, 515, 526,
- 536, 547, 558, 569, 580, 592, 603, 615, 627, 639,
- 651, 663, 676, 689, 701, 714, 727, 741, 754, 768,
- 781, 795, 809, 824, 838, 853, 867, 882, 897, 913,
- 928, 943, 959, 975, 991, 1008, 1024, 1041, 1058, 1075,
- 1092, 1109, 1127, 1144, 1162, 1180, 1199, 1217, 1236, 1255,
- 1274, 1293, 1312, 1332, 1352, 1372, 1392, 1412, 1433, 1454,
- 1475, 1496, 1517, 1539, 1561, 1583, 1605, 1628, 1650, 1673,
- 1696, 1719, 1743, 1767, 1791, 1815, 1839, 1864, 1888, 1913,
- 1939, 1964, 1990, 2016, 2042, 2068, 2095, 2121, 2148, 2176,
- 2203, 2231, 2259, 2287, 2315, 2344, 2373, 2402, 2431, 2461,
- 2491, 2521, 2551, 2581, 2612, 2643, 2675, 2706, 2738, 2770,
- 2802, 2835, 2867, 2900, 2934, 2967, 3001, 3035, 3069, 3104,
- 3138, 3174, 3209, 3244, 3280, 3316, 3353, 3389, 3426, 3463,
- 3501, 3539, 3576, 3615, 3653, 3692, 3731, 3770, 3810, 3850,
- 3890, 3930, 3971, 4012, 4053, 4095
-};
-#endif
-
void BusPwm::show() {
if (!_valid) return;
- unsigned numPins = NUM_PWM_PINS(_type);
- unsigned maxBri = (1<<_depth) - 1;
- #ifdef ESP8266
- unsigned pwmBri = (unsigned)(roundf(powf((float)_bri / 255.0f, 1.7f) * (float)maxBri)); // using gamma 1.7 to extrapolate PWM duty cycle
- #else
- unsigned pwmBri = cieLUT[_bri] >> (12 - _depth); // use CIE LUT
- #endif
+ // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
+ // https://github.com/Aircoookie/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1)
+ const bool dithering = _needsRefresh; // avoid working with bitfield
+ const unsigned numPins = getPins();
+ const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
+ [[maybe_unused]] const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
+
+ // use CIE brightness formula (cubic) to fit (or approximate linearity of) human eye perceived brightness
+ // the formula is based on 12 bit resolution as there is no need for greater precision
+ // see: https://en.wikipedia.org/wiki/Lightness
+ unsigned pwmBri = (unsigned)_bri * 100; // enlarge to use integer math for linear response
+ if (pwmBri < 2040) {
+ // linear response for values [0-20]
+ pwmBri = ((pwmBri << 12) + 115043) / 230087; //adding '0.5' before division for correct rounding
+ } else {
+ // cubic response for values [21-255]
+ pwmBri += 4080;
+ float temp = (float)pwmBri / 29580.0f;
+ temp = temp * temp * temp * (float)maxBri;
+ pwmBri = (unsigned)temp; // pwmBri is in range [0-maxBri]
+ }
+
+ [[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri)
+ // we will be phase shifting every channel by previous pulse length (plus dead time if required)
+ // phase shifting is only mandatory when using H-bridge to drive reverse-polarity PWM CCT (2 wire) LED type
+ // CCT additive blending must be 0 (WW & CW will not overlap) otherwise signals *will* overlap
+ // for all other cases it will just try to "spread" the load on PSU
+ // Phase shifting requires that LEDC timers are synchronised (see setup()). For PWM CCT (and H-bridge) it is
+ // also mandatory that both channels use the same timer (pinManager takes care of that).
for (unsigned i = 0; i < numPins; i++) {
- unsigned scaled = (_data[i] * pwmBri) / 255;
- if (_reversed) scaled = maxBri - scaled;
+ unsigned duty = (_data[i] * pwmBri) / 255;
#ifdef ESP8266
- analogWrite(_pins[i], scaled);
+ if (_reversed) duty = maxBri - duty;
+ analogWrite(_pins[i], duty);
#else
- ledcWrite(_ledcStart + i, scaled);
+ int deadTime = 0;
+ if (_type == TYPE_ANALOG_2CH && Bus::getCCTBlend() == 0) {
+ // add dead time between signals (when using dithering, two full 8bit pulses are required)
+ deadTime = (1+dithering) << bitShift;
+ // we only need to take care of shortening the signal at (almost) full brightness otherwise pulses may overlap
+ if (_bri >= 254 && duty >= maxBri / 2 && duty < maxBri) duty -= deadTime << 1; // shorten duty of larger signal except if full on
+ if (_reversed) deadTime = -deadTime; // need to invert dead time to make phaseshift go the opposite way so low signals dont overlap
+ }
+ if (_reversed) duty = maxBri - duty;
+ unsigned channel = _ledcStart + i;
+ unsigned gr = channel/8; // high/low speed group
+ unsigned ch = channel%8; // group channel
+ // directly write to LEDC struct as there is no HAL exposed function for dithering
+ // duty has 20 bit resolution with 4 fractional bits (24 bits in total)
+ LEDC.channel_group[gr].channel[ch].duty.duty = duty << ((!dithering)*4); // lowest 4 bits are used for dithering, shift by 4 bits if not using dithering
+ LEDC.channel_group[gr].channel[ch].hpoint.hpoint = hPoint >> bitShift; // hPoint is at _depth resolution (needs shifting if dithering)
+ ledc_update_duty((ledc_mode_t)gr, (ledc_channel_t)ch);
+ hPoint += duty + deadTime; // offset to cascade the signals
+ if (hPoint >= maxBri) hPoint = 0; // offset it out of bounds, reset
#endif
}
}
-uint8_t BusPwm::getPins(uint8_t* pinArray) {
+uint8_t BusPwm::getPins(uint8_t* pinArray) const {
if (!_valid) return 0;
- unsigned numPins = NUM_PWM_PINS(_type);
- for (unsigned i = 0; i < numPins; i++) {
- pinArray[i] = _pins[i];
- }
+ unsigned numPins = numPWMPins(_type);
+ if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins;
}
+// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
+std::vector BusPwm::getLEDTypes() {
+ return {
+ {TYPE_ANALOG_1CH, "A", PSTR("PWM White")},
+ {TYPE_ANALOG_2CH, "AA", PSTR("PWM CCT")},
+ {TYPE_ANALOG_3CH, "AAA", PSTR("PWM RGB")},
+ {TYPE_ANALOG_4CH, "AAAA", PSTR("PWM RGBW")},
+ {TYPE_ANALOG_5CH, "AAAAA", PSTR("PWM RGB+CCT")},
+ //{TYPE_ANALOG_6CH, "AAAAAA", PSTR("PWM RGB+DCCT")}, // unimplementable ATM
+ };
+}
+
void BusPwm::deallocatePins() {
- unsigned numPins = NUM_PWM_PINS(_type);
+ unsigned numPins = getPins();
for (unsigned i = 0; i < numPins; i++) {
- pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
- if (!pinManager.isPinOk(_pins[i])) continue;
+ PinManager::deallocatePin(_pins[i], PinOwner::BusPwm);
+ if (!PinManager::isPinOk(_pins[i])) continue;
#ifdef ESP8266
digitalWrite(_pins[i], LOW); //turn off PWM interrupt
#else
- if (_ledcStart < 16) ledcDetachPin(_pins[i]);
+ if (_ledcStart < WLED_MAX_ANALOG_CHANNELS) ledcDetachPin(_pins[i]);
#endif
}
#ifdef ARDUINO_ARCH_ESP32
- pinManager.deallocateLedc(_ledcStart, numPins);
+ PinManager::deallocateLedc(_ledcStart, numPins);
#endif
}
@@ -578,14 +658,17 @@ BusOnOff::BusOnOff(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
, _onoffdata(0)
{
- if (bc.type != TYPE_ONOFF) return;
+ if (!Bus::isOnOff(bc.type)) return;
uint8_t currentPin = bc.pins[0];
- if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
+ if (!PinManager::allocatePin(currentPin, true, PinOwner::BusOnOff)) {
return;
}
_pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT);
+ _hasRgb = false;
+ _hasWhite = false;
+ _hasCCT = false;
_data = &_onoffdata; // avoid malloc() and use stack
_valid = true;
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
@@ -601,7 +684,7 @@ void BusOnOff::setPixelColor(uint16_t pix, uint32_t c) {
_data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0;
}
-uint32_t BusOnOff::getPixelColor(uint16_t pix) {
+uint32_t BusOnOff::getPixelColor(uint16_t pix) const {
if (!_valid) return 0;
return RGBW32(_data[0], _data[0], _data[0], _data[0]);
}
@@ -611,12 +694,18 @@ void BusOnOff::show() {
digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
}
-uint8_t BusOnOff::getPins(uint8_t* pinArray) {
+uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
if (!_valid) return 0;
- pinArray[0] = _pin;
+ if (pinArray) pinArray[0] = _pin;
return 1;
}
+// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
+std::vector BusOnOff::getLEDTypes() {
+ return {
+ {TYPE_ONOFF, "", PSTR("On/Off")},
+ };
+}
BusNetwork::BusNetwork(BusConfig &bc)
: Bus(bc.type, bc.start, bc.autoWhite, bc.count)
@@ -624,59 +713,71 @@ BusNetwork::BusNetwork(BusConfig &bc)
{
switch (bc.type) {
case TYPE_NET_ARTNET_RGB:
- _rgbw = false;
_UDPtype = 2;
break;
case TYPE_NET_ARTNET_RGBW:
- _rgbw = true;
_UDPtype = 2;
break;
case TYPE_NET_E131_RGB:
- _rgbw = false;
_UDPtype = 1;
break;
default: // TYPE_NET_DDP_RGB / TYPE_NET_DDP_RGBW
- _rgbw = bc.type == TYPE_NET_DDP_RGBW;
_UDPtype = 0;
break;
}
- _UDPchannels = _rgbw ? 4 : 3;
+ _hasRgb = hasRGB(bc.type);
+ _hasWhite = hasWhite(bc.type);
+ _hasCCT = false;
+ _UDPchannels = _hasWhite + 3;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
- _valid = (allocData(_len * _UDPchannels) != nullptr);
+ _valid = (allocateData(_len * _UDPchannels) != nullptr);
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
}
void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
- if (_rgbw) c = autoWhiteCalc(c);
+ if (_hasWhite) c = autoWhiteCalc(c);
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
unsigned offset = pix * _UDPchannels;
_data[offset] = R(c);
_data[offset+1] = G(c);
_data[offset+2] = B(c);
- if (_rgbw) _data[offset+3] = W(c);
+ if (_hasWhite) _data[offset+3] = W(c);
}
-uint32_t BusNetwork::getPixelColor(uint16_t pix) {
+uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
if (!_valid || pix >= _len) return 0;
unsigned offset = pix * _UDPchannels;
- return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (_rgbw ? _data[offset+3] : 0));
+ return RGBW32(_data[offset], _data[offset+1], _data[offset+2], (hasWhite() ? _data[offset+3] : 0));
}
void BusNetwork::show() {
if (!_valid || !canShow()) return;
_broadcastLock = true;
- realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
+ realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, hasWhite());
_broadcastLock = false;
}
-uint8_t BusNetwork::getPins(uint8_t* pinArray) {
- for (unsigned i = 0; i < 4; i++) {
- pinArray[i] = _client[i];
- }
+uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
+ if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
return 4;
}
+// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
+std::vector BusNetwork::getLEDTypes() {
+ return {
+ {TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, // should be "NNNN" to determine 4 "pin" fields
+ {TYPE_NET_ARTNET_RGB, "N", PSTR("Art-Net RGB (network)")},
+ {TYPE_NET_DDP_RGBW, "N", PSTR("DDP RGBW (network)")},
+ {TYPE_NET_ARTNET_RGBW, "N", PSTR("Art-Net RGBW (network)")},
+ // hypothetical extensions
+ //{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
+ //{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
+ //{TYPE_VIRTUAL_I2C_RGB, "VVV", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] and 2 additional values in _pin[1] & _pin[2]
+ //{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/Aircoookie/WLED/pull/4123)
+ };
+}
+
void BusNetwork::cleanup() {
_type = I_NONE;
_valid = false;
@@ -686,13 +787,13 @@ void BusNetwork::cleanup() {
//utility to get the approx. memory usage of a given BusConfig
uint32_t BusManager::memUsage(BusConfig &bc) {
- if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
+ if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS;
unsigned len = bc.count + bc.skipAmount;
unsigned channels = Bus::getNumberOfChannels(bc.type);
unsigned multiplier = 1;
- if (IS_DIGITAL(bc.type)) { // digital types
- if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs
+ if (Bus::isDigital(bc.type)) { // digital types
+ if (Bus::is16bit(bc.type)) len *= 2; // 16-bit LEDs
#ifdef ESP8266
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5;
@@ -712,11 +813,11 @@ uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned
int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
- if (IS_VIRTUAL(bc.type)) {
+ if (Bus::isVirtual(bc.type)) {
busses[numBusses] = new BusNetwork(bc);
- } else if (IS_DIGITAL(bc.type)) {
+ } else if (Bus::isDigital(bc.type)) {
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
- } else if (bc.type == TYPE_ONOFF) {
+ } else if (Bus::isOnOff(bc.type)) {
busses[numBusses] = new BusOnOff(bc);
} else {
busses[numBusses] = new BusPwm(bc);
@@ -724,7 +825,32 @@ int BusManager::add(BusConfig &bc) {
return numBusses++;
}
-void BusManager::useParallelOutput(void) {
+// credit @willmmiles
+static String LEDTypesToJson(const std::vector& types) {
+ String json;
+ for (const auto &type : types) {
+ // capabilities follows similar pattern as JSON API
+ int capabilities = Bus::hasRGB(type.id) | Bus::hasWhite(type.id)<<1 | Bus::hasCCT(type.id)<<2 | Bus::is16bit(type.id)<<4 | Bus::mustRefresh(type.id)<<5;
+ char str[256];
+ sprintf_P(str, PSTR("{i:%d,c:%d,t:\"%s\",n:\"%s\"},"), type.id, capabilities, type.type, type.name);
+ json += str;
+ }
+ return json;
+}
+
+// credit @willmmiles & @netmindz https://github.com/Aircoookie/WLED/pull/4056
+String BusManager::getLEDTypesJSONString() {
+ String json = "[";
+ json += LEDTypesToJson(BusDigital::getLEDTypes());
+ json += LEDTypesToJson(BusOnOff::getLEDTypes());
+ json += LEDTypesToJson(BusPwm::getLEDTypes());
+ json += LEDTypesToJson(BusNetwork::getLEDTypes());
+ //json += LEDTypesToJson(BusVirtual::getLEDTypes());
+ json.setCharAt(json.length()-1, ']'); // replace last comma with bracket
+ return json;
+}
+
+void BusManager::useParallelOutput() {
_parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods
PolyBus::setParallelI2S1Output();
}
@@ -762,7 +888,7 @@ void BusManager::esp32RMTInvertIdle() {
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels
rmt = u - _parallelOutputs;
#endif
- if (busses[u]->getLength()==0 || !IS_DIGITAL(busses[u]->getType()) || IS_2PIN(busses[u]->getType())) continue;
+ if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
//assumes that bus number to rmt channel mapping stays 1:1
rmt_channel_t ch = static_cast(rmt);
rmt_idle_level_t lvl;
@@ -778,10 +904,10 @@ void BusManager::esp32RMTInvertIdle() {
void BusManager::on() {
#ifdef ESP8266
//Fix for turning off onboard LED breaking bus
- if (pinManager.getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
+ if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
for (unsigned i = 0; i < numBusses; i++) {
uint8_t pins[2] = {255,255};
- if (IS_DIGITAL(busses[i]->getType()) && busses[i]->getPins(pins)) {
+ if (busses[i]->isDigital() && busses[i]->getPins(pins)) {
if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) {
BusDigital *bus = static_cast(busses[i]);
bus->reinit();
@@ -800,7 +926,7 @@ void BusManager::off() {
#ifdef ESP8266
// turn off built-in LED if strip is turned off
// this will break digital bus so will need to be re-initialised on On
- if (pinManager.getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
+ if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) {
for (unsigned i = 0; i < numBusses; i++) if (busses[i]->isOffRefreshRequired()) return;
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
@@ -852,7 +978,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
uint32_t BusManager::getPixelColor(uint16_t pix) {
for (unsigned i = 0; i < numBusses; i++) {
unsigned bstart = busses[i]->getStart();
- if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue;
+ if (!busses[i]->containsPixel(pix)) continue;
return busses[i]->getPixelColor(pix - bstart);
}
return 0;
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 5e516d2e16..e96b9de714 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -6,6 +6,7 @@
*/
#include "const.h"
+#include
//colors.cpp
uint16_t approximateKelvinFromRGB(uint32_t rgb);
@@ -21,89 +22,45 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb);
#define IC_INDEX_WS2812_2CH_3X(i) ((i)*2/3)
#define WS2812_2CH_3X_SPANS_2_ICS(i) ((i)&0x01) // every other LED zone is on two different ICs
-//temporary struct for passing bus configuration to bus
-struct BusConfig {
- uint8_t type;
- uint16_t count;
- uint16_t start;
- uint8_t colorOrder;
- bool reversed;
- uint8_t skipAmount;
- bool refreshReq;
- uint8_t autoWhite;
- uint8_t pins[5] = {255, 255, 255, 255, 255};
- uint16_t frequency;
- bool doubleBuffer;
- uint8_t milliAmpsPerLed;
- uint16_t milliAmpsMax;
-
- 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, bool dblBfr=false, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
- : count(len)
- , start(pstart)
- , colorOrder(pcolorOrder)
- , reversed(rev)
- , skipAmount(skip)
- , autoWhite(aw)
- , frequency(clock_kHz)
- , doubleBuffer(dblBfr)
- , milliAmpsPerLed(maPerLed)
- , milliAmpsMax(maMax)
- {
- 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 = 1;
- if (IS_VIRTUAL(type)) nPins = 4; //virtual network bus. 4 "pins" store IP address
- else if (IS_2PIN(type)) nPins = 2;
- else if (IS_PWM(type)) nPins = NUM_PWM_PINS(type);
- for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
- }
-
- //validates start and length and extends total if needed
- bool adjustBounds(uint16_t& total) {
- if (!count) count = 1;
- if (count > MAX_LEDS_PER_BUS) count = MAX_LEDS_PER_BUS;
- if (start >= MAX_LEDS) return false;
- //limit length of strip if it would exceed total permissible LEDs
- if (start + count > MAX_LEDS) count = MAX_LEDS - start;
- //extend total count accordingly
- if (start + count > total) total = start + count;
- return true;
- }
-};
-
+struct BusConfig; // forward declaration
// Defines an LED Strip and its color ordering.
-struct ColorOrderMapEntry {
+typedef struct {
uint16_t start;
uint16_t len;
uint8_t colorOrder;
-};
+} ColorOrderMapEntry;
struct ColorOrderMap {
- void add(uint16_t start, uint16_t len, uint8_t colorOrder);
+ bool add(uint16_t start, uint16_t len, uint8_t colorOrder);
- uint8_t count() const { return _count; }
+ inline uint8_t count() const { return _mappings.size(); }
+ inline void reserve(size_t num) { _mappings.reserve(num); }
void reset() {
- _count = 0;
- memset(_mappings, 0, sizeof(_mappings));
+ _mappings.clear();
+ _mappings.shrink_to_fit();
}
const ColorOrderMapEntry* get(uint8_t n) const {
- if (n > _count) {
- return nullptr;
- }
+ if (n >= count()) return nullptr;
return &(_mappings[n]);
}
- uint8_t getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const;
+ [[gnu::hot]] uint8_t getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const;
private:
- uint8_t _count;
- ColorOrderMapEntry _mappings[WLED_MAX_COLOR_ORDER_MAPPINGS];
+ std::vector _mappings;
};
+typedef struct {
+ uint8_t id;
+ const char *type;
+ const char *name;
+} LEDType;
+
+
//parent class of BusDigital, BusPwm, and BusNetwork
class Bus {
public:
@@ -123,99 +80,99 @@ class Bus {
virtual ~Bus() {} //throw the bus under the bus
virtual void show() = 0;
- virtual bool canShow() { return true; }
- virtual void setStatusPixel(uint32_t c) {}
+ virtual bool canShow() const { return true; }
+ virtual void setStatusPixel(uint32_t c) {}
virtual void setPixelColor(uint16_t pix, uint32_t c) = 0;
- virtual uint32_t getPixelColor(uint16_t pix) { return 0; }
- virtual void setBrightness(uint8_t b) { _bri = b; };
- virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
- virtual uint16_t getLength() { return isOk() ? _len : 0; }
- virtual void setColorOrder(uint8_t co) {}
- virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
- virtual uint8_t skippedLeds() { return 0; }
- virtual uint16_t getFrequency() { return 0U; }
- virtual uint16_t getLEDCurrent() { return 0; }
- virtual uint16_t getUsedCurrent() { return 0; }
- virtual uint16_t getMaxCurrent() { return 0; }
- virtual uint8_t getNumberOfChannels() { return hasWhite(_type) + 3*hasRGB(_type) + hasCCT(_type); }
- static inline uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
- inline void setReversed(bool reversed) { _reversed = reversed; }
- inline uint16_t getStart() { return _start; }
- inline void setStart(uint16_t start) { _start = start; }
- inline uint8_t getType() { return _type; }
- inline bool isOk() { return _valid; }
- inline bool isReversed() { return _reversed; }
- inline bool isOffRefreshRequired() { return _needsRefresh; }
- bool containsPixel(uint16_t pix) { return pix >= _start && pix < _start+_len; }
-
- virtual bool hasRGB(void) { return Bus::hasRGB(_type); }
- static bool hasRGB(uint8_t type) {
- if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF) return false;
- return true;
- }
- virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
- static bool hasWhite(uint8_t type) {
- if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
- type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 ||
- type == TYPE_FW1906 || type == TYPE_WS2805 || type == TYPE_SM16825) return true; // digital types with white channel
- if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
- if (type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW) return true; // network types with white channel
- return false;
+ virtual void setBrightness(uint8_t b) { _bri = b; };
+ virtual void setColorOrder(uint8_t co) {}
+ virtual uint32_t getPixelColor(uint16_t pix) const { return 0; }
+ virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; }
+ virtual uint16_t getLength() const { return isOk() ? _len : 0; }
+ virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
+ virtual uint8_t skippedLeds() const { return 0; }
+ virtual uint16_t getFrequency() const { return 0U; }
+ virtual uint16_t getLEDCurrent() const { return 0; }
+ virtual uint16_t getUsedCurrent() const { return 0; }
+ virtual uint16_t getMaxCurrent() const { return 0; }
+
+ inline bool hasRGB() const { return _hasRgb; }
+ inline bool hasWhite() const { return _hasWhite; }
+ inline bool hasCCT() const { return _hasCCT; }
+ inline bool isDigital() const { return isDigital(_type); }
+ inline bool is2Pin() const { return is2Pin(_type); }
+ inline bool isOnOff() const { return isOnOff(_type); }
+ inline bool isPWM() const { return isPWM(_type); }
+ inline bool isVirtual() const { return isVirtual(_type); }
+ inline bool is16bit() const { return is16bit(_type); }
+ inline bool mustRefresh() const { return mustRefresh(_type); }
+ inline void setReversed(bool reversed) { _reversed = reversed; }
+ inline void setStart(uint16_t start) { _start = start; }
+ inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
+ inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
+ inline uint8_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
+ inline uint16_t getStart() const { return _start; }
+ inline uint8_t getType() const { return _type; }
+ inline bool isOk() const { return _valid; }
+ inline bool isReversed() const { return _reversed; }
+ inline bool isOffRefreshRequired() const { return _needsRefresh; }
+ inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
+
+ static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
+ static constexpr uint8_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
+ static constexpr uint8_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
+ static constexpr bool hasRGB(uint8_t type) {
+ return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
}
- virtual bool hasCCT(void) { return Bus::hasCCT(_type); }
- static bool hasCCT(uint8_t type) {
- if (type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
- type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH ||
- type == TYPE_FW1906 || type == TYPE_WS2805 ||
- type == TYPE_SM16825) return true;
- return false;
+ static constexpr bool hasWhite(uint8_t type) {
+ return (type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
+ type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 ||
+ type == TYPE_FW1906 || type == TYPE_WS2805 || type == TYPE_SM16825 || // digital types with white channel
+ (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) || // analog types with white channel
+ type == TYPE_NET_DDP_RGBW || type == TYPE_NET_ARTNET_RGBW; // network types with white channel
}
- static inline int16_t getCCT() { return _cct; }
- static void setCCT(int16_t cct) {
- _cct = cct;
+ static constexpr bool hasCCT(uint8_t type) {
+ return type == TYPE_WS2812_2CH_X3 || type == TYPE_WS2812_WWA ||
+ type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH ||
+ type == TYPE_FW1906 || type == TYPE_WS2805 ||
+ type == TYPE_SM16825;
}
- static inline uint8_t getCCTBlend() { return _cctBlend; }
- static void setCCTBlend(uint8_t b) {
- if (b > 100) b = 100;
- _cctBlend = (b * 127) / 100;
+ static constexpr bool isTypeValid(uint8_t type) { return (type > 15 && type < 128); }
+ static constexpr bool isDigital(uint8_t type) { return (type >= TYPE_DIGITAL_MIN && type <= TYPE_DIGITAL_MAX) || is2Pin(type); }
+ static constexpr bool is2Pin(uint8_t type) { return (type >= TYPE_2PIN_MIN && type <= TYPE_2PIN_MAX); }
+ 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 is16bit(uint8_t type) { return type == TYPE_UCS8903 || type == TYPE_UCS8904 || type == TYPE_SM16825; }
+ static constexpr bool mustRefresh(uint8_t type) { return type == TYPE_TM1814; }
+ static constexpr int numPWMPins(uint8_t type) { return (type - 40); }
+
+ static inline int16_t getCCT() { return _cct; }
+ static inline void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
+ static inline uint8_t getGlobalAWMode() { return _gAWM; }
+ static inline void setCCT(int16_t cct) { _cct = cct; }
+ static inline uint8_t getCCTBlend() { return _cctBlend; }
+ static inline void setCCTBlend(uint8_t b) {
+ _cctBlend = (std::min((int)b,100) * 127) / 100;
//compile-time limiter for hardware that can't power both white channels at max
#ifdef WLED_MAX_CCT_BLEND
if (_cctBlend > WLED_MAX_CCT_BLEND) _cctBlend = WLED_MAX_CCT_BLEND;
#endif
}
- static void calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) {
- uint8_t cct = 0; //0 - full warm white, 255 - full cold white
- uint8_t w = byte(c >> 24);
-
- if (_cct > -1) {
- if (_cct >= 1900) cct = (_cct - 1900) >> 5;
- else if (_cct < 256) cct = _cct;
- } else {
- cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
- }
-
- //0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
- if (cct < _cctBlend) ww = 255;
- else ww = ((255-cct) * 255) / (255 - _cctBlend);
- if ((255-cct) < _cctBlend) cw = 255;
- else cw = (cct * 255) / (255 - _cctBlend);
-
- ww = (w * ww) / 255; //brightness scaling
- cw = (w * cw) / 255;
- }
- inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
- inline uint8_t getAutoWhiteMode() { return _autoWhiteMode; }
- inline static void setGlobalAWMode(uint8_t m) { if (m < 5) _gAWM = m; else _gAWM = AW_GLOBAL_DISABLED; }
- inline static uint8_t getGlobalAWMode() { return _gAWM; }
+ static void calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw);
protected:
uint8_t _type;
uint8_t _bri;
uint16_t _start;
uint16_t _len;
- bool _reversed;
- bool _valid;
- bool _needsRefresh;
+ //struct { //using bitfield struct adds abour 250 bytes to binary size
+ bool _reversed;// : 1;
+ bool _valid;// : 1;
+ bool _needsRefresh;// : 1;
+ bool _hasRgb;// : 1;
+ bool _hasWhite;// : 1;
+ bool _hasCCT;// : 1;
+ //} __attribute__ ((packed));
uint8_t _autoWhiteMode;
uint8_t *_data;
// global Auto White Calculation override
@@ -231,8 +188,8 @@ class Bus {
// 127 - additive CCT blending (CCT 127 => 100% warm, 100% cold)
static uint8_t _cctBlend;
- uint32_t autoWhiteCalc(uint32_t c);
- uint8_t *allocData(size_t size = 1);
+ uint32_t autoWhiteCalc(uint32_t c) const;
+ uint8_t *allocateData(size_t size = 1);
void freeData() { if (_data != nullptr) free(_data); _data = nullptr; }
};
@@ -243,23 +200,24 @@ class BusDigital : public Bus {
~BusDigital() { cleanup(); }
void show() override;
- bool canShow() override;
+ bool canShow() const override;
void setBrightness(uint8_t b) override;
void setStatusPixel(uint32_t c) override;
- void setPixelColor(uint16_t pix, uint32_t c) override;
+ [[gnu::hot]] void setPixelColor(uint16_t pix, uint32_t c) override;
void setColorOrder(uint8_t colorOrder) override;
- uint32_t getPixelColor(uint16_t pix) override;
- uint8_t getColorOrder() override { return _colorOrder; }
- uint8_t getPins(uint8_t* pinArray) override;
- uint8_t skippedLeds() override { return _skip; }
- uint16_t getFrequency() override { return _frequencykHz; }
- uint8_t estimateCurrentAndLimitBri();
- uint16_t getLEDCurrent() override { return _milliAmpsPerLed; }
- uint16_t getUsedCurrent() override { return _milliAmpsTotal; }
- uint16_t getMaxCurrent() override { return _milliAmpsMax; }
+ [[gnu::hot]] uint32_t getPixelColor(uint16_t pix) const override;
+ uint8_t getColorOrder() const override { return _colorOrder; }
+ uint8_t getPins(uint8_t* pinArray = nullptr) const override;
+ uint8_t skippedLeds() const override { return _skip; }
+ uint16_t getFrequency() const override { return _frequencykHz; }
+ uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
+ uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
+ uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
void reinit();
void cleanup();
+ static std::vector getLEDTypes();
+
private:
uint8_t _skip;
uint8_t _colorOrder;
@@ -273,7 +231,7 @@ class BusDigital : public Bus {
static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show()
- inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) {
+ inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) const {
if (restoreBri < 255) {
uint8_t* chan = (uint8_t*) &c;
for (uint_fast8_t i=0; i<4; i++) {
@@ -283,6 +241,8 @@ class BusDigital : public Bus {
}
return c;
}
+
+ uint8_t estimateCurrentAndLimitBri();
};
@@ -292,15 +252,17 @@ class BusPwm : public Bus {
~BusPwm() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c) override;
- uint32_t getPixelColor(uint16_t pix) override; //does no index check
- uint8_t getPins(uint8_t* pinArray) override;
- uint16_t getFrequency() override { return _frequency; }
+ uint32_t getPixelColor(uint16_t pix) const override; //does no index check
+ uint8_t getPins(uint8_t* pinArray = nullptr) const override;
+ uint16_t getFrequency() const override { return _frequency; }
void show() override;
void cleanup() { deallocatePins(); }
+ static std::vector getLEDTypes();
+
private:
- uint8_t _pins[5];
- uint8_t _pwmdata[5];
+ uint8_t _pins[OUTPUT_MAX_PINS];
+ uint8_t _pwmdata[OUTPUT_MAX_PINS];
#ifdef ARDUINO_ARCH_ESP32
uint8_t _ledcStart;
#endif
@@ -317,10 +279,12 @@ class BusOnOff : public Bus {
~BusOnOff() { cleanup(); }
void setPixelColor(uint16_t pix, uint32_t c) override;
- uint32_t getPixelColor(uint16_t pix) override;
- uint8_t getPins(uint8_t* pinArray) override;
+ uint32_t getPixelColor(uint16_t pix) const override;
+ uint8_t getPins(uint8_t* pinArray) const override;
void show() override;
- void cleanup() { pinManager.deallocatePin(_pin, PinOwner::BusOnOff); }
+ void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
+
+ static std::vector getLEDTypes();
private:
uint8_t _pin;
@@ -333,24 +297,71 @@ class BusNetwork : public Bus {
BusNetwork(BusConfig &bc);
~BusNetwork() { cleanup(); }
- bool hasRGB() override { return true; }
- bool hasWhite() override { return _rgbw; }
- bool canShow() override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
+ bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
void setPixelColor(uint16_t pix, uint32_t c) override;
- uint32_t getPixelColor(uint16_t pix) override;
- uint8_t getPins(uint8_t* pinArray) override;
+ uint32_t getPixelColor(uint16_t pix) const override;
+ uint8_t getPins(uint8_t* pinArray = nullptr) const override;
void show() override;
void cleanup();
+ static std::vector getLEDTypes();
+
private:
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
- bool _rgbw;
bool _broadcastLock;
};
+//temporary struct for passing bus configuration to bus
+struct BusConfig {
+ uint8_t type;
+ uint16_t count;
+ uint16_t start;
+ uint8_t colorOrder;
+ bool reversed;
+ uint8_t skipAmount;
+ bool refreshReq;
+ uint8_t autoWhite;
+ uint8_t pins[5] = {255, 255, 255, 255, 255};
+ uint16_t frequency;
+ bool doubleBuffer;
+ uint8_t milliAmpsPerLed;
+ uint16_t milliAmpsMax;
+
+ 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, bool dblBfr=false, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
+ : count(len)
+ , start(pstart)
+ , colorOrder(pcolorOrder)
+ , reversed(rev)
+ , skipAmount(skip)
+ , autoWhite(aw)
+ , frequency(clock_kHz)
+ , doubleBuffer(dblBfr)
+ , milliAmpsPerLed(maPerLed)
+ , milliAmpsMax(maMax)
+ {
+ 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];
+ }
+
+ //validates start and length and extends total if needed
+ bool adjustBounds(uint16_t& total) {
+ if (!count) count = 1;
+ if (count > MAX_LEDS_PER_BUS) count = MAX_LEDS_PER_BUS;
+ if (start >= MAX_LEDS) return false;
+ //limit length of strip if it would exceed total permissible LEDs
+ if (start + count > MAX_LEDS) count = MAX_LEDS - start;
+ //extend total count accordingly
+ if (start + count > total) total = start + count;
+ return true;
+ }
+};
+
+
class BusManager {
public:
BusManager() {};
@@ -358,27 +369,27 @@ class BusManager {
//utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc);
static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1);
- static uint16_t currentMilliamps(void) { return _milliAmpsUsed; }
- static uint16_t ablMilliampsMax(void) { return _milliAmpsMax; }
+ static uint16_t currentMilliamps() { return _milliAmpsUsed; }
+ static uint16_t ablMilliampsMax() { return _milliAmpsMax; }
static int add(BusConfig &bc);
- static void useParallelOutput(void); // workaround for inaccessible PolyBus
+ static void useParallelOutput(); // workaround for inaccessible PolyBus
//do not call this method from system context (network callback)
static void removeAll();
- static void on(void);
- static void off(void);
+ static void on();
+ static void off();
static void show();
static bool canAllShow();
static void setStatusPixel(uint32_t c);
- static void setPixelColor(uint16_t pix, uint32_t c);
+ [[gnu::hot]] static void setPixelColor(uint16_t pix, uint32_t c);
static void setBrightness(uint8_t b);
// for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K
// WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT()
static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false);
- static void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
+ static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;}
static uint32_t getPixelColor(uint16_t pix);
static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
@@ -386,10 +397,10 @@ class BusManager {
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
static uint16_t getTotalLength();
- static uint8_t getNumBusses() { return numBusses; }
+ static inline uint8_t getNumBusses() { return numBusses; }
+ static String getLEDTypesJSONString();
- static void updateColorOrderMap(const ColorOrderMap &com) { memcpy(&colorOrderMap, &com, sizeof(ColorOrderMap)); }
- static const ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
+ static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
private:
static uint8_t numBusses;
@@ -400,11 +411,11 @@ class BusManager {
static uint8_t _parallelOutputs;
#ifdef ESP32_DATA_IDLE_HIGH
- static void esp32RMTInvertIdle();
+ static void esp32RMTInvertIdle() ;
#endif
static uint8_t getNumVirtualBusses() {
int j = 0;
- for (int i=0; igetType() >= TYPE_NET_DDP_RGB && busses[i]->getType() < 96) j++;
+ for (int i=0; iisVirtual()) j++;
return j;
}
};
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index ae39adc146..84c32f46bc 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -766,47 +766,47 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
// RMT buses
- case I_32_RN_NEO_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_NEO_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_400_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_TM1_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_TM2_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_UCS_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_UCS_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_APA106_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_FW6_5: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_2805_5: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_TM1914_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_RN_SM16825_5: (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_NEO_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_NEO_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_400_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_TM1_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_TM2_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_UCS_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_UCS_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_APA106_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_2805_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_TM1914_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_SM16825_5: return (static_cast(busPtr))->CanShow(); break;
// I2S1 bus or paralell buses
#ifndef WLED_NO_I2S1_PIXELBUS
- case I_32_I1_NEO_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_NEO_4: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_400_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_TM1_4: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_TM2_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_UCS_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_UCS_4: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_APA106_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_FW6_5: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_2805_5: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
- case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast(busPtr))->CanShow(); else (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_NEO_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_NEO_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_400_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_TM1_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_TM2_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_UCS_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_UCS_4: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_APA106_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_FW6_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_2805_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_TM1914_3: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I1_SM16825_5: if (useParallelI2S) return (static_cast(busPtr))->CanShow(); else return (static_cast(busPtr))->CanShow(); break;
#endif
// I2S0 bus
#ifndef WLED_NO_I2S0_PIXELBUS
- case I_32_I0_NEO_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_NEO_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_400_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_TM1_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_TM2_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_UCS_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_UCS_4: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_APA106_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_FW6_5: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_2805_5: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_TM1914_3: (static_cast(busPtr))->CanShow(); break;
- case I_32_I0_SM16825_5: (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_NEO_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_NEO_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_400_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_TM1_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_TM2_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_UCS_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_UCS_4: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_APA106_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_2805_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_TM1914_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_I0_SM16825_5: return (static_cast(busPtr))->CanShow(); break;
#endif
#endif
case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break;
@@ -1314,8 +1314,8 @@ class PolyBus {
//gives back the internal type index (I_XX_XXX_X above) for the input
static uint8_t getI(uint8_t busType, uint8_t* pins, uint8_t num = 0) {
- if (!IS_DIGITAL(busType)) return I_NONE;
- if (IS_2PIN(busType)) { //SPI LED chips
+ if (!Bus::isDigital(busType)) return I_NONE;
+ if (Bus::is2Pin(busType)) { //SPI LED chips
bool isHSPI = false;
#ifdef ESP8266
if (pins[0] == P_8266_HS_MOSI && pins[1] == P_8266_HS_CLK) isHSPI = true;
diff --git a/wled00/button.cpp b/wled00/button.cpp
index 23d7b8a90f..4d6f954f60 100644
--- a/wled00/button.cpp
+++ b/wled00/button.cpp
@@ -125,7 +125,7 @@ void handleSwitch(uint8_t b)
{
// isButtonPressed() handles inverted/noninverted logic
if (buttonPressedBefore[b] != isButtonPressed(b)) {
- DEBUG_PRINT(F("Switch: State changed ")); DEBUG_PRINTLN(b);
+ DEBUG_PRINTF_P(PSTR("Switch: State changed %u\n"), b);
buttonPressedTime[b] = millis();
buttonPressedBefore[b] = !buttonPressedBefore[b];
}
@@ -133,15 +133,15 @@ void handleSwitch(uint8_t b)
if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
- DEBUG_PRINT(F("Switch: Activating ")); DEBUG_PRINTLN(b);
+ DEBUG_PRINTF_P(PSTR("Switch: Activating %u\n"), b);
if (!buttonPressedBefore[b]) { // on -> off
- DEBUG_PRINT(F("Switch: On -> Off ")); DEBUG_PRINTLN(b);
+ DEBUG_PRINTF_P(PSTR("Switch: On -> Off (%u)\n"), b);
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
}
} else { // off -> on
- DEBUG_PRINT(F("Switch: Off -> On ")); DEBUG_PRINTLN(b);
+ DEBUG_PRINTF_P(PSTR("Switch: Off -> On (%u)\n"), b);
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
@@ -173,7 +173,7 @@ void handleAnalog(uint8_t b)
static float filteredReading[WLED_MAX_BUTTONS] = {0.0f};
unsigned rawReading; // raw value from analogRead, scaled to 12bit
- DEBUG_PRINT(F("Analog: Reading button ")); DEBUG_PRINTLN(b);
+ DEBUG_PRINTF_P(PSTR("Analog: Reading button %u\n"), b);
#ifdef ESP8266
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
@@ -193,8 +193,8 @@ void handleAnalog(uint8_t b)
// remove noise & reduce frequency of UI updates
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
- DEBUG_PRINT(F("Analog: Raw = ")); DEBUG_PRINT(rawReading);
- DEBUG_PRINT(F(" Filtered = ")); DEBUG_PRINTLN(aRead);
+ DEBUG_PRINTF_P(PSTR("Analog: Raw = %u\n"), rawReading);
+ DEBUG_PRINTF_P(PSTR(" Filtered = %u\n"), aRead);
// Unomment the next lines if you still see flickering related to potentiometer
// This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?)
@@ -207,7 +207,7 @@ void handleAnalog(uint8_t b)
// if no macro for "short press" and "long press" is defined use brightness control
if (!macroButton[b] && !macroLongPress[b]) {
- DEBUG_PRINT(F("Analog: Action = ")); DEBUG_PRINTLN(macroDoublePress[b]);
+ DEBUG_PRINTF_P(PSTR("Analog: Action = %u\n"), macroDoublePress[b]);
// if "double press" macro defines which option to change
if (macroDoublePress[b] >= 250) {
// global brightness
@@ -215,6 +215,7 @@ void handleAnalog(uint8_t b)
briLast = bri;
bri = 0;
} else {
+ if (bri == 0) strip.restartRuntime();
bri = aRead;
}
} else if (macroDoublePress[b] == 249) {
@@ -267,7 +268,7 @@ void handleButton()
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
#endif
- if (usermods.handleButton(b)) continue; // did usermod handle buttons
+ if (UsermodManager::handleButton(b)) continue; // did usermod handle buttons
if (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer
if (now - lastAnalogRead > ANALOG_BTN_READ_CYCLE) {
@@ -308,7 +309,7 @@ void handleButton()
buttonLongPressed[b] = true;
}
- } else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
+ } else if (buttonPressedBefore[b]) { //released
long dur = now - buttonPressedTime[b];
// released after rising-edge short press action
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index 89076efab6..3f6cfbacb6 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -173,8 +173,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
for (JsonObject elm : ins) {
unsigned type = elm["type"] | TYPE_WS2812_RGB;
unsigned len = elm["len"] | DEFAULT_LED_COUNT;
- if (!IS_DIGITAL(type)) continue;
- if (!IS_2PIN(type)) {
+ if (!Bus::isDigital(type)) continue;
+ if (!Bus::is2Pin(type)) {
digitalCount++;
unsigned channels = Bus::getNumberOfChannels(type);
if (len > maxLedsOnBus) maxLedsOnBus = len;
@@ -215,7 +215,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t maPerLed = elm[F("ledma")] | LED_MILLIAMPS_DEFAULT;
uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
- if (IS_PWM(ledType) || IS_ONOFF(ledType) || IS_VIRTUAL(ledType)) { // analog and virtual
+ if (Bus::isPWM(ledType) || Bus::isOnOff(ledType) || Bus::isVirtual(ledType)) { // analog and virtual
maPerLed = 0;
maMax = 0;
}
@@ -244,17 +244,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// read color order map configuration
JsonArray hw_com = hw[F("com")];
if (!hw_com.isNull()) {
- ColorOrderMap com = {};
- unsigned s = 0;
+ BusManager::getColorOrderMap().reserve(std::min(hw_com.size(), (size_t)WLED_MAX_COLOR_ORDER_MAPPINGS));
for (JsonObject entry : hw_com) {
- if (s > WLED_MAX_COLOR_ORDER_MAPPINGS) break;
uint16_t start = entry["start"] | 0;
uint16_t len = entry["len"] | 0;
uint8_t colorOrder = (int)entry[F("order")];
- com.add(start, len, colorOrder);
- s++;
+ if (!BusManager::getColorOrderMap().add(start, len, colorOrder)) break;
}
- BusManager::updateColorOrderMap(com);
}
// read multiple button configuration
@@ -265,23 +261,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonArray hw_btn_ins = btn_obj["ins"];
if (!hw_btn_ins.isNull()) {
// deallocate existing button pins
- for (unsigned b = 0; b < WLED_MAX_BUTTONS; b++) pinManager.deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
+ for (unsigned b = 0; b < WLED_MAX_BUTTONS; b++) PinManager::deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
unsigned s = 0;
for (JsonObject btn : hw_btn_ins) {
CJSON(buttonType[s], btn["type"]);
int8_t pin = btn["pin"][0] | -1;
- if (pin > -1 && pinManager.allocatePin(pin, false, PinOwner::Button)) {
+ if (pin > -1 && PinManager::allocatePin(pin, false, PinOwner::Button)) {
btnPin[s] = pin;
#ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that analog button pin is a valid ADC gpio
if ((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) {
if (digitalPinToAnalogChannel(btnPin[s]) < 0) {
// not an ADC analog pin
- DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
- DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
- DEBUG_PRINTLN(F(" is not an analog pin!"));
+ DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), btnPin[s], s);
btnPin[s] = -1;
- pinManager.deallocatePin(pin,PinOwner::Button);
+ PinManager::deallocatePin(pin,PinOwner::Button);
} else {
analogReadResolution(12); // see #4040
}
@@ -290,9 +284,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
{
if (digitalPinToTouchChannel(btnPin[s]) < 0) {
// not a touch pin
- DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), btnPin[s], s);
+ DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not a touch pin!\n"), btnPin[s], s);
btnPin[s] = -1;
- pinManager.deallocatePin(pin,PinOwner::Button);
+ PinManager::deallocatePin(pin,PinOwner::Button);
}
//if touch pin, enable the touch interrupt on ESP32 S2 & S3
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state but need to attach an interrupt to do so
@@ -337,7 +331,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS) {
// relies upon only being called once with fromFS == true, which is currently true.
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
- if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !pinManager.allocatePin(btnPin[s], false, PinOwner::Button)) {
+ if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
btnPin[s] = -1;
buttonType[s] = BTN_TYPE_NONE;
}
@@ -364,8 +358,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
- pinManager.deallocatePin(irPin, PinOwner::IR);
- if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) {
+ PinManager::deallocatePin(irPin, PinOwner::IR);
+ if (PinManager::allocatePin(hw_ir_pin, false, PinOwner::IR)) {
irPin = hw_ir_pin;
} else {
irPin = -1;
@@ -380,8 +374,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
rlyOpenDrain = relay[F("odrain")] | rlyOpenDrain;
int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin > -2) {
- pinManager.deallocatePin(rlyPin, PinOwner::Relay);
- if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
+ PinManager::deallocatePin(rlyPin, PinOwner::Relay);
+ if (PinManager::allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
rlyPin = hw_relay_pin;
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
} else {
@@ -400,7 +394,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(i2c_sda, hw_if_i2c[0]);
CJSON(i2c_scl, hw_if_i2c[1]);
PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } };
- if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
+ if (i2c_scl >= 0 && i2c_sda >= 0 && PinManager::allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
#ifdef ESP32
if (!Wire.setPins(i2c_sda, i2c_scl)) { i2c_scl = i2c_sda = -1; } // this will fail if Wire is initialised (Wire.begin() called prior)
else Wire.begin();
@@ -416,7 +410,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(spi_sclk, hw_if_spi[1]);
CJSON(spi_miso, hw_if_spi[2]);
PinManagerPinType spi[3] = { { spi_mosi, true }, { spi_miso, true }, { spi_sclk, true } };
- if (spi_mosi >= 0 && spi_sclk >= 0 && pinManager.allocateMultiplePins(spi, 3, PinOwner::HW_SPI)) {
+ if (spi_mosi >= 0 && spi_sclk >= 0 && PinManager::allocateMultiplePins(spi, 3, PinOwner::HW_SPI)) {
#ifdef ESP32
SPI.begin(spi_sclk, spi_miso, spi_mosi); // SPI global uses VSPI on ESP32 and FSPI on C3, S3
#else
@@ -488,6 +482,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
CJSON(receiveNotificationColor, if_sync_recv["col"]);
CJSON(receiveNotificationEffects, if_sync_recv["fx"]);
+ CJSON(receiveNotificationPalette, if_sync_recv["pal"]);
CJSON(receiveGroups, if_sync_recv["grp"]);
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
CJSON(receiveSegmentBounds, if_sync_recv["sb"]);
@@ -669,7 +664,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
DEBUG_PRINTLN(F("Starting usermod config."));
JsonObject usermods_settings = doc["um"];
if (!usermods_settings.isNull()) {
- needsSave = !usermods.readFromConfig(usermods_settings);
+ needsSave = !UsermodManager::readFromConfig(usermods_settings);
}
if (fromFS) return needsSave;
@@ -705,7 +700,7 @@ void deserializeConfigFromFS() {
// save default values to /cfg.json
// call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving
JsonObject empty = JsonObject();
- usermods.readFromConfig(empty);
+ UsermodManager::readFromConfig(empty);
serializeConfig();
// init Ethernet (in case default type is set at compile time)
#ifdef WLED_USE_ETHERNET
@@ -975,6 +970,7 @@ void serializeConfig() {
if_sync_recv["bri"] = receiveNotificationBrightness;
if_sync_recv["col"] = receiveNotificationColor;
if_sync_recv["fx"] = receiveNotificationEffects;
+ if_sync_recv["pal"] = receiveNotificationPalette;
if_sync_recv["grp"] = receiveGroups;
if_sync_recv["seg"] = receiveSegmentOptions;
if_sync_recv["sb"] = receiveSegmentBounds;
@@ -1125,7 +1121,7 @@ void serializeConfig() {
#endif
JsonObject usermods_settings = root.createNestedObject("um");
- usermods.addToConfig(usermods_settings);
+ UsermodManager::addToConfig(usermods_settings);
File f = WLED_FS.open(FPSTR(s_cfg_json), "w");
if (f) serializeJson(root, f);
diff --git a/wled00/colors.cpp b/wled00/colors.cpp
index ebea7ea055..478a0a277d 100644
--- a/wled00/colors.cpp
+++ b/wled00/colors.cpp
@@ -37,6 +37,8 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16)
*/
uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
{
+ if (c1 == BLACK) return c2;
+ if (c2 == BLACK) return c1;
if (fast) {
uint8_t r = R(c1);
uint8_t g = G(c1);
@@ -68,17 +70,18 @@ uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
{
+ if (c1 == BLACK || amount + video == 0) return BLACK;
uint32_t scaledcolor; // color order is: W R G B from MSB to LSB
uint32_t r = R(c1);
uint32_t g = G(c1);
uint32_t b = B(c1);
uint32_t w = W(c1);
- uint32_t scale = amount + !video; // 32bit for faster calculation
+ uint32_t scale = amount; // 32bit for faster calculation
if (video) {
- scaledcolor = (((r * scale) >> 8) << 16) + ((r && scale) ? 1 : 0);
- scaledcolor |= (((g * scale) >> 8) << 8) + ((g && scale) ? 1 : 0);
- scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0);
- scaledcolor |= (((w * scale) >> 8) << 24) + ((w && scale) ? 1 : 0);
+ scaledcolor = (((r * scale) >> 8) + ((r && scale) ? 1 : 0)) << 16;
+ scaledcolor |= (((g * scale) >> 8) + ((g && scale) ? 1 : 0)) << 8;
+ scaledcolor |= ((b * scale) >> 8) + ((b && scale) ? 1 : 0);
+ scaledcolor |= (((w * scale) >> 8) + ((w && scale) ? 1 : 0)) << 24;
} else {
scaledcolor = ((r * scale) >> 8) << 16;
scaledcolor |= ((g * scale) >> 8) << 8;
@@ -195,7 +198,7 @@ CRGBPalette16 generateHarmonicRandomPalette(CRGBPalette16 &basepalette)
RGBpalettecolors[3]);
}
-CRGBPalette16 generateRandomPalette(void) //generate fully random palette
+CRGBPalette16 generateRandomPalette() //generate fully random palette
{
return CRGBPalette16(CHSV(random8(), random8(160, 255), random8(128, 255)),
CHSV(random8(), random8(160, 255), random8(128, 255)),
@@ -476,14 +479,14 @@ void NeoGammaWLEDMethod::calcGammaTable(float gamma)
}
}
-uint8_t NeoGammaWLEDMethod::Correct(uint8_t value)
+uint8_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct(uint8_t value)
{
if (!gammaCorrectCol) return value;
return gammaT[value];
}
// used for color gamma correction
-uint32_t NeoGammaWLEDMethod::Correct32(uint32_t color)
+uint32_t IRAM_ATTR_YN NeoGammaWLEDMethod::Correct32(uint32_t color)
{
if (!gammaCorrectCol) return color;
uint8_t w = W(color);
diff --git a/wled00/const.h b/wled00/const.h
index d5cd049d5e..e3bf0247c6 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -51,27 +51,28 @@
#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB
#define WLED_MIN_VIRTUAL_BUSSES 2
#else
+ #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_BUSSES 4 // will allow 2 digital & 2 analog RGB
+ #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_ANALOG_CHANNELS 6
+ //#define WLED_MAX_ANALOG_CHANNELS 6
#define WLED_MIN_VIRTUAL_BUSSES 3
#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_BUSSES 7 // will allow 5 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 5
- #define WLED_MAX_ANALOG_CHANNELS 8
+ //#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 3
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM
#define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 4
- #define WLED_MAX_ANALOG_CHANNELS 8
+ //#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 4
#else
// 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_DIGITAL_CHANNELS 17
- #define WLED_MAX_ANALOG_CHANNELS 10
+ //#define WLED_MAX_ANALOG_CHANNELS 16
#define WLED_MIN_VIRTUAL_BUSSES 4
#endif
#endif
@@ -202,6 +203,7 @@
#define USERMOD_ID_LD2410 52 //Usermod "usermod_ld2410.h"
#define USERMOD_ID_POV_DISPLAY 53 //Usermod "usermod_pov_display.h"
#define USERMOD_ID_PIXELS_DICE_TRAY 54 //Usermod "pixels_dice_tray.h"
+#define USERMOD_ID_POWER_MEASUREMENT 55 //Usermod "Power_measurement.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@@ -281,6 +283,7 @@
#define TYPE_NONE 0 //light is not configured
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
//Digital types (data pin only) (16-39)
+#define TYPE_DIGITAL_MIN 16 // first usable digital type
#define TYPE_WS2812_1CH 18 //white-only chips (1 channel per IC) (unused)
#define TYPE_WS2812_1CH_X3 19 //white-only chips (3 channels per IC)
#define TYPE_WS2812_2CH_X3 20 //CCT chips (1st IC controls WW + CW of 1st zone and CW of 2nd zone, 2nd IC controls WW of 2nd zone and WW + CW of 3rd zone)
@@ -298,26 +301,36 @@
#define TYPE_WS2805 32 //RGB + WW + CW
#define TYPE_TM1914 33 //RGB
#define TYPE_SM16825 34 //RGB + WW + CW
+#define TYPE_DIGITAL_MAX 39 // last usable digital type
//"Analog" types (40-47)
#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)
+#define TYPE_ANALOG_MIN 41 // first usable analog type
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
#define TYPE_ANALOG_2CH 42 //analog WW + CW
#define TYPE_ANALOG_3CH 43 //analog RGB
#define TYPE_ANALOG_4CH 44 //analog RGBW
#define TYPE_ANALOG_5CH 45 //analog RGB + WW + CW
+#define TYPE_ANALOG_6CH 46 //analog RGB + A + WW + CW
+#define TYPE_ANALOG_MAX 47 // last usable analog type
//Digital types (data + clock / SPI) (48-63)
+#define TYPE_2PIN_MIN 48
#define TYPE_WS2801 50
#define TYPE_APA102 51
#define TYPE_LPD8806 52
#define TYPE_P9813 53
#define TYPE_LPD6803 54
+#define TYPE_2PIN_MAX 63
//Network types (master broadcast) (80-95)
+#define TYPE_VIRTUAL_MIN 80
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused)
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused)
#define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus)
#define TYPE_NET_ARTNET_RGBW 89 //network ArtNet RGB bus (master broadcast bus, unused)
+#define TYPE_VIRTUAL_MAX 95
+/*
+// old macros that have been moved to Bus class
#define IS_TYPE_VALID(t) ((t) > 15 && (t) < 128)
#define IS_DIGITAL(t) (((t) > 15 && (t) < 40) || ((t) > 47 && (t) < 64)) //digital are 16-39 and 48-63
#define IS_2PIN(t) ((t) > 47 && (t) < 64)
@@ -326,6 +339,7 @@
#define IS_PWM(t) ((t) > 40 && (t) < 46) //does not include on/Off type
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
#define IS_VIRTUAL(t) ((t) >= 80 && (t) < 96) //this was a poor choice a better would be 96-111
+*/
//Color orders
#define COL_ORDER_GRB 0 //GRB(w),defaut
@@ -453,6 +467,9 @@
#define NTP_PACKET_SIZE 48 // size of NTP receive buffer
#define NTP_MIN_PACKET_SIZE 48 // min expected size - NTP v4 allows for "extended information" appended to the standard fields
+// Maximum number of pins per output. 5 for RGBCCT analog LEDs.
+#define OUTPUT_MAX_PINS 5
+
//maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses
#ifndef MAX_LEDS
#ifdef ESP8266
@@ -480,7 +497,7 @@
// string temp buffer (now stored in stack locally)
#ifdef ESP8266
-#define SETTINGS_STACK_BUF_SIZE 2048
+#define SETTINGS_STACK_BUF_SIZE 2560
#else
#define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS)
#endif
@@ -520,7 +537,11 @@
#ifdef ESP8266
#define WLED_PWM_FREQ 880 //PWM frequency proven as good for LEDs
#else
- #define WLED_PWM_FREQ 19531
+ #ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
+ #define WLED_PWM_FREQ 9765 // XTAL clock is 40MHz (this will allow 12 bit resolution)
+ #else
+ #define WLED_PWM_FREQ 19531 // APB clock is 80MHz
+ #endif
#endif
#endif
@@ -547,26 +568,19 @@
#define WLED_MAX_NODES 150
#endif
-//this is merely a default now and can be changed at runtime
-#ifndef LEDPIN
-#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) //|| (defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)) || defined(ARDUINO_ESP32_PICO)
- #define LEDPIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board
+// Defaults pins, type and counts to configure LED output
+#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3)
+ #ifdef WLED_ENABLE_DMX
+ #define DEFAULT_LED_PIN 1
+ #warning "Compiling with DMX. The default LED pin has been changed to pin 1."
+ #else
+ #define DEFAULT_LED_PIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board
+ #endif
#else
- #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit())
-#endif
-#endif
-
-#ifdef WLED_ENABLE_DMX
-#if (LEDPIN == 2)
- #undef LEDPIN
- #define LEDPIN 1
- #warning "Pin conflict compiling with DMX and LEDs on pin 2. The default LED pin has been changed to pin 1."
-#endif
-#endif
-
-#ifndef DEFAULT_LED_COUNT
- #define DEFAULT_LED_COUNT 30
+ #define DEFAULT_LED_PIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit())
#endif
+#define DEFAULT_LED_TYPE TYPE_WS2812_RGB
+#define DEFAULT_LED_COUNT 30
#define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates
@@ -628,4 +642,12 @@
#define HW_PIN_MISOSPI MISO
#endif
+// IRAM_ATTR for 8266 with 32Kb IRAM causes error: section `.text1' will not fit in region `iram1_0_seg'
+// this hack removes the IRAM flag for some 1D/2D functions - somewhat slower, but it solves problems with some older 8266 chips
+#ifdef WLED_SAVE_IRAM
+ #define IRAM_ATTR_YN
+#else
+ #define IRAM_ATTR_YN IRAM_ATTR
+#endif
+
#endif
diff --git a/wled00/data/common.js b/wled00/data/common.js
new file mode 100644
index 0000000000..9378ef07a8
--- /dev/null
+++ b/wled00/data/common.js
@@ -0,0 +1,118 @@
+var d=document;
+var loc = false, locip, locproto = "http:";
+
+function H(pg="") { window.open("https://kno.wled.ge/"+pg); }
+function GH() { window.open("https://github.com/Aircoookie/WLED"); }
+function gId(c) { return d.getElementById(c); } // getElementById
+function cE(e) { return d.createElement(e); } // createElement
+function gEBCN(c) { return d.getElementsByClassName(c); } // getElementsByClassName
+function gN(s) { return d.getElementsByName(s)[0]; } // getElementsByName
+function isE(o) { return Object.keys(o).length === 0; } // isEmpty
+function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); } // isObject
+function isN(n) { return !isNaN(parseFloat(n)) && isFinite(n); } // isNumber
+// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
+function isF(n) { return n === +n && n !== (n|0); } // isFloat
+function isI(n) { return n === +n && n === (n|0); } // isInteger
+function toggle(el) { gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide"); }
+function tooltip(cont=null) {
+ d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
+ element.addEventListener("mouseover", ()=>{
+ // save title
+ element.setAttribute("data-title", element.getAttribute("title"));
+ const tooltip = d.createElement("span");
+ tooltip.className = "tooltip";
+ tooltip.textContent = element.getAttribute("title");
+
+ // prevent default title popup
+ element.removeAttribute("title");
+
+ let { top, left, width } = element.getBoundingClientRect();
+
+ d.body.appendChild(tooltip);
+
+ const { offsetHeight, offsetWidth } = tooltip;
+
+ const offset = element.classList.contains("sliderwrap") ? 4 : 10;
+ top -= offsetHeight + offset;
+ left += (width - offsetWidth) / 2;
+
+ tooltip.style.top = top + "px";
+ tooltip.style.left = left + "px";
+ tooltip.classList.add("visible");
+ });
+
+ element.addEventListener("mouseout", ()=>{
+ d.querySelectorAll('.tooltip').forEach((tooltip)=>{
+ tooltip.classList.remove("visible");
+ d.body.removeChild(tooltip);
+ });
+ // restore title
+ element.setAttribute("title", element.getAttribute("data-title"));
+ });
+ });
+};
+// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
+function loadJS(FILE_URL, async = true, preGetV = undefined, postGetV = undefined) {
+ let scE = d.createElement("script");
+ scE.setAttribute("src", FILE_URL);
+ scE.setAttribute("type", "text/javascript");
+ scE.setAttribute("async", async);
+ d.body.appendChild(scE);
+ // success event
+ scE.addEventListener("load", () => {
+ //console.log("File loaded");
+ if (preGetV) preGetV();
+ GetV();
+ if (postGetV) postGetV();
+ });
+ // error event
+ scE.addEventListener("error", (ev) => {
+ console.log("Error on loading file", ev);
+ alert("Loading of configuration script failed.\nIncomplete page data!");
+ });
+}
+function getLoc() {
+ let l = window.location;
+ if (l.protocol == "file:") {
+ loc = true;
+ locip = localStorage.getItem('locIp');
+ if (!locip) {
+ locip = prompt("File Mode. Please enter WLED IP!");
+ localStorage.setItem('locIp', locip);
+ }
+ } else {
+ // detect reverse proxy
+ let path = l.pathname;
+ let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
+ if (paths.length > 1) paths.pop(); // remove subpage (or "settings")
+ if (paths.length > 0 && paths[paths.length-1]=="settings") paths.pop(); // remove "settings"
+ if (paths.length > 1) {
+ locproto = l.protocol;
+ loc = true;
+ locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
+ }
+ }
+}
+function getURL(path) { return (loc ? locproto + "//" + locip : "") + path; }
+function B() { window.open(getURL("/settings"),"_self"); }
+var timeout;
+function showToast(text, error = false) {
+ var x = gId("toast");
+ if (!x) return;
+ x.innerHTML = text;
+ x.className = error ? "error":"show";
+ clearTimeout(timeout);
+ x.style.animation = 'none';
+ timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
+}
+function uploadFile(fileObj, name) {
+ var req = new XMLHttpRequest();
+ req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
+ req.addEventListener('error', function(e){showToast(e.stack,true);});
+ req.open("POST", "/upload");
+ var formData = new FormData();
+ formData.append("data", fileObj.files[0], name);
+ req.send(formData);
+ fileObj.value = '';
+ return false;
+}
diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm
index a4b9135924..b58c0987ab 100644
--- a/wled00/data/cpal/cpal.htm
+++ b/wled00/data/cpal/cpal.htm
@@ -608,8 +608,8 @@
}
function generatePaletteDivs() {
- const palettesDiv = d.getElementById("palettes");
- const staticPalettesDiv = d.getElementById("staticPalettes");
+ const palettesDiv = gId("palettes");
+ const staticPalettesDiv = gId("staticPalettes");
const paletteDivs = Array.from(palettesDiv.children).filter((child) => {
return child.id.match(/^palette\d$/); // match only elements with id starting with "palette" followed by a single digit
});
@@ -620,25 +620,25 @@