Compare commits
144 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b5a8f3156c | ||
|
de245c08ce | ||
|
d9a96fbb29 | ||
|
41bc799c3f | ||
|
8f7f9ec367 | ||
|
21de8f2284 | ||
|
15797a89e7 | ||
|
1049d65621 | ||
|
552ae836e9 | ||
|
1852c0a88c | ||
|
d0d399bdfc | ||
|
3975fe0f02 | ||
|
0739cfc240 | ||
|
f31bf4f7c9 | ||
|
f413671915 | ||
|
ae92a5c25e | ||
|
fabd3b8421 | ||
|
94d754e63a | ||
|
f869542a47 | ||
|
2b616b688d | ||
|
0925ea6f9c | ||
|
87b5565679 | ||
|
adcecf969a | ||
|
f3831665d4 | ||
|
e1075a3bbf | ||
|
3b6f499fc5 | ||
|
2c1fbe103b | ||
|
e42ca2dbf2 | ||
|
1495b756d3 | ||
|
7f78d00b97 | ||
|
bfd58b3cdf | ||
|
31a4e38bc0 | ||
|
9c6dda9bd2 | ||
|
0232117de5 | ||
|
367a4e17a2 | ||
|
eba0d7c1e0 | ||
|
341a4a0d60 | ||
|
d116424241 | ||
|
db881ee011 | ||
|
2512bebc62 | ||
|
309dbd7585 | ||
|
a1f28caa8e | ||
|
4204393337 | ||
|
789e34702b | ||
|
47bc1bf88d | ||
|
9867227ccd | ||
|
26302d478e | ||
|
0decf94c9c | ||
|
0ae78efd43 | ||
|
b5af6b0bf9 | ||
|
3eabefd3d6 | ||
|
7ff6a6e0e7 | ||
|
94dc611024 | ||
|
0b28107432 | ||
|
fe717dad7f | ||
|
fb3816e2e5 | ||
|
535bf4c848 | ||
|
1fc60b0682 | ||
|
302f1ee8e3 | ||
|
42aec56b8e | ||
|
199c00c59b | ||
|
107bb14555 | ||
|
5820792013 | ||
|
0e20248494 | ||
|
c4f4b247bc | ||
|
0d66bc49c2 | ||
|
bdc78a826f | ||
|
6570062e61 | ||
|
820facb833 | ||
|
a933fcf7e2 | ||
|
2d3039c6a2 | ||
|
aca01044f4 | ||
|
dffd1b1d69 | ||
|
14fc64f2f1 | ||
|
04791d06d0 | ||
|
a485451328 | ||
|
c7d399c122 | ||
|
df155bed89 | ||
|
3ee3630d22 | ||
|
6b2911b8c8 | ||
|
d474200d7f | ||
|
cc6f6277f6 | ||
|
0521e988bc | ||
|
832ff39eb6 | ||
|
134798e82f | ||
|
b6f43966ea | ||
|
afa7be6fdc | ||
|
36290c20aa | ||
|
c33c7eb68e | ||
|
5da33afecd | ||
|
a49b49aaa9 | ||
|
225fd0d05b | ||
|
c43b4f9cf0 | ||
|
3581f4c87e | ||
|
7a4e0cc850 | ||
|
ba49da75de | ||
|
f329a5950e | ||
|
d95158003c | ||
|
1be6223a31 | ||
|
76178b423e | ||
|
195e537499 | ||
|
418abc2b0a | ||
|
277f0346f2 | ||
|
6148cbb122 | ||
|
4c1861dd27 | ||
|
7f6486c77d | ||
|
1b2a50f9a3 | ||
|
347ec91bfc | ||
|
86300a8e3c | ||
|
b56490650c | ||
|
8ccfb606c0 | ||
|
3b74cd5676 | ||
|
6f96dc8f23 | ||
|
76e0e935f0 | ||
|
4194e66d81 | ||
|
c9b9d86892 | ||
|
f2d00e6e42 | ||
|
acfc166a11 | ||
|
3255382132 | ||
|
25831bfb60 | ||
|
d011ca0626 | ||
|
2e28df3e8a | ||
|
dcace43ce2 | ||
|
befce5b887 | ||
|
9929c96650 | ||
|
b4aec91d67 | ||
|
d2a149ee23 | ||
|
8a72f94b3d | ||
|
ae1d0a18f3 | ||
|
9709ca331e | ||
|
b4a9641c31 | ||
|
cc68e6b6e6 | ||
|
37dc17ae15 | ||
|
cd6b3d7dee | ||
|
aec0bc5029 | ||
|
e010e67717 | ||
|
44197d91c6 | ||
|
94aeb19245 | ||
|
0d287283d4 | ||
|
ea964124d6 | ||
|
c361c34b16 | ||
|
68860ae866 | ||
|
61637f12c7 | ||
|
b4f08fa8d5 |
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -9,8 +9,8 @@
|
||||
],
|
||||
"dependsOrder": "sequence",
|
||||
"problemMatcher": [
|
||||
"$platformio",
|
||||
],
|
||||
"$platformio"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "PlatformIO",
|
||||
@ -18,7 +18,7 @@
|
||||
"task": "Build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true,
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$platformio"
|
||||
|
44
CHANGELOG.md
44
CHANGELOG.md
@ -1,5 +1,49 @@
|
||||
## WLED changelog
|
||||
|
||||
#### Build 2309120 till build 2311120
|
||||
- WLED version 0.15.0-a0
|
||||
- Spookier Halloween Eyes (#3501)
|
||||
- Compile time options for Multi Relay usermod (#3498)
|
||||
- Fix for Dissolve (#3502)
|
||||
- Better reverse proxy support (nested paths)
|
||||
- Implement global JSON API boolean toggle (i.e. instead of "var":true or "var":false -> "var":"t").
|
||||
- Sort presets by ID
|
||||
- Fix #3496
|
||||
- Improved random bg image and added random bg image options (@WoodyLetsCode, #3481)
|
||||
- Audio palettes (Audioreactive usermod, credit @netmindz)
|
||||
- Better UI tooltips (@ajotnac, #3464)
|
||||
- Better effect filters (filter dropdown)
|
||||
- Fix udp sync (fix for #3487)
|
||||
- Power button override (solves #3431)
|
||||
- Additional HTTP request throttling (ESP8266)
|
||||
- Additional UI/UX improvements
|
||||
- Segment class optimisations (internal)
|
||||
- ESP-NOW sync
|
||||
- ESP-NOW Wiz remote JSON overrides (similar to IR JSON) & bugfixes
|
||||
- Gamma correction for custom palettes (#3399).
|
||||
- Restore presets from browser local storage
|
||||
- Optional effect blending
|
||||
- Restructured UDP Sync (internal)
|
||||
- Remove sync receive
|
||||
- Sync clarification
|
||||
- Disallow 2D effects on non-2D segments
|
||||
- Return of 2 audio simulations
|
||||
- Bugfix in sync #3344 (internal)
|
||||
- remove excessive segments
|
||||
- ignore inactive segments if not syncing bounds
|
||||
- send UDP/WS on segment change
|
||||
- pop_back() when removing last segment
|
||||
|
||||
#### Build 2311160
|
||||
- Version bump: 0.14.1-b1
|
||||
- Bugfixes (#3526, #3502, #3496, #3484, #3487, #3445, #3466, #3296, #3382, #3312)
|
||||
- New feature: Sort presets by ID
|
||||
- New usermod: LDR sensor (#3490 by @JeffWDH)
|
||||
- Effect: Twinklefox & Tinklecat metadata fix
|
||||
- Effect: separate #HH and #MM for Scrolling Text (#3480)
|
||||
- SSDR usermod enhancements (#3368)
|
||||
- PWM fan usermod enhancements (#3414)
|
||||
|
||||
#### Build 2310010, build 2310130
|
||||
- Release of WLED version 0.14.0 "Hoshi"
|
||||
- Bugfixes for #3400, #3403, #3405
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0-a0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.0-a0",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
@ -181,6 +181,9 @@ lib_deps =
|
||||
IRremoteESP8266 @ 2.8.2
|
||||
makuna/NeoPixelBus @ 2.7.5
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.7
|
||||
# ESP-NOW library (includes mandatory QuickDebug library)
|
||||
; gmag11/QuickESPNow @ 0.6.2
|
||||
https://github.com/blazoncek/QuickESPNow.git#optional-debug
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For compatible OLED display uncomment following
|
||||
@ -217,7 +220,8 @@ build_flags =
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
|
||||
; decrease code cache size and increase IRAM to fit all pixel functions
|
||||
-D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
|
||||
|
||||
lib_deps =
|
||||
|
@ -53,7 +53,7 @@ See the [documentation on our official site](https://kno.wled.ge)!
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running!
|
||||
|
||||
## 🖼️ User interface
|
||||
<img src="images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="images/walking-with-iphone-x.jpg" width="50%">
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
||||
## 💾 Compatible hardware
|
||||
|
||||
|
@ -52,9 +52,15 @@ class PWMFanUsermod : public Usermod {
|
||||
uint8_t tachoUpdateSec = 30;
|
||||
float targetTemperature = 35.0;
|
||||
uint8_t minPWMValuePct = 0;
|
||||
uint8_t maxPWMValuePct = 100;
|
||||
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
|
||||
uint8_t pwmValuePct = 0;
|
||||
|
||||
// constant values
|
||||
static const uint8_t _pwmMaxValue = 255;
|
||||
static const uint8_t _pwmMaxStepCount = 7;
|
||||
float _pwmTempStepSize = 0.5f;
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
@ -63,6 +69,7 @@ class PWMFanUsermod : public Usermod {
|
||||
static const char _temperature[];
|
||||
static const char _tachoUpdateSec[];
|
||||
static const char _minPWMValuePct[];
|
||||
static const char _maxPWMValuePct[];
|
||||
static const char _IRQperRotation[];
|
||||
static const char _speed[];
|
||||
static const char _lock[];
|
||||
@ -156,31 +163,25 @@ class PWMFanUsermod : public Usermod {
|
||||
|
||||
void setFanPWMbasedOnTemperature(void) {
|
||||
float temp = getActualTemperature();
|
||||
float difftemp = temp - targetTemperature;
|
||||
// Default to run fan at full speed.
|
||||
int newPWMvalue = 255;
|
||||
int pwmStep = ((100 - minPWMValuePct) * newPWMvalue) / (7*100);
|
||||
int pwmMinimumValue = (minPWMValuePct * newPWMvalue) / 100;
|
||||
|
||||
if ((temp == NAN) || (temp <= -100.0)) {
|
||||
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
|
||||
} else if (difftemp <= 0.0) {
|
||||
// Temperature is below target temperature. Run fan at minimum speed.
|
||||
newPWMvalue = pwmMinimumValue;
|
||||
} else if (difftemp <= 0.5) {
|
||||
newPWMvalue = pwmMinimumValue + pwmStep;
|
||||
} else if (difftemp <= 1.0) {
|
||||
newPWMvalue = pwmMinimumValue + 2*pwmStep;
|
||||
} else if (difftemp <= 1.5) {
|
||||
newPWMvalue = pwmMinimumValue + 3*pwmStep;
|
||||
} else if (difftemp <= 2.0) {
|
||||
newPWMvalue = pwmMinimumValue + 4*pwmStep;
|
||||
} else if (difftemp <= 2.5) {
|
||||
newPWMvalue = pwmMinimumValue + 5*pwmStep;
|
||||
} else if (difftemp <= 3.0) {
|
||||
newPWMvalue = pwmMinimumValue + 6*pwmStep;
|
||||
// dividing minPercent and maxPercent into equal pwmvalue sizes
|
||||
int pwmStepSize = ((maxPWMValuePct - minPWMValuePct) * _pwmMaxValue) / (_pwmMaxStepCount*100);
|
||||
int pwmStep = calculatePwmStep(temp - targetTemperature);
|
||||
// minimum based on full speed - not entered MaxPercent
|
||||
int pwmMinimumValue = (minPWMValuePct * _pwmMaxValue) / 100;
|
||||
updateFanSpeed(pwmMinimumValue + pwmStep*pwmStepSize);
|
||||
}
|
||||
updateFanSpeed(newPWMvalue);
|
||||
|
||||
uint8_t calculatePwmStep(float diffTemp){
|
||||
if ((diffTemp == NAN) || (diffTemp <= -100.0)) {
|
||||
DEBUG_PRINTLN(F("WARNING: no temperature value available. Cannot do temperature control. Will set PWM fan to 255."));
|
||||
return _pwmMaxStepCount;
|
||||
}
|
||||
if(diffTemp <=0){
|
||||
return 0;
|
||||
}
|
||||
int calculatedStep = (diffTemp / _pwmTempStepSize)+1;
|
||||
// anything greater than max stepcount gets max
|
||||
return (uint8_t)min((int)_pwmMaxStepCount,calculatedStep);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -312,6 +313,7 @@ class PWMFanUsermod : public Usermod {
|
||||
top[FPSTR(_tachoUpdateSec)] = tachoUpdateSec;
|
||||
top[FPSTR(_temperature)] = targetTemperature;
|
||||
top[FPSTR(_minPWMValuePct)] = minPWMValuePct;
|
||||
top[FPSTR(_maxPWMValuePct)] = maxPWMValuePct;
|
||||
top[FPSTR(_IRQperRotation)] = numberOfInterrupsInOneSingleRotation;
|
||||
DEBUG_PRINTLN(F("Autosave config saved."));
|
||||
}
|
||||
@ -345,6 +347,8 @@ class PWMFanUsermod : public Usermod {
|
||||
targetTemperature = top[FPSTR(_temperature)] | targetTemperature;
|
||||
minPWMValuePct = top[FPSTR(_minPWMValuePct)] | minPWMValuePct;
|
||||
minPWMValuePct = (uint8_t) min(100,max(0,(int)minPWMValuePct)); // bounds checking
|
||||
maxPWMValuePct = top[FPSTR(_maxPWMValuePct)] | maxPWMValuePct;
|
||||
maxPWMValuePct = (uint8_t) min(100,max((int)minPWMValuePct,(int)maxPWMValuePct)); // bounds checking
|
||||
numberOfInterrupsInOneSingleRotation = top[FPSTR(_IRQperRotation)] | numberOfInterrupsInOneSingleRotation;
|
||||
numberOfInterrupsInOneSingleRotation = (uint8_t) max(1,(int)numberOfInterrupsInOneSingleRotation); // bounds checking
|
||||
|
||||
@ -389,6 +393,7 @@ const char PWMFanUsermod::_pwmPin[] PROGMEM = "PWM-pin";
|
||||
const char PWMFanUsermod::_temperature[] PROGMEM = "target-temp-C";
|
||||
const char PWMFanUsermod::_tachoUpdateSec[] PROGMEM = "tacho-update-s";
|
||||
const char PWMFanUsermod::_minPWMValuePct[] PROGMEM = "min-PWM-percent";
|
||||
const char PWMFanUsermod::_maxPWMValuePct[] PROGMEM = "max-PWM-percent";
|
||||
const char PWMFanUsermod::_IRQperRotation[] PROGMEM = "IRQs-per-rotation";
|
||||
const char PWMFanUsermod::_speed[] PROGMEM = "speed";
|
||||
const char PWMFanUsermod::_lock[] PROGMEM = "lock";
|
||||
|
@ -51,6 +51,8 @@
|
||||
#define PLOT_PRINTF(x...)
|
||||
#endif
|
||||
|
||||
#define MAX_PALETTES 3
|
||||
|
||||
// use audio source class (ESP32 specific)
|
||||
#include "audio_source.h"
|
||||
constexpr i2s_port_t I2S_PORT = I2S_NUM_0; // I2S port to use (do not change !)
|
||||
@ -614,6 +616,8 @@ class AudioReactive : public Usermod {
|
||||
// set your config variables to their boot default value (this can also be done in readFromConfig() or a constructor if you prefer)
|
||||
bool enabled = false;
|
||||
bool initDone = false;
|
||||
bool addPalettes = false;
|
||||
int8_t palettes = 0;
|
||||
|
||||
// variables for UDP sound sync
|
||||
WiFiUDP fftUdp; // UDP object for sound sync (from WiFi UDP, not Async UDP!)
|
||||
@ -652,10 +656,15 @@ class AudioReactive : public Usermod {
|
||||
static const char _inputLvl[];
|
||||
static const char _analogmic[];
|
||||
static const char _digitalmic[];
|
||||
static const char _addPalettes[];
|
||||
static const char UDP_SYNC_HEADER[];
|
||||
static const char UDP_SYNC_HEADER_v1[];
|
||||
|
||||
// private methods
|
||||
void removeAudioPalettes(void);
|
||||
void createAudioPalettes(void);
|
||||
CRGB getCRGBForBand(int x, int pal);
|
||||
void fillAudioPalettes(void);
|
||||
|
||||
////////////////////
|
||||
// Debug support //
|
||||
@ -1199,6 +1208,7 @@ class AudioReactive : public Usermod {
|
||||
}
|
||||
|
||||
if (enabled) connectUDPSoundSync();
|
||||
if (enabled && addPalettes) createAudioPalettes();
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
@ -1361,6 +1371,7 @@ class AudioReactive : public Usermod {
|
||||
lastTime = millis();
|
||||
}
|
||||
|
||||
fillAudioPalettes();
|
||||
}
|
||||
|
||||
|
||||
@ -1613,13 +1624,28 @@ class AudioReactive : public Usermod {
|
||||
if (usermod[FPSTR(_enabled)].is<bool>()) {
|
||||
enabled = usermod[FPSTR(_enabled)].as<bool>();
|
||||
if (prevEnabled != enabled) onUpdateBegin(!enabled);
|
||||
if (addPalettes) {
|
||||
// add/remove custom/audioreactive palettes
|
||||
if (prevEnabled && !enabled) removeAudioPalettes();
|
||||
if (!prevEnabled && enabled) createAudioPalettes();
|
||||
}
|
||||
}
|
||||
if (usermod[FPSTR(_inputLvl)].is<int>()) {
|
||||
inputLevel = min(255,max(0,usermod[FPSTR(_inputLvl)].as<int>()));
|
||||
}
|
||||
}
|
||||
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
|
||||
// handle removal of custom palettes from JSON call so we don't break things
|
||||
removeAudioPalettes();
|
||||
}
|
||||
}
|
||||
|
||||
void onStateChange(uint8_t callMode) {
|
||||
if (initDone && enabled && addPalettes && palettes==0 && strip.customPalettes.size()<10) {
|
||||
// if palettes were removed during JSON call re-add them
|
||||
createAudioPalettes();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
@ -1660,6 +1686,7 @@ class AudioReactive : public Usermod {
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_addPalettes)] = addPalettes;
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
JsonObject amic = top.createNestedObject(FPSTR(_analogmic));
|
||||
@ -1712,8 +1739,11 @@ class AudioReactive : public Usermod {
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
bool configComplete = !top.isNull();
|
||||
bool oldEnabled = enabled;
|
||||
bool oldAddPalettes = addPalettes;
|
||||
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], enabled);
|
||||
configComplete &= getJsonValue(top[FPSTR(_addPalettes)], addPalettes);
|
||||
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
configComplete &= getJsonValue(top[FPSTR(_analogmic)]["pin"], audioPin);
|
||||
@ -1747,6 +1777,11 @@ class AudioReactive : public Usermod {
|
||||
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
|
||||
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
|
||||
|
||||
if (initDone) {
|
||||
// add/remove custom/audioreactive palettes
|
||||
if ((oldAddPalettes && !addPalettes) || (oldAddPalettes && !enabled)) removeAudioPalettes();
|
||||
if ((addPalettes && !oldAddPalettes && enabled) || (addPalettes && !oldEnabled && enabled)) createAudioPalettes();
|
||||
} // else setup() will create palettes
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
@ -1822,6 +1857,92 @@ class AudioReactive : public Usermod {
|
||||
}
|
||||
};
|
||||
|
||||
void AudioReactive::removeAudioPalettes(void) {
|
||||
DEBUG_PRINTLN(F("Removing audio palettes."));
|
||||
while (palettes>0) {
|
||||
strip.customPalettes.pop_back();
|
||||
DEBUG_PRINTLN(palettes);
|
||||
palettes--;
|
||||
}
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
|
||||
}
|
||||
|
||||
void AudioReactive::createAudioPalettes(void) {
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
|
||||
if (palettes) return;
|
||||
DEBUG_PRINTLN(F("Adding audio palettes."));
|
||||
for (int i=0; i<MAX_PALETTES; i++)
|
||||
if (strip.customPalettes.size() < 10) {
|
||||
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
|
||||
palettes++;
|
||||
DEBUG_PRINTLN(palettes);
|
||||
} else break;
|
||||
}
|
||||
|
||||
// credit @netmindz ar palette, adapted for usermod @blazoncek
|
||||
CRGB AudioReactive::getCRGBForBand(int x, int pal) {
|
||||
CRGB value;
|
||||
CHSV hsv;
|
||||
int b;
|
||||
switch (pal) {
|
||||
case 2:
|
||||
b = map(x, 0, 255, 0, NUM_GEQ_CHANNELS/2); // convert palette position to lower half of freq band
|
||||
hsv = CHSV(fftResult[b], 255, x);
|
||||
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
|
||||
break;
|
||||
case 1:
|
||||
b = map(x, 1, 255, 0, 10); // convert palette position to lower half of freq band
|
||||
hsv = CHSV(fftResult[b], 255, map(fftResult[b], 0, 255, 30, 255)); // pick hue
|
||||
hsv2rgb_rainbow(hsv, value); // convert to R,G,B
|
||||
break;
|
||||
default:
|
||||
if (x == 1) {
|
||||
value = CRGB(fftResult[10]/2, fftResult[4]/2, fftResult[0]/2);
|
||||
} else if(x == 255) {
|
||||
value = CRGB(fftResult[10]/2, fftResult[0]/2, fftResult[4]/2);
|
||||
} else {
|
||||
value = CRGB(fftResult[0]/2, fftResult[4]/2, fftResult[10]/2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void AudioReactive::fillAudioPalettes() {
|
||||
if (!palettes) return;
|
||||
size_t lastCustPalette = strip.customPalettes.size();
|
||||
if (lastCustPalette >= palettes) lastCustPalette -= palettes;
|
||||
for (size_t pal=0; pal<palettes; pal++) {
|
||||
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
||||
// 3 colors = 12, 4 colors = 16, etc.
|
||||
|
||||
tcp[0] = 0; // anchor of first color - must be zero
|
||||
tcp[1] = 0;
|
||||
tcp[2] = 0;
|
||||
tcp[3] = 0;
|
||||
|
||||
CRGB rgb = getCRGBForBand(1, pal);
|
||||
tcp[4] = 1; // anchor of first color
|
||||
tcp[5] = rgb.r;
|
||||
tcp[6] = rgb.g;
|
||||
tcp[7] = rgb.b;
|
||||
|
||||
rgb = getCRGBForBand(128, pal);
|
||||
tcp[8] = 128;
|
||||
tcp[9] = rgb.r;
|
||||
tcp[10] = rgb.g;
|
||||
tcp[11] = rgb.b;
|
||||
|
||||
rgb = getCRGBForBand(255, pal);
|
||||
tcp[12] = 255; // anchor of last color - must be 255
|
||||
tcp[13] = rgb.r;
|
||||
tcp[14] = rgb.g;
|
||||
tcp[15] = rgb.b;
|
||||
|
||||
strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
|
||||
}
|
||||
}
|
||||
|
||||
// strings to reduce flash memory usage (used more than twice)
|
||||
const char AudioReactive::_name[] PROGMEM = "AudioReactive";
|
||||
const char AudioReactive::_enabled[] PROGMEM = "enabled";
|
||||
@ -1830,5 +1951,6 @@ const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
|
||||
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
|
||||
#endif
|
||||
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
|
||||
const char AudioReactive::_addPalettes[] PROGMEM = "add-palettes";
|
||||
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
|
||||
const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature
|
||||
|
@ -47,6 +47,24 @@ or
|
||||
|
||||
You can override the default maximum number of relays (which is 4) by defining MULTI_RELAY_MAX_RELAYS.
|
||||
|
||||
Some settings can be defined (defaults) at compile time by setting the following defines:
|
||||
|
||||
```cpp
|
||||
// enable or disable HA discovery for externally controlled relays
|
||||
#define MULTI_RELAY_HA_DISCOVERY true
|
||||
```
|
||||
|
||||
The following definitions should be a list of values (maximum number of entries is MULTI_RELAY_MAX_RELAYS) that will be applied to the relays in order:
|
||||
(e.g. assuming MULTI_RELAY_MAX_RELAYS=2)
|
||||
|
||||
```cpp
|
||||
#define MULTI_RELAY_PINS 12,18
|
||||
#define MULTI_RELAY_DELAYS 0,0
|
||||
#define MULTI_RELAY_EXTERNALS false,true
|
||||
#define MULTI_RELAY_INVERTS false,false
|
||||
```
|
||||
These can be set via your `platformio_override.ini` file or as `#define` in your `my_config.h` (remember to set `WLED_USE_MY_CONFIG` in your `platformio_override.ini`)
|
||||
|
||||
Example **usermods_list.cpp**:
|
||||
|
||||
```cpp
|
||||
@ -108,3 +126,6 @@ Have fun - @blazoncek
|
||||
|
||||
2023-05
|
||||
* Added support for PCF8574 I2C port expander (multiple)
|
||||
|
||||
2023-11
|
||||
* @chrisburrows Added support for compile time defaults for setting DELAY, EXTERNAL, INVERTS and HA discovery
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
|
||||
#ifndef MULTI_RELAY_MAX_RELAYS
|
||||
#define MULTI_RELAY_MAX_RELAYS 4
|
||||
#else
|
||||
@ -19,6 +21,22 @@
|
||||
#define MULTI_RELAY_ENABLED true
|
||||
#endif
|
||||
|
||||
#ifndef MULTI_RELAY_HA_DISCOVERY
|
||||
#define MULTI_RELAY_HA_DISCOVERY false
|
||||
#endif
|
||||
|
||||
#ifndef MULTI_RELAY_DELAYS
|
||||
#define MULTI_RELAY_DELAYS 0
|
||||
#endif
|
||||
|
||||
#ifndef MULTI_RELAY_EXTERNALS
|
||||
#define MULTI_RELAY_EXTERNALS false
|
||||
#endif
|
||||
|
||||
#ifndef MULTI_RELAY_INVERTS
|
||||
#define MULTI_RELAY_INVERTS false
|
||||
#endif
|
||||
|
||||
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
|
||||
|
||||
#define ON true
|
||||
@ -343,18 +361,22 @@ MultiRelay::MultiRelay()
|
||||
, initDone(false)
|
||||
, usePcf8574(USE_PCF8574)
|
||||
, addrPcf8574(PCF8574_ADDRESS)
|
||||
, HAautodiscovery(false)
|
||||
, HAautodiscovery(MULTI_RELAY_HA_DISCOVERY)
|
||||
, periodicBroadcastSec(60)
|
||||
, lastBroadcast(0)
|
||||
{
|
||||
const int8_t defPins[] = {MULTI_RELAY_PINS};
|
||||
const int8_t relayDelays[] = {MULTI_RELAY_DELAYS};
|
||||
const bool relayExternals[] = {MULTI_RELAY_EXTERNALS};
|
||||
const bool relayInverts[] = {MULTI_RELAY_INVERTS};
|
||||
|
||||
for (size_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||
_relay[i].pin = i<sizeof(defPins) ? defPins[i] : -1;
|
||||
_relay[i].delay = 0;
|
||||
_relay[i].invert = false;
|
||||
_relay[i].pin = i < COUNT_OF(defPins) ? defPins[i] : -1;
|
||||
_relay[i].delay = i < COUNT_OF(relayDelays) ? relayDelays[i] : 0;
|
||||
_relay[i].invert = i < COUNT_OF(relayInverts) ? relayInverts[i] : false;
|
||||
_relay[i].active = false;
|
||||
_relay[i].state = false;
|
||||
_relay[i].external = false;
|
||||
_relay[i].external = i < COUNT_OF(relayExternals) ? relayExternals[i] : false;
|
||||
_relay[i].button = -1;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ Enables the inverted mode in which the background should be enabled and the digi
|
||||
### Colon-blinking
|
||||
Enables the blinking colon(s) if they are defined
|
||||
|
||||
### Leading-Zero
|
||||
Shows the leading zero of the hour if it exists (i.e. shows `07` instead of `7`)
|
||||
|
||||
### enable-auto-brightness
|
||||
Enables the auto brightness feature. Can be used only when the usermod SN_Photoresistor is installed.
|
||||
|
||||
|
@ -17,6 +17,7 @@ private:
|
||||
bool umSSDRDisplayTime = false;
|
||||
bool umSSDRInverted = false;
|
||||
bool umSSDRColonblink = true;
|
||||
bool umSSDRLeadingZero = false;
|
||||
bool umSSDREnableLDR = false;
|
||||
String umSSDRHours = "";
|
||||
String umSSDRMinutes = "";
|
||||
@ -79,6 +80,7 @@ private:
|
||||
static const char _str_timeEnabled[];
|
||||
static const char _str_inverted[];
|
||||
static const char _str_colonblink[];
|
||||
static const char _str_leadingZero[];
|
||||
static const char _str_displayMask[];
|
||||
static const char _str_hours[];
|
||||
static const char _str_minutes[];
|
||||
@ -105,15 +107,15 @@ private:
|
||||
switch (umSSDRDisplayMask[index]) {
|
||||
case 'h':
|
||||
timeVar = hourFormat12(localTime);
|
||||
_showElements(&umSSDRHours, timeVar, 0, 1);
|
||||
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
|
||||
break;
|
||||
case 'H':
|
||||
timeVar = hour(localTime);
|
||||
_showElements(&umSSDRHours, timeVar, 0, 1);
|
||||
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
|
||||
break;
|
||||
case 'k':
|
||||
timeVar = hour(localTime) + 1;
|
||||
_showElements(&umSSDRHours, timeVar, 0, 0);
|
||||
_showElements(&umSSDRHours, timeVar, 0, !umSSDRLeadingZero);
|
||||
break;
|
||||
case 'm':
|
||||
timeVar = minute(localTime);
|
||||
@ -309,6 +311,9 @@ private:
|
||||
if (_cmpIntSetting_P(topic, payload, _str_colonblink, &umSSDRColonblink)) {
|
||||
return true;
|
||||
}
|
||||
if (_cmpIntSetting_P(topic, payload, _str_leadingZero, &umSSDRLeadingZero)) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp_P(topic, _str_displayMask) == 0) {
|
||||
umSSDRDisplayMask = String(payload);
|
||||
_publishMQTTstr_P(_str_displayMask, umSSDRDisplayMask);
|
||||
@ -323,6 +328,7 @@ private:
|
||||
_publishMQTTint_P(_str_ldrEnabled, umSSDREnableLDR);
|
||||
_publishMQTTint_P(_str_inverted, umSSDRInverted);
|
||||
_publishMQTTint_P(_str_colonblink, umSSDRColonblink);
|
||||
_publishMQTTint_P(_str_leadingZero, umSSDRLeadingZero);
|
||||
|
||||
_publishMQTTstr_P(_str_hours, umSSDRHours);
|
||||
_publishMQTTstr_P(_str_minutes, umSSDRMinutes);
|
||||
@ -347,6 +353,7 @@ private:
|
||||
ssdrObj[FPSTR(_str_ldrEnabled)] = umSSDREnableLDR;
|
||||
ssdrObj[FPSTR(_str_inverted)] = umSSDRInverted;
|
||||
ssdrObj[FPSTR(_str_colonblink)] = umSSDRColonblink;
|
||||
ssdrObj[FPSTR(_str_leadingZero)] = umSSDRLeadingZero;
|
||||
ssdrObj[FPSTR(_str_displayMask)] = umSSDRDisplayMask;
|
||||
ssdrObj[FPSTR(_str_hours)] = umSSDRHours;
|
||||
ssdrObj[FPSTR(_str_minutes)] = umSSDRMinutes;
|
||||
@ -425,6 +432,8 @@ public:
|
||||
invert.add(umSSDRInverted);
|
||||
JsonArray blink = user.createNestedArray("Blinking colon");
|
||||
blink.add(umSSDRColonblink);
|
||||
JsonArray zero = user.createNestedArray("Show the hour leading zero");
|
||||
zero.add(umSSDRLeadingZero);
|
||||
JsonArray ldrEnable = user.createNestedArray("Auto Brightness enabled");
|
||||
ldrEnable.add(umSSDREnableLDR);
|
||||
|
||||
@ -454,6 +463,7 @@ public:
|
||||
umSSDREnableLDR = ssdrObj[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR;
|
||||
umSSDRInverted = ssdrObj[FPSTR(_str_inverted)] | umSSDRInverted;
|
||||
umSSDRColonblink = ssdrObj[FPSTR(_str_colonblink)] | umSSDRColonblink;
|
||||
umSSDRLeadingZero = ssdrObj[FPSTR(_str_leadingZero)] | umSSDRLeadingZero;
|
||||
umSSDRDisplayMask = ssdrObj[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
||||
}
|
||||
}
|
||||
@ -516,6 +526,7 @@ public:
|
||||
umSSDREnableLDR = (top[FPSTR(_str_ldrEnabled)] | umSSDREnableLDR);
|
||||
umSSDRInverted = (top[FPSTR(_str_inverted)] | umSSDRInverted);
|
||||
umSSDRColonblink = (top[FPSTR(_str_colonblink)] | umSSDRColonblink);
|
||||
umSSDRLeadingZero = (top[FPSTR(_str_leadingZero)] | umSSDRLeadingZero);
|
||||
|
||||
umSSDRDisplayMask = top[FPSTR(_str_displayMask)] | umSSDRDisplayMask;
|
||||
umSSDRHours = top[FPSTR(_str_hours)] | umSSDRHours;
|
||||
@ -546,6 +557,7 @@ const char UsermodSSDR::_str_name[] PROGMEM = "UsermodSSDR";
|
||||
const char UsermodSSDR::_str_timeEnabled[] PROGMEM = "enabled";
|
||||
const char UsermodSSDR::_str_inverted[] PROGMEM = "inverted";
|
||||
const char UsermodSSDR::_str_colonblink[] PROGMEM = "Colon-blinking";
|
||||
const char UsermodSSDR::_str_leadingZero[] PROGMEM = "Leading-Zero";
|
||||
const char UsermodSSDR::_str_displayMask[] PROGMEM = "Display-Mask";
|
||||
const char UsermodSSDR::_str_hours[] PROGMEM = "LED-Numbers-Hours";
|
||||
const char UsermodSSDR::_str_minutes[] PROGMEM = "LED-Numbers-Minutes";
|
||||
|
337
wled00/FX.cpp
337
wled00/FX.cpp
@ -177,11 +177,11 @@ uint16_t color_wipe(bool rev, bool useRandomColors) {
|
||||
SEGENV.step = 3;
|
||||
}
|
||||
if (SEGENV.step == 1) { //if flag set, change to new random color
|
||||
SEGENV.aux1 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
|
||||
SEGENV.aux1 = get_random_wheel_index(SEGENV.aux0);
|
||||
SEGENV.step = 2;
|
||||
}
|
||||
if (SEGENV.step == 3) {
|
||||
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux1);
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux1);
|
||||
SEGENV.step = 0;
|
||||
}
|
||||
}
|
||||
@ -271,7 +271,7 @@ uint16_t mode_random_color(void) {
|
||||
if (it != SEGENV.step) //new color
|
||||
{
|
||||
SEGENV.aux1 = SEGENV.aux0;
|
||||
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0); //aux0 will store our random color wheel index
|
||||
SEGENV.step = it;
|
||||
}
|
||||
|
||||
@ -587,7 +587,7 @@ uint16_t mode_twinkle(void) {
|
||||
|
||||
uint16_t PRNG16 = SEGENV.aux1;
|
||||
|
||||
for (uint16_t i = 0; i < SEGENV.aux0; i++)
|
||||
for (unsigned i = 0; i < SEGENV.aux0; i++)
|
||||
{
|
||||
PRNG16 = (uint16_t)(PRNG16 * 2053) + 13849; // next 'random' number
|
||||
uint32_t p = (uint32_t)SEGLEN * (uint32_t)PRNG16;
|
||||
@ -604,22 +604,36 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
|
||||
* Dissolve function
|
||||
*/
|
||||
uint16_t dissolve(uint32_t color) {
|
||||
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
if (SEGENV.call == 0) {
|
||||
memset(SEGMENT.data, 0xFF, dataSize); // start by fading pixels up
|
||||
SEGENV.aux0 = 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j <= SEGLEN / 15; j++) {
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
for (size_t times = 0; times < 10; times++) //attempt to spawn a new pixel 10 times
|
||||
{
|
||||
uint16_t i = random16(SEGLEN);
|
||||
for (size_t times = 0; times < 10; times++) { //attempt to spawn a new pixel 10 times
|
||||
unsigned i = random16(SEGLEN);
|
||||
unsigned index = i >> 3;
|
||||
unsigned bitNum = i & 0x07;
|
||||
bool fadeUp = bitRead(SEGENV.data[index], bitNum);
|
||||
if (SEGENV.aux0) { //dissolve to primary/palette
|
||||
if (SEGMENT.getPixelColor(i) == SEGCOLOR(1) /*|| wa*/) {
|
||||
if (fadeUp) {
|
||||
if (color == SEGCOLOR(0)) {
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
} else {
|
||||
SEGMENT.setPixelColor(i, color);
|
||||
}
|
||||
bitWrite(SEGENV.data[index], bitNum, false);
|
||||
break; //only spawn 1 new pixel per frame per 50 LEDs
|
||||
}
|
||||
} else { //dissolve to secondary
|
||||
if (SEGMENT.getPixelColor(i) != SEGCOLOR(1)) { SEGMENT.setPixelColor(i, SEGCOLOR(1)); break; }
|
||||
if (!fadeUp) {
|
||||
SEGMENT.setPixelColor(i, SEGCOLOR(1)); break;
|
||||
bitWrite(SEGENV.data[index], bitNum, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -628,6 +642,7 @@ uint16_t dissolve(uint32_t color) {
|
||||
if (SEGENV.step > (255 - SEGMENT.speed) + 15U) {
|
||||
SEGENV.aux0 = !SEGENV.aux0;
|
||||
SEGENV.step = 0;
|
||||
memset(SEGMENT.data, (SEGENV.aux0 ? 0xFF : 0), dataSize); // switch fading
|
||||
} else {
|
||||
SEGENV.step++;
|
||||
}
|
||||
@ -681,7 +696,7 @@ static const char _data_FX_MODE_SPARKLE[] PROGMEM = "Sparkle@!,,,,,,Overlay;!,!;
|
||||
* Inspired by www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/
|
||||
*/
|
||||
uint16_t mode_flash_sparkle(void) {
|
||||
if (!SEGMENT.check2) for(uint16_t i = 0; i < SEGLEN; i++) {
|
||||
if (!SEGMENT.check2) for (int i = 0; i < SEGLEN; i++) {
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
}
|
||||
|
||||
@ -816,7 +831,7 @@ uint16_t chase(uint32_t color1, uint32_t color2, uint32_t color3, bool do_palett
|
||||
if (a < SEGENV.step) //we hit the start again, choose new color for Chase random
|
||||
{
|
||||
SEGENV.aux1 = SEGENV.aux0; //store previous random color
|
||||
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
|
||||
}
|
||||
color1 = SEGMENT.color_wheel(SEGENV.aux0);
|
||||
}
|
||||
@ -1056,7 +1071,7 @@ uint16_t mode_chase_flash_random(void) {
|
||||
SEGENV.aux1 = (SEGENV.aux1 + 1) % SEGLEN;
|
||||
|
||||
if (SEGENV.aux1 == 0) {
|
||||
SEGENV.aux0 = SEGMENT.get_random_wheel_index(SEGENV.aux0);
|
||||
SEGENV.aux0 = get_random_wheel_index(SEGENV.aux0);
|
||||
}
|
||||
}
|
||||
return delay;
|
||||
@ -2182,7 +2197,7 @@ uint16_t mode_colortwinkle() {
|
||||
CRGB fastled_col, prev;
|
||||
fract8 fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness();
|
||||
fract8 fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness();
|
||||
for (uint16_t i = 0; i < SEGLEN; i++) {
|
||||
for (int i = 0; i < SEGLEN; i++) {
|
||||
fastled_col = SEGMENT.getPixelColor(i);
|
||||
prev = fastled_col;
|
||||
uint16_t index = i >> 3;
|
||||
@ -2209,9 +2224,9 @@ uint16_t mode_colortwinkle() {
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t j = 0; j <= SEGLEN / 50; j++) {
|
||||
for (unsigned j = 0; j <= SEGLEN / 50; j++) {
|
||||
if (random8() <= SEGMENT.intensity) {
|
||||
for (uint8_t times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
|
||||
for (unsigned times = 0; times < 5; times++) { //attempt to spawn a new pixel 5 times
|
||||
int i = random16(SEGLEN);
|
||||
if (SEGMENT.getPixelColor(i) == 0) {
|
||||
fastled_col = ColorFromPalette(SEGPALETTE, random8(), 64, NOBLEND);
|
||||
@ -2590,19 +2605,38 @@ uint16_t mode_twinklefox()
|
||||
{
|
||||
return twinklefox_base(false);
|
||||
}
|
||||
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;;!";
|
||||
static const char _data_FX_MODE_TWINKLEFOX[] PROGMEM = "Twinklefox@!,Twinkle rate,,,,Cool;!,!;!";
|
||||
|
||||
|
||||
uint16_t mode_twinklecat()
|
||||
{
|
||||
return twinklefox_base(true);
|
||||
}
|
||||
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;;!";
|
||||
static const char _data_FX_MODE_TWINKLECAT[] PROGMEM = "Twinklecat@!,Twinkle rate,,,,Cool;!,!;!";
|
||||
|
||||
|
||||
//inspired by https://www.tweaking4all.com/hardware/arduino/adruino-led-strip-effects/#LEDStripEffectBlinkingHalloweenEyes
|
||||
uint16_t mode_halloween_eyes()
|
||||
{
|
||||
enum eyeState : uint8_t {
|
||||
initializeOn = 0,
|
||||
on,
|
||||
blink,
|
||||
initializeOff,
|
||||
off,
|
||||
|
||||
count
|
||||
};
|
||||
struct EyeData {
|
||||
eyeState state;
|
||||
uint8_t color;
|
||||
uint16_t startPos;
|
||||
// duration + endTime could theoretically be replaced by a single endTime, however we would lose
|
||||
// the ability to end the animation early when the user reduces the animation time.
|
||||
uint16_t duration;
|
||||
uint32_t startTime;
|
||||
uint32_t blinkEndTime;
|
||||
};
|
||||
|
||||
if (SEGLEN == 1) return mode_static();
|
||||
const uint16_t maxWidth = strip.isMatrix ? SEGMENT.virtualWidth() : SEGLEN;
|
||||
const uint16_t HALLOWEEN_EYE_SPACE = MAX(2, strip.isMatrix ? SEGMENT.virtualWidth()>>4: SEGLEN>>5);
|
||||
@ -2610,57 +2644,132 @@ uint16_t mode_halloween_eyes()
|
||||
uint16_t eyeLength = (2*HALLOWEEN_EYE_WIDTH) + HALLOWEEN_EYE_SPACE;
|
||||
if (eyeLength >= maxWidth) return mode_static(); //bail if segment too short
|
||||
|
||||
if (!SEGENV.allocateData(sizeof(EyeData))) return mode_static(); //allocation failed
|
||||
EyeData& data = *reinterpret_cast<EyeData*>(SEGENV.data);
|
||||
|
||||
if (!SEGMENT.check2) SEGMENT.fill(SEGCOLOR(1)); //fill background
|
||||
|
||||
uint8_t state = SEGENV.aux1 >> 8;
|
||||
uint16_t stateTime = SEGENV.call;
|
||||
if (stateTime == 0) stateTime = 2000;
|
||||
data.state = static_cast<eyeState>(data.state % eyeState::count);
|
||||
uint16_t duration = max(uint16_t{1u}, data.duration);
|
||||
const uint32_t elapsedTime = strip.now - data.startTime;
|
||||
|
||||
if (state == 0) { //spawn eyes
|
||||
SEGENV.aux0 = random16(0, maxWidth - eyeLength - 1); //start pos
|
||||
SEGENV.aux1 = random8(); //color
|
||||
switch (data.state) {
|
||||
case eyeState::initializeOn: {
|
||||
// initialize the eyes-on state:
|
||||
// - select eye position and color
|
||||
// - select a duration
|
||||
// - immediately switch to eyes on state.
|
||||
|
||||
data.startPos = random16(0, maxWidth - eyeLength - 1);
|
||||
data.color = random8();
|
||||
if (strip.isMatrix) SEGMENT.offset = random16(SEGMENT.virtualHeight()-1); // a hack: reuse offset since it is not used in matrices
|
||||
state = 1;
|
||||
duration = 128u + random16(SEGMENT.intensity*64u);
|
||||
data.duration = duration;
|
||||
data.state = eyeState::on;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case eyeState::on: {
|
||||
// eyes-on steate:
|
||||
// - fade eyes in for some time
|
||||
// - keep eyes on until the pre-selected duration is over
|
||||
// - randomly switch to the blink (sub-)state, and initialize it with a blink duration (more precisely, a blink end time stamp)
|
||||
// - never switch to the blink state if the animation just started or is about to end
|
||||
|
||||
uint16_t start2ndEye = data.startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
|
||||
// If the user reduces the input while in this state, limit the duration.
|
||||
duration = min(duration, static_cast<uint16_t>(128u + (SEGMENT.intensity * 64u)));
|
||||
|
||||
constexpr uint32_t minimumOnTimeBegin = 1024u;
|
||||
constexpr uint32_t minimumOnTimeEnd = 1024u;
|
||||
const uint32_t fadeInAnimationState = elapsedTime * uint32_t{256u * 8u} / duration;
|
||||
const uint32_t backgroundColor = SEGCOLOR(1);
|
||||
const uint32_t eyeColor = SEGMENT.color_from_palette(data.color, false, false, 0);
|
||||
uint32_t c = eyeColor;
|
||||
if (fadeInAnimationState < 256u) {
|
||||
c = color_blend(backgroundColor, eyeColor, fadeInAnimationState);
|
||||
} else if (elapsedTime > minimumOnTimeBegin) {
|
||||
const uint32_t remainingTime = (elapsedTime >= duration) ? 0u : (duration - elapsedTime);
|
||||
if (remainingTime > minimumOnTimeEnd) {
|
||||
if (random8() < 4u)
|
||||
{
|
||||
c = backgroundColor;
|
||||
data.state = eyeState::blink;
|
||||
data.blinkEndTime = strip.now + random8(8, 128);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state < 2) { //fade eyes
|
||||
uint16_t startPos = SEGENV.aux0;
|
||||
uint16_t start2ndEye = startPos + HALLOWEEN_EYE_WIDTH + HALLOWEEN_EYE_SPACE;
|
||||
|
||||
uint32_t fadestage = (strip.now - SEGENV.step)*255 / stateTime;
|
||||
if (fadestage > 255) fadestage = 255;
|
||||
uint32_t c = color_blend(SEGMENT.color_from_palette(SEGENV.aux1 & 0xFF, false, false, 0), SEGCOLOR(1), fadestage);
|
||||
|
||||
if (c != backgroundColor) {
|
||||
// render eyes
|
||||
for (int i = 0; i < HALLOWEEN_EYE_WIDTH; i++) {
|
||||
if (strip.isMatrix) {
|
||||
SEGMENT.setPixelColorXY(startPos + i, SEGMENT.offset, c);
|
||||
SEGMENT.setPixelColorXY(data.startPos + i, SEGMENT.offset, c);
|
||||
SEGMENT.setPixelColorXY(start2ndEye + i, SEGMENT.offset, c);
|
||||
} else {
|
||||
SEGMENT.setPixelColor(startPos + i, c);
|
||||
SEGMENT.setPixelColor(data.startPos + i, c);
|
||||
SEGMENT.setPixelColor(start2ndEye + i, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strip.now - SEGENV.step > stateTime) {
|
||||
state++;
|
||||
if (state > 2) state = 0;
|
||||
|
||||
if (state < 2) {
|
||||
stateTime = 100 + SEGMENT.intensity*10; //eye fade time
|
||||
} else {
|
||||
uint16_t eyeOffTimeBase = (256 - SEGMENT.speed)*10;
|
||||
stateTime = eyeOffTimeBase + random16(eyeOffTimeBase);
|
||||
break;
|
||||
}
|
||||
case eyeState::blink: {
|
||||
// eyes-on but currently blinking state:
|
||||
// - wait until the blink time is over, then switch back to eyes-on
|
||||
|
||||
if (strip.now >= data.blinkEndTime) {
|
||||
data.state = eyeState::on;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eyeState::initializeOff: {
|
||||
// initialize eyes-off state:
|
||||
// - select a duration
|
||||
// - immediately switch to eyes-off state
|
||||
|
||||
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
|
||||
duration = eyeOffTimeBase + random16(eyeOffTimeBase);
|
||||
data.duration = duration;
|
||||
data.state = eyeState::off;
|
||||
[[fallthrough]];
|
||||
}
|
||||
case eyeState::off: {
|
||||
// eyes-off state:
|
||||
// - not much to do here
|
||||
|
||||
// If the user reduces the input while in this state, limit the duration.
|
||||
const uint16_t eyeOffTimeBase = SEGMENT.speed*128u;
|
||||
duration = min(duration, static_cast<uint16_t>(2u * eyeOffTimeBase));
|
||||
break;
|
||||
}
|
||||
case eyeState::count: {
|
||||
// Can't happen, not an actual state.
|
||||
data.state = eyeState::initializeOn;
|
||||
break;
|
||||
}
|
||||
SEGENV.step = strip.now;
|
||||
SEGENV.call = stateTime;
|
||||
}
|
||||
|
||||
SEGENV.aux1 = (SEGENV.aux1 & 0xFF) + (state << 8); //save state
|
||||
if (elapsedTime > duration) {
|
||||
// The current state duration is over, switch to the next state.
|
||||
switch (data.state) {
|
||||
case eyeState::initializeOn:
|
||||
case eyeState::on:
|
||||
case eyeState::blink:
|
||||
data.state = eyeState::initializeOff;
|
||||
break;
|
||||
case eyeState::initializeOff:
|
||||
case eyeState::off:
|
||||
case eyeState::count:
|
||||
default:
|
||||
data.state = eyeState::initializeOn;
|
||||
break;
|
||||
}
|
||||
data.startTime = strip.now;
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Duration,Eye fade time,,,,,Overlay;!,!;!;12";
|
||||
static const char _data_FX_MODE_HALLOWEEN_EYES[] PROGMEM = "Halloween Eyes@Eye off time,Eye on time,,,,,Overlay;!,!;!;12";
|
||||
|
||||
|
||||
//Speed slider sets amount of LEDs lit, intensity sets unlit
|
||||
@ -4661,7 +4770,7 @@ static const char _data_FX_MODE_FLOWSTRIPE[] PROGMEM = "Flow Stripe@Hue speed,Ef
|
||||
|
||||
// Black hole
|
||||
uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4695,7 +4804,7 @@ static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Ou
|
||||
// 2D Colored Bursts //
|
||||
////////////////////////////
|
||||
uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.soulmatelights.com/gallery/819-colored-bursts , modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4747,7 +4856,7 @@ static const char _data_FX_MODE_2DCOLOREDBURSTS[] PROGMEM = "Colored Bursts@Spee
|
||||
// 2D DNA //
|
||||
/////////////////////
|
||||
uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pastebin.com/pCkkkzcs. Updated by Preyy. WLED conversion by Andrew Tuline.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4768,7 +4877,7 @@ static const char _data_FX_MODE_2DDNA[] PROGMEM = "DNA@Scroll speed,Blur;;!;2";
|
||||
// 2D DNA Spiral //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4813,7 +4922,7 @@ static const char _data_FX_MODE_2DDNASPIRAL[] PROGMEM = "DNA Spiral@Scroll speed
|
||||
// 2D Drift //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmatelights.com/gallery/884-drift , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4839,7 +4948,7 @@ static const char _data_FX_MODE_2DDRIFT[] PROGMEM = "Drift@Rotation speed,Blur a
|
||||
// 2D Firenoise //
|
||||
//////////////////////////
|
||||
uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline. Yet another short routine.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4873,7 +4982,7 @@ static const char _data_FX_MODE_2DFIRENOISE[] PROGMEM = "Firenoise@X scale,Y sca
|
||||
// 2D Frizzles //
|
||||
//////////////////////////////
|
||||
uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.soulmatelights.com/gallery/640-color-frizzles , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -4900,7 +5009,7 @@ typedef struct ColorCount {
|
||||
} colorCount;
|
||||
|
||||
uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https://natureofcode.com/book/chapter-7-cellular-automata/ and https://github.com/DougHaber/nlife-color
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5006,7 +5115,7 @@ static const char _data_FX_MODE_2DGAMEOFLIFE[] PROGMEM = "Game Of Life@!;!,!;!;2
|
||||
// 2D Hiphotic //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DHiphotic() { // By: ldirko https://editor.soulmatelights.com/gallery/810 , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5038,7 +5147,7 @@ typedef struct Julia {
|
||||
} julia;
|
||||
|
||||
uint16_t mode_2DJulia(void) { // An animated Julia set by Andrew Tuline.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5144,7 +5253,7 @@ static const char _data_FX_MODE_2DJULIA[] PROGMEM = "Julia@,Max iterations per p
|
||||
// 2D Lissajous //
|
||||
//////////////////////////////
|
||||
uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5172,71 +5281,67 @@ static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,F
|
||||
// 2D Matrix //
|
||||
///////////////////////
|
||||
uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams. Adapted by Andrew Tuline & improved by merkisoft and ewowi, and softhack007.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
|
||||
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED for trails
|
||||
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
|
||||
|
||||
if (SEGENV.call == 0) {
|
||||
memset(SEGMENT.data, 0, dataSize); // no falling spawns
|
||||
SEGMENT.fill(BLACK);
|
||||
SEGENV.aux0 = SEGENV.aux1 = UINT16_MAX;
|
||||
SEGENV.step = 0;
|
||||
}
|
||||
|
||||
uint8_t fade = map(SEGMENT.custom1, 0, 255, 50, 250); // equals trail size
|
||||
uint8_t speed = (256-SEGMENT.speed) >> map(MIN(rows, 150), 0, 150, 0, 3); // slower speeds for small displays
|
||||
|
||||
CRGB spawnColor;
|
||||
CRGB trailColor;
|
||||
uint32_t spawnColor;
|
||||
uint32_t trailColor;
|
||||
if (SEGMENT.check1) {
|
||||
spawnColor = SEGCOLOR(0);
|
||||
trailColor = SEGCOLOR(1);
|
||||
} else {
|
||||
spawnColor = CRGB(175,255,175);
|
||||
trailColor = CRGB(27,130,39);
|
||||
spawnColor = RGBW32(175,255,175,0);
|
||||
trailColor = RGBW32(27,130,39,0);
|
||||
}
|
||||
|
||||
bool emptyScreen = true;
|
||||
if (strip.now - SEGENV.step >= speed) {
|
||||
SEGENV.step = strip.now;
|
||||
// find out what color value is returned by gPC for a "falling code" example pixel
|
||||
// the color values returned may differ from the previously set values, due to
|
||||
// - auto brightness limiter (dimming)
|
||||
// - lossy color buffer (when not using global buffer)
|
||||
// - color balance correction
|
||||
// - segment opacity
|
||||
CRGB oldSpawnColor = spawnColor;
|
||||
if ((SEGENV.aux0 < cols) && (SEGENV.aux1 < rows)) { // we have a hint from last run
|
||||
oldSpawnColor = SEGMENT.getPixelColorXY(SEGENV.aux0, SEGENV.aux1); // find color of previous spawns
|
||||
SEGENV.aux1 ++; // our sample pixel will be one row down the next time
|
||||
}
|
||||
if ((oldSpawnColor == CRGB::Black) || (oldSpawnColor == trailColor)) oldSpawnColor = spawnColor; // reject "black", as it would mean that ALL pixels create trails
|
||||
|
||||
// move pixels one row down. Falling codes keep color and add trail pixels; all others pixels are faded
|
||||
// TODO: it would be better to paint trails idividually instead of relying on fadeToBlackBy()
|
||||
SEGMENT.fadeToBlackBy(fade);
|
||||
for (int row = rows-1; row >= 0; row--) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
CRGB pix = SEGMENT.getPixelColorXY(col, row);
|
||||
if (pix == oldSpawnColor) { // this comparison may still fail due to overlays changing pixels, or due to gaps (2d-gaps.json)
|
||||
unsigned index = XY(col, row) >> 3;
|
||||
unsigned bitNum = XY(col, row) & 0x07;
|
||||
if (bitRead(SEGENV.data[index], bitNum)) {
|
||||
SEGMENT.setPixelColorXY(col, row, trailColor); // create trail
|
||||
if (row < rows-1) SEGMENT.setPixelColorXY(col, row+1, spawnColor);
|
||||
} else {
|
||||
// fade other pixels
|
||||
if (pix != CRGB::Black) SEGMENT.setPixelColorXY(col, row, pix.nscale8(fade)); // optimization: don't fade black pixels
|
||||
bitClear(SEGENV.data[index], bitNum);
|
||||
if (row < rows-1) {
|
||||
SEGMENT.setPixelColorXY(col, row+1, spawnColor);
|
||||
index = XY(col, row+1) >> 3;
|
||||
bitNum = XY(col, row+1) & 0x07;
|
||||
bitSet(SEGENV.data[index], bitNum);
|
||||
emptyScreen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for empty screen to ensure code spawn
|
||||
bool emptyScreen = (SEGENV.aux1 >= rows); // empty screen means that the last falling code has moved out of screen area
|
||||
|
||||
// spawn new falling code
|
||||
if (random8() <= SEGMENT.intensity || emptyScreen) {
|
||||
uint8_t spawnX = random8(cols);
|
||||
SEGMENT.setPixelColorXY(spawnX, 0, spawnColor);
|
||||
// update hint for next run
|
||||
SEGENV.aux0 = spawnX;
|
||||
SEGENV.aux1 = 0;
|
||||
unsigned index = XY(spawnX, 0) >> 3;
|
||||
unsigned bitNum = XY(spawnX, 0) & 0x07;
|
||||
bitSet(SEGENV.data[index], bitNum);
|
||||
}
|
||||
}
|
||||
} // if millis
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_2Dmatrix()
|
||||
@ -5247,7 +5352,7 @@ static const char _data_FX_MODE_2DMATRIX[] PROGMEM = "Matrix@!,Spawning rate,Tra
|
||||
// 2D Metaballs //
|
||||
/////////////////////////
|
||||
uint16_t mode_2Dmetaballs(void) { // Metaballs by Stefan Petrick. Cannot have one of the dimensions be 2 or less. Adapted by Andrew Tuline.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5306,7 +5411,7 @@ static const char _data_FX_MODE_2DMETABALLS[] PROGMEM = "Metaballs@!;;!;2";
|
||||
// 2D Noise //
|
||||
//////////////////////
|
||||
uint16_t mode_2Dnoise(void) { // By Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5329,7 +5434,7 @@ static const char _data_FX_MODE_2DNOISE[] PROGMEM = "Noise2D@!,Scale;;!;2";
|
||||
// 2D Plasma Ball //
|
||||
//////////////////////////////
|
||||
uint16_t mode_2DPlasmaball(void) { // By: Stepko https://editor.soulmatelights.com/gallery/659-plasm-ball , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5369,7 +5474,7 @@ static const char _data_FX_MODE_2DPLASMABALL[] PROGMEM = "Plasma Ball@Speed,,Fad
|
||||
// return (out_max - out_min) * (x - in_min) / (in_max - in_min) + out_min;
|
||||
//}
|
||||
uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https://editor.soulmatelights.com/gallery/762-polar-lights , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5420,7 +5525,7 @@ static const char _data_FX_MODE_2DPOLARLIGHTS[] PROGMEM = "Polar Lights@!,Scale;
|
||||
// 2D Pulser //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DPulser(void) { // By: ldirko https://editor.soulmatelights.com/gallery/878-pulse-test , modifed by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5442,7 +5547,7 @@ static const char _data_FX_MODE_2DPULSER[] PROGMEM = "Pulser@!,Blur;;!;2";
|
||||
// 2D Sindots //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DSindots(void) { // By: ldirko https://editor.soulmatelights.com/gallery/597-sin-dots , modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5473,7 +5578,7 @@ static const char _data_FX_MODE_2DSINDOTS[] PROGMEM = "Sindots@!,Dot distance,Fa
|
||||
// custom3 affects the blur amount.
|
||||
uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://gist.github.com/kriegsman/368b316c55221134b160
|
||||
// Modifed by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5507,7 +5612,7 @@ static const char _data_FX_MODE_2DSQUAREDSWIRL[] PROGMEM = "Squared Swirl@,,,,Bl
|
||||
// 2D Sun Radiation //
|
||||
//////////////////////////////
|
||||
uint16_t mode_2DSunradiation(void) { // By: ldirko https://editor.soulmatelights.com/gallery/599-sun-radiation , modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5557,7 +5662,7 @@ static const char _data_FX_MODE_2DSUNRADIATION[] PROGMEM = "Sun Radiation@Varian
|
||||
// 2D Tartan //
|
||||
/////////////////////////
|
||||
uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.soulmatelights.com/gallery/3-tartan , Modified by: Andrew Tuline
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5596,7 +5701,7 @@ static const char _data_FX_MODE_2DTARTAN[] PROGMEM = "Tartan@X scale,Y scale,,,S
|
||||
// 2D spaceships //
|
||||
/////////////////////////
|
||||
uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [https://editor.soulmatelights.com/gallery/639-space-ships], adapted by Blaz Kristan (AKA blazoncek)
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5639,7 +5744,7 @@ static const char _data_FX_MODE_2DSPACESHIPS[] PROGMEM = "Spaceships@!,Blur;;!;2
|
||||
//// Crazy bees by stepko (c)12.02.21 [https://editor.soulmatelights.com/gallery/651-crazy-bees], adapted by Blaz Kristan (AKA blazoncek)
|
||||
#define MAX_BEES 5
|
||||
uint16_t mode_2Dcrazybees(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5711,7 +5816,7 @@ static const char _data_FX_MODE_2DCRAZYBEES[] PROGMEM = "Crazy Bees@!,Blur;;;2";
|
||||
//// Ghost Rider by stepko (c)2021 [https://editor.soulmatelights.com/gallery/716-ghost-rider], adapted by Blaz Kristan (AKA blazoncek)
|
||||
#define LIGHTERS_AM 64 // max lighters (adequate for 32x32 matrix)
|
||||
uint16_t mode_2Dghostrider(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5801,7 +5906,7 @@ static const char _data_FX_MODE_2DGHOSTRIDER[] PROGMEM = "Ghost Rider@Fade rate,
|
||||
//// Floating Blobs by stepko (c)2021 [https://editor.soulmatelights.com/gallery/573-blobs], adapted by Blaz Kristan (AKA blazoncek)
|
||||
#define MAX_BLOBS 8
|
||||
uint16_t mode_2Dfloatingblobs(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5899,7 +6004,7 @@ static const char _data_FX_MODE_2DBLOBS[] PROGMEM = "Blobs@!,# blobs,Blur,Trail;
|
||||
// 2D Scrolling text //
|
||||
////////////////////////////
|
||||
uint16_t mode_2Dscrollingtext(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -5946,6 +6051,8 @@ uint16_t mode_2Dscrollingtext(void) {
|
||||
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
|
||||
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
|
||||
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
|
||||
else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), AmPmHour);
|
||||
else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf_P(text, zero?PSTR("%02d") :PSTR("%d"), minute(localTime));
|
||||
}
|
||||
|
||||
const int numberOfLetters = strlen(text);
|
||||
@ -5999,7 +6106,7 @@ static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Off
|
||||
////////////////////////////
|
||||
//// Drift Rose by stepko (c)2021 [https://editor.soulmatelights.com/gallery/1369-drift-rose-pattern], adapted by Blaz Kristan (AKA blazoncek)
|
||||
uint16_t mode_2Ddriftrose(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -6153,7 +6260,7 @@ static const char _data_FX_MODE_RIPPLEPEAK[] PROGMEM = "Ripple Peak@Fade rate,Ma
|
||||
/////////////////////////
|
||||
// By: Mark Kriegsman https://gist.github.com/kriegsman/5adca44e14ad025e6d3b , modified by Andrew Tuline
|
||||
uint16_t mode_2DSwirl(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -6197,7 +6304,7 @@ static const char _data_FX_MODE_2DSWIRL[] PROGMEM = "Swirl@!,Sensitivity,Blur;,B
|
||||
/////////////////////////
|
||||
// By: Stepko, https://editor.soulmatelights.com/gallery/652-wave , modified by Andrew Tuline
|
||||
uint16_t mode_2DWaverly(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7184,7 +7291,7 @@ static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,
|
||||
// ** 2D GEQ //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const int NUM_BANDS = map(SEGMENT.custom1, 0, 255, 1, 16);
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
@ -7242,7 +7349,7 @@ static const char _data_FX_MODE_2DGEQ[] PROGMEM = "GEQ@Fade speed,Ripple decay,#
|
||||
// ** 2D Funky plank //
|
||||
/////////////////////////
|
||||
uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Will Tatam.
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7334,7 +7441,7 @@ static uint8_t akemi[] PROGMEM = {
|
||||
};
|
||||
|
||||
uint16_t mode_2DAkemi(void) {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7402,7 +7509,7 @@ static const char _data_FX_MODE_2DAKEMI[] PROGMEM = "Akemi@Color speed,Dance;Hea
|
||||
// https://editor.soulmatelights.com/gallery/1089-distorsion-waves
|
||||
// adapted for WLED by @blazoncek
|
||||
uint16_t mode_2Ddistortionwaves() {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7457,7 +7564,7 @@ static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@
|
||||
//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
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7569,7 +7676,7 @@ static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2";
|
||||
//Stepko and Sutaburosu
|
||||
// adapted for WLED by @blazoncek
|
||||
uint16_t mode_2Doctopus() {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
@ -7625,7 +7732,7 @@ static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offse
|
||||
//@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells)
|
||||
// adapted for WLED by @blazoncek
|
||||
uint16_t mode_2Dwavingcell() {
|
||||
if (!strip.isMatrix) return mode_static(); // not a 2D set-up
|
||||
if (!strip.isMatrix || !SEGMENT.is2D()) return mode_static(); // not a 2D set-up
|
||||
|
||||
const uint16_t cols = SEGMENT.virtualWidth();
|
||||
const uint16_t rows = SEGMENT.virtualHeight();
|
||||
|
173
wled00/FX.h
173
wled00/FX.h
@ -109,20 +109,15 @@
|
||||
#define PINK (uint32_t)0xFF1493
|
||||
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
|
||||
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
|
||||
#define DARKSLATEGREY (uint32_t)0x2F4F4F
|
||||
#define DARKSLATEGREY DARKSLATEGRAY
|
||||
|
||||
// options
|
||||
// bit 7: segment is in transition mode
|
||||
// bits 4-6: TBD
|
||||
// bit 3: mirror effect within segment
|
||||
// bit 2: segment is on
|
||||
// bit 1: reverse segment
|
||||
// bit 0: segment is selected
|
||||
// segment options
|
||||
#define NO_OPTIONS (uint16_t)0x0000
|
||||
#define TRANSPOSED (uint16_t)0x0400 // rotated 90deg & reversed
|
||||
#define REVERSE_Y_2D (uint16_t)0x0200
|
||||
#define MIRROR_Y_2D (uint16_t)0x0100
|
||||
#define TRANSITIONAL (uint16_t)0x0080
|
||||
#define TRANSPOSED (uint16_t)0x0100 // rotated 90deg & reversed
|
||||
#define MIRROR_Y_2D (uint16_t)0x0080
|
||||
#define REVERSE_Y_2D (uint16_t)0x0040
|
||||
#define RESET_REQ (uint16_t)0x0020
|
||||
#define FROZEN (uint16_t)0x0010
|
||||
#define MIRROR (uint16_t)0x0008
|
||||
#define SEGMENT_ON (uint16_t)0x0004
|
||||
#define REVERSE (uint16_t)0x0002
|
||||
@ -348,12 +343,11 @@ typedef struct Segment {
|
||||
bool mirror : 1; // 3 : mirrored
|
||||
bool freeze : 1; // 4 : paused/frozen
|
||||
bool reset : 1; // 5 : indicates that Segment runtime requires reset
|
||||
bool transitional: 1; // 6 : transitional (there is transition occuring)
|
||||
bool reverse_y : 1; // 7 : reversed Y (2D)
|
||||
bool mirror_y : 1; // 8 : mirrored Y (2D)
|
||||
bool transpose : 1; // 9 : transposed (2D, swapped X & Y)
|
||||
uint8_t map1D2D : 3; // 10-12 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
|
||||
uint8_t soundSim : 1; // 13 : 0-1 sound simulation types ("soft" & "hard" or "on"/"off")
|
||||
bool reverse_y : 1; // 6 : reversed Y (2D)
|
||||
bool mirror_y : 1; // 7 : mirrored Y (2D)
|
||||
bool transpose : 1; // 8 : transposed (2D, swapped X & Y)
|
||||
uint8_t map1D2D : 3; // 9-11 : mapping for 1D effect on 2D (0-use as strip, 1-expand vertically, 2-circular/arc, 3-rectangular/corner, ...)
|
||||
uint8_t soundSim : 2; // 12-13 : 0-3 sound simulation types ("soft" & "hard" or "on"/"off")
|
||||
uint8_t set : 2; // 14-15 : 0-3 UI segment sets/groups
|
||||
};
|
||||
};
|
||||
@ -484,7 +478,6 @@ typedef struct Segment {
|
||||
_dataLen(0),
|
||||
_t(nullptr)
|
||||
{
|
||||
//refreshLightCapabilities();
|
||||
#ifdef WLED_DEBUG
|
||||
//Serial.printf("-- Creating segment: %p\n", this);
|
||||
#endif
|
||||
@ -519,6 +512,7 @@ typedef struct Segment {
|
||||
|
||||
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
|
||||
inline bool isSelected(void) const { return selected; }
|
||||
inline bool isInTransition(void) const { return _t != nullptr; }
|
||||
inline bool isActive(void) const { return stop > start; }
|
||||
inline bool is2D(void) const { return (width()>1 && height()>1); }
|
||||
inline bool hasRGB(void) const { return _isRGB; }
|
||||
@ -569,33 +563,33 @@ typedef struct Segment {
|
||||
void restoreSegenv(tmpsegd_t &tmpSegD);
|
||||
#endif
|
||||
uint16_t progress(void); //transition progression between 0-65535
|
||||
uint8_t currentBri(uint8_t briNew, bool useCct = false);
|
||||
uint8_t currentMode(uint8_t modeNew);
|
||||
uint32_t currentColor(uint8_t slot, uint32_t colorNew);
|
||||
uint8_t currentBri(bool useCct = false);
|
||||
uint8_t currentMode(void);
|
||||
uint32_t currentColor(uint8_t slot);
|
||||
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal);
|
||||
CRGBPalette16 ¤tPalette(CRGBPalette16 &tgt, uint8_t paletteID);
|
||||
|
||||
// 1D strip
|
||||
uint16_t virtualLength(void) const;
|
||||
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
|
||||
void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
|
||||
void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
|
||||
inline void setPixelColor(unsigned n, uint32_t c) { setPixelColor(int(n), c); }
|
||||
inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); }
|
||||
void setPixelColor(float i, uint32_t c, bool aa = true);
|
||||
void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
|
||||
void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
uint32_t getPixelColor(int i);
|
||||
// 1D support functions (some implement 2D as well)
|
||||
void blur(uint8_t);
|
||||
void fill(uint32_t c);
|
||||
void fade_out(uint8_t r);
|
||||
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, 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);
|
||||
inline void blendPixelColor(int n, uint32_t color, uint8_t blend) { setPixelColor(n, color_blend(getPixelColor(n), color, blend)); }
|
||||
inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
inline void addPixelColor(int n, uint32_t color, bool fast = false) { setPixelColor(n, color_add(getPixelColor(n), color, fast)); }
|
||||
inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); }
|
||||
inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); }
|
||||
inline void fadePixelColor(uint16_t n, uint8_t fade) { setPixelColor(n, color_fade(getPixelColor(n), fade, true)); }
|
||||
uint32_t color_from_palette(uint16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
|
||||
uint32_t color_wheel(uint8_t pos);
|
||||
|
||||
@ -606,19 +600,20 @@ typedef struct Segment {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
uint16_t XY(uint16_t x, uint16_t y); // support function to get relative index within segment
|
||||
void setPixelColorXY(int x, int y, uint32_t c); // set relative pixel within segment with color
|
||||
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
|
||||
void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
|
||||
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
|
||||
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
|
||||
void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
|
||||
void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
uint32_t getPixelColorXY(uint16_t x, uint16_t y);
|
||||
// 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, 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);
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) { setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color, fast)); }
|
||||
inline 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); }
|
||||
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
|
||||
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), fade, true)); }
|
||||
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);
|
||||
@ -628,43 +623,43 @@ typedef struct Segment {
|
||||
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);
|
||||
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0);
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
|
||||
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
|
||||
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
|
||||
void blur2d(fract8 blur_amount) { blur(blur_amount); }
|
||||
void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
|
||||
inline void blur2d(fract8 blur_amount) { blur(blur_amount); }
|
||||
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
|
||||
void nscale8(uint8_t scale);
|
||||
#else
|
||||
uint16_t XY(uint16_t x, uint16_t y) { return x; }
|
||||
void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
|
||||
void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
|
||||
void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
|
||||
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
|
||||
void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
|
||||
void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
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, 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, 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) {}
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
|
||||
void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
|
||||
inline uint16_t XY(uint16_t x, uint16_t y) { return x; }
|
||||
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
|
||||
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
|
||||
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
|
||||
inline void setPixelColorXY(float x, float y, CRGB c, bool aa = true) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0), aa); }
|
||||
inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(x); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t c, uint8_t blend) { blendPixelColor(x, c, blend); }
|
||||
inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColor(x, RGBW32(c.r,c.g,c.b,0), blend); }
|
||||
inline void addPixelColorXY(int x, int y, uint32_t color, bool fast = false) { addPixelColor(x, color, fast); }
|
||||
inline 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); }
|
||||
inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColor(x, RGBW32(c.r,c.g,c.b,0), fast); }
|
||||
inline void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) { fadePixelColor(x, fade); }
|
||||
inline void box_blur(uint16_t i, bool vertical, fract8 blur_amount) {}
|
||||
inline void blurRow(uint16_t row, fract8 blur_amount) {}
|
||||
inline void blurCol(uint16_t col, fract8 blur_amount) {}
|
||||
inline void moveX(int8_t delta, bool wrap = false) {}
|
||||
inline void moveY(int8_t delta, bool wrap = false) {}
|
||||
inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
|
||||
inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
|
||||
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
|
||||
inline void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
|
||||
#endif
|
||||
} segment;
|
||||
//static int segSize = sizeof(Segment);
|
||||
@ -759,23 +754,22 @@ class WS2812FX { // 96 bytes
|
||||
setCCT(uint16_t k),
|
||||
setBrightness(uint8_t b, bool direct = false),
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
setTransitionMode(bool t),
|
||||
purgeSegments(bool force = false),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 1, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=1),
|
||||
setMainSegmentId(uint8_t n),
|
||||
restartRuntime(),
|
||||
resetSegments(),
|
||||
makeAutoSegments(bool forceReset = false),
|
||||
fixInvalidSegments(),
|
||||
setPixelColor(int n, uint32_t c),
|
||||
show(void),
|
||||
setTargetFps(uint8_t fps);
|
||||
|
||||
void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
|
||||
void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
|
||||
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
|
||||
void setupEffectData(void); // add default effects to the list; defined in FX.cpp
|
||||
setTargetFps(uint8_t fps),
|
||||
addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name), // add effect to the list; defined in FX.cpp
|
||||
setupEffectData(void); // add default effects to the list; defined in FX.cpp
|
||||
|
||||
inline void restartRuntime() { for (Segment &seg : _segments) seg.markForReset(); }
|
||||
inline void setTransitionMode(bool t) { for (Segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0); }
|
||||
inline void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
|
||||
inline void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
|
||||
// outsmart the compiler :) by correctly overloading
|
||||
inline void setPixelColor(int n, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColor(int n, CRGB c) { setPixelColor(n, c.red, c.green, c.blue); }
|
||||
@ -881,16 +875,14 @@ class WS2812FX { // 96 bytes
|
||||
std::vector<Panel> panel;
|
||||
#endif
|
||||
|
||||
void
|
||||
setUpMatrix(),
|
||||
setPixelColorXY(int x, int y, uint32_t c);
|
||||
void setUpMatrix();
|
||||
|
||||
// outsmart the compiler :) by correctly overloading
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } // automatically inline
|
||||
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(y * Segment::maxWidth + x, c); }
|
||||
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
|
||||
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
|
||||
|
||||
uint32_t
|
||||
getPixelColorXY(uint16_t, uint16_t);
|
||||
inline uint32_t getPixelColorXY(uint16_t x, uint16_t y) { return getPixelColor(isMatrix ? y * Segment::maxWidth + x : x);}
|
||||
|
||||
// end 2D support
|
||||
|
||||
@ -941,9 +933,6 @@ class WS2812FX { // 96 bytes
|
||||
uint8_t _qGrouping, _qSpacing;
|
||||
uint16_t _qOffset;
|
||||
|
||||
uint8_t
|
||||
estimateCurrentAndLimitBri(void);
|
||||
|
||||
void
|
||||
setUpSegmentFromQueuedChanges(void);
|
||||
};
|
||||
|
@ -134,7 +134,7 @@ void WS2812FX::setUpMatrix() {
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("Matrix ledmap:"));
|
||||
for (uint16_t i=0; i<customMappingSize; i++) {
|
||||
for (unsigned i=0; i<customMappingSize; i++) {
|
||||
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
|
||||
DEBUG_PRINTF("%4d,", customMappingTable[i]);
|
||||
}
|
||||
@ -155,31 +155,6 @@ void WS2812FX::setUpMatrix() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// absolute matrix version of setPixelColor()
|
||||
void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
|
||||
{
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (!isMatrix) return; // not a matrix set-up
|
||||
uint16_t index = y * Segment::maxWidth + x;
|
||||
#else
|
||||
uint16_t index = x;
|
||||
#endif
|
||||
if (index < customMappingSize) index = customMappingTable[index];
|
||||
if (index >= _length) return;
|
||||
busses.setPixelColor(index, col);
|
||||
}
|
||||
|
||||
// returns RGBW values of pixel
|
||||
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
uint16_t index = (y * Segment::maxWidth + x);
|
||||
#else
|
||||
uint16_t index = x;
|
||||
#endif
|
||||
if (index < customMappingSize) index = customMappingTable[index];
|
||||
if (index >= _length) return 0;
|
||||
return busses.getPixelColor(index);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Segment:: routines
|
||||
@ -188,18 +163,19 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
|
||||
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
|
||||
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
|
||||
uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y)
|
||||
{
|
||||
uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
|
||||
return isActive() ? (x%width) + (y%height) * width : 0;
|
||||
}
|
||||
|
||||
void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
{
|
||||
if (!isActive()) return; // not active
|
||||
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
|
||||
|
||||
uint8_t _bri_t = currentBri(on ? opacity : 0);
|
||||
uint8_t _bri_t = currentBri();
|
||||
if (_bri_t < 255) {
|
||||
byte r = scale8(R(col), _bri_t);
|
||||
byte g = scale8(G(col), _bri_t);
|
||||
@ -289,7 +265,7 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
|
||||
}
|
||||
|
||||
// returns RGBW values of pixel
|
||||
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
uint32_t IRAM_ATTR Segment::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
if (!isActive()) return 0; // not active
|
||||
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
|
||||
if (reverse ) x = virtualWidth() - x - 1;
|
||||
@ -301,41 +277,9 @@ uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
|
||||
return strip.getPixelColorXY(start + x, startY + y);
|
||||
}
|
||||
|
||||
// Blends the specified color with the existing pixel color.
|
||||
void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) {
|
||||
setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend));
|
||||
}
|
||||
|
||||
// Adds the specified color with the existing pixel color perserving color balance.
|
||||
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
|
||||
if (!isActive()) return; // not active
|
||||
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
|
||||
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) {
|
||||
if (!isActive()) return; // not active
|
||||
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
|
||||
setPixelColorXY(x, y, pix);
|
||||
}
|
||||
|
||||
// blurRow: perform a blur on a row of a rectangular matrix
|
||||
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
|
||||
@ -344,7 +288,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
for (uint_fast16_t x = 0; x < cols; x++) {
|
||||
for (unsigned x = 0; x < cols; x++) {
|
||||
CRGB cur = getPixelColorXY(x, row);
|
||||
CRGB before = cur; // remember color before blur
|
||||
CRGB part = cur;
|
||||
@ -363,7 +307,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
|
||||
|
||||
// blurCol: perform a blur on a column of a rectangular matrix
|
||||
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
|
||||
@ -372,7 +316,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
for (uint_fast16_t y = 0; y < rows; y++) {
|
||||
for (unsigned y = 0; y < rows; y++) {
|
||||
CRGB cur = getPixelColorXY(col, y);
|
||||
CRGB part = cur;
|
||||
CRGB before = cur; // remember color before blur
|
||||
@ -391,7 +335,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
|
||||
|
||||
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
|
||||
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
const uint16_t dim1 = vertical ? rows : cols;
|
||||
@ -401,7 +345,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
|
||||
const float keep = 3.f - 2.f*seep;
|
||||
// 1D box blur
|
||||
CRGB tmp[dim1];
|
||||
for (uint16_t j = 0; j < dim1; j++) {
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
uint16_t x = vertical ? i : j;
|
||||
uint16_t y = vertical ? j : i;
|
||||
int16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
|
||||
@ -417,7 +361,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
|
||||
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
|
||||
tmp[j] = CRGB(r,g,b);
|
||||
}
|
||||
for (uint16_t j = 0; j < dim1; j++) {
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
uint16_t x = vertical ? i : j;
|
||||
uint16_t y = vertical ? j : i;
|
||||
setPixelColorXY(x, y, tmp[j]);
|
||||
@ -440,7 +384,7 @@ void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
|
||||
|
||||
void Segment::blur1d(fract8 blur_amount) {
|
||||
const uint16_t rows = virtualHeight();
|
||||
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
|
||||
for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount);
|
||||
}
|
||||
|
||||
void Segment::moveX(int8_t delta, bool wrap) {
|
||||
@ -498,7 +442,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
|
||||
}
|
||||
|
||||
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || radius == 0) return; // not active
|
||||
// Bresenham’s Algorithm
|
||||
int d = 3 - (2*radius);
|
||||
int y = radius, x = 0;
|
||||
@ -523,7 +467,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
|
||||
|
||||
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
|
||||
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
|
||||
if (!isActive()) return; // not active
|
||||
if (!isActive() || radius == 0) return; // not active
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
for (int16_t y = -radius; y <= radius; y++) {
|
||||
@ -540,7 +484,7 @@ void Segment::nscale8(uint8_t scale) {
|
||||
if (!isActive()) return; // not active
|
||||
const uint16_t cols = virtualWidth();
|
||||
const uint16_t rows = virtualHeight();
|
||||
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
|
||||
}
|
||||
}
|
||||
|
@ -89,25 +89,22 @@ bool Segment::_modeBlend = false;
|
||||
Segment::Segment(const Segment &orig) {
|
||||
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
transitional = false; // copied segment cannot be in transition
|
||||
_t = nullptr; // copied segment cannot be in transition
|
||||
name = nullptr;
|
||||
data = nullptr;
|
||||
_dataLen = 0;
|
||||
_t = nullptr;
|
||||
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
|
||||
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
|
||||
//if (orig._t) { _t = new Transition(orig._t->_dur); }
|
||||
}
|
||||
|
||||
// move constructor
|
||||
Segment::Segment(Segment &&orig) noexcept {
|
||||
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.transitional = false; // old segment cannot be in transition any more
|
||||
orig._t = nullptr; // old segment cannot be in transition any more
|
||||
orig.name = nullptr;
|
||||
orig.data = nullptr;
|
||||
orig._dataLen = 0;
|
||||
orig._t = nullptr;
|
||||
}
|
||||
|
||||
// copy assignment
|
||||
@ -115,27 +112,17 @@ Segment& Segment::operator= (const Segment &orig) {
|
||||
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
|
||||
if (this != &orig) {
|
||||
// clean destination
|
||||
transitional = false; // copied segment cannot be in transition
|
||||
if (name) delete[] name;
|
||||
if (_t) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT) free(_t->_segT._dataT);
|
||||
#endif
|
||||
delete _t;
|
||||
}
|
||||
if (name) { delete[] name; name = nullptr; }
|
||||
stopTransition();
|
||||
deallocateData();
|
||||
// copy source
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
transitional = false;
|
||||
// erase pointers to allocated data
|
||||
name = nullptr;
|
||||
data = nullptr;
|
||||
_dataLen = 0;
|
||||
_t = nullptr;
|
||||
// copy source data
|
||||
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
|
||||
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
|
||||
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@ -144,27 +131,19 @@ Segment& Segment::operator= (const Segment &orig) {
|
||||
Segment& Segment::operator= (Segment &&orig) noexcept {
|
||||
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
|
||||
if (this != &orig) {
|
||||
transitional = false; // just temporary
|
||||
if (name) { delete[] name; name = nullptr; } // free old name
|
||||
stopTransition();
|
||||
deallocateData(); // free old runtime data
|
||||
if (_t) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT) free(_t->_segT._dataT);
|
||||
#endif
|
||||
delete _t;
|
||||
_t = nullptr;
|
||||
}
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.transitional = false; // old segment cannot be in transition
|
||||
orig.name = nullptr;
|
||||
orig.data = nullptr;
|
||||
orig._dataLen = 0;
|
||||
orig._t = nullptr;
|
||||
orig._t = nullptr; // old segment cannot be in transition
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Segment::allocateData(size_t len) {
|
||||
bool IRAM_ATTR Segment::allocateData(size_t len) {
|
||||
if (data && _dataLen == len) return true; //already allocated
|
||||
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
|
||||
deallocateData();
|
||||
@ -185,7 +164,7 @@ bool Segment::allocateData(size_t len) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Segment::deallocateData() {
|
||||
void IRAM_ATTR Segment::deallocateData() {
|
||||
if (!data) { _dataLen = 0; return; }
|
||||
//DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data);
|
||||
if ((Segment::getUsedSegmentData() > 0) && (_dataLen > 0)) { // check that we don't have a dangling / inconsistent data pointer
|
||||
@ -217,7 +196,7 @@ void Segment::resetIfRequired() {
|
||||
reset = false;
|
||||
}
|
||||
|
||||
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
CRGBPalette16 IRAM_ATTR &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
if (pal < 245 && pal > GRADIENT_PALETTE_COUNT+13) pal = 0;
|
||||
if (pal > 245 && (strip.customPalettes.size() == 0 || 255U-pal > strip.customPalettes.size()-1)) pal = 0;
|
||||
//default palette. Differs depending on effect
|
||||
@ -237,7 +216,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
switch (pal) {
|
||||
case 0: //default palette. Exceptions for specific effects above
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
|
||||
case 1: {//periodically replace palette with a random one
|
||||
unsigned long timeSinceLastChange = millis() - _lastPaletteChange;
|
||||
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
|
||||
_randomPalette = _newRandomPalette;
|
||||
@ -301,50 +280,48 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
}
|
||||
|
||||
void Segment::startTransition(uint16_t dur) {
|
||||
if (!dur) {
|
||||
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
|
||||
else transitional = false;
|
||||
if (dur == 0) {
|
||||
if (isInTransition()) _t->_dur = dur; // this will stop transition in next handleTransisiton()
|
||||
return;
|
||||
}
|
||||
if (transitional && _t) return; // already in transition no need to store anything
|
||||
if (isInTransition()) return; // already in transition no need to store anything
|
||||
|
||||
// starting a transition has to occur before change so we get current values 1st
|
||||
_t = new Transition(dur); // no previous transition running
|
||||
if (!_t) return; // failed to allocate data
|
||||
|
||||
//DEBUG_PRINTF("-- Started transition: %p\n", this);
|
||||
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
|
||||
_t->_palT = _palT;
|
||||
loadPalette(_t->_palT, palette);
|
||||
_t->_briT = on ? opacity : 0;
|
||||
_t->_cctT = cct;
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (modeBlending) {
|
||||
swapSegenv(_t->_segT);
|
||||
_t->_modeT = mode;
|
||||
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
|
||||
_t->_segT._dataLenT = 0;
|
||||
_t->_segT._dataT = nullptr;
|
||||
if (_dataLen > 0 && data) {
|
||||
_t->_segT._dataT = (byte *)malloc(_dataLen);
|
||||
if (_t->_segT._dataT) {
|
||||
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
|
||||
//DEBUG_PRINTF("-- Allocated duplicate data (%d) for %p: %p\n", _dataLen, this, _t->_segT._dataT);
|
||||
memcpy(_t->_segT._dataT, data, _dataLen);
|
||||
_t->_segT._dataLenT = _dataLen;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<NUM_COLORS; i++) _t->_segT._colorT[i] = colors[i];
|
||||
}
|
||||
#else
|
||||
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
|
||||
#endif
|
||||
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
|
||||
}
|
||||
|
||||
void Segment::stopTransition() {
|
||||
if (!transitional) return;
|
||||
transitional = false; // finish transitioning segment
|
||||
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
|
||||
if (_t) {
|
||||
if (isInTransition()) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
|
||||
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
|
||||
//DEBUG_PRINTF("-- Released duplicate data (%d) for %p: %p\n", _t->_segT._dataLenT, this, _t->_segT._dataT);
|
||||
free(_t->_segT._dataT);
|
||||
_t->_segT._dataT = nullptr;
|
||||
_t->_segT._dataLenT = 0;
|
||||
@ -356,14 +333,13 @@ void Segment::stopTransition() {
|
||||
}
|
||||
|
||||
void Segment::handleTransition() {
|
||||
if (!transitional) return;
|
||||
uint16_t _progress = progress();
|
||||
if (_progress == 0xFFFFU) stopTransition();
|
||||
}
|
||||
|
||||
// transition progression between 0-65535
|
||||
uint16_t Segment::progress() {
|
||||
if (transitional && _t) {
|
||||
uint16_t IRAM_ATTR Segment::progress() {
|
||||
if (isInTransition()) {
|
||||
unsigned long timeNow = millis();
|
||||
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
|
||||
}
|
||||
@ -420,8 +396,8 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
|
||||
_t->_segT._stepT = step;
|
||||
_t->_segT._callT = call;
|
||||
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
|
||||
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
|
||||
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
|
||||
_t->_segT._dataT = data;
|
||||
_t->_segT._dataLenT = _dataLen;
|
||||
}
|
||||
options = tmpSeg._optionsT;
|
||||
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
|
||||
@ -443,39 +419,40 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
|
||||
uint8_t IRAM_ATTR Segment::currentBri(bool useCct) {
|
||||
uint32_t prog = progress();
|
||||
if (prog < 0xFFFFU) {
|
||||
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
|
||||
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
|
||||
uint32_t curBri = (useCct ? cct : (on ? opacity : 0)) * prog;
|
||||
curBri += (useCct ? _t->_cctT : (on ? _t->_briT : 0)) * (0xFFFFU - prog);
|
||||
return curBri / 0xFFFFU;
|
||||
}
|
||||
return briNew;
|
||||
return (useCct ? cct : (on ? opacity : 0));
|
||||
}
|
||||
|
||||
uint8_t Segment::currentMode(uint8_t newMode) {
|
||||
uint8_t IRAM_ATTR Segment::currentMode() {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
|
||||
if (prog < 0xFFFFU) return _t->_modeT;
|
||||
uint16_t prog = progress();
|
||||
if (modeBlending && prog < 0xFFFFU) return _t->_modeT;
|
||||
#endif
|
||||
return newMode;
|
||||
return mode;
|
||||
}
|
||||
|
||||
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
|
||||
uint32_t IRAM_ATTR Segment::currentColor(uint8_t slot) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
|
||||
return isInTransition() ? color_blend(_t->_segT._colorT[slot], colors[slot], progress(), true) : colors[slot];
|
||||
#else
|
||||
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
|
||||
return isInTransition() ? color_blend(_t->_colorT[slot], colors[slot], progress(), true) : colors[slot];
|
||||
#endif
|
||||
}
|
||||
|
||||
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
CRGBPalette16 IRAM_ATTR &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
loadPalette(targetPalette, pal);
|
||||
if (progress() < 0xFFFFU) {
|
||||
uint16_t prog = progress();
|
||||
if (strip.paletteFade && prog < 0xFFFFU) {
|
||||
// blend palettes
|
||||
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
|
||||
// minimum blend time is 100ms maximum is 65535ms
|
||||
unsigned long timeMS = millis() - _t->_start;
|
||||
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
|
||||
uint16_t noOfBlends = ((255U * prog) / 0xFFFFU) - _t->_prevPaletteBlends;
|
||||
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
|
||||
targetPalette = _t->_palT; // copy transitioning/temporary palette
|
||||
}
|
||||
@ -500,6 +477,8 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
||||
&& (!grp || (grouping == grp && spacing == spc))
|
||||
&& (ofs == UINT16_MAX || ofs == offset)) return;
|
||||
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
|
||||
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
|
||||
if (grp) { // prevent assignment of 0
|
||||
grouping = grp;
|
||||
@ -510,6 +489,10 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
|
||||
}
|
||||
if (ofs < UINT16_MAX) offset = ofs;
|
||||
|
||||
DEBUG_PRINT(F("setUp segment: ")); DEBUG_PRINT(i1);
|
||||
DEBUG_PRINT(','); DEBUG_PRINT(i2);
|
||||
DEBUG_PRINT(F(" -> ")); DEBUG_PRINT(i1Y);
|
||||
DEBUG_PRINT(','); DEBUG_PRINTLN(i2Y);
|
||||
markForReset();
|
||||
if (boundsUnchanged) return;
|
||||
|
||||
@ -564,7 +547,6 @@ void Segment::setCCT(uint16_t k) {
|
||||
void Segment::setOpacity(uint8_t o) {
|
||||
if (opacity == o) return;
|
||||
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
|
||||
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
|
||||
opacity = o;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
@ -574,14 +556,16 @@ void Segment::setOption(uint8_t n, bool val) {
|
||||
if (fadeTransition && n == SEG_OPTION_ON && val != prevOn) startTransition(strip.getTransition()); // start transition prior to change
|
||||
if (val) options |= 0x01 << n;
|
||||
else options &= ~(0x01 << n);
|
||||
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast
|
||||
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET)) stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
|
||||
void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
// if we have a valid mode & is not reserved
|
||||
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
|
||||
if (fx != mode) {
|
||||
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (modeBlending) startTransition(strip.getTransition()); // set effect transitions
|
||||
#endif
|
||||
mode = fx;
|
||||
// load default values from effect string
|
||||
if (loadDefaults) {
|
||||
@ -595,7 +579,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
|
||||
sOpt = extractModeDefaults(fx, "o3"); check3 = (sOpt >= 0) ? (bool)sOpt : false;
|
||||
sOpt = extractModeDefaults(fx, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
|
||||
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 1);
|
||||
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 3);
|
||||
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
|
||||
@ -619,21 +603,21 @@ void Segment::setPalette(uint8_t pal) {
|
||||
}
|
||||
|
||||
// 2D matrix
|
||||
uint16_t Segment::virtualWidth() const {
|
||||
uint16_t IRAM_ATTR Segment::virtualWidth() const {
|
||||
uint16_t groupLen = groupLength();
|
||||
uint16_t vWidth = ((transpose ? height() : width()) + groupLen - 1) / groupLen;
|
||||
if (mirror) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vWidth;
|
||||
}
|
||||
|
||||
uint16_t Segment::virtualHeight() const {
|
||||
uint16_t IRAM_ATTR Segment::virtualHeight() const {
|
||||
uint16_t groupLen = groupLength();
|
||||
uint16_t vHeight = ((transpose ? width() : height()) + groupLen - 1) / groupLen;
|
||||
if (mirror_y) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
|
||||
return vHeight;
|
||||
}
|
||||
|
||||
uint16_t Segment::nrOfVStrips() const {
|
||||
uint16_t IRAM_ATTR Segment::nrOfVStrips() const {
|
||||
uint16_t vLen = 1;
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
@ -648,7 +632,7 @@ uint16_t Segment::nrOfVStrips() const {
|
||||
}
|
||||
|
||||
// 1D strip
|
||||
uint16_t Segment::virtualLength() const {
|
||||
uint16_t IRAM_ATTR Segment::virtualLength() const {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
uint16_t vW = virtualWidth();
|
||||
@ -743,7 +727,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
#endif
|
||||
|
||||
uint16_t len = length();
|
||||
uint8_t _bri_t = currentBri(on ? opacity : 0);
|
||||
uint8_t _bri_t = currentBri();
|
||||
if (_bri_t < 255) {
|
||||
byte r = scale8(R(col), _bri_t);
|
||||
byte g = scale8(G(col), _bri_t);
|
||||
@ -820,7 +804,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Segment::getPixelColor(int i)
|
||||
uint32_t IRAM_ATTR Segment::getPixelColor(int i)
|
||||
{
|
||||
if (!isActive()) return 0; // not active
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@ -877,10 +861,11 @@ uint8_t Segment::differs(Segment& b) const {
|
||||
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
|
||||
|
||||
//bit pattern: (msb first) set:2, sound:1, mapping:3, transposed, mirrorY, reverseY, [transitional, reset,] paused, mirrored, on, reverse, [selected]
|
||||
if ((options & 0b1111111110011110U) != (b.options & 0b1111111110011110U)) d |= SEG_DIFFERS_OPT;
|
||||
//bit pattern: (msb first)
|
||||
// set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected]
|
||||
if ((options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT;
|
||||
if ((options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
|
||||
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
for (unsigned i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
|
||||
return d;
|
||||
}
|
||||
@ -912,7 +897,7 @@ void Segment::refreshLightCapabilities() {
|
||||
segStopIdx = stop;
|
||||
}
|
||||
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
for (unsigned b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
if (!bus->isOk()) continue;
|
||||
@ -942,43 +927,12 @@ void Segment::fill(uint32_t c) {
|
||||
if (!isActive()) return; // not active
|
||||
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const uint16_t rows = virtualHeight(); // will be 1 for 1D
|
||||
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, c);
|
||||
else setPixelColor(x, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Blends the specified color with the existing pixel color.
|
||||
void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
|
||||
setPixelColor(n, color_blend(getPixelColor(n), color, blend));
|
||||
}
|
||||
|
||||
// Adds the specified color with the existing pixel color perserving color balance.
|
||||
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
|
||||
if (!isActive()) return; // not active
|
||||
uint32_t col = getPixelColor(n);
|
||||
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);
|
||||
}
|
||||
setPixelColor(n, col);
|
||||
}
|
||||
|
||||
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
|
||||
if (!isActive()) return; // not active
|
||||
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
|
||||
setPixelColor(n, pix);
|
||||
}
|
||||
|
||||
/*
|
||||
* fade out function, higher rate = quicker fade
|
||||
*/
|
||||
@ -996,7 +950,7 @@ void Segment::fade_out(uint8_t rate) {
|
||||
int g2 = G(color);
|
||||
int b2 = B(color);
|
||||
|
||||
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
color = is2D() ? getPixelColorXY(x, y) : getPixelColor(x);
|
||||
int w1 = W(color);
|
||||
int r1 = R(color);
|
||||
@ -1025,49 +979,40 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
|
||||
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
|
||||
const uint16_t rows = virtualHeight(); // will be 1 for 1D
|
||||
|
||||
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, CRGB(getPixelColorXY(x,y)).nscale8(255-fadeBy));
|
||||
else setPixelColor(x, CRGB(getPixelColor(x)).nscale8(255-fadeBy));
|
||||
for (int y = 0; y < rows; y++) for (int x = 0; x < cols; x++) {
|
||||
if (is2D()) setPixelColorXY(x, y, color_fade(getPixelColorXY(x,y), 255-fadeBy));
|
||||
else setPixelColor(x, color_fade(getPixelColor(x), 255-fadeBy));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* blurs segment content, source: FastLED colorutils.cpp
|
||||
*/
|
||||
void Segment::blur(uint8_t blur_amount)
|
||||
{
|
||||
void Segment::blur(uint8_t blur_amount) {
|
||||
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (is2D()) {
|
||||
// compatibility with 2D
|
||||
const uint_fast16_t cols = virtualWidth();
|
||||
const uint_fast16_t rows = virtualHeight();
|
||||
for (uint_fast16_t i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
|
||||
for (uint_fast16_t k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
for (unsigned i = 0; i < rows; i++) blurRow(i, blur_amount); // blur all rows
|
||||
for (unsigned k = 0; k < cols; k++) blurCol(k, blur_amount); // blur all columns
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
uint8_t keep = 255 - blur_amount;
|
||||
uint8_t seep = blur_amount >> 1;
|
||||
CRGB carryover = CRGB::Black;
|
||||
uint_fast16_t vlength = virtualLength();
|
||||
for(uint_fast16_t i = 0; i < vlength; i++)
|
||||
{
|
||||
CRGB cur = CRGB(getPixelColor(i));
|
||||
CRGB part = cur;
|
||||
CRGB before = cur; // remember color before blur
|
||||
part.nscale8(seep);
|
||||
cur.nscale8(keep);
|
||||
cur += carryover;
|
||||
uint32_t carryover = BLACK;
|
||||
unsigned vlength = virtualLength();
|
||||
for (unsigned i = 0; i < vlength; i++) {
|
||||
uint32_t cur = getPixelColor(i);
|
||||
uint32_t part = color_fade(cur, seep);
|
||||
cur = color_add(color_fade(cur, keep), carryover, true);
|
||||
if (i > 0) {
|
||||
uint32_t c = getPixelColor(i-1);
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(c);
|
||||
setPixelColor((uint16_t)(i-1), qadd8(r, part.red), qadd8(g, part.green), qadd8(b, part.blue));
|
||||
setPixelColor(i-1, color_add(c, part, true));
|
||||
}
|
||||
if (before != cur) // optimization: only set pixel if color has changed
|
||||
setPixelColor((uint16_t)i,cur.red, cur.green, cur.blue);
|
||||
setPixelColor(i, cur);
|
||||
carryover = part;
|
||||
}
|
||||
}
|
||||
@ -1091,21 +1036,6 @@ uint32_t Segment::color_wheel(uint8_t pos) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new, random wheel index with a minimum distance of 42 from pos.
|
||||
*/
|
||||
uint8_t Segment::get_random_wheel_index(uint8_t pos) {
|
||||
uint8_t r = 0, x = 0, y = 0, d = 0;
|
||||
|
||||
while(d < 42) {
|
||||
r = random8();
|
||||
x = abs(pos - r);
|
||||
y = 255 - x;
|
||||
d = MIN(x, y);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets a single color from the currently selected palette.
|
||||
* @param i Palette Index (if mapping is true, the full palette will be _virtualSegmentLength long, if false, 255). Will wrap around automatically.
|
||||
@ -1115,24 +1045,21 @@ uint8_t Segment::get_random_wheel_index(uint8_t pos) {
|
||||
* @param pbri Value to scale the brightness of the returned color by. Default is 255. (no scaling)
|
||||
* @returns Single color from palette
|
||||
*/
|
||||
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
|
||||
{
|
||||
uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri) {
|
||||
// default palette or no RGB support on segment
|
||||
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) {
|
||||
uint32_t color = currentColor(mcol, colors[mcol]);
|
||||
uint32_t color = currentColor(mcol);
|
||||
color = gamma32(color);
|
||||
if (pbri == 255) return color;
|
||||
return RGBW32(scale8_video(R(color),pbri), scale8_video(G(color),pbri), scale8_video(B(color),pbri), scale8_video(W(color),pbri));
|
||||
return color_fade(color, pbri, true);
|
||||
}
|
||||
|
||||
uint8_t paletteIndex = i;
|
||||
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
|
||||
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGB fastled_col;
|
||||
if (!wrap && strip.paletteBlend != 3) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
|
||||
CRGBPalette16 curPal;
|
||||
if (transitional && _t) curPal = _t->_palT;
|
||||
else loadPalette(curPal, palette);
|
||||
fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
curPal = currentPalette(curPal, palette);
|
||||
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global
|
||||
|
||||
return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, 0);
|
||||
}
|
||||
@ -1143,8 +1070,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit(void)
|
||||
{
|
||||
void WS2812FX::finalizeInit(void) {
|
||||
//reset segment runtimes
|
||||
for (segment &seg : _segments) {
|
||||
seg.markForReset();
|
||||
@ -1166,7 +1092,7 @@ void WS2812FX::finalizeInit(void)
|
||||
const uint8_t defNumBusses = ((sizeof defDataPins) / (sizeof defDataPins[0]));
|
||||
const uint8_t defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0]));
|
||||
uint16_t prevLen = 0;
|
||||
for (uint8_t i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
for (int i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
|
||||
uint8_t defPin[] = {defDataPins[i]};
|
||||
uint16_t start = prevLen;
|
||||
uint16_t count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
@ -1177,7 +1103,7 @@ void WS2812FX::finalizeInit(void)
|
||||
}
|
||||
|
||||
_length = 0;
|
||||
for (uint8_t i=0; i<busses.getNumBusses(); i++) {
|
||||
for (int i=0; i<busses.getNumBusses(); i++) {
|
||||
Bus *bus = busses.getBus(i);
|
||||
if (bus == nullptr) continue;
|
||||
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
@ -1234,24 +1160,24 @@ void WS2812FX::service() {
|
||||
|
||||
if (!seg.freeze) { //only run effect function if not frozen
|
||||
_virtualSegmentLength = seg.virtualLength();
|
||||
_colors_t[0] = seg.currentColor(0, seg.colors[0]);
|
||||
_colors_t[1] = seg.currentColor(1, seg.colors[1]);
|
||||
_colors_t[2] = seg.currentColor(2, seg.colors[2]);
|
||||
seg.currentPalette(_currentPalette, seg.palette);
|
||||
_colors_t[0] = seg.currentColor(0);
|
||||
_colors_t[1] = seg.currentColor(1);
|
||||
_colors_t[2] = seg.currentColor(2);
|
||||
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
|
||||
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
|
||||
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB);
|
||||
for (int c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
|
||||
// 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/transitional mode.
|
||||
// data needs to be saved first and then restored before running previous mode.
|
||||
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be
|
||||
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
|
||||
// would need to be allocated for each effect and then blended together for each pixel.
|
||||
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
|
||||
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(); // this will return old mode while in transition
|
||||
delay = (*_mode[seg.mode])(); // run new/current mode
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (seg.mode != tmpMode) {
|
||||
if (modeBlending && seg.mode != tmpMode) {
|
||||
Segment::tmpsegd_t _tmpSegData;
|
||||
Segment::modeBlend(true); // set semaphore
|
||||
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
|
||||
@ -1261,8 +1187,8 @@ void WS2812FX::service() {
|
||||
Segment::modeBlend(false); // unset semaphore
|
||||
}
|
||||
#endif
|
||||
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
|
||||
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
||||
seg.call++;
|
||||
if (seg.isInTransition() && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
||||
}
|
||||
|
||||
seg.next_time = nowUp + delay;
|
||||
@ -1287,124 +1213,34 @@ void WS2812FX::service() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
|
||||
{
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col) {
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return;
|
||||
busses.setPixelColor(i, col);
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
uint32_t IRAM_ATTR WS2812FX::getPixelColor(uint16_t i) {
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return 0;
|
||||
return busses.getPixelColor(i);
|
||||
}
|
||||
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages!
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
#define MA_FOR_ESP 100 //how much mA does the ESP use (Wemos D1 about 80mA, ESP32 about 120mA)
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
|
||||
uint8_t WS2812FX::estimateCurrentAndLimitBri() {
|
||||
//power limit calculation
|
||||
//each LED can draw up 195075 "power units" (approx. 53mA)
|
||||
//one PU is the power it takes to have 1 channel 1 step brighter per brightness step
|
||||
//so A=2,R=255,G=0,B=0 would use 510 PU per LED (1mA is about 3700 PU)
|
||||
bool useWackyWS2815PowerModel = false;
|
||||
byte actualMilliampsPerLed = milliampsPerLed;
|
||||
|
||||
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
|
||||
currentMilliamps = 0;
|
||||
return _brightness;
|
||||
}
|
||||
|
||||
if (milliampsPerLed == 255) {
|
||||
useWackyWS2815PowerModel = true;
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
|
||||
|
||||
size_t pLen = 0; //getLengthPhysical();
|
||||
size_t powerSum = 0;
|
||||
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
|
||||
Bus *bus = busses.getBus(bNum);
|
||||
if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
|
||||
uint16_t len = bus->getLength();
|
||||
pLen += len;
|
||||
uint32_t busPowerSum = 0;
|
||||
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
|
||||
uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if(useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (MAX(MAX(r,g),b)) * 3;
|
||||
} else {
|
||||
busPowerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum >>= 2; //same as /= 4
|
||||
}
|
||||
powerSum += busPowerSum;
|
||||
}
|
||||
|
||||
if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= pLen;
|
||||
} else {
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
// powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps
|
||||
powerSum = (powerSum * actualMilliampsPerLed) / 765;
|
||||
|
||||
uint8_t newBri = _brightness;
|
||||
if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit
|
||||
float scale = (float)(powerBudget * 255) / (float)(powerSum * _brightness);
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
newBri = scale8(_brightness, scaleB) + 1;
|
||||
}
|
||||
currentMilliamps = (powerSum * newBri) / 255;
|
||||
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
|
||||
currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate
|
||||
return newBri;
|
||||
}
|
||||
|
||||
void WS2812FX::show(void) {
|
||||
// avoid race condition, caputre _callback value
|
||||
show_callback callback = _callback;
|
||||
if (callback) callback();
|
||||
|
||||
uint8_t newBri = estimateCurrentAndLimitBri();
|
||||
busses.setBrightness(newBri); // "repaints" all pixels if brightness changed
|
||||
|
||||
// some buses send asynchronously and this method will return before
|
||||
// all of the data has been sent.
|
||||
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
|
||||
busses.show();
|
||||
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
if (newBri < _brightness) busses.setBrightness(_brightness);
|
||||
|
||||
unsigned long now = millis();
|
||||
size_t diff = now - _lastShow;
|
||||
unsigned long showNow = millis();
|
||||
size_t diff = showNow - _lastShow;
|
||||
size_t fpsCurr = 200;
|
||||
if (diff > 0) fpsCurr = 1000 / diff;
|
||||
_cumulativeFps = (3 * _cumulativeFps + fpsCurr +2) >> 2; // "+2" for proper rounding (2/4 = 0.5)
|
||||
_lastShow = now;
|
||||
_lastShow = showNow;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1486,8 +1322,7 @@ uint8_t WS2812FX::getActiveSegsLightCapabilities(bool selectedOnly) {
|
||||
return totalLC;
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::getFirstSelectedSegId(void)
|
||||
{
|
||||
uint8_t WS2812FX::getFirstSelectedSegId(void) {
|
||||
size_t i = 0;
|
||||
for (segment &seg : _segments) {
|
||||
if (seg.isActive() && seg.isSelected()) return i;
|
||||
@ -1597,10 +1432,12 @@ void WS2812FX::setSegment(uint8_t segId, uint16_t i1, uint16_t i2, uint8_t group
|
||||
_qStart = i1; _qStop = i2; _qStartY = startY; _qStopY = stopY;
|
||||
_qGrouping = grouping; _qSpacing = spacing; _qOffset = offset;
|
||||
_queuedChangesSegId = segId;
|
||||
DEBUG_PRINT(F("Segment queued: ")); DEBUG_PRINTLN(segId);
|
||||
return; // queued changes are applied immediately after effect function returns
|
||||
}
|
||||
|
||||
_segments[segId].setUp(i1, i2, grouping, spacing, offset, startY, stopY);
|
||||
if (segId > 0 && segId == getSegmentsNum()-1 && i2 <= i1) _segments.pop_back(); // if last segment was deleted remove it from vector
|
||||
}
|
||||
|
||||
void WS2812FX::setUpSegmentFromQueuedChanges() {
|
||||
@ -1609,10 +1446,6 @@ void WS2812FX::setUpSegmentFromQueuedChanges() {
|
||||
_queuedChangesSegId = 255;
|
||||
}
|
||||
|
||||
void WS2812FX::restartRuntime() {
|
||||
for (segment &seg : _segments) seg.markForReset();
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
_segments.clear(); // destructs all Segment as part of clearing
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@ -1729,7 +1562,7 @@ void WS2812FX::fixInvalidSegments() {
|
||||
bool WS2812FX::checkSegmentAlignment() {
|
||||
bool aligned = false;
|
||||
for (segment &seg : _segments) {
|
||||
for (uint8_t b = 0; b<busses.getNumBusses(); b++) {
|
||||
for (unsigned b = 0; b<busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
|
||||
}
|
||||
@ -1752,17 +1585,8 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n) {
|
||||
}
|
||||
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
|
||||
if (i2 >= i)
|
||||
{
|
||||
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
|
||||
} else
|
||||
{
|
||||
for (uint16_t x = i2; x <= i; x++) setPixelColor(x, col);
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t) {
|
||||
for (segment &seg : _segments) seg.startTransition(t ? _transitionDur : 0);
|
||||
if (i2 < i) std::swap(i,i2);
|
||||
for (unsigned x = i; x <= i2; x++) setPixelColor(x, col);
|
||||
}
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
@ -1793,7 +1617,7 @@ void WS2812FX::loadCustomPalettes() {
|
||||
|
||||
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
|
||||
JsonArray pal = pDoc[F("palette")];
|
||||
if (!pal.isNull() && pal.size()>4) { // not an empty palette (at least 2 entries)
|
||||
if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries)
|
||||
if (pal[0].is<int>() && pal[1].is<const char *>()) {
|
||||
// we have an array of index & hex strings
|
||||
size_t palSize = MIN(pal.size(), 36);
|
||||
@ -1802,7 +1626,7 @@ void WS2812FX::loadCustomPalettes() {
|
||||
uint8_t rgbw[] = {0,0,0,0};
|
||||
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||
colorFromHexString(rgbw, pal[i+1].as<const char *>()); // will catch non-string entires
|
||||
for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component
|
||||
for (size_t c=0; c<3; c++) tcp[j+1+c] = gamma8(rgbw[c]); // only use RGB component
|
||||
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3]));
|
||||
}
|
||||
} else {
|
||||
@ -1810,13 +1634,15 @@ void WS2812FX::loadCustomPalettes() {
|
||||
palSize -= palSize % 4; // make sure size is multiple of 4
|
||||
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
|
||||
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||
tcp[i+1] = (uint8_t) pal[i+1].as<int>(); // R
|
||||
tcp[i+2] = (uint8_t) pal[i+2].as<int>(); // G
|
||||
tcp[i+3] = (uint8_t) pal[i+3].as<int>(); // B
|
||||
tcp[i+1] = gamma8((uint8_t) pal[i+1].as<int>()); // R
|
||||
tcp[i+2] = gamma8((uint8_t) pal[i+2].as<int>()); // G
|
||||
tcp[i+3] = gamma8((uint8_t) pal[i+3].as<int>()); // B
|
||||
DEBUG_PRINTF("%d(%d) : %d %d %d\n", i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
|
||||
}
|
||||
}
|
||||
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("Wrong palette format."));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1832,7 +1658,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
if (n) sprintf(fileName +7, "%d", n);
|
||||
strcat(fileName, ".json");
|
||||
strcat_P(fileName, PSTR(".json"));
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
|
||||
if (!isFile) {
|
||||
@ -1866,7 +1692,7 @@ bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
customMappingSize = map.size();
|
||||
customMappingTable = new uint16_t[customMappingSize];
|
||||
for (uint16_t i=0; i<customMappingSize; i++) {
|
||||
for (unsigned i=0; i<customMappingSize; i++) {
|
||||
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul
|
||||
if (_count == 0) return defaultColorOrder;
|
||||
// upper nibble containd W swap information
|
||||
uint8_t swapW = defaultColorOrder >> 4;
|
||||
for (uint8_t i = 0; i < _count; i++) {
|
||||
for (unsigned i = 0; i < _count; i++) {
|
||||
if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) {
|
||||
return _mappings[i].colorOrder | (swapW << 4);
|
||||
}
|
||||
@ -101,6 +101,8 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
|
||||
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
|
||||
, _skip(bc.skipAmount) //sacrificial pixels
|
||||
, _colorOrder(bc.colorOrder)
|
||||
, _milliAmpsPerLed(bc.milliAmpsPerLed)
|
||||
, _milliAmpsMax(bc.milliAmpsMax)
|
||||
, _colorOrderMap(com)
|
||||
{
|
||||
if (!IS_DIGITAL(bc.type) || !bc.count) return;
|
||||
@ -118,7 +120,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
|
||||
_buffering = bc.doubleBuffer;
|
||||
//_buffering = bc.doubleBuffer;
|
||||
uint16_t lenToCreate = bc.count;
|
||||
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr, _frequencykHz);
|
||||
@ -126,9 +128,82 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
|
||||
DEBUG_PRINTF("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n", _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], _pins[1], _iType);
|
||||
}
|
||||
|
||||
//fine tune power estimation constants for your setup
|
||||
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
|
||||
#ifndef MA_FOR_ESP
|
||||
#ifdef ESP8266
|
||||
#define MA_FOR_ESP 80 //how much mA does the ESP use (Wemos D1 about 80mA)
|
||||
#else
|
||||
#define MA_FOR_ESP 120 //how much mA does the ESP use (ESP32 about 120mA)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//DISCLAIMER
|
||||
//The following function attemps to calculate the current LED power usage,
|
||||
//and will limit the brightness to stay below a set amperage threshold.
|
||||
//It is NOT a measurement and NOT guaranteed to stay within the ablMilliampsMax margin.
|
||||
//Stay safe with high amperage and have a reasonable safety margin!
|
||||
//I am NOT to be held liable for burned down garages or houses!
|
||||
|
||||
// To disable brightness limiter we either set output max current to 0 or single LED current to 0
|
||||
uint8_t BusDigital::estimateCurrentAndLimitBri() {
|
||||
bool useWackyWS2815PowerModel = false;
|
||||
byte actualMilliampsPerLed = _milliAmpsPerLed;
|
||||
|
||||
if (_milliAmpsMax < MA_FOR_ESP || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
|
||||
return _bri;
|
||||
}
|
||||
|
||||
if (_milliAmpsPerLed == 255) {
|
||||
useWackyWS2815PowerModel = true;
|
||||
actualMilliampsPerLed = 12; // from testing an actual strip
|
||||
}
|
||||
|
||||
size_t powerBudget = (_milliAmpsMax - MA_FOR_ESP); //100mA for ESP power
|
||||
|
||||
uint32_t busPowerSum = 0;
|
||||
for (unsigned i = 0; i < getLength(); i++) { //sum up the usage of each LED
|
||||
uint32_t c = getPixelColor(i); // always returns original or restored color without brightness scaling
|
||||
byte r = R(c), g = G(c), b = B(c), w = W(c);
|
||||
|
||||
if (useWackyWS2815PowerModel) { //ignore white component on WS2815 power calculation
|
||||
busPowerSum += (max(max(r,g),b)) * 3;
|
||||
} else {
|
||||
busPowerSum += (r + g + b + w);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum >>= 2; //same as /= 4
|
||||
}
|
||||
|
||||
if (powerBudget > getLength()) { //each LED uses about 1mA in standby, exclude that from power budget
|
||||
powerBudget -= getLength();
|
||||
} else {
|
||||
powerBudget = 0;
|
||||
}
|
||||
|
||||
// powerSum has all the values of channels summed (max would be getLength()*765 as white is excluded) so convert to milliAmps
|
||||
busPowerSum = (busPowerSum * actualMilliampsPerLed) / 765;
|
||||
|
||||
uint8_t newBri = _bri;
|
||||
if (busPowerSum * _bri / 255 > powerBudget) { //scale brightness down to stay in current limit
|
||||
float scale = (float)(powerBudget * 255) / (float)(busPowerSum * _bri);
|
||||
uint16_t scaleI = scale * 255;
|
||||
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
|
||||
newBri = unsigned(_bri * scaleB) / 256 + 1;
|
||||
}
|
||||
return newBri;
|
||||
}
|
||||
|
||||
void BusDigital::show() {
|
||||
if (!_valid) return;
|
||||
if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
|
||||
|
||||
uint8_t newBri = estimateCurrentAndLimitBri();
|
||||
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);
|
||||
for (size_t i=0; i<_len; i++) {
|
||||
size_t offset = i*channels;
|
||||
@ -152,8 +227,22 @@ void BusDigital::show() {
|
||||
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
|
||||
} else {
|
||||
if (newBri < _bri) {
|
||||
uint16_t hwLen = _len;
|
||||
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
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
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
|
||||
}
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important (use !_buffering this causes 20% FPS drop)
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, _bri);
|
||||
}
|
||||
|
||||
bool BusDigital::canShow() {
|
||||
@ -172,19 +261,20 @@ void BusDigital::setBrightness(uint8_t b) {
|
||||
uint8_t prevBri = _bri;
|
||||
Bus::setBrightness(b);
|
||||
PolyBus::setBrightness(_busPtr, _iType, b);
|
||||
|
||||
if (_buffering) return;
|
||||
/*
|
||||
if (_data) return; // use _buffering this causes ~20% FPS drop
|
||||
|
||||
// must update/repaint every LED in the NeoPixelBus buffer to the new brightness
|
||||
// the only case where repainting is unnecessary is when all pixels are set after the brightness change but before the next show
|
||||
// (which we can't rely on)
|
||||
uint16_t hwLen = _len;
|
||||
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
for (uint_fast16_t i = 0; i < hwLen; i++) {
|
||||
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), prevBri);
|
||||
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
//If LEDs are skipped, it is possible to use the first as a status LED.
|
||||
@ -200,7 +290,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (!_valid) return;
|
||||
if (Bus::hasWhite(_type)) c = autoWhiteCalc(c);
|
||||
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
|
||||
if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
|
||||
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)) {
|
||||
@ -228,9 +318,9 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
}
|
||||
|
||||
// returns original color if global buffering is enabled, else returns lossly restored color from bus
|
||||
uint32_t BusDigital::getPixelColor(uint16_t pix) {
|
||||
uint32_t IRAM_ATTR BusDigital::getPixelColor(uint16_t pix) {
|
||||
if (!_valid) return 0;
|
||||
if (_buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
|
||||
if (_data) { // use _buffering this causes ~20% FPS drop
|
||||
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
|
||||
size_t offset = pix*channels;
|
||||
uint32_t c;
|
||||
@ -261,7 +351,7 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) {
|
||||
|
||||
uint8_t BusDigital::getPins(uint8_t* pinArray) {
|
||||
uint8_t numPins = IS_2PIN(_type) ? 2 : 1;
|
||||
for (uint8_t i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
return numPins;
|
||||
}
|
||||
|
||||
@ -305,7 +395,7 @@ BusPwm::BusPwm(BusConfig &bc)
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
uint8_t currentPin = bc.pins[i];
|
||||
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
|
||||
deallocatePins(); return;
|
||||
@ -384,7 +474,7 @@ uint32_t BusPwm::getPixelColor(uint16_t pix) {
|
||||
void BusPwm::show() {
|
||||
if (!_valid) return;
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
uint8_t scaled = (_data[i] * _bri) / 255;
|
||||
if (_reversed) scaled = 255 - scaled;
|
||||
#ifdef ESP8266
|
||||
@ -398,7 +488,7 @@ void BusPwm::show() {
|
||||
uint8_t BusPwm::getPins(uint8_t* pinArray) {
|
||||
if (!_valid) return 0;
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
pinArray[i] = _pins[i];
|
||||
}
|
||||
return numPins;
|
||||
@ -406,7 +496,7 @@ uint8_t BusPwm::getPins(uint8_t* pinArray) {
|
||||
|
||||
void BusPwm::deallocatePins() {
|
||||
uint8_t numPins = NUM_PWM_PINS(_type);
|
||||
for (uint8_t i = 0; i < numPins; i++) {
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
|
||||
if (!pinManager.isPinOk(_pins[i])) continue;
|
||||
#ifdef ESP8266
|
||||
@ -512,7 +602,7 @@ void BusNetwork::show() {
|
||||
}
|
||||
|
||||
uint8_t BusNetwork::getPins(uint8_t* pinArray) {
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
pinArray[i] = _client[i];
|
||||
}
|
||||
return 4;
|
||||
@ -566,24 +656,24 @@ void BusManager::removeAll() {
|
||||
DEBUG_PRINTLN(F("Removing all."));
|
||||
//prevents crashes due to deleting busses while in use.
|
||||
while (!canAllShow()) yield();
|
||||
for (uint8_t i = 0; i < numBusses; i++) delete busses[i];
|
||||
for (unsigned i = 0; i < numBusses; i++) delete busses[i];
|
||||
numBusses = 0;
|
||||
}
|
||||
|
||||
void BusManager::show() {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->show();
|
||||
}
|
||||
}
|
||||
|
||||
void BusManager::setStatusPixel(uint32_t c) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->setStatusPixel(c);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
if (pix < bstart || pix >= bstart + b->getLength()) continue;
|
||||
@ -592,7 +682,7 @@ void IRAM_ATTR BusManager::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
}
|
||||
|
||||
void BusManager::setBrightness(uint8_t b) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
busses[i]->setBrightness(b);
|
||||
}
|
||||
}
|
||||
@ -607,7 +697,7 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
|
||||
}
|
||||
|
||||
uint32_t BusManager::getPixelColor(uint16_t pix) {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
Bus* b = busses[i];
|
||||
uint16_t bstart = b->getStart();
|
||||
if (pix < bstart || pix >= bstart + b->getLength()) continue;
|
||||
@ -617,7 +707,7 @@ uint32_t BusManager::getPixelColor(uint16_t pix) {
|
||||
}
|
||||
|
||||
bool BusManager::canAllShow() {
|
||||
for (uint8_t i = 0; i < numBusses; i++) {
|
||||
for (unsigned i = 0; i < numBusses; i++) {
|
||||
if (!busses[i]->canShow()) return false;
|
||||
}
|
||||
return true;
|
||||
@ -631,7 +721,7 @@ Bus* BusManager::getBus(uint8_t busNr) {
|
||||
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
|
||||
uint16_t BusManager::getTotalLength() {
|
||||
uint16_t len = 0;
|
||||
for (uint8_t i=0; i<numBusses; i++) len += busses[i]->getLength();
|
||||
for (unsigned i=0; i<numBusses; i++) len += busses[i]->getLength();
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,10 @@ struct BusConfig {
|
||||
uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255};
|
||||
uint16_t frequency;
|
||||
bool doubleBuffer;
|
||||
uint8_t milliAmpsPerLed;
|
||||
uint16_t milliAmpsMax;
|
||||
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false)
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false, uint8_t maPerLed=55, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
|
||||
: count(len)
|
||||
, start(pstart)
|
||||
, colorOrder(pcolorOrder)
|
||||
@ -45,6 +47,8 @@ struct BusConfig {
|
||||
, autoWhite(aw)
|
||||
, frequency(clock_kHz)
|
||||
, doubleBuffer(dblBfr)
|
||||
, milliAmpsPerLed(maPerLed)
|
||||
, milliAmpsMax(maMax)
|
||||
{
|
||||
refreshReq = (bool) GET_BIT(busType,7);
|
||||
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
|
||||
@ -132,6 +136,8 @@ class Bus {
|
||||
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
|
||||
virtual uint8_t skippedLeds() { return 0; }
|
||||
virtual uint16_t getFrequency() { return 0U; }
|
||||
virtual uint16_t getLEDCurrent() { return 0; }
|
||||
virtual uint16_t getMaxCurrent() { return 0; }
|
||||
inline void setReversed(bool reversed) { _reversed = reversed; }
|
||||
inline uint16_t getStart() { return _start; }
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
@ -211,6 +217,9 @@ class BusDigital : public Bus {
|
||||
uint8_t getPins(uint8_t* pinArray);
|
||||
uint8_t skippedLeds() { return _skip; }
|
||||
uint16_t getFrequency() { return _frequencykHz; }
|
||||
uint8_t estimateCurrentAndLimitBri();
|
||||
uint16_t getLEDCurrent() { return _milliAmpsPerLed; }
|
||||
uint16_t getMaxCurrent() { return _milliAmpsMax; }
|
||||
void reinit();
|
||||
void cleanup();
|
||||
|
||||
@ -220,9 +229,11 @@ class BusDigital : public Bus {
|
||||
uint8_t _pins[2];
|
||||
uint8_t _iType;
|
||||
uint16_t _frequencykHz;
|
||||
uint8_t _milliAmpsPerLed;
|
||||
uint16_t _milliAmpsMax;
|
||||
void * _busPtr;
|
||||
const ColorOrderMap &_colorOrderMap;
|
||||
bool _buffering; // temporary until we figure out why comparison "_data != nullptr" causes severe FPS drop
|
||||
//bool _buffering; // temporary until we figure out why comparison "_data" causes severe FPS drop
|
||||
|
||||
inline uint32_t restoreColorLossy(uint32_t c, uint8_t restoreBri) {
|
||||
if (restoreBri < 255) {
|
||||
|
@ -35,7 +35,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(simplifiedUI, id[F("sui")]);
|
||||
#endif
|
||||
|
||||
JsonObject nw_ins_0 = doc["nw"]["ins"][0];
|
||||
JsonObject nw = doc["nw"];
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
CJSON(enableESPNow, nw[F("espnow")]);
|
||||
getStringFromJson(linked_remote, nw[F("linked_remote")], 13);
|
||||
linked_remote[12] = '\0';
|
||||
#endif
|
||||
|
||||
JsonObject nw_ins_0 = nw["ins"][0];
|
||||
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
|
||||
//int nw_ins_0_pskl = nw_ins_0[F("pskl")];
|
||||
//The WiFi PSK is normally not contained in the regular file for security reasons.
|
||||
@ -82,8 +89,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject hw_led = hw["led"];
|
||||
|
||||
uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY;
|
||||
uint16_t total = hw_led[F("total")] | strip.getLengthTotal();
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used
|
||||
Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255);
|
||||
CJSON(correctWB, hw_led["cct"]);
|
||||
CJSON(cctFromRgb, hw_led[F("cr")]);
|
||||
@ -160,8 +168,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
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;
|
||||
uint8_t maPerLed = elm[F("ledma")] | strip.milliampsPerLed; // replace with 55 when removing strip.milliampsPerLed
|
||||
uint16_t maMax = elm[F("maxpwr")] | (strip.ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
|
||||
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
|
||||
if ((ledType > TYPE_TM1814 && ledType < TYPE_WS2801) || ledType >= TYPE_NET_DDP_RGB) { // analog and virtual
|
||||
maPerLed = 0;
|
||||
maMax = 0;
|
||||
}
|
||||
if (fromFS) {
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
|
||||
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
mem += BusManager::memUsage(bc);
|
||||
if (useGlobalLedBuffer && start + length > maxlen) {
|
||||
maxlen = start + length;
|
||||
@ -170,7 +185,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (mem + globalBufMem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
|
||||
} else {
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer);
|
||||
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
s++;
|
||||
@ -216,7 +231,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0))
|
||||
{
|
||||
// not an ADC analog pin
|
||||
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[s], s);
|
||||
DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
|
||||
DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
|
||||
DEBUG_PRINTLN(F(" is not an analog pin!"));
|
||||
btnPin[s] = -1;
|
||||
pinManager.deallocatePin(pin,PinOwner::Button);
|
||||
}
|
||||
@ -357,6 +374,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
JsonObject light_tr = light["tr"];
|
||||
CJSON(fadeTransition, light_tr["mode"]);
|
||||
CJSON(modeBlending, light_tr["fx"]);
|
||||
int tdd = light_tr["dur"] | -1;
|
||||
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
|
||||
CJSON(strip.paletteFade, light_tr["pal"]);
|
||||
@ -382,6 +400,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(udpPort, if_sync[F("port0")]); // 21324
|
||||
CJSON(udpPort2, if_sync[F("port1")]); // 65506
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
CJSON(useESPNowSync, if_sync[F("espnow")]);
|
||||
#endif
|
||||
|
||||
JsonObject if_sync_recv = if_sync["recv"];
|
||||
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
|
||||
CJSON(receiveNotificationColor, if_sync_recv["col"]);
|
||||
@ -389,17 +411,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(receiveGroups, if_sync_recv["grp"]);
|
||||
CJSON(receiveSegmentOptions, if_sync_recv["seg"]);
|
||||
CJSON(receiveSegmentBounds, if_sync_recv["sb"]);
|
||||
//! following line might be a problem if called after boot
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
|
||||
|
||||
JsonObject if_sync_send = if_sync["send"];
|
||||
prev = notifyDirectDefault;
|
||||
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
|
||||
if (notifyDirectDefault != prev) notifyDirect = notifyDirectDefault;
|
||||
CJSON(sendNotifications, if_sync_send["en"]);
|
||||
sendNotificationsRT = sendNotifications;
|
||||
CJSON(notifyDirect, if_sync_send[F("dir")]);
|
||||
CJSON(notifyButton, if_sync_send["btn"]);
|
||||
CJSON(notifyAlexa, if_sync_send["va"]);
|
||||
CJSON(notifyHue, if_sync_send["hue"]);
|
||||
CJSON(notifyMacro, if_sync_send["macro"]);
|
||||
// CJSON(notifyMacro, if_sync_send["macro"]);
|
||||
CJSON(syncGroups, if_sync_send["grp"]);
|
||||
if (if_sync_send[F("twice")]) udpNumRetries = 1; // import setting from 0.13 and earlier
|
||||
CJSON(udpNumRetries, if_sync_send["ret"]);
|
||||
@ -409,7 +429,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(nodeBroadcastEnabled, if_nodes[F("bcast")]);
|
||||
|
||||
JsonObject if_live = interfaces["live"];
|
||||
CJSON(receiveDirect, if_live["en"]);
|
||||
CJSON(receiveDirect, if_live["en"]); // UDP/Hyperion realtime
|
||||
CJSON(useMainSegmentOnly, if_live[F("mso")]);
|
||||
CJSON(e131Port, if_live["port"]); // 5568
|
||||
if (e131Port == DDP_DEFAULT_PORT) e131Port = E131_DEFAULT_PORT; // prevent double DDP port allocation
|
||||
@ -453,13 +473,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
JsonObject remote = doc["remote"];
|
||||
CJSON(enable_espnow_remote, remote[F("remote_enabled")]);
|
||||
getStringFromJson(linked_remote, remote[F("linked_remote")], 13);
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
JsonObject if_hue = interfaces["hue"];
|
||||
CJSON(huePollingEnabled, if_hue["en"]);
|
||||
@ -647,6 +660,10 @@ void serializeConfig() {
|
||||
#endif
|
||||
|
||||
JsonObject nw = doc.createNestedObject("nw");
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
nw[F("espnow")] = enableESPNow;
|
||||
nw[F("linked_remote")] = linked_remote;
|
||||
#endif
|
||||
|
||||
JsonArray nw_ins = nw.createNestedArray("ins");
|
||||
|
||||
@ -708,9 +725,9 @@ void serializeConfig() {
|
||||
JsonObject hw = doc.createNestedObject("hw");
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //no longer read, but provided for compatibility on downgrade
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed; // no longer used
|
||||
hw_led["cct"] = correctWB;
|
||||
hw_led[F("cr")] = cctFromRgb;
|
||||
hw_led[F("cb")] = strip.cctBlending;
|
||||
@ -757,6 +774,8 @@ void serializeConfig() {
|
||||
ins["ref"] = bus->isOffRefreshRequired();
|
||||
ins[F("rgbwm")] = bus->getAutoWhiteMode();
|
||||
ins[F("freq")] = bus->getFrequency();
|
||||
ins[F("maxpwr")] = bus->getMaxCurrent();
|
||||
ins[F("ledma")] = bus->getLEDCurrent();
|
||||
}
|
||||
|
||||
JsonArray hw_com = hw.createNestedArray(F("com"));
|
||||
@ -827,6 +846,7 @@ void serializeConfig() {
|
||||
|
||||
JsonObject light_tr = light.createNestedObject("tr");
|
||||
light_tr["mode"] = fadeTransition;
|
||||
light_tr["fx"] = modeBlending;
|
||||
light_tr["dur"] = transitionDelayDefault / 100;
|
||||
light_tr["pal"] = strip.paletteFade;
|
||||
light_tr[F("rpc")] = randomPaletteChangeTime;
|
||||
@ -848,6 +868,10 @@ void serializeConfig() {
|
||||
if_sync[F("port0")] = udpPort;
|
||||
if_sync[F("port1")] = udpPort2;
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if_sync[F("espnow")] = useESPNowSync;
|
||||
#endif
|
||||
|
||||
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
|
||||
if_sync_recv["bri"] = receiveNotificationBrightness;
|
||||
if_sync_recv["col"] = receiveNotificationColor;
|
||||
@ -857,11 +881,12 @@ void serializeConfig() {
|
||||
if_sync_recv["sb"] = receiveSegmentBounds;
|
||||
|
||||
JsonObject if_sync_send = if_sync.createNestedObject("send");
|
||||
if_sync_send["en"] = sendNotifications;
|
||||
if_sync_send[F("dir")] = notifyDirect;
|
||||
if_sync_send["btn"] = notifyButton;
|
||||
if_sync_send["va"] = notifyAlexa;
|
||||
if_sync_send["hue"] = notifyHue;
|
||||
if_sync_send["macro"] = notifyMacro;
|
||||
// if_sync_send["macro"] = notifyMacro;
|
||||
if_sync_send["grp"] = syncGroups;
|
||||
if_sync_send["ret"] = udpNumRetries;
|
||||
|
||||
@ -870,7 +895,7 @@ void serializeConfig() {
|
||||
if_nodes[F("bcast")] = nodeBroadcastEnabled;
|
||||
|
||||
JsonObject if_live = interfaces.createNestedObject("live");
|
||||
if_live["en"] = receiveDirect;
|
||||
if_live["en"] = receiveDirect; // UDP/Hyperion realtime
|
||||
if_live[F("mso")] = useMainSegmentOnly;
|
||||
if_live["port"] = e131Port;
|
||||
if_live[F("mc")] = e131Multicast;
|
||||
@ -888,6 +913,7 @@ void serializeConfig() {
|
||||
if_live[F("no-gc")] = arlsDisableGammaCorrection;
|
||||
if_live[F("offset")] = arlsOffset;
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
JsonObject if_va = interfaces.createNestedObject("va");
|
||||
if_va[F("alexa")] = alexaEnabled;
|
||||
|
||||
@ -896,6 +922,7 @@ void serializeConfig() {
|
||||
if_va_macros.add(macroAlexaOff);
|
||||
|
||||
if_va["p"] = alexaNumPresets;
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
|
||||
@ -912,13 +939,6 @@ void serializeConfig() {
|
||||
if_mqtt_topics[F("group")] = mqttGroupTopic;
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
JsonObject remote = doc.createNestedObject(F("remote"));
|
||||
remote[F("remote_enabled")] = enable_espnow_remote;
|
||||
remote[F("linked_remote")] = linked_remote;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
JsonObject if_hue = interfaces.createNestedObject("hue");
|
||||
if_hue["en"] = huePollingEnabled;
|
||||
@ -1033,7 +1053,7 @@ bool deserializeConfigSec() {
|
||||
JsonObject ap = doc["ap"];
|
||||
getStringFromJson(apPass, ap["psk"] , 65);
|
||||
|
||||
JsonObject interfaces = doc["if"];
|
||||
[[maybe_unused]] JsonObject interfaces = doc["if"];
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
JsonObject if_mqtt = interfaces["mqtt"];
|
||||
@ -1072,7 +1092,7 @@ void serializeConfigSec() {
|
||||
JsonObject ap = doc.createNestedObject("ap");
|
||||
ap["psk"] = apPass;
|
||||
|
||||
JsonObject interfaces = doc.createNestedObject("if");
|
||||
[[maybe_unused]] JsonObject interfaces = doc.createNestedObject("if");
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
|
||||
if_mqtt["psk"] = mqttPass;
|
||||
|
@ -35,8 +35,19 @@ uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16)
|
||||
* color add function that preserves ratio
|
||||
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
|
||||
*/
|
||||
uint32_t color_add(uint32_t c1, uint32_t c2)
|
||||
uint32_t color_add(uint32_t c1, uint32_t c2, bool fast)
|
||||
{
|
||||
if (fast) {
|
||||
uint8_t r = R(c1);
|
||||
uint8_t g = G(c1);
|
||||
uint8_t b = B(c1);
|
||||
uint8_t w = W(c1);
|
||||
r = qadd8(r, R(c2));
|
||||
g = qadd8(g, G(c2));
|
||||
b = qadd8(b, B(c2));
|
||||
w = qadd8(w, W(c2));
|
||||
return RGBW32(r,g,b,w);
|
||||
} else {
|
||||
uint32_t r = R(c1) + R(c2);
|
||||
uint32_t g = G(c1) + G(c2);
|
||||
uint32_t b = B(c1) + B(c2);
|
||||
@ -48,10 +59,35 @@ uint32_t color_add(uint32_t c1, uint32_t c2)
|
||||
if (max < 256) return RGBW32(r, g, b, w);
|
||||
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fades color toward black
|
||||
* if using "video" method the resulting color will never become black unless it is already black
|
||||
*/
|
||||
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video)
|
||||
{
|
||||
uint8_t r = R(c1);
|
||||
uint8_t g = G(c1);
|
||||
uint8_t b = B(c1);
|
||||
uint8_t w = W(c1);
|
||||
if (video) {
|
||||
r = scale8_video(r, amount);
|
||||
g = scale8_video(g, amount);
|
||||
b = scale8_video(b, amount);
|
||||
w = scale8_video(w, amount);
|
||||
} else {
|
||||
r = scale8(r, amount);
|
||||
g = scale8(g, amount);
|
||||
b = scale8(b, amount);
|
||||
w = scale8(w, amount);
|
||||
}
|
||||
return RGBW32(r, g, b, w);
|
||||
}
|
||||
|
||||
void setRandomColor(byte* rgb)
|
||||
{
|
||||
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex);
|
||||
lastRandomIndex = get_random_wheel_index(lastRandomIndex);
|
||||
colorHStoRGB(lastRandomIndex*256,255,rgb);
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@
|
||||
#define CALL_MODE_NO_NOTIFY 5
|
||||
#define CALL_MODE_FX_CHANGED 6 //no longer used
|
||||
#define CALL_MODE_HUE 7
|
||||
#define CALL_MODE_PRESET_CYCLE 8
|
||||
#define CALL_MODE_PRESET_CYCLE 8 //no longer used
|
||||
#define CALL_MODE_BLYNK 9 //no longer used
|
||||
#define CALL_MODE_ALEXA 10
|
||||
#define CALL_MODE_WS_SEND 11 //special call mode, not for notifier, updates websocket only
|
||||
@ -270,6 +270,10 @@
|
||||
#define COL_ORDER_GBR 5
|
||||
#define COL_ORDER_MAX 5
|
||||
|
||||
//ESP-NOW
|
||||
#define ESP_NOW_STATE_UNINIT 0
|
||||
#define ESP_NOW_STATE_ON 1
|
||||
#define ESP_NOW_STATE_ERROR 2
|
||||
|
||||
//Button type
|
||||
#define BTN_TYPE_NONE 0
|
||||
@ -313,10 +317,9 @@
|
||||
#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment
|
||||
#define SEG_OPTION_FREEZE 4 //Segment contents will not be refreshed
|
||||
#define SEG_OPTION_RESET 5 //Segment runtime requires reset
|
||||
#define SEG_OPTION_TRANSITIONAL 6
|
||||
#define SEG_OPTION_REVERSED_Y 7
|
||||
#define SEG_OPTION_MIRROR_Y 8
|
||||
#define SEG_OPTION_TRANSPOSED 9
|
||||
#define SEG_OPTION_REVERSED_Y 6
|
||||
#define SEG_OPTION_MIRROR_Y 7
|
||||
#define SEG_OPTION_TRANSPOSED 8
|
||||
|
||||
//Segment differs return byte
|
||||
#define SEG_DIFFERS_BRI 0x01 // opacity
|
||||
@ -345,6 +348,7 @@
|
||||
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached
|
||||
#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist
|
||||
#define ERR_FS_IRLOAD 13 // It was attempted to load an IR JSON cmd, but the "ir.json" file does not exist
|
||||
#define ERR_FS_RMLOAD 14 // It was attempted to load an remote JSON cmd, but the "remote.json" file does not exist
|
||||
#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured
|
||||
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
|
||||
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
|
||||
|
@ -20,8 +20,8 @@
|
||||
--c-g: #2c1;
|
||||
--c-l: #48a;
|
||||
--c-y: #a90;
|
||||
--t-b: 0.5;
|
||||
--c-o: rgba(34, 34, 34, 0.9);
|
||||
--t-b: .5;
|
||||
--c-o: rgba(34, 34, 34, .9);
|
||||
--c-tb : rgba(34, 34, 34, var(--t-b));
|
||||
--c-tba: rgba(102, 102, 102, var(--t-b));
|
||||
--c-tbh: rgba(51, 51, 51, var(--t-b));
|
||||
@ -33,7 +33,8 @@
|
||||
--bbp: 9px 0 7px 0;
|
||||
--bhd: none;
|
||||
--sgp: "block";
|
||||
--bmt: 0px;
|
||||
--bmt: 0;
|
||||
--sti: 42px;
|
||||
}
|
||||
|
||||
html {
|
||||
@ -88,7 +89,7 @@ a, a:visited {
|
||||
}
|
||||
|
||||
button {
|
||||
outline: none;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -238,7 +239,7 @@ button {
|
||||
.flr {
|
||||
color: var(--c-f);
|
||||
transform: rotate(0deg);
|
||||
transition: transform 0.3s;
|
||||
transition: transform .3s;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@ -258,13 +259,13 @@ button {
|
||||
#liveview {
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
border: 0px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#liveview2D {
|
||||
height: 90%;
|
||||
width: 90%;
|
||||
border: 0px;
|
||||
border: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
@ -287,8 +288,8 @@ button {
|
||||
.tab button {
|
||||
background-color: transparent;
|
||||
float: left;
|
||||
border: none;
|
||||
transition: color 0.3s, background-color 0.3s;
|
||||
border: 0;
|
||||
transition: color .3s, background-color .3s;
|
||||
font-size: 17px;
|
||||
color: var(--c-c);
|
||||
min-width: 44px;
|
||||
@ -336,8 +337,9 @@ button {
|
||||
width: 100%;
|
||||
width: calc(100%/var(--n));
|
||||
box-sizing: border-box;
|
||||
border: 0px;
|
||||
overflow: auto;
|
||||
border: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: 100%;
|
||||
overscroll-behavior: none;
|
||||
padding: 0 4px;
|
||||
@ -388,8 +390,8 @@ button {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 11;
|
||||
opacity: 0.95;
|
||||
transition: 0.7s;
|
||||
opacity: .95;
|
||||
transition: .7s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -456,65 +458,55 @@ button {
|
||||
padding: 4px 2px;
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s linear, height 0.5s, transform 0.5s;
|
||||
transition: opacity .5s linear, height .25s, transform .25s;
|
||||
}
|
||||
|
||||
.filter {
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
/*overflow: visible;*/
|
||||
border-radius: 0 0 16px 16px;
|
||||
max-width: 220px;
|
||||
height: 54px;
|
||||
line-height: 1.5;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Tooltip text */
|
||||
.slider .tooltiptext, .option .tooltiptext {
|
||||
/* New tooltip */
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity .25s ease, visibility .25s ease;
|
||||
background-color: var(--c-5);
|
||||
/*border: 2px solid var(--c-2);*/
|
||||
box-shadow: 4px 4px 10px 4px var(--c-1);
|
||||
color: var(--c-f);
|
||||
text-align: center;
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
|
||||
/* Position the tooltip text */
|
||||
width: 160px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 80%;
|
||||
left: 50%;
|
||||
margin-left: -92px;
|
||||
|
||||
/* Ensure tooltip goes away when mouse leaves control */
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Fade in tooltip */
|
||||
opacity: 0;
|
||||
transition: opacity 0.75s;
|
||||
}
|
||||
.option .tooltiptext {
|
||||
bottom: 120%;
|
||||
}
|
||||
/* Tooltip arrow */
|
||||
.slider .tooltiptext::after, .option .tooltiptext::after {
|
||||
.tooltip::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border: 8px solid;
|
||||
border-color: var(--c-5) transparent transparent transparent;
|
||||
top: 100%;
|
||||
left: calc(50% - 8px);
|
||||
z-index: 0;
|
||||
}
|
||||
/* Show the tooltip text when you mouse over the tooltip container */
|
||||
.slider:hover .tooltiptext, .option .check:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
|
||||
.tooltip.visible {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.fade {
|
||||
visibility: hidden; /* hide it */
|
||||
opacity: 0; /* make it transparent */
|
||||
transform: scaleY(0); /* shrink content */
|
||||
height: 0px; /* force other elements to move */
|
||||
height: 0; /* force other elements to move */
|
||||
padding: 0; /* remove empty space */
|
||||
}
|
||||
|
||||
@ -542,24 +534,24 @@ button {
|
||||
|
||||
#toast.show {
|
||||
opacity: 1;
|
||||
animation: fadein 0.5s, fadein 0.5s 2.5s reverse;
|
||||
animation: fadein .5s, fadein .5s 2.5s reverse;
|
||||
}
|
||||
|
||||
#toast.error {
|
||||
opacity: 1;
|
||||
background-color: #b21;
|
||||
animation: fadein 0.5s;
|
||||
animation: fadein .5s;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: calc(var(--th) - 1px);
|
||||
background-color: var(--c-o);
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.4s;
|
||||
transition: transform .4s;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
overflow: auto;
|
||||
@ -641,7 +633,7 @@ button {
|
||||
}
|
||||
|
||||
#heart {
|
||||
transition: color 0.9s;
|
||||
transition: color .9s;
|
||||
font-size: 16px;
|
||||
color: #f00;
|
||||
}
|
||||
@ -720,7 +712,7 @@ input[type=range] {
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
outline: 0;
|
||||
}
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
@ -743,7 +735,7 @@ input[type=range]::-moz-range-track {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
input[type=range]::-moz-range-thumb {
|
||||
border: 0px solid rgba(0, 0, 0, 0);
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
border-radius: 50%;
|
||||
@ -770,7 +762,7 @@ input[type=range]::-moz-range-thumb {
|
||||
}
|
||||
|
||||
#briwrap {
|
||||
min-width: 267px;
|
||||
min-width: 300px;
|
||||
float: right;
|
||||
margin-top: var(--bmt);
|
||||
}
|
||||
@ -789,7 +781,7 @@ input[type=range]::-moz-range-thumb {
|
||||
color: var(--c-d);
|
||||
cursor: pointer;
|
||||
border-radius: 25px;
|
||||
transition-duration: 0.3s;
|
||||
transition-duration: .3s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
backface-visibility: hidden;
|
||||
@ -893,7 +885,7 @@ select {
|
||||
cursor: pointer;
|
||||
border: 0 solid var(--c-2);
|
||||
border-radius: 20px;
|
||||
transition-duration: 0.5s;
|
||||
transition-duration: .5s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
-webkit-appearance: none;
|
||||
@ -942,13 +934,13 @@ input[type=number],
|
||||
input[type=text] {
|
||||
background: var(--c-3);
|
||||
color: var(--c-f);
|
||||
border: 0px solid var(--c-2);
|
||||
border: 0 solid var(--c-2);
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
/*margin: 6px 6px 6px 0;*/
|
||||
font-size: 19px;
|
||||
transition: background-color 0.2s;
|
||||
outline: none;
|
||||
transition: background-color .2s;
|
||||
outline: 0;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
appearance: textfield;
|
||||
@ -988,7 +980,7 @@ textarea {
|
||||
height: 90px;
|
||||
border-radius: 5px;
|
||||
border: 2px solid var(--c-5);
|
||||
outline: none;
|
||||
outline: 0;
|
||||
resize: none;
|
||||
font-size: 19px;
|
||||
padding: 5px;
|
||||
@ -1121,8 +1113,8 @@ textarea {
|
||||
}
|
||||
|
||||
.revchkl {
|
||||
padding: 4px 0px 0px 35px;
|
||||
margin-bottom: 0px;
|
||||
padding: 4px 0 0 35px;
|
||||
margin-bottom: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@ -1218,9 +1210,9 @@ TD .checkmark, TD .radiomark {
|
||||
.seg, .pres {
|
||||
background-color: var(--c-2);
|
||||
/*color: var(--c-f);*/ /* seems to affect only the Add segment button, which should be same color as reset segments */
|
||||
border: 0px solid var(--c-f);
|
||||
border: 0 solid var(--c-f);
|
||||
text-align: left;
|
||||
transition: background-color 0.5s;
|
||||
transition: background-color .5s;
|
||||
border-radius: 21px;
|
||||
}
|
||||
|
||||
@ -1237,14 +1229,18 @@ TD .checkmark, TD .radiomark {
|
||||
/* checkmark labels */
|
||||
.filter .fchkl, .option .ochkl {
|
||||
display: inline-block;
|
||||
min-width: 0.7em;
|
||||
padding: 1px 4px 4px 32px;
|
||||
min-width: .7em;
|
||||
padding: 1px 4px 1px 32px;
|
||||
text-align: left;
|
||||
line-height: 24px;
|
||||
vertical-align: middle;
|
||||
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
.filter .fchkl {
|
||||
margin: 0 4px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.lbl-l {
|
||||
font-size: 13px;
|
||||
@ -1263,7 +1259,7 @@ TD .checkmark, TD .radiomark {
|
||||
/* list wrapper */
|
||||
.list {
|
||||
position: relative;
|
||||
transition: background-color 0.5s;
|
||||
transition: background-color .5s;
|
||||
margin: auto auto 10px;
|
||||
line-height: 24px;
|
||||
}
|
||||
@ -1313,7 +1309,7 @@ TD .checkmark, TD .radiomark {
|
||||
.lstI.sticky,
|
||||
.lstI.selected {
|
||||
z-index: 1;
|
||||
box-shadow: 0px 0px 10px 4px var(--c-1);
|
||||
box-shadow: 0 0 10px 4px var(--c-1);
|
||||
}
|
||||
|
||||
#pcont .selected:not([class*="expanded"]) {
|
||||
@ -1321,20 +1317,14 @@ TD .checkmark, TD .radiomark {
|
||||
top: 42px;
|
||||
}
|
||||
|
||||
#fxlist .lstI.selected {
|
||||
top: 84px;
|
||||
}
|
||||
|
||||
#fxlist .lstI.sticky {
|
||||
top: 42px;
|
||||
}
|
||||
|
||||
#fxlist .lstI.selected,
|
||||
#pallist .lstI.selected {
|
||||
top: 84px;
|
||||
top: calc(var(--sti) + 42px);
|
||||
}
|
||||
|
||||
#fxlist .lstI.sticky,
|
||||
#pallist .lstI.sticky {
|
||||
top: 42px;
|
||||
top: var(--sti);
|
||||
}
|
||||
|
||||
/* list item content */
|
||||
@ -1457,7 +1447,7 @@ TD .checkmark, TD .radiomark {
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--c-sb);
|
||||
opacity: 0.2;
|
||||
opacity: .2;
|
||||
border-radius: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
|
@ -48,7 +48,7 @@
|
||||
});
|
||||
setTimeout(()=>{h.appendChild(l)},100);
|
||||
});
|
||||
setTimeout(()=>{h.appendChild(l)},100);
|
||||
setTimeout(()=>{h.appendChild(l)},200);
|
||||
</script>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
@ -72,8 +72,8 @@
|
||||
</div>
|
||||
<div id="briwrap">
|
||||
<p class="hd">Brightness</p>
|
||||
<div class="slider" style="padding-right:32px;">
|
||||
<i class="icons slider-icon" onclick="tglTheme()" style="transform: translate(-32px,5px);"></i>
|
||||
<div class="slider">
|
||||
<i class="icons slider-icon" onclick="tglTheme()" style="transform: translateY(2px);"></i>
|
||||
<div class="sliderwrap il">
|
||||
<input id="sliderBri" onchange="setBri()" oninput="updateTrail(this)" max="255" min="1" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
@ -88,98 +88,91 @@
|
||||
<div class ="container">
|
||||
<div id="Colors" class="tabcontent">
|
||||
<div id="picker" class="noslide"></div>
|
||||
<div id="hwrap" class="slider" style="margin-top: 20px;">
|
||||
<div class="sliderwrap il">
|
||||
<div id="hwrap" class="slider">
|
||||
<div tooltip="Hue" class="sliderwrap il">
|
||||
<input id="sliderH" class="noslide" oninput="fromH()" onchange="setColor(0)" max="359" min="0" type="range" value="0" step="any">
|
||||
<div class="sliderdisplay" style="background: linear-gradient(90deg, #f00 2%, #ff0 19%, #0f0 35%, #0ff 52%, #00f 68%, #f0f 85%, #f00)"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Hue</span>
|
||||
</div>
|
||||
<div id="swrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Saturation" class="sliderwrap il">
|
||||
<input id="sliderS" class="noslide" oninput="fromS()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any">
|
||||
<div class="sliderdisplay" style="background: linear-gradient(90deg, #aaa 0%, #f00)"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Saturation</span>
|
||||
</div>
|
||||
<div id="vwrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Value/Brightness" class="sliderwrap il">
|
||||
<input id="sliderV" class="noslide" oninput="fromV()" onchange="setColor(0)" max="100" min="0" type="range" value="100" step="any" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Value/Brightness</span>
|
||||
</div>
|
||||
<div id="kwrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Kelvin/Temperature" class="sliderwrap il">
|
||||
<input id="sliderK" class="noslide" oninput="fromK()" onchange="setColor(0)" max="10091" min="1900" type="range" value="6550" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Kelvin/Temperature</span>
|
||||
</div>
|
||||
<div id="rgbwrap">
|
||||
<!--p class="labels hd">RGB color</p-->
|
||||
<div id="rwrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Red channel" class="sliderwrap il">
|
||||
<input id="sliderR" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Red channel</span>
|
||||
</div>
|
||||
<div id="gwrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Green channel" class="sliderwrap il">
|
||||
<input id="sliderG" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Green channel</span>
|
||||
</div>
|
||||
<div id="bwrap" class="slider">
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Blue channel" class="sliderwrap il">
|
||||
<input id="sliderB" class="noslide" oninput="fromRgb()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">Blue channel</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wwrap" class="slider">
|
||||
<!--p class="labels hd">White channel</p-->
|
||||
<div id="whibri" class="sliderwrap il">
|
||||
<div id="whibri" tooltip="White channel" class="sliderwrap il">
|
||||
<input id="sliderW" class="noslide" oninput="fromW()" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">White channel</span>
|
||||
</div>
|
||||
<div id="wbal" class="slider">
|
||||
<!--p class="labels hd">White balance</p-->
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="White balance" class="sliderwrap il">
|
||||
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<span class="tooltiptext">White balance</span>
|
||||
</div>
|
||||
<div id="qcs-w">
|
||||
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffc800');" title="Yellow" style="background-color:#ffc800;"></div>
|
||||
<div class="qcs" onclick="pC('#ffe0a0');" title="Warm White" style="background-color:#ffe0a0;"></div>
|
||||
<div class="qcs" onclick="pC('#ffffff');" title="White" style="background-color:#ffffff;"></div>
|
||||
<div class="qcs qcsb" onclick="pC('#000000');" title="Black" style="background-color:#000000;"></div><br>
|
||||
<div class="qcs" onclick="pC('#ff00ff');" title="Pink" style="background-color:#ff00ff;"></div>
|
||||
<div class="qcs" onclick="pC('#0000ff');" title="Blue" style="background-color:#0000ff;"></div>
|
||||
<div class="qcs" onclick="pC('#00ffc8');" title="Cyan" style="background-color:#00ffc8;"></div>
|
||||
<div class="qcs" onclick="pC('#08ff00');" title="Green" style="background-color:#08ff00;"></div>
|
||||
<div class="qcs" onclick="pC('rnd');" title="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
|
||||
<div class="qcs" onclick="pC('#ff0000');" tooltip="Red" style="background-color:#ff0000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffa000');" tooltip="Orange" style="background-color:#ffa000;"></div>
|
||||
<div class="qcs" onclick="pC('#ffc800');" tooltip="Yellow" style="background-color:#ffc800;"></div>
|
||||
<div class="qcs" onclick="pC('#ffe0a0');" tooltip="Warm White" style="background-color:#ffe0a0;"></div>
|
||||
<div class="qcs" onclick="pC('#ffffff');" tooltip="White" style="background-color:#ffffff;"></div>
|
||||
<div class="qcs qcsb" onclick="pC('#000000');" tooltip="Black" style="background-color:#000000;"></div><br>
|
||||
<div class="qcs" onclick="pC('#ff00ff');" tooltip="Pink" style="background-color:#ff00ff;"></div>
|
||||
<div class="qcs" onclick="pC('#0000ff');" tooltip="Blue" style="background-color:#0000ff;"></div>
|
||||
<div class="qcs" onclick="pC('#00ffc8');" tooltip="Cyan" style="background-color:#00ffc8;"></div>
|
||||
<div class="qcs" onclick="pC('#08ff00');" tooltip="Green" style="background-color:#08ff00;"></div>
|
||||
<div class="qcs" onclick="pC('rnd');" tooltip="Random" style="background:linear-gradient(to right, red, orange, yellow, green, blue, purple);transform: translateY(-11px);">R</div>
|
||||
</div>
|
||||
<div id="csl">
|
||||
<button id="csl0" class="btn xxs cl" onclick="selectSlot(0);" data-r="0" data-g="0" data-b="0" data-w="0">1</button>
|
||||
<button id="csl1" class="btn xxs cl" onclick="selectSlot(1);" data-r="0" data-g="0" data-b="0" data-w="0">2</button>
|
||||
<button id="csl2" class="btn xxs cl" onclick="selectSlot(2);" data-r="0" data-g="0" data-b="0" data-w="0">3</button>
|
||||
<button id="csl0" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(0);" data-r="0" data-g="0" data-b="0" data-w="0">1</button>
|
||||
<button id="csl1" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(1);" data-r="0" data-g="0" data-b="0" data-w="0">2</button>
|
||||
<button id="csl2" tooltip="Select slot" class="btn xxs cl" onclick="selectSlot(2);" data-r="0" data-g="0" data-b="0" data-w="0">3</button>
|
||||
</div>
|
||||
<p class="labels h" id="cslLabel"></p>
|
||||
<div id="hexw">
|
||||
<i class="icons sel-icon" onclick="tglRgb()"></i>
|
||||
<input id="hexc" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
|
||||
<input id="hexc" tooltip="Hex RGB" type="text" class="noslide" onkeydown="hexEnter()" autocomplete="off" maxlength="8" />
|
||||
<button id="hexcnf" class="btn btn-xs" onclick="fromHex();"><i class="icons btn-icon"></i></button>
|
||||
</div>
|
||||
<div style="padding: 8px 0;">
|
||||
<button class="btn btn-xs" tooltip="Pixel Magic Tool" type="button" id="pxmb" onclick="window.location.href=getURL('/pxmagic.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" tooltip="Add custom palette" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" tooltip="Remove custom palette" type="button" id="rmPal" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
||||
</div>
|
||||
<p class="labels" id="pall"><i class="icons sel-icon" onclick="tglHex()"></i> Color palette</p>
|
||||
<div id="palw" class="il">
|
||||
<div class="staytop fnd">
|
||||
@ -198,10 +191,6 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding-bottom: 10px;">
|
||||
<button class="btn btn-xs" type="button" onclick="window.location.href=getURL('/cpal.htm')"><i class="icons btn-icon"></i></button>
|
||||
<button class="btn btn-xs" type="button" onclick="palettesData=null;localStorage.removeItem('wledPalx');requestJson({rmcpal:true});setTimeout(loadPalettes,250,loadPalettesData);"><i class="icons btn-icon"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -209,9 +198,35 @@
|
||||
<div id="fx">
|
||||
<p class="labels hd" id="modeLabel">Effect mode</p>
|
||||
<div class="staytop fnd" id="fxFind">
|
||||
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist');gId('filters').classList.add('fade');" onblur="gId('filters').classList.remove('fade')"/>
|
||||
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="filterFocus(event);search(this,'fxlist');" onblur="filterFocus(event);" />
|
||||
<i class="icons clear-icon" onclick="clean(this);"></i>
|
||||
<i class="icons search-icon" onclick="gId('filters').classList.toggle('hide');" style="cursor:pointer;"></i>
|
||||
<i class="icons search-icon" style="cursor:pointer;"></i>
|
||||
<div id="filters" class="filter fade">
|
||||
<label id="filterPal" tooltip="Uses palette" class="check fchkl">🎨
|
||||
<input type="checkbox" data-flt="🎨" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter0D" tooltip="Single pixel" class="check fchkl">•
|
||||
<input type="checkbox" data-flt="•" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter1D" tooltip="1D" class="check fchkl">⋮
|
||||
<input type="checkbox" data-flt="⋮" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter2D" tooltip="2D" class="check fchkl">▦
|
||||
<input type="checkbox" data-flt="▦" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterVol" tooltip="Volume" class="check fchkl">♪
|
||||
<input type="checkbox" data-flt="♪" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterFreq" tooltip="Frequency" class="check fchkl">♫
|
||||
<input type="checkbox" data-flt="♫" onchange="filterFx(this);">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fxlist" class="list">
|
||||
<div class="lstI">
|
||||
@ -226,87 +241,56 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="sliders">
|
||||
<div id="filters" class="filter">
|
||||
<label id="filterPal" class="check fchkl">🎨
|
||||
<input type="checkbox" data-flt="🎨" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter0D" class="check fchkl hide">•
|
||||
<input type="checkbox" data-flt="•" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter1D" class="check fchkl">⋮
|
||||
<input type="checkbox" data-flt="⋮" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filter2D" class="check fchkl">▦
|
||||
<input type="checkbox" data-flt="▦" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterVol" class="check fchkl">♪
|
||||
<input type="checkbox" data-flt="♪" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="filterFreq" class="check fchkl">♫
|
||||
<input type="checkbox" data-flt="♫" onchange="filterFx(this)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="slider0" class="slider">
|
||||
<i class="icons slider-icon" onclick="tglFreeze()"></i>
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Effect speed" class="sliderwrap il">
|
||||
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span id="sliderLabel0" class="tooltiptext">Effect speed</span>
|
||||
</div>
|
||||
<div id="slider1" class="slider">
|
||||
<i class="icons slider-icon" onclick="tglLabels()"></i>
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Effect intensity" class="sliderwrap il">
|
||||
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel1">Effect intensity</span>
|
||||
</div>
|
||||
<div id="slider2" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Custom 1" class="sliderwrap il">
|
||||
<input id="sliderC1" class="noslide" onchange="setCustom(1)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel2">Custom 1</span>
|
||||
</div>
|
||||
<div id="slider3" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Custom 2" class="sliderwrap il">
|
||||
<input id="sliderC2" class="noslide" onchange="setCustom(2)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel3">Custom 2</span>
|
||||
</div>
|
||||
<div id="slider4" class="slider hide">
|
||||
<i class="icons slider-icon"></i>
|
||||
<div class="sliderwrap il">
|
||||
<div tooltip="Custom 3" class="sliderwrap il">
|
||||
<input id="sliderC3" class="noslide" onchange="setCustom(3)" oninput="updateTrail(this)" max="31" min="0" type="range" value="0" />
|
||||
<div class="sliderdisplay"></div>
|
||||
</div>
|
||||
<output class="sliderbubble"></output>
|
||||
<span class="tooltiptext" id="sliderLabel4">Custom 3</span>
|
||||
</div>
|
||||
<div id="fxopt" class="option fade">
|
||||
<label id="opt0" class="check ochkl hide"><i class="icons"></i><span class="tooltiptext" id="optLabel0">Check 1</span>
|
||||
<label id="opt0" tooltip="Check 1" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO1" type="checkbox" onchange="setOption(1, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="opt1" class="check ochkl hide"><i class="icons"></i><span class="tooltiptext" id="optLabel1">Check 2</span>
|
||||
<label id="opt1" tooltip="Check 2" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO2" type="checkbox" onchange="setOption(2, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label id="opt2" class="check ochkl hide"><i class="icons"></i><span class="tooltiptext" id="optLabel2">Check 3</span>
|
||||
<label id="opt2" tooltip="Check 3" class="check ochkl hide"><i class="icons"></i>
|
||||
<input id="checkO3" type="checkbox" onchange="setOption(3, this.checked)">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
@ -392,6 +376,7 @@
|
||||
<button class="btn" onclick="setLor(2)">Override until reboot</button><br>
|
||||
<span class="h">For best performance, it is recommended to turn off the streaming source when not in use.</span>
|
||||
</div>
|
||||
|
||||
<i id="roverstar" class="icons huge" onclick="setLor(0)"></i><br>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
|
@ -1,6 +1,6 @@
|
||||
//page js
|
||||
var loc = false, locip, locproto = "http:";
|
||||
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false, syncTglRecv = true;
|
||||
var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, syncSend = false/*, syncTglRecv = true*/;
|
||||
var hasWhite = false, hasRGB = false, hasCCT = false;
|
||||
var nlDur = 60, nlTar = 0;
|
||||
var nlMode = false;
|
||||
@ -24,19 +24,18 @@ var lastinfo = {};
|
||||
var isM = false, mw = 0, mh=0;
|
||||
var ws, cpick, ranges, wsRpt=0;
|
||||
var cfg = {
|
||||
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
|
||||
theme:{base:"dark", bg:{url:"", rnd: false, rndGrayscale: false, rndBlur: false}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
|
||||
comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
|
||||
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false,
|
||||
css:true, hdays:false, fxdef:true}
|
||||
css:true, hdays:false, fxdef:true, on:0, off:0, idsort: false}
|
||||
};
|
||||
var hol = [
|
||||
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
|
||||
[0,2,17,1,"https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
|
||||
[2025,3,20,2,"https://aircoookie.github.io/easter.png"],
|
||||
[2023,3,9,2,"https://aircoookie.github.io/easter.png"],
|
||||
[2024,2,31,2,"https://aircoookie.github.io/easter.png"],
|
||||
[0,6,4,1,"https://initiate.alphacoders.com/download/wallpaper/516792/images/jpg/510921363292536"], // 4th of July
|
||||
[0,0,1,1,"https://initiate.alphacoders.com/download/wallpaper/1198800/images/jpg/2522807481585600"] // new year
|
||||
[0,6,4,1,"https://images.alphacoders.com/516/516792.jpg"], // 4th of July
|
||||
[0,0,1,1,"https://images.alphacoders.com/119/1198800.jpg"] // new year
|
||||
];
|
||||
|
||||
function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3000) requestJson();}
|
||||
@ -74,6 +73,7 @@ function setCSL(cs)
|
||||
function applyCfg()
|
||||
{
|
||||
cTheme(cfg.theme.base === "light");
|
||||
gId("Colors").style.paddingTop = cfg.comp.colors.picker ? "0" : "28px";
|
||||
var bg = cfg.theme.color.bg;
|
||||
if (bg) sCol('--c-1', bg);
|
||||
var l = cfg.comp.labels;
|
||||
@ -109,6 +109,7 @@ function tglLabels()
|
||||
function tglRgb()
|
||||
{
|
||||
cfg.comp.colors.rgb = !cfg.comp.colors.rgb;
|
||||
cfg.comp.colors.picker = !cfg.comp.colors.picker;
|
||||
applyCfg();
|
||||
}
|
||||
|
||||
@ -217,13 +218,11 @@ function onLoad()
|
||||
// detect reverse proxy and/or HTTPS
|
||||
let pathn = l.pathname;
|
||||
let paths = pathn.slice(1,pathn.endsWith('/')?-1:undefined).split("/");
|
||||
//if (paths[0]==="sliders") paths.shift();
|
||||
//while (paths[0]==="") paths.shift();
|
||||
locproto = l.protocol;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "");
|
||||
if (paths.length > 0 && paths[0]!=="") {
|
||||
loc = true;
|
||||
locip += "/" + paths[0];
|
||||
locip += "/" + paths.join('/');
|
||||
} else if (locproto==="https:") {
|
||||
loc = true;
|
||||
}
|
||||
@ -231,6 +230,7 @@ function onLoad()
|
||||
var sett = localStorage.getItem('wledUiCfg');
|
||||
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
|
||||
|
||||
tooltip();
|
||||
resetPUtil();
|
||||
|
||||
if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true);
|
||||
@ -255,7 +255,6 @@ function onLoad()
|
||||
});
|
||||
} else
|
||||
loadBg(cfg.theme.bg.url);
|
||||
if (cfg.comp.css) loadSkinCSS('skinCss');
|
||||
|
||||
selectSlot(0);
|
||||
updateTablinks(0);
|
||||
@ -265,14 +264,17 @@ function onLoad()
|
||||
loadPalettes(()=>{
|
||||
// fill effect extra data array
|
||||
loadFXData(()=>{
|
||||
setTimeout(()=>{ // ESP8266 can't handle quick requests
|
||||
// load and populate effects
|
||||
loadFX(()=>{
|
||||
setTimeout(()=>{ // ESP8266 can't handle quick requests
|
||||
loadPalettesData(()=>{
|
||||
requestJson();// will load presets and create WS
|
||||
if (cfg.comp.css) setTimeout(()=>{loadSkinCSS('skinCss')},100);
|
||||
});
|
||||
},100);
|
||||
});
|
||||
},100);
|
||||
});
|
||||
});
|
||||
resetUtil();
|
||||
@ -280,11 +282,10 @@ function onLoad()
|
||||
d.addEventListener("visibilitychange", handleVisibilityChange, false);
|
||||
//size();
|
||||
gId("cv").style.opacity=0;
|
||||
var sls = d.querySelectorAll('input[type="range"]');
|
||||
for (var sl of sls) {
|
||||
d.querySelectorAll('input[type="range"]').forEach((sl)=>{
|
||||
sl.addEventListener('touchstart', toggleBubble);
|
||||
sl.addEventListener('touchend', toggleBubble);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateTablinks(tabI)
|
||||
@ -426,18 +427,30 @@ function presetError(empty)
|
||||
if (hasBackup) {
|
||||
cn += `<br><br>`;
|
||||
if (empty)
|
||||
cn += `However, there is backup preset data of a previous installation available.<br>
|
||||
(Saving a preset will hide this and overwrite the backup)`;
|
||||
cn += `However, there is backup preset data of a previous installation available.<br>(Saving a preset will hide this and overwrite the backup)`;
|
||||
else
|
||||
cn += `Here is a backup of the last known good state:`;
|
||||
cn += `<textarea id="bck"></textarea><br>
|
||||
<button class="btn" onclick="cpBck()">Copy to clipboard</button>`;
|
||||
cn += `<textarea id="bck"></textarea><br><button class="btn" onclick="cpBck()">Copy to clipboard</button>`;
|
||||
cn += `<br><button type="button" class="btn" onclick="restore(gId('bck').value)">Restore</button>`;
|
||||
}
|
||||
cn += `</div>`;
|
||||
gId('pcont').innerHTML = cn;
|
||||
if (hasBackup) gId('bck').value = bckstr;
|
||||
}
|
||||
|
||||
function restore(txt) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
||||
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
||||
req.open("POST", getURL("/upload"));
|
||||
var formData = new FormData();
|
||||
var b = new Blob([txt], {type: "application/json"});
|
||||
formData.append("data", b, '/presets.json');
|
||||
req.send(formData);
|
||||
setTimeout(loadPresets, 2000);
|
||||
return false;
|
||||
}
|
||||
|
||||
function loadPresets(callback = null)
|
||||
{
|
||||
// 1st boot (because there is a callback)
|
||||
@ -566,8 +579,7 @@ function populatePresets(fromls)
|
||||
if (!pJson) {setTimeout(loadPresets,250); return;}
|
||||
delete pJson["0"];
|
||||
var cn = "";
|
||||
var arr = Object.entries(pJson);
|
||||
arr.sort(cmpP);
|
||||
var arr = Object.entries(pJson).sort(cmpP);
|
||||
pQL = [];
|
||||
var is = [];
|
||||
pNum = 0;
|
||||
@ -613,7 +625,7 @@ function parseInfo(i) {
|
||||
if (loc) name = "(L) " + name;
|
||||
d.title = name;
|
||||
ledCount = i.leds.count;
|
||||
syncTglRecv = i.str;
|
||||
//syncTglRecv = i.str;
|
||||
maxSeg = i.leds.maxseg;
|
||||
pmt = i.fs.pmt;
|
||||
gId('buttonNodes').style.display = lastinfo.ndc > 0 ? null:"none";
|
||||
@ -622,12 +634,12 @@ function parseInfo(i) {
|
||||
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("filter0D").classList.remove('hide');
|
||||
//gId("filter1D").classList.add('hide');
|
||||
gId("filter2D").classList.add('hide');
|
||||
} else {
|
||||
gId("filter0D").classList.add('hide');
|
||||
gId("filter1D").classList.remove('hide');
|
||||
//gId("filter0D").classList.add('hide');
|
||||
//gId("filter1D").classList.remove('hide');
|
||||
gId("filter2D").classList.remove('hide');
|
||||
}
|
||||
// if (i.noaudio) {
|
||||
@ -672,8 +684,6 @@ function populateInfo(i)
|
||||
}
|
||||
}
|
||||
var vcn = "Kuuhaku";
|
||||
if (i.ver.startsWith("0.14.")) vcn = "Hoshi";
|
||||
// if (i.ver.includes("-bl")) vcn = "Supāku";
|
||||
if (i.cn) vcn = i.cn;
|
||||
|
||||
cn += `v${i.ver} "${vcn}"<br><br><table>
|
||||
@ -694,7 +704,7 @@ ${inforow("Environment",i.arch + " " + i.core + " (" + i.lwip + ")")}
|
||||
</table>`;
|
||||
gId('kv').innerHTML = cn;
|
||||
// update all sliders in Info
|
||||
for (let sd of (gId('kv').getElementsByClassName('sliderdisplay')||[])) {
|
||||
for (let sd of (d.querySelectorAll('#kv .sliderdisplay')||[])) {
|
||||
let s = sd.previousElementSibling;
|
||||
if (s) updateTrail(s);
|
||||
}
|
||||
@ -916,8 +926,9 @@ 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<lastinfo.cpalcount; j++) {
|
||||
let li = lastinfo;
|
||||
if (!isEmpty(li) && li.cpalcount) {
|
||||
for (let j = 0; j<li.cpalcount; j++) {
|
||||
let div = d.createElement("div");
|
||||
gId('pallist').appendChild(div);
|
||||
div.outerHTML = generateListItemHtml(
|
||||
@ -929,6 +940,8 @@ function populatePalettes()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (li.cpalcount>0) gId("rmPal").classList.remove("hide");
|
||||
else gId("rmPal").classList.add("hide");
|
||||
}
|
||||
|
||||
function redrawPalPrev()
|
||||
@ -1074,7 +1087,7 @@ function updateTrail(e)
|
||||
{
|
||||
if (e==null) return;
|
||||
let sd = e.parentNode.getElementsByClassName('sliderdisplay')[0];
|
||||
if (sd && getComputedStyle(sd).getPropertyValue("--bg") !== "none") {
|
||||
if (sd && getComputedStyle(sd).getPropertyValue("--bg").trim() !== "none") { // trim() for Safari
|
||||
var max = e.hasAttribute('max') ? e.attributes.max.value : 255;
|
||||
var perc = Math.round(e.value * 100 / max);
|
||||
if (perc < 50) perc += 2;
|
||||
@ -1194,6 +1207,7 @@ function updateUI()
|
||||
gId('buttonPower').className = (isOn) ? 'active':'';
|
||||
gId('buttonNl').className = (nlA) ? 'active':'';
|
||||
gId('buttonSync').className = (syncSend) ? 'active':'';
|
||||
gId('pxmb').style.display = (isM) ? "inline-block" : "none";
|
||||
|
||||
updateSelectedFx();
|
||||
updateSelectedPalette(selectedPal); // must be after updateSelectedFx() to un-hide color slots for * palettes
|
||||
@ -1271,11 +1285,11 @@ function updateSelectedFx()
|
||||
selectedEffect.classList.add('selected');
|
||||
setEffectParameters(selectedFx);
|
||||
// hide non-0D effects if segment only has 1 pixel (0D)
|
||||
var fxs = parent.querySelectorAll('.lstI');
|
||||
for (const fx of fxs) {
|
||||
if (!fx.dataset.opt) continue;
|
||||
let opts = fx.dataset.opt.split(";");
|
||||
if (fx.dataset.id>0) {
|
||||
parent.querySelectorAll('.lstI').forEach((fx)=>{
|
||||
let ds = fx.dataset;
|
||||
if (ds.opt) {
|
||||
let opts = ds.opt.split(";");
|
||||
if (ds.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)) || (!isM && opts[3] && ((opts[3].indexOf("2")>=0 && opts[3].indexOf("1")<0)))) fx.classList.add('hide');
|
||||
@ -1283,6 +1297,7 @@ function updateSelectedFx()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// hide 2D mapping and/or sound simulation options
|
||||
var selectedName = selectedEffect.querySelector(".lstIname").innerText;
|
||||
var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
|
||||
@ -1302,7 +1317,7 @@ function displayRover(i,s)
|
||||
|
||||
function cmpP(a, b)
|
||||
{
|
||||
if (!a[1].n) return (a[0] > b[0]);
|
||||
if (cfg.comp.idsort || !a[1].n) return (parseInt(a[0]) > parseInt(b[0]));
|
||||
// sort playlists first, followed by presets with characters and last presets with special 1st character
|
||||
const c = a[1].n.charCodeAt(0);
|
||||
const d = b[1].n.charCodeAt(0);
|
||||
@ -1404,7 +1419,7 @@ function readState(s,command=false)
|
||||
|
||||
if (s.seg.length>2) d.querySelectorAll(".pop").forEach((e)=>{e.classList.remove("hide");});
|
||||
|
||||
var cd = gId('csl').children;
|
||||
var cd = gId('csl').querySelectorAll("button");
|
||||
for (let e = cd.length-1; e >= 0; e--) {
|
||||
cd[e].dataset.r = i.col[e][0];
|
||||
cd[e].dataset.g = i.col[e][1];
|
||||
@ -1485,46 +1500,47 @@ function setEffectParameters(idx)
|
||||
var paOnOff = (effectPars.length<3 || effectPars[2]=='')?[]:effectPars[2].split(",");
|
||||
|
||||
// set html slider items on/off
|
||||
let nSliders = 5;
|
||||
for (let i=0; i<nSliders; i++) {
|
||||
var slider = gId("slider" + i);
|
||||
var label = gId("sliderLabel" + i);
|
||||
// if (not controlDefined and for AC speed or intensity and for SR all sliders) or slider has a value
|
||||
let sliders = d.querySelectorAll("#sliders .sliderwrap");
|
||||
sliders.forEach((slider, i)=>{
|
||||
let text = slider.getAttribute("tooltip");
|
||||
if ((!controlDefined && i<((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i]!="")) {
|
||||
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
|
||||
else if (i==0) label.innerHTML = "Effect speed";
|
||||
else if (i==1) label.innerHTML = "Effect intensity";
|
||||
else label.innerHTML = "Custom" + (i-1);
|
||||
slider.classList.remove('hide');
|
||||
} else {
|
||||
slider.classList.add('hide');
|
||||
}
|
||||
}
|
||||
if (slOnOff.length>i && slOnOff[i]!="!") text = slOnOff[i];
|
||||
slider.setAttribute("tooltip", text);
|
||||
slider.parentElement.classList.remove('hide');
|
||||
} else
|
||||
slider.parentElement.classList.add('hide');
|
||||
});
|
||||
|
||||
if (slOnOff.length > 5) { // up to 3 checkboxes
|
||||
gId('fxopt').classList.remove('fade');
|
||||
for (let i = 0; i<3; i++) {
|
||||
let checks = d.querySelectorAll("#sliders .ochkl");
|
||||
checks.forEach((check, i)=>{
|
||||
let text = check.getAttribute("tooltip");
|
||||
if (5+i<slOnOff.length && slOnOff[5+i]!=='') {
|
||||
gId('opt'+i).classList.remove('hide');
|
||||
gId('optLabel'+i).innerHTML = slOnOff[5+i]=="!" ? 'Option' : slOnOff[5+i].substr(0,16);
|
||||
if (slOnOff.length>5+i && slOnOff[5+i]!="!") text = slOnOff[5+i];
|
||||
check.setAttribute("tooltip", text);
|
||||
check.classList.remove('hide');
|
||||
} else
|
||||
gId('opt'+i).classList.add('hide');
|
||||
}
|
||||
} else {
|
||||
gId('fxopt').classList.add('fade');
|
||||
}
|
||||
check.classList.add('hide');
|
||||
});
|
||||
} else gId('fxopt').classList.add('fade');
|
||||
|
||||
// set the bottom position of selected effect (sticky) as the top of sliders div
|
||||
setInterval(()=>{
|
||||
function setSelectedEffectPosition() {
|
||||
let top = parseInt(getComputedStyle(gId("sliders")).height);
|
||||
top += 5;
|
||||
let sel = d.querySelector('#fxlist .selected');
|
||||
if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setFX())
|
||||
},750);
|
||||
}
|
||||
|
||||
setSelectedEffectPosition();
|
||||
setInterval(setSelectedEffectPosition,750);
|
||||
// set html color items on/off
|
||||
var cslLabel = '';
|
||||
var sep = '';
|
||||
var cslCnt = 0, oCsel = csel;
|
||||
for (let i=0; i<gId("csl").children.length; i++) {
|
||||
// for (let i=0; i<gId("csl").querySelectorAll("button"); i++) {
|
||||
d.querySelectorAll("#csl button").forEach((e,i)=>{
|
||||
var btn = gId("csl" + i);
|
||||
// if no controlDefined or coOnOff has a value
|
||||
if (coOnOff.length>i && coOnOff[i] != "") {
|
||||
@ -1554,12 +1570,16 @@ function setEffectParameters(idx)
|
||||
btn.dataset.hide = 1;
|
||||
btn.innerHTML = `${i+1}`; // name hidden buttons 1..3 for * palettes
|
||||
}
|
||||
}
|
||||
});
|
||||
gId("cslLabel").innerHTML = cslLabel;
|
||||
if (cslLabel!=="") gId("cslLabel").classList.remove("hide");
|
||||
else gId("cslLabel").classList.add("hide");
|
||||
|
||||
// set palette on/off
|
||||
var palw = gId("palw"); // wrapper
|
||||
var pall = gId("pall"); // label
|
||||
var icon = '<i class="icons sel-icon" onclick="tglHex()"></i> ';
|
||||
var text = 'Color palette';
|
||||
// if not controlDefined or palette has a value
|
||||
if (hasRGB && ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0])))) {
|
||||
palw.style.display = "inline-block";
|
||||
@ -1569,13 +1589,13 @@ function setEffectParameters(idx)
|
||||
var v = Math.max(0,Math.min(255,parseInt(paOnOff[0].substr(dPos+1))));
|
||||
paOnOff[0] = paOnOff[0].substring(0,dPos);
|
||||
}
|
||||
if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0];
|
||||
else pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()"></i> Color palette';
|
||||
if (paOnOff.length>0 && paOnOff[0] != "!") text = paOnOff[0];
|
||||
} else {
|
||||
// disable palette list
|
||||
pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()"></i> Color palette not used';
|
||||
text += ' not used';
|
||||
palw.style.display = "none";
|
||||
}
|
||||
pall.innerHTML = icon + text;
|
||||
// not all color selectors shown, hide palettes created from color selectors
|
||||
// NOTE: this will disallow user to select "* Color ..." palettes which may be undesirable in some cases or for some users
|
||||
//for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
|
||||
@ -1666,6 +1686,8 @@ function togglePower()
|
||||
obj.seg = [];
|
||||
obj.seg[0] = {"id": lastinfo.liveseg, "frz": false};
|
||||
}
|
||||
if (cfg.comp.on >0 && isOn) obj = {"ps": cfg.comp.on }; // don't use setPreset()
|
||||
if (cfg.comp.off>0 && !isOn) obj = {"ps": cfg.comp.off}; // don't use setPreset()
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1688,7 +1710,7 @@ function toggleSync()
|
||||
if (syncSend) showToast('Other lights in the network will now sync to this one.');
|
||||
else showToast('This light and other lights in the network will no longer sync.');
|
||||
var obj = {"udpn": {"send": syncSend}};
|
||||
if (syncTglRecv) obj.udpn.recv = syncSend;
|
||||
//if (syncTglRecv) obj.udpn.recv = syncSend;
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
@ -1793,12 +1815,12 @@ function makePlSel(el, incPl=false)
|
||||
{
|
||||
var plSelContent = "";
|
||||
delete pJson["0"]; // remove filler preset
|
||||
var arr = Object.entries(pJson);
|
||||
for (var a of arr) {
|
||||
Object.entries(pJson).sort(cmpP).forEach((a)=>{
|
||||
var n = a[1].n ? a[1].n : "Preset " + a[0];
|
||||
if (!incPl && a[1].playlist && a[1].playlist.ps) continue; // remove playlists, sub-playlists not yet supported
|
||||
plSelContent += `<option value="${a[0]}" ${a[0]==el?"selected":""}>${n}</option>`
|
||||
}
|
||||
if (cfg.comp.idsort) n = a[0] + ' ' + n;
|
||||
if (!(!incPl && a[1].playlist && a[1].playlist.ps)) // skip playlists, sub-playlists not yet supported
|
||||
plSelContent += `<option value="${a[0]}" ${a[0]==el?"selected":""}>${n}</option>`;
|
||||
});
|
||||
return plSelContent;
|
||||
}
|
||||
|
||||
@ -1807,21 +1829,19 @@ function refreshPlE(p)
|
||||
var plEDiv = gId(`ple${p}`);
|
||||
if (!plEDiv) return;
|
||||
var content = "<div class=\"first c\">Playlist entries</div>";
|
||||
for (var i = 0; i < plJson[p].ps.length; i++) {
|
||||
content += makePlEntry(p,i);
|
||||
}
|
||||
plJson[p].ps.forEach((e,i)=>{content += makePlEntry(p,i);});
|
||||
|
||||
content += `<div class="hrz"></div>`;
|
||||
plEDiv.innerHTML = content;
|
||||
var dels = plEDiv.getElementsByClassName("btn-pl-del");
|
||||
if (dels.length < 2) dels[0].style.display = "none";
|
||||
|
||||
var sels = gId(`seg${p+100}`).getElementsByClassName("sel");
|
||||
for (var i of sels) {
|
||||
d.querySelectorAll(`#seg${p+100} .sel`).forEach((i)=>{
|
||||
if (i.dataset.val) {
|
||||
if (parseInt(i.dataset.val) > 0) i.value = i.dataset.val;
|
||||
else plJson[p].ps[i.dataset.index] = parseInt(i.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// p: preset ID, i: ps index
|
||||
@ -1909,23 +1929,17 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
|
||||
} else {
|
||||
content =
|
||||
`<label class="check revchkl">
|
||||
<span class="lstIname">
|
||||
Include brightness
|
||||
</span>
|
||||
<span class="lstIname">Include brightness</span>
|
||||
<input type="checkbox" id="p${i}ibtgl" checked>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
<span class="lstIname">
|
||||
Save segment bounds
|
||||
</span>
|
||||
<span class="lstIname">Save segment bounds</span>
|
||||
<input type="checkbox" id="p${i}sbtgl" checked>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="check revchkl">
|
||||
<span class="lstIname">
|
||||
Checked segments only
|
||||
</span>
|
||||
<span class="lstIname">Checked segments only</span>
|
||||
<input type="checkbox" id="p${i}sbchk">
|
||||
<span class="checkmark"></span>
|
||||
</label>`;
|
||||
@ -1941,9 +1955,7 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
|
||||
<div class="h">(leave empty for no Quick load button)</div>
|
||||
<div ${pl&&i==0?"style='display:none'":""}>
|
||||
<label class="check revchkl">
|
||||
<span class="lstIname">
|
||||
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
|
||||
</span>
|
||||
<span class="lstIname">${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}</span>
|
||||
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
@ -2729,6 +2741,25 @@ function clean(c)
|
||||
}
|
||||
}
|
||||
|
||||
function filterFocus(e)
|
||||
{
|
||||
let f = gId("filters");
|
||||
if (e.type === "focus") f.classList.remove('fade'); // immediately show (still has transition)
|
||||
// compute sticky top (with delay for transition)
|
||||
setTimeout(()=>{
|
||||
let sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti')) + (e.type === "focus" ? 1 : -1) * f.offsetHeight;
|
||||
sCol('--sti', sti+"px");
|
||||
}, 252);
|
||||
if (e.type === "blur") {
|
||||
let t = e.relatedTarget ? e.relatedTarget : e.explicitOriginalTarget;
|
||||
do {
|
||||
if (t.id && (t.id === "fxFind")) { setTimeout(()=>{t.firstElementChild.focus();},150); return; }
|
||||
t = t.parentElement;
|
||||
} while (t.tagName !== "BODY");
|
||||
setTimeout(()=>{f.classList.add('fade');},255); // wait with hiding
|
||||
}
|
||||
}
|
||||
|
||||
function filterFx(o)
|
||||
{
|
||||
if (!o) return;
|
||||
@ -2915,6 +2946,40 @@ function mergeDeep(target, ...sources)
|
||||
return mergeDeep(target, ...sources);
|
||||
}
|
||||
|
||||
function tooltip()
|
||||
{
|
||||
const elements = d.querySelectorAll("[tooltip]");
|
||||
elements.forEach((element)=>{
|
||||
element.addEventListener("mouseover", ()=>{
|
||||
const tooltip = d.createElement("span");
|
||||
tooltip.className = "tooltip";
|
||||
tooltip.textContent = element.getAttribute("tooltip");
|
||||
|
||||
let { top, left, width } = element.getBoundingClientRect();
|
||||
|
||||
d.body.appendChild(tooltip);
|
||||
|
||||
const { offsetHeight, offsetWidth } = tooltip;
|
||||
|
||||
const offset = element.classList.contains("sliderwrap") ? 4 : 10;
|
||||
top -= offsetHeight + offset;
|
||||
left += (width - offsetWidth) / 2;
|
||||
|
||||
tooltip.style.top = top + "px";
|
||||
tooltip.style.left = left + "px";
|
||||
tooltip.classList.add("visible");
|
||||
});
|
||||
|
||||
element.addEventListener("mouseout", ()=>{
|
||||
const tooltips = d.querySelectorAll('.tooltip');
|
||||
tooltips.forEach((tooltip)=>{
|
||||
tooltip.classList.remove("visible");
|
||||
d.body.removeChild(tooltip);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
size();
|
||||
_C.style.setProperty('--n', N);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -40,9 +40,10 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 1) {
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=0'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
|
@ -47,9 +47,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "2d"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=10'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
|
@ -66,9 +66,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "dmx"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=7'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
|
@ -26,8 +26,8 @@
|
||||
// success event
|
||||
scE.addEventListener("load", () => {
|
||||
GetV();
|
||||
checkSi();
|
||||
setABL();
|
||||
checkSi();
|
||||
d.Sf.addEventListener("submit", trySubmit);
|
||||
if (d.um_p[0]==-1) d.um_p.shift();
|
||||
pinDropdowns();
|
||||
@ -107,32 +107,55 @@
|
||||
}
|
||||
function enABL()
|
||||
{
|
||||
var en = gId('able').checked;
|
||||
d.Sf.LA.value = (en) ? laprev:0;
|
||||
var en = d.Sf["ABL"].checked;
|
||||
d.Sf["MA"].min = en ? 250 : 0;
|
||||
gId('abl').style.display = (en) ? 'inline':'none';
|
||||
gId('psu2').style.display = (en) ? 'inline':'none';
|
||||
if (d.Sf.LA.value > 0) setABL();
|
||||
if (!en) d.Sf["PPL"].checked = false;
|
||||
enPPL();
|
||||
UI();
|
||||
}
|
||||
function enLA()
|
||||
function enPPL()
|
||||
{
|
||||
var val = d.Sf.LAsel.value;
|
||||
d.Sf.LA.value = val;
|
||||
gId('LAdis').style.display = (val == 50) ? 'inline':'none';
|
||||
UI();
|
||||
const abl = d.Sf["ABL"].checked;
|
||||
const en = d.Sf["PPL"].checked;
|
||||
d.Sf["MA"].readonly = en;
|
||||
gId("ppldis").style.display = en ? 'inline' : 'none';
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
|
||||
gId("PSU"+n).style.display = en ? "inline" : "none";
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
i.min = en && !((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 250 : 0;
|
||||
if (!abl) i.value = 0;
|
||||
});
|
||||
}
|
||||
function enLA(s,n)
|
||||
{
|
||||
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none";
|
||||
d.Sf["LA"+n].value = s.value==="0" ? 55 : s.value;
|
||||
d.Sf["LA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 1;
|
||||
}
|
||||
function setABL()
|
||||
{
|
||||
gId('able').checked = true;
|
||||
d.Sf.LAsel.value = 50;
|
||||
switch (parseInt(d.Sf.LA.value)) {
|
||||
case 0: gId('able').checked = false; enABL(); break;
|
||||
case 30: d.Sf.LAsel.value = 30; break;
|
||||
case 35: d.Sf.LAsel.value = 35; break;
|
||||
case 55: d.Sf.LAsel.value = 55; break;
|
||||
case 255: d.Sf.LAsel.value = 255; break;
|
||||
default: gId('LAdis').style.display = 'inline';
|
||||
d.Sf["ABL"].checked = false;
|
||||
// check if ABL is enabled (max mA entered per output)
|
||||
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
|
||||
if (parseInt(i.value) > 0) d.Sf["ABL"].checked = true;
|
||||
});
|
||||
// select appropriate LED current
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,n)=>{
|
||||
sel.value = 0; // set custom
|
||||
switch (parseInt(d.Sf["LA"+n].value)) {
|
||||
case 0: break; // disable ABL
|
||||
case 15: sel.value = 15; break;
|
||||
case 30: sel.value = 30; break;
|
||||
case 35: sel.value = 35; break;
|
||||
case 55: sel.value = 55; break;
|
||||
case 255: sel.value = 255; break;
|
||||
}
|
||||
enLA(sel,n);
|
||||
});
|
||||
enABL();
|
||||
gId('m1').innerHTML = maxM;
|
||||
}
|
||||
//returns mem usage
|
||||
@ -161,19 +184,22 @@
|
||||
function UI(change=false)
|
||||
{
|
||||
let isRGBW = false, gRGBW = false, memu = 0;
|
||||
|
||||
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
|
||||
|
||||
if (d.Sf.LA.value == 255) laprev = 12;
|
||||
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
|
||||
let sumMA = 0, busMA = 0;
|
||||
let sLC = 0, sPC = 0, sDI = 0, maxLC = 0;
|
||||
const ablEN = d.Sf["ABL"].checked;
|
||||
const pplEN = d.Sf["PPL"].checked;
|
||||
|
||||
// enable/disable LED fields
|
||||
d.Sf.querySelectorAll("#mLC select[name^=LT]").forEach((s)=>{
|
||||
// is the field a LED type?
|
||||
var n = s.name.substring(2);
|
||||
var t = parseInt(s.value);
|
||||
let isDig = ((t >= 16 && t < 32) || (t >= 50 && t < 64));
|
||||
let isVir = (t >= 80 && t < 96);
|
||||
let isPwm = (t >= 40 && t < 48);
|
||||
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:";
|
||||
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
|
||||
gId("abl"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none" : "inline";
|
||||
//var LK = d.getElementsByName("L1"+n)[0]; // clock pin
|
||||
|
||||
memu += getMem(t, n); // calc memory
|
||||
@ -197,6 +223,8 @@
|
||||
if (change) {
|
||||
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state
|
||||
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
|
||||
d.Sf["LA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 1;
|
||||
d.Sf["MA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 250;
|
||||
}
|
||||
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
|
||||
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
|
||||
@ -211,6 +239,9 @@
|
||||
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
|
||||
if (ablEN && pplEN && !((t >= 80 && t < 96) || (t >= 40 && t < 48))) {
|
||||
sumMA += parseInt(d.Sf["MA"+n].value);
|
||||
}
|
||||
});
|
||||
// display global white channel overrides
|
||||
gId("wc").style.display = (gRGBW) ? 'inline':'none';
|
||||
@ -220,7 +251,6 @@
|
||||
}
|
||||
// check for pin conflicts
|
||||
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields
|
||||
var sLC = 0, sPC = 0, maxLC = 0;
|
||||
for (i=0; i<LCs.length; i++) {
|
||||
var nm = LCs[i].name.substring(0,2); // field name
|
||||
var n = LCs[i].name.substring(2); // bus number
|
||||
@ -233,19 +263,25 @@
|
||||
var s = parseInt(gId("ls"+n).value); //start value
|
||||
if (s+c > sLC) sLC = s+c; //update total count
|
||||
if(c>maxLC)maxLC=c; //max per output
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
var t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs
|
||||
if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) sDI+=c;
|
||||
if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) {
|
||||
let maPL = parseInt(d.Sf["LA"+n].value);
|
||||
if (maPL==255) maPL = 12;
|
||||
busMA += maPL*c;
|
||||
}
|
||||
} // increase led count
|
||||
continue;
|
||||
}
|
||||
// do we have led pins for digital leds
|
||||
if (nm=="L0" || nm=="L1") {
|
||||
var lc=d.getElementsByName("LC"+n)[0];
|
||||
var lc=d.Sf["LC"+n];
|
||||
lc.max=maxPB; // update max led count value
|
||||
}
|
||||
// ignore IP address (stored in pins for virtual busses)
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
|
||||
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
|
||||
var t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
|
||||
if (t>=80) {
|
||||
LCs[i].max = 255;
|
||||
LCs[i].min = 0;
|
||||
@ -267,7 +303,7 @@
|
||||
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) {
|
||||
if (n2.substring(0,1)==="L") {
|
||||
var m = LCs[j].name.substring(2);
|
||||
var t2 = parseInt(d.getElementsByName("LT"+m)[0].value, 10);
|
||||
var t2 = parseInt(d.Sf["LT"+m].value, 10);
|
||||
if (t2>=80) continue;
|
||||
}
|
||||
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); // add current pin
|
||||
@ -282,6 +318,14 @@
|
||||
// LCs[i].min = -1;
|
||||
//}
|
||||
}
|
||||
d.Sf.querySelectorAll("#mLC input[name^=LC]").forEach((s,n)=>{
|
||||
let c = parseInt(s.value,10); //get LED count
|
||||
let t = parseInt(d.Sf["LT"+n].value);
|
||||
if (ablEN) {
|
||||
let v = Math.round(parseInt(d.Sf["MA"].value,10)*c/sDI);
|
||||
if (!pplEN && !((t >= 80 && t < 96) || (t >= 40 && t < 48))) d.Sf["MA"+n].value = v;
|
||||
} else d.Sf["MA"+n].value = 0;
|
||||
});
|
||||
// update total led count
|
||||
gId("lc").textContent = sLC;
|
||||
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";
|
||||
@ -294,27 +338,20 @@
|
||||
gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
|
||||
// calculate power
|
||||
var val = Math.ceil((100 + sPC * laprev)/500)/2;
|
||||
if (pplEN) d.Sf.MA.value = sumMA;
|
||||
gId('ampwarning').style.display = (parseInt(d.Sf.MA.value,10) > 7200) ? 'inline':'none';
|
||||
var val = Math.ceil((100 + busMA)/500)/2;
|
||||
val = (val > 5) ? Math.ceil(val) : val;
|
||||
var s = "";
|
||||
var is12V = (d.Sf.LAsel.value == 30);
|
||||
var isWS2815 = (d.Sf.LAsel.value == 255);
|
||||
if (val < 1.02 && !is12V && !isWS2815)
|
||||
{
|
||||
s = "ESP 5V pin with 1A USB supply";
|
||||
} else
|
||||
{
|
||||
s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V ";
|
||||
var s = "A power supply with total of ";
|
||||
s += val;
|
||||
s += "A supply connected to LEDs";
|
||||
}
|
||||
var val2 = Math.ceil((100 + sPC * laprev)/1500)/2;
|
||||
s += "A is required.";
|
||||
var val2 = Math.ceil((100 + busMA)/1500)/2;
|
||||
val2 = (val2 > 5) ? Math.ceil(val2) : val2;
|
||||
var s2 = "(for most effects, ~";
|
||||
s2 += val2;
|
||||
s2 += "A is enough)<br>";
|
||||
gId('psu').innerHTML = s;
|
||||
gId('psu2').innerHTML = isWS2815 ? "" : s2;
|
||||
gId('psu2').innerHTML = s2;
|
||||
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
|
||||
}
|
||||
function lastEnd(i) {
|
||||
@ -364,6 +401,18 @@ ${i+1}:
|
||||
<option value="82">Art-Net RGB (network)</option>
|
||||
<option value="88">DDP RGBW (network)</option>
|
||||
</select><br>
|
||||
<div id="abl${i}">
|
||||
mA/LED: <select name="LAsel${i}" onchange="enLA(this,${i});UI();">
|
||||
<option value="55" selected>55mA (typ. 5V WS281x)</option>
|
||||
<option value="35">35mA (eco WS2812)</option>
|
||||
<option value="30">30mA (typ. 12V)</option>
|
||||
<option value="255">12mA (WS2815)</option>
|
||||
<option value="15">15mA (seed/fairy pixels)</option>
|
||||
<option value="0">Custom</option>
|
||||
</select><br>
|
||||
<div id="LAdis${i}" style="display: none;">max. mA/LED: <input name="LA${i}" type="number" min="1" max="254" oninput="UI()"> mA<br></div>
|
||||
<div id="PSU${i}">PSU: <input name="MA${i}" type="number" class="xl" min="250" max="65000" oninput="UI()"> mA<br></div>
|
||||
</div>
|
||||
<div id="co${i}" style="display:inline">Color Order:
|
||||
<select name="CO${i}">
|
||||
<option value="0">GRB</option>
|
||||
@ -669,9 +718,11 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "leds"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=2'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
@ -695,29 +746,23 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<b><span id="psu">?</span></b><br>
|
||||
<span id="psu2"><br></span>
|
||||
<br>
|
||||
Enable automatic brightness limiter: <input type="checkbox" name="ABen" onchange="enABL()" id="able"><br>
|
||||
Enable automatic brightness limiter: <input type="checkbox" name="ABL" onchange="enABL()"><br>
|
||||
<div id="abl">
|
||||
Maximum Current: <input name="MA" type="number" class="l" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
<i>Automatically limits brightness to stay close to the limit.<br>
|
||||
Keep at <1A if poweing LEDs directly from the ESP 5V pin!<br>
|
||||
Analog (PWM) and virtual LEDs cannot use automatic brightness limiter.<br></i>
|
||||
Maximum PSU Current: <input name="MA" type="number" class="xl" min="250" max="65000" oninput="UI()" required> mA<br>
|
||||
Use per-output limiter: <input type="checkbox" name="PPL" onchange="enPPL()"><br>
|
||||
<div id="ppldis" style="display:none;">
|
||||
<i>Make sure you enter correct values in each LED output.<br>
|
||||
If using multiple outputs with only one PSU, distribute its power proportionally amongst ouputs.</i><br>
|
||||
</div>
|
||||
<div id="ampwarning" class="warn" style="display: none;">
|
||||
⚠ Your power supply provides high current.<br>
|
||||
To improve the safety of your setup,<br>
|
||||
please use thick cables,<br>
|
||||
multiple power injection points and a fuse!<br>
|
||||
</div>
|
||||
<i>Automatically limits brightness to stay close to the limit.<br>
|
||||
Keep at <1A if powering LEDs directly from the ESP 5V pin!<br>
|
||||
If you are using an external power supply, enter its rating.<br>
|
||||
(Current estimated usage: <span class="pow">unknown</span>)</i><br><br>
|
||||
LED voltage (Max. current for a single LED):<br>
|
||||
<select name="LAsel" onchange="enLA()">
|
||||
<option value="55" selected>5V default (55mA)</option>
|
||||
<option value="35">5V efficient (35mA)</option>
|
||||
<option value="30">12V (30mA)</option>
|
||||
<option value="255">WS2815 (12mA)</option>
|
||||
<option value="50">Custom</option>
|
||||
</select><br>
|
||||
<span id="LAdis" style="display: none;">Custom max. current per LED: <input name="LA" type="number" min="0" max="255" id="la" oninput="UI()" required> mA<br></span>
|
||||
<i>Keep at default if you are unsure about your type of LEDs.</i><br>
|
||||
</div>
|
||||
<h3>Hardware setup</h3>
|
||||
<div id="mLC">LED outputs:</div>
|
||||
@ -773,6 +818,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
|
||||
<h3>Transitions</h3>
|
||||
Crossfade: <input type="checkbox" name="TF"><br>
|
||||
Effect blending: <input type="checkbox" name="EB"><br>
|
||||
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
|
||||
Enable Palette transitions: <input type="checkbox" name="PF"><br>
|
||||
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>
|
||||
|
@ -77,9 +77,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "sec"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
if (loc) {
|
||||
|
@ -67,9 +67,11 @@
|
||||
// detect reverse proxy
|
||||
let paths = l.pathname.slice(1,l.pathname.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "sync"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=4'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
@ -91,6 +93,12 @@
|
||||
<h3>WLED Broadcast</h3>
|
||||
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
|
||||
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>
|
||||
<div id="NoESPNOW" class="hide">
|
||||
<i class="warn">ESP-NOW support is disabled.<br></i>
|
||||
</div>
|
||||
<div id="ESPNOW">
|
||||
Use ESP-NOW sync: <input type="checkbox" name="EN"><br><i>(in AP mode or no WiFi)</i><br>
|
||||
</div>
|
||||
<h3>Sync groups</h3>
|
||||
<input name="GS" id="GS" type="number" style="display: none;"><!-- hidden inputs for bitwise group checkboxes -->
|
||||
<input name="GR" id="GR" type="number" style="display: none;">
|
||||
@ -128,14 +136,17 @@ UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required
|
||||
<td><input type="checkbox" id="R7" name="R7"></td>
|
||||
<td><input type="checkbox" id="R8" name="R8"></td>
|
||||
</tr>
|
||||
</table><br>
|
||||
Receive: <nowrap><input type="checkbox" name="RB">Brightness,</nowrap> <nowrap><input type="checkbox" name="RC">Color,</nowrap> <nowrap>and <input type="checkbox" name="RX">Effects</nowrap><br>
|
||||
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds<br>
|
||||
</table>
|
||||
<h3>Receive</h3>
|
||||
<nowrap><input type="checkbox" name="RB">Brightness,</nowrap> <nowrap><input type="checkbox" name="RC">Color,</nowrap> <nowrap>and <input type="checkbox" name="RX">Effects</nowrap><br>
|
||||
<input type="checkbox" name="SO"> Segment options, <input type="checkbox" name="SG"> bounds
|
||||
<h3>Send</h3>
|
||||
Enable Sync on start: <input type="checkbox" name="SS"><br>
|
||||
Send notifications on direct change: <input type="checkbox" name="SD"><br>
|
||||
Send notifications on button press or IR: <input type="checkbox" name="SB"><br>
|
||||
Send Alexa notifications: <input type="checkbox" name="SA"><br>
|
||||
Send Philips Hue change notifications: <input type="checkbox" name="SH"><br>
|
||||
Send Macro notifications: <input type="checkbox" name="SM"><br>
|
||||
<!-- Send Macro notifications: <input type="checkbox" name="SM"><br> -->
|
||||
UDP packet retransmissions: <input name="UR" type="number" min="0" max="30" class="d5" required><br><br>
|
||||
<i>Reboot required to apply changes. </i>
|
||||
<hr class="sml">
|
||||
|
@ -45,9 +45,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "time"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=5'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script>
|
||||
var d = document;
|
||||
var loc = false, locip, locproto = "http:";
|
||||
var initial_ds, initial_st, initial_su;
|
||||
var initial_ds, initial_st, initial_su, oldUrl;
|
||||
var sett = null;
|
||||
var l = {
|
||||
"comp":{
|
||||
@ -26,7 +26,10 @@
|
||||
"segexp" : "Always expand first segment",
|
||||
"css": "Enable custom CSS",
|
||||
"hdays": "Enable custom Holidays list",
|
||||
"fxdef": "Use effect default parameters"
|
||||
"fxdef": "Use effect default parameters",
|
||||
"on": "Power button preset override for On",
|
||||
"off": "Power button preset override for Off",
|
||||
"idsort": "Sort presets by ID"
|
||||
},
|
||||
"theme":{
|
||||
"alpha": {
|
||||
@ -35,7 +38,9 @@
|
||||
},
|
||||
"bg":{
|
||||
"url":"BG image URL",
|
||||
"random":"Random BG image"
|
||||
"rnd":"Random BG image",
|
||||
"rndGrayscale":"Grayscale",
|
||||
"rndBlur":"Blur"
|
||||
},
|
||||
"color":{
|
||||
"bg":"BG HEX color"
|
||||
@ -71,7 +76,7 @@
|
||||
function addRec(s, path = "", label = null)
|
||||
{
|
||||
var str = "";
|
||||
for (i in s)
|
||||
for (let i in s)
|
||||
{
|
||||
var fk = path + (path?'_':'') + i;
|
||||
if (isObject(s[i])) {
|
||||
@ -115,6 +120,11 @@
|
||||
str = addRec(s,"",l);
|
||||
|
||||
gId('gen').innerHTML = str;
|
||||
if (gId('theme_bg_rnd').checked) {
|
||||
oldUrl = "";
|
||||
toggle("Image");
|
||||
randomBg();
|
||||
} else oldUrl = gId("theme_bg_url").value;
|
||||
}
|
||||
function GetLS()
|
||||
{
|
||||
@ -160,7 +170,7 @@
|
||||
|
||||
function Save() {
|
||||
SetLS();
|
||||
if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st || d.Sf.SU.checked != initial_su) d.Sf.submit();
|
||||
if (d.Sf.DS.value != initial_ds || /*d.Sf.ST.checked != initial_st ||*/ d.Sf.SU.checked != initial_su) d.Sf.submit();
|
||||
}
|
||||
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
@ -175,7 +185,7 @@
|
||||
//console.log("File loaded");
|
||||
GetV();
|
||||
initial_ds = d.Sf.DS.value;
|
||||
initial_st = d.Sf.ST.checked;
|
||||
//initial_st = d.Sf.ST.checked;
|
||||
initial_su = d.Sf.SU.checked;
|
||||
GetLS();
|
||||
});
|
||||
@ -199,9 +209,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "ui"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=3'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
@ -220,20 +232,15 @@
|
||||
}
|
||||
|
||||
// random BG image
|
||||
function setRandomBg() {
|
||||
if (gId("theme_bg_random").checked) {
|
||||
gId("theme_bg_url").value = "https://picsum.photos/1920/1080";
|
||||
} else {
|
||||
gId("theme_bg_url").value = "";
|
||||
}
|
||||
|
||||
}
|
||||
function checkRandomBg() {
|
||||
if (gId("theme_bg_url").value === "https://picsum.photos/1920/1080") {
|
||||
gId("theme_bg_random").checked = true;
|
||||
} else {
|
||||
gId("theme_bg_random").checked = false;
|
||||
function randomBg() {
|
||||
let url = oldUrl;
|
||||
let t = "theme_bg_rnd";
|
||||
if (gId(t).checked) {
|
||||
url = "https://picsum.photos/1920/1080";
|
||||
if (gId(`${t}Grayscale`).checked) url += "?grayscale";
|
||||
if (gId(`${t}Blur`).checked) url += (url.includes("?") ? "&" : "?") + "blur";
|
||||
}
|
||||
gId("theme_bg_url").value = url;
|
||||
}
|
||||
function uploadFile(fO,name) {
|
||||
var req = new XMLHttpRequest();
|
||||
@ -259,7 +266,7 @@
|
||||
</div>
|
||||
<h2>Web Setup</h2>
|
||||
Server description: <input type="text" name="DS" maxlength="32"><br>
|
||||
Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
|
||||
<!-- Sync button toggles both send and receive: <input type="checkbox" name="ST"><br> -->
|
||||
<div id="NoSimple" class="hide">
|
||||
<i class="warn">This firmware build does not include simplified UI support.<br></i>
|
||||
</div>
|
||||
@ -277,13 +284,24 @@
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_seglen" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_segpwr" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_segexp" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_fxdef" class="agi cb"><br>
|
||||
<span class="l"></span>: <input type="number" min=0 max=250 step=1 id="comp_on" class="agi"><br>
|
||||
<span class="l"></span>: <input type="number" min=0 max=250 step=1 id="comp_off" class="agi"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_idsort" class="agi cb"><br>
|
||||
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br>
|
||||
<span id="idonthateyou" style="display:none"><i>Why would you? </i>🥺<br></span>
|
||||
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_tab" class="agi"><br>
|
||||
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_bg" class="agi"><br>
|
||||
<span class="l"></span>: <input type="text" id="theme_color_bg" maxlength="9" class="agi"><br>
|
||||
<span class="l">BG image URL</span>: <input type="text" id="theme_bg_url" class="agi" oninput="checkRandomBg()"><br>
|
||||
<span class="l">Random BG image</span>: <input type="checkbox" id="theme_bg_random" class="agi cb" onchange="setRandomBg()"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="theme_bg_rnd" class="agi cb" onchange="randomBg();toggle('Image');">
|
||||
<div id="Image">
|
||||
<span class="l"></span>: <input type="text" id="theme_bg_url" class="agi"><br>
|
||||
</div>
|
||||
<div id="NoImage" class="hide">
|
||||
<h4>Random BG image settings</h4>
|
||||
<span class="l"></span>: <input type="checkbox" id="theme_bg_rndGrayscale" class="agi cb" onchange="randomBg()"><br>
|
||||
<span class="l"></span>: <input type="checkbox" id="theme_bg_rndBlur" class="agi cb" onchange="randomBg()"><br>
|
||||
</div>
|
||||
<input id="theme_base" class="agi" style="display:none">
|
||||
<span class="l"></span>: <input type="checkbox" id="comp_css" class="agi cb"><br>
|
||||
<div id="skin">Custom CSS: <input type="file" name="data" accept=".css"> <input type="button" value="Upload" onclick="uploadFile(d.Sf.data,'/skin.css');"><br></div>
|
||||
|
@ -56,9 +56,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "um"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
ldS();
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
function gId(e) { return d.getElementById(e); }
|
||||
function cE(e) { return d.createElement(e); }
|
||||
function toggle(el){gId(el).classList.toggle("hide"); gId('No'+el).classList.toggle("hide");}
|
||||
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");}
|
||||
function B(){window.open(getURL("/settings"),"_self");}
|
||||
function N() {
|
||||
@ -132,9 +133,11 @@
|
||||
let path = l.pathname;
|
||||
let paths = path.slice(1,path.endsWith('/')?-1:undefined).split("/");
|
||||
if (paths.length > 2) {
|
||||
paths.pop(); // remove "wifi"
|
||||
paths.pop(); // remove "settings"
|
||||
locproto = l.protocol;
|
||||
loc = true;
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths[0];
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "") + "/" + paths.join('/');
|
||||
}
|
||||
}
|
||||
loadJS(getURL('/settings/s.js?p=1'), false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
@ -193,14 +196,16 @@
|
||||
<i>Can help with connectivity issues.<br>
|
||||
Do not enable if WiFi is working correctly, increases power consumption.</i>
|
||||
|
||||
<div id="remd">
|
||||
<h3>Wireless Remote</h3>
|
||||
<h3>ESP-NOW Wireless</h3>
|
||||
<div id="NoESPNOW" class="hide">
|
||||
<i class="warn">This firmware build does not include ESP-NOW support.<br></i>
|
||||
</div>
|
||||
<div id="ESPNOW">
|
||||
Enable ESP-NOW: <input type="checkbox" name="RE"><br>
|
||||
<i>Listen for events over ESP-NOW<br>
|
||||
Keep disabled if not using a remote, increases power consumption.<br></i>
|
||||
|
||||
Enable Remote: <input type="checkbox" name="RE"><br>
|
||||
Hardware MAC: <input type="text" name="RMAC"><br>
|
||||
Last Seen: <span class="rlid">None</span> <br>
|
||||
Keep disabled if not using a remote or wireless sync, increases power consumption.<br></i>
|
||||
Paired Remote MAC: <input type="text" name="RMAC" minlength="12" maxlength="12"><br>
|
||||
Last device seen: <span class="rlid" onclick="d.Sf.RMAC.value=this.textContent;" style="cursor:pointer;">None</span> <br>
|
||||
</div>
|
||||
|
||||
<div id="ethd">
|
||||
|
@ -19,8 +19,8 @@
|
||||
--c-r: #e42;
|
||||
--c-g: #4e2;
|
||||
--c-l: #48a;
|
||||
--t-b: 0.5;
|
||||
--c-o: rgba(34, 34, 34, 0.9);
|
||||
--t-b: .5;
|
||||
--c-o: rgba(34, 34, 34, .9);
|
||||
--c-tb : rgba(34, 34, 34, var(--t-b));
|
||||
--c-tba: rgba(102, 102, 102, var(--t-b));
|
||||
--c-tbh: rgba(51, 51, 51, var(--t-b));
|
||||
@ -31,7 +31,7 @@
|
||||
--tbp: 14px 8px 10px;
|
||||
--bbp: 9px 0 7px 0;
|
||||
--bhd: none;
|
||||
--bmt: 0px;
|
||||
--bmt: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
@ -86,11 +86,11 @@ a, a:visited {
|
||||
}
|
||||
|
||||
button {
|
||||
outline: none;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
transition: color 0.3s, background-color 0.3s;
|
||||
border: 0;
|
||||
transition: color .3s, background-color .3s;
|
||||
font-size: 19px;
|
||||
color: var(--c-c);
|
||||
min-width: 40px;
|
||||
@ -180,8 +180,8 @@ button:hover {
|
||||
.tab button {
|
||||
background-color: transparent;
|
||||
float: left;
|
||||
border: none;
|
||||
transition: color 0.3s, background-color 0.3s;
|
||||
border: 0;
|
||||
transition: color .3s, background-color .3s;
|
||||
font-size: 17px;
|
||||
color: var(--c-c);
|
||||
min-width: 44px;
|
||||
@ -218,7 +218,7 @@ button:hover {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: 0px;
|
||||
border: 0;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
overscroll-behavior: none;
|
||||
@ -243,8 +243,8 @@ button:hover {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 11;
|
||||
opacity: 0.95;
|
||||
transition: 0.7s;
|
||||
opacity: .95;
|
||||
transition: .7s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -267,24 +267,24 @@ button:hover {
|
||||
|
||||
#toast.show {
|
||||
opacity: 1;
|
||||
animation: fadein 0.5s, fadein 0.5s 2.5s reverse;
|
||||
animation: fadein .5s, fadein .5s 2.5s reverse;
|
||||
}
|
||||
|
||||
#toast.error {
|
||||
opacity: 1;
|
||||
background-color: #b21;
|
||||
animation: fadein 0.5s;
|
||||
animation: fadein .5s;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
right: 0px;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: calc(var(--th) - 1px);
|
||||
background-color: var(--c-o);
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.4s;
|
||||
transition: transform .4s;
|
||||
padding: 8px;
|
||||
font-size: 20px;
|
||||
overflow: auto;
|
||||
@ -356,7 +356,7 @@ button:hover {
|
||||
}
|
||||
|
||||
#heart {
|
||||
transition: color 0.9s;
|
||||
transition: color .9s;
|
||||
font-size: 16px;
|
||||
color: #f00;
|
||||
}
|
||||
@ -421,7 +421,7 @@ img {
|
||||
padding: 4px 4px 2px;
|
||||
font-size: 14px;
|
||||
right: 3px;
|
||||
transition: visibility 0.25s ease, opacity 0.25s ease;
|
||||
transition: visibility .25s ease, opacity .25s ease;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
@ -531,7 +531,7 @@ input[type=range]::-moz-range-thumb {
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--c-3);
|
||||
border-radius: 25px;
|
||||
transition-duration: 0.3s;
|
||||
transition-duration: .3s;
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-transform: translate3d(0,0,0);
|
||||
overflow: clip;
|
||||
@ -586,13 +586,13 @@ option {
|
||||
input[type=number], input[type=text] {
|
||||
background: var(--c-3);
|
||||
color: var(--c-f);
|
||||
border: 0px solid var(--c-f);
|
||||
border: 0 solid var(--c-f);
|
||||
border-radius: 5px;
|
||||
padding: 8px;
|
||||
margin: 6px 6px 6px 0;
|
||||
font-size: 19px;
|
||||
transition: background-color 0.2s;
|
||||
outline: none;
|
||||
transition: background-color .2s;
|
||||
outline: 0;
|
||||
width: 50px;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
@ -614,9 +614,9 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
.pid {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
padding: 12px 0px 0px 12px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 12px 0 0 12px;
|
||||
font-size: 16px;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
@ -746,7 +746,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
.list {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
transition: background-color 0.5s;
|
||||
transition: background-color .5s;
|
||||
margin: auto auto 20px;
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
@ -774,7 +774,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
}
|
||||
/*
|
||||
.lstI:last-child {
|
||||
border: none;
|
||||
border: 0;
|
||||
border-radius: 0 0 20px 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
@ -879,7 +879,7 @@ input[type=text].fnd:not(:placeholder-shown), input[type=text].fnd:hover {
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--c-sb);
|
||||
opacity: 0.2;
|
||||
opacity: .2;
|
||||
border-radius: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
@ -927,7 +927,7 @@ input[type=text].fnd:not(:placeholder-shown), input[type=text].fnd:hover {
|
||||
display: none !important;
|
||||
}
|
||||
#briwrap {
|
||||
margin-top: 0px !important;
|
||||
margin-top: 0 !important;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ async function onLoad()
|
||||
locip = l.hostname + (l.port ? ":" + l.port : "");
|
||||
if (paths.length > 0 && paths[0]!=="") {
|
||||
loc = true;
|
||||
locip += "/" + paths[0];
|
||||
locip += "/" + paths.join('/');
|
||||
} else if (locproto==="https:") {
|
||||
loc = true;
|
||||
}
|
||||
@ -489,7 +489,7 @@ function parseInfo() {
|
||||
d.title = name;
|
||||
isRgbw = li.leds.wv;
|
||||
ledCount = li.leds.count;
|
||||
syncTglRecv = li.str;
|
||||
// syncTglRecv = li.str;
|
||||
maxSeg = li.leds.maxseg;
|
||||
pmt = li.fs.pmt;
|
||||
cct = li.leds.cct;
|
||||
|
@ -16,6 +16,9 @@ hr {
|
||||
hr.sml {
|
||||
width: 260px;
|
||||
}
|
||||
h4 {
|
||||
margin: 0;
|
||||
}
|
||||
a, a:hover {
|
||||
color: #28f;
|
||||
text-decoration: none;
|
||||
|
@ -63,7 +63,8 @@ class NeoGammaWLEDMethod {
|
||||
#define gamma32(c) NeoGammaWLEDMethod::Correct32(c)
|
||||
#define gamma8(c) NeoGammaWLEDMethod::rawGamma8(c)
|
||||
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
|
||||
uint32_t color_add(uint32_t,uint32_t);
|
||||
uint32_t color_add(uint32_t,uint32_t, bool fast=false);
|
||||
uint32_t color_fade(uint32_t c1, uint8_t amount, bool video=false);
|
||||
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
|
||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
|
||||
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
||||
@ -218,7 +219,7 @@ void deletePreset(byte index);
|
||||
bool getPresetName(byte index, String& name);
|
||||
|
||||
//remote.cpp
|
||||
void handleRemote();
|
||||
void handleRemote(uint8_t *data, size_t len);
|
||||
|
||||
//set.cpp
|
||||
bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
@ -234,6 +235,9 @@ void handleNotifications();
|
||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w);
|
||||
void refreshNodeList();
|
||||
void sendSysInfoUDP();
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast);
|
||||
#endif
|
||||
|
||||
//network.cpp
|
||||
int getSignalQuality(int rssi);
|
||||
@ -337,6 +341,7 @@ void userLoop();
|
||||
int getNumVal(const String* req, uint16_t pos);
|
||||
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
|
||||
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255);
|
||||
bool getBoolVal(JsonVariant elem, bool dflt);
|
||||
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||
bool oappend(const char* txt); // append new c string to temp buffer efficiently
|
||||
bool oappendi(int i); // append new number to temp buffer efficiently
|
||||
@ -353,6 +358,7 @@ void checkSettingsPIN(const char *pin);
|
||||
uint16_t crc16(const unsigned char* data_p, size_t length);
|
||||
um_data_t* simulateSound(uint8_t simulationId);
|
||||
void enumerateLedmaps();
|
||||
uint8_t get_random_wheel_index(uint8_t pos);
|
||||
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
//wled_eeprom.cpp
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
|
||||
const uint16_t PAGE_cpal_L = 4721;
|
||||
const uint8_t PAGE_cpal[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
|
||||
0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2,
|
||||
0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d,
|
||||
0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91,
|
||||
|
1574
wled00/html_other.h
1574
wled00/html_other.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2261
wled00/html_simple.h
2261
wled00/html_simple.h
File diff suppressed because it is too large
Load Diff
4041
wled00/html_ui.h
4041
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
|
||||
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
||||
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
||||
char vString[20];
|
||||
sprintf_P(vString, PSTR("0.14.0/%i"), VERSION);
|
||||
sprintf_P(vString, PSTR("0.15.0-a0/%i"), VERSION);
|
||||
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
||||
|
||||
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
||||
|
@ -675,7 +675,7 @@ void decodeIRJson(uint32_t code)
|
||||
} else {
|
||||
// HTTP API command
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
if (cmdStr.indexOf("~") || fdo["rpt"]) lastValidCode = code; // repeatable action
|
||||
if (cmdStr.indexOf("~") > 0 || fdo["rpt"]) lastValidCode = code; // repeatable action
|
||||
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
|
||||
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
|
||||
char tmp[10];
|
||||
|
@ -33,9 +33,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
|
||||
//DEBUG_PRINTLN("-- JSON deserialize segment.");
|
||||
Segment& seg = strip.getSegment(id);
|
||||
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
|
||||
Segment prev = seg; //make a backup so we can tell if something changed
|
||||
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
|
||||
//DEBUG_PRINTF("-- Original segment: %p (%p)\n", &seg, seg.data);
|
||||
Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor)
|
||||
//DEBUG_PRINTF("-- Duplicate segment: %p (%p)\n", &prev, prev.data);
|
||||
|
||||
uint16_t start = elem["start"] | seg.start;
|
||||
if (stop < 0) {
|
||||
@ -100,7 +100,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
|
||||
|
||||
seg.map1D2D = constrain(map1D2D, 0, 7);
|
||||
seg.soundSim = constrain(soundSim, 0, 1);
|
||||
seg.soundSim = constrain(soundSim, 0, 3);
|
||||
|
||||
uint8_t set = elem[F("set")] | seg.set;
|
||||
seg.set = constrain(set, 0, 3);
|
||||
@ -133,12 +133,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
seg.setOption(SEG_OPTION_ON, segbri); // use transition
|
||||
}
|
||||
|
||||
bool on = elem["on"] | seg.on;
|
||||
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
|
||||
seg.setOption(SEG_OPTION_ON, on); // use transition
|
||||
bool frz = elem["frz"] | seg.freeze;
|
||||
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.freeze;
|
||||
seg.freeze = frz;
|
||||
seg.setOption(SEG_OPTION_ON, getBoolVal(elem["on"], seg.on)); // use transition
|
||||
seg.freeze = getBoolVal(elem["frz"], seg.freeze);
|
||||
|
||||
seg.setCCT(elem["cct"] | seg.cct);
|
||||
|
||||
@ -201,15 +197,15 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
bool reverse = seg.reverse;
|
||||
bool mirror = seg.mirror;
|
||||
#endif
|
||||
seg.selected = elem["sel"] | seg.selected;
|
||||
seg.reverse = elem["rev"] | seg.reverse;
|
||||
seg.mirror = elem["mi"] | seg.mirror;
|
||||
seg.selected = getBoolVal(elem["sel"], seg.selected);
|
||||
seg.reverse = getBoolVal(elem["rev"], seg.reverse);
|
||||
seg.mirror = getBoolVal(elem["mi"] , seg.mirror);
|
||||
#ifndef WLED_DISABLE_2D
|
||||
bool reverse_y = seg.reverse_y;
|
||||
bool mirror_y = seg.mirror_y;
|
||||
seg.reverse_y = elem["rY"] | seg.reverse_y;
|
||||
seg.mirror_y = elem["mY"] | seg.mirror_y;
|
||||
seg.transpose = elem[F("tp")] | seg.transpose;
|
||||
seg.reverse_y = getBoolVal(elem["rY"] , seg.reverse_y);
|
||||
seg.mirror_y = getBoolVal(elem["mY"] , seg.mirror_y);
|
||||
seg.transpose = getBoolVal(elem[F("tp")], seg.transpose);
|
||||
if (seg.is2D() && seg.map1D2D == M12_pArc && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.fill(BLACK); // clear entire segment (in case of Arc 1D to 2D expansion)
|
||||
#endif
|
||||
|
||||
@ -234,9 +230,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
getVal(elem["c3"], &cust3); // we can't pass reference to bifield
|
||||
seg.custom3 = constrain(cust3, 0, 31);
|
||||
|
||||
seg.check1 = elem["o1"] | seg.check1;
|
||||
seg.check2 = elem["o2"] | seg.check2;
|
||||
seg.check3 = elem["o3"] | seg.check3;
|
||||
seg.check1 = getBoolVal(elem["o1"], seg.check1);
|
||||
seg.check2 = getBoolVal(elem["o2"], seg.check2);
|
||||
seg.check3 = getBoolVal(elem["o3"], seg.check3);
|
||||
|
||||
JsonArray iarr = elem[F("i")]; //set individual LEDs
|
||||
if (!iarr.isNull()) {
|
||||
@ -349,15 +345,14 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
|
||||
|
||||
JsonObject nl = root["nl"];
|
||||
nightlightActive = nl["on"] | nightlightActive;
|
||||
nightlightActive = getBoolVal(nl["on"], nightlightActive);
|
||||
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
|
||||
nightlightMode = nl["mode"] | nightlightMode;
|
||||
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
|
||||
|
||||
JsonObject udpn = root["udpn"];
|
||||
notifyDirect = udpn["send"] | notifyDirect;
|
||||
sendNotificationsRT = getBoolVal(udpn["send"], sendNotificationsRT);
|
||||
syncGroups = udpn["sgrp"] | syncGroups;
|
||||
receiveNotifications = udpn["recv"] | receiveNotifications;
|
||||
receiveGroups = udpn["rgrp"] | receiveGroups;
|
||||
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
|
||||
|
||||
@ -501,7 +496,8 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
|
||||
root["cct"] = seg.cct;
|
||||
root[F("set")] = seg.set;
|
||||
|
||||
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
|
||||
if (seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
|
||||
else if (forPreset) root["n"] = "";
|
||||
|
||||
// to conserve RAM we will serialize the col array manually
|
||||
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
|
||||
@ -573,8 +569,8 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
}
|
||||
|
||||
JsonObject udpn = root.createNestedObject("udpn");
|
||||
udpn["send"] = notifyDirect;
|
||||
udpn["recv"] = receiveNotifications;
|
||||
udpn["send"] = sendNotificationsRT;
|
||||
udpn["recv"] = receiveGroups != 0;
|
||||
udpn["sgrp"] = syncGroups;
|
||||
udpn["rgrp"] = receiveGroups;
|
||||
|
||||
@ -609,7 +605,7 @@ void serializeInfo(JsonObject root)
|
||||
{
|
||||
root[F("ver")] = versionString;
|
||||
root[F("vid")] = VERSION;
|
||||
//root[F("cn")] = WLED_CODENAME;
|
||||
root[F("cn")] = F(WLED_CODENAME);
|
||||
|
||||
JsonObject leds = root.createNestedObject("leds");
|
||||
leds[F("count")] = strip.getLengthTotal();
|
||||
@ -654,7 +650,7 @@ void serializeInfo(JsonObject root)
|
||||
spi.add(spi_miso);
|
||||
#endif
|
||||
|
||||
root[F("str")] = syncToggleReceive;
|
||||
root[F("str")] = false; //syncToggleReceive;
|
||||
|
||||
root[F("name")] = serverDescription;
|
||||
root[F("udpport")] = udpPort;
|
||||
@ -1016,8 +1012,15 @@ void serializeModeNames(JsonArray arr)
|
||||
}
|
||||
}
|
||||
|
||||
static volatile bool servingClient = false;
|
||||
void serveJson(AsyncWebServerRequest* request)
|
||||
{
|
||||
if (servingClient) {
|
||||
request->send(503, "application/json", F("{\"error\":2}")); // ERR_CONCURENCY
|
||||
return;
|
||||
}
|
||||
servingClient = true;
|
||||
|
||||
byte subJson = 0;
|
||||
const String& url = request->url();
|
||||
if (url.indexOf("state") > 0) subJson = JSON_PATH_STATE;
|
||||
@ -1031,23 +1034,28 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
else if (url.indexOf("live") > 0) {
|
||||
serveLiveLeds(request);
|
||||
servingClient = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
else if (url.indexOf("pal") > 0) {
|
||||
request->send_P(200, "application/json", JSON_palette_names);
|
||||
servingClient = false;
|
||||
return;
|
||||
}
|
||||
else if (url.indexOf("cfg") > 0 && handleFileRead(request, "/cfg.json")) {
|
||||
servingClient = false;
|
||||
return;
|
||||
}
|
||||
else if (url.length() > 6) { //not just /json
|
||||
request->send(501, "application/json", F("{\"error\":\"Not implemented\"}"));
|
||||
servingClient = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requestJSONBufferLock(17)) {
|
||||
request->send(503, "application/json", F("{\"error\":3}"));
|
||||
servingClient = false;
|
||||
return;
|
||||
}
|
||||
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==JSON_PATH_FXDATA || subJson==JSON_PATH_EFFECTS); // will clear and convert JsonDocument into JsonArray if necessary
|
||||
@ -1094,10 +1102,11 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
|
||||
request->send(response);
|
||||
releaseJSONBufferLock();
|
||||
servingClient = false;
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
#define MAX_LIVE_LEDS 180
|
||||
#define MAX_LIVE_LEDS 256
|
||||
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
{
|
||||
@ -1111,13 +1120,26 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
|
||||
char buffer[2000];
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (strip.isMatrix) {
|
||||
// ignore anything behid matrix (i.e. extra strip)
|
||||
used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal())
|
||||
n = 1;
|
||||
if (used > MAX_LIVE_LEDS) n = 2;
|
||||
if (used > MAX_LIVE_LEDS*4) n = 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
char buffer[2048]; // shoud be enough for 256 LEDs [RRGGBB] + all other text (9+25)
|
||||
strcpy_P(buffer, PSTR("{\"leds\":["));
|
||||
obuf = buffer;
|
||||
obuf = buffer; // assign buffer for oappnd() functions
|
||||
olen = 9;
|
||||
|
||||
for (size_t i = 0; i < used; i += n)
|
||||
{
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (strip.isMatrix && n>1 && (i/Segment::maxWidth)%n) i += Segment::maxWidth * (n-1);
|
||||
#endif
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
uint8_t r = R(c);
|
||||
uint8_t g = G(c);
|
||||
@ -1126,11 +1148,19 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
r = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
g = scale8(qadd8(w, g), strip.getBrightness()); //G
|
||||
b = scale8(qadd8(w, b), strip.getBrightness()); //B
|
||||
olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
|
||||
olen += sprintf_P(obuf + olen, PSTR("\"%06X\","), RGBW32(r,g,b,0));
|
||||
}
|
||||
olen -= 1;
|
||||
oappend((const char*)F("],\"n\":"));
|
||||
oappendi(n);
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (strip.isMatrix) {
|
||||
oappend((const char*)F(",\"w\":"));
|
||||
oappendi(Segment::maxWidth/n);
|
||||
oappend((const char*)F(",\"h\":"));
|
||||
oappendi(Segment::maxHeight/n);
|
||||
}
|
||||
#endif
|
||||
oappend("}");
|
||||
if (request) {
|
||||
request->send(200, "application/json", buffer);
|
||||
@ -1140,6 +1170,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
|
||||
wsc->text(obuf, olen);
|
||||
}
|
||||
#endif
|
||||
obuf = nullptr;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -165,6 +165,8 @@ void updateInterfaces(uint8_t callMode)
|
||||
|
||||
sendDataWs();
|
||||
lastInterfaceUpdate = millis();
|
||||
interfaceUpdateCallMode = 0; //disable further updates
|
||||
|
||||
if (callMode == CALL_MODE_WS_SEND) return;
|
||||
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
@ -174,7 +176,6 @@ void updateInterfaces(uint8_t callMode)
|
||||
}
|
||||
#endif
|
||||
doPublishMqtt = true;
|
||||
interfaceUpdateCallMode = 0; //disable
|
||||
}
|
||||
|
||||
|
||||
@ -226,7 +227,7 @@ void handleNightlight()
|
||||
if (!nightlightActiveOld) //init
|
||||
{
|
||||
nightlightStartTime = millis();
|
||||
nightlightDelayMs = (int)(nightlightDelayMins*60000);
|
||||
nightlightDelayMs = (unsigned)(nightlightDelayMins*60000);
|
||||
nightlightActiveOld = true;
|
||||
briNlT = bri;
|
||||
for (byte i=0; i<4; i++) colNlT[i] = col[i]; // remember starting color
|
||||
|
@ -212,9 +212,9 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
playlistSave = false;
|
||||
if (sObj[F("ql")].is<const char*>()) strlcpy(quickLoad, sObj[F("ql")].as<const char*>(), 9); // client limits QL to 2 chars, buffer for 8 bytes to allow unicode
|
||||
|
||||
if (sObj["o"].isNull()) { // no "o" means not a playlist or custom API call, saving of state is async (not immediately)
|
||||
includeBri = sObj["ib"].as<bool>() || index==255; // temporary preset needs brightness
|
||||
segBounds = sObj["sb"].as<bool>() || index==255; // temporary preset needs bounds
|
||||
if (sObj.size()==0 || sObj["o"].isNull()) { // no "o" means not a playlist or custom API call, saving of state is async (not immediately)
|
||||
includeBri = sObj["ib"].as<bool>() || sObj.size()==0 || index==255; // temporary preset needs brightness
|
||||
segBounds = sObj["sb"].as<bool>() || sObj.size()==0 || index==255; // temporary preset needs bounds
|
||||
selectedOnly = sObj[F("sc")].as<bool>();
|
||||
saveLedmap = sObj[F("ledmap")] | -1;
|
||||
} else {
|
||||
|
@ -1,8 +1,5 @@
|
||||
#include "wled.h"
|
||||
|
||||
#define ESP_NOW_STATE_UNINIT 0
|
||||
#define ESP_NOW_STATE_ON 1
|
||||
#define ESP_NOW_STATE_ERROR 2
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
|
||||
#define NIGHT_MODE_DEACTIVATED -1
|
||||
#define NIGHT_MODE_BRIGHTNESS 5
|
||||
@ -17,59 +14,54 @@
|
||||
#define WIZMOTE_BUTTON_BRIGHT_UP 9
|
||||
#define WIZMOTE_BUTTON_BRIGHT_DOWN 8
|
||||
|
||||
#ifdef WLED_DISABLE_ESPNOW
|
||||
void handleRemote(){}
|
||||
#else
|
||||
|
||||
// This is kind of an esoteric strucure because it's pulled from the "Wizmote"
|
||||
// product spec. That remote is used as the baseline for behavior and availability
|
||||
// since it's broadly commercially available and works out of the box as a drop-in
|
||||
typedef struct message_structure {
|
||||
typedef struct WizMoteMessageStructure {
|
||||
uint8_t program; // 0x91 for ON button, 0x81 for all others
|
||||
uint8_t seq[4]; // Incremetal sequence number 32 bit unsigned integer LSB first
|
||||
uint8_t byte5 = 32; // Unknown
|
||||
uint8_t byte5; // Unknown (seen 0x20)
|
||||
uint8_t button; // Identifies which button is being pressed
|
||||
uint8_t byte8 = 1; // Unknown, but always 0x01
|
||||
uint8_t byte9 = 100; // Unnkown, but always 0x64
|
||||
uint8_t byte8; // Unknown, but always 0x01
|
||||
uint8_t byte9; // Unnkown, but always 0x64
|
||||
|
||||
uint8_t byte10; // Unknown, maybe checksum
|
||||
uint8_t byte11; // Unknown, maybe checksum
|
||||
uint8_t byte12; // Unknown, maybe checksum
|
||||
uint8_t byte13; // Unknown, maybe checksum
|
||||
} message_structure;
|
||||
} message_structure_t;
|
||||
|
||||
static int esp_now_state = ESP_NOW_STATE_UNINIT;
|
||||
static uint32_t last_seq = UINT32_MAX;
|
||||
static int brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
|
||||
static message_structure incoming;
|
||||
|
||||
// Pulled from the IR Remote logic but reduced to 10 steps with a constant of 3
|
||||
static const byte brightnessSteps[] = {
|
||||
6, 9, 14, 22, 33, 50, 75, 113, 170, 255
|
||||
};
|
||||
static const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
|
||||
static const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(byte);
|
||||
|
||||
static bool nightModeActive() {
|
||||
inline bool nightModeActive() {
|
||||
return brightnessBeforeNightMode != NIGHT_MODE_DEACTIVATED;
|
||||
}
|
||||
|
||||
static void activateNightMode() {
|
||||
if (nightModeActive()) return;
|
||||
brightnessBeforeNightMode = bri;
|
||||
bri = NIGHT_MODE_BRIGHTNESS;
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
static bool resetNightMode() {
|
||||
if (!nightModeActive()) {
|
||||
return false;
|
||||
}
|
||||
if (!nightModeActive()) return false;
|
||||
bri = brightnessBeforeNightMode;
|
||||
brightnessBeforeNightMode = NIGHT_MODE_DEACTIVATED;
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
return true;
|
||||
}
|
||||
|
||||
// increment `bri` to the next `brightnessSteps` value
|
||||
static void brightnessUp() {
|
||||
if (nightModeActive()) { return; }
|
||||
if (nightModeActive()) return;
|
||||
// dumb incremental search is efficient enough for so few items
|
||||
for (uint8_t index = 0; index < numBrightnessSteps; ++index) {
|
||||
if (brightnessSteps[index] > bri) {
|
||||
@ -77,11 +69,12 @@ static void brightnessUp() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
// decrement `bri` to the next `brightnessSteps` value
|
||||
static void brightnessDown() {
|
||||
if (nightModeActive()) { return; }
|
||||
if (nightModeActive()) return;
|
||||
// dumb incremental search is efficient enough for so few items
|
||||
for (int index = numBrightnessSteps - 1; index >= 0; --index) {
|
||||
if (brightnessSteps[index] < bri) {
|
||||
@ -89,39 +82,98 @@ static void brightnessDown() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
|
||||
static void setOn() {
|
||||
if (resetNightMode()) {
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
resetNightMode();
|
||||
if (!bri) {
|
||||
toggleOnOff();
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
static void setOff() {
|
||||
if (resetNightMode()) {
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
resetNightMode();
|
||||
if (bri) {
|
||||
toggleOnOff();
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
}
|
||||
}
|
||||
|
||||
static void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
|
||||
void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) {
|
||||
resetNightMode();
|
||||
unloadPlaylist();
|
||||
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
|
||||
}
|
||||
|
||||
// Callback function that will be executed when data is received
|
||||
#ifdef ESP8266
|
||||
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
|
||||
#else
|
||||
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
|
||||
#endif
|
||||
// this function follows the same principle as decodeIRJson()
|
||||
static bool remoteJson(int button)
|
||||
{
|
||||
char objKey[10];
|
||||
bool parsed = false;
|
||||
|
||||
sprintf (last_signal_src, "%02x%02x%02x%02x%02x%02x",
|
||||
mac [0], mac [1], mac [2], mac [3], mac [4], mac [5]);
|
||||
if (!requestJSONBufferLock(22)) return false;
|
||||
|
||||
sprintf_P(objKey, PSTR("\"%d\":"), button);
|
||||
|
||||
// attempt to read command from remote.json
|
||||
readObjectFromFile("/remote.json", objKey, &doc);
|
||||
JsonObject fdo = doc.as<JsonObject>();
|
||||
if (fdo.isNull()) {
|
||||
// the received button does not exist
|
||||
if (!WLED_FS.exists("/remote.json")) errorFlag = ERR_FS_RMLOAD; //warn if file itself doesn't exist
|
||||
releaseJSONBufferLock();
|
||||
return parsed;
|
||||
}
|
||||
|
||||
String cmdStr = fdo["cmd"].as<String>();
|
||||
JsonObject jsonCmdObj = fdo["cmd"]; //object
|
||||
|
||||
if (jsonCmdObj.isNull()) // we could also use: fdo["cmd"].is<String>()
|
||||
{
|
||||
if (cmdStr.startsWith("!")) {
|
||||
// call limited set of C functions
|
||||
if (cmdStr.startsWith(F("!incBri"))) {
|
||||
brightnessUp();
|
||||
parsed = true;
|
||||
} else if (cmdStr.startsWith(F("!decBri"))) {
|
||||
brightnessDown();
|
||||
parsed = true;
|
||||
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
|
||||
uint8_t p1 = fdo["PL"] | 1;
|
||||
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
|
||||
uint8_t p3 = fdo["FP"] | 0;
|
||||
presetWithFallback(p1, p2, p3);
|
||||
parsed = true;
|
||||
}
|
||||
} else {
|
||||
// HTTP API command
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
//if (cmdStr.indexOf("~") || fdo["rpt"]) lastValidCode = code; // repeatable action
|
||||
if (!cmdStr.startsWith(apireq)) cmdStr = apireq + cmdStr; // if no "win&" prefix
|
||||
if (!irApplyToAllSelected && cmdStr.indexOf(F("SS="))<0) {
|
||||
char tmp[10];
|
||||
sprintf_P(tmp, PSTR("&SS=%d"), strip.getMainSegmentId());
|
||||
cmdStr += tmp;
|
||||
}
|
||||
fdo.clear(); // clear JSON buffer (it is no longer needed)
|
||||
handleSet(nullptr, cmdStr, false); // no stateUpdated() call here
|
||||
stateUpdated(CALL_MODE_BUTTON);
|
||||
parsed = true;
|
||||
}
|
||||
} else {
|
||||
// command is JSON object (TODO: currently will not handle irApplyToAllSelected correctly)
|
||||
deserializeState(jsonCmdObj, CALL_MODE_BUTTON);
|
||||
parsed = true;
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// Callback function that will be executed when data is received
|
||||
void handleRemote(uint8_t *incomingData, size_t len) {
|
||||
message_structure_t *incoming = reinterpret_cast<message_structure_t *>(incomingData);
|
||||
|
||||
if (strcmp(last_signal_src, linked_remote) != 0) {
|
||||
DEBUG_PRINT(F("ESP Now Message Received from Unlinked Sender: "));
|
||||
@ -129,72 +181,40 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (len != sizeof(incoming)) {
|
||||
if (len != sizeof(message_structure_t)) {
|
||||
DEBUG_PRINT(F("Unknown incoming ESP Now message received of length "));
|
||||
DEBUG_PRINTLN(len);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&(incoming.program), incomingData, sizeof(incoming));
|
||||
uint32_t cur_seq = incoming.seq[0] | (incoming.seq[1] << 8) | (incoming.seq[2] << 16) | (incoming.seq[3] << 24);
|
||||
|
||||
uint32_t cur_seq = incoming->seq[0] | (incoming->seq[1] << 8) | (incoming->seq[2] << 16) | (incoming->seq[3] << 24);
|
||||
if (cur_seq == last_seq) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_PRINT(F("Incoming ESP Now Packet ["));
|
||||
DEBUG_PRINT(cur_seq);
|
||||
DEBUG_PRINT(F("] from sender ["));
|
||||
DEBUG_PRINT(last_signal_src);
|
||||
DEBUG_PRINT(F("] button: "));
|
||||
DEBUG_PRINTLN(incoming.button);
|
||||
switch (incoming.button) {
|
||||
case WIZMOTE_BUTTON_ON : setOn(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
case WIZMOTE_BUTTON_OFF : setOff(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); resetNightMode(); break;
|
||||
case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); resetNightMode(); break;
|
||||
case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); resetNightMode(); break;
|
||||
case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); resetNightMode(); break;
|
||||
case WIZMOTE_BUTTON_NIGHT : activateNightMode(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||
DEBUG_PRINTLN(incoming->button);
|
||||
|
||||
if (!remoteJson(incoming->button))
|
||||
switch (incoming->button) {
|
||||
case WIZMOTE_BUTTON_ON : setOn(); break;
|
||||
case WIZMOTE_BUTTON_OFF : setOff(); break;
|
||||
case WIZMOTE_BUTTON_ONE : presetWithFallback(1, FX_MODE_STATIC, 0); break;
|
||||
case WIZMOTE_BUTTON_TWO : presetWithFallback(2, FX_MODE_BREATH, 0); break;
|
||||
case WIZMOTE_BUTTON_THREE : presetWithFallback(3, FX_MODE_FIRE_FLICKER, 0); break;
|
||||
case WIZMOTE_BUTTON_FOUR : presetWithFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
case WIZMOTE_BUTTON_NIGHT : activateNightMode(); break;
|
||||
case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); break;
|
||||
case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); break;
|
||||
default: break;
|
||||
|
||||
}
|
||||
|
||||
last_seq = cur_seq;
|
||||
}
|
||||
|
||||
void handleRemote() {
|
||||
if (enable_espnow_remote) {
|
||||
if ((esp_now_state == ESP_NOW_STATE_UNINIT) && (interfacesInited || apActive)) { // ESPNOW requires Wifi to be initialized (either STA, or AP Mode)
|
||||
DEBUG_PRINTLN(F("Initializing ESP_NOW listener"));
|
||||
// Init ESP-NOW
|
||||
if (esp_now_init() != 0) {
|
||||
DEBUG_PRINTLN(F("Error initializing ESP-NOW"));
|
||||
esp_now_state = ESP_NOW_STATE_ERROR;
|
||||
}
|
||||
|
||||
#ifdef ESP8266
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
|
||||
#endif
|
||||
|
||||
esp_now_register_recv_cb(OnDataRecv);
|
||||
esp_now_state = ESP_NOW_STATE_ON;
|
||||
}
|
||||
} else {
|
||||
if (esp_now_state == ESP_NOW_STATE_ON) {
|
||||
DEBUG_PRINTLN(F("Disabling ESP-NOW Remote Listener"));
|
||||
if (esp_now_deinit() != 0) {
|
||||
DEBUG_PRINTLN(F("Error de-initializing ESP-NOW"));
|
||||
}
|
||||
esp_now_state = ESP_NOW_STATE_UNINIT;
|
||||
} else if (esp_now_state == ESP_NOW_STATE_ERROR) {
|
||||
//Clear any error states (allows retrying by cycling)
|
||||
esp_now_state = ESP_NOW_STATE_UNINIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void handleRemote(uint8_t *incomingData, size_t len) {}
|
||||
#endif
|
@ -19,27 +19,41 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
//WIFI SETTINGS
|
||||
if (subPage == SUBPAGE_WIFI)
|
||||
{
|
||||
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
|
||||
char oldSSID[sizeof(clientSSID)];
|
||||
|
||||
if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) strlcpy(clientPass, request->arg(F("CP")).c_str(), 65);
|
||||
strcpy(oldSSID, clientSSID);
|
||||
strlcpy(clientSSID,request->arg(F("CS")).c_str(), 33);
|
||||
if (!strcmp(oldSSID, clientSSID)) forceReconnect = true;
|
||||
|
||||
if (!isAsterisksOnly(request->arg(F("CP")).c_str(), 65)) {
|
||||
strlcpy(clientPass, request->arg(F("CP")).c_str(), 65);
|
||||
forceReconnect = true;
|
||||
}
|
||||
|
||||
strlcpy(cmDNS, request->arg(F("CM")).c_str(), 33);
|
||||
|
||||
apBehavior = request->arg(F("AB")).toInt();
|
||||
strcpy(oldSSID, apSSID);
|
||||
strlcpy(apSSID, request->arg(F("AS")).c_str(), 33);
|
||||
if (!strcmp(oldSSID, apSSID) && apActive) forceReconnect = true;
|
||||
apHide = request->hasArg(F("AH"));
|
||||
int passlen = request->arg(F("AP")).length();
|
||||
if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg(F("AP")).c_str(), 65))) strlcpy(apPass, request->arg(F("AP")).c_str(), 65);
|
||||
int t = request->arg(F("AC")).toInt(); if (t > 0 && t < 14) apChannel = t;
|
||||
if (passlen == 0 || (passlen > 7 && !isAsterisksOnly(request->arg(F("AP")).c_str(), 65))) {
|
||||
strlcpy(apPass, request->arg(F("AP")).c_str(), 65);
|
||||
forceReconnect = true;
|
||||
}
|
||||
int t = request->arg(F("AC")).toInt();
|
||||
if (t != apChannel) forceReconnect = true;
|
||||
if (t > 0 && t < 14) apChannel = t;
|
||||
|
||||
noWifiSleep = request->hasArg(F("WS"));
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
enable_espnow_remote = request->hasArg(F("RE"));
|
||||
bool oldESPNow = enableESPNow;
|
||||
enableESPNow = request->hasArg(F("RE"));
|
||||
if (oldESPNow != enableESPNow) forceReconnect = true;
|
||||
strlcpy(linked_remote, request->arg(F("RMAC")).c_str(), 13);
|
||||
|
||||
//Normalize MAC format to lowercase
|
||||
strlcpy(linked_remote,strlwr(linked_remote), 13);
|
||||
strlwr(linked_remote); //Normalize MAC format to lowercase
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
@ -80,10 +94,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t colorOrder, type, skip, awmode, channelSwap;
|
||||
uint16_t length, start;
|
||||
uint8_t colorOrder, type, skip, awmode, channelSwap, maPerLed;
|
||||
uint16_t length, start, maMax;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
|
||||
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
//strip.milliampsPerLed = request->arg(F("LA")).toInt();
|
||||
|
||||
autoSegments = request->hasArg(F("MS"));
|
||||
correctWB = request->hasArg(F("CCT"));
|
||||
cctFromRgb = request->hasArg(F("CR"));
|
||||
@ -106,6 +123,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed (DotStar & PWM)
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED mA
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max mA
|
||||
if (!request->hasArg(lp)) {
|
||||
DEBUG_PRINT(F("No data for "));
|
||||
DEBUG_PRINTLN(s);
|
||||
@ -150,10 +169,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
freqHz = 0;
|
||||
}
|
||||
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0;
|
||||
if ((type > TYPE_TM1814 && type < TYPE_WS2801) || type >= TYPE_NET_DDP_RGB) { // analog and virtual
|
||||
maPerLed = 0;
|
||||
maMax = 0;
|
||||
} else {
|
||||
maPerLed = request->arg(la).toInt();
|
||||
maMax = request->arg(ma).toInt(); // if ABL is disabled this will be 0
|
||||
}
|
||||
// actual finalization is done in WLED::loop() (removing old busses and adding new)
|
||||
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
|
||||
if (busConfigs[s] != nullptr) delete busConfigs[s];
|
||||
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer);
|
||||
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freqHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
@ -227,9 +253,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
touchThreshold = request->arg(F("TT")).toInt();
|
||||
|
||||
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
strip.milliampsPerLed = request->arg(F("LA")).toInt();
|
||||
|
||||
briS = request->arg(F("CA")).toInt();
|
||||
|
||||
turnOnAtBoot = request->hasArg(F("BO"));
|
||||
@ -247,6 +270,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
|
||||
fadeTransition = request->hasArg(F("TF"));
|
||||
modeBlending = request->hasArg(F("EB"));
|
||||
t = request->arg(F("TD")).toInt();
|
||||
if (t >= 0) transitionDelayDefault = t;
|
||||
strip.paletteFade = request->hasArg(F("PF"));
|
||||
@ -271,7 +295,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (subPage == SUBPAGE_UI)
|
||||
{
|
||||
strlcpy(serverDescription, request->arg(F("DS")).c_str(), 33);
|
||||
syncToggleReceive = request->hasArg(F("ST"));
|
||||
//syncToggleReceive = request->hasArg(F("ST"));
|
||||
#ifdef WLED_ENABLE_SIMPLE_UI
|
||||
if (simplifiedUI ^ request->hasArg(F("SU"))) {
|
||||
// UI selection changed, invalidate browser cache
|
||||
@ -293,6 +317,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
t = request->arg(F("U2")).toInt();
|
||||
if (t > 0) udpPort2 = t;
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
useESPNowSync = request->hasArg(F("EN"));
|
||||
#endif
|
||||
|
||||
syncGroups = request->arg(F("GS")).toInt();
|
||||
receiveGroups = request->arg(F("GR")).toInt();
|
||||
|
||||
@ -301,9 +329,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
receiveNotificationEffects = request->hasArg(F("RX"));
|
||||
receiveSegmentOptions = request->hasArg(F("SO"));
|
||||
receiveSegmentBounds = request->hasArg(F("SG"));
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects || receiveSegmentOptions);
|
||||
notifyDirectDefault = request->hasArg(F("SD"));
|
||||
notifyDirect = notifyDirectDefault;
|
||||
sendNotifications = request->hasArg(F("SS"));
|
||||
notifyDirect = request->hasArg(F("SD"));
|
||||
notifyButton = request->hasArg(F("SB"));
|
||||
notifyAlexa = request->hasArg(F("SA"));
|
||||
notifyHue = request->hasArg(F("SH"));
|
||||
@ -317,7 +344,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (!nodeListEnabled) Nodes.clear();
|
||||
nodeBroadcastEnabled = request->hasArg(F("NB"));
|
||||
|
||||
receiveDirect = request->hasArg(F("RD"));
|
||||
receiveDirect = request->hasArg(F("RD")); // UDP realtime
|
||||
useMainSegmentOnly = request->hasArg(F("MO"));
|
||||
e131SkipOutOfSequence = request->hasArg(F("ES"));
|
||||
e131Multicast = request->hasArg(F("EM"));
|
||||
@ -1003,7 +1030,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
//toggle receive UDP direct notifications
|
||||
pos = req.indexOf(F("RN="));
|
||||
if (pos > 0) receiveNotifications = (req.charAt(pos+3) != '0');
|
||||
if (pos > 0) receiveGroups = (req.charAt(pos+3) != '0') ? receiveGroups | 1 : receiveGroups & 0xFE;
|
||||
|
||||
//receive live data via UDP/Hyperion
|
||||
pos = req.indexOf(F("RD="));
|
||||
|
478
wled00/udp.cpp
478
wled00/udp.cpp
@ -5,15 +5,26 @@
|
||||
*/
|
||||
|
||||
#define UDP_SEG_SIZE 36
|
||||
#define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
|
||||
#define SEG_OFFSET (41)
|
||||
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
|
||||
#define UDP_IN_MAXSIZE 1472
|
||||
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
|
||||
|
||||
typedef struct PartialEspNowPacket {
|
||||
uint8_t magic;
|
||||
uint8_t packet;
|
||||
uint8_t segs;
|
||||
uint8_t data[247];
|
||||
} partial_packet_t;
|
||||
|
||||
void notify(byte callMode, bool followUp)
|
||||
{
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (!udpConnected && !useESPNowSync) return;
|
||||
#else
|
||||
if (!udpConnected) return;
|
||||
if (!syncGroups) return;
|
||||
#endif
|
||||
if (!syncGroups || !sendNotificationsRT) return;
|
||||
switch (callMode)
|
||||
{
|
||||
case CALL_MODE_INIT: return;
|
||||
@ -26,7 +37,7 @@ void notify(byte callMode, bool followUp)
|
||||
case CALL_MODE_ALEXA: if (!notifyAlexa) return; break;
|
||||
default: return;
|
||||
}
|
||||
byte udpOut[WLEDPACKETSIZE];
|
||||
byte udpOut[WLEDPACKETSIZE]; //TODO: optimize size to use only active segments
|
||||
Segment& mainseg = strip.getMainSegment();
|
||||
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
|
||||
udpOut[1] = callMode;
|
||||
@ -138,17 +149,233 @@ void notify(byte callMode, bool followUp)
|
||||
//uint16_t offs = SEG_OFFSET;
|
||||
//next value to be added has index: udpOut[offs + 0]
|
||||
|
||||
IPAddress broadcastIp;
|
||||
broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (enableESPNow && useESPNowSync && statusESPNow == ESP_NOW_STATE_ON) {
|
||||
partial_packet_t buffer = {'W', 0, (uint8_t)s, {0}};
|
||||
// send global data
|
||||
DEBUG_PRINTLN(F("ESP-NOW sending first packet."));
|
||||
memcpy(buffer.data, udpOut, 41);
|
||||
auto err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), 41+3);
|
||||
if (!err) {
|
||||
// send segment data
|
||||
buffer.packet++;
|
||||
size_t packetSize = 0;
|
||||
int32_t err = 0;
|
||||
for (size_t i = 0; i < s; i++) {
|
||||
memcpy(buffer.data + packetSize, &udpOut[41+i*UDP_SEG_SIZE], UDP_SEG_SIZE);
|
||||
packetSize += UDP_SEG_SIZE;
|
||||
if (packetSize + UDP_SEG_SIZE < sizeof(buffer.data)/sizeof(uint8_t)) continue;
|
||||
DEBUG_PRINTF("ESP-NOW sending packet: %d (%d)\n", (int)buffer.packet, packetSize+3);
|
||||
err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), packetSize+3);
|
||||
buffer.packet++;
|
||||
packetSize = 0;
|
||||
if (err) break;
|
||||
}
|
||||
if (!err && packetSize > 0) {
|
||||
DEBUG_PRINTF("ESP-NOW sending last packet: %d (%d)\n", (int)buffer.packet, packetSize+3);
|
||||
err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), packetSize+3);
|
||||
}
|
||||
if (err) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW sending packet failed."));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (udpConnected)
|
||||
#endif
|
||||
{
|
||||
DEBUG_PRINTLN(F("UDP sending packet."));
|
||||
IPAddress broadcastIp = ~uint32_t(Network.subnetMask()) | uint32_t(Network.gatewayIP());
|
||||
notifierUdp.beginPacket(broadcastIp, udpPort);
|
||||
notifierUdp.write(udpOut, WLEDPACKETSIZE);
|
||||
notifierUdp.write(udpOut, WLEDPACKETSIZE); // TODO: add actual used buffer size
|
||||
notifierUdp.endPacket();
|
||||
}
|
||||
notificationSentCallMode = callMode;
|
||||
notificationSentTime = millis();
|
||||
notificationCount = followUp ? notificationCount + 1 : 0;
|
||||
}
|
||||
|
||||
void parseNotifyPacket(uint8_t *udpIn) {
|
||||
//ignore notification if received within a second after sending a notification ourselves
|
||||
if (millis() - notificationSentTime < 1000) return;
|
||||
if (udpIn[1] > 199) return; //do not receive custom versions
|
||||
|
||||
//compatibilityVersionByte:
|
||||
byte version = udpIn[11];
|
||||
DEBUG_PRINT(F("UDP packet version: ")); DEBUG_PRINTLN(version);
|
||||
|
||||
// if we are not part of any sync group ignore message
|
||||
if (version < 9) {
|
||||
// legacy senders are treated as if sending in sync group 1 only
|
||||
if (!(receiveGroups & 0x01)) return;
|
||||
} else if (!(receiveGroups & udpIn[36])) return;
|
||||
|
||||
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
|
||||
//apply colors from notification to main segment, only if not syncing full segments
|
||||
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
|
||||
// primary color, only apply white if intented (version > 0)
|
||||
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
|
||||
if (version > 1) {
|
||||
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
|
||||
}
|
||||
if (version > 6) {
|
||||
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
|
||||
if (version > 9 && udpIn[37] < 255) { // valid CCT/Kelvin value
|
||||
uint16_t cct = udpIn[38];
|
||||
if (udpIn[37] > 0) { //Kelvin
|
||||
cct |= (udpIn[37] << 8);
|
||||
}
|
||||
strip.setCCT(cct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool timebaseUpdated = false;
|
||||
//apply effects from notification
|
||||
bool applyEffects = (receiveNotificationEffects || !someSel);
|
||||
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
|
||||
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
|
||||
uint8_t numSrcSegs = udpIn[39];
|
||||
DEBUG_PRINT(F("UDP segments: ")); DEBUG_PRINTLN(numSrcSegs);
|
||||
// are we syncing bounds and slave has more active segments than master?
|
||||
if (receiveSegmentBounds && numSrcSegs < strip.getActiveSegmentsNum()) {
|
||||
DEBUG_PRINTLN(F("Removing excessive segments."));
|
||||
for (size_t i=strip.getSegmentsNum(); i>numSrcSegs; i--) {
|
||||
if (strip.getSegment(i).isActive()) {
|
||||
strip.setSegment(i-1,0,0); // delete segment
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t inactiveSegs = 0;
|
||||
for (size_t i = 0; i < numSrcSegs && i < strip.getMaxSegments(); i++) {
|
||||
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
|
||||
uint8_t id = udpIn[0 +ofs];
|
||||
DEBUG_PRINT(F("UDP segment received: ")); DEBUG_PRINTLN(id);
|
||||
if (id > strip.getSegmentsNum()) break;
|
||||
else if (id == strip.getSegmentsNum()) {
|
||||
if (receiveSegmentBounds && id < strip.getMaxSegments()) strip.appendSegment();
|
||||
else break;
|
||||
}
|
||||
DEBUG_PRINT(F("UDP segment check: ")); DEBUG_PRINTLN(id);
|
||||
Segment& selseg = strip.getSegment(id);
|
||||
// if we are not syncing bounds skip unselected segments
|
||||
if (selseg.isActive() && !(selseg.isSelected() || receiveSegmentBounds)) continue;
|
||||
// ignore segment if it is inactive and we are not syncing bounds
|
||||
if (!receiveSegmentBounds) {
|
||||
if (!selseg.isActive()) {
|
||||
inactiveSegs++;
|
||||
continue;
|
||||
} else {
|
||||
id += inactiveSegs; // adjust id
|
||||
}
|
||||
}
|
||||
DEBUG_PRINT(F("UDP segment processing: ")); DEBUG_PRINTLN(id);
|
||||
|
||||
uint16_t startY = 0, start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
|
||||
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
|
||||
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
|
||||
if (!receiveSegmentOptions) {
|
||||
//selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
|
||||
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
|
||||
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
|
||||
continue; // we do receive bounds, but not options
|
||||
}
|
||||
selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional
|
||||
selseg.setOpacity(udpIn[10+ofs]);
|
||||
if (applyEffects) {
|
||||
selseg.setMode(udpIn[11+ofs]);
|
||||
selseg.speed = udpIn[12+ofs];
|
||||
selseg.intensity = udpIn[13+ofs];
|
||||
selseg.palette = udpIn[14+ofs];
|
||||
}
|
||||
if (receiveNotificationColor || !someSel) {
|
||||
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
|
||||
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
|
||||
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
|
||||
selseg.setCCT(udpIn[27+ofs]);
|
||||
}
|
||||
if (version > 11) {
|
||||
// when applying synced options ignore selected as it may be used as indicator of which segments to sync
|
||||
// freeze, reset should never be synced
|
||||
// LSB to MSB: select, reverse, on, mirror, freeze, reset, reverse_y, mirror_y, transpose, map1d2d (3), ssim (2), set (2)
|
||||
selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
|
||||
if (applyEffects) {
|
||||
selseg.custom1 = udpIn[29+ofs];
|
||||
selseg.custom2 = udpIn[30+ofs];
|
||||
selseg.custom3 = udpIn[31+ofs] & 0x1F;
|
||||
selseg.check1 = (udpIn[31+ofs]>>5) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>7) & 0x1;
|
||||
}
|
||||
startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]);
|
||||
stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]);
|
||||
}
|
||||
if (receiveSegmentBounds) {
|
||||
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
|
||||
strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
|
||||
} else {
|
||||
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
|
||||
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
|
||||
}
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
// simple effect sync, applies to all selected segments
|
||||
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
|
||||
for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.setMode(udpIn[8]);
|
||||
seg.speed = udpIn[9];
|
||||
if (version > 2) seg.intensity = udpIn[16];
|
||||
if (version > 4) seg.setPalette(udpIn[19]);
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
if (applyEffects && version > 5) {
|
||||
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
|
||||
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
|
||||
t -= millis();
|
||||
strip.timebase = t;
|
||||
timebaseUpdated = true;
|
||||
}
|
||||
|
||||
//adjust system time, but only if sender is more accurate than self
|
||||
if (version > 7) {
|
||||
Toki::Time tm;
|
||||
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
|
||||
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
|
||||
if (udpIn[29] > toki.getTimeSource()) { //if sender's time source is more accurate
|
||||
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
|
||||
uint8_t ts = TOKI_TS_UDP;
|
||||
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
|
||||
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
|
||||
toki.setTime(tm, ts);
|
||||
} else if (timebaseUpdated && toki.getTimeSource() > 99) { //if we both have good times, get a more accurate timebase
|
||||
Toki::Time myTime = toki.getTime();
|
||||
uint32_t diff = toki.msDifference(tm, myTime);
|
||||
strip.timebase -= PRESUMED_NETWORK_DELAY; //no need to presume, use difference between NTP times at send and receive points
|
||||
if (toki.isLater(tm, myTime)) {
|
||||
strip.timebase += diff;
|
||||
} else {
|
||||
strip.timebase -= diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version > 3) {
|
||||
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
|
||||
}
|
||||
|
||||
nightlightActive = udpIn[6];
|
||||
if (nightlightActive) nightlightDelayMins = udpIn[7];
|
||||
|
||||
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
}
|
||||
|
||||
void realtimeLock(uint32_t timeoutMs, byte md)
|
||||
{
|
||||
if (!realtimeMode && !realtimeOverride) {
|
||||
@ -262,8 +489,6 @@ void handleNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
if (!(receiveNotifications || receiveDirect)) return;
|
||||
|
||||
localIP = Network.localIP();
|
||||
//notifier and UDP realtime
|
||||
if (!packetSize || packetSize > UDP_IN_MAXSIZE) return;
|
||||
@ -306,160 +531,10 @@ void handleNotifications()
|
||||
}
|
||||
|
||||
//wled notifier, ignore if realtime packets active
|
||||
if (udpIn[0] == 0 && !realtimeMode && receiveNotifications)
|
||||
if (udpIn[0] == 0 && !realtimeMode && receiveGroups)
|
||||
{
|
||||
//ignore notification if received within a second after sending a notification ourselves
|
||||
if (millis() - notificationSentTime < 1000) return;
|
||||
if (udpIn[1] > 199) return; //do not receive custom versions
|
||||
|
||||
//compatibilityVersionByte:
|
||||
byte version = udpIn[11];
|
||||
|
||||
// if we are not part of any sync group ignore message
|
||||
if (version < 9 || version > 199) {
|
||||
// legacy senders are treated as if sending in sync group 1 only
|
||||
if (!(receiveGroups & 0x01)) return;
|
||||
} else if (!(receiveGroups & udpIn[36])) return;
|
||||
|
||||
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
|
||||
//apply colors from notification to main segment, only if not syncing full segments
|
||||
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
|
||||
// primary color, only apply white if intented (version > 0)
|
||||
strip.setColor(0, RGBW32(udpIn[3], udpIn[4], udpIn[5], (version > 0) ? udpIn[10] : 0));
|
||||
if (version > 1) {
|
||||
strip.setColor(1, RGBW32(udpIn[12], udpIn[13], udpIn[14], udpIn[15])); // secondary color
|
||||
}
|
||||
if (version > 6) {
|
||||
strip.setColor(2, RGBW32(udpIn[20], udpIn[21], udpIn[22], udpIn[23])); // tertiary color
|
||||
if (version > 9 && version < 200 && udpIn[37] < 255) { // valid CCT/Kelvin value
|
||||
uint16_t cct = udpIn[38];
|
||||
if (udpIn[37] > 0) { //Kelvin
|
||||
cct |= (udpIn[37] << 8);
|
||||
}
|
||||
strip.setCCT(cct);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool timebaseUpdated = false;
|
||||
//apply effects from notification
|
||||
bool applyEffects = (receiveNotificationEffects || !someSel);
|
||||
if (version < 200)
|
||||
{
|
||||
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
|
||||
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
|
||||
uint8_t numSrcSegs = udpIn[39];
|
||||
for (size_t i = 0; i < numSrcSegs; i++) {
|
||||
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
|
||||
uint8_t id = udpIn[0 +ofs];
|
||||
if (id > strip.getSegmentsNum()) break;
|
||||
|
||||
Segment& selseg = strip.getSegment(id);
|
||||
if (!selseg.isActive() || !selseg.isSelected()) continue; //do not apply to non selected segments
|
||||
|
||||
uint16_t startY = 0, start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
|
||||
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
|
||||
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
|
||||
if (!receiveSegmentOptions) {
|
||||
selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
|
||||
continue;
|
||||
}
|
||||
//for (size_t j = 1; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, on, reversed; ignore selected
|
||||
selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional
|
||||
selseg.setOpacity(udpIn[10+ofs]);
|
||||
if (applyEffects) {
|
||||
strip.setMode(id, udpIn[11+ofs]);
|
||||
selseg.speed = udpIn[12+ofs];
|
||||
selseg.intensity = udpIn[13+ofs];
|
||||
selseg.palette = udpIn[14+ofs];
|
||||
}
|
||||
if (receiveNotificationColor || !someSel) {
|
||||
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
|
||||
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
|
||||
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
|
||||
selseg.setCCT(udpIn[27+ofs]);
|
||||
}
|
||||
if (version > 11) {
|
||||
// when applying synced options ignore selected as it may be used as indicator of which segments to sync
|
||||
// freeze, reset & transitional should never be synced
|
||||
selseg.options = (selseg.options & 0x0071U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0x8E); // ignore selected, freeze, reset & transitional
|
||||
if (applyEffects) {
|
||||
selseg.custom1 = udpIn[29+ofs];
|
||||
selseg.custom2 = udpIn[30+ofs];
|
||||
selseg.custom3 = udpIn[31+ofs] & 0x1F;
|
||||
selseg.check1 = (udpIn[31+ofs]>>5) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
|
||||
selseg.check1 = (udpIn[31+ofs]>>7) & 0x1;
|
||||
}
|
||||
startY = (udpIn[32+ofs] << 8 | udpIn[33+ofs]);
|
||||
stopY = (udpIn[34+ofs] << 8 | udpIn[35+ofs]);
|
||||
}
|
||||
if (receiveSegmentBounds) {
|
||||
selseg.setUp(start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
|
||||
} else {
|
||||
selseg.setUp(selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
|
||||
}
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
// simple effect sync, applies to all selected segments
|
||||
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
|
||||
for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.setMode(udpIn[8]);
|
||||
seg.speed = udpIn[9];
|
||||
if (version > 2) seg.intensity = udpIn[16];
|
||||
if (version > 4) seg.setPalette(udpIn[19]);
|
||||
}
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
if (applyEffects && version > 5) {
|
||||
uint32_t t = (udpIn[25] << 24) | (udpIn[26] << 16) | (udpIn[27] << 8) | (udpIn[28]);
|
||||
t += PRESUMED_NETWORK_DELAY; //adjust trivially for network delay
|
||||
t -= millis();
|
||||
strip.timebase = t;
|
||||
timebaseUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
//adjust system time, but only if sender is more accurate than self
|
||||
if (version > 7 && version < 200)
|
||||
{
|
||||
Toki::Time tm;
|
||||
tm.sec = (udpIn[30] << 24) | (udpIn[31] << 16) | (udpIn[32] << 8) | (udpIn[33]);
|
||||
tm.ms = (udpIn[34] << 8) | (udpIn[35]);
|
||||
if (udpIn[29] > toki.getTimeSource()) { //if sender's time source is more accurate
|
||||
toki.adjust(tm, PRESUMED_NETWORK_DELAY); //adjust trivially for network delay
|
||||
uint8_t ts = TOKI_TS_UDP;
|
||||
if (udpIn[29] > 99) ts = TOKI_TS_UDP_NTP;
|
||||
else if (udpIn[29] >= TOKI_TS_SEC) ts = TOKI_TS_UDP_SEC;
|
||||
toki.setTime(tm, ts);
|
||||
} else if (timebaseUpdated && toki.getTimeSource() > 99) { //if we both have good times, get a more accurate timebase
|
||||
Toki::Time myTime = toki.getTime();
|
||||
uint32_t diff = toki.msDifference(tm, myTime);
|
||||
strip.timebase -= PRESUMED_NETWORK_DELAY; //no need to presume, use difference between NTP times at send and receive points
|
||||
if (toki.isLater(tm, myTime)) {
|
||||
strip.timebase += diff;
|
||||
} else {
|
||||
strip.timebase -= diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version > 3)
|
||||
{
|
||||
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
|
||||
}
|
||||
|
||||
nightlightActive = udpIn[6];
|
||||
if (nightlightActive) nightlightDelayMins = udpIn[7];
|
||||
|
||||
if (receiveNotificationBrightness || !someSel) bri = udpIn[2];
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
DEBUG_PRINT(F("UDP notification from: ")); DEBUG_PRINTLN(notifierUdp.remoteIP());
|
||||
parseNotifyPacket(udpIn);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -739,7 +814,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
|
||||
if (sequenceNumber > 15) sequenceNumber = 0;
|
||||
|
||||
if (!ddpUdp.beginPacket(client, DDP_DEFAULT_PORT)) { // port defined in ESPAsyncE131.h
|
||||
DEBUG_PRINTLN(F("WiFiUDP.beginPacket returned an error"));
|
||||
//DEBUG_PRINTLN(F("WiFiUDP.beginPacket returned an error"));
|
||||
return 1; // problem
|
||||
}
|
||||
|
||||
@ -780,7 +855,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
|
||||
}
|
||||
|
||||
if (!ddpUdp.endPacket()) {
|
||||
DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error"));
|
||||
//DEBUG_PRINTLN(F("WiFiUDP.endPacket returned an error"));
|
||||
return 1; // problem
|
||||
}
|
||||
|
||||
@ -849,3 +924,78 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
// ESP-NOW message receive callback function
|
||||
void espNowReceiveCB(uint8_t* address, uint8_t* data, uint8_t len, signed int rssi, bool broadcast) {
|
||||
sprintf_P(last_signal_src, PSTR("%02x%02x%02x%02x%02x%02x"), address[0], address[1], address[2], address[3], address[4], address[5]);
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("ESP-NOW: ")); DEBUG_PRINT(last_signal_src); DEBUG_PRINT(F(" -> ")); DEBUG_PRINTLN(len);
|
||||
for (int i=0; i<len; i++) DEBUG_PRINTF("%02x ", data[i]);
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
|
||||
// handle WiZ Mote data
|
||||
if (data[0] == 0x91 || data[0] == 0x81 || data[0] == 0x80) {
|
||||
handleRemote(data, len);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(linked_remote) == 12 && strcmp(last_signal_src, linked_remote) != 0) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW unpaired remote sender."));
|
||||
return;
|
||||
}
|
||||
|
||||
partial_packet_t *buffer = reinterpret_cast<partial_packet_t *>(data);
|
||||
if (len < 3 || !broadcast || buffer->magic != 'W' || !useESPNowSync || WLED_CONNECTED) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW unexpected packet, not syncing or connected to WiFi."));
|
||||
return;
|
||||
}
|
||||
|
||||
static uint8_t *udpIn = nullptr;
|
||||
static uint8_t packetsReceived = 0; // bitfield (max 5 packets ATM)
|
||||
static uint8_t segsReceived = 0;
|
||||
static unsigned long lastProcessed = 0;
|
||||
|
||||
if (buffer->packet == 0) {
|
||||
if (udpIn == nullptr) udpIn = (uint8_t *)malloc(WLEDPACKETSIZE); // we cannot use stack as we are in callback
|
||||
DEBUG_PRINTLN(F("ESP-NOW inited UDP buffer."));
|
||||
memcpy(udpIn, buffer->data, len-3); // global data (41 bytes)
|
||||
packetsReceived |= 0x01 << buffer->packet;
|
||||
segsReceived = 0;
|
||||
return;
|
||||
} else if (((len-3)/UDP_SEG_SIZE)*UDP_SEG_SIZE != (len-3)) {
|
||||
DEBUG_PRINTF("ESP-NOW incorrect packet size: %d (%d) [%d]\n", (int)buffer->packet, (int)len-3, (int)UDP_SEG_SIZE);
|
||||
if (udpIn) free(udpIn);
|
||||
udpIn = nullptr;
|
||||
packetsReceived = 0;
|
||||
segsReceived = 0;
|
||||
return;
|
||||
}
|
||||
if (!udpIn) return;
|
||||
|
||||
// TODO add verification if segsReceived > MAX_NUM_SEGMENTS or WLEDPACKETSIZE
|
||||
|
||||
memcpy(udpIn+41+segsReceived, buffer->data, len-3);
|
||||
packetsReceived |= 0x01 << buffer->packet;
|
||||
segsReceived += (len-3)/UDP_SEG_SIZE;
|
||||
|
||||
DEBUG_PRINTF("ESP-NOW packet received: %d (%d) [%d]\n", (int)buffer->packet, (int)len-3, (int)segsReceived);
|
||||
|
||||
if (segsReceived == buffer->segs) {
|
||||
// last packet received
|
||||
if (millis() - lastProcessed > 250) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW processing complete message."));
|
||||
parseNotifyPacket(udpIn);
|
||||
lastProcessed = millis();
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("ESP-NOW ignoring complete message."));
|
||||
}
|
||||
free(udpIn);
|
||||
udpIn = nullptr;
|
||||
packetsReceived = 0;
|
||||
segsReceived = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -69,6 +69,15 @@ bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
|
||||
}
|
||||
|
||||
|
||||
bool getBoolVal(JsonVariant elem, bool dflt) {
|
||||
if (elem.is<const char*>() && elem.as<const char*>()[0] == 't') {
|
||||
return !dflt;
|
||||
} else {
|
||||
return elem | dflt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv)
|
||||
{
|
||||
const char *v = strstr(req, key);
|
||||
@ -416,9 +425,9 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
|
||||
// (only 2 used as stored in 1 bit in segment options, consider switching to a single global simulation type)
|
||||
typedef enum UM_SoundSimulations {
|
||||
UMS_BeatSin = 0,
|
||||
UMS_WeWillRockYou
|
||||
//UMS_10_13,
|
||||
//UMS_14_3
|
||||
UMS_WeWillRockYou,
|
||||
UMS_10_13,
|
||||
UMS_14_3
|
||||
} um_soundSimulations_t;
|
||||
|
||||
um_data_t* simulateSound(uint8_t simulationId)
|
||||
@ -503,7 +512,7 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
fftResult[i] = 0;
|
||||
}
|
||||
break;
|
||||
/*case UMS_10_3:
|
||||
case UMS_10_13:
|
||||
for (int i = 0; i<16; i++)
|
||||
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
|
||||
volumeSmth = fftResult[8];
|
||||
@ -512,7 +521,7 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
for (int i = 0; i<16; i++)
|
||||
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
|
||||
volumeSmth = fftResult[8];
|
||||
break;*/
|
||||
break;
|
||||
}
|
||||
|
||||
samplePeak = random8() > 250;
|
||||
@ -573,3 +582,17 @@ void enumerateLedmaps() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
|
||||
*/
|
||||
uint8_t get_random_wheel_index(uint8_t pos) {
|
||||
uint8_t r = 0, x = 0, y = 0, d = 0;
|
||||
while (d < 42) {
|
||||
r = random8();
|
||||
x = abs(pos - r);
|
||||
y = 255 - x;
|
||||
d = MIN(x, y);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
@ -54,9 +54,6 @@ void WLED::loop()
|
||||
handleIR(); // 2nd call to function needed for ESP32 to return valid results -- should be good for ESP8266, too
|
||||
#endif
|
||||
handleConnection();
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
handleRemote();
|
||||
#endif
|
||||
handleSerial();
|
||||
handleImprovWifiScan();
|
||||
handleNotifications();
|
||||
@ -64,11 +61,11 @@ void WLED::loop()
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
handleDMX();
|
||||
#endif
|
||||
userLoop();
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
unsigned long usermodMillis = millis();
|
||||
#endif
|
||||
userLoop();
|
||||
usermods.loop();
|
||||
#ifdef WLED_DEBUG
|
||||
usermodMillis = millis() - usermodMillis;
|
||||
@ -189,7 +186,9 @@ void WLED::loop()
|
||||
|
||||
yield();
|
||||
handleWs();
|
||||
#if defined(STATUSLED)
|
||||
handleStatusLED();
|
||||
#endif
|
||||
|
||||
toki.resetTick();
|
||||
|
||||
@ -259,8 +258,8 @@ void WLED::loop()
|
||||
#endif // WLED_DEBUG
|
||||
}
|
||||
|
||||
void WLED::enableWatchdog() {
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
void WLED::enableWatchdog() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_err_t watchdog = esp_task_wdt_init(WLED_WATCHDOG_TIMEOUT, true);
|
||||
DEBUG_PRINT(F("Watchdog enabled: "));
|
||||
@ -274,19 +273,17 @@ void WLED::enableWatchdog() {
|
||||
#else
|
||||
ESP.wdtEnable(WLED_WATCHDOG_TIMEOUT * 1000);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void WLED::disableWatchdog() {
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
DEBUG_PRINTLN(F("Watchdog: disabled"));
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_task_wdt_delete(NULL);
|
||||
#else
|
||||
ESP.wdtDisable();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void WLED::setup()
|
||||
{
|
||||
@ -470,12 +467,16 @@ void WLED::setup()
|
||||
#ifdef ESP8266
|
||||
wifi_set_sleep_type(NONE_SLEEP_T);
|
||||
#endif
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().disableWatchdog();
|
||||
#endif
|
||||
DEBUG_PRINTLN(F("Start ArduinoOTA"));
|
||||
});
|
||||
ArduinoOTA.onError([](ota_error_t error) {
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
// reenable watchdog on failed update
|
||||
WLED::instance().enableWatchdog();
|
||||
#endif
|
||||
});
|
||||
if (strlen(cmDNS) > 0)
|
||||
ArduinoOTA.setHostname(cmDNS);
|
||||
@ -494,7 +495,9 @@ void WLED::setup()
|
||||
initServer();
|
||||
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
enableWatchdog();
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_DISABLE_BROWNOUT_DET)
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
|
||||
@ -676,6 +679,14 @@ void WLED::initConnection()
|
||||
ws.onEvent(wsEvent);
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (statusESPNow == ESP_NOW_STATE_ON) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW stopping."));
|
||||
quickEspNow.stop();
|
||||
statusESPNow = ESP_NOW_STATE_UNINIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
WiFi.disconnect(true); // close old connections
|
||||
#ifdef ESP8266
|
||||
WiFi.setPhyMode(WIFI_PHY_MODE_11N);
|
||||
@ -692,7 +703,6 @@ void WLED::initConnection()
|
||||
if (!WLED_WIFI_CONFIGURED) {
|
||||
DEBUG_PRINTLN(F("No connection configured."));
|
||||
if (!apActive) initAP(); // instantly go to ap mode
|
||||
return;
|
||||
} else if (!apActive) {
|
||||
if (apBehavior == AP_BEHAVIOR_ALWAYS) {
|
||||
DEBUG_PRINTLN(F("Access point ALWAYS enabled."));
|
||||
@ -705,6 +715,7 @@ void WLED::initConnection()
|
||||
}
|
||||
showWelcomePage = false;
|
||||
|
||||
if (WLED_WIFI_CONFIGURED) {
|
||||
DEBUG_PRINT(F("Connecting to "));
|
||||
DEBUG_PRINT(clientSSID);
|
||||
DEBUG_PRINTLN("...");
|
||||
@ -712,11 +723,6 @@ void WLED::initConnection()
|
||||
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
|
||||
char hostname[25];
|
||||
prepareHostname(hostname);
|
||||
|
||||
#ifdef ESP8266
|
||||
WiFi.hostname(hostname);
|
||||
#endif
|
||||
|
||||
WiFi.begin(clientSSID, clientPass);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if defined(LOLIN_WIFI_FIX) && (defined(ARDUINO_ARCH_ESP32C3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32S3))
|
||||
@ -726,6 +732,26 @@ void WLED::initConnection()
|
||||
WiFi.setHostname(hostname);
|
||||
#else
|
||||
wifi_set_sleep_type((noWifiSleep) ? NONE_SLEEP_T : MODEM_SLEEP_T);
|
||||
WiFi.hostname(hostname);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (enableESPNow) {
|
||||
quickEspNow.onDataRcvd(espNowReceiveCB);
|
||||
bool espNowOK;
|
||||
if (apActive) {
|
||||
DEBUG_PRINTLN(F("ESP-NOW initing in AP mode."));
|
||||
#ifdef ESP32
|
||||
quickEspNow.setWiFiBandwidth(WIFI_IF_AP, WIFI_BW_HT20); // Only needed for ESP32 in case you need coexistence with ESP8266 in the same network
|
||||
#endif //ESP32
|
||||
espNowOK = quickEspNow.begin(apChannel, WIFI_IF_AP); // Same channel must be used for both AP and ESP-NOW
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("ESP-NOW initing in STA mode."));
|
||||
espNowOK = quickEspNow.begin(); // Use no parameters to start ESP-NOW on same channel as WiFi, in STA mode
|
||||
}
|
||||
statusESPNow = espNowOK ? ESP_NOW_STATE_ON : ESP_NOW_STATE_ERROR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -850,8 +876,8 @@ void WLED::handleConnection()
|
||||
if (!Network.isConnected()) {
|
||||
if (interfacesInited) {
|
||||
DEBUG_PRINTLN(F("Disconnected!"));
|
||||
interfacesInited = false;
|
||||
initConnection();
|
||||
interfacesInited = false;
|
||||
}
|
||||
//send improv failed 6 seconds after second init attempt (24 sec. after provisioning)
|
||||
if (improvActive > 2 && now - lastReconnectAttempt > 6000) {
|
||||
@ -895,9 +921,9 @@ void WLED::handleConnection()
|
||||
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
|
||||
// else blink at 2Hz when MQTT is enabled but not connected
|
||||
// else turn the status LED off
|
||||
#if defined(STATUSLED)
|
||||
void WLED::handleStatusLED()
|
||||
{
|
||||
#if defined(STATUSLED)
|
||||
uint32_t c = 0;
|
||||
|
||||
#if STATUSLED>=0
|
||||
@ -937,5 +963,5 @@ void WLED::handleStatusLED()
|
||||
busses.setStatusPixel(0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
@ -3,12 +3,12 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.14.0-b4
|
||||
@version 0.15.0-a0
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2310130
|
||||
#define VERSION 2311150
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -77,6 +77,9 @@
|
||||
}
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
#include <espnow.h>
|
||||
#define WIFI_MODE_STA WIFI_STA
|
||||
#define WIFI_MODE_AP WIFI_AP
|
||||
#include <QuickEspNow.h>
|
||||
#endif
|
||||
#else // ESP32
|
||||
#include <HardwareSerial.h> // ensure we have the correct "Serial" on new MCUs (depends on ARDUINO_USB_MODE and ARDUINO_USB_CDC_ON_BOOT)
|
||||
@ -97,6 +100,7 @@
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
#include <esp_now.h>
|
||||
#include <QuickEspNow.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <Wire.h>
|
||||
@ -256,7 +260,7 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
|
||||
|
||||
// Global Variable definitions
|
||||
WLED_GLOBAL char versionString[] _INIT(TOSTRING(WLED_VERSION));
|
||||
#define WLED_CODENAME "Hoshi"
|
||||
#define WLED_CODENAME "Kōsen"
|
||||
|
||||
// AP and OTA default passwords (for maximum security change them!)
|
||||
WLED_GLOBAL char apPass[65] _INIT(WLED_AP_PASS);
|
||||
@ -351,6 +355,7 @@ WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlig
|
||||
WLED_GLOBAL byte nightlightDelayMins _INIT(60);
|
||||
WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade
|
||||
WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading color transition
|
||||
WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending
|
||||
WLED_GLOBAL uint16_t transitionDelay _INIT(750); // default crossfade duration in ms
|
||||
|
||||
WLED_GLOBAL byte briMultiplier _INIT(100); // % of brightness to set (to limit power, if you set it to 50 and set bri to 255, actual brightness will be 127)
|
||||
@ -361,7 +366,7 @@ WLED_GLOBAL char serverDescription[33] _INIT("WLED"); // Name of module - use d
|
||||
#else
|
||||
WLED_GLOBAL char serverDescription[33] _INIT(SERVERNAME); // use predefined name
|
||||
#endif
|
||||
WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
|
||||
//WLED_GLOBAL bool syncToggleReceive _INIT(false); // UIs which only have a single button for sync should toggle send+receive if this is true, only send otherwise
|
||||
WLED_GLOBAL bool simplifiedUI _INIT(false); // enable simplified UI
|
||||
WLED_GLOBAL byte cacheInvalidate _INIT(0); // used to invalidate browser cache when switching from regular to simplified UI
|
||||
|
||||
@ -402,7 +407,7 @@ WLED_GLOBAL byte alexaNumPresets _INIT(0); // number of p
|
||||
|
||||
WLED_GLOBAL uint16_t realtimeTimeoutMs _INIT(2500); // ms timeout of realtime mode before returning to normal mode
|
||||
WLED_GLOBAL int arlsOffset _INIT(0); // realtime LED offset
|
||||
WLED_GLOBAL bool receiveDirect _INIT(true); // receive UDP realtime
|
||||
WLED_GLOBAL bool receiveDirect _INIT(true); // receive UDP/Hyperion realtime
|
||||
WLED_GLOBAL bool arlsDisableGammaCorrection _INIT(true); // activate if gamma correction is handled by the source
|
||||
WLED_GLOBAL bool arlsForceMaxBri _INIT(false); // enable to force max brightness if source has very dark colors that would be black
|
||||
|
||||
@ -465,9 +470,11 @@ WLED_GLOBAL bool hueApplyColor _INIT(true);
|
||||
WLED_GLOBAL uint16_t serialBaud _INIT(1152); // serial baud rate, multiply by 100
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
WLED_GLOBAL bool enable_espnow_remote _INIT(false);
|
||||
WLED_GLOBAL char linked_remote[13] _INIT("");
|
||||
WLED_GLOBAL char last_signal_src[13] _INIT("");
|
||||
WLED_GLOBAL bool enableESPNow _INIT(false); // global on/off for ESP-NOW
|
||||
WLED_GLOBAL byte statusESPNow _INIT(ESP_NOW_STATE_UNINIT); // state of ESP-NOW stack (0 uninitialised, 1 initialised, 2 error)
|
||||
WLED_GLOBAL bool useESPNowSync _INIT(false); // use ESP-NOW wireless technology for sync
|
||||
WLED_GLOBAL char linked_remote[13] _INIT(""); // MAC of ESP-NOW remote (Wiz Mote)
|
||||
WLED_GLOBAL char last_signal_src[13] _INIT(""); // last seen ESP-NOW sender
|
||||
#endif
|
||||
|
||||
// Time CONFIG
|
||||
@ -563,8 +570,8 @@ WLED_GLOBAL bool disablePullUp _INIT(false);
|
||||
WLED_GLOBAL byte touchThreshold _INIT(TOUCH_THRESHOLD);
|
||||
|
||||
// notifications
|
||||
WLED_GLOBAL bool notifyDirectDefault _INIT(notifyDirect);
|
||||
WLED_GLOBAL bool receiveNotifications _INIT(true);
|
||||
WLED_GLOBAL bool sendNotifications _INIT(false); // master notification switch
|
||||
WLED_GLOBAL bool sendNotificationsRT _INIT(false); // master notification switch (runtime)
|
||||
WLED_GLOBAL unsigned long notificationSentTime _INIT(0);
|
||||
WLED_GLOBAL byte notificationSentCallMode _INIT(CALL_MODE_INIT);
|
||||
WLED_GLOBAL uint8_t notificationCount _INIT(0);
|
||||
@ -857,8 +864,12 @@ public:
|
||||
void initAP(bool resetAP = false);
|
||||
void initConnection();
|
||||
void initInterfaces();
|
||||
#if defined(STATUSLED)
|
||||
void handleStatusLED();
|
||||
#endif
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
void enableWatchdog();
|
||||
void disableWatchdog();
|
||||
#endif
|
||||
};
|
||||
#endif // WLED_H
|
||||
|
@ -83,8 +83,8 @@ void loadSettingsFromEEPROM()
|
||||
nightlightDelayMinsDefault = EEPROM.read(224);
|
||||
nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
nightlightMode = EEPROM.read(225);
|
||||
notifyDirectDefault = EEPROM.read(226);
|
||||
notifyDirect = notifyDirectDefault;
|
||||
notifyDirect = EEPROM.read(226);
|
||||
sendNotificationsRT = notifyDirect;
|
||||
|
||||
apChannel = EEPROM.read(227);
|
||||
if (apChannel > 13 || apChannel < 1) apChannel = 1;
|
||||
@ -163,7 +163,6 @@ void loadSettingsFromEEPROM()
|
||||
receiveNotificationColor = EEPROM.read(391);
|
||||
receiveNotificationEffects = EEPROM.read(392);
|
||||
}
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
|
||||
if (lastEEPROMversion > 4) {
|
||||
#ifndef WLED_DISABLE_HUESYNC
|
||||
@ -278,10 +277,10 @@ void loadSettingsFromEEPROM()
|
||||
if (lastEEPROMversion > 13)
|
||||
{
|
||||
mqttEnabled = EEPROM.read(2299);
|
||||
syncToggleReceive = EEPROM.read(397);
|
||||
//syncToggleReceive = EEPROM.read(397);
|
||||
} else {
|
||||
mqttEnabled = true;
|
||||
syncToggleReceive = false;
|
||||
//syncToggleReceive = false;
|
||||
}
|
||||
|
||||
if (lastEEPROMversion > 14)
|
||||
|
@ -40,7 +40,7 @@ bool isIp(String str) {
|
||||
|
||||
void handleUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||
if (!correctPIN) {
|
||||
if (final) request->send(500, "text/plain", FPSTR(s_unlock_cfg));
|
||||
if (final) request->send(401, "text/plain", FPSTR(s_unlock_cfg));
|
||||
return;
|
||||
}
|
||||
if (!index) {
|
||||
@ -86,7 +86,7 @@ void createEditHandler(bool enable) {
|
||||
#endif
|
||||
} else {
|
||||
editHandler = &server.on("/edit", HTTP_ANY, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_cfg), 254);
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_cfg), 254);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -201,7 +201,7 @@ void initServer()
|
||||
verboseResponse = deserializeState(root);
|
||||
} else {
|
||||
if (!correctPIN && strlen(settingsPIN)>0) {
|
||||
request->send(403, "application/json", F("{\"error\":1}")); // ERR_DENIED
|
||||
request->send(401, "application/json", F("{\"error\":1}")); // ERR_DENIED
|
||||
releaseJSONBufferLock();
|
||||
return;
|
||||
}
|
||||
@ -211,6 +211,8 @@ void initServer()
|
||||
|
||||
if (verboseResponse) {
|
||||
if (!isConfig) {
|
||||
lastInterfaceUpdate = millis(); // prevent WS update until cooldown
|
||||
interfaceUpdateCallMode = CALL_MODE_WS_SEND; // schedule WS update
|
||||
serveJson(request); return; //if JSON contains "v"
|
||||
} else {
|
||||
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
||||
@ -282,7 +284,7 @@ void initServer()
|
||||
//init ota page
|
||||
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
if (otaLock) {
|
||||
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254);
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
||||
} else
|
||||
serveSettings(request); // checks for "upd" in URL and handles PIN
|
||||
});
|
||||
@ -292,7 +294,11 @@ void initServer()
|
||||
serveSettings(request, true); // handle PIN page POST request
|
||||
return;
|
||||
}
|
||||
if (Update.hasError() || otaLock) {
|
||||
if (otaLock) {
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254);
|
||||
return;
|
||||
}
|
||||
if (Update.hasError()) {
|
||||
serveMessage(request, 500, F("Update failed!"), F("Please check your file and retry!"), 254);
|
||||
} else {
|
||||
serveMessage(request, 200, F("Update successful!"), F("Rebooting..."), 131);
|
||||
@ -302,10 +308,13 @@ void initServer()
|
||||
if (!correctPIN || otaLock) return;
|
||||
if(!index){
|
||||
DEBUG_PRINTLN(F("OTA Update Start"));
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().disableWatchdog();
|
||||
#endif
|
||||
usermods.onUpdateBegin(true); // notify usermods that update is about to begin (some may require task de-init)
|
||||
lastEditTime = millis(); // make sure PIN does not lock during update
|
||||
#ifdef ESP8266
|
||||
strip.purgeSegments(true); // free as much memory as you can
|
||||
Update.runAsync(true);
|
||||
#endif
|
||||
Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
||||
@ -317,7 +326,9 @@ void initServer()
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("Update Failed"));
|
||||
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
WLED::instance().enableWatchdog();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -533,7 +544,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
|
||||
}
|
||||
if (subPage > 0 && !correctPIN && strlen(settingsPIN)>0) {
|
||||
strcpy_P(buf, PSTR("alert('PIN incorrect.');"));
|
||||
request->send(403, "application/javascript", buf);
|
||||
request->send(401, "application/javascript", buf);
|
||||
return;
|
||||
}
|
||||
strcat_P(buf,PSTR("function GetV(){var d=document;"));
|
||||
@ -575,7 +586,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
// if OTA locked or too frequent PIN entry requests fail hard
|
||||
if ((subPage == SUBPAGE_WIFI && wifiLock && otaLock) || (post && !correctPIN && millis()-lastEditTime < PIN_RETRY_COOLDOWN))
|
||||
{
|
||||
serveMessage(request, 500, "Access Denied", FPSTR(s_unlock_ota), 254); return;
|
||||
serveMessage(request, 401, "Access Denied", FPSTR(s_unlock_ota), 254); return;
|
||||
}
|
||||
|
||||
if (post) { //settings/set POST request, saving
|
||||
@ -585,7 +596,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
char s2[45] = "";
|
||||
|
||||
switch (subPage) {
|
||||
case SUBPAGE_WIFI : strcpy_P(s, PSTR("WiFi")); strcpy_P(s2, PSTR("Please connect to the new IP (if changed)")); forceReconnect = true; break;
|
||||
case SUBPAGE_WIFI : strcpy_P(s, PSTR("WiFi")); strcpy_P(s2, PSTR("Please connect to the new IP (if changed)")); break;
|
||||
case SUBPAGE_LEDS : strcpy_P(s, PSTR("LED")); break;
|
||||
case SUBPAGE_UI : strcpy_P(s, PSTR("UI")); break;
|
||||
case SUBPAGE_SYNC : strcpy_P(s, PSTR("Sync")); break;
|
||||
@ -605,7 +616,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
if (!s2[0]) strcpy_P(s2, s_redirecting);
|
||||
|
||||
bool redirectAfter9s = (subPage == SUBPAGE_WIFI || ((subPage == SUBPAGE_SEC || subPage == SUBPAGE_UM) && doReboot));
|
||||
serveMessage(request, 200, s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
|
||||
serveMessage(request, (correctPIN ? 200 : 401), s, s2, redirectAfter9s ? 129 : (correctPIN ? 1 : 3));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -633,7 +644,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
|
||||
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
||||
return;
|
||||
}
|
||||
case SUBPAGE_PINREQ : response = request->beginResponse_P(200, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
|
||||
case SUBPAGE_PINREQ : response = request->beginResponse_P(401, "text/html", PAGE_settings_pin, PAGE_settings_pin_length); break;
|
||||
case SUBPAGE_CSS : response = request->beginResponse_P(200, "text/css", PAGE_settingsCss, PAGE_settingsCss_length); break;
|
||||
case SUBPAGE_JS : serveSettingsJS(request); return;
|
||||
case SUBPAGE_WELCOME : response = request->beginResponse_P(200, "text/html", PAGE_welcome, PAGE_welcome_length); break;
|
||||
|
@ -164,39 +164,39 @@ bool sendLiveLedsWs(uint32_t wsClient)
|
||||
const size_t MAX_LIVE_LEDS_WS = 1024U;
|
||||
#endif
|
||||
size_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
|
||||
size_t pos = (strip.isMatrix ? 4 : 2); // start of data
|
||||
size_t pos = 2; // start of data
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (strip.isMatrix) {
|
||||
// ignore anything behid matrix (i.e. extra strip)
|
||||
used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal())
|
||||
n = 1;
|
||||
if (used > MAX_LIVE_LEDS_WS) n = 2;
|
||||
if (used > MAX_LIVE_LEDS_WS*4) n = 4;
|
||||
pos = 4;
|
||||
}
|
||||
#endif
|
||||
size_t bufSize = pos + (used/n)*3;
|
||||
|
||||
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
|
||||
if (!wsBuf) return false; //out of memory
|
||||
uint8_t* buffer = wsBuf->get();
|
||||
if (!buffer) return false; //out of memory
|
||||
wsBuf->lock(); // protect buffer from being cleaned by another WS instance
|
||||
buffer[0] = 'L';
|
||||
buffer[1] = 1; //version
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
size_t skipLines = 0;
|
||||
if (strip.isMatrix) {
|
||||
buffer[1] = 2; //version
|
||||
buffer[2] = Segment::maxWidth;
|
||||
buffer[3] = Segment::maxHeight;
|
||||
if (used > MAX_LIVE_LEDS_WS*4) {
|
||||
buffer[2] = Segment::maxWidth/4;
|
||||
buffer[3] = Segment::maxHeight/4;
|
||||
skipLines = 3;
|
||||
} else if (used > MAX_LIVE_LEDS_WS) {
|
||||
buffer[2] = Segment::maxWidth/2;
|
||||
buffer[3] = Segment::maxHeight/2;
|
||||
skipLines = 1;
|
||||
}
|
||||
buffer[2] = Segment::maxWidth/n;
|
||||
buffer[3] = Segment::maxHeight/n;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = 0; pos < bufSize -2; i += n)
|
||||
{
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (strip.isMatrix && skipLines) {
|
||||
if ((i/Segment::maxWidth)%(skipLines+1)) i += Segment::maxWidth * skipLines;
|
||||
}
|
||||
if (strip.isMatrix && n>1 && (i/Segment::maxWidth)%n) i += Segment::maxWidth * (n-1);
|
||||
#endif
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
uint8_t r = R(c);
|
||||
@ -209,6 +209,8 @@ bool sendLiveLedsWs(uint32_t wsClient)
|
||||
}
|
||||
|
||||
wsc->binary(wsBuf);
|
||||
wsBuf->unlock(); // un-protect buffer
|
||||
ws._cleanBuffers();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ void XML_response(AsyncWebServerRequest *request, char* dest)
|
||||
oappend(SET_F("<ns>"));
|
||||
oappendi(notifyDirect);
|
||||
oappend(SET_F("</ns><nr>"));
|
||||
oappendi(receiveNotifications);
|
||||
oappendi(receiveGroups!=0);
|
||||
oappend(SET_F("</nr><nl>"));
|
||||
oappendi(nightlightActive);
|
||||
oappend(SET_F("</nl><nf>"));
|
||||
@ -280,11 +280,11 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("WS"),noWifiSleep);
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
sappend('c',SET_F("RE"),enable_espnow_remote);
|
||||
sappend('c',SET_F("RE"),enableESPNow);
|
||||
sappends('s',SET_F("RMAC"),linked_remote);
|
||||
#else
|
||||
//hide remote settings if not compiled
|
||||
oappend(SET_F("document.getElementById('remd').style.display='none';"));
|
||||
oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
@ -321,14 +321,11 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (last_signal_src[0] != 0) //Have seen an ESP-NOW Remote
|
||||
{
|
||||
if (strlen(last_signal_src) > 0) { //Have seen an ESP-NOW Remote
|
||||
sappends('m',SET_F("(\"rlid\")[0]"),last_signal_src);
|
||||
} else if (!enable_espnow_remote)
|
||||
{
|
||||
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("(Enable remote to listen)"));
|
||||
} else
|
||||
{
|
||||
} else if (!enableESPNow) {
|
||||
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("(Enable ESP-NOW to listen)"));
|
||||
} else {
|
||||
sappends('m',SET_F("(\"rlid\")[0]"),(char*)F("None"));
|
||||
}
|
||||
#endif
|
||||
@ -357,6 +354,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("AW"),Bus::getGlobalAWMode());
|
||||
sappend('c',SET_F("LD"),useGlobalLedBuffer);
|
||||
|
||||
uint16_t sumMa = 0;
|
||||
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
|
||||
Bus* bus = busses.getBus(s);
|
||||
if (bus == nullptr) continue;
|
||||
@ -371,6 +369,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
|
||||
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //swap channels
|
||||
char sp[4] = "SP"; sp[2] = 48+s; sp[3] = 0; //bus clock speed
|
||||
char la[4] = "LA"; la[2] = 48+s; la[3] = 0; //LED current
|
||||
char ma[4] = "MA"; ma[2] = 48+s; ma[3] = 0; //max per-port PSU current
|
||||
oappend(SET_F("addLEDs(1);"));
|
||||
uint8_t pins[5];
|
||||
uint8_t nPins = bus->getPins(pins);
|
||||
@ -408,8 +408,13 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
}
|
||||
}
|
||||
sappend('v',sp,speed);
|
||||
sappend('v',la,bus->getLEDCurrent());
|
||||
sappend('v',ma,bus->getMaxCurrent());
|
||||
sumMa += bus->getMaxCurrent();
|
||||
}
|
||||
sappend('c',SET_F("PPL"),(sumMa>0 && abs(sumMa - strip.ablMilliampsMax)>2)); // approxiamte detection if per-output limiter is enabled
|
||||
sappend('v',SET_F("MA"),strip.ablMilliampsMax);
|
||||
/*
|
||||
sappend('v',SET_F("LA"),strip.milliampsPerLed);
|
||||
if (strip.currentMilliamps)
|
||||
{
|
||||
@ -418,7 +423,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappendi(strip.currentMilliamps);
|
||||
oappend(SET_F("mA\";"));
|
||||
}
|
||||
|
||||
*/
|
||||
oappend(SET_F("resetCOM("));
|
||||
oappend(itoa(WLED_MAX_COLOR_ORDER_MAPPINGS,nS,10));
|
||||
oappend(SET_F(");"));
|
||||
@ -441,6 +446,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("GC"),gammaCorrectCol);
|
||||
dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS);
|
||||
sappend('c',SET_F("TF"),fadeTransition);
|
||||
sappend('c',SET_F("EB"),modeBlending);
|
||||
sappend('v',SET_F("TD"),transitionDelayDefault);
|
||||
sappend('c',SET_F("PF"),strip.paletteFade);
|
||||
sappend('v',SET_F("TP"),randomPaletteChangeTime);
|
||||
@ -468,7 +474,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
if (subPage == SUBPAGE_UI)
|
||||
{
|
||||
sappends('s',SET_F("DS"),serverDescription);
|
||||
sappend('c',SET_F("ST"),syncToggleReceive);
|
||||
//sappend('c',SET_F("ST"),syncToggleReceive);
|
||||
#ifdef WLED_ENABLE_SIMPLE_UI
|
||||
sappend('c',SET_F("SU"),simplifiedUI);
|
||||
#else
|
||||
@ -478,9 +484,15 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
|
||||
if (subPage == SUBPAGE_SYNC)
|
||||
{
|
||||
char nS[32];
|
||||
[[maybe_unused]] char nS[32];
|
||||
sappend('v',SET_F("UP"),udpPort);
|
||||
sappend('v',SET_F("U2"),udpPort2);
|
||||
#ifndef WLED_DISABLE_ESPNOW
|
||||
if (enableESPNow) sappend('c',SET_F("EN"),useESPNowSync);
|
||||
else oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
|
||||
#else
|
||||
oappend(SET_F("toggle('ESPNOW');")); // hide ESP-NOW setting
|
||||
#endif
|
||||
sappend('v',SET_F("GS"),syncGroups);
|
||||
sappend('v',SET_F("GR"),receiveGroups);
|
||||
|
||||
@ -489,10 +501,11 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("RX"),receiveNotificationEffects);
|
||||
sappend('c',SET_F("SO"),receiveSegmentOptions);
|
||||
sappend('c',SET_F("SG"),receiveSegmentBounds);
|
||||
sappend('c',SET_F("SD"),notifyDirectDefault);
|
||||
sappend('c',SET_F("SS"),sendNotifications);
|
||||
sappend('c',SET_F("SD"),notifyDirect);
|
||||
sappend('c',SET_F("SB"),notifyButton);
|
||||
sappend('c',SET_F("SH"),notifyHue);
|
||||
sappend('c',SET_F("SM"),notifyMacro);
|
||||
// sappend('c',SET_F("SM"),notifyMacro);
|
||||
sappend('v',SET_F("UR"),udpNumRetries);
|
||||
|
||||
sappend('c',SET_F("NL"),nodeListEnabled);
|
||||
|
Loading…
Reference in New Issue
Block a user