mirror of
https://github.com/wled/WLED.git
synced 2025-07-16 07:16:31 +00:00
Merge pull request #4658 from wled/layers
Segment layers and better effect transitions (blending)
This commit is contained in:
commit
23a51e0982
@ -102,9 +102,9 @@ private:
|
||||
void secondsEffectSineFade(int16_t secondLed, Toki::Time const& time) {
|
||||
uint32_t ms = time.ms % 1000;
|
||||
uint8_t b0 = (cos8_t(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(secondLed, gamma32(scale32(secondColor, b0)));
|
||||
setPixelColor(secondLed, scale32(secondColor, b0));
|
||||
uint8_t b1 = (sin8_t(ms * 64 / 1000) - 128) * 2;
|
||||
setPixelColor(inc(secondLed, 1, secondsSegment), gamma32(scale32(secondColor, b1)));
|
||||
setPixelColor(inc(secondLed, 1, secondsSegment), scale32(secondColor, b1));
|
||||
}
|
||||
|
||||
static inline uint32_t qadd32(uint32_t c1, uint32_t c2) {
|
||||
@ -191,7 +191,7 @@ public:
|
||||
// for (uint16_t i = 1; i < secondsTrail + 1; ++i) {
|
||||
// uint16_t trailLed = dec(secondLed, i, secondsSegment);
|
||||
// uint8_t trailBright = 255 / (secondsTrail + 1) * (secondsTrail - i + 1);
|
||||
// setPixelColor(trailLed, gamma32(scale32(secondColor, trailBright)));
|
||||
// setPixelColor(trailLed, scale32(secondColor, trailBright));
|
||||
// }
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,7 @@ class UsermodCronixie : public Usermod {
|
||||
|
||||
if (backlight && _digitOut[i] <11)
|
||||
{
|
||||
uint32_t col = gamma32(strip.getSegment(0).colors[1]);
|
||||
uint32_t col = strip.getSegment(0).colors[1];
|
||||
for (uint16_t j=o; j< o+10; j++) {
|
||||
if (j != excl) strip.setPixelColor(j, col);
|
||||
}
|
||||
|
@ -1736,7 +1736,7 @@ class AudioReactive : public Usermod {
|
||||
}
|
||||
|
||||
void onStateChange(uint8_t callMode) override {
|
||||
if (initDone && enabled && addPalettes && palettes==0 && strip.customPalettes.size()<10) {
|
||||
if (initDone && enabled && addPalettes && palettes==0 && customPalettes.size()<10) {
|
||||
// if palettes were removed during JSON call re-add them
|
||||
createAudioPalettes();
|
||||
}
|
||||
@ -1966,20 +1966,20 @@ class AudioReactive : public Usermod {
|
||||
void AudioReactive::removeAudioPalettes(void) {
|
||||
DEBUG_PRINTLN(F("Removing audio palettes."));
|
||||
while (palettes>0) {
|
||||
strip.customPalettes.pop_back();
|
||||
customPalettes.pop_back();
|
||||
DEBUG_PRINTLN(palettes);
|
||||
palettes--;
|
||||
}
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(customPalettes.size());
|
||||
}
|
||||
|
||||
void AudioReactive::createAudioPalettes(void) {
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(strip.customPalettes.size());
|
||||
DEBUG_PRINT(F("Total # of palettes: ")); DEBUG_PRINTLN(customPalettes.size());
|
||||
if (palettes) return;
|
||||
DEBUG_PRINTLN(F("Adding audio palettes."));
|
||||
for (int i=0; i<MAX_PALETTES; i++)
|
||||
if (strip.customPalettes.size() < 10) {
|
||||
strip.customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
|
||||
if (customPalettes.size() < 10) {
|
||||
customPalettes.push_back(CRGBPalette16(CRGB(BLACK)));
|
||||
palettes++;
|
||||
DEBUG_PRINTLN(palettes);
|
||||
} else break;
|
||||
@ -2016,7 +2016,7 @@ CRGB AudioReactive::getCRGBForBand(int x, int pal) {
|
||||
|
||||
void AudioReactive::fillAudioPalettes() {
|
||||
if (!palettes) return;
|
||||
size_t lastCustPalette = strip.customPalettes.size();
|
||||
size_t lastCustPalette = customPalettes.size();
|
||||
if (int(lastCustPalette) >= palettes) lastCustPalette -= palettes;
|
||||
for (int pal=0; pal<palettes; pal++) {
|
||||
uint8_t tcp[16]; // Needs to be 4 times however many colors are being used.
|
||||
@ -2045,7 +2045,7 @@ void AudioReactive::fillAudioPalettes() {
|
||||
tcp[14] = rgb.g;
|
||||
tcp[15] = rgb.b;
|
||||
|
||||
strip.customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
|
||||
customPalettes[lastCustPalette+pal].loadDynamicGradientPalette(tcp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,20 +400,20 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
DEBUG_PRINT(F("Sorting palettes: ")); DEBUG_PRINT(strip.getPaletteCount()); DEBUG_PRINT('/'); DEBUG_PRINTLN(strip.customPalettes.size());
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());
|
||||
if (strip.customPalettes.size()) {
|
||||
for (int i=0; i<strip.customPalettes.size(); i++) {
|
||||
palettes_alpha_indexes[strip.getPaletteCount()-strip.customPalettes.size()+i] = 255-i;
|
||||
palettes_qstrings[strip.getPaletteCount()-strip.customPalettes.size()+i] = PSTR("~Custom~");
|
||||
DEBUG_PRINT(F("Sorting palettes: ")); DEBUG_PRINT(getPaletteCount()); DEBUG_PRINT('/'); DEBUG_PRINTLN(customPalettes.size());
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(getPaletteCount());
|
||||
if (customPalettes.size()) {
|
||||
for (int i=0; i<customPalettes.size(); i++) {
|
||||
palettes_alpha_indexes[getPaletteCount()-customPalettes.size()+i] = 255-i;
|
||||
palettes_qstrings[getPaletteCount()-customPalettes.size()+i] = PSTR("~Custom~");
|
||||
}
|
||||
}
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
int skipPaletteCount = 1;
|
||||
while (pgm_read_byte_near(palettes_qstrings[skipPaletteCount]) == '*') skipPaletteCount++;
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount()-strip.customPalettes.size(), skipPaletteCount);
|
||||
re_sortModes(palettes_qstrings, palettes_alpha_indexes, getPaletteCount()-customPalettes.size(), skipPaletteCount);
|
||||
}
|
||||
|
||||
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
|
||||
@ -702,7 +702,7 @@ void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
|
||||
|
||||
effectPaletteIndex = 0;
|
||||
DEBUG_PRINTLN(effectPalette);
|
||||
for (unsigned i = 0; i < strip.getPaletteCount()+strip.customPalettes.size(); i++) {
|
||||
for (unsigned i = 0; i < getPaletteCount()+customPalettes.size(); i++) {
|
||||
if (palettes_alpha_indexes[i] == effectPalette) {
|
||||
effectPaletteIndex = i;
|
||||
DEBUG_PRINTLN(F("Found palette."));
|
||||
@ -892,7 +892,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) {
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()+strip.customPalettes.size()-1), 0U);
|
||||
effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), getPaletteCount()+customPalettes.size()-1), 0U);
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
|
@ -5041,7 +5041,11 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
|
||||
unsigned yscale = SEGMENT.speed*8;
|
||||
unsigned indexx = 0;
|
||||
|
||||
CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : SEGMENT.loadPalette(pal, 35);
|
||||
//CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : SEGMENT.loadPalette(pal, 35);
|
||||
CRGBPalette16 pal = SEGMENT.check1 ? SEGPALETTE : CRGBPalette16(CRGB::Black, CRGB::Black, CRGB::Black, CRGB::Black,
|
||||
CRGB::Red, CRGB::Red, CRGB::Red, CRGB::DarkOrange,
|
||||
CRGB::DarkOrange,CRGB::DarkOrange, CRGB::Orange, CRGB::Orange,
|
||||
CRGB::Yellow, CRGB::Orange, CRGB::Yellow, CRGB::Yellow);
|
||||
for (int j=0; j < cols; j++) {
|
||||
for (int i=0; i < rows; i++) {
|
||||
indexx = perlin8(j*yscale*rows/255, i*xscale+strip.now/4); // We're moving along our Perlin map.
|
||||
@ -6076,7 +6080,8 @@ uint16_t mode_2Dscrollingtext(void) {
|
||||
case 5: letterWidth = 5; letterHeight = 12; break;
|
||||
}
|
||||
// letters are rotated
|
||||
if (((SEGMENT.custom3+1)>>3) % 2) {
|
||||
const int8_t rotate = map(SEGMENT.custom3, 0, 31, -2, 2);
|
||||
if (rotate == 1 || rotate == -1) {
|
||||
rotLH = letterWidth;
|
||||
rotLW = letterHeight;
|
||||
} else {
|
||||
@ -6114,6 +6119,7 @@ uint16_t mode_2Dscrollingtext(void) {
|
||||
else if (!strncmp_P(text,PSTR("#DD"),3)) sprintf (text, zero? ("%02d") : ("%d"), day(localTime));
|
||||
else if (!strncmp_P(text,PSTR("#DAY"),4)) sprintf (text, ("%s") , dayShortStr(day(localTime)));
|
||||
else if (!strncmp_P(text,PSTR("#DDDD"),5)) sprintf (text, ("%s") , dayStr(day(localTime)));
|
||||
else if (!strncmp_P(text,PSTR("#DAYL"),5)) sprintf (text, ("%s") , dayStr(day(localTime)));
|
||||
else if (!strncmp_P(text,PSTR("#MO"),3)) sprintf (text, zero? ("%02d") : ("%d"), month(localTime));
|
||||
else if (!strncmp_P(text,PSTR("#MON"),4)) sprintf (text, ("%s") , monthShortStr(month(localTime)));
|
||||
else if (!strncmp_P(text,PSTR("#MMMM"),5)) sprintf (text, ("%s") , monthStr(month(localTime)));
|
||||
@ -6147,27 +6153,28 @@ uint16_t mode_2Dscrollingtext(void) {
|
||||
SEGENV.step = strip.now + map(SEGMENT.speed, 0, 255, 250, 50); // shift letters every ~250ms to ~50ms
|
||||
}
|
||||
|
||||
if (!SEGMENT.check2) SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
|
||||
bool usePaletteGradient = false;
|
||||
SEGMENT.fade_out(255 - (SEGMENT.custom1>>4)); // trail
|
||||
uint32_t col1 = SEGMENT.color_from_palette(SEGENV.aux1, false, PALETTE_SOLID_WRAP, 0);
|
||||
uint32_t col2 = BLACK;
|
||||
// if gradient is selected and palette is default (0) drawCharacter() uses gradient from SEGCOLOR(0) to SEGCOLOR(2)
|
||||
// otherwise col2 == BLACK means use currently selected palette for gradient
|
||||
// if gradient is not selected set both colors the same
|
||||
if (SEGMENT.check1) { // use gradient
|
||||
if(SEGMENT.palette == 0) { // use colors for gradient
|
||||
col1 = SEGCOLOR(0);
|
||||
col2 = SEGCOLOR(2);
|
||||
if (SEGMENT.palette == 0) { // use colors for gradient
|
||||
col1 = SEGCOLOR(0);
|
||||
col2 = SEGCOLOR(2);
|
||||
}
|
||||
else usePaletteGradient = true;
|
||||
}
|
||||
} else col2 = col1; // force characters to use single color (from palette)
|
||||
|
||||
for (int i = 0; i < numberOfLetters; i++) {
|
||||
int xoffset = int(cols) - int(SEGENV.aux0) + rotLW*i;
|
||||
if (xoffset + rotLW < 0) continue; // don't draw characters off-screen
|
||||
SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, map(SEGMENT.custom3, 0, 31, -2, 2), usePaletteGradient);
|
||||
SEGMENT.drawCharacter(text[i], xoffset, yoffset, letterWidth, letterHeight, col1, col2, rotate);
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,Overlay,Reverse;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
|
||||
static const char _data_FX_MODE_2DSCROLLTEXT[] PROGMEM = "Scrolling Text@!,Y Offset,Trail,Font size,Rotate,Gradient,,Reverse;!,!,Gradient;!;2;ix=128,c1=0,rev=0,mi=0,rY=0,mY=0";
|
||||
|
||||
|
||||
////////////////////////////
|
||||
|
709
wled00/FX.h
Normal file → Executable file
709
wled00/FX.h
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,6 @@
|
||||
Parts of the code adapted from WLED Sound Reactive
|
||||
*/
|
||||
#include "wled.h"
|
||||
#include "FX.h"
|
||||
#include "palettes.h"
|
||||
|
||||
// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels
|
||||
@ -26,8 +25,7 @@ void WS2812FX::setUpMatrix() {
|
||||
// calculate width dynamically because it may have gaps
|
||||
Segment::maxWidth = 1;
|
||||
Segment::maxHeight = 1;
|
||||
for (size_t i = 0; i < panel.size(); i++) {
|
||||
Panel &p = panel[i];
|
||||
for (const Panel &p : panel) {
|
||||
if (p.xOffset + p.width > Segment::maxWidth) {
|
||||
Segment::maxWidth = p.xOffset + p.width;
|
||||
}
|
||||
@ -37,21 +35,24 @@ void WS2812FX::setUpMatrix() {
|
||||
}
|
||||
|
||||
// safety check
|
||||
if (Segment::maxWidth * Segment::maxHeight > MAX_LEDS || Segment::maxWidth <= 1 || Segment::maxHeight <= 1) {
|
||||
if (Segment::maxWidth * Segment::maxHeight > MAX_LEDS || Segment::maxWidth > 255 || Segment::maxHeight > 255 || Segment::maxWidth <= 1 || Segment::maxHeight <= 1) {
|
||||
DEBUG_PRINTLN(F("2D Bounds error."));
|
||||
isMatrix = false;
|
||||
Segment::maxWidth = _length;
|
||||
Segment::maxHeight = 1;
|
||||
panels = 0;
|
||||
panel.clear(); // release memory allocated by panels
|
||||
panel.shrink_to_fit(); // release memory if allocated
|
||||
resetSegments();
|
||||
return;
|
||||
}
|
||||
|
||||
suspend();
|
||||
waitForIt();
|
||||
|
||||
customMappingSize = 0; // prevent use of mapping if anything goes wrong
|
||||
|
||||
if (customMappingTable) free(customMappingTable);
|
||||
customMappingTable = static_cast<uint16_t*>(malloc(sizeof(uint16_t)*getLengthTotal()));
|
||||
d_free(customMappingTable);
|
||||
customMappingTable = static_cast<uint16_t*>(d_malloc(sizeof(uint16_t)*getLengthTotal())); // prefer to not use SPI RAM
|
||||
|
||||
if (customMappingTable) {
|
||||
customMappingSize = getLengthTotal();
|
||||
@ -85,7 +86,7 @@ void WS2812FX::setUpMatrix() {
|
||||
JsonArray map = pDoc->as<JsonArray>();
|
||||
gapSize = map.size();
|
||||
if (!map.isNull() && gapSize >= matrixSize) { // not an empty map
|
||||
gapTable = static_cast<int8_t*>(malloc(gapSize));
|
||||
gapTable = static_cast<int8_t*>(p_malloc(gapSize));
|
||||
if (gapTable) for (size_t i = 0; i < gapSize; i++) {
|
||||
gapTable[i] = constrain(map[i], -1, 1);
|
||||
}
|
||||
@ -96,8 +97,7 @@ void WS2812FX::setUpMatrix() {
|
||||
}
|
||||
|
||||
unsigned x, y, pix=0; //pixel
|
||||
for (size_t pan = 0; pan < panel.size(); pan++) {
|
||||
Panel &p = panel[pan];
|
||||
for (const Panel &p : panel) {
|
||||
unsigned h = p.vertical ? p.height : p.width;
|
||||
unsigned v = p.vertical ? p.width : p.height;
|
||||
for (size_t j = 0; j < v; j++){
|
||||
@ -113,7 +113,8 @@ void WS2812FX::setUpMatrix() {
|
||||
}
|
||||
|
||||
// delete gap array as we no longer need it
|
||||
if (gapTable) free(gapTable);
|
||||
p_free(gapTable);
|
||||
resume();
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("Matrix ledmap:"));
|
||||
@ -126,7 +127,6 @@ void WS2812FX::setUpMatrix() {
|
||||
} else { // memory allocation error
|
||||
DEBUG_PRINTLN(F("ERROR 2D LED map allocation error."));
|
||||
isMatrix = false;
|
||||
panels = 0;
|
||||
panel.clear();
|
||||
Segment::maxWidth = _length;
|
||||
Segment::maxHeight = 1;
|
||||
@ -144,103 +144,50 @@ void WS2812FX::setUpMatrix() {
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
|
||||
// raw setColor function without checks (checks are done in setPixelColorXY())
|
||||
void IRAM_ATTR_YN Segment::_setPixelColorXY_raw(const int& x, const int& y, uint32_t& col) const
|
||||
{
|
||||
const int baseX = start + x;
|
||||
const int baseY = startY + y;
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
// if blending modes, blend with underlying pixel
|
||||
if (_modeBlend && blendingStyle == BLEND_STYLE_FADE) col = color_blend16(strip.getPixelColorXY(baseX, baseY), col, 0xFFFFU - progress());
|
||||
#endif
|
||||
strip.setPixelColorXY(baseX, baseY, col);
|
||||
|
||||
// Apply mirroring
|
||||
if (mirror || mirror_y) {
|
||||
const int mirrorX = start + width() - x - 1;
|
||||
const int mirrorY = startY + height() - y - 1;
|
||||
if (mirror) strip.setPixelColorXY(transpose ? baseX : mirrorX, transpose ? mirrorY : baseY, col);
|
||||
if (mirror_y) strip.setPixelColorXY(transpose ? mirrorX : baseX, transpose ? baseY : mirrorY, col);
|
||||
if (mirror && mirror_y) strip.setPixelColorXY(mirrorX, mirrorY, col);
|
||||
}
|
||||
}
|
||||
|
||||
// pixel is clipped if it falls outside clipping range (_modeBlend==true) or is inside clipping range (_modeBlend==false)
|
||||
// pixel is clipped if it falls outside clipping range
|
||||
// if clipping start > stop the clipping range is inverted
|
||||
// _modeBlend==true -> old effect during transition
|
||||
// _modeBlend==false -> new effect during transition
|
||||
bool IRAM_ATTR_YN Segment::isPixelXYClipped(int x, int y) const {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_clipStart != _clipStop && blendingStyle != BLEND_STYLE_FADE) {
|
||||
const bool invertX = _clipStart > _clipStop;
|
||||
if (blendingStyle != BLEND_STYLE_FADE && isInTransition() && _clipStart != _clipStop) {
|
||||
const bool invertX = _clipStart > _clipStop;
|
||||
const bool invertY = _clipStartY > _clipStopY;
|
||||
const int startX = invertX ? _clipStop : _clipStart;
|
||||
const int stopX = invertX ? _clipStart : _clipStop;
|
||||
const int startY = invertY ? _clipStopY : _clipStartY;
|
||||
const int stopY = invertY ? _clipStartY : _clipStopY;
|
||||
const int cStartX = invertX ? _clipStop : _clipStart;
|
||||
const int cStopX = invertX ? _clipStart : _clipStop;
|
||||
const int cStartY = invertY ? _clipStopY : _clipStartY;
|
||||
const int cStopY = invertY ? _clipStartY : _clipStopY;
|
||||
if (blendingStyle == BLEND_STYLE_FAIRY_DUST) {
|
||||
const unsigned width = stopX - startX; // assumes full segment width (faster than virtualWidth())
|
||||
const unsigned len = width * (stopY - startY); // assumes full segment height (faster than virtualHeight())
|
||||
const unsigned width = cStopX - cStartX; // assumes full segment width (faster than virtualWidth())
|
||||
const unsigned len = width * (cStopY - cStartY); // assumes full segment height (faster than virtualHeight())
|
||||
if (len < 2) return false;
|
||||
const unsigned shuffled = hashInt(x + y * width) % len;
|
||||
const unsigned pos = (shuffled * 0xFFFFU) / len;
|
||||
return progress() > pos;
|
||||
return progress() <= pos;
|
||||
}
|
||||
bool xInside = (x >= startX && x < stopX); if (invertX) xInside = !xInside;
|
||||
bool yInside = (y >= startY && y < stopY); if (invertY) yInside = !yInside;
|
||||
const bool clip = (invertX && invertY) ? !_modeBlend : _modeBlend;
|
||||
if (xInside && yInside) return clip; // covers window & corners (inverted)
|
||||
if (blendingStyle == BLEND_STYLE_CIRCULAR_IN || blendingStyle == BLEND_STYLE_CIRCULAR_OUT) {
|
||||
const int cx = (cStopX-cStartX+1) / 2;
|
||||
const int cy = (cStopY-cStartY+1) / 2;
|
||||
const bool out = (blendingStyle == BLEND_STYLE_CIRCULAR_OUT);
|
||||
const unsigned prog = out ? progress() : 0xFFFFU - progress();
|
||||
int radius2 = max(cx, cy) * prog / 0xFFFF;
|
||||
radius2 = 2 * radius2 * radius2;
|
||||
if (radius2 == 0) return out;
|
||||
const int dx = x - cx;
|
||||
const int dy = y - cy;
|
||||
const bool outside = dx * dx + dy * dy > radius2;
|
||||
return out ? outside : !outside;
|
||||
}
|
||||
bool xInside = (x >= cStartX && x < cStopX); if (invertX) xInside = !xInside;
|
||||
bool yInside = (y >= cStartY && y < cStopY); if (invertY) yInside = !yInside;
|
||||
const bool clip = blendingStyle == BLEND_STYLE_OUTSIDE_IN ? xInside || yInside : xInside && yInside;
|
||||
return !clip;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) const
|
||||
{
|
||||
if (!isActive()) return; // not active
|
||||
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
unsigned prog = 0xFFFF - progress();
|
||||
if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) {
|
||||
unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
|
||||
unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
|
||||
if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x += dX;
|
||||
else x -= dX;
|
||||
if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
|
||||
else y += dY;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (x >= vW || y >= vH || x < 0 || y < 0 || isPixelXYClipped(x,y)) return; // if pixel would fall out of virtual segment just exit
|
||||
|
||||
// if color is unscaled
|
||||
if (!_colorScaled) col = color_fade(col, _segBri);
|
||||
|
||||
if (reverse ) x = vW - x - 1;
|
||||
if (reverse_y) y = vH - y - 1;
|
||||
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
|
||||
unsigned groupLen = groupLength();
|
||||
|
||||
if (groupLen > 1) {
|
||||
int W = width();
|
||||
int H = height();
|
||||
x *= groupLen; // expand to physical pixels
|
||||
y *= groupLen; // expand to physical pixels
|
||||
const int maxY = std::min(y + grouping, H);
|
||||
const int maxX = std::min(x + grouping, W);
|
||||
for (int yY = y; yY < maxY; yY++) {
|
||||
for (int xX = x; xX < maxX; xX++) {
|
||||
_setPixelColorXY_raw(xX, yY, col);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_setPixelColorXY_raw(x, y, col);
|
||||
}
|
||||
if (x >= (int)vWidth() || y >= (int)vHeight() || x < 0 || y < 0) return; // if pixel would fall out of virtual segment just exit
|
||||
setPixelColorXYRaw(x, y, col);
|
||||
}
|
||||
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
@ -289,39 +236,17 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa) const
|
||||
// returns RGBW values of pixel
|
||||
uint32_t IRAM_ATTR_YN Segment::getPixelColorXY(int x, int y) const {
|
||||
if (!isActive()) return 0; // not active
|
||||
|
||||
const int vW = vWidth();
|
||||
const int vH = vHeight();
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
unsigned prog = 0xFFFF - progress();
|
||||
if (!prog && !_modeBlend && (blendingStyle & BLEND_STYLE_PUSH_MASK)) {
|
||||
unsigned dX = (blendingStyle == BLEND_STYLE_PUSH_UP || blendingStyle == BLEND_STYLE_PUSH_DOWN) ? 0 : prog * vW / 0xFFFF;
|
||||
unsigned dY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : prog * vH / 0xFFFF;
|
||||
if (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_BL) x -= dX;
|
||||
else x += dX;
|
||||
if (blendingStyle == BLEND_STYLE_PUSH_DOWN || blendingStyle == BLEND_STYLE_PUSH_TL || blendingStyle == BLEND_STYLE_PUSH_TR) y -= dY;
|
||||
else y += dY;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (x >= vW || y >= vH || x<0 || y<0 || isPixelXYClipped(x,y)) return 0; // if pixel would fall out of virtual segment just exit
|
||||
|
||||
if (reverse ) x = vW - x - 1;
|
||||
if (reverse_y) y = vH - y - 1;
|
||||
if (transpose) { std::swap(x,y); } // swap X & Y if segment transposed
|
||||
x *= groupLength(); // expand to physical pixels
|
||||
y *= groupLength(); // expand to physical pixels
|
||||
if (x >= width() || y >= height()) return 0;
|
||||
return strip.getPixelColorXY(start + x, startY + y);
|
||||
if (x >= (int)vWidth() || y >= (int)vHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
|
||||
return getPixelColorXYRaw(x,y);
|
||||
}
|
||||
|
||||
// 2D blurring, can be asymmetrical
|
||||
void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) {
|
||||
void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) const {
|
||||
if (!isActive()) return; // not active
|
||||
const unsigned cols = vWidth();
|
||||
const unsigned rows = vHeight();
|
||||
uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration
|
||||
const auto XY = [&](unsigned x, unsigned y){ return x + y*cols; };
|
||||
uint32_t lastnew; // not necessary to initialize lastnew and last, as both will be initialized by the first loop iteration
|
||||
uint32_t last;
|
||||
if (blur_x) {
|
||||
const uint8_t keepx = smear ? 255 : 255 - blur_x;
|
||||
@ -330,20 +255,20 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) {
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t curnew = BLACK;
|
||||
for (unsigned x = 0; x < cols; x++) {
|
||||
uint32_t cur = getPixelColorXY(x, row);
|
||||
uint32_t cur = getPixelColorRaw(XY(x, row));
|
||||
uint32_t part = color_fade(cur, seepx);
|
||||
curnew = color_fade(cur, keepx);
|
||||
if (x > 0) {
|
||||
if (carryover) curnew = color_add(curnew, carryover);
|
||||
uint32_t prev = color_add(lastnew, part);
|
||||
// optimization: only set pixel if color has changed
|
||||
if (last != prev) setPixelColorXY(x - 1, row, prev);
|
||||
} else setPixelColorXY(x, row, curnew); // first pixel
|
||||
if (last != prev) setPixelColorRaw(XY(x - 1, row), prev);
|
||||
} else setPixelColorRaw(XY(x, row), curnew); // first pixel
|
||||
lastnew = curnew;
|
||||
last = cur; // save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
}
|
||||
setPixelColorXY(cols-1, row, curnew); // set last pixel
|
||||
setPixelColorRaw(XY(cols-1, row), curnew); // set last pixel
|
||||
}
|
||||
}
|
||||
if (blur_y) {
|
||||
@ -353,20 +278,20 @@ void Segment::blur2D(uint8_t blur_x, uint8_t blur_y, bool smear) {
|
||||
uint32_t carryover = BLACK;
|
||||
uint32_t curnew = BLACK;
|
||||
for (unsigned y = 0; y < rows; y++) {
|
||||
uint32_t cur = getPixelColorXY(col, y);
|
||||
uint32_t cur = getPixelColorRaw(XY(col, y));
|
||||
uint32_t part = color_fade(cur, seepy);
|
||||
curnew = color_fade(cur, keepy);
|
||||
if (y > 0) {
|
||||
if (carryover) curnew = color_add(curnew, carryover);
|
||||
uint32_t prev = color_add(lastnew, part);
|
||||
// optimization: only set pixel if color has changed
|
||||
if (last != prev) setPixelColorXY(col, y - 1, prev);
|
||||
} else setPixelColorXY(col, y, curnew); // first pixel
|
||||
if (last != prev) setPixelColorRaw(XY(col, y - 1), prev);
|
||||
} else setPixelColorRaw(XY(col, y), curnew); // first pixel
|
||||
lastnew = curnew;
|
||||
last = cur; //save original value for comparison on next iteration
|
||||
carryover = part;
|
||||
}
|
||||
setPixelColorXY(col, rows - 1, curnew);
|
||||
setPixelColorRaw(XY(col, rows - 1), curnew);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -445,10 +370,11 @@ void Segment::box_blur(unsigned radius, bool smear) {
|
||||
delete[] tmpWSum;
|
||||
}
|
||||
*/
|
||||
void Segment::moveX(int delta, bool wrap) {
|
||||
void Segment::moveX(int delta, bool wrap) const {
|
||||
if (!isActive() || !delta) return; // not active
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
const auto XY = [&](unsigned x, unsigned y){ return x + y*vW; };
|
||||
int absDelta = abs(delta);
|
||||
if (absDelta >= vW) return;
|
||||
uint32_t newPxCol[vW];
|
||||
@ -465,16 +391,17 @@ void Segment::moveX(int delta, bool wrap) {
|
||||
for (int x = 0; x < stop; x++) {
|
||||
int srcX = x + newDelta;
|
||||
if (wrap) srcX %= vW; // Wrap using modulo when `wrap` is true
|
||||
newPxCol[x] = getPixelColorXY(srcX, y);
|
||||
newPxCol[x] = getPixelColorRaw(XY(srcX, y));
|
||||
}
|
||||
for (int x = 0; x < stop; x++) setPixelColorXY(x + start, y, newPxCol[x]);
|
||||
for (int x = 0; x < stop; x++) setPixelColorRaw(XY(x + start, y), newPxCol[x]);
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::moveY(int delta, bool wrap) {
|
||||
void Segment::moveY(int delta, bool wrap) const {
|
||||
if (!isActive() || !delta) return; // not active
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
const auto XY = [&](unsigned x, unsigned y){ return x + y*vW; };
|
||||
int absDelta = abs(delta);
|
||||
if (absDelta >= vH) return;
|
||||
uint32_t newPxCol[vH];
|
||||
@ -491,9 +418,9 @@ void Segment::moveY(int delta, bool wrap) {
|
||||
for (int y = 0; y < stop; y++) {
|
||||
int srcY = y + newDelta;
|
||||
if (wrap) srcY %= vH; // Wrap using modulo when `wrap` is true
|
||||
newPxCol[y] = getPixelColorXY(x, srcY);
|
||||
newPxCol[y] = getPixelColorRaw(XY(x, srcY));
|
||||
}
|
||||
for (int y = 0; y < stop; y++) setPixelColorXY(x, y + start, newPxCol[y]);
|
||||
for (int y = 0; y < stop; y++) setPixelColorRaw(XY(x, y + start), newPxCol[y]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -501,7 +428,7 @@ void Segment::moveY(int delta, bool wrap) {
|
||||
// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down
|
||||
// @param delta number of pixels to move
|
||||
// @param wrap around
|
||||
void Segment::move(unsigned dir, unsigned delta, bool wrap) {
|
||||
void Segment::move(unsigned dir, unsigned delta, bool wrap) const {
|
||||
if (delta==0) return;
|
||||
switch (dir) {
|
||||
case 0: moveX( delta, wrap); break;
|
||||
@ -515,7 +442,7 @@ void Segment::move(unsigned dir, unsigned delta, bool wrap) {
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
|
||||
void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) const {
|
||||
if (!isActive() || radius == 0) return; // not active
|
||||
if (soft) {
|
||||
// Xiaolin Wu’s algorithm
|
||||
@ -549,9 +476,6 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
|
||||
x++;
|
||||
}
|
||||
} else {
|
||||
// pre-scale color for all pixels
|
||||
col = color_fade(col, _segBri);
|
||||
_colorScaled = true;
|
||||
// Bresenham’s Algorithm
|
||||
int d = 3 - (2*radius);
|
||||
int y = radius, x = 0;
|
||||
@ -570,20 +494,16 @@ void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
|
||||
d += 4 * x + 6;
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
|
||||
void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
|
||||
void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) const {
|
||||
if (!isActive() || radius == 0) return; // not active
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
// draw soft bounding circle
|
||||
if (soft) drawCircle(cx, cy, radius, col, soft);
|
||||
// pre-scale color for all pixels
|
||||
col = color_fade(col, _segBri);
|
||||
_colorScaled = true;
|
||||
// fill it
|
||||
for (int y = -radius; y <= radius; y++) {
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
@ -593,11 +513,10 @@ void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col,
|
||||
setPixelColorXY(cx + x, cy + y, col);
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
}
|
||||
|
||||
//line function
|
||||
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) {
|
||||
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) const {
|
||||
if (!isActive()) return; // not active
|
||||
const int vW = vWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
|
||||
const int vH = vHeight(); // segment height in logical pixels (is always >= 1)
|
||||
@ -633,15 +552,12 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
|
||||
int y = int(intersectY);
|
||||
if (steep) std::swap(x,y); // temporaryly swap if steep
|
||||
// pixel coverage is determined by fractional part of y co-ordinate
|
||||
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep));
|
||||
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep));
|
||||
blendPixelColorXY(x, y, c, seep);
|
||||
blendPixelColorXY(x+int(steep), y+int(!steep), c, keep);
|
||||
intersectY += gradient;
|
||||
if (steep) std::swap(x,y); // restore if steep
|
||||
}
|
||||
} else {
|
||||
// pre-scale color for all pixels
|
||||
c = color_fade(c, _segBri);
|
||||
_colorScaled = true;
|
||||
// Bresenham's algorithm
|
||||
int err = (dx>dy ? dx : -dy)/2; // error direction
|
||||
for (;;) {
|
||||
@ -651,7 +567,6 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
|
||||
if (e2 >-dx) { err -= dy; x0 += sx; }
|
||||
if (e2 < dy) { err += dx; y0 += sy; }
|
||||
}
|
||||
_colorScaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -663,29 +578,26 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
|
||||
|
||||
// draws a raster font character on canvas
|
||||
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
|
||||
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate, bool usePalGrad) {
|
||||
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate) const {
|
||||
if (!isActive()) return; // not active
|
||||
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
|
||||
chr -= 32; // align with font table entries
|
||||
const int font = w*h;
|
||||
|
||||
CRGB col = CRGB(color);
|
||||
CRGBPalette16 grad = CRGBPalette16(col, col2 ? CRGB(col2) : col);
|
||||
if(usePalGrad) grad = SEGPALETTE; // selected palette as gradient
|
||||
// if col2 == BLACK then use currently selected palette for gradient otherwise create gradient from color and col2
|
||||
CRGBPalette16 grad = col2 ? CRGBPalette16(CRGB(color), CRGB(col2)) : SEGPALETTE; // selected palette as gradient
|
||||
|
||||
//if (w<5 || w>6 || h!=8) return;
|
||||
for (int i = 0; i<h; i++) { // character height
|
||||
uint8_t bits = 0;
|
||||
switch (font) {
|
||||
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
|
||||
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 4x6 font
|
||||
case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); break; // 5x8 font
|
||||
case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); break; // 6x8 font
|
||||
case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); break; // 7x9 font
|
||||
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font
|
||||
default: return;
|
||||
}
|
||||
CRGBW c = ColorFromPalette(grad, (i+1)*255/h, _segBri, LINEARBLEND_NOWRAP);
|
||||
_colorScaled = true;
|
||||
CRGBW c = ColorFromPalette(grad, (i+1)*255/h, 255, LINEARBLEND_NOWRAP); // NOBLEND is faster
|
||||
for (int j = 0; j<w; j++) { // character width
|
||||
int x0, y0;
|
||||
switch (rotate) {
|
||||
@ -697,15 +609,14 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
}
|
||||
if (x0 < 0 || x0 >= (int)vWidth() || y0 < 0 || y0 >= (int)vHeight()) continue; // drawing off-screen
|
||||
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
|
||||
setPixelColorXY(x0, y0, c.color32);
|
||||
setPixelColorXYRaw(x0, y0, c.color32);
|
||||
}
|
||||
}
|
||||
_colorScaled = false;
|
||||
}
|
||||
}
|
||||
|
||||
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
|
||||
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
|
||||
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) const { //awesome wu_pixel procedure by reddit u/sutaburosu
|
||||
if (!isActive()) return; // not active
|
||||
// extract the fractional parts and derive their inverses
|
||||
unsigned xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
|
||||
|
1831
wled00/FX_fcn.cpp
Normal file → Executable file
1831
wled00/FX_fcn.cpp
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -32,8 +32,31 @@ extern bool useParallelI2S;
|
||||
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
|
||||
|
||||
//udp.cpp
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const byte *buffer, uint8_t bri=255, bool isRGBW=false);
|
||||
|
||||
//util.cpp
|
||||
// PSRAM allocation wrappers
|
||||
#ifndef ESP8266
|
||||
extern "C" {
|
||||
void *p_malloc(size_t); // prefer PSRAM over DRAM
|
||||
void *p_calloc(size_t, size_t); // prefer PSRAM over DRAM
|
||||
void *p_realloc(void *, size_t); // prefer PSRAM over DRAM
|
||||
inline void p_free(void *ptr) { heap_caps_free(ptr); }
|
||||
void *d_malloc(size_t); // prefer DRAM over PSRAM
|
||||
void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM
|
||||
void *d_realloc(void *, size_t); // prefer DRAM over PSRAM
|
||||
inline void d_free(void *ptr) { heap_caps_free(ptr); }
|
||||
}
|
||||
#else
|
||||
#define p_malloc malloc
|
||||
#define p_calloc calloc
|
||||
#define p_realloc realloc
|
||||
#define p_free free
|
||||
#define d_malloc malloc
|
||||
#define d_calloc calloc
|
||||
#define d_realloc realloc
|
||||
#define d_free free
|
||||
#endif
|
||||
|
||||
//color mangling macros
|
||||
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
|
||||
@ -72,7 +95,7 @@ void Bus::calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) {
|
||||
} else {
|
||||
cct = (approximateKelvinFromRGB(c) - 1900) >> 5; // convert K (from RGB value) to relative format
|
||||
}
|
||||
|
||||
|
||||
//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);
|
||||
@ -106,7 +129,6 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
|
||||
, _colorOrder(bc.colorOrder)
|
||||
, _milliAmpsPerLed(bc.milliAmpsPerLed)
|
||||
, _milliAmpsMax(bc.milliAmpsMax)
|
||||
, _data(nullptr)
|
||||
{
|
||||
DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
|
||||
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
|
||||
@ -127,10 +149,6 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
_hasCCT = hasCCT(bc.type);
|
||||
if (bc.doubleBuffer) {
|
||||
_data = (uint8_t*)calloc(_len, Bus::getNumberOfChannels(_type));
|
||||
if (!_data) DEBUGBUS_PRINTLN(F("Bus: Buffer allocation failed!"));
|
||||
}
|
||||
uint16_t lenToCreate = bc.count;
|
||||
if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr);
|
||||
@ -213,43 +231,6 @@ void BusDigital::show() {
|
||||
uint8_t cctWW = 0, cctCW = 0;
|
||||
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere())
|
||||
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
|
||||
|
||||
if (_data) {
|
||||
size_t channels = getNumberOfChannels();
|
||||
int16_t oldCCT = Bus::_cct; // temporarily save bus CCT
|
||||
for (size_t i=0; i<_len; i++) {
|
||||
size_t offset = i * channels;
|
||||
unsigned co = _colorOrderMap.getPixelColorOrder(i+_start, _colorOrder);
|
||||
uint32_t c;
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs (_len is always a multiple of 3)
|
||||
switch (i%3) {
|
||||
case 0: c = RGBW32(_data[offset] , _data[offset+1], _data[offset+2], 0); break;
|
||||
case 1: c = RGBW32(_data[offset-1], _data[offset] , _data[offset+1], 0); break;
|
||||
case 2: c = RGBW32(_data[offset-2], _data[offset-1], _data[offset] , 0); break;
|
||||
}
|
||||
} else {
|
||||
if (hasRGB()) c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
|
||||
else c = RGBW32(0, 0, 0, _data[offset]);
|
||||
}
|
||||
if (hasCCT()) {
|
||||
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
|
||||
// we need to extract and appy CCT value for each pixel individually even though all buses share the same _cct variable
|
||||
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
|
||||
Bus::_cct = _data[offset+channels-1];
|
||||
Bus::calculateCCT(c, cctWW, cctCW);
|
||||
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
|
||||
}
|
||||
unsigned pix = i;
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, (cctCW<<8) | cctWW);
|
||||
}
|
||||
#if !defined(STATUSLED) || STATUSLED>=0
|
||||
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
#endif
|
||||
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
Bus::_cct = oldCCT;
|
||||
} else {
|
||||
if (newBri < _bri) {
|
||||
unsigned hwLen = _len;
|
||||
if (_type == TYPE_WS2812_1CH_X3) hwLen = NUM_ICS_WS2812_1CH_3X(_len); // only needs a third of "RGB" LEDs for NeoPixelBus
|
||||
@ -260,8 +241,7 @@ void BusDigital::show() {
|
||||
PolyBus::setPixelColor(_busPtr, _iType, i, c, 0, (cctCW<<8) | cctWW); // repaint all pixels with new brightness
|
||||
}
|
||||
}
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important
|
||||
PolyBus::show(_busPtr, _iType, false); // faster if buffer consistency is not important
|
||||
// restore bus brightness to its original value
|
||||
// this is done right after show, so this is only OK if LED updates are completed before show() returns
|
||||
// or async show has a separate buffer (ESP32 RMT and I2S are ok)
|
||||
@ -292,86 +272,61 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
|
||||
if (!_valid) return;
|
||||
if (hasWhite()) c = autoWhiteCalc(c);
|
||||
if (Bus::_cct >= 1900) c = colorBalanceFromKelvin(Bus::_cct, c); //color correction from CCT
|
||||
if (_data) {
|
||||
size_t offset = pix * getNumberOfChannels();
|
||||
uint8_t* dataptr = _data + offset;
|
||||
if (hasRGB()) {
|
||||
*dataptr++ = R(c);
|
||||
*dataptr++ = G(c);
|
||||
*dataptr++ = B(c);
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
unsigned pOld = pix;
|
||||
pix = IC_INDEX_WS2812_1CH_3X(pix);
|
||||
uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri);
|
||||
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
|
||||
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
|
||||
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
|
||||
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
|
||||
}
|
||||
if (hasWhite()) *dataptr++ = W(c);
|
||||
// unfortunately as a segment may span multiple buses or a bus may contain multiple segments and each segment may have different CCT
|
||||
// we need to store CCT value for each pixel (if there is a color correction in play, convert K in CCT ratio)
|
||||
if (hasCCT()) *dataptr = Bus::_cct >= 1900 ? (Bus::_cct - 1900) >> 5 : (Bus::_cct < 0 ? 127 : Bus::_cct); // TODO: if _cct == -1 we simply ignore it
|
||||
} else {
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
unsigned pOld = pix;
|
||||
pix = IC_INDEX_WS2812_1CH_3X(pix);
|
||||
uint32_t cOld = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, pix, co),_bri);
|
||||
switch (pOld % 3) { // change only the single channel (TODO: this can cause loss because of get/set)
|
||||
case 0: c = RGBW32(R(cOld), W(c) , B(cOld), 0); break;
|
||||
case 1: c = RGBW32(W(c) , G(cOld), B(cOld), 0); break;
|
||||
case 2: c = RGBW32(R(cOld), G(cOld), W(c) , 0); break;
|
||||
}
|
||||
}
|
||||
uint16_t wwcw = 0;
|
||||
if (hasCCT()) {
|
||||
uint8_t cctWW = 0, cctCW = 0;
|
||||
Bus::calculateCCT(c, cctWW, cctCW);
|
||||
wwcw = (cctCW<<8) | cctWW;
|
||||
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
|
||||
}
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
|
||||
}
|
||||
uint16_t wwcw = 0;
|
||||
if (hasCCT()) {
|
||||
uint8_t cctWW = 0, cctCW = 0;
|
||||
Bus::calculateCCT(c, cctWW, cctCW);
|
||||
wwcw = (cctCW<<8) | cctWW;
|
||||
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c));
|
||||
}
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
|
||||
}
|
||||
|
||||
// returns original color if global buffering is enabled, else returns lossly restored color from bus
|
||||
uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
|
||||
if (!_valid) return 0;
|
||||
if (_data) {
|
||||
const size_t offset = pix * getNumberOfChannels();
|
||||
uint32_t c;
|
||||
if (!hasRGB()) {
|
||||
c = RGBW32(_data[offset], _data[offset], _data[offset], _data[offset]);
|
||||
} else {
|
||||
c = RGBW32(_data[offset], _data[offset+1], _data[offset+2], hasWhite() ? _data[offset+3] : 0);
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
unsigned r = R(c);
|
||||
unsigned g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
|
||||
unsigned b = _reversed ? G(c) : B(c);
|
||||
switch (pix % 3) { // get only the single channel
|
||||
case 0: c = RGBW32(g, g, g, g); break;
|
||||
case 1: c = RGBW32(r, r, r, r); break;
|
||||
case 2: c = RGBW32(b, b, b, b); break;
|
||||
}
|
||||
return c;
|
||||
} else {
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
const unsigned co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
unsigned r = R(c);
|
||||
unsigned g = _reversed ? B(c) : G(c); // should G and B be switched if _reversed?
|
||||
unsigned b = _reversed ? G(c) : B(c);
|
||||
switch (pix % 3) { // get only the single channel
|
||||
case 0: c = RGBW32(g, g, g, g); break;
|
||||
case 1: c = RGBW32(r, r, r, r); break;
|
||||
case 2: c = RGBW32(b, b, b, b); break;
|
||||
}
|
||||
}
|
||||
if (_type == TYPE_WS2812_WWA) {
|
||||
uint8_t w = R(c) | G(c);
|
||||
c = RGBW32(w, w, 0, w);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
if (_type == TYPE_WS2812_WWA) {
|
||||
uint8_t w = R(c) | G(c);
|
||||
c = RGBW32(w, w, 0, w);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
unsigned BusDigital::getPins(uint8_t* pinArray) const {
|
||||
size_t BusDigital::getPins(uint8_t* pinArray) const {
|
||||
unsigned numPins = is2Pin(_type) + 1;
|
||||
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
return numPins;
|
||||
}
|
||||
|
||||
unsigned BusDigital::getBusSize() const {
|
||||
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0);
|
||||
size_t BusDigital::getBusSize() const {
|
||||
return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) /*+ (_data ? _len * getNumberOfChannels() : 0)*/ : 0);
|
||||
}
|
||||
|
||||
void BusDigital::setColorOrder(uint8_t colorOrder) {
|
||||
@ -380,7 +335,7 @@ void BusDigital::setColorOrder(uint8_t colorOrder) {
|
||||
_colorOrder = colorOrder;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled/WLED/pull/4056
|
||||
std::vector<LEDType> BusDigital::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_WS2812_RGB, "D", PSTR("WS281x")},
|
||||
@ -414,8 +369,6 @@ void BusDigital::begin() {
|
||||
void BusDigital::cleanup() {
|
||||
DEBUGBUS_PRINTLN(F("Digital Cleanup."));
|
||||
PolyBus::cleanup(_busPtr, _iType);
|
||||
free(_data);
|
||||
_data = nullptr;
|
||||
_iType = I_NONE;
|
||||
_valid = false;
|
||||
_busPtr = nullptr;
|
||||
@ -453,7 +406,7 @@ BusPwm::BusPwm(const BusConfig &bc)
|
||||
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering
|
||||
{
|
||||
if (!isPWM(bc.type)) return;
|
||||
unsigned numPins = numPWMPins(bc.type);
|
||||
const unsigned numPins = numPWMPins(bc.type);
|
||||
[[maybe_unused]] const bool dithering = _needsRefresh;
|
||||
_frequency = bc.frequency ? bc.frequency : WLED_PWM_FREQ;
|
||||
// duty cycle resolution (_depth) can be extracted from this formula: CLOCK_FREQUENCY > _frequency * 2^_depth
|
||||
@ -461,36 +414,40 @@ BusPwm::BusPwm(const BusConfig &bc)
|
||||
|
||||
managed_pin_type pins[numPins];
|
||||
for (unsigned i = 0; i < numPins; i++) pins[i] = {(int8_t)bc.pins[i], true};
|
||||
if (!PinManager::allocateMultiplePins(pins, numPins, PinOwner::BusPwm)) return;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// for 2 pin PWM CCT strip pinManager will make sure both LEDC channels are in the same speed group and sharing the same timer
|
||||
_ledcStart = PinManager::allocateLedc(numPins);
|
||||
if (_ledcStart == 255) { //no more free LEDC channels
|
||||
PinManager::deallocateMultiplePins(pins, numPins, PinOwner::BusPwm);
|
||||
return;
|
||||
}
|
||||
// if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
|
||||
if (dithering) _depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering)
|
||||
#endif
|
||||
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
_pins[i] = bc.pins[i]; // store only after allocateMultiplePins() succeeded
|
||||
if (PinManager::allocateMultiplePins(pins, numPins, PinOwner::BusPwm)) {
|
||||
#ifdef ESP8266
|
||||
pinMode(_pins[i], OUTPUT);
|
||||
analogWriteRange((1<<_depth)-1);
|
||||
analogWriteFreq(_frequency);
|
||||
#else
|
||||
unsigned channel = _ledcStart + i;
|
||||
ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
|
||||
ledcAttachPin(_pins[i], channel);
|
||||
// LEDC timer reset credit @dedehai
|
||||
uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup()
|
||||
ledc_timer_rst((ledc_mode_t)group, (ledc_timer_t)timer); // reset timer so all timers are almost in sync (for phase shift)
|
||||
// for 2 pin PWM CCT strip pinManager will make sure both LEDC channels are in the same speed group and sharing the same timer
|
||||
_ledcStart = PinManager::allocateLedc(numPins);
|
||||
if (_ledcStart == 255) { //no more free LEDC channels
|
||||
PinManager::deallocateMultiplePins(pins, numPins, PinOwner::BusPwm);
|
||||
DEBUGBUS_PRINTLN(F("No more free LEDC channels!"));
|
||||
return;
|
||||
}
|
||||
// if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
|
||||
if (dithering) _depth = 12; // fixed 8 bit depth PWM with 4 bit dithering (ESP8266 has no hardware to support dithering)
|
||||
#endif
|
||||
|
||||
for (unsigned i = 0; i < numPins; i++) {
|
||||
_pins[i] = bc.pins[i]; // store only after allocateMultiplePins() succeeded
|
||||
#ifdef ESP8266
|
||||
pinMode(_pins[i], OUTPUT);
|
||||
#else
|
||||
unsigned channel = _ledcStart + i;
|
||||
ledcSetup(channel, _frequency, _depth - (dithering*4)); // with dithering _frequency doesn't really matter as resolution is 8 bit
|
||||
ledcAttachPin(_pins[i], channel);
|
||||
// LEDC timer reset credit @dedehai
|
||||
uint8_t group = (channel / 8), timer = ((channel / 2) % 4); // same fromula as in ledcSetup()
|
||||
ledc_timer_rst((ledc_mode_t)group, (ledc_timer_t)timer); // reset timer so all timers are almost in sync (for phase shift)
|
||||
#endif
|
||||
}
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
_hasCCT = hasCCT(bc.type);
|
||||
_valid = true;
|
||||
}
|
||||
_hasRgb = hasRGB(bc.type);
|
||||
_hasWhite = hasWhite(bc.type);
|
||||
_hasCCT = hasCCT(bc.type);
|
||||
_valid = true;
|
||||
DEBUGBUS_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]);
|
||||
}
|
||||
|
||||
@ -561,7 +518,7 @@ void BusPwm::show() {
|
||||
constexpr unsigned bitShift = 8; // 256 clocks for dead time, ~3us at 80MHz
|
||||
#else
|
||||
// if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor)
|
||||
// https://github.com/wled-dev/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1)
|
||||
// https://github.com/wled/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1)
|
||||
const bool dithering = _needsRefresh; // avoid working with bitfield
|
||||
const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8)
|
||||
const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits)
|
||||
@ -620,14 +577,14 @@ void BusPwm::show() {
|
||||
}
|
||||
}
|
||||
|
||||
unsigned BusPwm::getPins(uint8_t* pinArray) const {
|
||||
size_t BusPwm::getPins(uint8_t* pinArray) const {
|
||||
if (!_valid) return 0;
|
||||
unsigned numPins = numPWMPins(_type);
|
||||
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
|
||||
return numPins;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled/WLED/pull/4056
|
||||
std::vector<LEDType> BusPwm::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_ANALOG_1CH, "A", PSTR("PWM White")},
|
||||
@ -695,13 +652,13 @@ void BusOnOff::show() {
|
||||
digitalWrite(_pin, _reversed ? !(bool)_data : (bool)_data);
|
||||
}
|
||||
|
||||
unsigned BusOnOff::getPins(uint8_t* pinArray) const {
|
||||
size_t BusOnOff::getPins(uint8_t* pinArray) const {
|
||||
if (!_valid) return 0;
|
||||
if (pinArray) pinArray[0] = _pin;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled/WLED/pull/4056
|
||||
std::vector<LEDType> BusOnOff::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_ONOFF, "", PSTR("On/Off")},
|
||||
@ -731,7 +688,7 @@ BusNetwork::BusNetwork(const BusConfig &bc)
|
||||
_hasCCT = false;
|
||||
_UDPchannels = _hasWhite + 3;
|
||||
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
|
||||
_data = (uint8_t*)calloc(_len, _UDPchannels);
|
||||
_data = (uint8_t*)d_calloc(_len, _UDPchannels);
|
||||
_valid = (_data != nullptr);
|
||||
DEBUGBUS_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]);
|
||||
}
|
||||
@ -760,12 +717,12 @@ void BusNetwork::show() {
|
||||
_broadcastLock = false;
|
||||
}
|
||||
|
||||
unsigned BusNetwork::getPins(uint8_t* pinArray) const {
|
||||
size_t BusNetwork::getPins(uint8_t* pinArray) const {
|
||||
if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
|
||||
return 4;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled/WLED/pull/4056
|
||||
std::vector<LEDType> BusNetwork::getLEDTypes() {
|
||||
return {
|
||||
{TYPE_NET_DDP_RGB, "N", PSTR("DDP RGB (network)")}, // should be "NNNN" to determine 4 "pin" fields
|
||||
@ -776,13 +733,13 @@ std::vector<LEDType> BusNetwork::getLEDTypes() {
|
||||
//{TYPE_VIRTUAL_I2C_W, "V", PSTR("I2C White (virtual)")}, // allows setting I2C address in _pin[0]
|
||||
//{TYPE_VIRTUAL_I2C_CCT, "V", PSTR("I2C CCT (virtual)")}, // allows setting I2C address in _pin[0]
|
||||
//{TYPE_VIRTUAL_I2C_RGB, "VVV", PSTR("I2C RGB (virtual)")}, // allows setting I2C address in _pin[0] and 2 additional values in _pin[1] & _pin[2]
|
||||
//{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/wled-dev/WLED/pull/4123)
|
||||
//{TYPE_USERMOD, "VVVVV", PSTR("Usermod (virtual)")}, // 5 data fields (see https://github.com/wled/WLED/pull/4123)
|
||||
};
|
||||
}
|
||||
|
||||
void BusNetwork::cleanup() {
|
||||
DEBUGBUS_PRINTLN(F("Virtual Cleanup."));
|
||||
free(_data);
|
||||
d_free(_data);
|
||||
_data = nullptr;
|
||||
_type = I_NONE;
|
||||
_valid = false;
|
||||
@ -790,11 +747,11 @@ void BusNetwork::cleanup() {
|
||||
|
||||
|
||||
//utility to get the approx. memory usage of a given BusConfig
|
||||
unsigned BusConfig::memUsage(unsigned nr) const {
|
||||
size_t BusConfig::memUsage(unsigned nr) const {
|
||||
if (Bus::isVirtual(type)) {
|
||||
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
|
||||
} else if (Bus::isDigital(type)) {
|
||||
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type);
|
||||
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) /*+ doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type)*/;
|
||||
} else if (Bus::isOnOff(type)) {
|
||||
return sizeof(BusOnOff);
|
||||
} else {
|
||||
@ -803,7 +760,7 @@ unsigned BusConfig::memUsage(unsigned nr) const {
|
||||
}
|
||||
|
||||
|
||||
unsigned BusManager::memUsage() {
|
||||
size_t BusManager::memUsage() {
|
||||
// when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers
|
||||
// front buffers are always allocated per bus
|
||||
unsigned size = 0;
|
||||
@ -832,22 +789,24 @@ unsigned BusManager::memUsage() {
|
||||
}
|
||||
|
||||
int BusManager::add(const BusConfig &bc) {
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus (%d - %d >= %d)\n"), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES);
|
||||
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
|
||||
unsigned numDigital = 0;
|
||||
for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++;
|
||||
DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus (p:%d v:%d)\n"), getNumBusses(), getNumVirtualBusses());
|
||||
unsigned digital = 0;
|
||||
unsigned analog = 0;
|
||||
unsigned twoPin = 0;
|
||||
for (const auto &bus : busses) {
|
||||
if (bus->isPWM()) analog += bus->getPins(); // number of analog channels used
|
||||
if (bus->isDigital() && !bus->is2Pin()) digital++;
|
||||
if (bus->is2Pin()) twoPin++;
|
||||
}
|
||||
if (digital > WLED_MAX_DIGITAL_CHANNELS || analog > WLED_MAX_ANALOG_CHANNELS) return -1;
|
||||
if (Bus::isVirtual(bc.type)) {
|
||||
busses.push_back(make_unique<BusNetwork>(bc));
|
||||
//busses.push_back(new BusNetwork(bc));
|
||||
} else if (Bus::isDigital(bc.type)) {
|
||||
busses.push_back(make_unique<BusDigital>(bc, numDigital));
|
||||
//busses.push_back(new BusDigital(bc, numDigital));
|
||||
busses.push_back(make_unique<BusDigital>(bc, Bus::is2Pin(bc.type) ? twoPin : digital));
|
||||
} else if (Bus::isOnOff(bc.type)) {
|
||||
busses.push_back(make_unique<BusOnOff>(bc));
|
||||
//busses.push_back(new BusOnOff(bc));
|
||||
} else {
|
||||
busses.push_back(make_unique<BusPwm>(bc));
|
||||
//busses.push_back(new BusPwm(bc));
|
||||
}
|
||||
return busses.size();
|
||||
}
|
||||
@ -865,7 +824,7 @@ static String LEDTypesToJson(const std::vector<LEDType>& types) {
|
||||
return json;
|
||||
}
|
||||
|
||||
// credit @willmmiles & @netmindz https://github.com/wled-dev/WLED/pull/4056
|
||||
// credit @willmmiles & @netmindz https://github.com/wled/WLED/pull/4056
|
||||
String BusManager::getLEDTypesJSONString() {
|
||||
String json = "[";
|
||||
json += LEDTypesToJson(BusDigital::getLEDTypes());
|
||||
@ -891,7 +850,6 @@ void BusManager::removeAll() {
|
||||
DEBUGBUS_PRINTLN(F("Removing all."));
|
||||
//prevents crashes due to deleting busses while in use.
|
||||
while (!canAllShow()) yield();
|
||||
//for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11
|
||||
busses.clear();
|
||||
PolyBus::setParallelI2S1Output(false);
|
||||
}
|
||||
@ -980,9 +938,8 @@ void BusManager::show() {
|
||||
|
||||
void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) {
|
||||
for (auto &bus : busses) {
|
||||
unsigned bstart = bus->getStart();
|
||||
if (pix < bstart || pix >= bstart + bus->getLength()) continue;
|
||||
bus->setPixelColor(pix - bstart, c);
|
||||
if (!bus->containsPixel(pix)) continue;
|
||||
bus->setPixelColor(pix - bus->getStart(), c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -997,9 +954,8 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) {
|
||||
|
||||
uint32_t BusManager::getPixelColor(unsigned pix) {
|
||||
for (auto &bus : busses) {
|
||||
unsigned bstart = bus->getStart();
|
||||
if (!bus->containsPixel(pix)) continue;
|
||||
return bus->getPixelColor(pix - bstart);
|
||||
return bus->getPixelColor(pix - bus->getStart());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1022,6 +978,5 @@ uint8_t Bus::_gAWM = 255;
|
||||
uint16_t BusDigital::_milliAmpsTotal = 0;
|
||||
|
||||
std::vector<std::unique_ptr<Bus>> BusManager::busses;
|
||||
//std::vector<Bus*> BusManager::busses;
|
||||
uint16_t BusManager::_gMilliAmpsUsed = 0;
|
||||
uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT;
|
||||
|
@ -114,17 +114,17 @@ class Bus {
|
||||
_autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY;
|
||||
};
|
||||
|
||||
virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData())
|
||||
virtual ~Bus() {} //throw the bus under the bus
|
||||
|
||||
virtual void begin() {};
|
||||
virtual void show() = 0;
|
||||
virtual void show() = 0;
|
||||
virtual bool canShow() const { return true; }
|
||||
virtual void setStatusPixel(uint32_t c) {}
|
||||
virtual void setPixelColor(unsigned pix, uint32_t c) = 0;
|
||||
virtual void setPixelColor(unsigned pix, uint32_t c) = 0;
|
||||
virtual void setBrightness(uint8_t b) { _bri = b; };
|
||||
virtual void setColorOrder(uint8_t co) {}
|
||||
virtual uint32_t getPixelColor(unsigned pix) const { return 0; }
|
||||
virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; }
|
||||
virtual size_t getPins(uint8_t* pinArray = nullptr) const { return 0; }
|
||||
virtual uint16_t getLength() const { return isOk() ? _len : 0; }
|
||||
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
|
||||
virtual unsigned skippedLeds() const { return 0; }
|
||||
@ -132,7 +132,7 @@ class Bus {
|
||||
virtual uint16_t getLEDCurrent() const { return 0; }
|
||||
virtual uint16_t getUsedCurrent() const { return 0; }
|
||||
virtual uint16_t getMaxCurrent() const { return 0; }
|
||||
virtual unsigned getBusSize() const { return sizeof(Bus); }
|
||||
virtual size_t getBusSize() const { return sizeof(Bus); }
|
||||
|
||||
inline bool hasRGB() const { return _hasRgb; }
|
||||
inline bool hasWhite() const { return _hasWhite; }
|
||||
@ -148,7 +148,7 @@ class Bus {
|
||||
inline void setStart(uint16_t start) { _start = start; }
|
||||
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
|
||||
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
|
||||
inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
|
||||
inline size_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
|
||||
inline uint16_t getStart() const { return _start; }
|
||||
inline uint8_t getType() const { return _type; }
|
||||
inline bool isOk() const { return _valid; }
|
||||
@ -157,8 +157,8 @@ class Bus {
|
||||
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
|
||||
|
||||
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
|
||||
static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
|
||||
static constexpr size_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
|
||||
static constexpr size_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
|
||||
static constexpr bool hasRGB(uint8_t type) {
|
||||
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
|
||||
}
|
||||
@ -243,13 +243,13 @@ class BusDigital : public Bus {
|
||||
void setColorOrder(uint8_t colorOrder) override;
|
||||
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
|
||||
uint8_t getColorOrder() const override { return _colorOrder; }
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
size_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
unsigned skippedLeds() const override { return _skip; }
|
||||
uint16_t getFrequency() const override { return _frequencykHz; }
|
||||
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
|
||||
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
|
||||
uint16_t getMaxCurrent() const override { return _milliAmpsMax; }
|
||||
unsigned getBusSize() const override;
|
||||
size_t getBusSize() const override;
|
||||
void begin() override;
|
||||
void cleanup();
|
||||
|
||||
@ -263,7 +263,6 @@ class BusDigital : public Bus {
|
||||
uint16_t _frequencykHz;
|
||||
uint8_t _milliAmpsPerLed;
|
||||
uint16_t _milliAmpsMax;
|
||||
uint8_t *_data;
|
||||
void *_busPtr;
|
||||
|
||||
static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show()
|
||||
@ -290,9 +289,9 @@ class BusPwm : public Bus {
|
||||
|
||||
void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
uint32_t getPixelColor(unsigned pix) const override; //does no index check
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
size_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
uint16_t getFrequency() const override { return _frequency; }
|
||||
unsigned getBusSize() const override { return sizeof(BusPwm); }
|
||||
size_t getBusSize() const override { return sizeof(BusPwm); }
|
||||
void show() override;
|
||||
inline void cleanup() { deallocatePins(); }
|
||||
|
||||
@ -318,8 +317,8 @@ class BusOnOff : public Bus {
|
||||
|
||||
void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
uint32_t getPixelColor(unsigned pix) const override;
|
||||
unsigned getPins(uint8_t* pinArray) const override;
|
||||
unsigned getBusSize() const override { return sizeof(BusOnOff); }
|
||||
size_t getPins(uint8_t* pinArray) const override;
|
||||
size_t getBusSize() const override { return sizeof(BusOnOff); }
|
||||
void show() override;
|
||||
inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); }
|
||||
|
||||
@ -339,10 +338,10 @@ class BusNetwork : public Bus {
|
||||
bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out
|
||||
[[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override;
|
||||
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
|
||||
unsigned getPins(uint8_t* pinArray = nullptr) const override;
|
||||
unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); }
|
||||
void show() override;
|
||||
void cleanup();
|
||||
size_t getPins(uint8_t* pinArray = nullptr) const override;
|
||||
size_t getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); }
|
||||
void show() override;
|
||||
void cleanup();
|
||||
|
||||
static std::vector<LEDType> getLEDTypes();
|
||||
|
||||
@ -367,11 +366,10 @@ struct BusConfig {
|
||||
uint8_t autoWhite;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
uint16_t frequency;
|
||||
bool doubleBuffer;
|
||||
uint8_t milliAmpsPerLed;
|
||||
uint16_t milliAmpsMax;
|
||||
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
|
||||
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT)
|
||||
: count(std::max(len,(uint16_t)1))
|
||||
, start(pstart)
|
||||
, colorOrder(pcolorOrder)
|
||||
@ -379,7 +377,6 @@ struct BusConfig {
|
||||
, skipAmount(skip)
|
||||
, autoWhite(aw)
|
||||
, frequency(clock_kHz)
|
||||
, doubleBuffer(dblBfr)
|
||||
, milliAmpsPerLed(maPerLed)
|
||||
, milliAmpsMax(maMax)
|
||||
{
|
||||
@ -411,7 +408,7 @@ struct BusConfig {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned memUsage(unsigned nr = 0) const;
|
||||
size_t memUsage(unsigned nr = 0) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -74,7 +74,7 @@ void doublePressAction(uint8_t b)
|
||||
if (!macroDoublePress[b]) {
|
||||
switch (b) {
|
||||
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
||||
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
case 1: ++effectPalette %= getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
|
||||
@ -226,8 +226,8 @@ void handleAnalog(uint8_t b)
|
||||
effectIntensity = aRead;
|
||||
} else if (macroDoublePress[b] == 247) {
|
||||
// selected palette
|
||||
effectPalette = map(aRead, 0, 252, 0, strip.getPaletteCount()-1);
|
||||
effectPalette = constrain(effectPalette, 0, strip.getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||
effectPalette = map(aRead, 0, 252, 0, getPaletteCount()-1);
|
||||
effectPalette = constrain(effectPalette, 0, getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||
} else if (macroDoublePress[b] == 200) {
|
||||
// primary color, hue, full saturation
|
||||
colorHStoRGB(aRead*256,255,colPri);
|
||||
|
229
wled00/cfg.cpp
229
wled00/cfg.cpp
@ -6,6 +6,35 @@
|
||||
* The structure of the JSON is not to be considered an official API and may change without notice.
|
||||
*/
|
||||
|
||||
#ifndef PIXEL_COUNTS
|
||||
#define PIXEL_COUNTS DEFAULT_LED_COUNT
|
||||
#endif
|
||||
|
||||
#ifndef DATA_PINS
|
||||
#define DATA_PINS DEFAULT_LED_PIN
|
||||
#endif
|
||||
|
||||
#ifndef LED_TYPES
|
||||
#define LED_TYPES DEFAULT_LED_TYPE
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_LED_COLOR_ORDER
|
||||
#define DEFAULT_LED_COLOR_ORDER COL_ORDER_GRB //default to GRB
|
||||
#endif
|
||||
|
||||
static constexpr unsigned sumPinsRequired(const unsigned* current, size_t count) {
|
||||
return (count > 0) ? (Bus::getNumberOfPins(*current) + sumPinsRequired(current+1,count-1)) : 0;
|
||||
}
|
||||
|
||||
static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTypes, unsigned numPins ) {
|
||||
// Pins provided < pins required -> always invalid
|
||||
// Pins provided = pins required -> always valid
|
||||
// Pins provided > pins required -> valid if excess pins are a product of last type pins since it will be repeated
|
||||
return (sumPinsRequired(types, numTypes) > numPins) ? false :
|
||||
(numPins - sumPinsRequired(types, numTypes)) % Bus::getNumberOfPins(types[numTypes-1]) == 0;
|
||||
}
|
||||
|
||||
|
||||
//simple macro for ArduinoJSON's or syntax
|
||||
#define CJSON(a,b) a = b | a
|
||||
|
||||
@ -20,7 +49,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
// NOTE: Ethernet configuration takes priority over other use of pins
|
||||
@ -136,7 +165,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend();
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
CJSON(useParallelI2S, hw_led[F("prl")]);
|
||||
#endif
|
||||
@ -146,12 +174,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject matrix = hw_led[F("matrix")];
|
||||
if (!matrix.isNull()) {
|
||||
strip.isMatrix = true;
|
||||
CJSON(strip.panels, matrix[F("mpc")]);
|
||||
unsigned numPanels = matrix[F("mpc")] | 1;
|
||||
numPanels = constrain(numPanels, 1, WLED_MAX_PANELS);
|
||||
strip.panel.clear();
|
||||
JsonArray panels = matrix[F("panels")];
|
||||
int s = 0;
|
||||
unsigned s = 0;
|
||||
if (!panels.isNull()) {
|
||||
strip.panel.reserve(max(1U,min((size_t)strip.panels,(size_t)WLED_MAX_PANELS))); // pre-allocate memory for panels
|
||||
strip.panel.reserve(numPanels); // pre-allocate default 8x8 panels
|
||||
for (JsonObject pnl : panels) {
|
||||
WS2812FX::Panel p;
|
||||
CJSON(p.bottomStart, pnl["b"]);
|
||||
@ -163,30 +192,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(p.height, pnl["h"]);
|
||||
CJSON(p.width, pnl["w"]);
|
||||
strip.panel.push_back(p);
|
||||
if (++s >= WLED_MAX_PANELS || s >= strip.panels) break; // max panels reached
|
||||
if (++s >= numPanels) break; // max panels reached
|
||||
}
|
||||
} else {
|
||||
// fallback
|
||||
WS2812FX::Panel p;
|
||||
strip.panels = 1;
|
||||
p.height = p.width = 8;
|
||||
p.xOffset = p.yOffset = 0;
|
||||
p.options = 0;
|
||||
strip.panel.push_back(p);
|
||||
}
|
||||
// cannot call strip.setUpMatrix() here due to already locked JSON buffer
|
||||
strip.panel.shrink_to_fit(); // release unused memory (just in case)
|
||||
// cannot call strip.deserializeLedmap()/strip.setUpMatrix() here due to already locked JSON buffer
|
||||
//if (!fromFS) doInit2D = true; // if called at boot (fromFS==true), WLED::beginStrip() will take care of setting up matrix
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
if (fromFS || !ins.isNull()) {
|
||||
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
|
||||
if (!ins.isNull()) {
|
||||
int s = 0; // bus iterator
|
||||
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
|
||||
|
||||
for (JsonObject elm : ins) {
|
||||
if (s >= WLED_MAX_BUSSES) break;
|
||||
if (s >= WLED_MAX_BUSSES) break; // only counts physical buses
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
JsonArray pinArr = elm["pin"];
|
||||
if (pinArr.size() == 0) continue;
|
||||
@ -215,11 +235,101 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
|
||||
//busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax)));
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax);
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
|
||||
}
|
||||
} else if (fromFS) {
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
BusManager::removeAll();
|
||||
busConfigs.clear();
|
||||
|
||||
DEBUG_PRINTLN(F("No busses, init default"));
|
||||
constexpr unsigned defDataTypes[] = {LED_TYPES};
|
||||
constexpr unsigned defDataPins[] = {DATA_PINS};
|
||||
constexpr unsigned defCounts[] = {PIXEL_COUNTS};
|
||||
constexpr unsigned defNumTypes = (sizeof(defDataTypes) / sizeof(defDataTypes[0]));
|
||||
constexpr unsigned defNumPins = (sizeof(defDataPins) / sizeof(defDataPins[0]));
|
||||
constexpr unsigned defNumCounts = (sizeof(defCounts) / sizeof(defCounts[0]));
|
||||
|
||||
static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins),
|
||||
"The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES");
|
||||
|
||||
unsigned mem = 0;
|
||||
unsigned pinsIndex = 0;
|
||||
unsigned digitalCount = 0;
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES; i++) {
|
||||
uint8_t defPin[OUTPUT_MAX_PINS];
|
||||
// if we have less types than requested outputs and they do not align, use last known type to set current type
|
||||
unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1];
|
||||
unsigned busPins = Bus::getNumberOfPins(dataType);
|
||||
|
||||
// if we need more pins than available all outputs have been configured
|
||||
if (pinsIndex + busPins > defNumPins) break;
|
||||
|
||||
// Assign all pins first so we can check for conflicts on this bus
|
||||
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j];
|
||||
|
||||
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) {
|
||||
bool validPin = true;
|
||||
// When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
|
||||
// i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc.
|
||||
// Pin should not be already allocated, read/only or defined for current bus
|
||||
while (PinManager::isPinAllocated(defPin[j]) || !PinManager::isPinOk(defPin[j],true)) {
|
||||
if (validPin) {
|
||||
DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output."));
|
||||
defPin[j] = 1; // start with GPIO1 and work upwards
|
||||
validPin = false;
|
||||
} else if (defPin[j] < WLED_NUM_PINS) {
|
||||
defPin[j]++;
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No available pins left! Can't configure output."));
|
||||
break;
|
||||
}
|
||||
// is the newly assigned pin already defined or used previously?
|
||||
// try next in line until there are no clashes or we run out of pins
|
||||
bool clash;
|
||||
do {
|
||||
clash = false;
|
||||
// check for conflicts on current bus
|
||||
for (const auto &pin : defPin) {
|
||||
if (&pin != &defPin[j] && pin == defPin[j]) {
|
||||
clash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We already have a clash on current bus, no point checking next buses
|
||||
if (!clash) {
|
||||
// check for conflicts in defined pins
|
||||
for (const auto &pin : defDataPins) {
|
||||
if (pin == defPin[j]) {
|
||||
clash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clash) defPin[j]++;
|
||||
if (defPin[j] >= WLED_NUM_PINS) break;
|
||||
} while (clash);
|
||||
}
|
||||
}
|
||||
pinsIndex += busPins;
|
||||
|
||||
// if we have less counts than pins and they do not align, use last known count to set current count
|
||||
unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
unsigned start = 0;
|
||||
// analog always has length 1
|
||||
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
|
||||
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0);
|
||||
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
|
||||
if (mem > MAX_LED_MEMORY) {
|
||||
DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)dataType, (int)count, digitalCount);
|
||||
break;
|
||||
}
|
||||
busConfigs.push_back(defCfg); // use push_back for simplification as we needed defCfg to calculate memory usage
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());
|
||||
}
|
||||
if (hw_led["rev"] && BusManager::getNumBusses()) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
|
||||
|
||||
@ -308,30 +418,28 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
}
|
||||
} else {
|
||||
} else if (fromFS) {
|
||||
// new install/missing configuration (button 0 has defaults)
|
||||
if (fromFS) {
|
||||
// relies upon only being called once with fromFS == true, which is currently true.
|
||||
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
|
||||
if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
|
||||
btnPin[s] = -1;
|
||||
buttonType[s] = BTN_TYPE_NONE;
|
||||
}
|
||||
if (btnPin[s] >= 0) {
|
||||
if (disablePullUp) {
|
||||
pinMode(btnPin[s], INPUT);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
macroButton[s] = 0;
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
// relies upon only being called once with fromFS == true, which is currently true.
|
||||
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
|
||||
if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
|
||||
btnPin[s] = -1;
|
||||
buttonType[s] = BTN_TYPE_NONE;
|
||||
}
|
||||
if (btnPin[s] >= 0) {
|
||||
if (disablePullUp) {
|
||||
pinMode(btnPin[s], INPUT);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
macroButton[s] = 0;
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,8 +516,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
JsonObject light = doc[F("light")];
|
||||
CJSON(briMultiplier, light[F("scale-bri")]);
|
||||
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
||||
CJSON(paletteBlend, light[F("pal-mode")]);
|
||||
CJSON(strip.autoSegments, light[F("aseg")]);
|
||||
CJSON(useRainbowWheel, light[F("rw")]);
|
||||
|
||||
CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.8
|
||||
float light_gc_bri = light["gc"]["bri"];
|
||||
@ -666,11 +775,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
static const char s_cfg_json[] PROGMEM = "/cfg.json";
|
||||
|
||||
void deserializeConfigFromFS() {
|
||||
bool success = deserializeConfigSec();
|
||||
[[maybe_unused]] bool success = deserializeConfigSec();
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
if (!success) { //if file does not exist, try reading from EEPROM
|
||||
deEEPSettings();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -679,23 +787,6 @@ void deserializeConfigFromFS() {
|
||||
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
|
||||
|
||||
success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
|
||||
if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS
|
||||
releaseJSONBufferLock();
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
deEEPSettings();
|
||||
#endif
|
||||
|
||||
// save default values to /cfg.json
|
||||
// call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving
|
||||
JsonObject empty = JsonObject();
|
||||
UsermodManager::readFromConfig(empty);
|
||||
serializeConfigToFS();
|
||||
// init Ethernet (in case default type is set at compile time)
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
initEthernet();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: This routine deserializes *and* applies the configuration
|
||||
// Therefore, must also initialize ethernet from this function
|
||||
@ -821,14 +912,13 @@ void serializeConfig(JsonObject root) {
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL
|
||||
hw_led[F("maxpwr")] = BusManager::ablMilliampsMax();
|
||||
hw_led[F("ledma")] = 0; // no longer used
|
||||
// hw_led[F("ledma")] = 0; // no longer used
|
||||
hw_led["cct"] = strip.correctWB;
|
||||
hw_led[F("cr")] = strip.cctFromRgb;
|
||||
hw_led[F("ic")] = cctICused;
|
||||
hw_led[F("cb")] = Bus::getCCTBlend();
|
||||
hw_led["fps"] = strip.getTargetFps();
|
||||
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
|
||||
hw_led[F("ld")] = useGlobalLedBuffer;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
hw_led[F("prl")] = BusManager::hasParallelOutput();
|
||||
#endif
|
||||
@ -837,7 +927,7 @@ void serializeConfig(JsonObject root) {
|
||||
// 2D Matrix Settings
|
||||
if (strip.isMatrix) {
|
||||
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
|
||||
matrix[F("mpc")] = strip.panels;
|
||||
matrix[F("mpc")] = strip.panel.size();
|
||||
JsonArray panels = matrix.createNestedArray(F("panels"));
|
||||
for (size_t i = 0; i < strip.panel.size(); i++) {
|
||||
JsonObject pnl = panels.createNestedObject();
|
||||
@ -947,8 +1037,9 @@ void serializeConfig(JsonObject root) {
|
||||
|
||||
JsonObject light = root.createNestedObject(F("light"));
|
||||
light[F("scale-bri")] = briMultiplier;
|
||||
light[F("pal-mode")] = strip.paletteBlend;
|
||||
light[F("pal-mode")] = paletteBlend;
|
||||
light[F("aseg")] = strip.autoSegments;
|
||||
light[F("rw")] = useRainbowWheel;
|
||||
|
||||
JsonObject light_gc = light.createNestedObject("gc");
|
||||
light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility
|
||||
|
@ -208,14 +208,14 @@ CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette)
|
||||
makepastelpalette = true;
|
||||
}
|
||||
|
||||
// apply saturation & gamma correction
|
||||
// apply saturation
|
||||
CRGB RGBpalettecolors[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (makepastelpalette && palettecolors[i].saturation > 180) {
|
||||
palettecolors[i].saturation -= 160; //desaturate all four colors
|
||||
}
|
||||
RGBpalettecolors[i] = (CRGB)palettecolors[i]; //convert to RGB
|
||||
RGBpalettecolors[i] = gamma32(((uint32_t)RGBpalettecolors[i]) & 0x00FFFFFFU); //strip alpha from CRGB
|
||||
RGBpalettecolors[i] = ((uint32_t)RGBpalettecolors[i]) & 0x00FFFFFFU; //strip alpha from CRGB
|
||||
}
|
||||
|
||||
return CRGBPalette16(RGBpalettecolors[0],
|
||||
@ -232,6 +232,54 @@ CRGBPalette16 generateRandomPalette() // generate fully random palette
|
||||
CHSV(hw_random8(), hw_random8(160, 255), hw_random8(128, 255)));
|
||||
}
|
||||
|
||||
void loadCustomPalettes() {
|
||||
byte tcp[72]; //support gradient palettes with up to 18 entries
|
||||
CRGBPalette16 targetPalette;
|
||||
customPalettes.clear(); // start fresh
|
||||
for (int index = 0; index<10; index++) {
|
||||
char fileName[32];
|
||||
sprintf_P(fileName, PSTR("/palette%d.json"), index);
|
||||
|
||||
StaticJsonDocument<1536> pDoc; // barely enough to fit 72 numbers
|
||||
if (WLED_FS.exists(fileName)) {
|
||||
DEBUGFX_PRINTF_P(PSTR("Reading palette from %s\n"), fileName);
|
||||
if (readObjectFromFile(fileName, nullptr, &pDoc)) {
|
||||
JsonArray pal = pDoc[F("palette")];
|
||||
if (!pal.isNull() && pal.size()>3) { // not an empty palette (at least 2 entries)
|
||||
memset(tcp, 255, sizeof(tcp));
|
||||
if (pal[0].is<int>() && pal[1].is<const char *>()) {
|
||||
// we have an array of index & hex strings
|
||||
size_t palSize = MIN(pal.size(), 36);
|
||||
palSize -= palSize % 2; // make sure size is multiple of 2
|
||||
for (size_t i=0, j=0; i<palSize && pal[i].as<int>()<256; i+=2) {
|
||||
uint8_t rgbw[] = {0,0,0,0};
|
||||
if (colorFromHexString(rgbw, pal[i+1].as<const char *>())) { // will catch non-string entires
|
||||
tcp[ j ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||
for (size_t c=0; c<3; c++) tcp[j+1+c] = rgbw[c]; // only use RGB component
|
||||
DEBUGFX_PRINTF_P(PSTR("%2u -> %3d [%3d,%3d,%3d]\n"), i, int(tcp[j]), int(tcp[j+1]), int(tcp[j+2]), int(tcp[j+3]));
|
||||
j += 4;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t palSize = MIN(pal.size(), 72);
|
||||
palSize -= palSize % 4; // make sure size is multiple of 4
|
||||
for (size_t i=0; i<palSize && pal[i].as<int>()<256; i+=4) {
|
||||
tcp[ i ] = (uint8_t) pal[ i ].as<int>(); // index
|
||||
for (size_t c=0; c<3; c++) tcp[i+1+c] = (uint8_t) pal[i+1+c].as<int>();
|
||||
DEBUGFX_PRINTF_P(PSTR("%2u -> %3d [%3d,%3d,%3d]\n"), i, int(tcp[i]), int(tcp[i+1]), int(tcp[i+2]), int(tcp[i+3]));
|
||||
}
|
||||
}
|
||||
customPalettes.push_back(targetPalette.loadDynamicGradientPalette(tcp));
|
||||
} else {
|
||||
DEBUGFX_PRINTLN(F("Wrong palette format."));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb) // convert HSV (16bit hue) to RGB (32bit with white = 0)
|
||||
{
|
||||
unsigned int remainder, region, p, q, t;
|
||||
|
144
wled00/const.h
144
wled00/const.h
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#ifndef WLED_CONST_H
|
||||
#define WLED_CONST_H
|
||||
|
||||
@ -44,66 +45,51 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef WLED_MAX_BUSSES
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 3
|
||||
#define WLED_MAX_ANALOG_CHANNELS 5
|
||||
#define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
|
||||
#define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 2
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 6
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
|
||||
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
|
||||
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 5
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
|
||||
#define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
|
||||
#define WLED_MAX_BUSSES 19 // will allow 16 digital & 3 analog RGB
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 16
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 3
|
||||
#define WLED_MAX_ANALOG_CHANNELS 5
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
#ifdef ESP8266
|
||||
#if WLED_MAX_BUSSES > 5
|
||||
#error Maximum number of buses is 5.
|
||||
#endif
|
||||
#ifndef WLED_MAX_ANALOG_CHANNELS
|
||||
#error You must also define WLED_MAX_ANALOG_CHANNELS.
|
||||
#endif
|
||||
#ifndef WLED_MAX_DIGITAL_CHANNELS
|
||||
#error You must also define WLED_MAX_DIGITAL_CHANNELS.
|
||||
#endif
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 3
|
||||
#else
|
||||
#if WLED_MAX_BUSSES > 20
|
||||
#error Maximum number of buses is 20.
|
||||
#endif
|
||||
#ifndef WLED_MAX_ANALOG_CHANNELS
|
||||
#error You must also define WLED_MAX_ANALOG_CHANNELS.
|
||||
#endif
|
||||
#ifndef WLED_MAX_DIGITAL_CHANNELS
|
||||
#error You must also define WLED_MAX_DIGITAL_CHANNELS.
|
||||
#endif
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4
|
||||
#else
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6
|
||||
#endif
|
||||
#if !defined(LEDC_CHANNEL_MAX) || !defined(LEDC_SPEED_MODE_MAX)
|
||||
#include "driver/ledc.h" // needed for analog/LEDC channel counts
|
||||
#endif
|
||||
#define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 2
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 6
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB
|
||||
// the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 8
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#else
|
||||
// the last digital bus (I2S0) will prevent Audioreactive usermod from functioning
|
||||
#define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT
|
||||
//#define WLED_MAX_ANALOG_CHANNELS 16
|
||||
#define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI
|
||||
#endif
|
||||
#endif
|
||||
// WLED_MAX_BUSSES was used to define the size of busses[] array which is no longer needed
|
||||
// instead it will help determine max number of buses that can be defined at compile time
|
||||
#ifdef WLED_MAX_BUSSES
|
||||
#undef WLED_MAX_BUSSES
|
||||
#endif
|
||||
#define WLED_MAX_BUSSES (WLED_MAX_DIGITAL_CHANNELS+WLED_MAX_ANALOG_CHANNELS)
|
||||
static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
|
||||
|
||||
// Maximum number of pins per output. 5 for RGBCCT analog LEDs.
|
||||
#define OUTPUT_MAX_PINS 5
|
||||
|
||||
// for pin manager
|
||||
#ifdef ESP8266
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17)
|
||||
#else
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT)
|
||||
#endif
|
||||
|
||||
#ifndef WLED_MAX_BUTTONS
|
||||
@ -151,6 +137,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define WLED_MAX_PANELS 18 // must not be more than 32
|
||||
|
||||
//Usermod IDs
|
||||
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
||||
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
||||
@ -336,18 +324,6 @@
|
||||
#define TYPE_NET_ARTNET_RGBW 89 //network ArtNet RGB bus (master broadcast bus, unused)
|
||||
#define TYPE_VIRTUAL_MAX 95
|
||||
|
||||
/*
|
||||
// old macros that have been moved to Bus class
|
||||
#define IS_TYPE_VALID(t) ((t) > 15 && (t) < 128)
|
||||
#define IS_DIGITAL(t) (((t) > 15 && (t) < 40) || ((t) > 47 && (t) < 64)) //digital are 16-39 and 48-63
|
||||
#define IS_2PIN(t) ((t) > 47 && (t) < 64)
|
||||
#define IS_16BIT(t) ((t) == TYPE_UCS8903 || (t) == TYPE_UCS8904)
|
||||
#define IS_ONOFF(t) ((t) == 40)
|
||||
#define IS_PWM(t) ((t) > 40 && (t) < 46) //does not include on/Off type
|
||||
#define NUM_PWM_PINS(t) ((t) - 40) //for analog PWM 41-45 only
|
||||
#define IS_VIRTUAL(t) ((t) >= 80 && (t) < 96) //this was a poor choice a better would be 96-111
|
||||
*/
|
||||
|
||||
//Color orders
|
||||
#define COL_ORDER_GRB 0 //GRB(w),defaut
|
||||
#define COL_ORDER_RGB 1 //common for WS2811
|
||||
@ -435,6 +411,7 @@
|
||||
#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_NOT_IMPL 4 // Not implemented
|
||||
#define ERR_NORAM_PX 7 // not enough RAM for pixels
|
||||
#define ERR_NORAM 8 // effect RAM depleted
|
||||
#define ERR_JSON 9 // JSON parsing failed (input too large?)
|
||||
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
|
||||
@ -474,30 +451,29 @@
|
||||
#define NTP_PACKET_SIZE 48 // size of NTP receive buffer
|
||||
#define NTP_MIN_PACKET_SIZE 48 // min expected size - NTP v4 allows for "extended information" appended to the standard fields
|
||||
|
||||
// Maximum number of pins per output. 5 for RGBCCT analog LEDs.
|
||||
#define OUTPUT_MAX_PINS 5
|
||||
|
||||
//maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses
|
||||
#ifndef MAX_LEDS
|
||||
#ifdef ESP8266
|
||||
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define MAX_LEDS 2048 //due to memory constraints
|
||||
#else
|
||||
#define MAX_LEDS 8192
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
#define MAX_LEDS 1536 //can't rely on memory limit to limit this to 1536 LEDs
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
#define MAX_LEDS 2048 //due to memory constraints S2
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define MAX_LEDS 4096
|
||||
#else
|
||||
#define MAX_LEDS 16384
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LED_MEMORY
|
||||
#ifdef ESP8266
|
||||
#define MAX_LED_MEMORY 4000
|
||||
#define MAX_LED_MEMORY 4096
|
||||
#else
|
||||
#if defined(ARDUINO_ARCH_ESP32S2)
|
||||
#define MAX_LED_MEMORY 16000
|
||||
#define MAX_LED_MEMORY 16384
|
||||
#elif defined(ARDUINO_ARCH_ESP32C3)
|
||||
#define MAX_LED_MEMORY 32000
|
||||
#define MAX_LED_MEMORY 32768
|
||||
#else
|
||||
#define MAX_LED_MEMORY 64000
|
||||
#define MAX_LED_MEMORY 65536
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
@ -353,12 +353,12 @@ button {
|
||||
padding: 4px 0 0;
|
||||
}
|
||||
|
||||
#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw,
|
||||
#segutil, #segutil2, #segcont, #putil, #pcont, #pql, #fx, #palw, #bsp,
|
||||
.fnd {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
#putil, #segutil, #segutil2 {
|
||||
#putil, #segutil, #segutil2, #bsp {
|
||||
min-height: 42px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
@ -268,28 +268,28 @@
|
||||
<button class="btn btn-s" id="rsbtn" onclick="rSegs()">Reset segments</button>
|
||||
</div>
|
||||
<p>Transition: <input id="tt" type="number" min="0" max="65.5" step="0.1" value="0.7" onchange="parseFloat(this.value)===0?gId('bsp').classList.add('hide'):gId('bsp').classList.remove('hide');"> s</p>
|
||||
<p id="bsp">Blend:
|
||||
<select id="bs" class="sel-sg" onchange="requestJson({'bs':parseInt(this.value)})">
|
||||
<option value="0">Fade</option>
|
||||
<option value="1">Fairy Dust</option>
|
||||
<option value="2">Swipe right</option>
|
||||
<option value="3">Swipe left</option>
|
||||
<option value="16">Push right</option>
|
||||
<option value="17">Push left</option>
|
||||
<option value="4">Pinch-out</option>
|
||||
<option value="5">Inside-out</option>
|
||||
<option value="6" data-type="2D">Swipe up</option>
|
||||
<option value="7" data-type="2D">Swipe down</option>
|
||||
<option value="8" data-type="2D">Open H</option>
|
||||
<option value="9" data-type="2D">Open V</option>
|
||||
<option value="18" data-type="2D">Push up</option>
|
||||
<option value="19" data-type="2D">Push down</option>
|
||||
<option value="20" data-type="2D">Push TL</option>
|
||||
<option value="21" data-type="2D">Push TR</option>
|
||||
<option value="22" data-type="2D">Push BR</option>
|
||||
<option value="23" data-type="2D">Push BL</option>
|
||||
</select>
|
||||
</p>
|
||||
<div id="bsp" class="sel-p"><select id="bs" class="sel-ple" onchange="requestJson({'bs':parseInt(this.value)})">
|
||||
<option value="0">Fade</option>
|
||||
<option value="1">Fairy Dust</option>
|
||||
<option value="2">Swipe right</option>
|
||||
<option value="3">Swipe left</option>
|
||||
<option value="16">Push right</option>
|
||||
<option value="17">Push left</option>
|
||||
<option value="4">Outside-in</option>
|
||||
<option value="5">Inside-out</option>
|
||||
<option value="6" data-type="2D">Swipe up</option>
|
||||
<option value="7" data-type="2D">Swipe down</option>
|
||||
<option value="8" data-type="2D">Open H</option>
|
||||
<option value="9" data-type="2D">Open V</option>
|
||||
<option value="18" data-type="2D">Push up</option>
|
||||
<option value="19" data-type="2D">Push down</option>
|
||||
<option value="10" data-type="2D">Swipe TL</option>
|
||||
<option value="11" data-type="2D">Swipe TR</option>
|
||||
<option value="12" data-type="2D">Swipe BR</option>
|
||||
<option value="13" data-type="2D">Swipe BL</option>
|
||||
<option value="14" data-type="2D">Circular Out</option>
|
||||
<option value="15" data-type="2D">Circular In</option>
|
||||
</select></div>
|
||||
<p id="ledmap" class="hide"></p>
|
||||
</div>
|
||||
|
||||
@ -363,7 +363,7 @@
|
||||
|
||||
<!--
|
||||
If you want to load iro.js and rangetouch.js as consecutive requests, you can do it like it was done in 0.14.0:
|
||||
https://github.com/wled-dev/WLED/blob/v0.14.0/wled00/data/index.htm
|
||||
https://github.com/wled/WLED/blob/v0.14.0/wled00/data/index.htm
|
||||
-->
|
||||
<script src="iro.js"></script>
|
||||
<script src="rangetouch.js"></script>
|
||||
|
@ -35,9 +35,10 @@ var cfg = {
|
||||
// [year, month (0 -> January, 11 -> December), day, duration in days, image url]
|
||||
var hol = [
|
||||
[0, 11, 24, 4, "https://aircoookie.github.io/xmas.png"], // christmas
|
||||
[0, 2, 17, 1, "https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
|
||||
[2025, 3, 20, 2, "https://aircoookie.github.io/easter.png"], // easter 2025
|
||||
[2024, 2, 31, 2, "https://aircoookie.github.io/easter.png"], // easter 2024
|
||||
[0, 2, 17, 1, "https://images.alphacoders.com/491/491123.jpg"], // st. Patrick's day
|
||||
[2026, 3, 5, 2, "https://aircoookie.github.io/easter.png"], // easter 2026
|
||||
[2027, 2, 28, 2, "https://aircoookie.github.io/easter.png"], // easter 2027
|
||||
//[2028, 3, 16, 2, "https://aircoookie.github.io/easter.png"], // easter 2028
|
||||
[0, 6, 4, 1, "https://images.alphacoders.com/516/516792.jpg"], // 4th of July
|
||||
[0, 0, 1, 1, "https://images.alphacoders.com/119/1198800.jpg"] // new year
|
||||
];
|
||||
@ -57,7 +58,7 @@ function handleVisibilityChange() {if (!d.hidden && new Date () - lastUpdate > 3
|
||||
function sCol(na, col) {d.documentElement.style.setProperty(na, col);}
|
||||
function gId(c) {return d.getElementById(c);}
|
||||
function gEBCN(c) {return d.getElementsByClassName(c);}
|
||||
function isEmpty(o) {return Object.keys(o).length === 0;}
|
||||
function isEmpty(o) {for (const i in o) return false; return true;}
|
||||
function isObj(i) {return (i && typeof i === 'object' && !Array.isArray(i));}
|
||||
function isNumeric(n) {return !isNaN(parseFloat(n)) && isFinite(n);}
|
||||
|
||||
@ -805,6 +806,26 @@ function populateSegments(s)
|
||||
`<option value="4" ${inst.m12==4?' selected':''}>Pinwheel</option>`+
|
||||
`</select></div>`+
|
||||
`</div>`;
|
||||
let blend = `<div class="lbl-l">Blend mode<br>`+
|
||||
`<div class="sel-p"><select class="sel-ple" id="seg${i}bm" onchange="setBm(${i})">`+
|
||||
`<option value="0" ${inst.bm==0?' selected':''}>Top/Default</option>`+
|
||||
`<option value="1" ${inst.bm==1?' selected':''}>Bottom/None</option>`+
|
||||
`<option value="2" ${inst.bm==2?' selected':''}>Add</option>`+
|
||||
`<option value="3" ${inst.bm==3?' selected':''}>Subtract</option>`+
|
||||
`<option value="4" ${inst.bm==4?' selected':''}>Difference</option>`+
|
||||
`<option value="5" ${inst.bm==5?' selected':''}>Average</option>`+
|
||||
`<option value="6" ${inst.bm==6?' selected':''}>Multiply</option>`+
|
||||
`<option value="7" ${inst.bm==7?' selected':''}>Divide</option>`+
|
||||
`<option value="8" ${inst.bm==8?' selected':''}>Lighten</option>`+
|
||||
`<option value="9" ${inst.bm==9?' selected':''}>Darken</option>`+
|
||||
`<option value="10" ${inst.bm==10?' selected':''}>Screen</option>`+
|
||||
`<option value="11" ${inst.bm==11?' selected':''}>Overlay</option>`+
|
||||
`<option value="12" ${inst.bm==12?' selected':''}>Hard Light</option>`+
|
||||
`<option value="13" ${inst.bm==13?' selected':''}>Soft Light</option>`+
|
||||
`<option value="14" ${inst.bm==14?' selected':''}>Dodge</option>`+
|
||||
`<option value="15" ${inst.bm==15?' selected':''}>Burn</option>`+
|
||||
`</select></div>`+
|
||||
`</div>`;
|
||||
let sndSim = `<div data-snd="si" class="lbl-s hide">Sound sim<br>`+
|
||||
`<div class="sel-p"><select class="sel-p" id="seg${i}si" onchange="setSi(${i})">`+
|
||||
`<option value="0" ${inst.si==0?' selected':''}>BeatSin</option>`+
|
||||
@ -860,6 +881,7 @@ function populateSegments(s)
|
||||
`</tr>`+
|
||||
`</table>`+
|
||||
`<div class="h bp" id="seg${i}len"></div>`+
|
||||
blend +
|
||||
(!isMSeg ? rvXck : '') +
|
||||
(isMSeg&&stoY-staY>1&&stoX-staX>1 ? map2D : '') +
|
||||
(s.AudioReactive && s.AudioReactive.on ? "" : sndSim) +
|
||||
@ -1420,7 +1442,7 @@ function makeWS() {
|
||||
ws = null;
|
||||
}
|
||||
ws.onopen = (e)=>{
|
||||
//ws.send("{'v':true}"); // unnecessary (https://github.com/wled-dev/WLED/blob/main/wled00/ws.cpp#L18)
|
||||
//ws.send("{'v':true}"); // unnecessary (https://github.com/wled/WLED/blob/master/wled00/ws.cpp#L18)
|
||||
wsRpt = 0;
|
||||
reqsLegal = true;
|
||||
}
|
||||
@ -1448,41 +1470,32 @@ function readState(s,command=false)
|
||||
else gId('bsp').classList.remove('hide')
|
||||
|
||||
populateSegments(s);
|
||||
var selc=0;
|
||||
var sellvl=0; // 0: selc is invalid, 1: selc is mainseg, 2: selc is first selected
|
||||
hasRGB = hasWhite = hasCCT = has2D = false;
|
||||
segLmax = 0;
|
||||
for (let i = 0; i < (s.seg||[]).length; i++)
|
||||
{
|
||||
if (sellvl == 0 && s.seg[i].id == s.mainseg) {
|
||||
selc = i;
|
||||
sellvl = 1;
|
||||
}
|
||||
if (s.seg[i].sel) {
|
||||
if (sellvl < 2) selc = i; // get first selected segment
|
||||
sellvl = 2;
|
||||
let w = (s.seg[i].stop - s.seg[i].start);
|
||||
let h = s.seg[i].stopY ? (s.seg[i].stopY - s.seg[i].startY) : 1;
|
||||
let lc = lastinfo.leds.seglc[i];
|
||||
let i = {};
|
||||
// determine light capabilities from selected segments
|
||||
for (let seg of (s.seg||[])) {
|
||||
let w = (seg.stop - seg.start);
|
||||
let h = seg.stopY ? (seg.stopY - seg.startY) : 1;
|
||||
let lc = seg.lc;
|
||||
if (w*h > segLmax) segLmax = w*h;
|
||||
if (seg.sel) {
|
||||
if (isEmpty(i) || (i.id == s.mainseg && !i.sel)) i = seg; // get first selected segment (and replace mainseg if it is not selected)
|
||||
hasRGB |= !!(lc & 0x01);
|
||||
hasWhite |= !!(lc & 0x02);
|
||||
hasCCT |= !!(lc & 0x04);
|
||||
has2D |= w > 1 && h > 1;
|
||||
if (w*h > segLmax) segLmax = w*h;
|
||||
}
|
||||
} else if (isEmpty(i) && seg.id == s.mainseg) i = seg; // assign mainseg if no segments are selected
|
||||
}
|
||||
var i=s.seg[selc];
|
||||
if (sellvl == 1) {
|
||||
let lc = lastinfo.leds.seglc[selc];
|
||||
hasRGB = !!(lc & 0x01);
|
||||
hasWhite = !!(lc & 0x02);
|
||||
hasCCT = !!(lc & 0x04);
|
||||
has2D = (i.stop - i.start) > 1 && (i.stopY ? (i.stopY - i.startY) : 1) > 1;
|
||||
}
|
||||
if (!i) {
|
||||
showToast('No Segments!', true);
|
||||
if (isEmpty(i)) {
|
||||
showToast('No segments!', true);
|
||||
updateUI();
|
||||
return true;
|
||||
} else if (i.id == s.mainseg) {
|
||||
// fallback if no segments are selected but we have mainseg
|
||||
hasRGB |= !!(i.lc & 0x01);
|
||||
hasWhite |= !!(i.lc & 0x02);
|
||||
hasCCT |= !!(i.lc & 0x04);
|
||||
has2D |= (i.stop - i.start) > 1 && (i.stopY ? (i.stopY - i.startY) : 1) > 1;
|
||||
}
|
||||
|
||||
var cd = gId('csl').querySelectorAll("button");
|
||||
@ -2339,6 +2352,13 @@ function setSi(s)
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setBm(s)
|
||||
{
|
||||
var value = gId(`seg${s}bm`).selectedIndex;
|
||||
var obj = {"seg": {"id": s, "bm": value}};
|
||||
requestJson(obj);
|
||||
}
|
||||
|
||||
function setTp(s)
|
||||
{
|
||||
var tp = gId(`seg${s}tp`).checked;
|
||||
@ -2762,7 +2782,7 @@ setInterval(()=>{
|
||||
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;
|
||||
}, 910);
|
||||
|
||||
function openGH() { window.open("https://github.com/wled-dev/WLED/wiki"); }
|
||||
function openGH() { window.open("https://github.com/wled/WLED/wiki"); }
|
||||
|
||||
var cnfr = false;
|
||||
function cnfReset()
|
||||
@ -3155,7 +3175,8 @@ function mergeDeep(target, ...sources)
|
||||
return mergeDeep(target, ...sources);
|
||||
}
|
||||
|
||||
function tooltip(cont=null) {
|
||||
function tooltip(cont=null)
|
||||
{
|
||||
d.querySelectorAll((cont?cont+" ":"")+"[title]").forEach((element)=>{
|
||||
element.addEventListener("pointerover", ()=>{
|
||||
// save title
|
||||
|
@ -202,7 +202,6 @@
|
||||
if (maxM >= 10000) { //ESP32 RMT uses double buffer?
|
||||
mul = 2;
|
||||
}
|
||||
if (d.Sf.LD.checked) dbl = len * ch; // double buffering
|
||||
}
|
||||
return len * ch * mul + dbl;
|
||||
}
|
||||
@ -326,7 +325,7 @@
|
||||
LC.style.color="#fff";
|
||||
return; // do not check conflicts
|
||||
} else {
|
||||
LC.max = d.max_gpio;
|
||||
LC.max = d.max_gpio-1;
|
||||
LC.min = -1;
|
||||
}
|
||||
}
|
||||
@ -641,7 +640,6 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
d.getElementsByName("MA"+i)[0].value = v.maxpwr;
|
||||
});
|
||||
d.getElementsByName("PR")[0].checked = l.prl | 0;
|
||||
d.getElementsByName("LD")[0].checked = l.ld;
|
||||
d.getElementsByName("MA")[0].value = l.maxpwr;
|
||||
d.getElementsByName("ABL")[0].checked = l.maxpwr > 0;
|
||||
}
|
||||
@ -823,7 +821,6 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
<div id="prl" class="hide">Use parallel I2S: <input type="checkbox" name="PR"><br></div>
|
||||
Make a segment for each output: <input type="checkbox" name="MS"><br>
|
||||
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"><br>
|
||||
Use global LED buffer: <input type="checkbox" name="LD" onchange="UI()"><br>
|
||||
<hr class="sml">
|
||||
<div id="color_order_mapping">
|
||||
Color Order Override:
|
||||
@ -866,7 +863,6 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
<h3>Transitions</h3>
|
||||
Default transition time: <input name="TD" type="number" class="xl" min="0" max="65500"> ms<br>
|
||||
<i>Random Cycle</i> Palette Time: <input name="TP" type="number" class="m" min="1" max="255"> s<br>
|
||||
Use harmonic <i>Random Cycle</i> Palette: <input type="checkbox" name="TH"><br>
|
||||
<h3>Timed light</h3>
|
||||
Default duration: <input name="TL" type="number" class="m" min="1" max="255" required> min<br>
|
||||
Default target brightness: <input name="TB" type="number" class="m" min="0" max="255" required><br>
|
||||
@ -903,8 +899,10 @@ Swap: <select id="xw${s}" name="XW${s}">
|
||||
<option value="2">Linear (never wrap)</option>
|
||||
<option value="3">None (not recommended)</option>
|
||||
</select><br>
|
||||
Use harmonic <i>Random Cycle</i> palette: <input type="checkbox" name="TH"><br>
|
||||
Use "rainbow" color wheel: <input type="checkbox" name="RW"><br>
|
||||
Target refresh rate: <input type="number" class="s" min="0" max="250" name="FR" oninput="UI()" required> FPS
|
||||
<div id="fpsNone" class="warn" style="display: none;">⚠ Unlimited FPS Mode is experimental ⚠<br></div>
|
||||
<div id="fpsNone" class="warn" style="display: none;">⚠ Unlimited FPS Mode is experimental ⚠<br></div>
|
||||
<div id="fpsHigh" class="warn" style="display: none;">⚠ High FPS Mode is experimental.<br></div>
|
||||
<div id="fpsWarn" class="warn" style="display: none;">Please <a class="lnk" href="sec#backup">backup</a> WLED configuration and presets first!<br></div>
|
||||
<hr class="sml">
|
||||
|
@ -38,8 +38,7 @@ void handleDDPPacket(e131_packet_t* p) {
|
||||
if (realtimeMode != REALTIME_MODE_DDP) ddpSeenPush = false; // just starting, no push yet
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
|
||||
|
||||
if (!realtimeOverride || (realtimeMode && useMainSegmentOnly)) {
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
|
||||
if (!realtimeOverride) {
|
||||
for (unsigned i = start; i < stop; i++, c += ddpChannelsPerLed) {
|
||||
setRealtimePixel(i, data[c], data[c+1], data[c+2], ddpChannelsPerLed >3 ? data[c+3] : 0);
|
||||
}
|
||||
@ -150,10 +149,9 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
|
||||
wChannel = (availDMXLen > 3) ? e131_data[dataOffset+3] : 0;
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
|
||||
for (unsigned i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel);
|
||||
break;
|
||||
@ -163,7 +161,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
if (availDMXLen < 4) return;
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
wChannel = (availDMXLen > 4) ? e131_data[dataOffset+4] : 0;
|
||||
|
||||
if (bri != e131_data[dataOffset+0]) {
|
||||
@ -171,7 +169,6 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
strip.setBrightness(bri, true);
|
||||
}
|
||||
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
|
||||
for (unsigned i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel);
|
||||
break;
|
||||
@ -228,16 +225,16 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3];
|
||||
if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]);
|
||||
|
||||
if ((e131_data[dataOffset+5] & 0b00000010) != seg.reverse_y) { seg.setOption(SEG_OPTION_REVERSED_Y, e131_data[dataOffset+5] & 0b00000010); }
|
||||
if ((e131_data[dataOffset+5] & 0b00000100) != seg.mirror_y) { seg.setOption(SEG_OPTION_MIRROR_Y, e131_data[dataOffset+5] & 0b00000100); }
|
||||
if ((e131_data[dataOffset+5] & 0b00001000) != seg.transpose) { seg.setOption(SEG_OPTION_TRANSPOSED, e131_data[dataOffset+5] & 0b00001000); }
|
||||
if ((e131_data[dataOffset+5] & 0b00110000) / 8 != seg.map1D2D) {
|
||||
seg.map1D2D = (e131_data[dataOffset+5] & 0b00110000) / 8;
|
||||
if (bool(e131_data[dataOffset+5] & 0b00000010) != seg.reverse_y) { seg.reverse_y = bool(e131_data[dataOffset+5] & 0b00000010); }
|
||||
if (bool(e131_data[dataOffset+5] & 0b00000100) != seg.mirror_y) { seg.mirror_y = bool(e131_data[dataOffset+5] & 0b00000100); }
|
||||
if (bool(e131_data[dataOffset+5] & 0b00001000) != seg.transpose) { seg.transpose = bool(e131_data[dataOffset+5] & 0b00001000); }
|
||||
if ((e131_data[dataOffset+5] & 0b00110000) >> 4 != seg.map1D2D) {
|
||||
seg.map1D2D = (e131_data[dataOffset+5] & 0b00110000) >> 4;
|
||||
}
|
||||
// To maintain backwards compatibility with prior e1.31 values, reverse is fixed to mask 0x01000000
|
||||
if ((e131_data[dataOffset+5] & 0b01000000) != seg.reverse) { seg.setOption(SEG_OPTION_REVERSED, e131_data[dataOffset+5] & 0b01000000); }
|
||||
if ((e131_data[dataOffset+5] & 0b01000000) != seg.reverse) { seg.reverse = bool(e131_data[dataOffset+5] & 0b01000000); }
|
||||
// To maintain backwards compatibility with prior e1.31 values, mirror is fixed to mask 0x10000000
|
||||
if ((e131_data[dataOffset+5] & 0b10000000) != seg.mirror) { seg.setOption(SEG_OPTION_MIRROR, e131_data[dataOffset+5] & 0b10000000); }
|
||||
if ((e131_data[dataOffset+5] & 0b10000000) != seg.mirror) { seg.mirror = bool(e131_data[dataOffset+5] & 0b10000000); }
|
||||
|
||||
uint32_t colors[3];
|
||||
byte whites[3] = {0,0,0};
|
||||
@ -271,7 +268,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
case DMX_MODE_MULTIPLE_RGB:
|
||||
case DMX_MODE_MULTIPLE_RGBW:
|
||||
{
|
||||
bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW);
|
||||
const bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW);
|
||||
const unsigned dmxChannelsPerLed = is4Chan ? 4 : 3;
|
||||
const unsigned ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE;
|
||||
uint8_t stripBrightness = bri;
|
||||
@ -303,7 +300,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
}
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
|
||||
if (ledsTotal > totalLen) {
|
||||
ledsTotal = totalLen;
|
||||
@ -316,17 +313,9 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
|
||||
}
|
||||
}
|
||||
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw();
|
||||
if (!is4Chan) {
|
||||
for (unsigned i = previousLeds; i < ledsTotal; i++) {
|
||||
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0);
|
||||
dmxOffset+=3;
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = previousLeds; i < ledsTotal; i++) {
|
||||
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], e131_data[dmxOffset+3]);
|
||||
dmxOffset+=4;
|
||||
}
|
||||
for (unsigned i = previousLeds; i < ledsTotal; i++) {
|
||||
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], is4Chan ? e131_data[dmxOffset+3] : 0);
|
||||
dmxOffset += dmxChannelsPerLed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -529,7 +518,7 @@ void sendArtnetPollReply(ArtPollReply *reply, IPAddress ipAddress, uint16_t port
|
||||
reply->reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F);
|
||||
reply->reply_sw_out[0] = (uint8_t)(portAddress & 0x000F);
|
||||
|
||||
snprintf_P((char *)reply->reply_node_report, sizeof(reply->reply_node_report)-1, PSTR("#0001 [%04u] OK - WLED v" TOSTRING(WLED_VERSION)), pollReplyCount);
|
||||
snprintf_P((char *)reply->reply_node_report, sizeof(reply->reply_node_report)-1, PSTR("#0001 [%04u] OK - WLED v%s"), pollReplyCount, versionString);
|
||||
|
||||
if (pollReplyCount < 9999) {
|
||||
pollReplyCount++;
|
||||
|
@ -172,6 +172,9 @@ inline uint32_t color_blend16(uint32_t c1, uint32_t c2, uint16_t b) { return col
|
||||
[[gnu::hot, gnu::pure]] uint32_t ColorFromPaletteWLED(const CRGBPalette16 &pal, unsigned index, uint8_t brightness = (uint8_t)255U, TBlendType blendType = LINEARBLEND);
|
||||
CRGBPalette16 generateHarmonicRandomPalette(const CRGBPalette16 &basepalette);
|
||||
CRGBPalette16 generateRandomPalette();
|
||||
void loadCustomPalettes();
|
||||
extern std::vector<CRGBPalette16> customPalettes;
|
||||
inline size_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT + customPalettes.size(); }
|
||||
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
|
||||
void hsv2rgb(const CHSV32& hsv, uint32_t& rgb);
|
||||
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb);
|
||||
@ -223,9 +226,8 @@ void onHueConnect(void* arg, AsyncClient* client);
|
||||
void sendHuePoll();
|
||||
void onHueData(void* arg, AsyncClient* client, void *data, size_t len);
|
||||
|
||||
#include "FX.h" // must be below colors.cpp declarations (potentially due to duplicate declarations of e.g. color_blend)
|
||||
|
||||
//image_loader.cpp
|
||||
class Segment;
|
||||
#ifdef WLED_ENABLE_GIF
|
||||
bool fileSeekCallback(unsigned long position);
|
||||
unsigned long filePositionCallback(void);
|
||||
@ -261,9 +263,7 @@ void handleIR();
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "src/dependencies/json/ArduinoJson-v6.h"
|
||||
#include "src/dependencies/json/AsyncJson-v6.h"
|
||||
#include "FX.h"
|
||||
|
||||
bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
|
||||
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
|
||||
void serializeSegment(const JsonObject& root, const Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false);
|
||||
@ -277,8 +277,8 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
|
||||
//led.cpp
|
||||
void setValuesFromSegment(uint8_t s);
|
||||
void setValuesFromMainSeg();
|
||||
void setValuesFromFirstSelectedSeg();
|
||||
#define setValuesFromMainSeg() setValuesFromSegment(strip.getMainSegmentId())
|
||||
#define setValuesFromFirstSelectedSeg() setValuesFromSegment(strip.getFirstSelectedSegId())
|
||||
void toggleOnOff();
|
||||
void applyBri();
|
||||
void applyFinalBri();
|
||||
@ -490,11 +490,11 @@ void userLoop();
|
||||
#define inoise8 perlin8 // fastled legacy alias
|
||||
#define inoise16 perlin16 // fastled legacy alias
|
||||
#define hex2int(a) (((a)>='0' && (a)<='9') ? (a)-'0' : ((a)>='A' && (a)<='F') ? (a)-'A'+10 : ((a)>='a' && (a)<='f') ? (a)-'a'+10 : 0)
|
||||
[[gnu::pure]] int getNumVal(const String* req, uint16_t pos);
|
||||
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
|
||||
bool getVal(JsonVariant elem, byte* val, byte vmin=0, byte vmax=255); // getVal supports inc/decrementing and random ("X~Y(r|[w]~[-][Z])" form)
|
||||
[[gnu::pure]] int getNumVal(const String &req, uint16_t pos);
|
||||
void parseNumber(const char* str, byte &val, byte minv=0, byte maxv=255);
|
||||
bool getVal(JsonVariant elem, byte &val, byte vmin=0, byte vmax=255); // getVal supports inc/decrementing and random ("X~Y(r|[w]~[-][Z])" form)
|
||||
[[gnu::pure]] bool getBoolVal(const JsonVariant &elem, bool dflt);
|
||||
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||
bool updateVal(const char* req, const char* key, byte &val, byte minv=0, byte maxv=255);
|
||||
size_t printSetFormCheckbox(Print& settingsScript, const char* key, int val);
|
||||
size_t printSetFormValue(Print& settingsScript, const char* key, int val);
|
||||
size_t printSetFormValue(Print& settingsScript, const char* key, const char* val);
|
||||
@ -544,6 +544,29 @@ inline uint8_t hw_random8() { return HW_RND_REGISTER; };
|
||||
inline uint8_t hw_random8(uint32_t upperlimit) { return (hw_random8() * upperlimit) >> 8; }; // input range 0-255
|
||||
inline uint8_t hw_random8(uint32_t lowerlimit, uint32_t upperlimit) { uint32_t range = upperlimit - lowerlimit; return lowerlimit + hw_random8(range); }; // input range 0-255
|
||||
|
||||
// PSRAM allocation wrappers
|
||||
#ifndef ESP8266
|
||||
extern "C" {
|
||||
void *p_malloc(size_t); // prefer PSRAM over DRAM
|
||||
void *p_calloc(size_t, size_t); // prefer PSRAM over DRAM
|
||||
void *p_realloc(void *, size_t); // prefer PSRAM over DRAM
|
||||
inline void p_free(void *ptr) { heap_caps_free(ptr); }
|
||||
void *d_malloc(size_t); // prefer DRAM over PSRAM
|
||||
void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM
|
||||
void *d_realloc(void *, size_t); // prefer DRAM over PSRAM
|
||||
inline void d_free(void *ptr) { heap_caps_free(ptr); }
|
||||
}
|
||||
#else
|
||||
#define p_malloc malloc
|
||||
#define p_calloc calloc
|
||||
#define p_realloc realloc
|
||||
#define p_free free
|
||||
#define d_malloc malloc
|
||||
#define d_calloc calloc
|
||||
#define d_realloc realloc
|
||||
#define d_free free
|
||||
#endif
|
||||
|
||||
// RAII guard class for the JSON Buffer lock
|
||||
// Modeled after std::lock_guard
|
||||
class JSONBufferGuard {
|
||||
|
@ -39,7 +39,7 @@ void closeFile() {
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("took %lu ms\n", millis() - s);
|
||||
doCloseFile = false;
|
||||
}
|
||||
|
||||
@ -69,14 +69,14 @@ static bool bufferedFind(const char *target, bool fromStart = true) {
|
||||
if(buf[count] == target[index]) {
|
||||
if(++index >= targetLen) { // return true if all chars in the target match
|
||||
f.seek((f.position() - bufsize) + count +1);
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %lu ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("No match, took %lu ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ static bool bufferedFindSpace(size_t targetLen, bool fromStart = true) {
|
||||
f.seek((f.position() - bufsize) + count +1 - targetLen);
|
||||
knownLargestSpace = MAX_SPACE; //there may be larger spaces after, so we don't know
|
||||
}
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %lu ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@ -125,7 +125,7 @@ static bool bufferedFindSpace(size_t targetLen, bool fromStart = true) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("No match, took %lu ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -151,13 +151,13 @@ static bool bufferedFindObjectEnd() {
|
||||
if (buf[count] == '}') objDepth--;
|
||||
if (objDepth == 0) {
|
||||
f.seek((f.position() - bufsize) + count +1);
|
||||
DEBUGFS_PRINTF("} at pos %d, took %d ms", f.position(), millis() - s);
|
||||
DEBUGFS_PRINTF("} at pos %d, took %lu ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("No match, took %lu ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -203,7 +203,7 @@ static bool appendObjectToFile(const char* key, const JsonDocument* content, uin
|
||||
if (f.position() > 2) f.write(','); //add comma if not first object
|
||||
f.print(key);
|
||||
serializeJson(*content, f);
|
||||
DEBUGFS_PRINTF("Inserted, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
DEBUGFS_PRINTF("Inserted, took %lu ms (total %lu)", millis() - s1, millis() - s);
|
||||
doCloseFile = true;
|
||||
return true;
|
||||
}
|
||||
@ -251,7 +251,7 @@ static bool appendObjectToFile(const char* key, const JsonDocument* content, uin
|
||||
f.write('}');
|
||||
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Appended, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
DEBUGFS_PRINTF("Appended, took %lu ms (total %lu)", millis() - s1, millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -321,7 +321,7 @@ bool writeObjectToFile(const char* file, const char* key, const JsonDocument* co
|
||||
}
|
||||
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Replaced/deleted, took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("Replaced/deleted, took %lu ms\n", millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -356,7 +356,7 @@ bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest, c
|
||||
else deserializeJson(*dest, f);
|
||||
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("Read, took %d ms\n", millis() - s);
|
||||
DEBUGFS_PRINTF("Read, took %lu ms\n", millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -392,7 +392,7 @@ static const uint8_t *getPresetCache(size_t &size) {
|
||||
|
||||
if ((presetsModifiedTime != presetsCachedTime) || (presetsCachedValidate != cacheInvalidate)) {
|
||||
if (presetsCached) {
|
||||
free(presetsCached);
|
||||
p_free(presetsCached);
|
||||
presetsCached = nullptr;
|
||||
}
|
||||
}
|
||||
@ -403,7 +403,7 @@ static const uint8_t *getPresetCache(size_t &size) {
|
||||
presetsCachedTime = presetsModifiedTime;
|
||||
presetsCachedValidate = cacheInvalidate;
|
||||
presetsCachedSize = 0;
|
||||
presetsCached = (uint8_t*)ps_malloc(file.size() + 1);
|
||||
presetsCached = (uint8_t*)p_malloc(file.size() + 1);
|
||||
if (presetsCached) {
|
||||
presetsCachedSize = file.size();
|
||||
file.read(presetsCached, presetsCachedSize);
|
||||
@ -419,7 +419,7 @@ static const uint8_t *getPresetCache(size_t &size) {
|
||||
#endif
|
||||
|
||||
bool handleFileRead(AsyncWebServerRequest* request, String path){
|
||||
DEBUG_PRINT(F("WS FileRead: ")); DEBUG_PRINTLN(path);
|
||||
DEBUGFS_PRINT(F("WS FileRead: ")); DEBUGFS_PRINTLN(path);
|
||||
if(path.endsWith("/")) path += "index.htm";
|
||||
if(path.indexOf(F("sec")) > -1) return false;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
@ -78,7 +78,7 @@ void drawPixelCallback(int16_t x, int16_t y, uint8_t red, uint8_t green, uint8_t
|
||||
byte renderImageToSegment(Segment &seg) {
|
||||
if (!seg.name) return IMAGE_ERROR_NO_NAME;
|
||||
// disable during effect transition, causes flickering, multiple allocations and depending on image, part of old FX remaining
|
||||
if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
|
||||
//if (seg.mode != seg.currentMode()) return IMAGE_ERROR_WAITING;
|
||||
if (activeSeg && activeSeg != &seg) return IMAGE_ERROR_SEG_LIMIT; // only one segment at a time
|
||||
activeSeg = &seg;
|
||||
|
||||
|
@ -425,8 +425,8 @@ static void decodeIR44(uint32_t code)
|
||||
case IR44_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
|
||||
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, strip.getModeCount() -1)); break;
|
||||
case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, strip.getPaletteCount() -1)); break;
|
||||
case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, getPaletteCount() -1)); break;
|
||||
case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, getPaletteCount() -1)); break;
|
||||
case IR44_BLUEPLUS : changeEffectIntensity( 16); break;
|
||||
case IR44_BLUEMINUS : changeEffectIntensity(-16); break;
|
||||
case IR44_QUICK : changeEffectSpeed( 16); break;
|
||||
@ -435,7 +435,7 @@ static void decodeIR44(uint32_t code)
|
||||
case IR44_DIY2 : presetFallback(2, FX_MODE_BREATH, 0); break;
|
||||
case IR44_DIY3 : presetFallback(3, FX_MODE_FIRE_FLICKER, 0); break;
|
||||
case IR44_DIY4 : presetFallback(4, FX_MODE_RAINBOW, 0); break;
|
||||
case IR44_DIY5 : presetFallback(5, FX_MODE_METEOR, 0); break;
|
||||
case IR44_DIY5 : presetFallback(5, FX_MODE_METEOR, 0); break;
|
||||
case IR44_DIY6 : presetFallback(6, FX_MODE_RAIN, 0); break;
|
||||
case IR44_AUTO : changeEffect(FX_MODE_STATIC); break;
|
||||
case IR44_FLASH : changeEffect(FX_MODE_PALETTE); break;
|
||||
@ -484,7 +484,7 @@ static void decodeIR6(uint32_t code)
|
||||
case IR6_CHANNEL_UP: incBrightness(); break;
|
||||
case IR6_CHANNEL_DOWN: decBrightness(); break;
|
||||
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
|
||||
case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1));
|
||||
case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, getPaletteCount() -1));
|
||||
switch(lastIR6ColourIdx) {
|
||||
case 0: changeColor(COLOR_RED); break;
|
||||
case 1: changeColor(COLOR_REDDISH); break;
|
||||
@ -530,7 +530,7 @@ static void decodeIR9(uint32_t code)
|
||||
|
||||
/*
|
||||
This allows users to customize IR actions without the need to edit C code and compile.
|
||||
From the https://github.com/wled-dev/WLED/wiki/Infrared-Control page, download the starter
|
||||
From the https://github.com/wled/WLED/wiki/Infrared-Control page, download the starter
|
||||
ir.json file that corresponds to the number of buttons on your remote.
|
||||
Many of the remotes with the same number of buttons emit the same codes, but will have
|
||||
different labels or colors. Once you edit the ir.json file, upload it to your controller
|
||||
|
242
wled00/json.cpp
242
wled00/json.cpp
@ -2,14 +2,74 @@
|
||||
|
||||
#include "palettes.h"
|
||||
|
||||
#define JSON_PATH_STATE 1
|
||||
#define JSON_PATH_INFO 2
|
||||
#define JSON_PATH_STATE_INFO 3
|
||||
#define JSON_PATH_NODES 4
|
||||
#define JSON_PATH_PALETTES 5
|
||||
#define JSON_PATH_FXDATA 6
|
||||
#define JSON_PATH_NETWORKS 7
|
||||
#define JSON_PATH_EFFECTS 8
|
||||
|
||||
/*
|
||||
* JSON API (De)serialization
|
||||
*/
|
||||
namespace {
|
||||
typedef struct {
|
||||
uint32_t colors[NUM_COLORS];
|
||||
uint16_t start;
|
||||
uint16_t stop;
|
||||
uint16_t offset;
|
||||
uint16_t grouping;
|
||||
uint16_t spacing;
|
||||
uint16_t startY;
|
||||
uint16_t stopY;
|
||||
uint16_t options;
|
||||
uint8_t mode;
|
||||
uint8_t palette;
|
||||
uint8_t opacity;
|
||||
uint8_t speed;
|
||||
uint8_t intensity;
|
||||
uint8_t custom1;
|
||||
uint8_t custom2;
|
||||
uint8_t custom3;
|
||||
bool check1;
|
||||
bool check2;
|
||||
bool check3;
|
||||
} SegmentCopy;
|
||||
|
||||
bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
uint8_t differs(const Segment& b, const SegmentCopy& a) {
|
||||
uint8_t d = 0;
|
||||
if (a.start != b.start) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (a.stop != b.stop) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (a.offset != b.offset) d |= SEG_DIFFERS_GSO;
|
||||
if (a.grouping != b.grouping) d |= SEG_DIFFERS_GSO;
|
||||
if (a.spacing != b.spacing) d |= SEG_DIFFERS_GSO;
|
||||
if (a.opacity != b.opacity) d |= SEG_DIFFERS_BRI;
|
||||
if (a.mode != b.mode) d |= SEG_DIFFERS_FX;
|
||||
if (a.speed != b.speed) d |= SEG_DIFFERS_FX;
|
||||
if (a.intensity != b.intensity) d |= SEG_DIFFERS_FX;
|
||||
if (a.palette != b.palette) d |= SEG_DIFFERS_FX;
|
||||
if (a.custom1 != b.custom1) d |= SEG_DIFFERS_FX;
|
||||
if (a.custom2 != b.custom2) d |= SEG_DIFFERS_FX;
|
||||
if (a.custom3 != b.custom3) d |= SEG_DIFFERS_FX;
|
||||
if (a.startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
|
||||
if (a.stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
|
||||
|
||||
//bit pattern: (msb first)
|
||||
// set:2, sound:2, mapping:3, transposed, mirrorY, reverseY, [reset,] paused, mirrored, on, reverse, [selected]
|
||||
if ((a.options & 0b1111111111011110U) != (b.options & 0b1111111111011110U)) d |= SEG_DIFFERS_OPT;
|
||||
if ((a.options & 0x0001U) != (b.options & 0x0001U)) d |= SEG_DIFFERS_SEL;
|
||||
for (unsigned i = 0; i < NUM_COLORS; i++) if (a.colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
static bool deserializeSegment(JsonObject elem, byte it, byte presetId = 0)
|
||||
{
|
||||
byte id = elem["id"] | it;
|
||||
if (id >= strip.getMaxSegments()) return false;
|
||||
if (id >= WS2812FX::getMaxSegments()) return false;
|
||||
|
||||
bool newSeg = false;
|
||||
int stop = elem["stop"] | -1;
|
||||
@ -17,16 +77,37 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
// append segment
|
||||
if (id >= strip.getSegmentsNum()) {
|
||||
if (stop <= 0) return false; // ignore empty/inactive segments
|
||||
strip.appendSegment(Segment(0, strip.getLengthTotal()));
|
||||
strip.appendSegment(0, strip.getLengthTotal());
|
||||
id = strip.getSegmentsNum()-1; // segments are added at the end of list
|
||||
newSeg = true;
|
||||
}
|
||||
|
||||
//DEBUG_PRINTLN(F("-- JSON deserialize segment."));
|
||||
Segment& seg = strip.getSegment(id);
|
||||
//DEBUG_PRINTF_P(PSTR("-- Original segment: %p (%p)\n"), &seg, seg.data);
|
||||
const Segment prev = seg; //make a backup so we can tell if something changed (calling copy constructor)
|
||||
//DEBUG_PRINTF_P(PSTR("-- Duplicate segment: %p (%p)\n"), &prev, prev.data);
|
||||
// we do not want to make segment copy as it may use a lot of RAM (effect data and pixel buffer)
|
||||
// so we will create a copy of segment options and compare it with original segment when done processing
|
||||
SegmentCopy prev = {
|
||||
{seg.colors[0], seg.colors[1], seg.colors[2]},
|
||||
seg.start,
|
||||
seg.stop,
|
||||
seg.offset,
|
||||
seg.grouping,
|
||||
seg.spacing,
|
||||
seg.startY,
|
||||
seg.stopY,
|
||||
seg.options,
|
||||
seg.mode,
|
||||
seg.palette,
|
||||
seg.opacity,
|
||||
seg.speed,
|
||||
seg.intensity,
|
||||
seg.custom1,
|
||||
seg.custom2,
|
||||
seg.custom3,
|
||||
seg.check1,
|
||||
seg.check2,
|
||||
seg.check3
|
||||
};
|
||||
|
||||
int start = elem["start"] | seg.start;
|
||||
if (stop < 0) {
|
||||
@ -44,7 +125,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
elem.remove("rpt"); // remove for recursive call
|
||||
elem.remove("n"); // remove for recursive call
|
||||
unsigned len = stop - start;
|
||||
for (size_t i=id+1; i<strip.getMaxSegments(); i++) {
|
||||
for (size_t i=id+1; i<WS2812FX::getMaxSegments(); i++) {
|
||||
start = start + len;
|
||||
if (start >= strip.getLengthTotal()) break;
|
||||
//TODO: add support for 2D
|
||||
@ -58,28 +139,11 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
|
||||
if (elem["n"]) {
|
||||
// name field exists
|
||||
if (seg.name) { //clear old name
|
||||
free(seg.name);
|
||||
seg.name = nullptr;
|
||||
}
|
||||
|
||||
const char * name = elem["n"].as<const char*>();
|
||||
size_t len = 0;
|
||||
if (name != nullptr) len = strlen(name);
|
||||
if (len > 0) {
|
||||
if (len > WLED_MAX_SEGNAME_LEN) len = WLED_MAX_SEGNAME_LEN;
|
||||
seg.name = static_cast<char*>(malloc(len+1));
|
||||
if (seg.name) strlcpy(seg.name, name, WLED_MAX_SEGNAME_LEN+1);
|
||||
} else {
|
||||
// but is empty (already deleted above)
|
||||
elem.remove("n");
|
||||
}
|
||||
seg.setName(name); // will resolve empty and null correctly
|
||||
} else if (start != seg.start || stop != seg.stop) {
|
||||
// clearing or setting segment without name field
|
||||
if (seg.name) {
|
||||
free(seg.name);
|
||||
seg.name = nullptr;
|
||||
}
|
||||
seg.clearName();
|
||||
}
|
||||
|
||||
uint16_t grp = elem["grp"] | seg.grouping;
|
||||
@ -97,6 +161,12 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
bool transpose = getBoolVal(elem[F("tp")], seg.transpose);
|
||||
#endif
|
||||
|
||||
// if segment's virtual dimensions change we need to restart effect (segment blending and PS rely on dimensions)
|
||||
if (seg.mirror != mirror) seg.markForReset();
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (seg.mirror_y != mirror_y || seg.transpose != transpose) seg.markForReset();
|
||||
#endif
|
||||
|
||||
int len = (stop > start) ? stop - start : 1;
|
||||
int offset = elem[F("of")] | INT32_MAX;
|
||||
if (offset != INT32_MAX) {
|
||||
@ -118,8 +188,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
|
||||
byte segbri = seg.opacity;
|
||||
if (getVal(elem["bri"], &segbri)) {
|
||||
if (segbri > 0) seg.setOpacity(segbri);
|
||||
if (getVal(elem["bri"], segbri)) {
|
||||
if (segbri > 0) seg.setOpacity(segbri); // use transition
|
||||
seg.setOption(SEG_OPTION_ON, segbri); // use transition
|
||||
}
|
||||
|
||||
@ -175,13 +245,13 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
|
||||
if (!colValid) continue;
|
||||
|
||||
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]));
|
||||
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3])); // use transition
|
||||
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
|
||||
}
|
||||
} else {
|
||||
// non RGB & non White segment (usually On/Off bus)
|
||||
seg.setColor(0, ULTRAWHITE);
|
||||
seg.setColor(1, BLACK);
|
||||
seg.setColor(0, ULTRAWHITE); // use transition
|
||||
seg.setColor(1, BLACK); // use transition
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,7 +267,6 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
#endif
|
||||
|
||||
//seg.map1D2D = constrain(map1D2D, 0, 7); // done in setGeometry()
|
||||
seg.set = constrain(set, 0, 3);
|
||||
seg.soundSim = constrain(soundSim, 0, 3);
|
||||
seg.selected = selected;
|
||||
@ -210,57 +279,58 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
#endif
|
||||
|
||||
byte fx = seg.mode;
|
||||
if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) {
|
||||
if (getVal(elem["fx"], fx, 0, strip.getModeCount())) {
|
||||
if (!presetId && currentPlaylist>=0) unloadPlaylist();
|
||||
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]);
|
||||
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]); // use transition (WARNING: may change map1D2D causing geometry change)
|
||||
}
|
||||
|
||||
getVal(elem["sx"], &seg.speed);
|
||||
getVal(elem["ix"], &seg.intensity);
|
||||
getVal(elem["sx"], seg.speed);
|
||||
getVal(elem["ix"], seg.intensity);
|
||||
|
||||
uint8_t pal = seg.palette;
|
||||
if (seg.getLightCapabilities() & 1) { // ignore palette for White and On/Off segments
|
||||
if (getVal(elem["pal"], &pal, 0, strip.getPaletteCount())) seg.setPalette(pal);
|
||||
if (getVal(elem["pal"], pal, 0, getPaletteCount())) seg.setPalette(pal);
|
||||
}
|
||||
|
||||
getVal(elem["c1"], &seg.custom1);
|
||||
getVal(elem["c2"], &seg.custom2);
|
||||
getVal(elem["c1"], seg.custom1);
|
||||
getVal(elem["c2"], seg.custom2);
|
||||
uint8_t cust3 = seg.custom3;
|
||||
getVal(elem["c3"], &cust3, 0, 31); // we can't pass reference to bitfield
|
||||
getVal(elem["c3"], cust3, 0, 31); // we can't pass reference to bitfield
|
||||
seg.custom3 = constrain(cust3, 0, 31);
|
||||
|
||||
seg.check1 = getBoolVal(elem["o1"], seg.check1);
|
||||
seg.check2 = getBoolVal(elem["o2"], seg.check2);
|
||||
seg.check3 = getBoolVal(elem["o3"], seg.check3);
|
||||
|
||||
uint8_t blend = seg.blendMode;
|
||||
getVal(elem["bm"], blend, 0, 15); // we can't pass reference to bitfield
|
||||
seg.blendMode = constrain(blend, 0, 15);
|
||||
|
||||
JsonArray iarr = elem[F("i")]; //set individual LEDs
|
||||
if (!iarr.isNull()) {
|
||||
uint8_t oldMap1D2D = seg.map1D2D;
|
||||
seg.map1D2D = M12_Pixels; // no mapping
|
||||
|
||||
// set brightness immediately and disable transition
|
||||
jsonTransitionOnce = true;
|
||||
seg.stopTransition();
|
||||
if (seg.isInTransition()) seg.startTransition(0); // setting transition time to 0 will stop transition in next frame
|
||||
strip.setTransition(0);
|
||||
strip.setBrightness(scaledBri(bri), true);
|
||||
|
||||
// freeze and init to black
|
||||
if (!seg.freeze) {
|
||||
seg.freeze = true;
|
||||
seg.fill(BLACK);
|
||||
seg.clear();
|
||||
}
|
||||
|
||||
start = 0, stop = 0;
|
||||
set = 0; //0 nothing set, 1 start set, 2 range set
|
||||
unsigned iStart = 0, iStop = 0;
|
||||
unsigned iSet = 0; //0 nothing set, 1 start set, 2 range set
|
||||
|
||||
for (size_t i = 0; i < iarr.size(); i++) {
|
||||
if(iarr[i].is<JsonInteger>()) {
|
||||
if (!set) {
|
||||
start = abs(iarr[i].as<int>());
|
||||
set++;
|
||||
if (iarr[i].is<JsonInteger>()) {
|
||||
if (!iSet) {
|
||||
iStart = abs(iarr[i].as<int>());
|
||||
iSet++;
|
||||
} else {
|
||||
stop = abs(iarr[i].as<int>());
|
||||
set++;
|
||||
iStop = abs(iarr[i].as<int>());
|
||||
iSet++;
|
||||
}
|
||||
} else { //color
|
||||
uint8_t rgbw[] = {0,0,0,0};
|
||||
@ -276,17 +346,16 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
}
|
||||
}
|
||||
|
||||
if (set < 2 || stop <= start) stop = start + 1;
|
||||
uint32_t c = gamma32(RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
|
||||
while (start < stop) seg.setPixelColor(start++, c);
|
||||
set = 0;
|
||||
if (iSet < 2 || iStop <= iStart) iStop = iStart + 1;
|
||||
uint32_t c = RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
|
||||
while (iStart < iStop) seg.setRawPixelColor(iStart++, c); // sets pixel color without 1D->2D expansion, grouping or spacing
|
||||
iSet = 0;
|
||||
}
|
||||
}
|
||||
seg.map1D2D = oldMap1D2D; // restore mapping
|
||||
strip.trigger(); // force segment update
|
||||
}
|
||||
// send UDP/WS if segment options changed (except selection; will also deselect current preset)
|
||||
if (seg.differs(prev) & 0x7F) stateChanged = true;
|
||||
if (differs(seg, prev) & ~SEG_DIFFERS_SEL) stateChanged = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -302,7 +371,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
#endif
|
||||
|
||||
bool onBefore = bri;
|
||||
getVal(root["bri"], &bri);
|
||||
getVal(root["bri"], bri);
|
||||
if (bri != briOld) stateChanged = true;
|
||||
|
||||
bool on = root["on"] | (bri > 0);
|
||||
if (!on != !bri) toggleOnOff();
|
||||
@ -329,10 +399,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
blendingStyle = root[F("bs")] | blendingStyle;
|
||||
blendingStyle &= 0x1F;
|
||||
#endif
|
||||
|
||||
// temporary transition (applies only once)
|
||||
tr = root[F("tt")] | -1;
|
||||
@ -345,6 +413,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
if (tr >= 0) strip.timebase = (unsigned long)tr - millis();
|
||||
|
||||
JsonObject nl = root["nl"];
|
||||
if (!nl.isNull()) stateChanged = true;
|
||||
nightlightActive = getBoolVal(nl["on"], nightlightActive);
|
||||
nightlightDelayMins = nl["dur"] | nightlightDelayMins;
|
||||
nightlightMode = nl["mode"] | nightlightMode;
|
||||
@ -371,6 +440,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
|
||||
if (realtimeMode && useMainSegmentOnly) {
|
||||
strip.getMainSegment().freeze = !realtimeOverride;
|
||||
realtimeOverride = REALTIME_OVERRIDE_NONE; // ignore request for override if using main segment only
|
||||
}
|
||||
|
||||
if (root.containsKey("live")) {
|
||||
@ -388,18 +458,14 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
if (!segVar.isNull()) {
|
||||
// we may be called during strip.service() so we must not modify segments while effects are executing
|
||||
strip.suspend();
|
||||
const unsigned long start = millis();
|
||||
while (strip.isServicing() && millis() - start < strip.getFrameTime()) yield(); // wait until frame is over
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - start > 0) DEBUG_PRINTLN(F("JSON: Waited for strip to finish servicing."));
|
||||
#endif
|
||||
strip.waitForIt();
|
||||
if (segVar.is<JsonObject>()) {
|
||||
int id = segVar["id"] | -1;
|
||||
//if "seg" is not an array and ID not specified, apply to all selected/checked segments
|
||||
if (id < 0) {
|
||||
//apply all selected segments
|
||||
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
|
||||
Segment &sg = strip.getSegment(s);
|
||||
const Segment &sg = strip.getSegment(s);
|
||||
if (sg.isActive() && sg.isSelected()) {
|
||||
deserializeSegment(segVar, s, presetId);
|
||||
}
|
||||
@ -449,7 +515,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
DEBUG_PRINTF_P(PSTR("Preset direct: %d\n"), currentPreset);
|
||||
} else if (!root["ps"].isNull()) {
|
||||
// we have "ps" call (i.e. from button or external API call) or "pd" that includes "ps" (i.e. from UI call)
|
||||
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 1, 250) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
|
||||
if (root["win"].isNull() && getVal(root["ps"], presetCycCurr, 1, 250) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
|
||||
DEBUG_PRINTF_P(PSTR("Preset select: %d\n"), presetCycCurr);
|
||||
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
|
||||
applyPreset(presetCycCurr, callMode); // async load from file system (only preset ID was specified)
|
||||
@ -465,11 +531,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
}
|
||||
|
||||
if (root.containsKey(F("rmcpal")) && root[F("rmcpal")].as<bool>()) {
|
||||
if (strip.customPalettes.size()) {
|
||||
if (customPalettes.size()) {
|
||||
char fileName[32];
|
||||
sprintf_P(fileName, PSTR("/palette%d.json"), strip.customPalettes.size()-1);
|
||||
sprintf_P(fileName, PSTR("/palette%d.json"), customPalettes.size()-1);
|
||||
if (WLED_FS.exists(fileName)) WLED_FS.remove(fileName);
|
||||
strip.loadCustomPalettes();
|
||||
loadCustomPalettes();
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,13 +554,13 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
//if (restart) forceReconnect = true;
|
||||
}
|
||||
|
||||
stateUpdated(callMode);
|
||||
if (stateChanged) stateUpdated(callMode);
|
||||
if (presetToRestore) currentPreset = presetToRestore;
|
||||
|
||||
return stateResponse;
|
||||
}
|
||||
|
||||
void serializeSegment(const JsonObject& root, const Segment& seg, byte id, bool forPreset, bool segmentBounds)
|
||||
static void serializeSegment(JsonObject& root, const Segment& seg, byte id, bool forPreset, bool segmentBounds)
|
||||
{
|
||||
root["id"] = id;
|
||||
if (segmentBounds) {
|
||||
@ -517,6 +583,7 @@ void serializeSegment(const JsonObject& root, const Segment& seg, byte id, bool
|
||||
root["bri"] = (segbri) ? segbri : 255;
|
||||
root["cct"] = seg.cct;
|
||||
root[F("set")] = seg.set;
|
||||
root["lc"] = seg.getLightCapabilities();
|
||||
|
||||
if (seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name); //not good practice, but decreases required JSON buffer
|
||||
else if (forPreset) root["n"] = "";
|
||||
@ -561,6 +628,7 @@ void serializeSegment(const JsonObject& root, const Segment& seg, byte id, bool
|
||||
root["o3"] = seg.check3;
|
||||
root["si"] = seg.soundSim;
|
||||
root["m12"] = seg.map1D2D;
|
||||
root["bm"] = seg.blendMode;
|
||||
}
|
||||
|
||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds, bool selectedSegmentsOnly)
|
||||
@ -569,9 +637,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
root["on"] = (bri > 0);
|
||||
root["bri"] = briLast;
|
||||
root[F("transition")] = transitionDelay/100; //in 100ms
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
root[F("bs")] = blendingStyle;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!forPreset) {
|
||||
@ -602,7 +668,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
root[F("mainseg")] = strip.getMainSegmentId();
|
||||
|
||||
JsonArray seg = root.createNestedArray("seg");
|
||||
for (size_t s = 0; s < strip.getMaxSegments(); s++) {
|
||||
for (size_t s = 0; s < WS2812FX::getMaxSegments(); s++) {
|
||||
if (s >= strip.getSegmentsNum()) {
|
||||
if (forPreset && segmentBounds && !selectedSegmentsOnly) { //disable segments not part of preset
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
@ -611,7 +677,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
} else
|
||||
break;
|
||||
}
|
||||
Segment &sg = strip.getSegment(s);
|
||||
const Segment &sg = strip.getSegment(s);
|
||||
if (forPreset && selectedSegmentsOnly && !sg.isSelected()) continue;
|
||||
if (sg.isActive()) {
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
@ -635,7 +701,7 @@ void serializeInfo(JsonObject root)
|
||||
leds[F("pwr")] = BusManager::currentMilliamps();
|
||||
leds["fps"] = strip.getFps();
|
||||
leds[F("maxpwr")] = BusManager::currentMilliamps()>0 ? BusManager::ablMilliampsMax() : 0;
|
||||
leds[F("maxseg")] = strip.getMaxSegments();
|
||||
leds[F("maxseg")] = WS2812FX::getMaxSegments();
|
||||
//leds[F("actseg")] = strip.getActiveSegmentsNum();
|
||||
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
|
||||
leds[F("bootps")] = bootPreset;
|
||||
@ -649,13 +715,13 @@ void serializeInfo(JsonObject root)
|
||||
#endif
|
||||
|
||||
unsigned totalLC = 0;
|
||||
JsonArray lcarr = leds.createNestedArray(F("seglc"));
|
||||
JsonArray lcarr = leds.createNestedArray(F("seglc")); // deprecated, use state.seg[].lc
|
||||
size_t nSegs = strip.getSegmentsNum();
|
||||
for (size_t s = 0; s < nSegs; s++) {
|
||||
if (!strip.getSegment(s).isActive()) continue;
|
||||
unsigned lc = strip.getSegment(s).getLightCapabilities();
|
||||
totalLC |= lc;
|
||||
lcarr.add(lc);
|
||||
lcarr.add(lc); // deprecated, use state.seg[].lc
|
||||
}
|
||||
|
||||
leds["lc"] = totalLC;
|
||||
@ -703,8 +769,8 @@ void serializeInfo(JsonObject root)
|
||||
#endif
|
||||
|
||||
root[F("fxcount")] = strip.getModeCount();
|
||||
root[F("palcount")] = strip.getPaletteCount();
|
||||
root[F("cpalcount")] = strip.customPalettes.size(); //number of custom palettes
|
||||
root[F("palcount")] = getPaletteCount();
|
||||
root[F("cpalcount")] = customPalettes.size(); //number of custom palettes
|
||||
|
||||
JsonArray ledmaps = root.createNestedArray(F("maps"));
|
||||
for (size_t i=0; i<WLED_MAX_LEDMAPS; i++) {
|
||||
@ -867,15 +933,15 @@ void serializePalettes(JsonObject root, int page)
|
||||
int itemPerPage = 8;
|
||||
#endif
|
||||
|
||||
int customPalettes = strip.customPalettes.size();
|
||||
int palettesCount = strip.getPaletteCount() - customPalettes;
|
||||
int customPalettesCount = customPalettes.size();
|
||||
int palettesCount = getPaletteCount() - customPalettesCount;
|
||||
|
||||
int maxPage = (palettesCount + customPalettes -1) / itemPerPage;
|
||||
int maxPage = (palettesCount + customPalettesCount -1) / itemPerPage;
|
||||
if (page > maxPage) page = maxPage;
|
||||
|
||||
int start = itemPerPage * page;
|
||||
int end = start + itemPerPage;
|
||||
if (end > palettesCount + customPalettes) end = palettesCount + customPalettes;
|
||||
if (end > palettesCount + customPalettesCount) end = palettesCount + customPalettesCount;
|
||||
|
||||
root[F("m")] = maxPage; // inform caller how many pages there are
|
||||
JsonObject palettes = root.createNestedObject("p");
|
||||
@ -911,7 +977,7 @@ void serializePalettes(JsonObject root, int page)
|
||||
break;
|
||||
default:
|
||||
if (i >= palettesCount)
|
||||
setPaletteColors(curPalette, strip.customPalettes[i - palettesCount]);
|
||||
setPaletteColors(curPalette, customPalettes[i - palettesCount]);
|
||||
else if (i < 13) // palette 6 - 12, fastled palettes
|
||||
setPaletteColors(curPalette, *fastledPalettes[i-6]);
|
||||
else {
|
||||
|
@ -4,11 +4,9 @@
|
||||
* LED methods
|
||||
*/
|
||||
|
||||
void setValuesFromMainSeg() { setValuesFromSegment(strip.getMainSegmentId()); }
|
||||
void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); }
|
||||
void setValuesFromSegment(uint8_t s)
|
||||
{
|
||||
Segment& seg = strip.getSegment(s);
|
||||
// applies chosen setment properties to legacy values
|
||||
void setValuesFromSegment(uint8_t s) {
|
||||
const Segment& seg = strip.getSegment(s);
|
||||
colPri[0] = R(seg.colors[0]);
|
||||
colPri[1] = G(seg.colors[0]);
|
||||
colPri[2] = B(seg.colors[0]);
|
||||
@ -24,25 +22,19 @@ void setValuesFromSegment(uint8_t s)
|
||||
}
|
||||
|
||||
|
||||
// applies global legacy values (col, colSec, effectCurrent...)
|
||||
// problem: if the first selected segment already has the value to be set, other selected segments are not updated
|
||||
void applyValuesToSelectedSegs()
|
||||
{
|
||||
// copy of first selected segment to tell if value was updated
|
||||
unsigned firstSel = strip.getFirstSelectedSegId();
|
||||
Segment selsegPrev = strip.getSegment(firstSel);
|
||||
// applies global legacy values (colPri, colSec, effectCurrent...) to each selected segment
|
||||
void applyValuesToSelectedSegs() {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
|
||||
|
||||
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
|
||||
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
|
||||
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
|
||||
if (!(seg.isActive() && seg.isSelected())) continue;
|
||||
if (effectSpeed != seg.speed) {seg.speed = effectSpeed; stateChanged = true;}
|
||||
if (effectIntensity != seg.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != seg.palette) {seg.setPalette(effectPalette);}
|
||||
if (effectCurrent != seg.mode) {seg.setMode(effectCurrent);}
|
||||
uint32_t col0 = RGBW32(colPri[0], colPri[1], colPri[2], colPri[3]);
|
||||
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
|
||||
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
|
||||
if (col0 != seg.colors[0]) {seg.setColor(0, col0);}
|
||||
if (col1 != seg.colors[1]) {seg.setColor(1, col1);}
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +65,8 @@ byte scaledBri(byte in)
|
||||
|
||||
//applies global temporary brightness (briT) to strip
|
||||
void applyBri() {
|
||||
if (!(realtimeMode && arlsForceMaxBri)) {
|
||||
if (realtimeOverride || !(realtimeMode && arlsForceMaxBri))
|
||||
{
|
||||
//DEBUG_PRINTF_P(PSTR("Applying strip brightness: %d (%d,%d)\n"), (int)briT, (int)bri, (int)briOld);
|
||||
strip.setBrightness(scaledBri(briT));
|
||||
}
|
||||
@ -94,7 +87,7 @@ void applyFinalBri() {
|
||||
void stateUpdated(byte callMode) {
|
||||
//call for notifier -> 0: init 1: direct change 2: button 3: notification 4: nightlight 5: other (No notification)
|
||||
// 6: fx changed 7: hue 8: preset cycle 9: blynk 10: alexa 11: ws send only 12: button preset
|
||||
setValuesFromFirstSelectedSeg();
|
||||
setValuesFromFirstSelectedSeg(); // a much better approach would be to use main segment: setValuesFromMainSeg()
|
||||
|
||||
if (bri != briOld || stateChanged) {
|
||||
if (stateChanged) currentPreset = 0; //something changed, so we are no longer in the preset
|
||||
@ -104,7 +97,6 @@ void stateUpdated(byte callMode) {
|
||||
|
||||
//set flag to update ws and mqtt
|
||||
interfaceUpdateCallMode = callMode;
|
||||
stateChanged = false;
|
||||
} else {
|
||||
if (nightlightActive && !nightlightActiveOld && callMode != CALL_MODE_NOTIFICATION && callMode != CALL_MODE_NO_NOTIFY) {
|
||||
notify(CALL_MODE_NIGHTLIGHT);
|
||||
@ -134,15 +126,16 @@ void stateUpdated(byte callMode) {
|
||||
jsonTransitionOnce = false;
|
||||
transitionActive = false;
|
||||
applyFinalBri();
|
||||
return;
|
||||
strip.trigger();
|
||||
} else {
|
||||
if (transitionActive) {
|
||||
briOld = briT;
|
||||
} else if (bri != briOld || stateChanged)
|
||||
strip.setTransitionMode(true); // force all segments to transition mode
|
||||
transitionActive = true;
|
||||
transitionStartTime = now;
|
||||
}
|
||||
|
||||
if (transitionActive) {
|
||||
briOld = briT;
|
||||
} else
|
||||
strip.setTransitionMode(true); // force all segments to transition mode
|
||||
transitionActive = true;
|
||||
transitionStartTime = now;
|
||||
stateChanged = false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,8 +75,8 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
|
||||
}
|
||||
|
||||
if (index == 0) { // start (1st partial packet or the only packet)
|
||||
if (payloadStr) free(payloadStr); // fail-safe: release buffer
|
||||
payloadStr = static_cast<char*>(malloc(total+1)); // allocate new buffer
|
||||
p_free(payloadStr); // release buffer if it exists
|
||||
payloadStr = static_cast<char*>(p_malloc(total+1)); // allocate new buffer
|
||||
}
|
||||
if (payloadStr == nullptr) return; // buffer not allocated
|
||||
|
||||
@ -101,7 +101,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
|
||||
} else {
|
||||
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||
UsermodManager::onMqttMessage(topic, payloadStr);
|
||||
free(payloadStr);
|
||||
p_free(payloadStr);
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
@ -131,7 +131,7 @@ static void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProp
|
||||
// topmost topic (just wled/MAC)
|
||||
parseMQTTBriPayload(payloadStr);
|
||||
}
|
||||
free(payloadStr);
|
||||
p_free(payloadStr);
|
||||
payloadStr = nullptr;
|
||||
}
|
||||
|
||||
@ -199,7 +199,8 @@ bool initMqtt()
|
||||
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
|
||||
|
||||
if (mqtt == nullptr) {
|
||||
mqtt = new AsyncMqttClient();
|
||||
void *ptr = p_malloc(sizeof(AsyncMqttClient));
|
||||
mqtt = new (ptr) AsyncMqttClient(); // use placement new (into PSRAM), client will never be deleted
|
||||
if (!mqtt) return false;
|
||||
mqtt->onMessage(onMqttMessage);
|
||||
mqtt->onConnect(onMqttConnect);
|
||||
|
@ -90,9 +90,8 @@ void _overlayAnalogCountdown()
|
||||
void handleOverlayDraw() {
|
||||
UsermodManager::handleOverlayDraw();
|
||||
if (analogClockSolidBlack) {
|
||||
const Segment* segments = strip.getSegments();
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
const Segment& segment = segments[i];
|
||||
const Segment& segment = strip.getSegment(i);
|
||||
if (!segment.isActive()) continue;
|
||||
if (segment.mode > 0 || segment.colors[0] > 0) {
|
||||
return;
|
||||
|
1117
wled00/palettes.h
Normal file → Executable file
1117
wled00/palettes.h
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
#include "pin_manager.h"
|
||||
#include "wled.h"
|
||||
#include "pin_manager.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef bitRead
|
||||
|
@ -3,11 +3,6 @@
|
||||
/*
|
||||
* Registers pins so there is no attempt for two interfaces to use the same pin
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "driver/ledc.h" // needed for analog/LEDC channel counts
|
||||
#endif
|
||||
#include "const.h" // for USERMOD_* values
|
||||
|
||||
#ifdef ESP8266
|
||||
#define WLED_NUM_PINS (GPIO_PIN_COUNT+1) // somehow they forgot GPIO 16 (0-16==17)
|
||||
|
@ -29,8 +29,9 @@ bool presetNeedsSaving() {
|
||||
static void doSaveState() {
|
||||
bool persist = (presetToSave < 251);
|
||||
|
||||
unsigned long start = millis();
|
||||
while (strip.isUpdating() && millis()-start < (2*FRAMETIME_FIXED)+1) yield(); // wait 2 frames
|
||||
unsigned long maxWait = millis() + strip.getFrameTime();
|
||||
while (strip.isUpdating() && millis() < maxWait) delay(1); // wait for strip to finish updating, accessing FS during sendout causes glitches
|
||||
|
||||
if (!requestJSONBufferLock(10)) return;
|
||||
|
||||
initPresetsFile(); // just in case if someone deleted presets.json using /edit
|
||||
@ -56,14 +57,10 @@ static void doSaveState() {
|
||||
*/
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (!persist) {
|
||||
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
|
||||
p_free(tmpRAMbuffer);
|
||||
size_t len = measureJson(*pDoc) + 1;
|
||||
DEBUG_PRINTLN(len);
|
||||
// if possible use SPI RAM on ESP32
|
||||
if (psramSafe && psramFound())
|
||||
tmpRAMbuffer = (char*) ps_malloc(len);
|
||||
else
|
||||
tmpRAMbuffer = (char*) malloc(len);
|
||||
tmpRAMbuffer = (char*)p_malloc(len);
|
||||
if (tmpRAMbuffer!=nullptr) {
|
||||
serializeJson(*pDoc, tmpRAMbuffer, len);
|
||||
} else {
|
||||
@ -80,8 +77,8 @@ static void doSaveState() {
|
||||
// clean up
|
||||
saveLedmap = -1;
|
||||
presetToSave = 0;
|
||||
free(saveName);
|
||||
free(quickLoad);
|
||||
p_free(saveName);
|
||||
p_free(quickLoad);
|
||||
saveName = nullptr;
|
||||
quickLoad = nullptr;
|
||||
playlistSave = false;
|
||||
@ -168,9 +165,9 @@ void handlePresets()
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("Applying preset: %u\n"), (unsigned)tmpPreset);
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32S3) || defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
unsigned long start = millis();
|
||||
while (strip.isUpdating() && millis() - start < FRAMETIME_FIXED) yield(); // wait for strip to finish updating, accessing FS during sendout causes glitches
|
||||
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
unsigned long maxWait = millis() + strip.getFrameTime();
|
||||
while (strip.isUpdating() && millis() < maxWait) delay(1); // wait for strip to finish updating, accessing FS during sendout causes glitches
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
@ -206,7 +203,7 @@ void handlePresets()
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
//Aircoookie recommended not to delete buffer
|
||||
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
|
||||
free(tmpRAMbuffer);
|
||||
p_free(tmpRAMbuffer);
|
||||
tmpRAMbuffer = nullptr;
|
||||
}
|
||||
#endif
|
||||
@ -220,8 +217,8 @@ void handlePresets()
|
||||
//called from handleSet(PS=) [network callback (sObj is empty), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
|
||||
void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
{
|
||||
if (!saveName) saveName = static_cast<char*>(malloc(33));
|
||||
if (!quickLoad) quickLoad = static_cast<char*>(malloc(9));
|
||||
if (!saveName) saveName = static_cast<char*>(p_malloc(33));
|
||||
if (!quickLoad) quickLoad = static_cast<char*>(p_malloc(9));
|
||||
if (!saveName || !quickLoad) return;
|
||||
|
||||
if (index == 0 || (index > 250 && index < 255)) return;
|
||||
@ -267,8 +264,8 @@ void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
presetsModifiedTime = toki.second(); //unix time
|
||||
updateFSInfo();
|
||||
}
|
||||
free(saveName);
|
||||
free(quickLoad);
|
||||
p_free(saveName);
|
||||
p_free(quickLoad);
|
||||
saveName = nullptr;
|
||||
quickLoad = nullptr;
|
||||
} else {
|
||||
|
142
wled00/set.cpp
142
wled00/set.cpp
@ -28,7 +28,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
char gw[5] = "GW"; gw[2] = 48+n; gw[4] = 0; //GW address
|
||||
char sn[5] = "SN"; sn[2] = 48+n; sn[4] = 0; //subnet mask
|
||||
if (request->hasArg(cs)) {
|
||||
if (n >= multiWiFi.size()) multiWiFi.push_back(WiFiConfig()); // expand vector by one
|
||||
if (n >= multiWiFi.size()) multiWiFi.emplace_back(); // expand vector by one
|
||||
char oldSSID[33]; strcpy(oldSSID, multiWiFi[n].clientSSID);
|
||||
char oldPass[65]; strcpy(oldPass, multiWiFi[n].clientPass);
|
||||
|
||||
@ -142,6 +142,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
unsigned length, start, maMax;
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
|
||||
// this will set global ABL max current used when per-port ABL is not used
|
||||
unsigned ablMilliampsMax = request->arg(F("MA")).toInt();
|
||||
BusManager::setMilliampsMax(ablMilliampsMax);
|
||||
|
||||
@ -149,10 +150,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
strip.correctWB = request->hasArg(F("CCT"));
|
||||
strip.cctFromRgb = request->hasArg(F("CR"));
|
||||
cctICused = request->hasArg(F("IC"));
|
||||
Bus::setCCTBlend(request->arg(F("CB")).toInt());
|
||||
uint8_t cctBlending = request->arg(F("CB")).toInt();
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
|
||||
strip.setTargetFps(request->arg(F("FR")).toInt());
|
||||
useGlobalLedBuffer = request->hasArg(F("LD"));
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
useParallelI2S = request->hasArg(F("PR"));
|
||||
#endif
|
||||
@ -220,12 +221,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
maMax = 0;
|
||||
} else {
|
||||
maPerLed = request->arg(la).toInt();
|
||||
maMax = request->arg(ma).toInt(); // if ABL is disabled this will be 0
|
||||
maMax = request->arg(ma).toInt() * request->hasArg(F("PPL")); // if PP-ABL is disabled maMax (per bus) must be 0
|
||||
}
|
||||
type |= request->hasArg(rf) << 7; // off refresh override
|
||||
// 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
|
||||
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, maPerLed, maMax);
|
||||
busesChanged = true;
|
||||
}
|
||||
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
|
||||
@ -347,6 +348,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
t = request->arg(F("TP")).toInt();
|
||||
randomPaletteChangeTime = MIN(255,MAX(1,t));
|
||||
useHarmonicRandomPalette = request->hasArg(F("TH"));
|
||||
useRainbowWheel = request->hasArg(F("RW"));
|
||||
|
||||
nightlightTargetBri = request->arg(F("TB")).toInt();
|
||||
t = request->arg(F("TL")).toInt();
|
||||
@ -355,7 +357,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
nightlightMode = request->arg(F("TW")).toInt();
|
||||
|
||||
t = request->arg(F("PB")).toInt();
|
||||
if (t >= 0 && t < 4) strip.paletteBlend = t;
|
||||
if (t >= 0 && t < 4) paletteBlend = t;
|
||||
t = request->arg(F("BF")).toInt();
|
||||
if (t > 0) briMultiplier = t;
|
||||
|
||||
@ -371,7 +373,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
DEBUG_PRINTLN(F("Enumerating ledmaps"));
|
||||
enumerateLedmaps();
|
||||
DEBUG_PRINTLN(F("Loading custom palettes"));
|
||||
strip.loadCustomPalettes(); // (re)load all custom palettes
|
||||
loadCustomPalettes(); // (re)load all custom palettes
|
||||
}
|
||||
|
||||
//SYNC
|
||||
@ -786,14 +788,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (subPage == SUBPAGE_2D)
|
||||
{
|
||||
strip.isMatrix = request->arg(F("SOMP")).toInt();
|
||||
strip.panel.clear(); // release memory if allocated
|
||||
strip.panel.clear();
|
||||
if (strip.isMatrix) {
|
||||
strip.panels = MAX(1,MIN(WLED_MAX_PANELS,request->arg(F("MPC")).toInt()));
|
||||
strip.panel.reserve(strip.panels); // pre-allocate memory
|
||||
for (unsigned i=0; i<strip.panels; i++) {
|
||||
unsigned panels = constrain(request->arg(F("MPC")).toInt(), 1, WLED_MAX_PANELS);
|
||||
strip.panel.reserve(panels); // pre-allocate memory
|
||||
for (unsigned i=0; i<panels; i++) {
|
||||
WS2812FX::Panel p;
|
||||
char pO[8] = { '\0' };
|
||||
snprintf_P(pO, 7, PSTR("P%d"), i); // MAX_PANELS is 64 so pO will always only be 4 characters or less
|
||||
snprintf_P(pO, 7, PSTR("P%d"), i); // WLED_MAX_PANELS is less than 100 so pO will always only be 4 characters or less
|
||||
pO[7] = '\0';
|
||||
unsigned l = strlen(pO);
|
||||
// create P0B, P1B, ..., P63B, etc for other PxxX
|
||||
@ -808,13 +810,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
pO[l] = 'H'; p.height = request->arg(pO).toInt();
|
||||
strip.panel.push_back(p);
|
||||
}
|
||||
strip.setUpMatrix(); // will check limits
|
||||
strip.makeAutoSegments(true);
|
||||
strip.deserializeMap();
|
||||
} else {
|
||||
Segment::maxWidth = strip.getLengthTotal();
|
||||
Segment::maxHeight = 1;
|
||||
}
|
||||
strip.panel.shrink_to_fit(); // release unused memory
|
||||
strip.deserializeMap(); // (re)load default ledmap (will also setUpMatrix() if ledmap does not exist)
|
||||
strip.makeAutoSegments(true); // force re-creation of segments
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -839,7 +838,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
//segment select (sets main segment)
|
||||
pos = req.indexOf(F("SM="));
|
||||
if (pos > 0 && !realtimeMode) {
|
||||
strip.setMainSegmentId(getNumVal(&req, pos));
|
||||
strip.setMainSegmentId(getNumVal(req, pos));
|
||||
}
|
||||
|
||||
byte selectedSeg = strip.getFirstSelectedSegId();
|
||||
@ -848,7 +847,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
pos = req.indexOf(F("SS="));
|
||||
if (pos > 0) {
|
||||
unsigned t = getNumVal(&req, pos);
|
||||
unsigned t = getNumVal(req, pos);
|
||||
if (t < strip.getSegmentsNum()) {
|
||||
selectedSeg = t;
|
||||
singleSegment = true;
|
||||
@ -858,7 +857,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
Segment& selseg = strip.getSegment(selectedSeg);
|
||||
pos = req.indexOf(F("SV=")); //segment selected
|
||||
if (pos > 0) {
|
||||
unsigned t = getNumVal(&req, pos);
|
||||
unsigned t = getNumVal(req, pos);
|
||||
if (t == 2) for (unsigned i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).selected = false; // unselect other segments
|
||||
selseg.selected = t;
|
||||
}
|
||||
@ -887,19 +886,19 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
uint16_t spcI = selseg.spacing;
|
||||
pos = req.indexOf(F("&S=")); //segment start
|
||||
if (pos > 0) {
|
||||
startI = std::abs(getNumVal(&req, pos));
|
||||
startI = std::abs(getNumVal(req, pos));
|
||||
}
|
||||
pos = req.indexOf(F("S2=")); //segment stop
|
||||
if (pos > 0) {
|
||||
stopI = std::abs(getNumVal(&req, pos));
|
||||
stopI = std::abs(getNumVal(req, pos));
|
||||
}
|
||||
pos = req.indexOf(F("GP=")); //segment grouping
|
||||
if (pos > 0) {
|
||||
grpI = std::max(1,getNumVal(&req, pos));
|
||||
grpI = std::max(1,getNumVal(req, pos));
|
||||
}
|
||||
pos = req.indexOf(F("SP=")); //segment spacing
|
||||
if (pos > 0) {
|
||||
spcI = std::max(0,getNumVal(&req, pos));
|
||||
spcI = std::max(0,getNumVal(req, pos));
|
||||
}
|
||||
strip.suspend(); // must suspend strip operations before changing geometry
|
||||
selseg.setGeometry(startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY, selseg.map1D2D);
|
||||
@ -913,7 +912,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
pos = req.indexOf(F("SB=")); //Segment brightness/opacity
|
||||
if (pos > 0) {
|
||||
byte segbri = getNumVal(&req, pos);
|
||||
byte segbri = getNumVal(req, pos);
|
||||
selseg.setOption(SEG_OPTION_ON, segbri); // use transition
|
||||
if (segbri) {
|
||||
selseg.setOpacity(segbri);
|
||||
@ -922,7 +921,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
pos = req.indexOf(F("SW=")); //segment power
|
||||
if (pos > 0) {
|
||||
switch (getNumVal(&req, pos)) {
|
||||
switch (getNumVal(req, pos)) {
|
||||
case 0: selseg.setOption(SEG_OPTION_ON, false); break; // use transition
|
||||
case 1: selseg.setOption(SEG_OPTION_ON, true); break; // use transition
|
||||
default: selseg.setOption(SEG_OPTION_ON, !selseg.on); break; // use transition
|
||||
@ -930,16 +929,16 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("PS=")); //saves current in preset
|
||||
if (pos > 0) savePreset(getNumVal(&req, pos));
|
||||
if (pos > 0) savePreset(getNumVal(req, pos));
|
||||
|
||||
pos = req.indexOf(F("P1=")); //sets first preset for cycle
|
||||
if (pos > 0) presetCycMin = getNumVal(&req, pos);
|
||||
if (pos > 0) presetCycMin = getNumVal(req, pos);
|
||||
|
||||
pos = req.indexOf(F("P2=")); //sets last preset for cycle
|
||||
if (pos > 0) presetCycMax = getNumVal(&req, pos);
|
||||
if (pos > 0) presetCycMax = getNumVal(req, pos);
|
||||
|
||||
//apply preset
|
||||
if (updateVal(req.c_str(), "PL=", &presetCycCurr, presetCycMin, presetCycMax)) {
|
||||
if (updateVal(req.c_str(), "PL=", presetCycCurr, presetCycMin, presetCycMax)) {
|
||||
applyPreset(presetCycCurr);
|
||||
}
|
||||
|
||||
@ -947,25 +946,25 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
if (pos > 0) doAdvancePlaylist = true;
|
||||
|
||||
//set brightness
|
||||
updateVal(req.c_str(), "&A=", &bri);
|
||||
updateVal(req.c_str(), "&A=", bri);
|
||||
|
||||
bool col0Changed = false, col1Changed = false, col2Changed = false;
|
||||
//set colors
|
||||
col0Changed |= updateVal(req.c_str(), "&R=", &colIn[0]);
|
||||
col0Changed |= updateVal(req.c_str(), "&G=", &colIn[1]);
|
||||
col0Changed |= updateVal(req.c_str(), "&B=", &colIn[2]);
|
||||
col0Changed |= updateVal(req.c_str(), "&W=", &colIn[3]);
|
||||
col0Changed |= updateVal(req.c_str(), "&R=", colIn[0]);
|
||||
col0Changed |= updateVal(req.c_str(), "&G=", colIn[1]);
|
||||
col0Changed |= updateVal(req.c_str(), "&B=", colIn[2]);
|
||||
col0Changed |= updateVal(req.c_str(), "&W=", colIn[3]);
|
||||
|
||||
col1Changed |= updateVal(req.c_str(), "R2=", &colInSec[0]);
|
||||
col1Changed |= updateVal(req.c_str(), "G2=", &colInSec[1]);
|
||||
col1Changed |= updateVal(req.c_str(), "B2=", &colInSec[2]);
|
||||
col1Changed |= updateVal(req.c_str(), "W2=", &colInSec[3]);
|
||||
col1Changed |= updateVal(req.c_str(), "R2=", colInSec[0]);
|
||||
col1Changed |= updateVal(req.c_str(), "G2=", colInSec[1]);
|
||||
col1Changed |= updateVal(req.c_str(), "B2=", colInSec[2]);
|
||||
col1Changed |= updateVal(req.c_str(), "W2=", colInSec[3]);
|
||||
|
||||
#ifdef WLED_ENABLE_LOXONE
|
||||
//lox parser
|
||||
pos = req.indexOf(F("LX=")); // Lox primary color
|
||||
if (pos > 0) {
|
||||
int lxValue = getNumVal(&req, pos);
|
||||
int lxValue = getNumVal(req, pos);
|
||||
if (parseLx(lxValue, colIn)) {
|
||||
bri = 255;
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
@ -974,7 +973,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
}
|
||||
pos = req.indexOf(F("LY=")); // Lox secondary color
|
||||
if (pos > 0) {
|
||||
int lxValue = getNumVal(&req, pos);
|
||||
int lxValue = getNumVal(req, pos);
|
||||
if(parseLx(lxValue, colInSec)) {
|
||||
bri = 255;
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
@ -986,11 +985,11 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
//set hue
|
||||
pos = req.indexOf(F("HU="));
|
||||
if (pos > 0) {
|
||||
uint16_t temphue = getNumVal(&req, pos);
|
||||
uint16_t temphue = getNumVal(req, pos);
|
||||
byte tempsat = 255;
|
||||
pos = req.indexOf(F("SA="));
|
||||
if (pos > 0) {
|
||||
tempsat = getNumVal(&req, pos);
|
||||
tempsat = getNumVal(req, pos);
|
||||
}
|
||||
byte sec = req.indexOf(F("H2"));
|
||||
colorHStoRGB(temphue, tempsat, (sec>0) ? colInSec : colIn);
|
||||
@ -1001,25 +1000,25 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
pos = req.indexOf(F("&K="));
|
||||
if (pos > 0) {
|
||||
byte sec = req.indexOf(F("K2"));
|
||||
colorKtoRGB(getNumVal(&req, pos), (sec>0) ? colInSec : colIn);
|
||||
colorKtoRGB(getNumVal(req, pos), (sec>0) ? colInSec : colIn);
|
||||
col0Changed |= (!sec); col1Changed |= sec;
|
||||
}
|
||||
|
||||
//set color from HEX or 32bit DEC
|
||||
pos = req.indexOf(F("CL="));
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(colIn, req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(colIn, (char*)req.substring(pos + 3).c_str());
|
||||
col0Changed = true;
|
||||
}
|
||||
pos = req.indexOf(F("C2="));
|
||||
if (pos > 0) {
|
||||
colorFromDecOrHexString(colInSec, req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(colInSec, (char*)req.substring(pos + 3).c_str());
|
||||
col1Changed = true;
|
||||
}
|
||||
pos = req.indexOf(F("C3="));
|
||||
if (pos > 0) {
|
||||
byte tmpCol[4];
|
||||
colorFromDecOrHexString(tmpCol, req.substring(pos + 3).c_str());
|
||||
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
|
||||
col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
|
||||
selseg.setColor(2, col2); // defined above (SS= or main)
|
||||
col2Changed = true;
|
||||
@ -1028,7 +1027,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
//set to random hue SR=0->1st SR=1->2nd
|
||||
pos = req.indexOf(F("SR"));
|
||||
if (pos > 0) {
|
||||
byte sec = getNumVal(&req, pos);
|
||||
byte sec = getNumVal(req, pos);
|
||||
setRandomColor(sec? colInSec : colIn);
|
||||
col0Changed |= (!sec); col1Changed |= sec;
|
||||
}
|
||||
@ -1054,19 +1053,19 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
|
||||
bool custom1Changed = false, custom2Changed = false, custom3Changed = false, check1Changed = false, check2Changed = false, check3Changed = false;
|
||||
// set effect parameters
|
||||
if (updateVal(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1)) {
|
||||
if (updateVal(req.c_str(), "FX=", effectIn, 0, strip.getModeCount()-1)) {
|
||||
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
|
||||
fxModeChanged = true;
|
||||
}
|
||||
speedChanged = updateVal(req.c_str(), "SX=", &speedIn);
|
||||
intensityChanged = updateVal(req.c_str(), "IX=", &intensityIn);
|
||||
paletteChanged = updateVal(req.c_str(), "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
|
||||
custom1Changed = updateVal(req.c_str(), "X1=", &custom1In);
|
||||
custom2Changed = updateVal(req.c_str(), "X2=", &custom2In);
|
||||
custom3Changed = updateVal(req.c_str(), "X3=", &custom3In);
|
||||
check1Changed = updateVal(req.c_str(), "M1=", &check1In);
|
||||
check2Changed = updateVal(req.c_str(), "M2=", &check2In);
|
||||
check3Changed = updateVal(req.c_str(), "M3=", &check3In);
|
||||
speedChanged = updateVal(req.c_str(), "SX=", speedIn);
|
||||
intensityChanged = updateVal(req.c_str(), "IX=", intensityIn);
|
||||
paletteChanged = updateVal(req.c_str(), "FP=", paletteIn, 0, getPaletteCount()-1);
|
||||
custom1Changed = updateVal(req.c_str(), "X1=", custom1In);
|
||||
custom2Changed = updateVal(req.c_str(), "X2=", custom2In);
|
||||
custom3Changed = updateVal(req.c_str(), "X3=", custom3In);
|
||||
check1Changed = updateVal(req.c_str(), "M1=", check1In);
|
||||
check2Changed = updateVal(req.c_str(), "M2=", check2In);
|
||||
check3Changed = updateVal(req.c_str(), "M3=", check3In);
|
||||
|
||||
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged || custom1Changed || custom2Changed || custom3Changed || check1Changed || check2Changed || check3Changed);
|
||||
|
||||
@ -1092,13 +1091,13 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
//set advanced overlay
|
||||
pos = req.indexOf(F("OL="));
|
||||
if (pos > 0) {
|
||||
overlayCurrent = getNumVal(&req, pos);
|
||||
overlayCurrent = getNumVal(req, pos);
|
||||
}
|
||||
|
||||
//apply macro (deprecated, added for compatibility with pre-0.11 automations)
|
||||
pos = req.indexOf(F("&M="));
|
||||
if (pos > 0) {
|
||||
applyPreset(getNumVal(&req, pos) + 16);
|
||||
applyPreset(getNumVal(req, pos) + 16);
|
||||
}
|
||||
|
||||
//toggle send UDP direct notifications
|
||||
@ -1117,7 +1116,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
pos = req.indexOf(F("&T="));
|
||||
if (pos > 0) {
|
||||
nightlightActive = false; //always disable nightlight when toggling
|
||||
switch (getNumVal(&req, pos))
|
||||
switch (getNumVal(req, pos))
|
||||
{
|
||||
case 0: if (bri != 0){briLast = bri; bri = 0;} break; //off, only if it was previously on
|
||||
case 1: if (bri == 0) bri = briLast; break; //on, only if it was previously off
|
||||
@ -1136,7 +1135,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
nightlightActive = false;
|
||||
} else {
|
||||
nightlightActive = true;
|
||||
if (!aNlDef) nightlightDelayMins = getNumVal(&req, pos);
|
||||
if (!aNlDef) nightlightDelayMins = getNumVal(req, pos);
|
||||
else nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
nightlightStartTime = millis();
|
||||
}
|
||||
@ -1150,7 +1149,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
//set nightlight target brightness
|
||||
pos = req.indexOf(F("NT="));
|
||||
if (pos > 0) {
|
||||
nightlightTargetBri = getNumVal(&req, pos);
|
||||
nightlightTargetBri = getNumVal(req, pos);
|
||||
nightlightActiveOld = false; //re-init
|
||||
}
|
||||
|
||||
@ -1158,35 +1157,36 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
pos = req.indexOf(F("NF="));
|
||||
if (pos > 0)
|
||||
{
|
||||
nightlightMode = getNumVal(&req, pos);
|
||||
nightlightMode = getNumVal(req, pos);
|
||||
|
||||
nightlightActiveOld = false; //re-init
|
||||
}
|
||||
if (nightlightMode > NL_MODE_SUN) nightlightMode = NL_MODE_SUN;
|
||||
|
||||
pos = req.indexOf(F("TT="));
|
||||
if (pos > 0) transitionDelay = getNumVal(&req, pos);
|
||||
if (pos > 0) transitionDelay = getNumVal(req, pos);
|
||||
strip.setTransition(transitionDelay);
|
||||
|
||||
//set time (unix timestamp)
|
||||
pos = req.indexOf(F("ST="));
|
||||
if (pos > 0) {
|
||||
setTimeFromAPI(getNumVal(&req, pos));
|
||||
setTimeFromAPI(getNumVal(req, pos));
|
||||
}
|
||||
|
||||
//set countdown goal (unix timestamp)
|
||||
pos = req.indexOf(F("CT="));
|
||||
if (pos > 0) {
|
||||
countdownTime = getNumVal(&req, pos);
|
||||
countdownTime = getNumVal(req, pos);
|
||||
if (countdownTime - toki.second() > 0) countdownOverTriggered = false;
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("LO="));
|
||||
if (pos > 0) {
|
||||
realtimeOverride = getNumVal(&req, pos);
|
||||
realtimeOverride = getNumVal(req, pos);
|
||||
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
|
||||
if (realtimeMode && useMainSegmentOnly) {
|
||||
strip.getMainSegment().freeze = !realtimeOverride;
|
||||
realtimeOverride = REALTIME_OVERRIDE_NONE; // ignore request for override if using main segment only
|
||||
}
|
||||
}
|
||||
|
||||
@ -1199,12 +1199,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
|
||||
pos = req.indexOf(F("U0=")); //user var 0
|
||||
if (pos > 0) {
|
||||
userVar0 = getNumVal(&req, pos);
|
||||
userVar0 = getNumVal(req, pos);
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("U1=")); //user var 1
|
||||
if (pos > 0) {
|
||||
userVar1 = getNumVal(&req, pos);
|
||||
userVar1 = getNumVal(req, pos);
|
||||
}
|
||||
// you can add more if you need
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#define UDP_SEG_SIZE 36
|
||||
#define SEG_OFFSET (41)
|
||||
#define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
|
||||
#define WLEDPACKETSIZE (41+(WS2812FX::getMaxSegments()*UDP_SEG_SIZE)+0)
|
||||
#define UDP_IN_MAXSIZE 1472
|
||||
#define PRESUMED_NETWORK_DELAY 3 //how many ms could it take on avg to reach the receiver? This will be added to transmitted times
|
||||
|
||||
@ -55,7 +55,7 @@ void notify(byte callMode, bool followUp)
|
||||
//0: old 1: supports white 2: supports secondary color
|
||||
//3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
|
||||
//6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
|
||||
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+MAX_NUM_SEGMENTS*3)
|
||||
//9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+WS2812FX::getMaxSegments()*3)
|
||||
//12: enhanced effect sliders, 2D & mapping options
|
||||
udpOut[11] = 12;
|
||||
col = mainseg.colors[1];
|
||||
@ -104,7 +104,7 @@ void notify(byte callMode, bool followUp)
|
||||
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
|
||||
size_t s = 0, nsegs = strip.getSegmentsNum();
|
||||
for (size_t i = 0; i < nsegs; i++) {
|
||||
Segment &selseg = strip.getSegment(i);
|
||||
const Segment &selseg = strip.getSegment(i);
|
||||
if (!selseg.isActive()) continue;
|
||||
unsigned ofs = 41 + s*UDP_SEG_SIZE; //start of segment offset byte
|
||||
udpOut[0 +ofs] = s;
|
||||
@ -177,7 +177,7 @@ void notify(byte callMode, bool followUp)
|
||||
memcpy(buffer.data + packetSize, &udpOut[41+i*UDP_SEG_SIZE], UDP_SEG_SIZE);
|
||||
packetSize += UDP_SEG_SIZE;
|
||||
if (packetSize + UDP_SEG_SIZE < bufferSize) continue;
|
||||
DEBUG_PRINTF_P(PSTR("ESP-NOW sending packet: %d (%d)\n"), (int)buffer.packet, packetSize+3);
|
||||
DEBUG_PRINTF_P(PSTR("ESP-NOW sending packet: %d (%u)\n"), (int)buffer.packet, packetSize+3);
|
||||
err = quickEspNow.send(ESPNOW_BROADCAST_ADDRESS, reinterpret_cast<const uint8_t*>(&buffer), packetSize+3);
|
||||
buffer.packet++;
|
||||
packetSize = 0;
|
||||
@ -266,13 +266,13 @@ static void parseNotifyPacket(const uint8_t *udpIn) {
|
||||
strip.resume();
|
||||
}
|
||||
size_t inactiveSegs = 0;
|
||||
for (size_t i = 0; i < numSrcSegs && i < strip.getMaxSegments(); i++) {
|
||||
for (size_t i = 0; i < numSrcSegs && i < WS2812FX::getMaxSegments(); i++) {
|
||||
unsigned ofs = 41 + i*udpIn[40]; //start of segment offset byte
|
||||
unsigned id = udpIn[0 +ofs];
|
||||
DEBUG_PRINTF_P(PSTR("UDP segment received: %u\n"), id);
|
||||
if (id > strip.getSegmentsNum()) break;
|
||||
else if (id == strip.getSegmentsNum()) {
|
||||
if (receiveSegmentBounds && id < strip.getMaxSegments()) strip.appendSegment();
|
||||
if (receiveSegmentBounds && id < WS2812FX::getMaxSegments()) strip.appendSegment();
|
||||
else break;
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("UDP segment check: %u\n"), id);
|
||||
@ -327,7 +327,7 @@ static void parseNotifyPacket(const uint8_t *udpIn) {
|
||||
// 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)
|
||||
DEBUG_PRINTF_P(PSTR("Apply options: %u\n"), id);
|
||||
selseg.options = (selseg.options & 0b0000000000110001U) | (udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
|
||||
selseg.options = (selseg.options & 0b0000000000110001U) | ((uint16_t)udpIn[28+ofs]<<8) | (udpIn[9 +ofs] & 0b11001110U); // ignore selected, freeze, reset
|
||||
if (applyEffects) {
|
||||
DEBUG_PRINTF_P(PSTR("Apply sliders: %u\n"), id);
|
||||
selseg.custom1 = udpIn[29+ofs];
|
||||
@ -406,31 +406,26 @@ static void parseNotifyPacket(const uint8_t *udpIn) {
|
||||
stateUpdated(CALL_MODE_NOTIFICATION);
|
||||
}
|
||||
|
||||
// realtimeLock() is called from UDP notifications, JSON API or serial Ada
|
||||
void realtimeLock(uint32_t timeoutMs, byte md)
|
||||
{
|
||||
if (!realtimeMode && !realtimeOverride) {
|
||||
unsigned stop, start;
|
||||
if (useMainSegmentOnly) {
|
||||
Segment& mainseg = strip.getMainSegment();
|
||||
start = mainseg.start;
|
||||
stop = mainseg.stop;
|
||||
mainseg.clear(); // clear entire segment (in case sender transmits less pixels)
|
||||
mainseg.freeze = true;
|
||||
// if WLED was off and using main segment only, freeze non-main segments so they stay off
|
||||
if (bri == 0) {
|
||||
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
|
||||
strip.getSegment(s).freeze = true;
|
||||
}
|
||||
for (size_t s = 0; s < strip.getSegmentsNum(); s++) strip.getSegment(s).freeze = true;
|
||||
}
|
||||
} else {
|
||||
start = 0;
|
||||
stop = strip.getLengthTotal();
|
||||
// clear entire strip
|
||||
strip.fill(BLACK);
|
||||
}
|
||||
// if strip is off (bri==0) and not already in RTM
|
||||
if (briT == 0) {
|
||||
strip.setBrightness(scaledBri(briLast), true);
|
||||
}
|
||||
// clear strip/segment
|
||||
for (size_t i = start; i < stop; i++) strip.setPixelColor(i,BLACK);
|
||||
}
|
||||
// if strip is off (bri==0) and not already in RTM
|
||||
if (briT == 0 && !realtimeMode && !realtimeOverride) {
|
||||
strip.setBrightness(scaledBri(briLast), true);
|
||||
}
|
||||
|
||||
if (realtimeTimeout != UINT32_MAX) {
|
||||
@ -452,6 +447,7 @@ void exitRealtime() {
|
||||
realtimeIP[0] = 0;
|
||||
if (useMainSegmentOnly) { // unfreeze live segment again
|
||||
strip.getMainSegment().freeze = false;
|
||||
strip.trigger();
|
||||
} else {
|
||||
strip.show(); // possible fix for #3589
|
||||
}
|
||||
@ -481,7 +477,8 @@ void handleNotifications()
|
||||
if (e131NewData && millis() - strip.getLastShow() > 15)
|
||||
{
|
||||
e131NewData = false;
|
||||
strip.show();
|
||||
if (useMainSegmentOnly) strip.trigger();
|
||||
else strip.show();
|
||||
}
|
||||
|
||||
//unlock strip when realtime UDP times out
|
||||
@ -508,13 +505,13 @@ void handleNotifications()
|
||||
uint8_t lbuf[packetSize];
|
||||
rgbUdp.read(lbuf, packetSize);
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
unsigned totalLen = strip.getLengthTotal();
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
|
||||
for (size_t i = 0, id = 0; i < packetSize -2 && id < totalLen; i += 3, id++) {
|
||||
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
|
||||
}
|
||||
if (!(realtimeMode && useMainSegmentOnly)) strip.show();
|
||||
if (useMainSegmentOnly) strip.trigger();
|
||||
else strip.show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -583,7 +580,7 @@ void handleNotifications()
|
||||
|
||||
realtimeIP = (isSupp) ? notifier2Udp.remoteIP() : notifierUdp.remoteIP();
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_TPM2NET);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
|
||||
tpmPacketCount++; //increment the packet count
|
||||
if (tpmPacketCount == 1) tpmPayloadFrameSize = (udpIn[2] << 8) + udpIn[3]; //save frame size for the whole payload if this is the first packet
|
||||
@ -592,13 +589,13 @@ void handleNotifications()
|
||||
|
||||
unsigned id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
|
||||
unsigned totalLen = strip.getLengthTotal();
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
|
||||
for (size_t i = 6; i < tpmPayloadFrameSize + 4U && id < totalLen; i += 3, id++) {
|
||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
||||
}
|
||||
if (tpmPacketCount == numPackets) { //reset packet count and show if all packets were received
|
||||
tpmPacketCount = 0;
|
||||
strip.show();
|
||||
if (useMainSegmentOnly) strip.trigger();
|
||||
else strip.show();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -610,17 +607,15 @@ void handleNotifications()
|
||||
DEBUG_PRINTLN(realtimeIP);
|
||||
if (packetSize < 2) return;
|
||||
|
||||
if (udpIn[1] == 0)
|
||||
{
|
||||
realtimeTimeout = 0;
|
||||
if (udpIn[1] == 0) {
|
||||
realtimeTimeout = 0; // cancel realtime mode immediately
|
||||
return;
|
||||
} else {
|
||||
realtimeLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP);
|
||||
}
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
if (realtimeOverride) return;
|
||||
|
||||
unsigned totalLen = strip.getLengthTotal();
|
||||
if (useMainSegmentOnly) strip.getMainSegment().beginDraw(); // set up parameters for get/setPixelColor()
|
||||
if (udpIn[0] == 1 && packetSize > 5) //warls
|
||||
{
|
||||
for (size_t i = 2; i < packetSize -3; i += 4)
|
||||
@ -654,7 +649,8 @@ void handleNotifications()
|
||||
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
||||
}
|
||||
}
|
||||
strip.show();
|
||||
if (useMainSegmentOnly) strip.trigger();
|
||||
else strip.show();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -679,20 +675,7 @@ void handleNotifications()
|
||||
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
|
||||
{
|
||||
unsigned pix = i + arlsOffset;
|
||||
if (pix < strip.getLengthTotal()) {
|
||||
if (!arlsDisableGammaCorrection && gammaCorrectCol) {
|
||||
r = gamma8(r);
|
||||
g = gamma8(g);
|
||||
b = gamma8(b);
|
||||
w = gamma8(w);
|
||||
}
|
||||
uint32_t col = RGBW32(r,g,b,w);
|
||||
if (useMainSegmentOnly) {
|
||||
strip.getMainSegment().setPixelColor(pix, col); // this expects that strip.getMainSegment().beginDraw() has been called in handleNotification()
|
||||
} else {
|
||||
strip.setPixelColor(pix, col);
|
||||
}
|
||||
}
|
||||
strip.setRealtimePixelColor(pix, RGBW32(r,g,b,w));
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
@ -808,7 +791,7 @@ static size_t sequenceNumber = 0; // this needs to be shared across all ou
|
||||
static const size_t ART_NET_HEADER_SIZE = 12;
|
||||
static const byte ART_NET_HEADER[] PROGMEM = {0x41,0x72,0x74,0x2d,0x4e,0x65,0x74,0x00,0x00,0x50,0x00,0x0e};
|
||||
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri, bool isRGBW) {
|
||||
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t *buffer, uint8_t bri, bool isRGBW) {
|
||||
if (!(apActive || interfacesInited) || !client[0] || !length) return 1; // network not initialised or dummy/unset IP address 031522 ajn added check for ap
|
||||
|
||||
WiFiUDP ddpUdp;
|
||||
|
@ -4,17 +4,17 @@
|
||||
|
||||
|
||||
//helper to get int value at a position in string
|
||||
int getNumVal(const String* req, uint16_t pos)
|
||||
int getNumVal(const String &req, uint16_t pos)
|
||||
{
|
||||
return req->substring(pos+3).toInt();
|
||||
return req.substring(pos+3).toInt();
|
||||
}
|
||||
|
||||
|
||||
//helper to get int value with in/decrementing support via ~ syntax
|
||||
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||
void parseNumber(const char* str, byte &val, byte minv, byte maxv)
|
||||
{
|
||||
if (str == nullptr || str[0] == '\0') return;
|
||||
if (str[0] == 'r') {*val = hw_random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
|
||||
if (str[0] == 'r') {val = hw_random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
|
||||
bool wrap = false;
|
||||
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
|
||||
if (str[0] == '~') {
|
||||
@ -22,19 +22,19 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||
if (out == 0) {
|
||||
if (str[1] == '0') return;
|
||||
if (str[1] == '-') {
|
||||
*val = (int)(*val -1) < (int)minv ? maxv : min((int)maxv,(*val -1)); //-1, wrap around
|
||||
val = (int)(val -1) < (int)minv ? maxv : min((int)maxv,(val -1)); //-1, wrap around
|
||||
} else {
|
||||
*val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1)); //+1, wrap around
|
||||
val = (int)(val +1) > (int)maxv ? minv : max((int)minv,(val +1)); //+1, wrap around
|
||||
}
|
||||
} else {
|
||||
if (wrap && *val == maxv && out > 0) out = minv;
|
||||
else if (wrap && *val == minv && out < 0) out = maxv;
|
||||
if (wrap && val == maxv && out > 0) out = minv;
|
||||
else if (wrap && val == minv && out < 0) out = maxv;
|
||||
else {
|
||||
out += *val;
|
||||
out += val;
|
||||
if (out > maxv) out = maxv;
|
||||
if (out < minv) out = minv;
|
||||
}
|
||||
*val = out;
|
||||
val = out;
|
||||
}
|
||||
return;
|
||||
} else if (minv == maxv && minv == 0) { // limits "unset" i.e. both 0
|
||||
@ -49,14 +49,14 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
|
||||
}
|
||||
}
|
||||
}
|
||||
*val = atoi(str);
|
||||
val = atoi(str);
|
||||
}
|
||||
|
||||
//getVal supports inc/decrementing and random ("X~Y(r|~[w][-][Z])" form)
|
||||
bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
|
||||
bool getVal(JsonVariant elem, byte &val, byte vmin, byte vmax) {
|
||||
if (elem.is<int>()) {
|
||||
if (elem < 0) return false; //ignore e.g. {"ps":-1}
|
||||
*val = elem;
|
||||
val = elem;
|
||||
return true;
|
||||
} else if (elem.is<const char*>()) {
|
||||
const char* str = elem;
|
||||
@ -82,7 +82,7 @@ bool getBoolVal(const JsonVariant &elem, bool dflt) {
|
||||
}
|
||||
|
||||
|
||||
bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv)
|
||||
bool updateVal(const char* req, const char* key, byte &val, byte minv, byte maxv)
|
||||
{
|
||||
const char *v = strstr(req, key);
|
||||
if (v) v += strlen(key);
|
||||
@ -619,6 +619,68 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
|
||||
return hw_random(diff) + lowerlimit;
|
||||
}
|
||||
|
||||
#ifndef ESP8266
|
||||
void *p_malloc(size_t size) {
|
||||
int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (heap_caps_get_free_size(caps2) > 3*MIN_HEAP_SIZE && size < 512) std::swap(caps1, caps2); // use DRAM for small alloactions & when heap is plenty
|
||||
return heap_caps_malloc_prefer(size, 2, caps1, caps2); // otherwise prefer PSRAM if it exists
|
||||
}
|
||||
return heap_caps_malloc(size, caps2);
|
||||
}
|
||||
|
||||
void *p_realloc(void *ptr, size_t size) {
|
||||
int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (heap_caps_get_free_size(caps2) > 3*MIN_HEAP_SIZE && size < 512) std::swap(caps1, caps2); // use DRAM for small alloactions & when heap is plenty
|
||||
return heap_caps_realloc_prefer(ptr, size, 2, caps1, caps2); // otherwise prefer PSRAM if it exists
|
||||
}
|
||||
return heap_caps_realloc(ptr, size, caps2);
|
||||
}
|
||||
|
||||
void *p_calloc(size_t count, size_t size) {
|
||||
int caps1 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (heap_caps_get_free_size(caps2) > 3*MIN_HEAP_SIZE && size < 512) std::swap(caps1, caps2); // use DRAM for small alloactions & when heap is plenty
|
||||
return heap_caps_calloc_prefer(count, size, 2, caps1, caps2); // otherwise prefer PSRAM if it exists
|
||||
}
|
||||
return heap_caps_calloc(count, size, caps2);
|
||||
}
|
||||
|
||||
void *d_malloc(size_t size) {
|
||||
int caps1 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (size > MIN_HEAP_SIZE) std::swap(caps1, caps2); // prefer PSRAM for large alloactions
|
||||
return heap_caps_malloc_prefer(size, 2, caps1, caps2); // otherwise prefer DRAM
|
||||
}
|
||||
return heap_caps_malloc(size, caps1);
|
||||
}
|
||||
|
||||
void *d_realloc(void *ptr, size_t size) {
|
||||
int caps1 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (size > MIN_HEAP_SIZE) std::swap(caps1, caps2); // prefer PSRAM for large alloactions
|
||||
return heap_caps_realloc_prefer(ptr, size, 2, caps1, caps2); // otherwise prefer DRAM
|
||||
}
|
||||
return heap_caps_realloc(ptr, size, caps1);
|
||||
}
|
||||
|
||||
void *d_calloc(size_t count, size_t size) {
|
||||
int caps1 = MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT;
|
||||
int caps2 = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
|
||||
if (psramSafe) {
|
||||
if (size > MIN_HEAP_SIZE) std::swap(caps1, caps2); // prefer PSRAM for large alloactions
|
||||
return heap_caps_calloc_prefer(count, size, 2, caps1, caps2); // otherwise prefer DRAM
|
||||
}
|
||||
return heap_caps_calloc(count, size, caps1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fixed point integer based Perlin noise functions by @dedehai
|
||||
* Note: optimized for speed and to mimic fastled inoise functions, not for accuracy or best randomness
|
||||
|
@ -529,6 +529,7 @@ void WLED::setup()
|
||||
void WLED::beginStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
strip.setTransition(0); // temporarily prevent transitions to reduce segment copies
|
||||
strip.finalizeInit(); // busses created during deserializeConfig() if config existed
|
||||
strip.makeAutoSegments();
|
||||
strip.setBrightness(0);
|
||||
@ -557,6 +558,8 @@ void WLED::beginStrip()
|
||||
applyPreset(bootPreset, CALL_MODE_INIT);
|
||||
}
|
||||
|
||||
strip.setTransition(transitionDelayDefault); // restore transitions
|
||||
|
||||
// init relay pin
|
||||
if (rlyPin >= 0) {
|
||||
pinMode(rlyPin, rlyOpenDrain ? OUTPUT_OPEN_DRAIN : OUTPUT);
|
||||
@ -747,7 +750,9 @@ void WLED::handleConnection()
|
||||
static bool scanDone = true;
|
||||
static byte stacO = 0;
|
||||
const unsigned long now = millis();
|
||||
#ifdef WLED_DEBUG
|
||||
const unsigned long nowS = now/1000;
|
||||
#endif
|
||||
const bool wifiConfigured = WLED_WIFI_CONFIGURED;
|
||||
|
||||
// ignore connection handling if WiFi is configured and scan still running
|
||||
|
@ -64,6 +64,9 @@
|
||||
//This is generally a terrible idea, but improves boot success on boards with a 3.3v regulator + cap setup that can't provide 400mA peaks
|
||||
//#define WLED_DISABLE_BROWNOUT_DET
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
// Library inclusions.
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP8266
|
||||
@ -607,6 +610,8 @@ WLED_GLOBAL bool wasConnected _INIT(false);
|
||||
|
||||
// color
|
||||
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
|
||||
WLED_GLOBAL std::vector<CRGBPalette16> customPalettes; // custom palettes
|
||||
WLED_GLOBAL uint8_t paletteBlend _INIT(0); // determines blending and wrapping of palette: 0: blend, wrap if moving (SEGMENT.speed>0); 1: blend, always wrap; 2: blend, never wrap; 3: don't blend or wrap
|
||||
|
||||
// transitions
|
||||
WLED_GLOBAL uint8_t blendingStyle _INIT(0); // effect blending/transitionig style
|
||||
@ -617,6 +622,7 @@ WLED_GLOBAL unsigned long transitionStartTime;
|
||||
WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt")
|
||||
WLED_GLOBAL uint8_t randomPaletteChangeTime _INIT(5); // amount of time [s] between random palette changes (min: 1s, max: 255s)
|
||||
WLED_GLOBAL bool useHarmonicRandomPalette _INIT(true); // use *harmonic* random palette generation (nicer looking) or truly random
|
||||
WLED_GLOBAL bool useRainbowWheel _INIT(false); // use "rainbow" color wheel instead of "spectrum" color wheel
|
||||
|
||||
// nightlight
|
||||
WLED_GLOBAL bool nightlightActive _INIT(false);
|
||||
|
@ -225,7 +225,7 @@ void loadSettingsFromEEPROM()
|
||||
if (lastEEPROMversion > 7)
|
||||
{
|
||||
//strip.paletteFade = EEPROM.read(374);
|
||||
strip.paletteBlend = EEPROM.read(382);
|
||||
paletteBlend = EEPROM.read(382);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
|
@ -176,7 +176,7 @@ static void handleUpload(AsyncWebServerRequest *request, const String& filename,
|
||||
doReboot = true;
|
||||
request->send(200, FPSTR(CONTENT_TYPE_PLAIN), F("Configuration restore successful.\nRebooting..."));
|
||||
} else {
|
||||
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) strip.loadCustomPalettes();
|
||||
if (filename.indexOf(F("palette")) >= 0 && filename.indexOf(F(".json")) >= 0) loadCustomPalettes();
|
||||
request->send(200, FPSTR(CONTENT_TYPE_PLAIN), F("File Uploaded!"));
|
||||
}
|
||||
cacheInvalidate++;
|
||||
|
@ -291,12 +291,11 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
printSetFormValue(settingsScript,PSTR("CB"),Bus::getCCTBlend());
|
||||
printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps());
|
||||
printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode());
|
||||
printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer);
|
||||
printSetFormCheckbox(settingsScript,PSTR("PR"),BusManager::hasParallelOutput()); // get it from bus manager not global variable
|
||||
|
||||
unsigned sumMa = 0;
|
||||
for (int s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
const Bus* bus = BusManager::getBus(s);
|
||||
for (size_t s = 0; s < BusManager::getNumBusses(); s++) {
|
||||
const Bus *bus = BusManager::getBus(s);
|
||||
if (!bus || !bus->isOk()) break; // should not happen but for safety
|
||||
int offset = s < 10 ? '0' : 'A' - 10;
|
||||
char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin
|
||||
@ -380,7 +379,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
printSetFormValue(settingsScript,PSTR("TB"),nightlightTargetBri);
|
||||
printSetFormValue(settingsScript,PSTR("TL"),nightlightDelayMinsDefault);
|
||||
printSetFormValue(settingsScript,PSTR("TW"),nightlightMode);
|
||||
printSetFormIndex(settingsScript,PSTR("PB"),strip.paletteBlend);
|
||||
printSetFormIndex(settingsScript,PSTR("PB"),paletteBlend);
|
||||
printSetFormCheckbox(settingsScript,PSTR("RW"),useRainbowWheel);
|
||||
printSetFormValue(settingsScript,PSTR("RL"),rlyPin);
|
||||
printSetFormCheckbox(settingsScript,PSTR("RM"),rlyMde);
|
||||
printSetFormCheckbox(settingsScript,PSTR("RO"),rlyOpenDrain);
|
||||
@ -670,16 +670,14 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
||||
#ifndef WLED_DISABLE_2D
|
||||
settingsScript.printf_P(PSTR("maxPanels=%d;resetPanels();"),WLED_MAX_PANELS);
|
||||
if (strip.isMatrix) {
|
||||
if(strip.panels>0){
|
||||
printSetFormValue(settingsScript,PSTR("PW"),strip.panel[0].width); //Set generator Width and Height to first panel size for convenience
|
||||
printSetFormValue(settingsScript,PSTR("PH"),strip.panel[0].height);
|
||||
}
|
||||
printSetFormValue(settingsScript,PSTR("MPC"),strip.panels);
|
||||
printSetFormValue(settingsScript,PSTR("PW"),strip.panel.size()>0?strip.panel[0].width:8); //Set generator Width and Height to first panel size for convenience
|
||||
printSetFormValue(settingsScript,PSTR("PH"),strip.panel.size()>0?strip.panel[0].height:8);
|
||||
printSetFormValue(settingsScript,PSTR("MPC"),strip.panel.size());
|
||||
// panels
|
||||
for (unsigned i=0; i<strip.panels; i++) {
|
||||
for (unsigned i=0; i<strip.panel.size(); i++) {
|
||||
settingsScript.printf_P(PSTR("addPanel(%d);"), i);
|
||||
char pO[8] = { '\0' };
|
||||
snprintf_P(pO, 7, PSTR("P%d"), i); // WLED_MAX_PANELS is 18 so pO will always only be 4 characters or less
|
||||
snprintf_P(pO, 7, PSTR("P%d"), i); // WLED_WLED_MAX_PANELS is less than 100 so pO will always only be 4 characters or less
|
||||
pO[7] = '\0';
|
||||
unsigned l = strlen(pO);
|
||||
// create P0B, P1B, ..., P63B, etc for other PxxX
|
||||
|
Loading…
x
Reference in New Issue
Block a user