Merge branch 'dev' into gzip

Minor fixes.
Enhanced SR UI handling for palettes.
This commit is contained in:
Blaz Kristan 2021-12-07 18:53:38 +01:00
commit 957d08f4c6
22 changed files with 836 additions and 698 deletions

View File

@ -2,6 +2,18 @@
### Builds after release 0.12.0
#### Build 2112070
- Added new effect "Fairy", replacing "Police All"
- Added new effect "Fairytwinkle", replacing "Two Areas"
- Static single JSON buffer (performance and stability improvement) (PR #2336)
#### Build 2112030
- Fixed ESP32 crash on Colortwinkles brightness change
- Fixed setting picker to black resetting hue and saturation
- Fixed auto white mode not saved to config
#### Build 2111300
- Added CCT and white balance correction support (PR #2285)

View File

@ -1216,12 +1216,13 @@ uint16_t WS2812FX::mode_loading(void) {
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2)
{
uint16_t delay = 1 + (FRAMETIME<<3) / SEGLEN; // longer segments should change faster
uint32_t it = now / map(SEGMENT.speed, 0, 255, delay<<4, delay);
uint16_t offset = it % SEGLEN;
uint16_t width = ((SEGLEN*(SEGMENT.intensity+1))>>9); //max width is half the strip
if (!width) width = 1;
for (uint16_t i = 0; i < width; i++) {
uint16_t indexR = (offset + i) % SEGLEN;
@ -1233,26 +1234,11 @@ uint16_t WS2812FX::police_base(uint32_t color1, uint32_t color2, uint16_t width)
}
//American Police Light with all LEDs Red and Blue
uint16_t WS2812FX::mode_police_all()
{
return police_base(RED, BLUE, (SEGLEN>>1));
}
//Police Lights Red and Blue
uint16_t WS2812FX::mode_police()
{
fill(SEGCOLOR(1));
return police_base(RED, BLUE, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
}
//Police All with custom colors
uint16_t WS2812FX::mode_two_areas()
{
fill(SEGCOLOR(2));
return police_base(SEGCOLOR(0), SEGCOLOR(1), ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
return police_base(RED, BLUE);
}
@ -1262,7 +1248,142 @@ uint16_t WS2812FX::mode_two_dots()
fill(SEGCOLOR(2));
uint32_t color2 = (SEGCOLOR(1) == SEGCOLOR(2)) ? SEGCOLOR(0) : SEGCOLOR(1);
return police_base(SEGCOLOR(0), color2, ((SEGLEN*(SEGMENT.intensity+1))>>9)); // max width is half the strip
return police_base(SEGCOLOR(0), color2);
}
/*
* Fairy, inspired by https://www.youtube.com/watch?v=zeOw5MZWq24
*/
//4 bytes
typedef struct Flasher {
uint16_t stateStart;
uint8_t stateDur;
bool stateOn;
} flasher;
#define FLASHERS_PER_ZONE 6
#define MAX_SHIMMER 92
uint16_t WS2812FX::mode_fairy() {
//set every pixel to a 'random' color from palette (using seed so it doesn't change between frames)
uint16_t PRNG16 = 5100 + _segment_index;
for (uint16_t i = 0; i < SEGLEN; i++) {
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0));
}
//amount of flasher pixels depending on intensity (0: none, 255: every LED)
if (SEGMENT.intensity == 0) return FRAMETIME;
uint8_t flasherDistance = ((255 - SEGMENT.intensity) / 28) +1; //1-10
uint16_t numFlashers = (SEGLEN / flasherDistance) +1;
uint16_t dataSize = sizeof(flasher) * numFlashers;
if (!SEGENV.allocateData(dataSize)) return FRAMETIME; //allocation failed
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
uint16_t now16 = now & 0xFFFF;
//Up to 11 flashers in one brightness zone, afterwards a new zone for every 6 flashers
uint16_t zones = numFlashers/FLASHERS_PER_ZONE;
if (!zones) zones = 1;
uint8_t flashersInZone = numFlashers/zones;
uint8_t flasherBri[FLASHERS_PER_ZONE*2 -1];
for (uint16_t z = 0; z < zones; z++) {
uint16_t flasherBriSum = 0;
uint16_t firstFlasher = z*flashersInZone;
if (z == zones-1) flashersInZone = numFlashers-(flashersInZone*(zones-1));
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
uint16_t stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 10) {
flashers[f].stateOn = !flashers[f].stateOn;
if (flashers[f].stateOn) {
flashers[f].stateDur = 12 + random8(12 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
} else {
flashers[f].stateDur = 20 + random8(6 + ((255 - SEGMENT.speed) >> 2)); //*10, 250ms to 1250ms
}
//flashers[f].stateDur = 51 + random8(2 + ((255 - SEGMENT.speed) >> 1));
flashers[f].stateStart = now16;
if (stateTime < 255) {
flashers[f].stateStart -= 255 -stateTime; //start early to get correct bri
flashers[f].stateDur += 26 - stateTime/10;
stateTime = 255 - stateTime;
} else {
stateTime = 0;
}
}
if (stateTime > 255) stateTime = 255; //for flasher brightness calculation, fades in first 255 ms of state
//flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? 255-gamma8((510 - stateTime) >> 1) : gamma8((510 - stateTime) >> 1);
flasherBri[f - firstFlasher] = (flashers[f].stateOn) ? stateTime : 255 - (stateTime >> 0);
flasherBriSum += flasherBri[f - firstFlasher];
}
//dim factor, to create "shimmer" as other pixels get less voltage if a lot of flashers are on
uint8_t avgFlasherBri = flasherBriSum / flashersInZone;
uint8_t globalPeakBri = 255 - ((avgFlasherBri * MAX_SHIMMER) >> 8); //183-255, suitable for 1/5th of LEDs flashers
for (uint16_t f = firstFlasher; f < firstFlasher + flashersInZone; f++) {
uint8_t bri = (flasherBri[f - firstFlasher] * globalPeakBri) / 255;
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
uint16_t flasherPos = f*flasherDistance;
setPixelColor(flasherPos, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), bri));
for (uint16_t i = flasherPos+1; i < flasherPos+flasherDistance && i < SEGLEN; i++) {
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
setPixelColor(i, color_from_palette(PRNG16 >> 8, false, false, 0, globalPeakBri));
}
}
}
return FRAMETIME;
}
/*
* Fairytwinkle. Like Colortwinkle, but starting from all lit and not relying on getPixelColor
* Warning: Uses 4 bytes of segment data per pixel
*/
uint16_t WS2812FX::mode_fairytwinkle() {
uint16_t dataSize = sizeof(flasher) * SEGLEN;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
Flasher* flashers = reinterpret_cast<Flasher*>(SEGENV.data);
uint16_t now16 = now & 0xFFFF;
uint16_t PRNG16 = 5100 + _segment_index;
uint16_t riseFallTime = 400 + (255-SEGMENT.speed)*3;
uint16_t maxDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + 13 + ((255 - SEGMENT.intensity) >> 1);
for (uint16_t f = 0; f < SEGLEN; f++) {
uint16_t stateTime = now16 - flashers[f].stateStart;
//random on/off time reached, switch state
if (stateTime > flashers[f].stateDur * 100) {
flashers[f].stateOn = !flashers[f].stateOn;
bool init = !flashers[f].stateDur;
if (flashers[f].stateOn) {
flashers[f].stateDur = riseFallTime/100 + ((255 - SEGMENT.intensity) >> 2) + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +1;
} else {
flashers[f].stateDur = riseFallTime/100 + random8(3 + ((255 - SEGMENT.speed) >> 6)) +1;
}
flashers[f].stateStart = now16;
stateTime = 0;
if (init) {
flashers[f].stateStart -= riseFallTime; //start lit
flashers[f].stateDur = riseFallTime/100 + random8(12 + ((255 - SEGMENT.intensity) >> 1)) +5; //fire up a little quicker
stateTime = riseFallTime;
}
}
if (flashers[f].stateOn && flashers[f].stateDur > maxDur) flashers[f].stateDur = maxDur; //react more quickly on intensity change
if (stateTime > riseFallTime) stateTime = riseFallTime; //for flasher brightness calculation, fades in first 255 ms of state
uint8_t fadeprog = 255 - ((stateTime * 255) / riseFallTime);
uint8_t flasherBri = (flashers[f].stateOn) ? 255-gamma8(fadeprog) : gamma8(fadeprog);
uint16_t lastR = PRNG16;
uint16_t diff = 0;
while (diff < 0x4000) { //make sure colors of two adjacent LEDs differ enough
PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; //next 'random' number
diff = (PRNG16 > lastR) ? PRNG16 - lastR : lastR - PRNG16;
}
setPixelColor(f, color_blend(SEGCOLOR(1), color_from_palette(PRNG16 >> 8, false, false, 0), flasherBri));
}
return FRAMETIME;
}

