Merge branch '0_15' into temporary-AP

This commit is contained in:
Blaz Kristan 2023-12-16 13:07:40 +01:00
commit 2944b2a8f6
31 changed files with 2987 additions and 2850 deletions

View File

@ -297,8 +297,8 @@ class Animated_Staircase : public Usermod {
offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1; offIndex = maxSegmentId = strip.getLastActiveSegmentId() + 1;
// shorten the strip transition time to be equal or shorter than segment delay // shorten the strip transition time to be equal or shorter than segment delay
transitionDelayTemp = transitionDelay = segment_delay_ms; transitionDelay = segment_delay_ms;
strip.setTransition(segment_delay_ms/100); strip.setTransition(segment_delay_ms);
strip.trigger(); strip.trigger();
} else { } else {
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off

View File

@ -70,6 +70,7 @@ private:
// Home Assistant // Home Assistant
bool HomeAssistantDiscovery = false; // is HA discovery turned on bool HomeAssistantDiscovery = false; // is HA discovery turned on
int16_t idx = -1; // Domoticz virtual switch idx
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -83,6 +84,7 @@ private:
static const char _haDiscovery[]; static const char _haDiscovery[];
static const char _notify[]; static const char _notify[];
static const char _override[]; static const char _override[];
static const char _domoticzIDX[];
/** /**
* check if it is daytime * check if it is daytime
@ -196,6 +198,7 @@ const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
const char PIRsensorSwitch::_haDiscovery[] PROGMEM = "HA-discovery"; const char PIRsensorSwitch::_haDiscovery[] PROGMEM = "HA-discovery";
const char PIRsensorSwitch::_notify[] PROGMEM = "notifications"; const char PIRsensorSwitch::_notify[] PROGMEM = "notifications";
const char PIRsensorSwitch::_override[] PROGMEM = "override"; const char PIRsensorSwitch::_override[] PROGMEM = "override";
const char PIRsensorSwitch::_domoticzIDX[] PROGMEM = "domoticz-idx";
bool PIRsensorSwitch::isDayTime() { bool PIRsensorSwitch::isDayTime() {
updateLocalTime(); updateLocalTime();
@ -271,9 +274,20 @@ void PIRsensorSwitch::publishMqtt(const char* state)
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
char buf[64]; char buf[128];
sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40 sprintf_P(buf, PSTR("%s/motion"), mqttDeviceTopic); //max length: 33 + 7 = 40
mqtt->publish(buf, 0, false, state); mqtt->publish(buf, 0, false, state);
// Domoticz formatted message
if (idx > 0) {
StaticJsonDocument <128> msg;
msg[F("idx")] = idx;
msg[F("RSSI")] = WiFi.RSSI();
msg[F("command")] = F("switchlight");
strcpy(buf, state); buf[0] = toupper(buf[0]);
msg[F("switchcmd")] = (const char *)buf;
serializeJson(msg, buf, 127);
mqtt->publish("domoticz/in", 0, false, buf);
}
} }
#endif #endif
} }
@ -482,6 +496,7 @@ void PIRsensorSwitch::addToConfig(JsonObject &root)
top[FPSTR(_offOnly)] = m_offOnly; top[FPSTR(_offOnly)] = m_offOnly;
top[FPSTR(_override)] = m_override; top[FPSTR(_override)] = m_override;
top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery; top[FPSTR(_haDiscovery)] = HomeAssistantDiscovery;
top[FPSTR(_domoticzIDX)] = idx;
top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY); top[FPSTR(_notify)] = (NotifyUpdateMode != CALL_MODE_NO_NOTIFY);
DEBUG_PRINTLN(F("PIR config saved.")); DEBUG_PRINTLN(F("PIR config saved."));
} }
@ -521,6 +536,7 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
m_override = top[FPSTR(_override)] | m_override; m_override = top[FPSTR(_override)] | m_override;
HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery; HomeAssistantDiscovery = top[FPSTR(_haDiscovery)] | HomeAssistantDiscovery;
idx = top[FPSTR(_domoticzIDX)] | idx;
NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY; NotifyUpdateMode = top[FPSTR(_notify)] ? CALL_MODE_DIRECT_CHANGE : CALL_MODE_NO_NOTIFY;
@ -549,5 +565,5 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_override)].isNull(); return !top[FPSTR(_domoticzIDX)].isNull();
} }

View File

@ -48,6 +48,7 @@ class UsermodTemperature : public Usermod {
bool enabled = true; bool enabled = true;
bool HApublished = false; bool HApublished = false;
int16_t idx = -1; // Domoticz virtual sensor idx
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
@ -55,6 +56,7 @@ class UsermodTemperature : public Usermod {
static const char _readInterval[]; static const char _readInterval[];
static const char _parasite[]; static const char _parasite[];
static const char _parasitePin[]; static const char _parasitePin[];
static const char _domoticzIDX[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
float readDallas(); float readDallas();
@ -264,7 +266,7 @@ void UsermodTemperature::loop() {
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
char subuf[64]; char subuf[128];
strcpy(subuf, mqttDeviceTopic); strcpy(subuf, mqttDeviceTopic);
if (temperature > -100.0f) { if (temperature > -100.0f) {
// dont publish super low temperature as the graph will get messed up // dont publish super low temperature as the graph will get messed up
@ -274,6 +276,16 @@ void UsermodTemperature::loop() {
mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str());
strcat_P(subuf, PSTR("_f")); strcat_P(subuf, PSTR("_f"));
mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str());
if (idx > 0) {
StaticJsonDocument <128> msg;
msg[F("idx")] = idx;
msg[F("RSSI")] = WiFi.RSSI();
msg[F("nvalue")] = 0;
msg[F("svalue")] = String(getTemperatureC());
serializeJson(msg, subuf, 127);
DEBUG_PRINTLN(subuf);
mqtt->publish("domoticz/in", 0, false, subuf);
}
} else { } else {
// publish something else to indicate status? // publish something else to indicate status?
} }
@ -360,6 +372,7 @@ void UsermodTemperature::addToConfig(JsonObject &root) {
top[FPSTR(_readInterval)] = readingInterval / 1000; top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite; top[FPSTR(_parasite)] = parasite;
top[FPSTR(_parasitePin)] = parasitePin; top[FPSTR(_parasitePin)] = parasitePin;
top[FPSTR(_domoticzIDX)] = idx;
DEBUG_PRINTLN(F("Temperature config saved.")); DEBUG_PRINTLN(F("Temperature config saved."));
} }
@ -386,6 +399,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) {
readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms
parasite = top[FPSTR(_parasite)] | parasite; parasite = top[FPSTR(_parasite)] | parasite;
parasitePin = top[FPSTR(_parasitePin)] | parasitePin; parasitePin = top[FPSTR(_parasitePin)] | parasitePin;
idx = top[FPSTR(_domoticzIDX)] | idx;
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
@ -406,7 +420,7 @@ bool UsermodTemperature::readFromConfig(JsonObject &root) {
} }
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_parasitePin)].isNull(); return !top[FPSTR(_domoticzIDX)].isNull();
} }
void UsermodTemperature::appendConfigData() { void UsermodTemperature::appendConfigData() {
@ -430,3 +444,4 @@ const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";
const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin"; const char UsermodTemperature::_parasitePin[] PROGMEM = "parasite-pwr-pin";
const char UsermodTemperature::_domoticzIDX[] PROGMEM = "domoticz-idx";

View File

@ -803,13 +803,6 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external; _relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
_relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button; _relay[i].button = top[parName][FPSTR(_button)] | _relay[i].button;
// begin backwards compatibility (beta) remove when 0.13 is released
parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].invert = top[parName+FPSTR(_activeHigh)] | _relay[i].invert;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min _relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
} }

View File

@ -20,7 +20,7 @@
| `pinSourceSelect` | GPIO that is connected to SD's `SS`(source select) / `CS`(chip select) | 16 | | `pinSourceSelect` | GPIO that is connected to SD's `SS`(source select) / `CS`(chip select) | 16 |
| `pinSourceClock` | GPIO that is connected to SD's `SCLK` (source clock) / `CLK`(clock) | 14 | | `pinSourceClock` | GPIO that is connected to SD's `SCLK` (source clock) / `CLK`(clock) | 14 |
| `pinPoci` | GPIO that is connected to SD's `POCI`<sup></sup> (Peripheral-Out-Ctrl-In) / `MISO` (deprecated) | 36 | | `pinPoci` | GPIO that is connected to SD's `POCI`<sup></sup> (Peripheral-Out-Ctrl-In) / `MISO` (deprecated) | 36 |
| `pinPico` | GPIO that is connected to SD's `PICO`<sup></sup> (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 14 | | `pinPico` | GPIO that is connected to SD's `PICO`<sup></sup> (Peripheral-In-Ctrl-Out) / `MOSI` (deprecated) | 15 |
| `sdEnable` | Enable to read data from the SD-card | true | | `sdEnable` | Enable to read data from the SD-card | true |
<sup></sup><sub>Following new naming convention of [OSHWA](https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/)</sub> <sup></sup><sub>Following new naming convention of [OSHWA](https://www.oshwa.org/a-resolution-to-redefine-spi-signal-names/)</sub>

View File

@ -91,7 +91,8 @@ class StairwayWipeUsermod : public Usermod {
void startWipe() void startWipe()
{ {
bri = briLast; //turn on bri = briLast; //turn on
transitionDelayTemp = 0; //no transition jsonTransitionOnce = true;
strip.setTransition(0); //no transition
effectCurrent = FX_MODE_COLOR_WIPE; effectCurrent = FX_MODE_COLOR_WIPE;
resetTimebase(); //make sure wipe starts from beginning resetTimebase(); //make sure wipe starts from beginning
@ -105,10 +106,11 @@ class StairwayWipeUsermod : public Usermod {
void turnOff() void turnOff()
{ {
jsonTransitionOnce = true;
#ifdef STAIRCASE_WIPE_OFF #ifdef STAIRCASE_WIPE_OFF
transitionDelayTemp = 0; //turn off immediately after wipe completed strip.setTransition(0); //turn off immediately after wipe completed
#else #else
transitionDelayTemp = 4000; //fade out slowly strip.setTransition(4000); //fade out slowly
#endif #endif
bri = 0; bri = 0;
stateUpdated(CALL_MODE_NOTIFICATION); stateUpdated(CALL_MODE_NOTIFICATION);

View File

@ -80,7 +80,7 @@ int8_t tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) {
*/ */
uint16_t mode_static(void) { uint16_t mode_static(void) {
SEGMENT.fill(SEGCOLOR(0)); SEGMENT.fill(SEGCOLOR(0));
return 350; return strip.isOffRefreshRequired() ? FRAMETIME : 350;
} }
static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid"; static const char _data_FX_MODE_STATIC[] PROGMEM = "Solid";
@ -1714,7 +1714,7 @@ uint16_t mode_multi_comet(void) {
} }
comets[i]++; comets[i]++;
} else { } else {
if(!random(SEGLEN)) { if(!random16(SEGLEN)) {
comets[i] = 0; comets[i] = 0;
} }
} }
@ -1990,7 +1990,7 @@ uint16_t mode_fire_2012() {
// Step 1. Cool down every cell a little // Step 1. Cool down every cell a little
for (int i = 0; i < SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random(4); uint8_t cool = (it != SEGENV.step) ? random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2) : random8(4);
uint8_t minTemp = (i<ignition) ? (ignition-i)/4 + 16 : 0; // should not become black in ignition area uint8_t minTemp = (i<ignition) ? (ignition-i)/4 + 16 : 0; // should not become black in ignition area
uint8_t temp = qsub8(heat[i], cool); uint8_t temp = qsub8(heat[i], cool);
heat[i] = temp<minTemp ? minTemp : temp; heat[i] = temp<minTemp ? minTemp : temp;
@ -4075,6 +4075,7 @@ static const char _data_FX_MODE_PHASEDNOISE[] PROGMEM = "Phased Noise@!,!;!,!;!"
uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline. uint16_t mode_twinkleup(void) { // A very short twinkle routine with fade-in and dual controls. By Andrew Tuline.
uint16_t prevSeed = random16_get_seed(); // save seed so we can restore it at the end of the function
random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through. random16_set_seed(535); // The randomizer needs to be re-set each time through the loop in order for the same 'random' numbers to be the same each time through.
for (int i = 0; i < SEGLEN; i++) { for (int i = 0; i < SEGLEN; i++) {
@ -4084,6 +4085,7 @@ uint16_t mode_twinkleup(void) { // A very short twinkle routine
SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri)); SEGMENT.setPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(random8()+strip.now/100, false, PALETTE_SOLID_WRAP, 0), pixBri));
} }
random16_set_seed(prevSeed); // restore original seed so other effects can use "random" PRNG
return FRAMETIME; return FRAMETIME;
} }
static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0"; static const char _data_FX_MODE_TWINKLEUP[] PROGMEM = "Twinkleup@!,Intensity;!,!;!;;m12=0";
@ -4566,15 +4568,15 @@ class AuroraWave {
public: public:
void init(uint32_t segment_length, CRGB color) { void init(uint32_t segment_length, CRGB color) {
ttl = random(500, 1501); ttl = random16(500, 1501);
basecolor = color; basecolor = color;
basealpha = random(60, 101) / (float)100; basealpha = random8(60, 101) / (float)100;
age = 0; age = 0;
width = random(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier width = random16(segment_length / 20, segment_length / W_WIDTH_FACTOR); //half of width to make math easier
if (!width) width = 1; if (!width) width = 1;
center = random(101) / (float)100 * segment_length; center = random8(101) / (float)100 * segment_length;
goingleft = random(0, 2) == 0; goingleft = random8(0, 2) == 0;
speed_factor = (random(10, 31) / (float)100 * W_MAX_SPEED / 255); speed_factor = (random8(10, 31) / (float)100 * W_MAX_SPEED / 255);
alive = true; alive = true;
} }
@ -4659,7 +4661,7 @@ uint16_t mode_aurora(void) {
waves = reinterpret_cast<AuroraWave*>(SEGENV.data); waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
for (int i = 0; i < SEGENV.aux1; i++) { for (int i = 0; i < SEGENV.aux1; i++) {
waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random8(0, 3))));
} }
} else { } else {
waves = reinterpret_cast<AuroraWave*>(SEGENV.data); waves = reinterpret_cast<AuroraWave*>(SEGENV.data);
@ -4671,7 +4673,7 @@ uint16_t mode_aurora(void) {
if(!(waves[i].stillAlive())) { if(!(waves[i].stillAlive())) {
//If a wave dies, reinitialize it starts over. //If a wave dies, reinitialize it starts over.
waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random(0, 3)))); waves[i].init(SEGLEN, CRGB(SEGMENT.color_from_palette(random8(), false, false, random8(0, 3))));
} }
} }
@ -5025,7 +5027,7 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) { if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
SEGENV.step = strip.now; SEGENV.step = strip.now;
SEGENV.aux0 = 0; SEGENV.aux0 = 0;
random16_set_seed(millis()>>2); //seed the random generator //random16_set_seed(millis()>>2); //seed the random generator
//give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen) //give the leds random state and colors (based on intensity, colors from palette or all posible colors are chosen)
for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) for (int y = 0; y < rows; y++) {
@ -5274,7 +5276,7 @@ uint16_t mode_2DLissajous(void) { // By: Andrew Tuline
return FRAMETIME; return FRAMETIME;
} // mode_2DLissajous() } // mode_2DLissajous()
static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;;c3=15"; static const char _data_FX_MODE_2DLISSAJOUS[] PROGMEM = "Lissajous@X frequency,Fade rate,,,Speed;!;!;2;c3=15";
/////////////////////// ///////////////////////
@ -5286,7 +5288,7 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = SEGMENT.virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = SEGMENT.virtualHeight();
uint16_t dataSize = (SEGLEN+7) >> 3; //1 bit per LED for trails uint16_t dataSize = (SEGMENT.length()+7) >> 3; //1 bit per LED for trails
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
if (SEGENV.call == 0) { if (SEGENV.call == 0) {
@ -5755,7 +5757,7 @@ uint16_t mode_2Dcrazybees(void) {
uint8_t posX, posY, aimX, aimY, hue; uint8_t posX, posY, aimX, aimY, hue;
int8_t deltaX, deltaY, signX, signY, error; int8_t deltaX, deltaY, signX, signY, error;
void aimed(uint16_t w, uint16_t h) { void aimed(uint16_t w, uint16_t h) {
random16_set_seed(millis()); //random16_set_seed(millis());
aimX = random8(0, w); aimX = random8(0, w);
aimY = random8(0, h); aimY = random8(0, h);
hue = random8(); hue = random8();
@ -5842,7 +5844,7 @@ uint16_t mode_2Dghostrider(void) {
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) { if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; SEGENV.aux0 = cols;
SEGENV.aux1 = rows; SEGENV.aux1 = rows;
random16_set_seed(strip.now); //random16_set_seed(strip.now);
lighter->angleSpeed = random8(0,20) - 10; lighter->angleSpeed = random8(0,20) - 10;
lighter->gAngle = random16(); lighter->gAngle = random16();
lighter->Vspeed = 5; lighter->Vspeed = 5;
@ -5883,7 +5885,7 @@ uint16_t mode_2Dghostrider(void) {
if (lighter->reg[i]) { if (lighter->reg[i]) {
lighter->lightersPosY[i] = lighter->gPosY; lighter->lightersPosY[i] = lighter->gPosY;
lighter->lightersPosX[i] = lighter->gPosX; lighter->lightersPosX[i] = lighter->gPosX;
lighter->Angle[i] = lighter->gAngle + random(-10, 10); lighter->Angle[i] = lighter->gAngle + ((int)random8(20) - 10);
lighter->time[i] = 0; lighter->time[i] = 0;
lighter->reg[i] = false; lighter->reg[i] = false;
} else { } else {
@ -6744,7 +6746,7 @@ uint16_t mode_puddlepeak(void) { // Puddlepeak. By Andrew Tuline.
uint16_t size = 0; uint16_t size = 0;
uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254); uint8_t fadeVal = map(SEGMENT.speed,0,255, 224, 254);
uint16_t pos = random(SEGLEN); // Set a random starting position. uint16_t pos = random16(SEGLEN); // Set a random starting position.
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {

View File

@ -493,9 +493,9 @@ typedef struct Segment {
~Segment() { ~Segment() {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
//Serial.printf("-- Destroying segment: %p\n", this); //Serial.printf("-- Destroying segment: %p", this);
//if (name) Serial.printf(" %s (%p)", name, name); //if (name) Serial.printf(" %s (%p)", name, name);
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data); //if (data) Serial.printf(" %d->(%p)", (int)_dataLen, data);
//Serial.println(); //Serial.println();
#endif #endif
if (name) { delete[] name; name = nullptr; } if (name) { delete[] name; name = nullptr; }
@ -779,6 +779,7 @@ class WS2812FX { // 96 bytes
inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); } inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); }
bool bool
paletteFade,
checkSegmentAlignment(void), checkSegmentAlignment(void),
hasRGBWBus(void), hasRGBWBus(void),
hasCCTBus(void), hasCCTBus(void),
@ -791,7 +792,6 @@ class WS2812FX { // 96 bytes
inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;} inline bool isOffRefreshRequired(void) {return _isOffRefreshRequired;}
uint8_t uint8_t
paletteFade,
paletteBlend, paletteBlend,
milliampsPerLed, milliampsPerLed,
cctBlending, cctBlending,

View File

@ -144,10 +144,13 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
} }
bool IRAM_ATTR Segment::allocateData(size_t len) { bool IRAM_ATTR Segment::allocateData(size_t len) {
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
if (call == 0) memset(data, 0, len); // erase buffer if called during effect initialisation
return true;
}
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this); //DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
deallocateData(); deallocateData();
if (len == 0) return(false); // nothing to do if (len == 0) return false; // nothing to do
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) { if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
// not enough memory // not enough memory
DEBUG_PRINT(F("!!! Effect RAM depleted: ")); DEBUG_PRINT(F("!!! Effect RAM depleted: "));
@ -290,7 +293,7 @@ void Segment::startTransition(uint16_t dur) {
_t = new Transition(dur); // no previous transition running _t = new Transition(dur); // no previous transition running
if (!_t) return; // failed to allocate data if (!_t) return; // failed to allocate data
//DEBUG_PRINTF("-- Started transition: %p\n", this); //DEBUG_PRINTF("-- Started transition: %p (%p)\n", this, _t);
loadPalette(_t->_palT, palette); loadPalette(_t->_palT, palette);
_t->_briT = on ? opacity : 0; _t->_briT = on ? opacity : 0;
_t->_cctT = cct; _t->_cctT = cct;
@ -317,8 +320,8 @@ void Segment::startTransition(uint16_t dur) {
} }
void Segment::stopTransition() { void Segment::stopTransition() {
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
if (isInTransition()) { if (isInTransition()) {
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) { if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
//DEBUG_PRINTF("-- Released duplicate data (%d) for %p: %p\n", _t->_segT._dataLenT, this, _t->_segT._dataT); //DEBUG_PRINTF("-- Released duplicate data (%d) for %p: %p\n", _t->_segT._dataLenT, this, _t->_segT._dataT);
@ -348,7 +351,7 @@ uint16_t IRAM_ATTR Segment::progress() {
#ifndef WLED_DISABLE_MODE_BLEND #ifndef WLED_DISABLE_MODE_BLEND
void Segment::swapSegenv(tmpsegd_t &tmpSeg) { void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg); //DEBUG_PRINTF("-- Saving temp seg: %p->(%p) [%d->%p]\n", this, &tmpSeg, _dataLen, data);
tmpSeg._optionsT = options; tmpSeg._optionsT = options;
for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i]; for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i];
tmpSeg._speedT = speed; tmpSeg._speedT = speed;
@ -384,11 +387,10 @@ void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
data = _t->_segT._dataT; data = _t->_segT._dataT;
_dataLen = _t->_segT._dataLenT; _dataLen = _t->_segT._dataLenT;
} }
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
} }
void Segment::restoreSegenv(tmpsegd_t &tmpSeg) { void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
//DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg); //DEBUG_PRINTF("-- Restoring temp seg: %p->(%p) [%d->%p]\n", &tmpSeg, this, _dataLen, data);
if (_t && &(_t->_segT) != &tmpSeg) { if (_t && &(_t->_segT) != &tmpSeg) {
// update possibly changed variables to keep old effect running correctly // update possibly changed variables to keep old effect running correctly
_t->_segT._aux0T = aux0; _t->_segT._aux0T = aux0;
@ -415,7 +417,6 @@ void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
call = tmpSeg._callT; call = tmpSeg._callT;
data = tmpSeg._dataT; data = tmpSeg._dataT;
_dataLen = tmpSeg._dataLenT; _dataLen = tmpSeg._dataLenT;
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
} }
#endif #endif
@ -578,7 +579,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o1"); check1 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false; sOpt = extractModeDefaults(fx, "o2"); check2 = (sOpt >= 0) ? (bool)sOpt : false;
sOpt = extractModeDefaults(fx, "o3"); check3 = (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, "m12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7); else map1D2D = M12_Pixels; // reset mapping if not defined (2D FX may not work)
sOpt = extractModeDefaults(fx, "si"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 3); 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, "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, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
@ -1024,15 +1025,16 @@ void Segment::blur(uint8_t blur_amount) {
*/ */
uint32_t Segment::color_wheel(uint8_t pos) { uint32_t Segment::color_wheel(uint8_t pos) {
if (palette) return color_from_palette(pos, false, true, 0); if (palette) return color_from_palette(pos, false, true, 0);
uint8_t w = W(currentColor(0));
pos = 255 - pos; pos = 255 - pos;
if (pos < 85) { if (pos < 85) {
return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); return RGBW32((255 - pos * 3), 0, (pos * 3), w);
} else if(pos < 170) { } else if(pos < 170) {
pos -= 85; pos -= 85;
return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); return RGBW32(0, (pos * 3), (255 - pos * 3), w);
} else { } else {
pos -= 170; pos -= 170;
return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); return RGBW32((pos * 3), (255 - pos * 3), 0, w);
} }
} }
@ -1046,13 +1048,10 @@ uint32_t Segment::color_wheel(uint8_t pos) {
* @returns Single color from palette * @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) {
uint32_t color = gamma32(currentColor(mcol));
// default palette or no RGB support on segment // default palette or no RGB support on segment
if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) { if ((palette == 0 && mcol < NUM_COLORS) || !_isRGB) return (pbri == 255) ? color : color_fade(color, pbri, true);
uint32_t color = currentColor(mcol);
color = gamma32(color);
if (pbri == 255) return color;
return color_fade(color, pbri, true);
}
uint8_t paletteIndex = i; uint8_t paletteIndex = i;
if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1); if (mapping && virtualLength() > 1) paletteIndex = (i*255)/(virtualLength() -1);
@ -1061,7 +1060,7 @@ uint32_t Segment::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_
curPal = currentPalette(curPal, palette); curPal = currentPalette(curPal, palette);
CRGB fastled_col = ColorFromPalette(curPal, paletteIndex, pbri, (strip.paletteBlend == 3)? NOBLEND:LINEARBLEND); // NOTE: paletteBlend should be global 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); return RGBW32(fastled_col.r, fastled_col.g, fastled_col.b, W(color));
} }
@ -1159,15 +1158,12 @@ void WS2812FX::service() {
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
if (!seg.freeze) { //only run effect function if not frozen if (!seg.freeze) { //only run effect function if not frozen
_virtualSegmentLength = seg.virtualLength(); _virtualSegmentLength = seg.virtualLength(); //SEGLEN
_colors_t[0] = seg.currentColor(0); _colors_t[0] = gamma32(seg.currentColor(0));
_colors_t[1] = seg.currentColor(1); _colors_t[1] = gamma32(seg.currentColor(1));
_colors_t[2] = seg.currentColor(2); _colors_t[2] = gamma32(seg.currentColor(2));
seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference seg.currentPalette(_currentPalette, seg.palette); // we need to pass reference
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(true), correctWB); 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 // Effect blending
// When two effects are being blended, each may have different segment data, this // When two effects are being blended, each may have different segment data, this
// data needs to be saved first and then restored before running previous mode. // data needs to be saved first and then restored before running previous mode.
@ -1181,6 +1177,7 @@ void WS2812FX::service() {
Segment::tmpsegd_t _tmpSegData; Segment::tmpsegd_t _tmpSegData;
Segment::modeBlend(true); // set semaphore Segment::modeBlend(true); // set semaphore
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state) seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
_virtualSegmentLength = seg.virtualLength(); // update SEGLEN (mapping may have changed)
uint16_t d2 = (*_mode[tmpMode])(); // run old mode uint16_t d2 = (*_mode[tmpMode])(); // run old mode
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state) seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
delay = MIN(delay,d2); // use shortest delay delay = MIN(delay,d2); // use shortest delay

View File

@ -77,7 +77,7 @@ uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaul
uint32_t Bus::autoWhiteCalc(uint32_t c) { uint32_t Bus::autoWhiteCalc(uint32_t c) {
uint8_t aWM = _autoWhiteMode; uint8_t aWM = _autoWhiteMode;
if (_gAWM < 255) aWM = _gAWM; if (_gAWM < AW_GLOBAL_DISABLED) aWM = _gAWM;
if (aWM == RGBW_MODE_MANUAL_ONLY) return c; if (aWM == RGBW_MODE_MANUAL_ONLY) return c;
uint8_t w = W(c); uint8_t w = W(c);
//ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0) //ignore auto-white calculation if w>0 and mode DUAL (DUAL behaves as BRIGHTER if w==0)

View File

@ -118,7 +118,7 @@ class Bus {
, _needsRefresh(refresh) , _needsRefresh(refresh)
, _data(nullptr) // keep data access consistent across all types of buses , _data(nullptr) // keep data access consistent across all types of buses
{ {
_autoWhiteMode = Bus::hasWhite(_type) ? aw : RGBW_MODE_MANUAL_ONLY; _autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
}; };
virtual ~Bus() {} //throw the bus under the bus virtual ~Bus() {} //throw the bus under the bus
@ -154,7 +154,7 @@ class Bus {
} }
virtual bool hasWhite(void) { return Bus::hasWhite(_type); } virtual bool hasWhite(void) { return Bus::hasWhite(_type); }
static bool hasWhite(uint8_t type) { static bool hasWhite(uint8_t type) {
if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true; // digital types with white channel if ((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_SK6812_RGBW || type == TYPE_TM1814 || type == TYPE_UCS8904) return true; // digital types with white channel
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true; // analog types with white channel
if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel if (type == TYPE_NET_DDP_RGBW) return true; // network types with white channel
return false; return false;

View File

@ -97,8 +97,9 @@ bool isButtonPressed(uint8_t i)
if (digitalRead(pin) == HIGH) return true; if (digitalRead(pin) == HIGH) return true;
break; break;
case BTN_TYPE_TOUCH: case BTN_TYPE_TOUCH:
case BTN_TYPE_TOUCH_SWITCH:
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3) #if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if (touchRead(pin) <= touchThreshold) return true; if (digitalPinToTouchChannel(btnPin[i]) >= 0 && touchRead(pin) <= touchThreshold) return true;
#endif #endif
break; break;
} }
@ -109,6 +110,7 @@ void handleSwitch(uint8_t b)
{ {
// isButtonPressed() handles inverted/noninverted logic // isButtonPressed() handles inverted/noninverted logic
if (buttonPressedBefore[b] != isButtonPressed(b)) { if (buttonPressedBefore[b] != isButtonPressed(b)) {
DEBUG_PRINT(F("Switch: State changed ")); DEBUG_PRINTLN(b);
buttonPressedTime[b] = millis(); buttonPressedTime[b] = millis();
buttonPressedBefore[b] = !buttonPressedBefore[b]; buttonPressedBefore[b] = !buttonPressedBefore[b];
} }
@ -116,12 +118,15 @@ void handleSwitch(uint8_t b)
if (buttonLongPressed[b] == buttonPressedBefore[b]) return; if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce) if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
DEBUG_PRINT(F("Switch: Activating ")); DEBUG_PRINTLN(b);
if (!buttonPressedBefore[b]) { // on -> off if (!buttonPressedBefore[b]) { // on -> off
DEBUG_PRINT(F("Switch: On -> Off ")); DEBUG_PRINTLN(b);
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET); if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
else { //turn on else { //turn on
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
} }
} else { // off -> on } else { // off -> on
DEBUG_PRINT(F("Switch: Off -> On ")); DEBUG_PRINTLN(b);
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
else { //turn off else { //turn off
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);} if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
@ -153,6 +158,8 @@ void handleAnalog(uint8_t b)
static float filteredReading[WLED_MAX_BUTTONS] = {0.0f}; static float filteredReading[WLED_MAX_BUTTONS] = {0.0f};
uint16_t rawReading; // raw value from analogRead, scaled to 12bit uint16_t rawReading; // raw value from analogRead, scaled to 12bit
DEBUG_PRINT(F("Analog: Reading button ")); DEBUG_PRINTLN(b);
#ifdef ESP8266 #ifdef ESP8266
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
#else #else
@ -161,6 +168,8 @@ void handleAnalog(uint8_t b)
#endif #endif
yield(); // keep WiFi task running - analog read may take several millis on ESP8266 yield(); // keep WiFi task running - analog read may take several millis on ESP8266
DEBUG_PRINT(F("Analog: Raw read = ")); DEBUG_PRINTLN(rawReading);
filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255] filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255]
uint16_t aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit uint16_t aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit
if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used
@ -171,6 +180,8 @@ void handleAnalog(uint8_t b)
// remove noise & reduce frequency of UI updates // remove noise & reduce frequency of UI updates
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
DEBUG_PRINT(F("Analog: Filtered read = ")); DEBUG_PRINTLN(aRead);
// Unomment the next lines if you still see flickering related to potentiometer // Unomment the next lines if you still see flickering related to potentiometer
// This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?) // This waits until strip finishes updating (why: strip was not updating at the start of handleButton() but may have started during analogRead()?)
//unsigned long wait_started = millis(); //unsigned long wait_started = millis();
@ -183,6 +194,7 @@ void handleAnalog(uint8_t b)
// if no macro for "short press" and "long press" is defined use brightness control // if no macro for "short press" and "long press" is defined use brightness control
if (!macroButton[b] && !macroLongPress[b]) { if (!macroButton[b] && !macroLongPress[b]) {
DEBUG_PRINT(F("Analog: Action = ")); DEBUG_PRINTLN(macroDoublePress[b]);
// if "double press" macro defines which option to change // if "double press" macro defines which option to change
if (macroDoublePress[b] >= 250) { if (macroDoublePress[b] >= 250) {
// global brightness // global brightness
@ -218,6 +230,7 @@ void handleAnalog(uint8_t b)
updateInterfaces(CALL_MODE_BUTTON); updateInterfaces(CALL_MODE_BUTTON);
} }
} else { } else {
DEBUG_PRINTLN(F("Analog: No action"));
//TODO: //TODO:
// we can either trigger a preset depending on the level (between short and long entries) // we can either trigger a preset depending on the level (between short and long entries)
// or use it for RGBW direct control // or use it for RGBW direct control
@ -252,7 +265,7 @@ void handleButton()
} }
// button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0) // button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) { if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_TOUCH_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) {
handleSwitch(b); handleSwitch(b);
continue; continue;
} }

