diff --git a/platformio.ini b/platformio.ini
index 755e575d5..947578d98 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -143,8 +143,8 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.6.0
IRremoteESP8266 @ 2.8.2
- makuna/NeoPixelBus @ 2.7.5
- https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.1.0
+ makuna/NeoPixelBus @ 2.7.8
+ https://github.com/Aircoookie/ESPAsyncWebServer.git @ ^2.2.0
# for I2C interface
;Wire
# ESP-NOW library
diff --git a/requirements.txt b/requirements.txt
index 1c0644f98..d6f86e202 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -36,7 +36,7 @@ marshmallow==3.19.0
# via platformio
packaging==23.1
# via marshmallow
-platformio==6.1.6
+platformio==6.1.14
# via -r requirements.in
pyelftools==0.29
# via platformio
diff --git a/usermods/usermod_v2_four_line_display_ALT/readme.md b/usermods/usermod_v2_four_line_display_ALT/readme.md
index ea9f43610..a8f386dac 100644
--- a/usermods/usermod_v2_four_line_display_ALT/readme.md
+++ b/usermods/usermod_v2_four_line_display_ALT/readme.md
@@ -1,4 +1,4 @@
-# I2C 4 Line Display Usermod ALT
+# I2C/SPI 4 Line Display Usermod ALT
Thank you to the authors of the original version of these usermods. It would not have been possible without them!
"usermod_v2_four_line_display"
@@ -8,21 +8,20 @@ The core of these usermods are a copy of the originals. The main changes are to
The display usermod UI has been completely changed.
-The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
-Without the display it, functions identical to the original.
+The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
+Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
- *Brightness
- *Speed
- *Intensity
- *Palette
- *Effect
- *Main Color (only if display is used)
- *Saturation (only if display is used)
+* Brightness
+* Speed
+* Intensity
+* Palette
+* Effect
+* Main Color (only if display is used)
+* Saturation (only if display is used)
-Press and hold the encoder to display Network Info
- if AP is active, it will display AP, SSID and password
+Press and hold the encoder to display Network Info. If AP is active, it will display AP, SSID and password
Also shows if the timer is enabled
@@ -30,11 +29,47 @@ Also shows if the timer is enabled
## Installation
-Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions
-Then to activate this alternative usermod add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
+Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
+
+Copy the example `platformio_override.sample.ini` from the usermod_v2_rotary_encoder_ui_ALT folder to the root directory of your particular build and rename it to `platformio_override.ini`.
+
+This file should be placed in the same directory as `platformio.ini`.
+
+Then, to activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file,
or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file
+## Configuration
+
+These options are configurable in Config > Usermods
+
+### Usermod Setup
+
+* Global I2C GPIOs (HW) - Set the SDA and SCL pins
+
+### 4LineDisplay
+
+* `enabled` - enable/disable usermod
+* `type` - display type in numeric format
+ * 1 = I2C SSD1306 128x32
+ * 2 = I2C SH1106 128x32
+ * 3 = I2C SSD1306 128x64 (4 double-height lines)
+ * 4 = I2C SSD1305 128x32
+ * 5 = I2C SSD1305 128x64 (4 double-height lines)
+ * 6 = SPI SSD1306 128x32
+ * 7 = SPI SSD1306 128x64 (4 double-height lines)
+ * 8 = SPI SSD1309 128x64 (4 double-height lines)
+ * 9 = I2C SSD1309 128x64 (4 double-height lines)
+* `pin` - GPIO pins used for display; SPI displays can use SCK, MOSI, CS, DC & RST
+* `flip` - flip/rotate display 180°
+* `contrast` - set display contrast (higher contrast may reduce display lifetime)
+* `screenTimeOutSec` - screen saver time-out in seconds
+* `sleepMode` - enable/disable screen saver
+* `clockMode` - enable/disable clock display in screen saver mode
+* `showSeconds` - Show seconds on the clock display
+* `i2c-freq-kHz` - I2C clock frequency in kHz (may help reduce dropped frames, range: 400-3400)
+
+
### PlatformIO requirements
Note: the Four Line Display usermod requires the libraries `U8g2` and `Wire`.
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 82a5e1a81..24eb9794f 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
@@ -17,7 +17,7 @@
// for WLED.
//
// Dependencies
-// * This Usermod works best, by far, when coupled
+// * This Usermod works best, by far, when coupled
// with RotaryEncoderUI ALT Usermod.
//
// Make sure to enable NTP and set your time zone in WLED Config | Time.
@@ -89,7 +89,8 @@ typedef enum {
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
- SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
+ SSD1309_SPI64, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
+ SSD1309_64 // U8X8_SSD1309_128X64_NONAME0_HW_I2C
} DisplayType;
@@ -235,7 +236,7 @@ class FourLineDisplayUsermod : public Usermod {
void updateSpeed();
void updateIntensity();
void drawStatusIcons();
-
+
/**
* marks the position of the arrow showing
* the current setting being changed
@@ -246,8 +247,8 @@ class FourLineDisplayUsermod : public Usermod {
//Draw the arrow for the current setting being changed
void drawArrow();
- //Display the current effect or palette (desiredEntry)
- // on the appropriate line (row).
+ //Display the current effect or palette (desiredEntry)
+ // on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row);
/**
@@ -314,14 +315,14 @@ class FourLineDisplayUsermod : public Usermod {
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
- *
+ *
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
- *
+ *
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
- *
+ *
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void addToConfig(JsonObject& root) override;
@@ -329,7 +330,7 @@ class FourLineDisplayUsermod : public Usermod {
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
- *
+ *
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
@@ -494,7 +495,7 @@ void FourLineDisplayUsermod::showTime() {
}
if (knownHour != hourCurrent) {
// only update date when hour changes
- sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
+ sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
}
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
@@ -556,6 +557,7 @@ void FourLineDisplayUsermod::setup() {
case SSD1306_64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(); break;
case SSD1305: u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(); break;
case SSD1305_64: u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(); break;
+ case SSD1309_64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_HW_I2C(); break;
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
@@ -581,7 +583,7 @@ void FourLineDisplayUsermod::setup() {
// gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here
void FourLineDisplayUsermod::connected() {
- knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
+ knownSsid = WiFi.SSID(); //apActive ? apSSID : WiFi.SSID(); //apActive ? WiFi.softAPSSID() :
knownIp = Network.localIP(); //apActive ? IPAddress(4, 3, 2, 1) : Network.localIP();
networkOverlay(PSTR("NETWORK INFO"),7000);
}
@@ -637,7 +639,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
powerON = !powerON;
drawStatusIcons();
return;
- } else if (knownnightlight != nightlightActive) { //trigger moon icon
+ } else if (knownnightlight != nightlightActive) { //trigger moon icon
knownnightlight = nightlightActive;
drawStatusIcons();
if (knownnightlight) {
@@ -652,7 +654,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
return;
} else if (knownMode != effectCurrent || knownPalette != effectPalette) {
if (displayTurnedOff) needRedraw = true;
- else {
+ else {
if (knownPalette != effectPalette) { showCurrentEffectOrPalette(effectPalette, JSON_palette_names, 2); knownPalette = effectPalette; }
if (knownMode != effectCurrent) { showCurrentEffectOrPalette(effectCurrent, JSON_mode_names, 3); knownMode = effectCurrent; }
lastRedraw = now;
@@ -703,7 +705,7 @@ void FourLineDisplayUsermod::redraw(bool forceRedraw) {
drawArrow();
drawStatusIcons();
- // Second row
+ // Second row
updateBrightness();
updateSpeed();
updateIntensity();
@@ -805,8 +807,8 @@ void FourLineDisplayUsermod::drawArrow() {
lockRedraw = false;
}
-//Display the current effect or palette (desiredEntry)
-// on the appropriate line (row).
+//Display the current effect or palette (desiredEntry)
+// on the appropriate line (row).
void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
#if defined(ARDUINO_ARCH_ESP32) && defined(FLD_ESP32_USE_THREADS)
unsigned long now = millis();
@@ -857,7 +859,7 @@ void FourLineDisplayUsermod::showCurrentEffectOrPalette(int inputEffPal, const c
while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
smallBuffer1[smallChars1] = 0;
drawString(1, row*lineHeight, smallBuffer1, true);
- while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
+ while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
smallBuffer2[smallChars2] = 0;
drawString(1, row*lineHeight+1, smallBuffer2, true);
}
@@ -1150,7 +1152,7 @@ void FourLineDisplayUsermod::onUpdateBegin(bool init) {
xTaskCreatePinnedToCore(
[](void * par) { // Function to implement the task
// see https://www.freertos.org/vtaskdelayuntil.html
- const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2;
+ const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2;
TickType_t xLastWakeTime = xTaskGetTickCount();
for(;;) {
delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy.
@@ -1205,6 +1207,7 @@ void FourLineDisplayUsermod::appendConfigData() {
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);"));
@@ -1218,14 +1221,14 @@ void FourLineDisplayUsermod::appendConfigData() {
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop().
- *
+ *
* CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks!
- *
+ *
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
- *
+ *
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/
void FourLineDisplayUsermod::addToConfig(JsonObject& root) {
@@ -1252,7 +1255,7 @@ void FourLineDisplayUsermod::addToConfig(JsonObject& root) {
/*
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
- *
+ *
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
@@ -1346,6 +1349,10 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1305_128x64_adafruit, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
break;
+ case SSD1309_64:
+ u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_ssd13xx_fast_i2c, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
+ u8x8_SetPin_HW_I2C(u8x8->getU8x8(), U8X8_PIN_NONE, U8X8_PIN_NONE, U8X8_PIN_NONE);
+ break;
case SSD1306_SPI:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x32_univision, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini b/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini
new file mode 100644
index 000000000..6b32c71fb
--- /dev/null
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/platformio–override.sample.ini
@@ -0,0 +1,17 @@
+[platformio]
+default_envs = esp32dev
+
+[env:esp32dev]
+board = esp32dev
+platform = ${esp32.platform}
+build_unflags = ${common.build_unflags}
+build_flags =
+ ${common.build_flags_esp32}
+ -D USERMOD_FOUR_LINE_DISPLAY -D USE_ALT_DISPlAY
+ -D USERMOD_ROTARY_ENCODER_UI -D ENCODER_DT_PIN=18 -D ENCODER_CLK_PIN=5 -D ENCODER_SW_PIN=19
+upload_speed = 460800
+lib_deps =
+ ${esp32.lib_deps}
+ U8g2@~2.34.4
+ Wire
+
diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md b/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
index 516362380..10db879fb 100644
--- a/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/readme.md
@@ -8,18 +8,18 @@ The core of these usermods are a copy of the originals. The main changes are to
The display usermod UI has been completely changed.
-The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
+The changes made to the RotaryEncoder usermod were made to support the new UI in the display usermod.
Without the display, it functions identical to the original.
The original "usermod_v2_auto_save" will not work with the display just yet.
Press the encoder to cycle through the options:
- *Brightness
- *Speed
- *Intensity
- *Palette
- *Effect
- *Main Color (only if display is used)
- *Saturation (only if display is used)
+* Brightness
+* Speed
+* Intensity
+* Palette
+* Effect
+* Main Color (only if display is used)
+* Saturation (only if display is used)
Press and hold the encoder to display Network Info
if AP is active, it will display the AP, SSID and Password
@@ -30,10 +30,23 @@ Also shows if the timer is enabled.
## Installation
-Please refer to the original `usermod_v2_rotary_encoder_ui` readme for the main instructions.
-To activate this alternative usermod, add `#define USE_ALT_DISPlAY` to the `usermods_list.cpp` file,
-or add `-D USE_ALT_DISPlAY` to the original `platformio_override.ini.sample` file.
+Copy the example `platformio_override.sample.ini` to the root directory of your particular build and rename it to `platformio_override.ini`.
+To activate this alternative usermod, add `#define USE_ALT_DISPlAY` (NOTE: CASE SENSITIVE) to the `usermods_list.cpp` file, or add `-D USE_ALT_DISPlAY` to your `platformio_override.ini` file
+
+### Define Your Options
+
+* `USERMOD_ROTARY_ENCODER_UI` - define this to have this user mod included wled00\usermods_list.cpp
+* `USERMOD_FOUR_LINE_DISPLAY` - define this to have this the Four Line Display mod included wled00\usermods_list.cpp
+ also tells this usermod that the display is available
+ (see the Four Line Display usermod `readme.md` for more details)
+* `USE_ALT_DISPlAY` - Mandatory to use Four Line Display
+* `ENCODER_DT_PIN` - defaults to 18
+* `ENCODER_CLK_PIN` - defaults to 5
+* `ENCODER_SW_PIN` - defaults to 19
+* `USERMOD_ROTARY_ENCODER_GPIO` - GPIO functionality:
+ `INPUT_PULLUP` to use internal pull-up
+ `INPUT` to use pull-up on the PCB
### PlatformIO requirements
diff --git a/wled00/FX.h b/wled00/FX.h
index 3aa19bc35..58c192bc9 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -810,7 +810,7 @@ class WS2812FX { // 96 bytes
inline uint8_t getSegmentsNum(void) { return _segments.size(); } // returns currently present segments
inline uint8_t getCurrSegmentId(void) { return _segment_index; } // returns current segment index (only valid while strip.isServicing())
inline uint8_t getMainSegmentId(void) { return _mainSegment; } // returns main segment index
- inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count
+ inline uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
inline uint8_t getTargetFps() { return _targetFps; } // returns rough FPS value for las 2s interval
inline uint8_t getModeCount() { return _modeCount; } // returns number of registered modes/effects
diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp
index 42e98452f..3566755f0 100644
--- a/wled00/FX_fcn.cpp
+++ b/wled00/FX_fcn.cpp
@@ -1162,12 +1162,16 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen
+ int16_t oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based)
_virtualSegmentLength = seg.virtualLength(); //SEGLEN
_colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = gamma32(seg.currentColor(2));
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
- if (!cctFromRgb || correctWB) BusManager::setSegmentCCT(seg.currentBri(true), correctWB);
+ // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio
+ // when cctFromRgb is true we implicitly calculate WW and CW from RGB values
+ if (cctFromRgb) BusManager::setSegmentCCT(-1);
+ else BusManager::setSegmentCCT(seg.currentBri(true), correctWB);
// Effect blending
// When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous mode.
@@ -1190,6 +1194,7 @@ void WS2812FX::service() {
#endif
seg.call++;
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
+ BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments
}
seg.next_time = nowUp + delay;
@@ -1198,7 +1203,6 @@ void WS2812FX::service() {
_segment_index++;
}
_virtualSegmentLength = 0;
- BusManager::setSegmentCCT(-1);
_isServicing = false;
_triggered = false;
@@ -1390,11 +1394,7 @@ bool WS2812FX::hasCCTBus(void) {
for (size_t b = 0; b < BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
if (bus == nullptr || bus->getLength()==0) break;
- switch (bus->getType()) {
- case TYPE_ANALOG_5CH:
- case TYPE_ANALOG_2CH:
- return true;
- }
+ if (bus->hasCCT()) return true;
}
return false;
}
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 3ac12c04e..eeb9a15e4 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -11,7 +11,6 @@
//colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
-uint16_t approximateKelvinFromRGB(uint32_t rgb);
//udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, uint8_t bri=255, bool isRGBW=false);
@@ -122,7 +121,7 @@ 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::hasWhite(_type) + 3*Bus::hasRGB(_type)))) return; //warning: hardcoded channel count
+ if (bc.doubleBuffer && !allocData(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
@@ -205,13 +204,15 @@ void BusDigital::show() {
_milliAmpsTotal = 0;
if (!_valid) return;
+ uint8_t cctWW = 0, cctCW = 0;
uint8_t newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
- if (_data) { // use _buffering this causes ~20% FPS drop
- size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
+ if (_data) {
+ size_t channels = getNumberOfChannels();
+ int16_t oldCCT = _cct; // temporarily save bus CCT
for (size_t i=0; i<_len; i++) {
- size_t offset = i*channels;
+ size_t offset = i * channels;
uint8_t co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
uint32_t c;
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
@@ -221,17 +222,26 @@ void BusDigital::show() {
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
}
} else {
- c = RGBW32(_data[offset],_data[offset+1],_data[offset+2],(Bus::hasWhite(_type)?_data[offset+3]:0));
+ if (hasRGB()) c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
+ else c = RGBW32(0, 0, 0, _data[offset]);
+ }
+ if (hasCCT()) {
+ // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
+ // we need to extract and appy CCT value for each pixel individually even though all buses share the same _cct variable
+ // TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
+ _cct = _data[offset+channels-1];
+ Bus::calculateCCT(c, cctWW, cctCW);
}
uint16_t pix = i;
if (_reversed) pix = _len - pix -1;
pix += _skip;
- PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
+ PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
}
#if !defined(STATUSLED) || STATUSLED>=0
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
#endif
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
+ _cct = oldCCT;
} else {
if (newBri < _bri) {
uint16_t hwLen = _len;
@@ -239,7 +249,8 @@ void BusDigital::show() {
for (unsigned i = 0; i < hwLen; i++) {
// use 0 as color order, actual order does not matter here as we just update the channel values as-is
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, i, 0), _bri);
- PolyBus::setPixelColor(_busPtr, _iType, i, c, 0); // repaint all pixels with new brightness
+ if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW); // this will unfortunately corrupt (segment) CCT data on every bus
+ PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
}
}
}
@@ -278,17 +289,20 @@ void BusDigital::setStatusPixel(uint32_t c) {
void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid) return;
- if (Bus::hasWhite(_type)) c = autoWhiteCalc(c);
+ uint8_t cctWW = 0, cctCW = 0;
+ if (hasWhite()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
- if (_data) { // use _buffering this causes ~20% FPS drop
- size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
- size_t offset = pix*channels;
- if (Bus::hasRGB(_type)) {
+ if (_data) {
+ size_t offset = pix * getNumberOfChannels();
+ if (hasRGB()) {
_data[offset++] = R(c);
_data[offset++] = G(c);
_data[offset++] = B(c);
}
- if (Bus::hasWhite(_type)) _data[offset] = W(c);
+ if (hasWhite()) _data[offset++] = W(c);
+ // unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
+ // we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio)
+ if (hasCCT()) _data[offset] = _cct >= 1900 ? (_cct - 1900) >> 5 : (_cct < 0 ? 127 : _cct); // TODO: if _cct == -1 we simply ignore it
} else {
if (_reversed) pix = _len - pix -1;
pix += _skip;
@@ -303,21 +317,21 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
}
}
- PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
+ if (hasCCT()) Bus::calculateCCT(c, cctWW, cctCW);
+ PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
}
}
// returns original color if global buffering is enabled, else returns lossly restored color from bus
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
if (!_valid) return 0;
- if (_data) { // use _buffering this causes ~20% FPS drop
- size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
- size_t offset = pix*channels;
+ if (_data) {
+ size_t offset = pix * getNumberOfChannels();
uint32_t c;
- if (!Bus::hasRGB(_type)) {
+ if (!hasRGB()) {
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
} else {
- c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], Bus::hasWhite(_type) ? _data[offset+3] : 0);
+ c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
}
return c;
} else {
@@ -421,41 +435,25 @@ void BusPwm::setPixelColor(uint16_t pix, uint32_t c) {
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
- uint8_t cct = 0; //0 - full warm white, 255 - full cold white
- if (_cct > -1) {
- if (_cct >= 1900) cct = (_cct - 1900) >> 5;
- else if (_cct < 256) cct = _cct;
- } else {
- cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
- }
-
- uint8_t ww, cw;
- #ifdef WLED_USE_IC_CCT
- ww = w;
- cw = cct;
- #else
- //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;
- #endif
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
_data[0] = w;
break;
case TYPE_ANALOG_2CH: //warm white + cold white
- _data[1] = cw;
- _data[0] = ww;
+ #ifdef WLED_USE_IC_CCT
+ _data[0] = w;
+ _data[1] = cct;
+ #else
+ Bus::calculateCCT(c, _data[0], _data[1]);
+ #endif
break;
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
- _data[4] = cw;
- w = ww;
+ #ifdef WLED_USE_IC_CCT
+ _data[4] = cct;
+ #else
+ Bus::calculateCCT(c, w, _data[4]);
+ #endif
case TYPE_ANALOG_4CH: //RGBW
_data[3] = w;
case TYPE_ANALOG_3CH: //standard dumb RGB
@@ -660,25 +658,18 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
uint16_t len = bc.count + bc.skipAmount;
- uint16_t channels = 3;
+ uint16_t channels = Bus::getNumberOfChannels(bc.type);
uint16_t multiplier = 1;
if (IS_DIGITAL(bc.type)) { // digital types
if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs
#ifdef ESP8266
- if (bc.type > 28) channels = 4; //RGBW
if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem
multiplier = 5;
}
#else //ESP32 RMT uses double buffer, I2S uses 5x buffer
- if (bc.type > 28) channels = 4; //RGBW
multiplier = 2;
#endif
}
- if (IS_VIRTUAL(bc.type)) {
- switch (bc.type) {
- case TYPE_NET_DDP_RGBW: channels = 4; break;
- }
- }
return len * channels * multiplier; //RGB
}
@@ -740,7 +731,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
if (cct >= 0) {
//if white balance correction allowed, save as kelvin value instead of 0-255
if (allowWBCorrection) cct = 1900 + (cct << 5);
- } else cct = -1;
+ } else cct = -1; // will use kelvin approximation from RGB
Bus::setCCT(cct);
}
diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h
index 0b791adf3..d4facb33b 100644
--- a/wled00/bus_manager.h
+++ b/wled00/bus_manager.h
@@ -7,6 +7,9 @@
#include "const.h"
+//colors.cpp
+uint16_t approximateKelvinFromRGB(uint32_t rgb);
+
#define GET_BIT(var,bit) (((var)>>(bit))&0x01)
#define SET_BIT(var,bit) ((var)|=(uint16_t)(0x0001<<(bit)))
#define UNSET_BIT(var,bit) ((var)&=(~(uint16_t)(0x0001<<(bit))))
@@ -138,6 +141,8 @@ class Bus {
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; }
@@ -154,18 +159,20 @@ class Bus {
}
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) return true; // digital types with white channel
+ if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) ||
+ type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904 || type == TYPE_FW1906) 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) return true; // network 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 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) return true;
+ type == TYPE_ANALOG_2CH || type == TYPE_ANALOG_5CH || type == TYPE_FW1906) return true;
return false;
}
- static void setCCT(uint16_t cct) {
+ static int16_t getCCT() { return _cct; }
+ static void setCCT(int16_t cct) {
_cct = cct;
}
static void setCCTBlend(uint8_t b) {
@@ -176,6 +183,26 @@ class Bus {
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; }
@@ -191,8 +218,17 @@ class Bus {
bool _needsRefresh;
uint8_t _autoWhiteMode;
uint8_t *_data;
+ // global Auto White Calculation override
static uint8_t _gAWM;
+ // _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()):
+ // -1 means to extract approximate CCT value in K from RGB (in calcualteCCT())
+ // [0,255] is the exact CCT value where 0 means warm and 255 cold
+ // [1900,10060] only for color correction expressed in K (colorBalanceFromKelvin())
static int16_t _cct;
+ // _cctBlend determines WW/CW blending:
+ // 0 - linear (CCT 127 => 50% warm, 50% cold)
+ // 63 - semi additive/nonlinear (CCT 127 => 66% warm, 66% cold)
+ // 127 - additive CCT blending (CCT 127 => 100% warm, 100% cold)
static uint8_t _cctBlend;
uint32_t autoWhiteCalc(uint32_t c);
@@ -334,9 +370,12 @@ class BusManager {
static void setStatusPixel(uint32_t c);
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 uint32_t getPixelColor(uint16_t pix);
+ static inline int16_t getSegmentCCT() { return Bus::getCCT(); }
static Bus* getBus(uint8_t busNr);
diff --git a/wled00/bus_wrapper.h b/wled00/bus_wrapper.h
index c63e055a8..fafe3a460 100644
--- a/wled00/bus_wrapper.h
+++ b/wled00/bus_wrapper.h
@@ -2,6 +2,7 @@
#define BusWrapper_h
#include "NeoPixelBusLg.h"
+#include "bus_manager.h"
// temporary - these defines should actually be set in platformio.ini
// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
@@ -63,6 +64,11 @@
#define I_8266_U1_UCS_4 54
#define I_8266_DM_UCS_4 55
#define I_8266_BB_UCS_4 56
+//FW1906 GRBCW
+#define I_8266_U0_FW6_5 66
+#define I_8266_U1_FW6_5 67
+#define I_8266_DM_FW6_5 68
+#define I_8266_BB_FW6_5 69
//ESP8266 APA106
#define I_8266_U0_APA106_3 81
#define I_8266_U1_APA106_3 82
@@ -104,12 +110,17 @@
#define I_32_RN_UCS_4 60
#define I_32_I0_UCS_4 61
#define I_32_I1_UCS_4 62
+//FW1906 GRBCW
+#define I_32_RN_FW6_5 63
+#define I_32_I0_FW6_5 64
+#define I_32_I1_FW6_5 65
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
#define I_32_RN_APA106_3 85
#define I_32_I0_APA106_3 86
#define I_32_I1_APA106_3 87
#define I_32_BB_APA106_3 88 // bitbangging on ESP32 not recommended
+
//APA102
#define I_HS_DOT_3 39 //hardware SPI
#define I_SS_DOT_3 40 //soft SPI
@@ -176,6 +187,11 @@
#define B_8266_U1_APA106_3 NeoPixelBusLg //3 chan, esp8266, gpio2
#define B_8266_DM_APA106_3 NeoPixelBusLg //3 chan, esp8266, gpio3
#define B_8266_BB_APA106_3 NeoPixelBusLg //3 chan, esp8266, bb (any pin but 16)
+//FW1906 GRBCW
+#define B_8266_U0_FW6_5 NeoPixelBusLg //esp8266, gpio1
+#define B_8266_U1_FW6_5 NeoPixelBusLg //esp8266, gpio2
+#define B_8266_DM_FW6_5 NeoPixelBusLg //esp8266, gpio3
+#define B_8266_BB_FW6_5 NeoPixelBusLg //esp8266, bb
#endif
/*** ESP32 Neopixel methods ***/
@@ -251,6 +267,14 @@
#define B_32_I1_APA106_3 NeoPixelBusLg
#endif
//#define B_32_BB_APA106_3 NeoPixelBusLg // NeoEsp8266BitBang800KbpsMethod
+//FW1906 GRBCW
+#define B_32_RN_FW6_5 NeoPixelBusLg
+#ifndef WLED_NO_I2S0_PIXELBUS
+#define B_32_I0_FW6_5 NeoPixelBusLg
+#endif
+#ifndef WLED_NO_I2S1_PIXELBUS
+#define B_32_I1_FW6_5 NeoPixelBusLg
+#endif
#endif
@@ -290,6 +314,7 @@
//handles pointer type conversion for all possible bus types
class PolyBus {
public:
+
// initialize SPI bus speed for DotStar methods
template
static void beginDotStar(void* busPtr, int8_t sck, int8_t miso, int8_t mosi, int8_t ss, uint16_t clock_kHz = 0U) {
@@ -353,6 +378,10 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast(busPtr))->Begin(); break;
case I_8266_DM_APA106_3: (static_cast(busPtr))->Begin(); break;
case I_8266_BB_APA106_3: (static_cast(busPtr))->Begin(); break;
+ case I_8266_U0_FW6_5: (static_cast(busPtr))->Begin(); break;
+ case I_8266_U1_FW6_5: (static_cast(busPtr))->Begin(); break;
+ case I_8266_DM_FW6_5: (static_cast(busPtr))->Begin(); break;
+ case I_8266_BB_FW6_5: (static_cast(busPtr))->Begin(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast(busPtr))->Begin(); break;
@@ -404,6 +433,14 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_UCS_4: (static_cast(busPtr))->Begin(); break;
#endif
+ case I_32_RN_FW6_5: (static_cast(busPtr))->Begin(); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: (static_cast(busPtr))->Begin(); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: (static_cast(busPtr))->Begin(); break;
+ #endif
+
// case I_32_BB_UCS_4: (static_cast(busPtr))->Begin(); break;
case I_32_RN_APA106_3: (static_cast(busPtr))->Begin(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
@@ -465,6 +502,10 @@ class PolyBus {
case I_8266_U1_APA106_3: busPtr = new B_8266_U1_APA106_3(len, pins[0]); break;
case I_8266_DM_APA106_3: busPtr = new B_8266_DM_APA106_3(len, pins[0]); break;
case I_8266_BB_APA106_3: busPtr = new B_8266_BB_APA106_3(len, pins[0]); break;
+ case I_8266_U0_FW6_5: busPtr = new B_8266_U0_FW6_5(len, pins[0]); break;
+ case I_8266_U1_FW6_5: busPtr = new B_8266_U1_FW6_5(len, pins[0]); break;
+ case I_8266_DM_FW6_5: busPtr = new B_8266_DM_FW6_5(len, pins[0]); break;
+ case I_8266_BB_FW6_5: busPtr = new B_8266_BB_FW6_5(len, pins[0]); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@@ -525,6 +566,13 @@ class PolyBus {
case I_32_I1_APA106_3: busPtr = new B_32_I1_APA106_3(len, pins[0]); break;
#endif
// case I_32_BB_APA106_3: busPtr = new B_32_BB_APA106_3(len, pins[0], (NeoBusChannel)channel); break;
+ case I_32_RN_FW6_5: busPtr = new B_32_RN_FW6_5(len, pins[0], (NeoBusChannel)channel); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: busPtr = new B_32_I0_FW6_5(len, pins[0]); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: busPtr = new B_32_I1_FW6_5(len, pins[0]); break;
+ #endif
#endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
@@ -578,6 +626,10 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast(busPtr))->Show(consistent); break;
case I_8266_DM_APA106_3: (static_cast(busPtr))->Show(consistent); break;
case I_8266_BB_APA106_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U0_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_U1_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_DM_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ case I_8266_BB_FW6_5: (static_cast(busPtr))->Show(consistent); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast(busPtr))->Show(consistent); break;
@@ -638,6 +690,13 @@ class PolyBus {
case I_32_I1_APA106_3: (static_cast(busPtr))->Show(consistent); break;
#endif
// case I_32_BB_APA106_3: (static_cast(busPtr))->Show(consistent); break;
+ case I_32_RN_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: (static_cast(busPtr))->Show(consistent); break;
+ #endif
#endif
case I_HS_DOT_3: (static_cast(busPtr))->Show(consistent); break;
case I_SS_DOT_3: (static_cast(busPtr))->Show(consistent); break;
@@ -687,6 +746,10 @@ class PolyBus {
case I_8266_U1_APA106_3: return (static_cast(busPtr))->CanShow(); break;
case I_8266_DM_APA106_3: return (static_cast(busPtr))->CanShow(); break;
case I_8266_BB_APA106_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_8266_U0_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_8266_U1_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_8266_DM_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ case I_8266_BB_FW6_5: return (static_cast(busPtr))->CanShow(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast(busPtr))->CanShow(); break;
@@ -747,6 +810,13 @@ class PolyBus {
case I_32_I1_APA106_3: return (static_cast(busPtr))->CanShow(); break;
#endif
// case I_32_BB_APA106_3: return (static_cast(busPtr))->CanShow(); break;
+ case I_32_RN_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: return (static_cast(busPtr))->CanShow(); break;
+ #endif
#endif
case I_HS_DOT_3: return (static_cast(busPtr))->CanShow(); break;
case I_SS_DOT_3: return (static_cast(busPtr))->CanShow(); break;
@@ -762,12 +832,13 @@ class PolyBus {
return true;
}
- static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co) {
+ static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) {
uint8_t r = c >> 16;
uint8_t g = c >> 8;
uint8_t b = c >> 0;
uint8_t w = c >> 24;
RgbwColor col;
+ uint8_t cctWW = wwcw & 0xFF, cctCW = (wwcw>>8) & 0xFF;
// reorder channels to selected order
switch (co & 0x0F) {
@@ -821,6 +892,10 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
+ case I_8266_U0_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ case I_8266_U1_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ case I_8266_DM_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ case I_8266_BB_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@@ -881,6 +956,13 @@ class PolyBus {
case I_32_I1_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
// case I_32_BB_APA106_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
+ case I_32_RN_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: (static_cast(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
+ #endif
#endif
case I_HS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_DOT_3: (static_cast(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@@ -931,6 +1013,10 @@ class PolyBus {
case I_8266_U1_APA106_3: (static_cast(busPtr))->SetLuminance(b); break;
case I_8266_DM_APA106_3: (static_cast(busPtr))->SetLuminance(b); break;
case I_8266_BB_APA106_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U0_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_U1_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_DM_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_8266_BB_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast(busPtr))->SetLuminance(b); break;
@@ -991,6 +1077,14 @@ class PolyBus {
case I_32_I1_APA106_3: (static_cast(busPtr))->SetLuminance(b); break;
#endif
// case I_32_BB_APA106_3: (static_cast(busPtr))->SetLuminance(b); break;
+ case I_32_RN_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: (static_cast(busPtr))->SetLuminance(b); break;
+ #endif
+
#endif
case I_HS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast(busPtr))->SetLuminance(b); break;
@@ -1042,6 +1136,10 @@ class PolyBus {
case I_8266_U1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
+ case I_8266_U0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ case I_8266_U1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ case I_8266_DM_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ case I_8266_BB_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
@@ -1102,6 +1200,13 @@ class PolyBus {
case I_32_I1_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
#endif
// case I_32_BB_APA106_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
+ case I_32_RN_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: { RgbwwColor c = (static_cast(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
+ #endif
#endif
case I_HS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
case I_SS_DOT_3: col = (static_cast(busPtr))->GetPixelColor(pix); break;
@@ -1171,6 +1276,10 @@ class PolyBus {
case I_8266_U1_APA106_3: delete (static_cast(busPtr)); break;
case I_8266_DM_APA106_3: delete (static_cast(busPtr)); break;
case I_8266_BB_APA106_3: delete (static_cast(busPtr)); break;
+ case I_8266_U0_FW6_5: delete (static_cast(busPtr)); break;
+ case I_8266_U1_FW6_5: delete (static_cast(busPtr)); break;
+ case I_8266_DM_FW6_5: delete (static_cast(busPtr)); break;
+ case I_8266_BB_FW6_5: delete (static_cast(busPtr)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast(busPtr)); break;
@@ -1231,6 +1340,13 @@ class PolyBus {
case I_32_I1_APA106_3: delete (static_cast(busPtr)); break;
#endif
// case I_32_BB_APA106_3: delete (static_cast(busPtr)); break;
+ case I_32_RN_FW6_5: delete (static_cast(busPtr)); break;
+ #ifndef WLED_NO_I2S0_PIXELBUS
+ case I_32_I0_FW6_5: delete (static_cast(busPtr)); break;
+ #endif
+ #ifndef WLED_NO_I2S1_PIXELBUS
+ case I_32_I1_FW6_5: delete (static_cast(busPtr)); break;
+ #endif
#endif
case I_HS_DOT_3: delete (static_cast(busPtr)); break;
case I_SS_DOT_3: delete (static_cast(busPtr)); break;
@@ -1292,6 +1408,8 @@ class PolyBus {
return I_8266_U0_UCS_4 + offset;
case TYPE_APA106:
return I_8266_U0_APA106_3 + offset;
+ case TYPE_FW1906:
+ return I_8266_U0_FW6_5 + offset;
}
#else //ESP32
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
@@ -1332,11 +1450,12 @@ class PolyBus {
return I_32_RN_UCS_4 + offset;
case TYPE_APA106:
return I_32_RN_APA106_3 + offset;
+ case TYPE_FW1906:
+ return I_32_RN_FW6_5 + offset;
}
#endif
}
return I_NONE;
}
};
-
-#endif
\ No newline at end of file
+#endif
diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp
index e51b666e4..6ccf8aa44 100644
--- a/wled00/cfg.cpp
+++ b/wled00/cfg.cpp
@@ -632,12 +632,12 @@ static const char s_cfg_json[] PROGMEM = "/cfg.json";
void deserializeConfigFromFS() {
bool success = deserializeConfigSec();
+ #ifdef WLED_ADD_EEPROM_SUPPORT
if (!success) { //if file does not exist, try reading from EEPROM
- #ifdef WLED_ADD_EEPROM_SUPPORT
deEEPSettings();
return;
- #endif
}
+ #endif
if (!requestJSONBufferLock(1)) return;
diff --git a/wled00/const.h b/wled00/const.h
index 611230ea2..73873d041 100644
--- a/wled00/const.h
+++ b/wled00/const.h
@@ -270,6 +270,7 @@
#define TYPE_TM1829 25
#define TYPE_UCS8903 26
#define TYPE_APA106 27
+#define TYPE_FW1906 28 //RGB + CW + WW + unused channel (6 channels per IC)
#define TYPE_UCS8904 29 //first RGBW digital type (hardcoded in busmanager.cpp, memUsage())
#define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31
@@ -374,6 +375,7 @@
//Playlist option byte
#define PL_OPTION_SHUFFLE 0x01
+#define PL_OPTION_RESTORE 0x02
// Segment capability byte
#define SEG_CAPABILITY_RGB 0x01
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 36c3eb1b9..4ad2044ad 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -1984,7 +1984,7 @@ function makeP(i,pl)