View File

@ -161,14 +161,14 @@
#define FX_MODE_COMET 41
#define FX_MODE_FIREWORKS 42
#define FX_MODE_RAIN 43
#define FX_MODE_TETRIX 44
#define FX_MODE_TETRIX 44 //was Merry Christmas prior to 0.12.0 (use "Chase 2" with Red/Green)
#define FX_MODE_FIRE_FLICKER 45
#define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47
#define FX_MODE_POLICE 48 // candidate for removal (after below three)
#define FX_MODE_POLICE_ALL 49 // candidate for removal
#define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity)
#define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51 // candidate for removal
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
#define FX_MODE_RUNNING_DUAL 52
#define FX_MODE_HALLOWEEN 53 // candidate for removal
#define FX_MODE_TRICOLOR_CHASE 54
@ -550,9 +550,9 @@ class WS2812FX {
_mode[FX_MODE_GRADIENT] = &WS2812FX::mode_gradient;
_mode[FX_MODE_LOADING] = &WS2812FX::mode_loading;
_mode[FX_MODE_POLICE] = &WS2812FX::mode_police;
_mode[FX_MODE_POLICE_ALL] = &WS2812FX::mode_police_all;
_mode[FX_MODE_FAIRY] = &WS2812FX::mode_fairy;
_mode[FX_MODE_TWO_DOTS] = &WS2812FX::mode_two_dots;
_mode[FX_MODE_TWO_AREAS] = &WS2812FX::mode_two_areas;
_mode[FX_MODE_FAIRYTWINKLE] = &WS2812FX::mode_fairytwinkle;
_mode[FX_MODE_RUNNING_DUAL] = &WS2812FX::mode_running_dual;
_mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween;
_mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase;
@ -773,9 +773,9 @@ class WS2812FX {
mode_gradient(void),
mode_loading(void),
mode_police(void),
mode_police_all(void),
mode_fairy(void),
mode_two_dots(void),
mode_two_areas(void),
mode_fairytwinkle(void),
mode_running_dual(void),
mode_bicolor_chase(void),
mode_tricolor_chase(void),
@ -878,7 +878,7 @@ class WS2812FX {
chase(uint32_t, uint32_t, uint32_t, bool),
gradient_base(bool),
ripple_base(bool),
police_base(uint32_t, uint32_t, uint16_t),
police_base(uint32_t, uint32_t),
running(uint32_t, uint32_t, bool theatre=false),
tricolor_chase(uint32_t, uint32_t),
twinklefox_base(bool),
@ -936,7 +936,7 @@ class WS2812FX {
// - a ! means that the default is used.
// - For sliders: Effect speeds, Effect intensity, Custom 1, Custom 2, Custom 3
// - For colors: Fx color, Background color, Custom
// - For palette: prompt Color palette
// - For palette: prompt for color palette OR palette ID if numeric (will hide palette selection)
//
// Note: If palette is on and no colors are specified 1,2 and 3 is shown in each color circle.
// If a color is specified, the 1,2 or 3 is replaced by that specification.
@ -992,14 +992,14 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Fire Flicker",
"Gradient",
"Loading",
"Police@!,Width;;",
"Police All@!,Width;;",
"Police@!,Width;;0",
"Fairy",
"Two Dots@!,Dot size;1,2,Bg;!",
"Two Areas@!,Size;1,2,Bg;!",
"Fairy Twinkle",
"Running Dual",
"Halloween",
"Chase 3@!,Size;1,2,3;",
"Tri Wipe@!,Width;1,2,3;",
"Chase 3@!,Size;1,2,3;0",
"Tri Wipe@!,Width;1,2,3;0",
"Tri Fade",
"Lightning",
"ICU",
@ -1027,12 +1027,12 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Twinklefox",
"Twinklecat",
"Halloween Eyes",
"Solid Pattern@Fg size,Bg size;Fg,Bg,;",
"Solid Pattern Tri@,Size;1,2,3;",
"Solid Pattern@Fg size,Bg size;Fg,Bg,;0",
"Solid Pattern Tri@,Size;1,2,3;0",
"Spots@Spread,Width;!,!,;!",
"Spots Fade@Spread,Width;!,!,;!",
"Glitter",
"Candle@Flicker rate,Flicker intensity;!,!,;",
"Candle@Flicker rate,Flicker intensity;!,!,;0",
"Fireworks Starburst",
"Fireworks 1D@Gravity,Firing side;!,!,;!",
"Bouncing Balls@Gravity,# of balls;!,!,;!",
@ -1046,9 +1046,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Ripple Rainbow",
"Heartbeat",
"Pacifica",
"Candle Multi@Flicker rate,Flicker intensity;!,!,;",
"Solid Glitter@,!;!,,;",
"Sunrise@Time [min],;;",
"Candle Multi@Flicker rate,Flicker intensity;!,!,;0",
"Solid Glitter@,!;!,,;0",
"Sunrise@Time [min],;;0",
"Phased",
"Twinkleup@!,Intensity;!,!,;!",
"Noise Pal",

View File

@ -442,14 +442,14 @@ void WS2812FX::setBrightness(uint8_t b) {
if (gammaCorrectBri) b = gamma8(b);
if (_brightness == b) return;
_brightness = b;
_segment_index = 0;
if (_brightness == 0) { //unfreeze all segments on power off
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
_segments[i].setOption(SEG_OPTION_FREEZE, false);
}
}
if (SEGENV.next_time > millis() + 22 && millis() - _lastShow > MIN_SHOW_DELAY) show();//apply brightness change immediately if no refresh soon
unsigned long t = millis();
if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon
}
uint8_t WS2812FX::getMode(void) {
@ -701,14 +701,35 @@ bool WS2812FX::checkSegmentAlignment() {
}
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
//Note: If called in an interrupt (e.g. JSON API), it must be reset with "setPixelColor(255)",
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
#ifdef ARDUINO_ARCH_ESP32
uint8_t _segment_index_prev = 0;
uint16_t _virtualSegmentLength_prev = 0;
bool _ps_set = false;
#endif
void WS2812FX::setPixelSegment(uint8_t n)
{
if (n < MAX_NUM_SEGMENTS) {
#ifdef ARDUINO_ARCH_ESP32
if (!_ps_set) {
_segment_index_prev = _segment_index;
_virtualSegmentLength_prev = _virtualSegmentLength;
_ps_set = true;
}
#endif
_segment_index = n;
_virtualSegmentLength = SEGMENT.length();
_virtualSegmentLength = SEGMENT.virtualLength();
} else {
_segment_index = 0;
_virtualSegmentLength = 0;
_virtualSegmentLength = 0;
#ifdef ARDUINO_ARCH_ESP32
if (_ps_set) {
_segment_index = _segment_index_prev;
_virtualSegmentLength = _virtualSegmentLength_prev;
_ps_set = false;
}
#endif
}
}
@ -735,13 +756,13 @@ void WS2812FX::setTransition(uint16_t t)
void WS2812FX::setTransitionMode(bool t)
{
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled
for (uint16_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{
_segment_index = i;
SEGMENT.setOption(SEG_OPTION_TRANSITIONAL, t);
_segments[i].setOption(SEG_OPTION_TRANSITIONAL, t);
if (t && SEGMENT.mode == FX_MODE_STATIC && SEGENV.next_time > waitMax) SEGENV.next_time = waitMax;
if (t && _segments[i].mode == FX_MODE_STATIC && _segment_runtimes[i].next_time > waitMax)
_segment_runtimes[i].next_time = waitMax;
}
}
@ -1097,7 +1118,7 @@ void WS2812FX::deserializeMap(uint8_t n) {
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(5)) return;
if (!requestJSONBufferLock(7)) return;
#endif
DEBUG_PRINT(F("Reading LED map from "));

View File

@ -318,16 +318,21 @@ class BusPwm : public Bus {
cct = (approximateKelvinFromRGB(c) - 1900) >> 5;
}
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
uint8_t ww, cw;
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
#ifdef WLED_USE_IC_CCT
ww = w;
cw = cct;
#else
//0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold)
if (cct < _cctBlend) ww = 255;
else ww = ((255-cct) * 255) / (255 - _cctBlend);
if ((255-cct) < _cctBlend) cw = 255;
else cw = (cct * 255) / (255 - _cctBlend);
ww = (w * ww) / 255; //brightness scaling
cw = (w * cw) / 255;
#endif
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), relies on auto white calculation
@ -448,7 +453,7 @@ class BusNetwork : public Bus {
void setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (_rgbw) c = autoWhiteCalc(c);
if (isRgbw()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);

View File

@ -86,8 +86,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setAutoWhiteMode(hw_led[F("rgbwm")] | Bus::getAutoWhiteMode());
CJSON(correctWB, hw_led["cct"]);
CJSON(cctFromRgb, hw_led[F("cr")]);
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
JsonArray ins = hw_led["ins"];
@ -542,7 +542,7 @@ void serializeConfig() {
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led["cct"] = correctWB;
hw_led[F("cr")] = cctFromRgb;
hw_led[F("cb")] = strip.cctBlending;
hw_led[F("cb")] = strip.cctBlending;
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode();
JsonArray hw_led_ins = hw_led.createNestedArray("ins");

View File

@ -240,7 +240,7 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M
float low = minf(rgb[0],minf(rgb[1],rgb[2]));
float high = maxf(rgb[0],maxf(rgb[1],rgb[2]));
if (high < 0.1f) return;
float sat = 100.0f * ((high - low) / high);; // maximum saturation is 100 (corrected from 255)
float sat = 100.0f * ((high - low) / high); // maximum saturation is 100 (corrected from 255)
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
}
*/

View File

@ -729,7 +729,7 @@ function populateEffects()
effects.unshift({
"id": 0,
"name": "Solid@;!;"
"name": "Solid@;!;0"
});
for (let i = 0; i < effects.length; i++) {
@ -1282,13 +1282,15 @@ function setSliderAndColorControl(idx/*, extra*/)
var palw = gId("palw"); // wrapper
var pall = gId("pall"); // list
// if not controlDefined or palette has a value
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="")) {
if ((!controlDefined) || (paOnOff.length>0 && paOnOff[0]!="" && isNaN(paOnOff[0]))) {
palw.style.display = "inline-block";
if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0];
else pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette';
} else {
// disable label and slider
palw.style.display = "none";
// if numeric set as selected palette
if (paOnOff.length>0 && paOnOff[0]!="" && !isNaN(paOnOff[0])) setPalette(parseInt(paOnOff[0]));
}
}
@ -1793,7 +1795,7 @@ function setPalette(paletteId = null)
if (paletteId === null) {
paletteId = parseInt(d.querySelector('#pallist input[name="palette"]:checked').value);
} else {
d.querySelector(`#pallist input[name="palette"][value="${paletteId}`).checked = true;
d.querySelector(`#pallist input[name="palette"][value="${paletteId}"]`).checked = true;
}
var selElement = d.querySelector('#pallist .selected');
if (selElement) {

File diff suppressed because it is too large Load Diff

View File

@ -575,11 +575,10 @@ void decodeIRJson(uint32_t code)
JsonObject fdo;
JsonObject jsonCmdObj;
DEBUG_PRINTLN(F("IR JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(6)) return;
if (!requestJSONBufferLock(13)) return;
#endif
sprintf_P(objKey, PSTR("\"0x%lX\":"), (unsigned long)code);
@ -593,12 +592,12 @@ void decodeIRJson(uint32_t code)
lastValidCode = 0;
if (fdo.isNull()) {
//the received code does not exist
releaseJSONBufferLock();
if (!WLED_FS.exists("/ir.json")) errorFlag = ERR_FS_IRLOAD; //warn if IR file itself doesn't exist
releaseJSONBufferLock();
return;
}
cmdStr = fdo["cmd"].as<String>();;
cmdStr = fdo["cmd"].as<String>();
jsonCmdObj = fdo["cmd"]; //object
// command is JSON object
@ -638,9 +637,9 @@ void decodeIRJson(uint32_t code)
}
colorUpdated(CALL_MODE_BUTTON);
} else if (!jsonCmdObj.isNull()) {
// command is JSON object
deserializeState(jsonCmdObj, CALL_MODE_BUTTON);
}
//fileDoc = nullptr;
releaseJSONBufferLock();
}
@ -669,7 +668,8 @@ void handleIR()
{
if (results.value != 0) // only print results if anything is received ( != 0 )
{
DEBUG_PRINTF("IR recv: 0x%lX\n", (unsigned long)results.value);
if (!pinManager.isPinAllocated(1)) //GPIO 1 - Serial TX pin
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
}
decodeIR(results.value);
irrecv->resume();

View File

@ -67,7 +67,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing;
strip.setSegment(id, start, stop, grp, spc);
uint16_t of = seg.offset;
uint16_t len = 1;
if (stop > start) len = stop - start;
@ -76,9 +76,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
int offsetAbs = abs(offset);
if (offsetAbs > len - 1) offsetAbs %= len;
if (offset < 0) offsetAbs = len - offsetAbs;
seg.offset = offsetAbs;
of = offsetAbs;
}
if (stop > start && seg.offset > len -1) seg.offset = len -1;
if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of);
byte segbri = 0;
if (getVal(elem["bri"], &segbri)) {
@ -159,17 +160,19 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
//temporary, strip object gets updated via colorUpdated()
if (id == strip.getMainSegmentId()) {
byte effectPrev = effectCurrent;
if (getVal(elem["fx"], &effectCurrent, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually
if (!presetId && effectCurrent != effectPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
effectSpeed = elem[F("sx")] | effectSpeed;
effectIntensity = elem[F("ix")] | effectIntensity;
getVal(elem["pal"], &effectPalette, 1, strip.getPaletteCount());
} else { //permanent
byte fx = seg.mode;
byte fxPrev = fx;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
strip.setMode(id, fx);
if (!presetId) unloadPlaylist(); //stop playlist if active and FX changed manually
if (!presetId && seg.mode != fxPrev) unloadPlaylist(); //stop playlist if active and FX changed manually
}
seg.speed = elem[F("sx")] | seg.speed;
seg.intensity = elem[F("ix")] | seg.intensity;
@ -345,11 +348,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
usermods.readFromJsonState(root);
int8_t ledmap = root[F("ledmap")] | -1;
if (ledmap >= 0) {
//strip.deserializeMap(ledmap); // requires separate JSON buffer
loadLedmap = ledmap;
}
loadLedmap = root[F("ledmap")] | loadLedmap;
byte ps = root[F("psave")];
if (ps > 0) {
@ -960,7 +959,7 @@ void serveJson(AsyncWebServerRequest* request)
#ifdef WLED_USE_DYNAMIC_JSON
AsyncJsonResponse* response = new AsyncJsonResponse(JSON_BUFFER_SIZE, subJson==6);
#else
if (!requestJSONBufferLock(7)) return;
if (!requestJSONBufferLock(17)) return;
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==6);
#endif

View File

@ -91,22 +91,20 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
DEBUG_PRINTLN(F("MQTT JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(8)) return;
#endif
if (payload[0] == '{') { //JSON API
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(15)) return;
#endif
deserializeJson(doc, payloadStr);
//fileDoc = &doc; // used for applying presets (presets.cpp)
deserializeState(doc.as<JsonObject>());
//fileDoc = nullptr;
releaseJSONBufferLock();
} else { //HTTP API
String apireq = "win&";
apireq += (char*)payloadStr;
handleSet(nullptr, apireq);
}
releaseJSONBufferLock();
} else if (strlen(topic) != 0) {
// non standard topic, check with usermods
usermods.onMqttMessage(topic, payloadStr);

View File

@ -27,7 +27,6 @@ bool applyPreset(byte index, byte callMode)
#else
if (!requestJSONBufferLock(9)) return false;
#endif
errorFlag = readObjectFromFileUsingId(filename, index, &doc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = doc.as<JsonObject>();
if (fdo["ps"] == index) fdo.remove("ps");
@ -59,7 +58,6 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
#else
if (!requestJSONBufferLock(10)) return;
#endif
sObj = doc.to<JsonObject>();
if (pname) sObj["n"] = pname;

View File

@ -420,7 +420,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(11)) return;
if (!requestJSONBufferLock(5)) return;
#endif
JsonObject um = doc.createNestedObject("um");

View File

@ -202,6 +202,7 @@ bool isAsterisksOnly(const char* str, byte maxLen)
}
//threading/network callback details: https://github.com/Aircoookie/WLED/pull/2336#discussion_r762276994
bool requestJSONBufferLock(uint8_t module)
{
unsigned long now = millis();

View File

@ -286,8 +286,10 @@ void WLED::setup()
WiFi.onEvent(WiFiEvent);
#endif
#ifdef WLED_ENABLE_ADALIGHT // reserve GPIO3 (RX) pin for ADALight
if (!pinManager.isPinAllocated(3)) {
#ifdef WLED_ENABLE_ADALIGHT
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
if (!pinManager.isPinAllocated(3) && !pinManager.isPinAllocated(1)) {
Serial.println(F("Ada"));
pinManager.allocatePin(3,false);
} else {

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2112071
#define VERSION 2112072
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -527,7 +527,7 @@ WLED_GLOBAL byte presetCycMax _INIT(5);
// realtime
WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE);
WLED_GLOBAL byte realtimeOverride _INIT(REALTIME_OVERRIDE_NONE);
WLED_GLOBAL IPAddress realtimeIP _INIT_N(((0, 0, 0, 0)));;
WLED_GLOBAL IPAddress realtimeIP _INIT_N(((0, 0, 0, 0)));
WLED_GLOBAL unsigned long realtimeTimeout _INIT(0);
WLED_GLOBAL uint8_t tpmPacketCount _INIT(0);
WLED_GLOBAL uint16_t tpmPayloadFrameSize _INIT(0);
@ -609,8 +609,8 @@ WLED_GLOBAL int8_t loadLedmap _INIT(-1);
// Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
// global ArduinoJson buffer
#ifndef WLED_USE_DYNAMIC_JSON
// global ArduinoJson buffer
WLED_GLOBAL StaticJsonDocument<JSON_BUFFER_SIZE> doc;
#endif
WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0);

View File

@ -385,7 +385,7 @@ void deEEP() {
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(12)) return;
if (!requestJSONBufferLock(8)) return;
#endif
JsonObject sObj = doc.to<JsonObject>();

View File

@ -48,11 +48,10 @@ void handleSerial()
Serial.print("WLED"); Serial.write(' '); Serial.println(VERSION);
} else if (next == '{') { //JSON API
bool verboseResponse = false;
DEBUG_PRINTLN(F("Serial JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(13)) return;
if (!requestJSONBufferLock(16)) return;
#endif
Serial.setTimeout(100);
DeserializationError error = deserializeJson(doc, Serial);
@ -60,10 +59,7 @@ void handleSerial()
releaseJSONBufferLock();
return;
}
//fileDoc = &doc; // used for applying presets (presets.cpp)
verboseResponse = deserializeState(doc.as<JsonObject>());
//fileDoc = nullptr;
//only send response if TX pin is unused for other purposes
if (verboseResponse && !pinManager.isPinAllocated(1)) {
doc.clear();

View File

@ -120,7 +120,6 @@ void initServer()
bool verboseResponse = false;
bool isConfig = false;
{ //scope JsonDocument so it releases its buffer
DEBUG_PRINTLN(F("HTTP JSON buffer requested."));
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
@ -142,9 +141,7 @@ void initServer()
serializeJson(root,Serial);
DEBUG_PRINTLN();
#endif
//fileDoc = &doc; // used for applying presets (presets.cpp)
verboseResponse = deserializeState(root);
//fileDoc = nullptr;
} else {
verboseResponse = deserializeConfig(root); //use verboseResponse to determine whether cfg change should be saved immediately
}

View File

@ -40,7 +40,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(15)) return;
if (!requestJSONBufferLock(11)) return;
#endif
DeserializationError error = deserializeJson(doc, data, len);
@ -49,13 +49,6 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
releaseJSONBufferLock();
return;
}
/*
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Incoming WS: "));
serializeJson(root,Serial);
DEBUG_PRINTLN();
#endif
*/
if (root["v"] && root.size() == 1) {
//if the received value is just "{"v":true}", send only to this client
verboseResponse = true;
@ -63,15 +56,13 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
{
wsLiveClientId = root["lv"] ? client->id() : 0;
} else {
//fileDoc = &doc; // used for applying presets (presets.cpp)
verboseResponse = deserializeState(root);
//fileDoc = nullptr;
if (!interfaceUpdateCallMode) {
//special case, only on playlist load, avoid sending twice in rapid succession
if (millis() - lastInterfaceUpdate > 1700) verboseResponse = false;
}
}
releaseJSONBufferLock();
releaseJSONBufferLock(); // will clean fileDoc
}
//update if it takes longer than 300ms until next "broadcast"
if (verboseResponse && (millis() - lastInterfaceUpdate < 1700 || !interfaceUpdateCallMode)) sendDataWs(client);
@ -117,7 +108,7 @@ void sendDataWs(AsyncWebSocketClient * client)
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
#else
if (!requestJSONBufferLock(16)) return;
if (!requestJSONBufferLock(12)) return;
#endif
JsonObject state = doc.createNestedObject("state");
@ -131,13 +122,6 @@ void sendDataWs(AsyncWebSocketClient * client)
releaseJSONBufferLock();
return; //out of memory
}
/*
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Outgoing WS: "));
serializeJson(doc,Serial);
DEBUG_PRINTLN();
#endif
*/
serializeJson(doc, (char *)buffer->get(), len +1);
releaseJSONBufferLock();
}

View File

@ -253,17 +253,18 @@ void getSettingsJS(byte subPage, char* dest)
// add reserved and usermod pins as d.um_p array
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
{ // scope so buffer can be released earlier
#ifdef WLED_USE_DYNAMIC_JSON
DynamicJsonDocument doc(2048); // 2k is enough for usermods
DynamicJsonDocument doc(3072);
#else
if (!requestJSONBufferLock(17)) return;
if (!requestJSONBufferLock(6)) return;
#endif
JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods);
if (!mods.isNull()) fillUMPins(mods);
releaseJSONBufferLock();
}
#ifdef WLED_ENABLE_DMX
oappend(SET_F(",2")); // DMX hardcoded pin