View File

@ -88,11 +88,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
// initialize LED pins and lengths prior to other HW (except for ethernet) // initialize LED pins and lengths prior to other HW (except for ethernet)
JsonObject hw_led = hw["led"]; JsonObject hw_led = hw["led"];
uint8_t autoWhiteMode = RGBW_MODE_MANUAL_ONLY;
uint16_t total = hw_led[F("total")] | strip.getLengthTotal(); uint16_t total = hw_led[F("total")] | strip.getLengthTotal();
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]); CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used CJSON(strip.milliampsPerLed, hw_led[F("ledma")]); // no longer used
Bus::setGlobalAWMode(hw_led[F("rgbwm")] | 255); Bus::setGlobalAWMode(hw_led[F("rgbwm")] | AW_GLOBAL_DISABLED);
CJSON(correctWB, hw_led["cct"]); CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]); CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(strip.cctBlending, hw_led[F("cb")]); CJSON(strip.cctBlending, hw_led[F("cb")]);
@ -158,7 +157,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
} }
uint16_t length = elm["len"] | 1; uint16_t length = elm["len"] | 1;
uint8_t colorOrder = (int)elm[F("order")]; uint8_t colorOrder = (int)elm[F("order")]; // contains white channel swap option in upper nibble
uint8_t skipFirst = elm[F("skip")]; uint8_t skipFirst = elm[F("skip")];
uint16_t start = elm["start"] | 0; uint16_t start = elm["start"] | 0;
if (length==0 || start + length > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop if (length==0 || start + length > MAX_LEDS) continue; // zero length or we reached max. number of LEDs, just stop
@ -166,8 +165,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
bool reversed = elm["rev"]; bool reversed = elm["rev"];
bool refresh = elm["ref"] | false; bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully) 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")] | RGBW_MODE_MANUAL_ONLY;
uint8_t AWmode = elm[F("rgbwm")] | autoWhiteMode;
uint8_t maPerLed = elm[F("ledma")] | strip.milliampsPerLed; // replace with 55 when removing strip.milliampsPerLed 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 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) // To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
@ -175,6 +173,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
maPerLed = 0; maPerLed = 0;
maMax = 0; maMax = 0;
} }
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
if (fromFS) { if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
mem += BusManager::memUsage(bc); mem += BusManager::memUsage(bc);
@ -377,6 +376,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(modeBlending, light_tr["fx"]); CJSON(modeBlending, light_tr["fx"]);
int tdd = light_tr["dur"] | -1; int tdd = light_tr["dur"] | -1;
if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100; if (tdd >= 0) transitionDelay = transitionDelayDefault = tdd * 100;
strip.setTransition(fadeTransition ? transitionDelayDefault : 0);
CJSON(strip.paletteFade, light_tr["pal"]); CJSON(strip.paletteFade, light_tr["pal"]);
CJSON(randomPaletteChangeTime, light_tr[F("rpc")]); CJSON(randomPaletteChangeTime, light_tr[F("rpc")]);

View File

@ -286,6 +286,7 @@
#define BTN_TYPE_TOUCH 6 #define BTN_TYPE_TOUCH 6
#define BTN_TYPE_ANALOG 7 #define BTN_TYPE_ANALOG 7
#define BTN_TYPE_ANALOG_INVERTED 8 #define BTN_TYPE_ANALOG_INVERTED 8
#define BTN_TYPE_TOUCH_SWITCH 9
//Ethernet board types //Ethernet board types
#define WLED_NUM_ETH_TYPES 11 #define WLED_NUM_ETH_TYPES 11
@ -342,8 +343,9 @@
// WLED Error modes // WLED Error modes
#define ERR_NONE 0 // All good :) #define ERR_NONE 0 // All good :)
#define ERR_DENIED 1 // Permission denied #define ERR_DENIED 1 // Permission denied
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?) OBSOLETE #define ERR_CONCURRENCY 2 // Conurrency (client active)
#define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time #define ERR_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time
#define ERR_NOT_IMPL 4 // Not implemented
#define ERR_JSON 9 // JSON parsing failed (input too large?) #define ERR_JSON 9 // JSON parsing failed (input too large?)
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?) #define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached #define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached

