diff --git a/.github/workflows/wled-ci.yml b/.github/workflows/wled-ci.yml index 1655f7ec..59661ff8 100644 --- a/.github/workflows/wled-ci.yml +++ b/.github/workflows/wled-ci.yml @@ -8,21 +8,23 @@ jobs: name: Gather Environments runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 + with: + python-version: '3.9' - name: Install PlatformIO run: pip install -r requirements.txt - name: Get default environments id: envs run: | - echo "::set-output name=environments::$(pio project config --json-output | jq -cr '.[0][1][0][1]')" + echo "environments=$(pio project config --json-output | jq -cr '.[0][1][0][1]')" >> $GITHUB_OUTPUT outputs: environments: ${{ steps.envs.outputs.environments }} @@ -32,24 +34,27 @@ jobs: runs-on: ubuntu-latest needs: get_default_envs strategy: + fail-fast: false matrix: environment: ${{ fromJSON(needs.get_default_envs.outputs.environments) }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Cache pip - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Cache PlatformIO - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.platformio key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 + with: + python-version: '3.9' - name: Install PlatformIO run: pip install -r requirements.txt - name: Build firmware diff --git a/CHANGELOG.md b/CHANGELOG.md index 69129061..641568de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ ## WLED changelog +#### Build 2304090 +- updated Arduino ESP8266 core to 4.1.0 (newer compiler) +- updated NeoPixelBus to 2.7.3 (with support for UCS890x chipset) +- better support for ESP32-C3, ESP32-S2 and ESP32-S3 (Arduino ESP32 core 5.2.0) +- iPad/tablet with 1024 pixels width in landscape orientation PC mode support (#3153) +- fix for Pixel Art Converter (#3155) + #### Build 2303240 - Peek scaling of large 2D matrices - Added 0D (1 pixel) metadata for effects & enhance 0D (analog strip) UI handling diff --git a/package-lock.json b/package-lock.json index 9a2d44e6..df1fb2de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wled", - "version": "0.14.0-b15.23", + "version": "0.14.0-b15.24", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wled", - "version": "0.14.0-b15.23", + "version": "0.14.0-b15.24", "license": "ISC", "dependencies": { "clean-css": "^4.2.3", diff --git a/package.json b/package.json index 87922f0b..bf855c85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wled", - "version": "0.14.0-b15.23", + "version": "0.14.0-b15.24", "description": "Tools for WLED project", "main": "tools/cdata.js", "directories": { diff --git a/platformio.ini b/platformio.ini index fa239a70..ac273f8b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,6 +12,9 @@ # Release / CI binaries ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32s2_saola, esp32c3, esp32s3dev_8MB +# Release binaries +; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB + # Build everything ; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0_6-rev2, codm-controller-0_6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips @@ -40,24 +43,24 @@ ; =================== default_envs = - esp32_4MB_S + ; esp32_4MB_S esp32_4MB_M ; recommended default esp32_4MB_M_debug esp32_4MB_XL esp32_16MB_M - esp32_16MB_M_debug - esp32_16MB_XL + ; esp32_16MB_M_debug + ; esp32_16MB_XL esp8266_4MB_S - esp8266_4MB_M + ; esp8266_4MB_M wemos_shield_esp32_4MB_M - wemos_shield_esp32_4MB_ICS4343x_M - wemos_shield_esp32_4MB_SPM1423_M - wemos_shield_esp32_4MB_LineIn_M - wemos_shield_esp32_16MB_M - wemos_shield_esp32_16MB_ICS4343x_M - wemos_shield_esp32_16MB_SPM1423_M - wemos_shield_esp32_16MB_SPM1423_XL - wemos_shield_esp32_16MB_LineIn_M + ; wemos_shield_esp32_4MB_ICS4343x_M + ; wemos_shield_esp32_4MB_SPM1423_M + ; wemos_shield_esp32_4MB_LineIn_M + ; wemos_shield_esp32_16MB_M + ; wemos_shield_esp32_16MB_ICS4343x_M + ; wemos_shield_esp32_16MB_SPM1423_M + ; wemos_shield_esp32_16MB_SPM1423_XL + ; wemos_shield_esp32_16MB_LineIn_M esp32_pico_4MB_M esp32_4MB_PSRAM_S esp32S3_8MB_M @@ -138,6 +141,7 @@ debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT # This reduces the OTA size with ~45KB, so it's especially useful on low memory boards (512k/1m). # ------------------------------------------------------------------------------ build_flags = + -Wno-attributes -DMQTT_MAX_PACKET_SIZE=1024 -DSECURE_CLIENT=SECURE_CLIENT_BEARSSL -DBEARSSL_SSL_BASIC @@ -839,7 +843,7 @@ build_flags_M = -D USERMOD_GAMES ; WLEDMM usermod -D USERMOD_BATTERY ;; enable Battery usermod -D USERMOD_BATTERY_USE_LIPO ;; use new "decharging curve" for LiPo cells - ; -D USERMOD_FASTLED ; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick, include this usermod only if you accept the terms! + -D USERMOD_ANIMARTRIX ; WLEDMM usermod: CC BY-NC 3.0 licensed effects by Stefan Petrick ;WLEDMM: only setting WLED_DEBUG_HOST is enough, ip and port can be defined in sync settings as well -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible -D WLED_DEBUG_PORT=1768 ;; port for network debugging. default = 7868 @@ -1099,7 +1103,7 @@ build_flags = ${common.build_flags_esp8266} -D USERMOD_FOUR_LINE_DISPLAY -D USERMOD_MPU6050_IMU ; gyro/accelero for USERMOD_GAMES (ONLY WORKS IF USERMOD_FOUR_LINE_DISPLAY NOT INCLUDED - I2C SHARING BUG) -D USERMOD_GAMES ; WLEDMM usermod - -D USERMOD_ARTIFX + ; -D USERMOD_ARTIFX ; this is compiling but not working due to low memory on 8266 -D USERMOD_BATTERY ;; enable Battery usermod -D USERMOD_BATTERY_USE_LIPO ;; use new "decharging curve" for LiPo cells -D WLED_DEBUG_HOST='"192.168.x.x"' ;; to send debug messages over network to host 192.168.x.y - FQDN is also possible diff --git a/requirements.txt b/requirements.txt index e484d7bc..da65486c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,63 +4,59 @@ # # pip-compile # -aiofiles==0.8.0 +aiofiles==22.1.0 # via platformio ajsonrpc==1.2.0 # via platformio -anyio==3.6.1 +anyio==3.6.2 # via starlette -async-timeout==4.0.2 - # via zeroconf -bottle==0.12.23 +bottle==0.12.25 # via platformio certifi==2022.12.7 # via requests -charset-normalizer==2.1.1 +charset-normalizer==3.1.0 # via requests click==8.1.3 # via # platformio # uvicorn -colorama==0.4.5 - # via platformio -h11==0.13.0 +colorama==0.4.6 + # via + # click + # platformio +h11==0.14.0 # via # uvicorn # wsproto -idna==3.3 +idna==3.4 # via # anyio # requests -ifaddr==0.2.0 - # via zeroconf -marshmallow==3.17.0 +marshmallow==3.19.0 # via platformio -packaging==21.3 +packaging==23.1 # via marshmallow -platformio==6.1.4 +platformio==6.1.6 # via -r requirements.in pyelftools==0.29 # via platformio -pyparsing==3.0.9 - # via packaging pyserial==3.5 # via platformio -requests==2.28.1 +requests==2.28.2 # via platformio semantic-version==2.10.0 # via platformio -sniffio==1.2.0 +sniffio==1.3.0 # via anyio -starlette==0.20.4 +starlette==0.23.1 # via platformio -tabulate==0.8.10 +tabulate==0.9.0 # via platformio -urllib3==1.26.11 +typing-extensions==4.5.0 + # via starlette +urllib3==1.26.15 # via requests -uvicorn==0.18.2 +uvicorn==0.20.0 # via platformio -wsproto==1.1.0 - # via platformio -zeroconf==0.39.0 +wsproto==1.2.0 # via platformio diff --git a/tools/cdata.js b/tools/cdata.js index 55b04e13..707360d6 100644 --- a/tools/cdata.js +++ b/tools/cdata.js @@ -221,6 +221,7 @@ function writeChunks(srcDir, specs, resultFile) { writeHtmlGzipped("wled00/data/index.htm", "wled00/html_ui.h", 'index'); writeHtmlGzipped("wled00/data/simple.htm", "wled00/html_simple.h", 'simple'); writeHtmlGzipped("wled00/data/pixart/pixart.htm", "wled00/html_pixart.h", 'pixart'); +writeHtmlGzipped("wled00/data/cpal/cpal.htm", "wled00/html_cpal.h", 'cpal'); /* writeChunks( "wled00/data", diff --git a/usermods/Battery/battery_defaults.h b/usermods/Battery/battery_defaults.h index 71c73b3b..c1e3c6bb 100644 --- a/usermods/Battery/battery_defaults.h +++ b/usermods/Battery/battery_defaults.h @@ -26,6 +26,15 @@ #endif #endif +//the default ratio for the voltage divider +#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER + #ifdef ARDUINO_ARCH_ESP32 + #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f + #else //ESP8266 boards + #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 4.2f + #endif +#endif + #ifndef USERMOD_BATTERY_MAX_VOLTAGE #define USERMOD_BATTERY_MAX_VOLTAGE 4.2f #endif diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 98aeda43..3d519724 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -29,6 +29,10 @@ class UsermodBattery : public Usermod float rawValue = 0.0f; // calculated voltage float voltage = maxBatteryVoltage; + // between 0 and 1, to control strength of voltage smoothing filter + float alpha = 0.05f; + // multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266 + float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER; // mapped battery level based on voltage int8_t batteryLevel = 100; // offset or calibration value to fine tune the calculated voltage @@ -110,6 +114,17 @@ class UsermodBattery : public Usermod } } + float readVoltage() + { + #ifdef ARDUINO_ARCH_ESP32 + // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value + return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; + #else + // use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value + return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; + #endif + } + public: //Functions called by WLED @@ -126,6 +141,7 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; + voltage = readVoltage(); } if (!success) { @@ -135,8 +151,8 @@ class UsermodBattery : public Usermod pinMode(batteryPin, INPUT); } #else //ESP8266 boards have only one analog input pin A0 - pinMode(batteryPin, INPUT); + voltage = readVoltage(); #endif nextReadTime = millis() + readingInterval; @@ -176,22 +192,12 @@ class UsermodBattery : public Usermod initializing = false; -#ifdef ARDUINO_ARCH_ESP32 - // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV) - rawValue = analogReadMilliVolts(batteryPin); - // calculate the voltage - voltage = (rawValue / 1000.0f) + calibration; - // usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2 - voltage *= 2.0f; -#else - // read battery raw input - rawValue = analogRead(batteryPin); + rawValue = readVoltage(); + // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout + voltage = voltage + alpha * (rawValue - voltage); - // calculate the voltage - voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration; -#endif - // check if voltage is within specified voltage range, allow 10% over/under voltage - voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; + // check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage + //voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; // translate battery voltage into percentage /* @@ -363,6 +369,7 @@ class UsermodBattery : public Usermod battery[F("max-voltage")] = maxBatteryVoltage; battery[F("capacity")] = totalBatteryCapacity; battery[F("calibration")] = calibration; + battery[F("voltage-multiplier")] = voltageMultiplier; battery[FPSTR(_readInterval)] = readingInterval; JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section @@ -375,6 +382,9 @@ class UsermodBattery : public Usermod lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold; lp[FPSTR(_duration)] = lowPowerIndicatorDuration; + // read voltage in case calibration or voltage multiplier changed to see immediate effect + voltage = readVoltage(); + DEBUG_PRINTLN(F("Battery config saved.")); } @@ -441,6 +451,7 @@ class UsermodBattery : public Usermod setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage); setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity); setCalibration(battery[F("calibration")] | calibration); + setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); JsonObject ao = battery[F("auto-off")]; @@ -597,21 +608,7 @@ class UsermodBattery : public Usermod totalBatteryCapacity = capacity; } - /* - * Get the choosen adc precision - * esp8266 = 10bit resolution = 1024.0f - * esp32 = 12bit resolution = 4095.0f - */ - float getAdcPrecision() - { - #ifdef ARDUINO_ARCH_ESP32 - // esp32 - return 4096.0f; - #else - // esp8266 - return 1024.0f; - #endif - } + /* * Get the calculated voltage @@ -649,6 +646,23 @@ class UsermodBattery : public Usermod calibration = offset; } + /* + * Set the voltage multiplier value + * A multiplier that may need adjusting for different voltage divider setups + */ + void setVoltageMultiplier(float multiplier) + { + voltageMultiplier = multiplier; + } + + /* + * Get the voltage multiplier value + * A multiplier that may need adjusting for different voltage divider setups + */ + float getVoltageMultiplier() + { + return voltageMultiplier; + } /* * Get auto-off feature enabled status diff --git a/usermods/artifx/artifx.js b/usermods/artifx/artifx.js index 892afb5c..d1658077 100644 --- a/usermods/artifx/artifx.js +++ b/usermods/artifx/artifx.js @@ -9,33 +9,9 @@ function toggleCEEditor(name, segID) { d.getElementById('ceEditor').style.transform = (isCEEditor) ? "translateY(0px)":"translateY(100%)"; } -function fetchAndExecute(url, name, callback, callError) -{ - fetch - (url+name, { - method: 'get' - }) - .then(res => { - if (!res.ok) { - callError("File " + name + " not found"); - return ""; - } - return res.text(); - }) - .then(text => { - callback(text); - }) - .catch(function (error) { - callError("Error getting " + name); - }) - .finally(() => { - // if (callback) setTimeout(callback,99); - }); -} - function loadLogFile(name, attempt) { var ceLogArea = d.getElementById("ceLogArea"); - fetchAndExecute((loc?`http://${locip}`:'.') + "/", name , function(logtext) + fetchAndExecute((loc?`http://${locip}`:'.') + "/", name, null, function(parms,logtext) { if (logtext == "") { if (attempt < 10) { @@ -50,7 +26,7 @@ function loadLogFile(name, attempt) { } else ceLogArea.value = logtext; - }, function(error){ + }, function(parms,error){ showToast(error); console.log(error); }); @@ -91,7 +67,7 @@ function saveCE(name, segID) { function populateCEEditor(name, segID) { - fetchAndExecute((loc?`http://${locip}`:'.') + "/", name + ".wled", function(text) + fetchAndExecute((loc?`http://${locip}`:'.') + "/", name + ".wled", null, function(parms,text) { var cn=`ARTI-FX Editor
${name}.wled
@@ -117,7 +93,7 @@ function populateCEEditor(name, segID) ceLogArea.value = "."; loadLogFile(name + ".log", 1); - }, function(error){ + }, function(parms,error){ showToast(error); console.log(error); }); @@ -129,7 +105,7 @@ function downloadGHFile(url, name, save=false, warn=false) { //Githubfile if (url == "HBE") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Presets/HB_PresetPack210808_32x32_16seg/Effects%20pack/"; if (url == "LM") url = "https://raw.githubusercontent.com/MoonModules/WLED-Effects/master/Ledmaps/"; - fetchAndExecute(url, name, function(text) { + fetchAndExecute(url, name, null, function(parms,text) { if (save) { if (warn && !confirm('Are you sure to download/overwrite ' + name + '?')) return; @@ -140,7 +116,7 @@ function downloadGHFile(url, name, save=false, warn=false) { //Githubfile var ceProgramArea = d.getElementById("ceProgramArea"); ceProgramArea.value = text; } - }, function(error){ + }, function(parms,error){ showToast(error); console.log(url + name,error); }); diff --git a/usermods/usermod_v2_fastled/readme.md b/usermods/usermod_v2_animartrix/readme.md similarity index 84% rename from usermods/usermod_v2_fastled/readme.md rename to usermods/usermod_v2_animartrix/readme.md index 93707655..d12dd1b0 100644 --- a/usermods/usermod_v2_fastled/readme.md +++ b/usermods/usermod_v2_animartrix/readme.md @@ -4,7 +4,7 @@ In this usermod file you can find the documentation on how to take advantage of ## Installation -Copy `usermod_v2_fastled.h` to the wled00 directory. +Copy `usermod_v2_animartrix.h` to the wled00 directory. Uncomment the corresponding lines in `usermods_list.cpp` and compile! _(You shouldn't need to actually install this, it does nothing useful)_ diff --git a/usermods/usermod_v2_fastled/usermod_v2_fastled.h b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h similarity index 97% rename from usermods/usermod_v2_fastled/usermod_v2_fastled.h rename to usermods/usermod_v2_animartrix/usermod_v2_animartrix.h index c105396b..ad21ae63 100644 --- a/usermods/usermod_v2_fastled/usermod_v2_fastled.h +++ b/usermods/usermod_v2_animartrix/usermod_v2_animartrix.h @@ -20,7 +20,7 @@ //based on: https://gist.github.com/StefanPetrick/9c091d9a28a902af5a7b540e40442c64 -class StefanPetrickCore { +class AnimartrixCore { private: public: @@ -60,11 +60,11 @@ class StefanPetrickCore { float noise_angle_c, noise_angle_d, noise_angle_e, noise_angle_f; // angles based on linear noise travel float dir_c, dir_d, dir_e, dir_f; // direction multiplicators - StefanPetrickCore() { - USER_PRINTLN("StefanPetrickCore constructor"); + AnimartrixCore() { + USER_PRINTLN("AnimartrixCore constructor"); } - ~StefanPetrickCore() { - USER_PRINTLN("StefanPetrickCore destructor"); + ~AnimartrixCore() { + USER_PRINTLN("AnimartrixCore destructor"); } void init() { @@ -177,7 +177,7 @@ class StefanPetrickCore { } }; -class PolarBasics:public StefanPetrickCore { +class PolarBasics:public AnimartrixCore { private: public: @@ -262,7 +262,7 @@ class PolarBasics:public StefanPetrickCore { void calculate_oscillators() { - StefanPetrickCore::calculate_oscillators(); + AnimartrixCore::calculate_oscillators(); uint16_t noi; noi = inoise16(10000 + linear_c * 100000); // some noise controlled angular offsets @@ -369,7 +369,7 @@ class PolarBasics:public StefanPetrickCore { //based on https://gist.github.com/StefanPetrick/35ffd8467df22a77067545cfb889aa4f //and Fastled podcast nr 3: https://www.youtube.com/watch?v=3tfjP7GJnZo -class CircularBlobs:public StefanPetrickCore { +class CircularBlobs:public AnimartrixCore { private: float fade(float t){ return t * t * t * (t * (t * 6 - 15) + 10); } @@ -498,7 +498,7 @@ class CircularBlobs:public StefanPetrickCore { void calculate_oscillators() { - StefanPetrickCore::calculate_oscillators(); + AnimartrixCore::calculate_oscillators(); float n; @@ -615,11 +615,11 @@ uint16_t mode_CircularBlobs(void) { static const char _data_FX_mode_CircularBlobs[] PROGMEM = "💡CircularBlobs ☾@AngleDist,AngleMult;;!;2;sx=51,ix=51,c1=0,c2=0,c3=0"; -class FastledUsermod : public Usermod { +class AnimartrixUsermod : public Usermod { public: - FastledUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM + AnimartrixUsermod(const char *name, bool enabled):Usermod(name, enabled) {} //WLEDMM void setup() { strip.addEffect(255, &mode_PolarBasics, _data_FX_mode_PolarBasics); @@ -640,7 +640,7 @@ class FastledUsermod : public Usermod { uint16_t getId() { - return USERMOD_ID_FASTLED; + return USERMOD_ID_ANIMARTRIX; } }; 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 c825a7af..058b8318 100644 --- a/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h +++ b/usermods/usermod_v2_word_clock/usermod_v2_word_clock.h @@ -40,39 +40,39 @@ class WordClockUsermod : public Usermod // Normal wiring const int maskMinutes[14][maskSizeMinutes] = { - {107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 - { 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // :05 fünf nach - { 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // :10 zehn nach - { 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel - { 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // :20 zwanzig nach - { 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // :25 fünf vor halb - { 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb - { 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // :35 fünf nach halb - { 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // :40 zwanzig vor - { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel - { 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // :50 zehn vor - { 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // :55 fünf vor - { 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // :15 alternative viertel nach - { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // :45 alternative viertel vor + {107, 108, 109, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00 + { 7, 8, 9, 10, 40, 41, 42, 43, -1, -1, -1, -1}, // 1 - 05 fünf nach + { 11, 12, 13, 14, 40, 41, 42, 43, -1, -1, -1, -1}, // 2 - 10 zehn nach + { 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel + { 15, 16, 17, 18, 19, 20, 21, 40, 41, 42, 43, -1}, // 4 - 20 zwanzig nach + { 7, 8, 9, 10, 33, 34, 35, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb + { 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb + { 7, 8, 9, 10, 40, 41, 42, 43, 44, 45, 46, 47}, // 7 - 35 fünf nach halb + { 15, 16, 17, 18, 19, 20, 21, 33, 34, 35, -1, -1}, // 8 - 40 zwanzig vor + { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel + { 11, 12, 13, 14, 33, 34, 35, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor + { 7, 8, 9, 10, 33, 34, 35, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor + { 26, 27, 28, 29, 30, 31, 32, 40, 41, 42, 43, -1}, // 12 - 15 alternative viertel nach + { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1} // 13 - 45 alternative viertel vor }; // Meander wiring const int maskMinutesMea[14][maskSizeMinutesMea] = { - { 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // :00 - { 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // :05 fünf nach - { 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // :10 zehn nach - { 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // :15 viertel - { 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // :20 zwanzig nach - { 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // :25 fünf vor halb - { 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // :30 halb - { 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // :35 fünf nach halb - { 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // :40 zwanzig vor - { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // :45 dreiviertel - { 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // :50 zehn vor - { 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // :55 fünf vor - { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // :15 alternative viertel nach - { 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // :45 alternative viertel vor + { 99, 100, 101, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0 - 00 + { 7, 8, 9, 10, 33, 34, 35, 36, -1, -1, -1, -1}, // 1 - 05 fünf nach + { 18, 19, 20, 21, 33, 34, 35, 36, -1, -1, -1, -1}, // 2 - 10 zehn nach + { 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1}, // 3 - 15 viertel + { 11, 12, 13, 14, 15, 16, 17, 33, 34, 35, 36, -1}, // 4 - 20 zwanzig nach + { 7, 8, 9, 10, 41, 42, 43, 44, 45, 46, 47, -1}, // 5 - 25 fünf vor halb + { 44, 45, 46, 47, -1, -1, -1, -1, -1, -1, -1, -1}, // 6 - 30 halb + { 7, 8, 9, 10, 33, 34, 35, 36, 44, 45, 46, 47}, // 7 - 35 fünf nach halb + { 11, 12, 13, 14, 15, 16, 17, 41, 42, 43, -1, -1}, // 8 - 40 zwanzig vor + { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1}, // 9 - 45 dreiviertel + { 18, 19, 20, 21, 41, 42, 43, -1, -1, -1, -1, -1}, // 10 - 50 zehn vor + { 7, 8, 9, 10, 41, 42, 43, -1, -1, -1, -1, -1}, // 11 - 55 fünf vor + { 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1}, // 12 - 15 alternative viertel nach + { 26, 27, 28, 29, 30, 31, 32, 41, 42, 43, -1, -1} // 13 - 45 alternative viertel vor }; @@ -284,12 +284,13 @@ class WordClockUsermod : public Usermod setHours(hours + 1, false); break; case 9: - // viertel vor bzw dreiviertel + // viertel vor if (nord) { - setMinutes(9); + setMinutes(13); } + // dreiviertel else { - setMinutes(12); + setMinutes(9); } setHours(hours + 1, false); break; @@ -422,12 +423,18 @@ class WordClockUsermod : public Usermod */ void addToConfig(JsonObject& root) { - JsonObject top = root.createNestedObject("WordClockUsermod"); - top["active"] = usermodActive; - top["displayItIs"] = displayItIs; - top["ledOffset"] = ledOffset; - top["Meander wiring?"] = meander; - top["Norddeutsch"] = nord; + JsonObject top = root.createNestedObject(F("WordClockUsermod")); + top[F("active")] = usermodActive; + top[F("displayItIs")] = displayItIs; + top[F("ledOffset")] = ledOffset; + top[F("Meander wiring?")] = meander; + top[F("Norddeutsch")] = nord; + } + + 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');")); } /* @@ -450,15 +457,15 @@ class WordClockUsermod : public Usermod // default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor // setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed) - JsonObject top = root["WordClockUsermod"]; + JsonObject top = root[F("WordClockUsermod")]; bool configComplete = !top.isNull(); - configComplete &= getJsonValue(top["active"], usermodActive); - configComplete &= getJsonValue(top["displayItIs"], displayItIs); - configComplete &= getJsonValue(top["ledOffset"], ledOffset); - configComplete &= getJsonValue(top["Meander wiring?"], meander); - configComplete &= getJsonValue(top["Norddeutsch"], nord); + configComplete &= getJsonValue(top[F("active")], usermodActive); + configComplete &= getJsonValue(top[F("displayItIs")], displayItIs); + configComplete &= getJsonValue(top[F("ledOffset")], ledOffset); + configComplete &= getJsonValue(top[F("Meander wiring?")], meander); + configComplete &= getJsonValue(top[F("Norddeutsch")], nord); return configComplete; } diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 586db297..dab70a6b 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -1247,10 +1247,10 @@ uint16_t mode_rain() { if (SEGENV.call && SEGENV.step > SPEED_FORMULA_L) { SEGENV.step = 1; if (strip.isMatrix) { - uint32_t ctemp[width]; - for (int i = 0; i> 6; //div 64 + const uint32_t it = strip.now >> 5; //div 32 struct virtualStrip { static void runStrip(uint16_t stripNr, byte* heat, uint32_t it) { @@ -1987,28 +1987,24 @@ uint16_t mode_fire_2012() { // Step 1. Cool down every cell a little for (int i = 0; i < SEGLEN; i++) { - uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(8); - uint8_t minTemp = 0; - if (i 1; k--) { heat[k] = (heat[k - 1] + (heat[k - 2]<<1) ) / 3; // heat[k-2] multiplied by 2 } - } - // Step 3. Randomly ignite new 'sparks' of heat near the bottom - if (random8() <= SEGMENT.intensity) { - uint8_t y = random8(ignition); - uint8_t boost = (32+SEGMENT.custom3*2) * (2*ignition-y) / (2*ignition); - heat[y] = qadd8(heat[y], random8(64+boost,128+boost)); + // Step 3. Randomly ignite new 'sparks' of heat near the bottom + if (random8() <= SEGMENT.intensity) { + uint8_t y = random8(ignition); + uint8_t boost = (17+SEGMENT.custom3) * (ignition - y/2) / ignition; // integer math! + heat[y] = qadd8(heat[y], random8(96+2*boost,207+boost)); + } } // Step 4. Map from heat cells to LED colors @@ -2028,7 +2024,7 @@ uint16_t mode_fire_2012() { return FRAMETIME; } -static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1.5d;pal=0,sx=120,ix=64,m12=1"; // bars WLEDMM 1.5d, pal = 0 +static const char _data_FX_MODE_FIRE_2012[] PROGMEM = "Fire 2012@Cooling,Spark rate,,,Boost;;!;1.5d;sx=64,ix=160,m12=1"; // bars WLEDMM 1.5d, // ColorWavesWithPalettes by Mark Kriegsman: https://gist.github.com/kriegsman/8281905786e8b2632aeb @@ -2765,7 +2761,7 @@ uint16_t mode_spots() { return spots_base((255 - SEGMENT.speed) << 8); } -static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@,Width,,,,,Overlay;!,!;!"; +static const char _data_FX_MODE_SPOTS[] PROGMEM = "Spots@Spread,Width,,,,,Overlay;!,!;!"; //Intensity slider sets number of "lights", LEDs per light fade in and out @@ -4111,7 +4107,7 @@ uint16_t mode_dancing_shadows(void) spotlights[i].type = random8(SPOT_TYPES_COUNT); } - uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 0); + uint32_t color = SEGMENT.color_from_palette(spotlights[i].colorIdx, false, false, 255); int start = spotlights[i].position; if (spotlights[i].width <= 1) { @@ -5530,22 +5526,30 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so SEGMENT.fill(BLACK); } - uint8_t hue; + uint8_t hue, bri; + size_t intensity; int offsetX = beatsin16(3, -360, 360); int offsetY = beatsin16(2, -360, 360); + int sharpness = SEGMENT.custom3 / 8; // 0-3 for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { hue = x * beatsin16(10, 1, 10) + offsetY; - SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(x * SEGMENT.speed + offsetX) * sin8(x * SEGMENT.speed + offsetX) / 255, LINEARBLEND)); + intensity = bri = sin8(x * SEGMENT.speed/2 + offsetX); + for (int i=0; i>= 8*sharpness; + SEGMENT.setPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); hue = y * 3 + offsetX; - SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, sin8(y * SEGMENT.intensity + offsetY) * sin8(y * SEGMENT.intensity + offsetY) / 255, LINEARBLEND)); + intensity = bri = sin8(y * SEGMENT.intensity/2 + offsetY); + for (int i=0; i>= 8*sharpness; + SEGMENT.addPixelColorXY(x, y, ColorFromPalette(SEGPALETTE, hue, intensity, LINEARBLEND)); } } return FRAMETIME; } // mode_2DTartan() -static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale;;!;2"; +static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,Sharpness;;!;2"; ///////////////////////// @@ -7576,7 +7580,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea // Distortion waves - ldirko // https://editor.soulmatelights.com/gallery/1089-distorsion-waves -// apated for WLD by @blazoncek +// adapted for WLED by @blazoncek uint16_t mode_2Ddistortionwaves() { if (!strip.isMatrix) return mode_static(); // not a 2D set-up @@ -7625,7 +7629,116 @@ uint16_t mode_2Ddistortionwaves() { return FRAMETIME; } -static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2;"; +static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2"; + +//Soap +//@Stepko +//Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick +// adapted for WLED by @blazoncek +uint16_t mode_2Dsoap() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(uint8_t); // prevent reallocation if mirrored or grouped + if (!SEGENV.allocateData(dataSize + sizeof(uint32_t)*3)) return mode_static(); //allocation failed + + uint8_t *noise3d = reinterpret_cast(SEGENV.data); + uint32_t *noise32_x = reinterpret_cast(SEGENV.data + dataSize); + uint32_t *noise32_y = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)); + uint32_t *noise32_z = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)*2); + uint32_t scale32_x = 160000U/cols; + uint32_t scale32_y = 160000U/rows; + + // init + if (SEGENV.call == 0) { + SEGMENT.setUpLeds(); + *noise32_x = random16(); + *noise32_y = random16(); + *noise32_z = random16(); + for (int i = 0; i < cols; i++) { + int32_t ioffset = scale32_x * (i - cols / 2); + for (int j = 0; j < rows; j++) { + int32_t joffset = scale32_y * (j - rows / 2); + uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; + noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], SEGMENT.intensity) + scale8(data, 255 - SEGMENT.intensity); + SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); + } + } + } + + uint32_t mov = MAX(cols,rows)*(SEGMENT.speed+1)/2; + *noise32_x += mov; + *noise32_y += mov; + *noise32_z += mov; + + for (int i = 0; i < cols; i++) { + int32_t ioffset = scale32_x * (i - cols / 2); + for (int j = 0; j < rows; j++) { + int32_t joffset = scale32_y * (j - rows / 2); + uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; + noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], SEGMENT.intensity) + scale8(data, 256 - SEGMENT.intensity); + } + } + + int zD; + int zF; + int amplitude; + int8_t shiftX = 0; //(SEGMENT.custom1 - 128) / 4; + int8_t shiftY = 0; //(SEGMENT.custom2 - 128) / 4; + + amplitude = (cols >= 16) ? (cols-8)/8 : 1; + for (int y = 0; y < rows; y++) { + int amount = ((int)noise3d[XY(0,y)] - 128) * 2 * amplitude + 256*shiftX; + int delta = abs(amount) >> 8; + int fraction = abs(amount) & 255; + for (int x = 0; x < cols; x++) { + if (amount < 0) { + zD = x - delta; + zF = zD - 1; + } else { + zD = x + delta; + zF = zD + 1; + } + CRGB PixelA = CRGB::Black; + if ((zD >= 0) && (zD < cols)) PixelA = SEGMENT.getPixelColorXY(zD, y); + else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zD),y)]*3); + CRGB PixelB = CRGB::Black; + if ((zF >= 0) && (zF < cols)) PixelB = SEGMENT.getPixelColorXY(zF, y); + else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(abs(zF),y)]*3); + CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + SEGMENT.setPixelColorXY(x, y, pix); + } + } + + amplitude = (rows >= 16) ? (rows-8)/8 : 1; + for (int x = 0; x < cols; x++) { + int amount = ((int)noise3d[XY(x,0)] - 128) * 2 * amplitude + 256*shiftY; + int delta = abs(amount) >> 8; + int fraction = abs(amount) & 255; + for (int y = 0; y < rows; y++) { + if (amount < 0) { + zD = y - delta; + zF = zD - 1; + } else { + zD = y + delta; + zF = zD + 1; + } + CRGB PixelA = CRGB::Black; + if ((zD >= 0) && (zD < rows)) PixelA = SEGMENT.getPixelColorXY(x, zD); + else PixelA = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zD))]*3); + CRGB PixelB = CRGB::Black; + if ((zF >= 0) && (zF < rows)) PixelB = SEGMENT.getPixelColorXY(x, zF); + else PixelB = ColorFromPalette(SEGPALETTE, ~noise3d[XY(x,abs(zF))]*3); + CRGB pix = (PixelA.nscale8(ease8InOutApprox(255 - fraction))) + (PixelB.nscale8(ease8InOutApprox(fraction))); + SEGMENT.setPixelColorXY(x, y, pix); + } + } + + return FRAMETIME; +} +static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; #endif // WLED_DISABLE_2D @@ -7860,6 +7973,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_2DSINDOTS, &mode_2DSindots, _data_FX_MODE_2DSINDOTS); addEffect(FX_MODE_2DDNASPIRAL, &mode_2DDNASpiral, _data_FX_MODE_2DDNASPIRAL); addEffect(FX_MODE_2DBLACKHOLE, &mode_2DBlackHole, _data_FX_MODE_2DBLACKHOLE); + addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP); addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio #endif // WLED_DISABLE_2D diff --git a/wled00/FX.h b/wled00/FX.h index 6fec67c8..08ac0797 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -266,7 +266,8 @@ #define FX_MODE_2DBLOBS 121 //gap fill #define FX_MODE_2DSCROLLTEXT 122 //gap fill #define FX_MODE_2DDRIFTROSE 123 //gap fill -#define FX_MODE_2DDISTORTIONWAVES 124 +#define FX_MODE_2DDISTORTIONWAVES 124 //gap fill +#define FX_MODE_2DSOAP 125 //gap fill // WLED-SR effects (SR compatible IDs !!!) #define FX_MODE_PIXELS 128 @@ -579,9 +580,9 @@ typedef struct Segment { void fadeToBlackBy(uint8_t fadeBy); void blendPixelColor(int n, uint32_t color, uint8_t blend); void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); } - void addPixelColor(int n, uint32_t color); - void addPixelColor(int n, byte r, byte g, byte b, byte w = 0) { addPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline - void addPixelColor(int n, CRGB c) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline + void addPixelColor(int n, uint32_t color, bool fast = false); + void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline + void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline void fadePixelColor(uint16_t n, uint8_t fade); uint8_t get_random_wheel_index(uint8_t pos); uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255); @@ -605,16 +606,16 @@ typedef struct Segment { // 2D support functions void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend); 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); } - void addPixelColorXY(int x, int y, uint32_t color); - void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline - void addPixelColorXY(int x, int y, CRGB c) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } + void addPixelColorXY(int x, int y, uint32_t color, bool fast = false); + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline + void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); } void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade); void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight) void blurRow(uint16_t row, fract8 blur_amount); void blurCol(uint16_t col, fract8 blur_amount); - void moveX(int8_t delta); - void moveY(int8_t delta); - void move(uint8_t dir, uint8_t delta); + void moveX(int8_t delta, bool wrap = false); + void moveY(int8_t delta, bool wrap = false); + void move(uint8_t dir, uint8_t delta, bool wrap = false); void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); @@ -628,7 +629,7 @@ typedef struct Segment { void blur2d(fract8 blur_amount) { blur(blur_amount); } void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } void nscale8(uint8_t scale); - bool jsonToPixels(char *name, uint8_t fileNr); + bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx #else uint16_t XY(uint16_t x, uint16_t y) { return x; } void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } @@ -640,16 +641,16 @@ typedef struct Segment { uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); } void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); } void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); } - void addPixelColorXY(int x, int y, uint32_t color) { addPixelColor(x, color); } - void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { addPixelColor(x, RGBW32(r,g,b,w)); } - void addPixelColorXY(int x, int y, CRGB c) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } + void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); } + void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(x, RGBW32(r,g,b,w), fast); } + void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); } void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); } void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {} void blurRow(uint16_t row, fract8 blur_amount) {} void blurCol(uint16_t col, fract8 blur_amount) {} - void moveX(int8_t delta) {} - void moveY(int8_t delta) {} - void move(uint8_t dir, uint8_t delta) {} + void moveX(int8_t delta, bool wrap = false) {} + void moveY(int8_t delta, bool wrap = false) {} + void move(uint8_t dir, uint8_t delta, bool wrap = false) {} void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} @@ -711,6 +712,7 @@ class WS2812FX { // 96 bytes _modeCount(MODE_COUNT), _callback(nullptr), customMappingTable(nullptr), + customMappingTableSize(0), //WLEDMM customMappingSize(0), _lastShow(0), _segment_index(0), @@ -941,8 +943,9 @@ class WS2812FX { // 96 bytes show_callback _callback; - uint16_t* customMappingTable = nullptr; - uint16_t customMappingSize = 0; + uint16_t* customMappingTable; + uint16_t customMappingTableSize; //WLEDMM + uint16_t customMappingSize; /*uint32_t*/ unsigned long _lastShow; // WLEDMM avoid losing precision diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp index 910405bf..1db4f50a 100644 --- a/wled00/FX_2Dfcn.cpp +++ b/wled00/FX_2Dfcn.cpp @@ -36,12 +36,6 @@ // so matrix should disable regular ledmap processing void WS2812FX::setUpMatrix() { #ifndef WLED_DISABLE_2D - // erase old ledmap, just in case. - //if (customMappingTable != nullptr) delete[] customMappingTable; - if (customMappingTable != nullptr) free(customMappingTable); - customMappingTable = nullptr; - customMappingSize = 0; - // isMatrix is set in cfg.cpp or set.cpp if (isMatrix) { // calculate width dynamically because it will have gaps @@ -69,15 +63,19 @@ void WS2812FX::setUpMatrix() { return; } - //customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight]; - if (nullptr != customMappingTable) free(customMappingTable); - customMappingTable = (uint16_t *) calloc(Segment::maxWidth * Segment::maxHeight, sizeof(uint16_t)); + //WLEDMM recreate customMappingTable if more space needed + if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { + USER_PRINTF("setupmatrix customMappingTable alloc %d from %d\n", Segment::maxWidth * Segment::maxHeight, customMappingTableSize); + if (customMappingTable != nullptr) delete[] customMappingTable; + customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight]; + if (customMappingTable != nullptr) customMappingTableSize = Segment::maxWidth * Segment::maxHeight; + } if (customMappingTable != nullptr) { customMappingSize = Segment::maxWidth * Segment::maxHeight; // fill with empty in case we don't fill the entire matrix - for (size_t i = 0; i< customMappingSize; i++) { + for (size_t i = 0; i< customMappingTableSize; i++) { //WLEDMM use customMappingTableSize customMappingTable[i] = (uint16_t)-1; } @@ -314,8 +312,22 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t } // Adds the specified color with the existing pixel color perserving color balance. -void Segment::addPixelColorXY(int x, int y, uint32_t color) { - setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); +void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) { + uint32_t col = getPixelColorXY(x,y); + uint8_t r = R(col); + uint8_t g = G(col); + uint8_t b = B(col); + uint8_t w = W(col); + if (fast) { + r = qadd8(r, R(color)); + g = qadd8(g, G(color)); + b = qadd8(b, B(color)); + w = qadd8(w, W(color)); + col = RGBW32(r,g,b,w); + } else { + col = color_add(col, color); + } + setPixelColorXY(x, y, col); } void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { @@ -426,54 +438,55 @@ void Segment::blur1d(fract8 blur_amount) { //WLEDMM: use fast types for (uint_fast16_t y = 0; y < rows; y++) blurRow(y, blur_amount); } -void Segment::moveX(int8_t delta) { +void Segment::moveX(int8_t delta, bool wrap) { const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); - if (!delta) return; - if (delta > 0) { - for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) { - if (x + delta >= cols) break; - setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y)); - } - } else { - for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) { - if (x + delta < 0) break; - setPixelColorXY(x, y, getPixelColorXY(x + delta, y)); + if (!delta || abs(delta) >= cols) return; + uint32_t newPxCol[cols]; + for (int y = 0; y < rows; y++) { + if (delta > 0) { + for (int x = 0; x < cols-delta; x++) newPxCol[x] = getPixelColorXY((x + delta), y); + for (int x = cols-delta; x < cols; x++) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) - cols : x, y); + } else { + for (int x = cols-1; x >= -delta; x--) newPxCol[x] = getPixelColorXY((x + delta), y); + for (int x = -delta-1; x >= 0; x--) newPxCol[x] = getPixelColorXY(wrap ? (x + delta) + cols : x, y); } + for (int x = 0; x < cols; x++) setPixelColorXY(x, y, newPxCol[x]); } } -void Segment::moveY(int8_t delta) { +void Segment::moveY(int8_t delta, bool wrap) { const uint16_t cols = virtualWidth(); const uint16_t rows = virtualHeight(); - if (!delta) return; - if (delta > 0) { - for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) { - if (y + delta >= rows) break; - setPixelColorXY(x, y, getPixelColorXY(x, (y + delta))); - } - } else { - for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) { - if (y + delta < 0) break; - setPixelColorXY(x, y, getPixelColorXY(x, y + delta)); + if (!delta || abs(delta) >= rows) return; + uint32_t newPxCol[rows]; + for (int x = 0; x < cols; x++) { + if (delta > 0) { + for (int y = 0; y < rows-delta; y++) newPxCol[y] = getPixelColorXY(x, (y + delta)); + for (int y = rows-delta; y < rows; y++) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) - rows : y); + } else { + for (int y = rows-1; y >= -delta; y--) newPxCol[y] = getPixelColorXY(x, (y + delta)); + for (int y = -delta-1; y >= 0; y--) newPxCol[y] = getPixelColorXY(x, wrap ? (y + delta) + rows : y); } + for (int y = 0; y < rows; y++) setPixelColorXY(x, y, newPxCol[y]); } } // move() - move all pixels in desired direction delta number of pixels // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param delta number of pixels to move -void Segment::move(uint8_t dir, uint8_t delta) { +// @param wrap around +void Segment::move(uint8_t dir, uint8_t delta, bool wrap) { if (delta==0) return; switch (dir) { - case 0: moveX( delta); break; - case 1: moveX( delta); moveY( delta); break; - case 2: moveY( delta); break; - case 3: moveX(-delta); moveY( delta); break; - case 4: moveX(-delta); break; - case 5: moveX(-delta); moveY(-delta); break; - case 6: moveY(-delta); break; - case 7: moveX( delta); moveY(-delta); break; + case 0: moveX( delta, wrap); break; + case 1: moveX( delta, wrap); moveY( delta, wrap); break; + case 2: moveY( delta, wrap); break; + case 3: moveX(-delta, wrap); moveY( delta, wrap); break; + case 4: moveX(-delta, wrap); break; + case 5: moveX(-delta, wrap); moveY(-delta, wrap); break; + case 6: moveY(-delta, wrap); break; + case 7: moveX( delta, wrap); moveY(-delta, wrap); break; } } @@ -562,6 +575,7 @@ void Segment::drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, } } +//WLEDMM for artifx bool Segment::jsonToPixels(char * name, uint8_t fileNr) { char fileName[32]; //WLEDMM: als support segment name ledmaps diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ccd7656e..275f91f2 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -235,6 +235,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) { case FX_MODE_GLITTER : pal = 11; break; // rainbow colors case FX_MODE_SUNRISE : pal = 35; break; // heat palette case FX_MODE_RAILWAY : pal = 3; break; // prim + sec + case FX_MODE_2DSOAP : pal = 11; break; // rainbow colors } switch (pal) { case 0: //default palette. Exceptions for specific effects above @@ -591,7 +592,6 @@ class JMapC { if (jVectorMap.size() > 0) { USER_PRINTLN("delete jVectorMap"); for (size_t i=0; i(); - if (name != nullptr) len = strlen(name); + if (name != nullptr) { + len = strlen(name); if (len > 0 && len < 33) { ledmapNames[i-1] = new char[len+1]; if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33); @@ -1371,6 +1392,7 @@ void WS2812FX::enumerateLedmaps() { if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33); } } + f.close(); releaseJSONBufferLock(); } #endif @@ -2090,12 +2112,9 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (!isFile) { // erase custom mapping if selecting nonexistent ledmap.json (n==0) - //WLEDM: doubt this is necessary as return false causes setupMatrix to deal with this - if (!isMatrix && !n && customMappingTable != nullptr) { + //WLEDM: doubt this is necessary as return false causes setupMatrix to deal with this !!!! + if (!isMatrix && !n) { customMappingSize = 0; - //delete[] customMappingTable; - free(customMappingTable); - customMappingTable = nullptr; loadedLedmap = 0; //WLEDMM } return false; @@ -2103,7 +2122,11 @@ bool WS2812FX::deserializeMap(uint8_t n) { if (!requestJSONBufferLock(7)) return false; - if (!readObjectFromFile(fileName, nullptr, &doc)) { + //WLEDMM: change upstream code: do not load complete ledmaps in json as this blows up memory, use file read instead + //read the file + File f; + f = WLED_FS.open(fileName, "r"); + if (!f) { releaseJSONBufferLock(); return false; //if file does not exist just exit } @@ -2111,41 +2134,46 @@ bool WS2812FX::deserializeMap(uint8_t n) { USER_PRINT(F("Reading LED map from ")); //WLEDMM use USER_PRINT USER_PRINTLN(fileName); - // erase old custom ledmap - if (customMappingTable != nullptr) { - customMappingSize = 0; - //delete[] customMappingTable; - free(customMappingTable); // softhack007 use calloc / free, as they behave better when heap is low - customMappingTable = nullptr; - loadedLedmap = 0; + //WLEDMM: read width and height (mandatory in file!!) + f.find("\"width\":"); + uint16_t maxWidth = f.readStringUntil('\n').toInt(); + + f.find("\"height\":"); + uint16_t maxHeight = f.readStringUntil('\n').toInt(); + + USER_PRINTF("deserializeMap %d x %d\n", maxWidth, maxHeight); + if (maxWidth * maxHeight <= 0) { + releaseJSONBufferLock(); + return false; } - JsonArray map = doc[F("map")]; - if (!map.isNull() && map.size()) { // not an empty map + //WLEDMM: support ledmap file properties width and height + Segment::maxWidth = maxWidth; + Segment::maxHeight = maxHeight; + resetSegments(true); //WLEDMM not makeAutoSegments() as we only want to change bounds - //WLEDMM: support ledmap file properties width and height - if (doc[F("width")]>0 && doc[F("height")]>0) { - Segment::maxWidth = doc[F("width")]; - Segment::maxHeight = doc[F("height")]; - resetSegments(true); //WLEDMM not makeAutoSegments() as we only want to change bounds - } + //WLEDMM recreate customMappingTable if more space needed + if (Segment::maxWidth * Segment::maxHeight > customMappingTableSize) { + USER_PRINTF("deserializemap customMappingTable alloc %d from %d\n", Segment::maxWidth * Segment::maxHeight, customMappingTableSize); + if (customMappingTable != nullptr) delete[] customMappingTable; + customMappingTable = new uint16_t[Segment::maxWidth * Segment::maxHeight]; + if (customMappingTable != nullptr) customMappingTableSize = Segment::maxWidth * Segment::maxHeight; + } - customMappingSize = map.size(); - if (nullptr != customMappingTable) free(customMappingTable); - //customMappingTable = new uint16_t[customMappingSize]; - customMappingTable = (uint16_t *) calloc(customMappingSize+1, sizeof(uint16_t)); // softhack007 use calloc / free, as they behave better when heap is low - if (nullptr == customMappingTable) { // WLEDMM handle out-of-memory - USER_PRINTF("deserializeMap(): cannot alloate %d bytes for customMappingTable[]\n", sizeof(uint16_t) * (customMappingSize+1)); - customMappingSize = 0; - loadedLedmap = 0; - releaseJSONBufferLock(); - return false; //if not enough memory - just exit - } + if (customMappingTable != nullptr) { + customMappingSize = maxWidth * maxHeight; - for (uint16_t i=0; i ANALOG_BTN_READ_CYCLE) { // button is not a button but a potentiometer - analog = true; - handleAnalog(b); continue; + if (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer + if (now - lastRead > ANALOG_BTN_READ_CYCLE) { + handleAnalog(b); + lastRead = now; + } + continue; } //button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0) if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) { - handleSwitch(b); continue; + handleSwitch(b); + continue; } //momentary button logic @@ -305,7 +308,6 @@ void handleButton() shortPressAction(b); } } - if (analog) lastRead = now; } // If enabled, RMT idle level is set to HIGH when off diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index d0a731b0..32712fb5 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -19,6 +19,8 @@ void getStringFromJson(char* dest, const char* src, size_t len) { bool deserializeConfig(JsonObject doc, bool fromFS) { //WLEDMM add USER_PRINT + // String temp; + // serializeJson(doc, temp); USER_PRINTF("deserializeConfig\n"); bool needsSave = false; @@ -177,10 +179,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { uint8_t ledType = elm["type"] | TYPE_WS2812_RGB; bool reversed = elm["rev"]; bool refresh = elm["ref"] | false; + uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully) ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode; if (fromFS) { - BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); + BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); //WLEDMM to do bus , freqkHz mem += BusManager::memUsage(bc); if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip() } else { @@ -784,6 +787,7 @@ void serializeConfig() { ins["type"] = bus->getType() & 0x7F; ins["ref"] = bus->isOffRefreshRequired(); ins[F("rgbwm")] = bus->getAutoWhiteMode(); + // ins[F("freq")] = bus->getFrequency(); WLEDMM to do bus } JsonArray hw_com = hw.createNestedArray(F("com")); diff --git a/wled00/const.h b/wled00/const.h index 449eeba0..a35da318 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -141,7 +141,7 @@ #define USERMOD_ID_ARTIFX 90 //Usermod "usermod_v2_artifx.h" #define USERMOD_ID_WEATHER 91 //Usermod "usermod_v2_weather.h" #define USERMOD_ID_GAMES 92 //Usermod "usermod_v2_games.h" -#define USERMOD_ID_FASTLED 93 //Usermod "usermod_v2_fastled.h" +#define USERMOD_ID_ANIMARTRIX 93 //Usermod "usermod_v2_animartrix.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot @@ -226,6 +226,8 @@ #define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern) #define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units #define TYPE_TM1829 25 +#define TYPE_UCS8903 26 +#define TYPE_UCS8904 29 #define TYPE_SK6812_RGBW 30 #define TYPE_TM1814 31 //"Analog" types (PWM) (32-47) @@ -274,7 +276,7 @@ #define BTN_TYPE_ANALOG_INVERTED 8 //Ethernet board types -#define WLED_NUM_ETH_TYPES 9 +#define WLED_NUM_ETH_TYPES 10 #define WLED_ETH_NONE 0 #define WLED_ETH_WT32_ETH01 1 @@ -283,6 +285,7 @@ #define WLED_ETH_QUINLED 4 #define WLED_ETH_TWILIGHTLORD 5 #define WLED_ETH_ESP32DEUX 6 +#define WLED_ETH_ABCWLEDV43ETH 9 //Hue error codes #define HUE_ERROR_INACTIVE 0 diff --git a/wled00/data/404.htm b/wled00/data/404.htm index 87244a94..260253a3 100644 --- a/wled00/data/404.htm +++ b/wled00/data/404.htm @@ -1,47 +1,47 @@ - - - - - Not found - - - - -

404 Not Found

-Akemi does not know where you are headed...

- - + img { + width: 400px; + max-width: 50%; + image-rendering: pixelated; + image-rendering: crisp-edges; + margin: 25px 0 -10px 0; + } + + button { + outline: none; + cursor: pointer; + padding: 8px; + margin: 10px; + width: 230px; + text-transform: uppercase; + font-family: helvetica; + font-size: 19px; + background-color: #333; + color: white; + border: 0px solid white; + border-radius: 25px; + } + + + + +

404 Not Found

+ Akemi does not know where you are headed...

+ + \ No newline at end of file diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm new file mode 100644 index 00000000..5a8c801e --- /dev/null +++ b/wled00/data/cpal/cpal.htm @@ -0,0 +1,689 @@ + + + + + + + WLED Custom Palette Editor + + + + + +
+
+

+ + + + + + + WLED Custom Palette Editor +

+
+ +
+
+
+
+
+
+ Currently in use custom palettes +
+
+
+ +
+
+ Click on the gradient editor to add new color slider, then the colored box below the slider to change its color. + Click the red box below indicator (and confirm) to delete. + Once finished, click the arrow icon to upload into the desired slot. + To edit existing palette, click the pencil icon. +
+
+
+
+
+ Available static palettes +
+
+
+ + + + + diff --git a/wled00/data/index.css b/wled00/data/index.css index d12942b6..6e68cd84 100644 --- a/wled00/data/index.css +++ b/wled00/data/index.css @@ -151,7 +151,7 @@ button { } .segt TD { - padding: 2px !important; + padding: 2px 0 !important; text-align: center; /*text-transform: uppercase;*/ } @@ -174,7 +174,10 @@ button { } .slider-icon { - transform: translate(3px,3px); + /*transform: translate(3px,3px);*/ + position: absolute; + left: 8px; + bottom: 5px; } .e-icon { @@ -314,14 +317,14 @@ button { overflow: auto; height: 100%; overscroll-behavior: none; -} - -#Effects { + padding: 0 4px; -webkit-overflow-scrolling: touch; } -#segutil, #segutil2, #segcont, #putil, #pcont, #pql { - width: 280px; +#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw, +.fnd { + max-width: 280px; + font-size: 19px; } #putil, #segutil, #segutil2 { @@ -333,7 +336,8 @@ button { padding-top: 12px; } -#pql, #segcont, #pcont { +#fx, #pql, #segcont, #pcont, #sliders, #picker, #qcs-w, #hexw, #pall, #ledmap, +.slider, .filter, .option, .segname, .pname, .fnd { margin: 0 auto; } @@ -380,8 +384,7 @@ button { } #sliders { - width: 300px; - margin: 0 auto; + position: -webkit-sticky; position: sticky; bottom: 0; } @@ -393,32 +396,44 @@ button { .slider { max-width: 300px; - min-width: 260px; - margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */ + /* margin: 5px auto; add 5px; if you want some vertical space but looks ugly */ border-radius: 24px; position: relative; padding-bottom: 2px; } +/* Slider wrapper div */ +.sliderwrap { + height: 30px; + width: 230px; + max-width: 230px; + position: relative; + z-index: 0; +} + #sliders .slider, #info .slider { background-color: var(--c-2); } +#sliders .sliderwrap, .sbs .sliderwrap { + left: 16px; /* offset for icon */ +} + .filter, .option { background-color: var(--c-4); border-radius: 26px; height: 26px; - margin: 0 auto; /* add 4-8px if you want space at the bottom */ + max-width: 300px; + /* margin: 0 auto 4px; add 4-8px if you want space at the bottom */ padding: 4px 2px; position: relative; - z-index: 1; opacity: 1; transition: opacity 0.5s linear, height 0.5s, transform 0.5s; - transform: scaleY(1); } -.option { - z-index: unset; +.filter { + z-index: 1; + overflow: hidden; } /* Tooltip text */ @@ -530,6 +545,7 @@ button { } .close { + position: -webkit-sticky; position: sticky; top: 0; float: right; @@ -575,11 +591,17 @@ button { width: 200px; } -#info table, #nodes table { +#info table { table-layout: fixed; width: 100%; } +/*WLEDMM nodes table auto*/ +#nodes table { + table-layout: auto; + width: 100%; +} + #info td, #nodes td { padding-bottom: 8px; } @@ -590,20 +612,21 @@ button { #info table .btn, #nodes table .btn { margin: 0; } -#info div, #nodes div { +#info div { max-width: 490px; margin: 0 auto; } +/*WLEDMM nodes div width must be bigger*/ +#nodes div { + max-width: 980px; + margin: 0 auto; +} + #info #imgw { - /*display: inline-block;*/ margin: 8px auto; } -/* -#kv, #kn { - display: inline-block; -} -*/ + #lv { max-width: 600px; display: inline-block; @@ -668,6 +691,7 @@ img { transition: visibility 0.25s ease, opacity 0.25s ease; opacity: 0; visibility: hidden; + left: 8px; } output.sliderbubbleshow { @@ -725,41 +749,24 @@ input[type=range]::-moz-range-thumb { border: 2px solid var(--c-1); } -/* Slider wrapper div */ -.sliderwrap { - height: 30px; - width: 230px; - position: relative; - z-index: 0; -} - #Colors .sliderwrap { - width: 260px; margin: 4px 0 0; } -/* #Colors { - padding-top: 18px; -} - */ /* Dynamically hide brightness slider label */ .hd { display: var(--bhd); } #briwrap { + min-width: 267px; float: right; margin-top: var(--bmt); } -#picker, #qcs-w, #hexw, #pall, #ledmap { - margin: 0 auto; - width: 260px; - /*background-color: unset;*/ -} - #picker { - margin-top: -10px !important; + margin-top: 8px !important; + max-width: 260px; } /* buttons */ @@ -994,14 +1001,12 @@ textarea { .segname, .pname { white-space: nowrap; - /*cursor: pointer;*/ text-align: center; overflow: clip; text-overflow: ellipsis; line-height: 24px; padding: 8px 24px; max-width: 170px; - margin: 0 auto; position: relative; } @@ -1012,7 +1017,7 @@ textarea { /* segment power wrapper */ .sbs { - padding: 4px 0 4px 8px; + padding: 1px 0 1px 20px; display: var(--sgp); } @@ -1208,10 +1213,7 @@ TD .checkmark, TD .radiomark { border: 0px solid var(--c-f); text-align: left; transition: background-color 0.5s; - /*filter: brightness(1);*/ - font-size: 19px; border-radius: 21px; - /*min-width: 264px;*/ } .seg { @@ -1254,10 +1256,8 @@ TD .checkmark, TD .radiomark { /* list wrapper */ .list { position: relative; - width: 280px; transition: background-color 0.5s; - margin: auto auto 20px; - font-size: 19px; + margin: auto auto 10px; line-height: 24px; } @@ -1267,6 +1267,7 @@ TD .checkmark, TD .radiomark { cursor: pointer; background-color: var(--c-2); overflow: hidden; + position: -webkit-sticky; position: sticky; border-radius: 21px; margin: 13px auto 0; @@ -1303,6 +1304,7 @@ TD .checkmark, TD .radiomark { .lstI.selected { top: 0; bottom: 0; + border: 1px solid var(--c-4); } .lstI.sticky, @@ -1358,8 +1360,6 @@ TD .checkmark, TD .radiomark { /* find/search element */ .fnd { - width: 280px; - margin: 0 auto; position: relative; } @@ -1391,7 +1391,6 @@ TD .checkmark, TD .radiomark { .presin { padding: 8px; position: relative; - width: 264px; } .btn-s, @@ -1469,6 +1468,16 @@ TD .checkmark, TD .radiomark { } } +@media all and (max-width: 1023px) { + .top button { + width: 8%; + padding: 10px 0 8px 0; + } + #buttonPcm { + display: none; + } +} + @media all and (max-width: 335px) { .sliderbubble { display: none; @@ -1487,38 +1496,84 @@ TD .checkmark, TD .radiomark { } } -@media all and (max-width: 540px) { - .top button { - width: 16.6%; - padding: 8px 0 4px 0; - } -} - -@media all and (min-width: 541px) and (max-width: 719px) { - .top button { - width: 14.2%; - padding: 8px 0 4px 0; - } -} - -@media all and (max-width: 719px) { - .hd { - display: none !important; - } - #briwrap { - margin-top: 0px !important; - float: none; - } -} - -@media all and (max-width: 798px) { +@media all and (max-width: 420px) { #buttonNodes { display: none; } } -@media all and (max-width: 1024px) { - #buttonPcm { +@media all and (max-width: 639px) { + .top button { + width: 16.6%; + padding: 8px 0 4px 0; + } + #briwrap { + margin: 0 auto !important; + float: none; + display: inline-block; + } + .hd { + display: none !important; + } +} + +@media all and (min-width: 420px) and (max-width: 639px) { + .top button { + width: 14.28%; + padding: 8px 0 4px 0; + } +} + +@media all and (min-width: 640px) and (max-width: 767px) { + #buttonNodes { display: none; } } + +/* small screen & tablet "PC mode" support */ +@media all and (min-width: 1024px) and (max-width: 1249px) { + #segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw, #psFind, #sliders { + width: 100%; + max-width: 280px; + font-size: 18px; + } + #picker { + width: 230px; + } + #putil .btn-s { + width: 114px; + } + #sliders .sliderbubble { + display: none; + } + .sliderwrap { + width: calc(100% - 28px); + } + #sliders .sliderwrap { + left: 12px; + } + .segname { + padding: 8px 16px; + max-width: 140px; + } + .segname, .pname { + max-width: 134px; + } + .segt TD { + padding: 0 !important; + } + input[type="number"], input[type=text], select, textarea { + font-size: 18px; + } + input[type="number"] { + width: 32px; + } + .lstIcontent { + padding-left: 8px; + } + .revchkl { + max-width: 183px; + text-overflow: ellipsis; + overflow-x: clip; + } +} diff --git a/wled00/data/index.htm b/wled00/data/index.htm index 6db80fb9..b3093816 100644 --- a/wled00/data/index.htm +++ b/wled00/data/index.htm @@ -74,8 +74,8 @@

Brightness

-
- +
+
@@ -203,6 +203,10 @@
+
+ + +
@@ -238,6 +242,10 @@ +
- +
@@ -265,7 +273,7 @@ Effect speed
- +
diff --git a/wled00/data/index.js b/wled00/data/index.js index c9c09ff1..13a59168 100644 --- a/wled00/data/index.js +++ b/wled00/data/index.js @@ -40,6 +40,7 @@ var hol = [ var ctx = null; // WLEDMM var ledmapNr = -1; //WLEDMM var ledmapFileNames = []; //WLEDMM +let extendedNodes = []; //WLEDMM function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();} function sCol(na, col) {d.documentElement.style.setProperty(na, col);} @@ -217,6 +218,7 @@ function onLoad() resetPUtil(); + if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true); applyCfg(); if (cfg.comp.hdays) { //load custom holiday list fetch((loc?`http://${locip}`:'.') + "/holidays.json", { // may be loaded from external source @@ -261,9 +263,8 @@ function onLoad() resetUtil(); d.addEventListener("visibilitychange", handleVisibilityChange, false); - size(); + //size(); gId("cv").style.opacity=0; - if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true); var sls = d.querySelectorAll('input[type="range"]'); for (var sl of sls) { sl.addEventListener('touchstart', toggleBubble); @@ -607,14 +608,19 @@ function parseInfo(i) { syncTglRecv = i.str; maxSeg = i.leds.maxseg; pmt = i.fs.pmt; + gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none"; // do we have a matrix set-up mw = i.leds.matrix ? i.leds.matrix.w : 0; mh = i.leds.matrix ? i.leds.matrix.h : 0; isM = mw>0 && mh>0; if (!isM) { + gId("filter0D").classList.remove('hide'); gId("filter1D").classList.add('hide'); - //gId("filter2D").classList.add('hide'); - hideModes("2D"); + gId("filter2D").classList.add('hide'); + } else { + gId("filter0D").classList.add('hide'); + gId("filter1D").classList.remove('hide'); + gId("filter2D").classList.remove('hide'); } // if (i.noaudio) { // gId("filterVol").classList.add("hide"); @@ -731,13 +737,13 @@ function populateSegments(s) ledmapFileNames.push((inst.n?inst.n:"default") + ".json"); //WLEDMM - let segp = `
- -
- -
-
-
`; + let segp = `
`+ + ``+ + `
`+ + ``+ + `
`+ + `
`+ + `
`; let staX = inst.start; let stoX = inst.stop; let staY = inst.startY; @@ -927,8 +933,21 @@ function populatePalettes() `
` ); } - gId('pallist').innerHTML=html; + // append custom palettes (when loading for the 1st time) + if (!isEmpty(lastinfo) && lastinfo.cpalcount) { + for (let j = 0; j
` + ); + } + } } function redrawPalPrev() @@ -991,18 +1010,16 @@ function genPalPrevCss(id) function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', effectPar = '') { - return `
- - ${extraHtml} -
`; + return `
`+ + ``+ + extraHtml + + `
`; } function btype(b) @@ -1023,17 +1040,111 @@ function bname(o) return o.name; } +//WLEDMM call a node with json api command +function callNode(ip, type, json) { + console.log("callNode", ip, json); + + fetch('http://'+ip+'/json/'+type, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + //'Content-Type': 'text/html; charset=UTF-8' + }, + body: JSON.stringify(json) + }) + .then((res)=>{ + console.log("then res", res); + loadNodes(); //reload nodes + }) + .then((json)=>{ + console.log("then json", json); + }); +} + +function ddpAll() { + ins = []; + start = 0; + order = 0; + for (var node of extendedNodes) { + console.log(node); + output = {}; + output.start = start; //increase with count + output.len = node.count; + output.pin = node.ip.split("."); + output.order = order++; + output.rev = false; + output.skip = 0; + output.type = 80; + output.ref = false; + output.rgbm = 0; + // "ins":[{"start":0,"len":24,"pin":[2],"order":0,"rev":false,"skip":0,"type":22,"ref":false,"rgbwm":0}, + // {"start":24,"len":241,"pin":[192,168,121,57],"order":1,"rev":false,"skip":0,"type":80,"ref":false,"rgbwm":0}] + ins.push(output); + start+=node.count; + } + console.log("ins", ins); + callNode("4.3.2.1", "cfg", {"hw":{"led":{"ins":ins}}}); +} + function populateNodes(i,n) { + //WLEDMM helper: add html to element + function addEl(element, html) { + let k = d.createElement(element); + k.innerHTML = html; + return k; + } + var cn=""; var urows=""; var nnodes = 0; + extendedNodes = []; //reset nodes if (n.nodes) { + //WLEDMM add this node to nodes + let thisNode = {}; + thisNode.name = i.name; + thisNode.ip = locip; //not working for ap node yet... + n.nodes.push(thisNode); + n.nodes.sort((a,b) => (a.name).localeCompare(b.name)); + // console.log("populateNodes",i,n); + //loop over nodes e.g. {name: "MM 32 L", type: 32, ip: "192.168.121.249", age: 1, vid: 2305080} for (var o of n.nodes) { if (o.name) { var url = ``; - urows += inforow(url,`${btype(o.type)}
${o.vid==0?"N/A":o.vid}`); + // urows += inforow(url,`${btype(o.type)}
${o.vid==0?"N/A":o.vid} ${o.mode}`); + + //WLEDMM fetch json from nodes and add in table rows + urows += `${url}${btype(o.type)}
${o.vid==0?"N/A":o.vid}`; + // console.log("Node", o); + if (o.ip) { //in ap mode no ip... + //fetch the rest of the nodes info + fetchAndExecute(`http://${o.ip}/`, "json", nnodes, function(nnodes,text) { + // console.log(text); + let state = JSON.parse(text)["state"]; + let info = JSON.parse(text)["info"]; + let effects = JSON.parse(text)["effects"]; + let nodeInfo = {}; + // console.log(nnodes, state, info, effects); + nodeInfo.ip = info.ip; + nodeInfo.count = info.leds.count; + //append to table row + + // gId(`node${nnodes}`).appendChild(addEl('td', ``)); + gId(`node${nnodes}`).appendChild(addEl('td', "")); + // ${i.opt&0x100?inforow("Net Print ☾",""):''} + gId(`node${nnodes}`).appendChild(addEl('td', info["ip"])); + gId(`node${nnodes}`).appendChild(addEl('td', info["rel"])); + gId(`node${nnodes}`).appendChild(addEl('td', info["ver"])); + gId(`node${nnodes}`).appendChild(addEl('td', info["leds"]["count"])); + gId(`node${nnodes}`).appendChild(addEl('td', effects[state["seg"][0]["fx"]])); + if (info["leds"]["matrix"]) + gId(`node${nnodes}`).appendChild(addEl('td', info["leds"]["matrix"]["w"] + "x" + info["leds"]["matrix"]["h"])); + if (nodeInfo.ip != thisNode.ip) + extendedNodes.push(nodeInfo); + }); + } + nnodes++; } } @@ -1041,10 +1152,11 @@ function populateNodes(i,n) if (i.ndc < 0) cn += `Instance List is disabled.`; else if (nnodes == 0) cn += `No other instances found.`; cn += ` - ${inforow("Current instance:",i.name)} ${urows}
`; + cn += "" gId('kn').innerHTML = cn; + // ${inforow("Current instance:",i.name)} //WLEDMM current instance is now also shown as node } function loadNodes() @@ -1162,6 +1274,7 @@ function updateLen(s, draw=true) //WLEDMM conditonally draw segment view gId(`seg${s}len`).innerHTML = out; + // console.log("drawSegmentView","updateLen"); if (draw && isM) drawSegmentView(); //WLEDMM draw new segmentview if something changes in a segment } @@ -1333,7 +1446,7 @@ function drawSegmentView() { else fileName = ledmapFileNames[ledmapNr-10]; - fetchAndExecute((loc?`http://${locip}`:'.') + "/", fileName , function(text) { + fetchAndExecute((loc?`http://${locip}`:'.') + "/", fileName, null, function(parms,text) { var ledmapJson = JSON.parse(text); var counter = 0; var noMap = []; @@ -1377,8 +1490,8 @@ function drawSegmentView() { ctx.fill(); } post(); - }, function(error) { //error handling - console.log(error); + }, function(parms,error) { //error handling + // console.log("drawledmap error fetching " + fileName +": ", error); // downloadGHFile("LM", fileName, true, false); WLEDMM: remove as this has too much impact post(); }); @@ -1415,7 +1528,6 @@ function updateUI() gId('buttonPower').className = (isOn) ? 'active':''; gId('buttonNl').className = (nlA) ? 'active':''; gId('buttonSync').className = (syncSend) ? 'active':''; - showNodes(); updateSelectedFx(); updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes @@ -1499,7 +1611,7 @@ function updateSelectedFx() if (fx.dataset.id>0) { if (segLmax==0) fx.classList.add('hide'); // none of the segments selected (hide all effects) else { - if (segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) fx.classList.add('hide'); + if ((segLmax==1 && (!opts[3] || opts[3].indexOf("0")<0)) || (!isM && opts[3] && ((opts[3].indexOf("2")>=0 && opts[3].indexOf("1")<0)))) fx.classList.add('hide'); else fx.classList.remove('hide'); } } @@ -1552,13 +1664,14 @@ function makeWS() { var i = json.info; if (i) { parseInfo(i); - showNodes(); if (isInfo) populateInfo(i); } else i = lastinfo; var s = json.state ? json.state : json; displayRover(i, s); readState(s); + // console.log("drawSegmentView","websocket", json); + // if (isM) drawSegmentView(); }; ws.onclose = (e)=>{ gId('connind').style.backgroundColor = "var(--c-r)"; @@ -1860,21 +1973,8 @@ function requestJson(command=null) // console.log("requestJson url return", json); //WLEDMM Debug if (json.info) { let i = json.info; - // append custom palettes (when loading for the 1st time) - if (!command && isEmpty(lastinfo) && i.cpalcount) { - for (let j = 0; j
` - ); - } - } parseInfo(i); + populatePalettes(i); if (isInfo) populateInfo(i); } var s = json.state ? json.state : json; @@ -1882,6 +1982,7 @@ function requestJson(command=null) //WLEDMM init, gfx default on upon web page load if (isM) { + // console.log("drawSegmentView","requestjson"); drawSegmentView(); toggleLiveview(); } @@ -2018,29 +2119,29 @@ function makeSeg() behavior: 'smooth', block: 'start', }); - var cn = `
-
- - - - - - - - - - - - - - - - -
${isM?'Start X':'Start LED'}${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}
Start Y${cfg.comp.seglen?'Height':'Stop Y'}
-
${ledCount - ns} LEDs
-
-
-
`; + var cn = `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
${isM?'Start X':'Start LED'}${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}
Start Y${cfg.comp.seglen?'Height':'Stop Y'}
`+ + `
${ledCount - ns} LEDs
`+ + `
`+ + `
`+ + `
`; gId('segutil').innerHTML = cn; } @@ -2954,7 +3055,7 @@ function genPresets() } //WLEDMM: utility function to load contents of file from FS (used in draw) -function fetchAndExecute(url, name, callback, callError) +function fetchAndExecute(url, name, parms, callback, callError = null) { fetch (url+name, { @@ -2968,11 +3069,10 @@ function fetchAndExecute(url, name, callback, callError) return res.text(); }) .then(text => { - console.log("text", text); - callback(text); + callback(parms, text); }) .catch(function (error) { - callError("Error getting " + name); + if (callError) callError(parms, "Error getting " + name); console.log(error); }) .finally(() => { @@ -3054,7 +3154,7 @@ function getPalettesData(page, callback) showToast(error, true); }); } - +/* function hideModes(txt) { for (let e of (gId('fxlist').querySelectorAll('.lstI')||[])) { @@ -3065,7 +3165,7 @@ function hideModes(txt) if (f) e.classList.add('hide'); //else e.classList.remove('hide'); } } - +*/ function search(f,l=null) { f.nextElementSibling.style.display=(f.value!=='')?'block':'none'; @@ -3230,20 +3330,16 @@ function move(e) x0 = null; } -function showNodes() { - gId('buttonNodes').style.display = (lastinfo.ndc > 0 && (wW > 797 || (wW > 539 && wW < 720))) ? "block":"none"; -} - function size() { wW = window.innerWidth; - showNodes(); var h = gId('top').clientHeight; sCol('--th', h + "px"); sCol('--bh', gId('bot').clientHeight + "px"); if (isLv && !isM) h -= 4; //WLEDMM: no for matrices sCol('--tp', h + "px"); togglePcMode(); + lastw = wW; } function togglePcMode(fromB = false) @@ -3251,21 +3347,18 @@ function togglePcMode(fromB = false) if (fromB) { pcModeA = !pcModeA; localStorage.setItem('pcm', pcModeA); - pcMode = pcModeA; } - if (wW <= 1024 && !pcMode) return; - if (!fromB && ((wW <= 1024 && lastw <= 1024) || (wW > 1024 && lastw > 1024))) return; + pcMode = (wW >= 1024) && pcModeA; + if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape + if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size() openTab(0, true); - if (wW <= 1024) {pcMode = false;} - else if (pcModeA && !fromB) pcMode = pcModeA; updateTablinks(0); gId('buttonPcm').className = (pcMode) ? "active":""; gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto"; sCol('--bh', gId('bot').clientHeight + "px"); _C.style.width = (pcMode)?'100%':'400%'; - lastw = wW; - //WLEDMM resize segmentview + // console.log("drawSegmentView","togglePCMode"); if (isM) drawSegmentView(); } @@ -3290,7 +3383,7 @@ function mergeDeep(target, ...sources) size(); _C.style.setProperty('--n', N); -window.addEventListener('resize', size, false); +window.addEventListener('resize', size, true); _C.addEventListener('mousedown', lock, false); _C.addEventListener('touchstart', lock, false); diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 4ec39407..96805c6c 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -126,19 +126,19 @@ let len = parseInt(d.getElementsByName("LC"+n)[0].value); len += parseInt(d.getElementsByName("SL"+n)[0].value); // skipped LEDs are allocated too if (t < 32) { + if (t==26 || t==29) len *= 2; // 16 bit LEDs if (maxM < 10000 && d.getElementsByName("L0"+n)[0].value == 3) { //8266 DMA uses 5x the mem - if (t > 29) return len*20; //RGBW + if (t > 28) return len*20; //RGBW return len*15; } else if (maxM >= 10000) //ESP32 RMT uses double buffer? { - if (t > 29) return len*8; //RGBW + if (t > 28) return len*8; //RGBW return len*6; } - if (t > 29) return len*4; //RGBW + if (t > 28) return len*4; //RGBW return len*3; } if (t > 31 && t < 48) return 5; - if (t == 44 || t == 45) return len*4; //RGBW return len*3; } @@ -190,15 +190,16 @@ if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED } gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814 - gRGBW |= isRGBW = ((t > 17 && t < 22) || t == 30 || t == 31 || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h + gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM - gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown - if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping + gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown + if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white + gId("dig"+n+"l").style.display = (t > 48 && t < 64) ? "inline":"none"; // bus clock speed gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description } @@ -336,6 +337,8 @@ ${i+1}: \ \ \ +\ +\ \ \ \ @@ -364,6 +367,7 @@ ${i+1}:
+
Start:  
Length:

diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 15092ea5..b3e4663d 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -186,6 +186,7 @@

Ethernet Type