View File

@ -469,6 +469,7 @@ button {
height: 54px; height: 54px;
line-height: 1.5; line-height: 1.5;
padding-bottom: 8px; padding-bottom: 8px;
pointer-events: none;
} }
/* New tooltip */ /* New tooltip */
@ -1240,6 +1241,7 @@ TD .checkmark, TD .radiomark {
.filter .fchkl { .filter .fchkl {
margin: 0 4px; margin: 0 4px;
min-width: 20px; min-width: 20px;
pointer-events: auto;
} }
.lbl-l { .lbl-l {

View File

@ -197,33 +197,33 @@
<div id="Effects" class="tabcontent"> <div id="Effects" class="tabcontent">
<div id="fx"> <div id="fx">
<p class="labels hd" id="modeLabel">Effect mode</p> <p class="labels hd" id="modeLabel">Effect mode</p>
<div class="staytop fnd" id="fxFind"> <div class="staytop fnd" id="fxFind" onmousedown="preventBlur(event);">
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="filterFocus(event);search(this,'fxlist');" onblur="filterFocus(event);" /> <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);">&#xe38f;</i> <i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon" style="cursor:pointer;">&#xe0a1;</i> <i class="icons search-icon" style="cursor:pointer;">&#xe0a1;</i>
<div id="filters" class="filter fade"> <div id="filters" class="filter fade">
<label id="filterPal" tooltip="Uses palette" class="check fchkl">&#x1F3A8; <label id="filterPal" tooltip="Uses palette" class="check fchkl">&#x1F3A8;
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<label id="filter0D" tooltip="Single pixel" class="check fchkl">&#8226; <label id="filter0D" tooltip="Single pixel" class="check fchkl">&#8226;
<input type="checkbox" data-flt="&#8226;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#8226;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<label id="filter1D" tooltip="1D" class="check fchkl">&#8942; <label id="filter1D" tooltip="1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#8942;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<label id="filter2D" tooltip="2D" class="check fchkl">&#9638; <label id="filter2D" tooltip="2D" class="check fchkl">&#9638;
<input type="checkbox" data-flt="&#9638;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#9638;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<label id="filterVol" tooltip="Volume" class="check fchkl">&#9834; <label id="filterVol" tooltip="Volume" class="check fchkl">&#9834;
<input type="checkbox" data-flt="&#9834;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#9834;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
<label id="filterFreq" tooltip="Frequency" class="check fchkl">&#9835; <label id="filterFreq" tooltip="Frequency" class="check fchkl">&#9835;
<input type="checkbox" data-flt="&#9835;" onchange="filterFx(this);"> <input type="checkbox" data-flt="&#9835;" onchange="filterFx();">
<span class="checkmark"></span> <span class="checkmark"></span>
</label> </label>
</div> </div>

View File

@ -232,6 +232,7 @@ function onLoad()
tooltip(); tooltip();
resetPUtil(); resetPUtil();
initFilters();
if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true); if (localStorage.getItem('pcm') == "true" || (!/Mobi/.test(navigator.userAgent) && localStorage.getItem('pcm') == null)) togglePcMode(true);
applyCfg(); applyCfg();
@ -1505,6 +1506,8 @@ function setEffectParameters(idx)
let text = slider.getAttribute("tooltip"); let text = slider.getAttribute("tooltip");
if ((!controlDefined && i<((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i]!="")) { if ((!controlDefined && i<((idx<128)?2:nSliders)) || (slOnOff.length>i && slOnOff[i]!="")) {
if (slOnOff.length>i && slOnOff[i]!="!") text = slOnOff[i]; if (slOnOff.length>i && slOnOff[i]!="!") text = slOnOff[i];
// restore overwritten default tooltips
if (i<2 && slOnOff[i]==="!") text = i==0 ? "Effect speed" : "Effect intensity";
slider.setAttribute("tooltip", text); slider.setAttribute("tooltip", text);
slider.parentElement.classList.remove('hide'); slider.parentElement.classList.remove('hide');
} else } else
@ -1539,7 +1542,6 @@ function setEffectParameters(idx)
var cslLabel = ''; var cslLabel = '';
var sep = ''; var sep = '';
var cslCnt = 0, oCsel = csel; var cslCnt = 0, oCsel = csel;
// for (let i=0; i<gId("csl").querySelectorAll("button"); i++) {
d.querySelectorAll("#csl button").forEach((e,i)=>{ d.querySelectorAll("#csl button").forEach((e,i)=>{
var btn = gId("csl" + i); var btn = gId("csl" + i);
// if no controlDefined or coOnOff has a value // if no controlDefined or coOnOff has a value
@ -2716,58 +2718,94 @@ function hideModes(txt)
} }
} }
*/ */
function search(f,l=null) function search(field, listId = null) {
{ field.nextElementSibling.style.display = (field.value !== '') ? 'block' : 'none';
f.nextElementSibling.style.display=(f.value!=='')?'block':'none'; if (!listId) return;
if (!l) return;
var el = gId(l).querySelectorAll('.lstI');
// filter list items but leave (Default & Solid) always visible
for (i = (l==='pcont'?0:1); i < el.length; i++) {
var it = el[i];
var itT = it.querySelector('.lstIname').innerText.toUpperCase();
it.style.display = (itT.indexOf(f.value.toUpperCase())<0) ? 'none' : '';
}
}
function clean(c) // clear filter if searching in fxlist
{ if (listId === 'fxlist' && field.value !== '') {
c.style.display='none';
var i=c.previousElementSibling;
i.value='';
i.focus();
i.dispatchEvent(new Event('input'));
if (i.parentElement.id=='fxFind') {
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { e.checked = false; }); gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { e.checked = false; });
} }
// do not search if filter is active
if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return;
const listItems = gId(listId).querySelectorAll('.lstI');
// filter list items but leave (Default & Solid) always visible
for (i = (listId === 'pcont' ? 0 : 1); i < listItems.length; i++) {
const listItem = listItems[i];
const listItemName = listItem.querySelector('.lstIname').innerText.toUpperCase();
const searchIndex = listItemName.indexOf(field.value.toUpperCase());
listItem.style.display = (searchIndex < 0) ? 'none' : '';
listItem.dataset.searchIndex = searchIndex;
} }
function filterFocus(e) // sort list items by search index and name
{ const sortedListItems = Array.from(listItems).sort((a, b) => {
let f = gId("filters"); const aSearchIndex = parseInt(a.dataset.searchIndex);
const bSearchIndex = parseInt(b.dataset.searchIndex);
if (aSearchIndex !== bSearchIndex) {
return aSearchIndex - bSearchIndex;
}
const aName = a.querySelector('.lstIname').innerText.toUpperCase();
const bName = b.querySelector('.lstIname').innerText.toUpperCase();
return aName.localeCompare(bName);
});
sortedListItems.forEach(item => {
gId(listId).append(item);
});
}
function clean(clearButton) {
clearButton.style.display = 'none';
const inputField = clearButton.previousElementSibling;
inputField.value = '';
search(inputField, clearButton.parentElement.nextElementSibling.id);
}
function initFilters() {
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { e.checked = false; });
}
function filterFocus(e) {
const f = gId("filters");
if (e.type === "focus") f.classList.remove('fade'); // immediately show (still has transition) if (e.type === "focus") f.classList.remove('fade'); // immediately show (still has transition)
// compute sticky top (with delay for transition) // compute sticky top (with delay for transition)
setTimeout(() => { setTimeout(() => {
let sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti')) + (e.type === "focus" ? 1 : -1) * f.offsetHeight; const sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti')) + (e.type === "focus" ? 1 : -1) * f.offsetHeight;
sCol('--sti', sti + "px"); sCol('--sti', sti + "px");
}, 252); }, 252);
if (e.type === "blur") { if (e.type === "blur") {
let t = e.relatedTarget ? e.relatedTarget : e.explicitOriginalTarget; setTimeout(() => {
do { if (e.target === document.activeElement && document.hasFocus()) return;
if (t.id && (t.id === "fxFind")) { setTimeout(()=>{t.firstElementChild.focus();},150); return; } // do not hide if filter is active
t = t.parentElement; if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return;
} while (t.tagName !== "BODY"); f.classList.add('fade');
setTimeout(()=>{f.classList.add('fade');},255); // wait with hiding }, 255); // wait with hiding
} }
} }
function filterFx(o) function filterFx() {
{ const inputField = gId('fxFind').children[0];
if (!o) return; inputField.value = '';
let i = gId('fxFind').children[0]; inputField.focus();
i.value=!o.checked?'':o.dataset.flt; clean(inputField.nextElementSibling);
i.focus(); const listItems = gId("fxlist").querySelectorAll('.lstI');
i.dispatchEvent(new Event('input')); for (let i = 1; i < listItems.length; i++) {
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e)=>{if(e!==o)e.checked=false;}); const listItem = listItems[i];
const listItemName = listItem.querySelector('.lstIname').innerText;
let hide = false;
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = true; });
listItem.style.display = hide ? 'none' : '';
}
}
function preventBlur(e) {
if (e.target === gId("fxFind").children[0] || e.target === gId("filters")) return;
e.preventDefault();
} }
// make sure "dur" and "transition" are arrays with at least the length of "ps" // make sure "dur" and "transition" are arrays with at least the length of "ps"

View File

@ -52,51 +52,55 @@
maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l; maxB = b; maxV = v; maxM = m; maxPB = p; maxL = l;
} }
function pinsOK() { function pinsOK() {
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields var ok = true;
for (i=0; i<LCs.length; i++) { var nList = d.Sf.querySelectorAll("#mLC input[name^=L]");
var nm = LCs[i].name.substring(0,2); nList.forEach((LC,i)=>{
if (!ok) return; // prevent iteration after conflict
let nm = LC.name.substring(0,2);
let n = LC.name.substring(2);
let t = parseInt(d.Sf["LT"+n].value, 10); // LED type SELECT
// ignore IP address // ignore IP address
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") { if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
var n = LCs[i].name.substring(2); if (t>=80) return;
var t = parseInt(d.getElementsByName("LT"+n)[0].value, 10); // LED type SELECT
if (t>=80) continue;
} }
//check for pin conflicts //check for pin conflicts
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4"/* || nm=="RL" || nm=="BT" || nm=="IR"*/) if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4")
if (LCs[i].value!="" && LCs[i].value!="-1") { if (LC.value!="" && LC.value!="-1") {
var p = d.rsvd.concat(d.um_p); // used pin array let p = d.rsvd.concat(d.um_p); // used pin array
d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
if (p.some((e)=>e==parseInt(LCs[i].value))) { if (p.some((e)=>e==parseInt(LC.value))) {
alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`); alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);
LCs[i].value=""; LC.value="";
LCs[i].focus(); LC.focus();
return false; ok = false;
} return;
else if (/*!(nm == "IR" || nm=="BT") &&*/ d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))) { } else if (d.ro_gpio.some((e)=>e==parseInt(LC.value))) {
alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`); alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);
LCs[i].value=""; LC.value="";
LCs[i].focus(); LC.focus();
return false; ok = false;
return;
} }
for (j=i+1; j<LCs.length; j++) { for (j=i+1; j<nList.length; j++) {
var n2 = LCs[j].name.substring(0,2); let n2 = nList[j].name.substring(0,2);
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) { if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4") {
if (n2.substring(0,1)==="L") { if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2); var m = nList[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 (t2>=80) continue;
} }
if (LCs[j].value!="" && LCs[i].value==LCs[j].value) { if (nList[j].value!="" && nList[i].value==nList[j].value) {
alert(`Pin conflict between ${LCs[i].name}/${LCs[j].name}!`); alert(`Pin conflict between ${LC.name}/${nList[j].name}!`);
LCs[j].value=""; nList[j].value="";
LCs[j].focus(); nList[j].focus();
return false; ok = false;
return;
} }
} }
} }
} }
} });
return true; return ok;
} }
function trySubmit(e) { function trySubmit(e) {
d.Sf.data.value = ''; d.Sf.data.value = '';
@ -206,7 +210,7 @@
// enumerate pins // enumerate pins
for (p=1; p<5; p++) { for (p=1; p<5; p++) {
var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins var LK = d.Sf["L"+p+n]; // secondary pins
if (!LK) continue; if (!LK) continue;
if (((t>=80 && t<96) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h if (((t>=80 && t<96) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h
{ {
@ -222,7 +226,7 @@
} }
if (change) { if (change) {
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state 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 if (t > 31 && t < 48) d.Sf["LC"+n].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["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; d.Sf["MA"+n].min = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? 0 : 250;
} }
@ -230,7 +234,7 @@
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 gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown
if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping if (!(t > 28 && t < 32)) d.Sf["WO"+n].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
@ -249,21 +253,21 @@
d.Sf.AW.selectedIndex = 0; d.Sf.AW.selectedIndex = 0;
d.Sf.CR.checked = false; d.Sf.CR.checked = false;
} }
// check for pin conflicts // update start indexes, max values, calculate current, etc
var LCs = d.Sf.querySelectorAll("#mLC input[name^=L]"); // input fields var nList = d.Sf.querySelectorAll("#mLC input[name^=L]");
for (i=0; i<LCs.length; i++) { nList.forEach((LC,i)=>{
var nm = LCs[i].name.substring(0,2); // field name let nm = LC.name.substring(0,2); // field name
var n = LCs[i].name.substring(2); // bus number let n = LC.name.substring(2); // bus number
let t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
// do we have a led count field // do we have a led count field
if (nm=="LC") { if (nm=="LC") {
var c=parseInt(LCs[i].value,10); //get LED count let c = parseInt(LC.value,10); //get LED count
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
gId("ls"+n).disabled = !customStarts; //enable/disable field editing gId("ls"+n).disabled = !customStarts; //enable/disable field editing
if (c) { if (c) {
var s = parseInt(gId("ls"+n).value); //start value let s = parseInt(gId("ls"+n).value); //start value
if (s+c > sLC) sLC = s+c; //update total count if (s+c > sLC) sLC = s+c; //update total count
if (c > maxLC) maxLC = c; //max per output if (c > maxLC) maxLC = c; //max per output
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) 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))) sDI += c;
if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) { if (!((t >= 80 && t < 96) || (t >= 40 && t < 48))) {
@ -272,52 +276,46 @@
busMA += maPL*c; busMA += maPL*c;
} }
} // increase led count } // increase led count
continue; return;
} }
// do we have led pins for digital leds // do we have led pins for digital leds
if (nm=="L0" || nm=="L1") { if (nm=="L0" || nm=="L1") {
var lc=d.Sf["LC"+n]; d.Sf["LC"+n].max = maxPB; // update max led count value
lc.max=maxPB; // update max led count value
} }
// ignore IP address (stored in pins for virtual busses) // ignore IP address (stored in pins for virtual busses)
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") { if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") {
var t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
if (t>=80) { if (t>=80) {
LCs[i].max = 255; LC.max = 255;
LCs[i].min = 0; LC.min = 0;
LCs[i].style.color="#fff"; LC.style.color="#fff";
continue; // do not check conflicts return; // do not check conflicts
} else { } else {
LCs[i].max = d.max_gpio; LC.max = d.max_gpio;
LCs[i].min = -1; LC.min = -1;
} }
} }
// check for pin conflicts // check for pin conflicts & color fields
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4"/* || nm=="RL" || nm=="BT" || nm=="IR"*/) if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4")
if (LCs[i].value!="" && LCs[i].value!="-1") { if (LC.value!="" && LC.value!="-1") {
var p = d.rsvd.concat(d.um_p); // used pin array let p = d.rsvd.concat(d.um_p); // used pin array
d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay d.Sf.querySelectorAll("select.pin").forEach((e)=>{if(e.value>-1)p.push(parseInt(e.value));}) // buttons, IR & relay
for (j=0; j<LCs.length; j++) { for (j=0; j<nList.length; j++) {
if (i==j) continue; if (i==j) continue;
var n2 = LCs[j].name.substring(0,2); let n2 = nList[j].name.substring(0,2);
if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4"/* || n2=="RL" || n2=="BT" || n2=="IR"*/) { if (n2=="L0" || n2=="L1" || n2=="L2" || n2=="L3" || n2=="L4") {
if (n2.substring(0,1)==="L") { if (n2.substring(0,1)==="L") {
var m = LCs[j].name.substring(2); let m = nList[j].name.substring(2);
var t2 = parseInt(d.Sf["LT"+m].value, 10); let t2 = parseInt(d.Sf["LT"+m].value, 10);
if (t2 >= 80) continue; if (t2 >= 80) continue;
} }
if (LCs[j].value!="" && LCs[j].value!="-1") p.push(parseInt(LCs[j].value,10)); // add current pin if (nList[j].value!="" && nList[j].value!="-1") p.push(parseInt(nList[j].value,10)); // add current pin
} }
} }
// now check for conflicts // now check for conflicts
if (p.some((e)=>e==parseInt(LCs[i].value))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value))?"orange":"#fff"; if (p.some((e)=>e==parseInt(LC.value))) LC.style.color = "red";
} else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff";
// check buttons, IR & relay
//if (nm=="IR" || nm=="BT" || nm=="RL") {
// LCs[i].max = d.max_gpio;
// LCs[i].min = -1;
//}
} }
});
d.Sf.querySelectorAll("#mLC input[name^=LC]").forEach((s,n)=>{ d.Sf.querySelectorAll("#mLC input[name^=LC]").forEach((s,n)=>{
let c = parseInt(s.value,10); //get LED count let c = parseInt(s.value,10); //get LED count
let t = parseInt(d.Sf["LT"+n].value); let t = parseInt(d.Sf["LT"+n].value);
@ -512,6 +510,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
c += `<option value="6" ${t==6?"selected":""}>Touch</option>`; c += `<option value="6" ${t==6?"selected":""}>Touch</option>`;
c += `<option value="7" ${t==7?"selected":""}>Analog</option>`; c += `<option value="7" ${t==7?"selected":""}>Analog</option>`;
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`; c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
c += `<option value="9" ${t==9?"selected":""}>Touch (switch)</option>`;
c += `</select>`; c += `</select>`;
c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#x2715;</span><br>`; c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#x2715;</span><br>`;
gId("btns").innerHTML = c; gId("btns").innerHTML = c;
@ -817,10 +816,12 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Use Gamma value: <input name="GV" type="number" class="m" placeholder="2.8" min="1" max="3" step="0.1" required><br><br> Use Gamma value: <input name="GV" type="number" class="m" placeholder="2.8" min="1" max="3" step="0.1" required><br><br>
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> % Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
<h3>Transitions</h3> <h3>Transitions</h3>
Crossfade: <input type="checkbox" name="TF"><br> Enable transitions: <input type="checkbox" name="TF" onchange="gId('tran').style.display=this.checked?'inline':'none';"><br>
<span id="tran">
Effect blending: <input type="checkbox" name="EB"><br> Effect blending: <input type="checkbox" name="EB"><br>
Transition Time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<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> Palette transitions: <input type="checkbox" name="PF"><br>
</span>
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br> <i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>
<h3>Timed light</h3> <h3>Timed light</h3>
Default Duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br> Default Duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br>

View File

@ -399,10 +399,10 @@ bool isIp(String str);
void createEditHandler(bool enable); void createEditHandler(bool enable);
bool captivePortal(AsyncWebServerRequest *request); bool captivePortal(AsyncWebServerRequest *request);
void initServer(); void initServer();
void serveIndexOrWelcome(AsyncWebServerRequest *request);
void serveIndex(AsyncWebServerRequest* request); void serveIndex(AsyncWebServerRequest* request);
String msgProcessor(const String& var); String msgProcessor(const String& var);
void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl="", byte optionT=255); void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& headl, const String& subl="", byte optionT=255);
void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t error);
String dmxProcessor(const String& var); String dmxProcessor(const String& var);
void serveSettings(AsyncWebServerRequest* request, bool post = false); void serveSettings(AsyncWebServerRequest* request, bool post = false);
void serveSettingsJS(AsyncWebServerRequest* request); void serveSettingsJS(AsyncWebServerRequest* request);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -240,8 +240,9 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.map1D2D = M12_Pixels; // no mapping seg.map1D2D = M12_Pixels; // no mapping
// set brightness immediately and disable transition // set brightness immediately and disable transition
transitionDelayTemp = 0;
jsonTransitionOnce = true; jsonTransitionOnce = true;
seg.stopTransition();
strip.setTransition(0);
strip.setBrightness(scaledBri(bri), true); strip.setBrightness(scaledBri(bri), true);
// freeze and init to black // freeze and init to black
@ -323,23 +324,18 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
int tr = -1; int tr = -1;
if (!presetId || currentPlaylist < 0) { //do not apply transition time from preset if playlist active, as it would override playlist transition times if (!presetId || currentPlaylist < 0) { //do not apply transition time from preset if playlist active, as it would override playlist transition times
tr = root[F("transition")] | -1; tr = root[F("transition")] | -1;
if (tr >= 0) if (tr >= 0) {
{ transitionDelay = tr * 100;
transitionDelay = tr; if (fadeTransition) strip.setTransition(transitionDelay);
transitionDelay *= 100;
transitionDelayTemp = transitionDelay;
} }
} }
// temporary transition (applies only once) // temporary transition (applies only once)
tr = root[F("tt")] | -1; tr = root[F("tt")] | -1;
if (tr >= 0) if (tr >= 0) {
{
transitionDelayTemp = tr;
transitionDelayTemp *= 100;
jsonTransitionOnce = true; jsonTransitionOnce = true;
if (fadeTransition) strip.setTransition(tr * 100);
} }
strip.setTransition(transitionDelayTemp); // required here for color transitions to have correct duration
tr = root[F("tb")] | -1; tr = root[F("tb")] | -1;
if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis(); if (tr >= 0) strip.timebase = ((uint32_t)tr) - millis();
@ -375,8 +371,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (root.containsKey("live")) { if (root.containsKey("live")) {
if (root["live"].as<bool>()) { if (root["live"].as<bool>()) {
transitionDelayTemp = 0;
jsonTransitionOnce = true; jsonTransitionOnce = true;
strip.setTransition(0);
realtimeLock(65000); realtimeLock(65000);
} else { } else {
exitRealtime(); exitRealtime();
@ -962,11 +958,11 @@ void serializeNetworks(JsonObject root)
for (int i = 0; i < status; i++) { for (int i = 0; i < status; i++) {
JsonObject node = networks.createNestedObject(); JsonObject node = networks.createNestedObject();
node["ssid"] = WiFi.SSID(i); node[F("ssid")] = WiFi.SSID(i);
node["rssi"] = WiFi.RSSI(i); node[F("rssi")] = WiFi.RSSI(i);
node["bssid"] = WiFi.BSSIDstr(i); node[F("bssid")] = WiFi.BSSIDstr(i);
node["channel"] = WiFi.channel(i); node[F("channel")] = WiFi.channel(i);
node["enc"] = WiFi.encryptionType(i); node[F("enc")] = WiFi.encryptionType(i);
} }
WiFi.scanDelete(); WiFi.scanDelete();
@ -1029,7 +1025,7 @@ static volatile bool servingClient = false;
void serveJson(AsyncWebServerRequest* request) void serveJson(AsyncWebServerRequest* request)
{ {
if (servingClient) { if (servingClient) {
request->send(503, "application/json", F("{\"error\":2}")); // ERR_CONCURENCY serveJsonError(request, 503, ERR_CONCURRENCY);
return; return;
} }
servingClient = true; servingClient = true;
@ -1061,13 +1057,13 @@ void serveJson(AsyncWebServerRequest* request)
return; return;
} }
else if (url.length() > 6) { //not just /json else if (url.length() > 6) { //not just /json
request->send(501, "application/json", F("{\"error\":\"Not implemented\"}")); serveJsonError(request, 501, ERR_NOT_IMPL);
servingClient = false; servingClient = false;
return; return;
} }
if (!requestJSONBufferLock(17)) { if (!requestJSONBufferLock(17)) {
request->send(503, "application/json", F("{\"error\":3}")); serveJsonError(request, 503, ERR_NOBUF);
servingClient = false; servingClient = false;
return; return;
} }

View File

@ -134,11 +134,9 @@ void stateUpdated(byte callMode) {
usermods.onStateChange(callMode); usermods.onStateChange(callMode);
if (fadeTransition) { if (fadeTransition) {
//set correct delay if not using notification delay if (strip.getTransition() == 0) {
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; // load actual transition duration
jsonTransitionOnce = false; jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp); transitionActive = false;
if (transitionDelayTemp == 0) {
applyFinalBri(); applyFinalBri();
strip.trigger(); strip.trigger();
return; return;
@ -152,7 +150,6 @@ void stateUpdated(byte callMode) {
transitionActive = true; transitionActive = true;
transitionStartTime = millis(); transitionStartTime = millis();
} else { } else {
strip.setTransition(0);
applyFinalBri(); applyFinalBri();
strip.trigger(); strip.trigger();
} }
@ -187,13 +184,14 @@ void handleTransitions()
if (doPublishMqtt) publishMqtt(); if (doPublishMqtt) publishMqtt();
#endif #endif
if (transitionActive && transitionDelayTemp > 0) if (transitionActive && strip.getTransition() > 0) {
{ float tper = (millis() - transitionStartTime)/(float)strip.getTransition();
float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp; if (tper >= 1.0f) {
if (tper >= 1.0f) strip.setTransitionMode(false); // stop all transitions
{ // restore (global) transition time if not called from UDP notifier or single/temporary transition from JSON (also playlist)
strip.setTransitionMode(false); if (jsonTransitionOnce) strip.setTransition(transitionDelay);
transitionActive = false; transitionActive = false;
jsonTransitionOnce = false;
tperLast = 0; tperLast = 0;
applyFinalBri(); applyFinalBri();
return; return;

View File

@ -144,7 +144,7 @@ void handlePlaylist() {
} }
jsonTransitionOnce = true; jsonTransitionOnce = true;
transitionDelayTemp = playlistEntries[playlistIndex].tr * 100; strip.setTransition(fadeTransition ? playlistEntries[playlistIndex].tr * 100 : 0);
playlistEntryDur = playlistEntries[playlistIndex].dur; playlistEntryDur = playlistEntries[playlistIndex].dur;
applyPreset(playlistEntries[playlistIndex].preset); applyPreset(playlistEntries[playlistIndex].preset);
} }

View File

@ -14,6 +14,11 @@
#define WIZMOTE_BUTTON_BRIGHT_UP 9 #define WIZMOTE_BUTTON_BRIGHT_UP 9
#define WIZMOTE_BUTTON_BRIGHT_DOWN 8 #define WIZMOTE_BUTTON_BRIGHT_DOWN 8
#define WIZ_SMART_BUTTON_ON 100
#define WIZ_SMART_BUTTON_OFF 101
#define WIZ_SMART_BUTTON_BRIGHT_UP 102
#define WIZ_SMART_BUTTON_BRIGHT_DOWN 103
// This is kind of an esoteric strucure because it's pulled from the "Wizmote" // 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 // 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 // since it's broadly commercially available and works out of the box as a drop-in
@ -210,6 +215,10 @@ void handleRemote(uint8_t *incomingData, size_t len) {
case WIZMOTE_BUTTON_NIGHT : activateNightMode(); break; case WIZMOTE_BUTTON_NIGHT : activateNightMode(); break;
case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); break; case WIZMOTE_BUTTON_BRIGHT_UP : brightnessUp(); break;
case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); break; case WIZMOTE_BUTTON_BRIGHT_DOWN : brightnessDown(); break;
case WIZ_SMART_BUTTON_ON : setOn(); break;
case WIZ_SMART_BUTTON_OFF : setOff(); break;
case WIZ_SMART_BUTTON_BRIGHT_UP : brightnessUp(); break;
case WIZ_SMART_BUTTON_BRIGHT_DOWN : brightnessDown(); break;
default: break; default: break;
} }
last_seq = cur_seq; last_seq = cur_seq;

View File

@ -136,7 +136,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255; pins[i] = (request->arg(lp).length() > 0) ? request->arg(lp).toInt() : 255;
} }
type = request->arg(lt).toInt(); type = request->arg(lt).toInt();
type |= request->hasArg(rf) << 7; // off refresh override
skip = request->arg(sl).toInt(); skip = request->arg(sl).toInt();
colorOrder = request->arg(co).toInt(); colorOrder = request->arg(co).toInt();
start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t; start = (request->hasArg(ls)) ? request->arg(ls).toInt() : t;
@ -168,7 +167,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} else { } else {
freqHz = 0; freqHz = 0;
} }
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0; channelSwap = Bus::hasWhite(type) ? request->arg(wo).toInt() : 0;
if ((type > TYPE_TM1814 && type < TYPE_WS2801) || type >= TYPE_NET_DDP_RGB) { // analog and virtual if ((type > TYPE_TM1814 && type < TYPE_WS2801) || type >= TYPE_NET_DDP_RGB) { // analog and virtual
maPerLed = 0; maPerLed = 0;
maMax = 0; maMax = 0;
@ -176,6 +175,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
maPerLed = request->arg(la).toInt(); maPerLed = request->arg(la).toInt();
maMax = request->arg(ma).toInt(); // if ABL is disabled this will be 0 maMax = request->arg(ma).toInt(); // if ABL is disabled this will be 0
} }
type |= request->hasArg(rf) << 7; // off refresh override
// actual finalization is done in WLED::loop() (removing old busses and adding new) // 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 // this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
@ -221,15 +221,22 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10) char bt[4] = "BT"; bt[2] = (i<10?48:55)+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10) char be[4] = "BE"; be[2] = (i<10?48:55)+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
int hw_btn_pin = request->arg(bt).toInt(); int hw_btn_pin = request->arg(bt).toInt();
if (pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) { if (hw_btn_pin >= 0 && pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
btnPin[i] = hw_btn_pin; btnPin[i] = hw_btn_pin;
buttonType[i] = request->arg(be).toInt(); buttonType[i] = request->arg(be).toInt();
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that analog button pin is a valid ADC gpio // ESP32 only: check that button pin is a valid gpio
if (((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[i]) < 0)) if (((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[i]) < 0))
{ {
// not an ADC analog pin // not an ADC analog pin
if (btnPin[i] >= 0) DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[i], i); DEBUG_PRINTF("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n", btnPin[i], i);
btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
}
else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH) && digitalPinToTouchChannel(btnPin[i]) < 0)
{
// not a touch pin
DEBUG_PRINTF("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n", btnPin[i], i);
btnPin[i] = -1; btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button); pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
} }
@ -1089,6 +1096,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("TT=")); pos = req.indexOf(F("TT="));
if (pos > 0) transitionDelay = getNumVal(&req, pos); if (pos > 0) transitionDelay = getNumVal(&req, pos);
if (fadeTransition) strip.setTransition(transitionDelay);
//set time (unix timestamp) //set time (unix timestamp)
pos = req.indexOf(F("ST=")); pos = req.indexOf(F("ST="));

View File

@ -139,9 +139,9 @@ void notify(byte callMode, bool followUp)
udpOut[29+ofs] = selseg.custom1; udpOut[29+ofs] = selseg.custom1;
udpOut[30+ofs] = selseg.custom2; udpOut[30+ofs] = selseg.custom2;
udpOut[31+ofs] = selseg.custom3 | (selseg.check1<<5) | (selseg.check2<<6) | (selseg.check3<<7); udpOut[31+ofs] = selseg.custom3 | (selseg.check1<<5) | (selseg.check2<<6) | (selseg.check3<<7);
udpOut[32+ofs] = selseg.startY >> 8; udpOut[32+ofs] = selseg.startY >> 8; // ATM always 0 as Segment::startY is 8-bit
udpOut[33+ofs] = selseg.startY & 0xFF; udpOut[33+ofs] = selseg.startY & 0xFF;
udpOut[34+ofs] = selseg.stopY >> 8; udpOut[34+ofs] = selseg.stopY >> 8; // ATM always 0 as Segment::stopY is 8-bit
udpOut[35+ofs] = selseg.stopY & 0xFF; udpOut[35+ofs] = selseg.stopY & 0xFF;
++s; ++s;
} }
@ -211,6 +211,14 @@ void parseNotifyPacket(uint8_t *udpIn) {
bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects); bool someSel = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
// set transition time before making any segment changes
if (version > 3) {
if (fadeTransition) {
jsonTransitionOnce = true;
strip.setTransition(((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00));
}
}
//apply colors from notification to main segment, only if not syncing full segments //apply colors from notification to main segment, only if not syncing full segments
if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) { if ((receiveNotificationColor || !someSel) && (version < 11 || !receiveSegmentOptions)) {
// primary color, only apply white if intented (version > 0) // primary color, only apply white if intented (version > 0)
@ -264,6 +272,7 @@ void parseNotifyPacket(uint8_t *udpIn) {
if (!receiveSegmentBounds) { if (!receiveSegmentBounds) {
if (!selseg.isActive()) { if (!selseg.isActive()) {
inactiveSegs++; inactiveSegs++;
DEBUG_PRINTLN(F("Inactive segment."));
continue; continue;
} else { } else {
id += inactiveSegs; // adjust id id += inactiveSegs; // adjust id
@ -271,24 +280,29 @@ void parseNotifyPacket(uint8_t *udpIn) {
} }
DEBUG_PRINT(F("UDP segment processing: ")); DEBUG_PRINTLN(id); DEBUG_PRINT(F("UDP segment processing: ")); DEBUG_PRINTLN(id);
uint16_t startY = 0, start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]); uint16_t start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stopY = 1, stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]); uint16_t stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t startY = version > 11 ? (udpIn[32+ofs] << 8 | udpIn[33+ofs]) : 0;
uint16_t stopY = version > 11 ? (udpIn[34+ofs] << 8 | udpIn[35+ofs]) : 1;
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]); uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
if (!receiveSegmentOptions) { if (!receiveSegmentOptions) {
//selseg.setUp(start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); //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 // we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
DEBUG_PRINTF("Set segment w/o options: %d [%d,%d;%d,%d]\n", id, (int)start, (int)stop, (int)startY, (int)stopY);
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY); strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset, startY, stopY);
continue; // we do receive bounds, but not options continue; // we do receive bounds, but not options
} }
selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional
selseg.setOpacity(udpIn[10+ofs]); selseg.setOpacity(udpIn[10+ofs]);
if (applyEffects) { if (applyEffects) {
DEBUG_PRINT(F("Apply effect: ")); DEBUG_PRINTLN(id);
selseg.setMode(udpIn[11+ofs]); selseg.setMode(udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs]; selseg.speed = udpIn[12+ofs];
selseg.intensity = udpIn[13+ofs]; selseg.intensity = udpIn[13+ofs];
selseg.palette = udpIn[14+ofs]; selseg.palette = udpIn[14+ofs];
} }
if (receiveNotificationColor || !someSel) { if (receiveNotificationColor || !someSel) {
DEBUG_PRINT(F("Apply color: ")); DEBUG_PRINTLN(id);
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs])); 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(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.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
@ -298,8 +312,10 @@ void parseNotifyPacket(uint8_t *udpIn) {
// when applying synced options ignore selected as it may be used as indicator of which segments to sync // when applying synced options ignore selected as it may be used as indicator of which segments to sync
// freeze, reset should never be synced // 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) // LSB to MSB: select, reverse, on, mirror, freeze, reset, reverse_y, mirror_y, transpose, map1d2d (3), ssim (2), set (2)
DEBUG_PRINT(F("Apply options: ")); DEBUG_PRINTLN(id);
selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
if (applyEffects) { if (applyEffects) {
DEBUG_PRINT(F("Apply sliders: ")); DEBUG_PRINTLN(id);
selseg.custom1 = udpIn[29+ofs]; selseg.custom1 = udpIn[29+ofs];
selseg.custom2 = udpIn[30+ofs]; selseg.custom2 = udpIn[30+ofs];
selseg.custom3 = udpIn[31+ofs] & 0x1F; selseg.custom3 = udpIn[31+ofs] & 0x1F;
@ -307,14 +323,14 @@ void parseNotifyPacket(uint8_t *udpIn) {
selseg.check1 = (udpIn[31+ofs]>>6) & 0x1; selseg.check1 = (udpIn[31+ofs]>>6) & 0x1;
selseg.check1 = (udpIn[31+ofs]>>7) & 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) { if (receiveSegmentBounds) {
DEBUG_PRINTF("Set segment w/ options: %d [%d,%d;%d,%d]\n", id, (int)start, (int)stop, (int)startY, (int)stopY);
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing // 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); strip.setSegment(id, start, stop, udpIn[5+ofs], udpIn[6+ofs], offset, startY, stopY);
} else { } else {
// we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing // we have to use strip.setSegment() instead of selseg.setUp() to prevent crash if segment would change during drawing
DEBUG_PRINTF("Set segment grouping: %d [%d,%d]\n", id, (int)udpIn[5+ofs], (int)udpIn[6+ofs]);
strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY); strip.setSegment(id, selseg.start, selseg.stop, udpIn[5+ofs], udpIn[6+ofs], selseg.offset, selseg.startY, selseg.stopY);
} }
} }
@ -365,10 +381,6 @@ void parseNotifyPacket(uint8_t *udpIn) {
} }
} }
if (version > 3) {
transitionDelayTemp = ((udpIn[17] << 0) & 0xFF) + ((udpIn[18] << 8) & 0xFF00);
}
nightlightActive = udpIn[6]; nightlightActive = udpIn[6];
if (nightlightActive) nightlightDelayMins = udpIn[7]; if (nightlightActive) nightlightDelayMins = udpIn[7];

View File

@ -373,7 +373,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
} }
// extracts mode parameter defaults from last section of mode data (e.g. "Juggle@!,Trail;!,!,;!;sx=16,ix=240,1d") // extracts mode parameter defaults from last section of mode data (e.g. "Juggle@!,Trail;!,!,;!;012;sx=16,ix=240")
int16_t extractModeDefaults(uint8_t mode, const char *segVar) int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{ {
if (mode < strip.getModeCount()) { if (mode < strip.getModeCount()) {

View File

@ -512,6 +512,16 @@ void WLED::setup()
initServer(); initServer();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
// Seed FastLED random functions with an esp random value, which already works properly at this point.
#if defined(ARDUINO_ARCH_ESP32)
const uint32_t seed32 = esp_random();
#elif defined(ARDUINO_ARCH_ESP8266)
const uint32_t seed32 = RANDOM_REG32;
#else
const uint32_t seed32 = random(std::numeric_limits<long>::max());
#endif
random16_set_seed((uint16_t)((seed32 & 0xFFFF) ^ (seed32 >> 16)));
#if WLED_WATCHDOG_TIMEOUT > 0 #if WLED_WATCHDOG_TIMEOUT > 0
enableWatchdog(); enableWatchdog();
#endif #endif

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2311150 #define VERSION 2312160
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -354,9 +354,6 @@ WLED_GLOBAL byte briS _INIT(128); // default brightness
WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over WLED_GLOBAL byte nightlightTargetBri _INIT(0); // brightness after nightlight is over
WLED_GLOBAL byte nightlightDelayMins _INIT(60); WLED_GLOBAL byte nightlightDelayMins _INIT(60);
WLED_GLOBAL byte nightlightMode _INIT(NL_MODE_FADE); // See const.h for available modes. Was nightlightFade 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) 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)
@ -534,9 +531,11 @@ WLED_GLOBAL bool wasConnected _INIT(false);
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions // transitions
WLED_GLOBAL bool fadeTransition _INIT(true); // enable crossfading brightness/color
WLED_GLOBAL bool modeBlending _INIT(true); // enable effect blending
WLED_GLOBAL bool transitionActive _INIT(false); WLED_GLOBAL bool transitionActive _INIT(false);
WLED_GLOBAL uint16_t transitionDelayDefault _INIT(transitionDelay); // default transition time (storec in cfg.json) WLED_GLOBAL uint16_t transitionDelay _INIT(750); // global transition duration
WLED_GLOBAL uint16_t transitionDelayTemp _INIT(transitionDelay); // actual transition duration (overrides transitionDelay in certain cases) WLED_GLOBAL uint16_t transitionDelayDefault _INIT(750); // default transition time (stored in cfg.json)
WLED_GLOBAL unsigned long transitionStartTime; WLED_GLOBAL unsigned long transitionStartTime;
WLED_GLOBAL float tperLast _INIT(0.0f); // crossfade transition progress, 0.0f - 1.0f WLED_GLOBAL float tperLast _INIT(0.0f); // crossfade transition progress, 0.0f - 1.0f
WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt") WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt")

View File

@ -183,7 +183,7 @@ void initServer()
JsonObject root = doc.as<JsonObject>(); JsonObject root = doc.as<JsonObject>();
if (error || root.isNull()) { if (error || root.isNull()) {
releaseJSONBufferLock(); releaseJSONBufferLock();
request->send(400, "application/json", F("{\"error\":9}")); // ERR_JSON serveJsonError(request, 400, ERR_JSON);
return; return;
} }
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>()); if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>());
@ -201,8 +201,8 @@ void initServer()
verboseResponse = deserializeState(root); verboseResponse = deserializeState(root);
} else { } else {
if (!correctPIN && strlen(settingsPIN)>0) { if (!correctPIN && strlen(settingsPIN)>0) {
request->send(401, "application/json", F("{\"error\":1}")); // ERR_DENIED
releaseJSONBufferLock(); releaseJSONBufferLock();
serveJsonError(request, 401, ERR_DENIED);
return; return;
} }
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
@ -509,6 +509,18 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, const String& h
} }
void serveJsonError(AsyncWebServerRequest* request, uint16_t code, uint16_t error)
{
AsyncJsonResponse *response = new AsyncJsonResponse(64);
if (error < ERR_NOT_IMPL) response->addHeader("Retry-After", "1");
response->setContentType("application/json");
response->setCode(code);
JsonObject obj = response->getRoot();
obj[F("error")] = error;
response->setLength();
request->send(response);
}
#ifdef WLED_ENABLE_DMX #ifdef WLED_ENABLE_DMX
String dmxProcessor(const String& var) String dmxProcessor(const String& var)
{ {