Multiple updates:

- additional debug timings
- removed local leds[] buffer
- async segment bounds change (crashes seen otherwise)
- added isActive() check to Segment drawing methods
- ABL simplification
- palette option for Black hole (FX)
- (possible) crash mitigation is Segment handling (rapid preset changes)
This commit is contained in:
Blaz Kristan 2023-07-12 20:52:34 +02:00
parent 6267d11e51
commit fa6070c680
8 changed files with 210 additions and 218 deletions

View File

@ -606,7 +606,6 @@ static const char _data_FX_MODE_TWINKLE[] PROGMEM = "Twinkle@!,!;!,!;!;;m12=0";
uint16_t dissolve(uint32_t color) {
//bool wa = (SEGCOLOR(1) != 0 && strip.getBrightness() < 255); //workaround, can't compare getPixel to color if not full brightness
if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(SEGCOLOR(1));
}
@ -1205,7 +1204,6 @@ uint16_t mode_fireworks() {
const uint16_t height = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(SEGCOLOR(1));
SEGENV.aux0 = UINT16_MAX;
SEGENV.aux1 = UINT16_MAX;
@ -1904,7 +1902,6 @@ static const char _data_FX_MODE_PRIDE_2015[] PROGMEM = "Pride 2015@!;;";
uint16_t mode_juggle(void) {
if (SEGLEN == 1) return mode_static();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds(); //lossless getPixelColor()
SEGMENT.fill(BLACK);
}
@ -4585,7 +4582,6 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
// initialize on first call
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4595,22 +4591,22 @@ uint16_t mode_2DBlackHole(void) { // By: Stepko https://editor.soulma
for (size_t i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.custom1>>3, 0, cols - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.intensity>>3, 0, rows - 1, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255));
SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(i*32, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// inner stars
for (size_t i = 0; i < 4; i++) {
x = beatsin8(SEGMENT.custom2>>3, cols/4, cols - 1 - cols/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.custom3 , rows/4, rows - 1 - rows/4, 0, ((i % 2) ? 192 : 64) + t * i);
SEGMENT.addPixelColorXY(x, y, CHSV(i*32, 255, 255));
SEGMENT.addPixelColorXY(x, y, SEGMENT.color_from_palette(255-i*64, false, PALETTE_SOLID_WRAP, SEGMENT.check1?0:255));
}
// central white dot
SEGMENT.setPixelColorXY(cols/2, rows/2, CHSV(0, 0, 255));
SEGMENT.setPixelColorXY(cols/2, rows/2, WHITE);
// blur everything a bit
SEGMENT.blur(16);
return FRAMETIME;
} // mode_2DBlackHole()
static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.;;;2";
static const char _data_FX_MODE_2DBLACKHOLE[] PROGMEM = "Black Hole@Fade rate,Outer Y freq.,Outer X freq.,Inner X freq.,Inner Y freq.,Solid;!;!;2;pal=11";
////////////////////////////
@ -4623,7 +4619,6 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.aux0 = 0; // start with red hue
}
@ -4677,7 +4672,6 @@ uint16_t mode_2Ddna(void) { // dna originally by by ldirko at https://pa
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4704,7 +4698,6 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4750,7 +4743,6 @@ uint16_t mode_2DDrift() { // By: Stepko https://editor.soulmateli
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4781,7 +4773,6 @@ uint16_t mode_2Dfirenoise(void) { // firenoise2d. By Andrew Tuline
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4816,7 +4807,6 @@ uint16_t mode_2DFrizzles(void) { // By: Stepko https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -4855,8 +4845,6 @@ uint16_t mode_2Dgameoflife(void) { // Written by Ewoud Wijma, inspired by https:
CRGB backgroundColor = SEGCOLOR(1);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.call == 0 || strip.now - SEGMENT.step > 3000) {
SEGENV.step = strip.now;
SEGENV.aux0 = 0;
@ -5122,7 +5110,6 @@ uint16_t mode_2Dmatrix(void) { // Matrix2D. By Jeremy Williams.
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5267,7 +5254,6 @@ uint16_t mode_2DPlasmaball(void) { // By: Stepko https://edito
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5315,7 +5301,6 @@ uint16_t mode_2DPolarLights(void) { // By: Kostyantyn Matviyevskyy https
CRGBPalette16 auroraPalette = {0x000000, 0x003300, 0x006600, 0x009900, 0x00cc00, 0x00ff00, 0x33ff00, 0x66ff00, 0x99ff00, 0xccff00, 0xffff00, 0xffcc00, 0xff9900, 0xff6600, 0xff3300, 0xff0000};
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.step = 0;
}
@ -5365,7 +5350,6 @@ uint16_t mode_2DPulser(void) { // By: ldirko https://edi
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5393,7 +5377,6 @@ uint16_t mode_2DSindots(void) { // By: ldirko http
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5425,7 +5408,6 @@ uint16_t mode_2Dsquaredswirl(void) { // By: Mark Kriegsman. https://g
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5468,7 +5450,6 @@ uint16_t mode_2DSunradiation(void) { // By: ldirko https://edi
byte *bump = reinterpret_cast<byte*>(SEGENV.data);
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5516,7 +5497,6 @@ uint16_t mode_2Dtartan(void) { // By: Elliott Kember https://editor.so
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5556,7 +5536,6 @@ uint16_t mode_2Dspaceships(void) { //// Space ships by stepko (c)05.02.21 [ht
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -5625,7 +5604,6 @@ uint16_t mode_2Dcrazybees(void) {
bee_t *bee = reinterpret_cast<bee_t*>(SEGENV.data);
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
for (size_t i = 0; i < n; i++) {
bee[i].posX = random8(0, cols);
@ -5695,7 +5673,6 @@ uint16_t mode_2Dghostrider(void) {
const size_t maxLighters = min(cols + rows, LIGHTERS_AM);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols;
SEGENV.aux1 = rows;
@ -5780,7 +5757,6 @@ uint16_t mode_2Dfloatingblobs(void) {
if (!SEGENV.allocateData(sizeof(blob_t))) return mode_static(); //allocation failed
blob_t *blob = reinterpret_cast<blob_t*>(SEGENV.data);
if (SEGENV.call == 0) SEGMENT.setUpLeds();
if (SEGENV.aux0 != cols || SEGENV.aux1 != rows) {
SEGENV.aux0 = cols; // re-initialise if virtual size changes
SEGENV.aux1 = rows;
@ -5946,7 +5922,6 @@ uint16_t mode_2Ddriftrose(void) {
const float L = min(cols, rows) / 2.f;
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6101,7 +6076,6 @@ uint16_t mode_2DSwirl(void) {
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6148,7 +6122,6 @@ uint16_t mode_2DWaverly(void) {
const uint16_t rows = SEGMENT.virtualHeight();
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6377,7 +6350,6 @@ uint16_t mode_matripix(void) { // Matripix. By Andrew Tuline.
int16_t volumeRaw = *(int16_t*)um_data->u_data[1];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6505,7 +6477,6 @@ uint16_t mode_pixelwave(void) { // Pixelwave. By Andrew Tuline.
// even with 1D effect we have to take logic for 2D segments for allocation as fill_solid() fills whole segment
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6731,7 +6702,6 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6800,7 +6770,6 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -6902,7 +6871,6 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
float volumeSmth = *(float*)um_data->u_data[0];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -7087,7 +7055,6 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
if (FFT_MajorPeak < 1) FFT_MajorPeak = 1; // log10(0) is "forbidden" (throws exception)
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
SEGENV.aux0 = 255;
SEGMENT.custom1 = *binNum;
@ -7204,7 +7171,6 @@ uint16_t mode_2DFunkyPlank(void) { // Written by ??? Adapted by Wil
uint8_t *fftResult = (uint8_t*)um_data->u_data[2];
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
SEGMENT.fill(BLACK);
}
@ -7417,7 +7383,6 @@ uint16_t mode_2Dsoap() {
// init
if (SEGENV.call == 0) {
SEGMENT.setUpLeds();
*noise32_x = random16();
*noise32_y = random16();
*noise32_z = random16();
@ -7535,12 +7500,12 @@ uint16_t mode_2Doctopus() {
SEGENV.aux1 = rows;
*offsX = SEGMENT.custom1;
*offsY = SEGMENT.custom2;
const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255;
const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255;
const int C_X = (cols / 2) + ((SEGMENT.custom1 - 128)*cols)/255;
const int C_Y = (rows / 2) + ((SEGMENT.custom2 - 128)*rows)/255;
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu
rMap[XY(x, y)].angle = 40.7436f * atan2f((y - C_Y), (x - C_X)); // avoid 128*atan2()/PI
rMap[XY(x, y)].radius = hypotf((x - C_X), (y - C_Y)) * mapp; //thanks Sutaburosu
}
}
}

View File

@ -329,7 +329,7 @@ typedef enum mapping1D2D {
M12_pCorner = 3
} mapping1D2D_t;
// segment, 72 bytes
// segment, 80 bytes
typedef struct Segment {
public:
uint16_t start; // start index / start X coordinate 2D (left)
@ -370,7 +370,7 @@ typedef struct Segment {
};
uint8_t startY; // start Y coodrinate 2D (top); there should be no more than 255 rows
uint8_t stopY; // stop Y coordinate 2D (bottom); there should be no more than 255 rows
char *name;
char *name;
// runtime data
unsigned long next_time; // millis() of next update
@ -378,8 +378,7 @@ typedef struct Segment {
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
byte* data; // effect data pointer
uint32_t* leds; // local leds[] array (may be a pointer to global)
byte *data; // effect data pointer
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
private:
@ -393,10 +392,13 @@ typedef struct Segment {
uint8_t _reserved : 4;
};
};
uint16_t _dataLen;
uint16_t _dataLen;
static uint16_t _usedSegmentData;
// transition data, valid only if transitional==true, holds values during transition
uint16_t _qStart, _qStop, _qStartY, _qStopY;
bool _queuedChanges;
// transition data, valid only if transitional==true, holds values during transition (72 bytes)
struct Transition {
uint32_t _colorT[NUM_COLORS];
uint8_t _briT; // temporary brightness
@ -407,7 +409,7 @@ typedef struct Segment {
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data
uint32_t _start;
unsigned long _start; // must accommodate millis()
uint16_t _dur;
Transition(uint16_t dur=750)
: _briT(255)
@ -462,9 +464,9 @@ typedef struct Segment {
aux0(0),
aux1(0),
data(nullptr),
leds(nullptr),
_capabilities(0),
_dataLen(0),
_queuedChanges(false),
_t(nullptr)
{
//refreshLightCapabilities();
@ -485,9 +487,8 @@ typedef struct Segment {
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
//Serial.println();
//#endif
if (name) delete[] name;
if (_t) delete _t;
if (leds) free(leds);
if (name) { delete[] name; name = nullptr; }
if (_t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
}
@ -495,7 +496,7 @@ typedef struct Segment {
Segment& operator= (Segment &&orig) noexcept; // move assignment
#ifdef WLED_DEBUG
size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0) + (leds?sizeof(CRGB)*length():0); }
size_t getSize() const { return sizeof(Segment) + (data?_dataLen:0) + (name?strlen(name):0) + (_t?sizeof(Transition):0); }
#endif
inline bool getOption(uint8_t n) const { return ((options >> n) & 0x01); }
@ -505,16 +506,16 @@ typedef struct Segment {
inline bool hasRGB(void) const { return _isRGB; }
inline bool hasWhite(void) const { return _hasW; }
inline bool isCCT(void) const { return _isCCT; }
inline uint16_t width(void) const { return (stop > start) ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return (stopY > startY) ? (stopY - startY) : 0; } // segment height (if 2D) in physical pixels // softhack007: make sure its always > 0
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t width(void) const { return isActive() ? (stop - start) : 0; } // segment width in physical pixels (length if 1D)
inline uint16_t height(void) const { return stopY - startY; } // segment height (if 2D) in physical pixels (it *is* always >=1)
inline uint16_t length(void) const { return width() * height(); } // segment length (count) in physical pixels
inline uint16_t groupLength(void) const { return grouping + spacing; }
inline uint8_t getLightCapabilities(void) const { return _capabilities; }
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1);
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, bool immediate=false);
bool setColor(uint8_t slot, uint32_t c); //returns true if changed
void setCCT(uint16_t k);
void setOpacity(uint8_t o);
@ -536,7 +537,6 @@ typedef struct Segment {
* Safe to call from interrupts and network requests.
*/
inline void markForReset(void) { reset = true; } // setOption(SEG_OPTION_RESET, true)
void setUpLeds(void); // local double buffer/lossless getPixelColor()
// transition functions
void startTransition(uint16_t dur); // transition has to start before actual segment values change
@ -896,7 +896,7 @@ class WS2812FX { // 96 bytes
uint16_t* customMappingTable;
uint16_t customMappingSize;
uint32_t _lastShow;
unsigned long _lastShow;
uint8_t _segment_index;
uint8_t _mainSegment;

View File

@ -189,20 +189,16 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t /*IRAM_ATTR*/ Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels
if (width == 0) return 0; // softhack007 avoid div/0
if (height == 0) return (x%width); // softhack007 avoid div/0
return (x%width) + (y%height) * width;
uint16_t width = virtualWidth(); // segment width in logical pixels (can be 0 if segment is inactive)
uint16_t height = virtualHeight(); // segment height in logical pixels (is always >= 1)
return isActive() ? (x%width) + (y%height) * width : 0;
}
void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (Segment::maxHeight==1) return; // not a matrix set-up
if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
@ -245,7 +241,7 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
// anti-aliased version of setPixelColorXY()
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
{
if (Segment::maxHeight==1) return; // not a matrix set-up
if (!isActive()) return; // not active
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = virtualWidth();
@ -288,7 +284,8 @@ void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
// returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
if (leds) return leds[XY(x,y)];
if (!isActive()) return 0; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return 0; // if pixel would fall out of virtual segment just exit
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
@ -305,6 +302,7 @@ void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
if (!isActive()) return; // not active
if (x >= virtualWidth() || y >= virtualHeight() || x<0 || y<0) return; // if pixel would fall out of virtual segment just exit
uint32_t col = getPixelColorXY(x,y);
uint8_t r = R(col);
@ -324,12 +322,14 @@ void Segment::addPixelColorXY(int x, int y, uint32_t color, bool fast) {
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColorXY(x, y, pix);
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@ -357,6 +357,7 @@ void Segment::blurRow(uint16_t row, fract8 blur_amount) {
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint_fast16_t cols = virtualWidth();
const uint_fast16_t rows = virtualHeight();
@ -384,6 +385,7 @@ void Segment::blurCol(uint16_t col, fract8 blur_amount) {
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols;
@ -436,6 +438,7 @@ void Segment::blur1d(fract8 blur_amount) {
}
void Segment::moveX(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= cols) return;
@ -453,6 +456,7 @@ void Segment::moveX(int8_t delta, bool wrap) {
}
void Segment::moveY(int8_t delta, bool wrap) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta || abs(delta) >= rows) return;
@ -488,6 +492,7 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
}
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
@ -512,6 +517,7 @@ void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
@ -525,6 +531,7 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
}
void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -534,6 +541,7 @@ void Segment::nscale8(uint8_t scale) {
//line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
@ -558,6 +566,7 @@ 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) {
if (!isActive()) return; // not active
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
chr -= 32; // align with font table entries
const uint16_t cols = virtualWidth();
@ -593,6 +602,7 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
#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
if (!isActive()) return; // not active
// extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel

View File

@ -81,22 +81,21 @@ uint16_t Segment::maxHeight = 1;
Segment::Segment(const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copy segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false; // copied segment cannot be in transition
name = nullptr;
data = nullptr;
_dataLen = 0;
_t = nullptr;
leds = nullptr;
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds) { leds = (uint32_t*)malloc(sizeof(uint32_t)*length()); if (leds) memcpy(leds, orig.leds, sizeof(uint32_t)*length()); }
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
}
// move constructor
Segment::Segment(Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.leds = nullptr;
orig.transitional = false; // old segment cannot be in transition any more
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
@ -108,23 +107,22 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTLN(F("-- Copying segment --"));
if (this != &orig) {
// clean destination
transitional = false; // copied segment cannot be in transition
if (name) delete[] name;
if (_t) delete _t;
if (leds) free(leds);
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
transitional = false;
// erase pointers to allocated data
name = nullptr;
data = nullptr;
_dataLen = 0;
_t = nullptr;
leds = nullptr;
// copy source data
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
if (orig.leds) { leds = (uint32_t*)malloc(sizeof(uint32_t)*length()); if (leds) memcpy(leds, orig.leds, sizeof(uint32_t)*length()); }
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
}
return *this;
}
@ -133,16 +131,16 @@ Segment& Segment::operator= (const Segment &orig) {
Segment& Segment::operator= (Segment &&orig) noexcept {
//DEBUG_PRINTLN(F("-- Moving segment --"));
if (this != &orig) {
if (name) delete[] name; // free old name
if (leds) free(leds);
transitional = false; // just temporary
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) delete _t;
if (_t) { delete _t; _t = nullptr; }
memcpy((void*)this, (void*)&orig, sizeof(Segment));
orig.transitional = false; // old segment cannot be in transition
orig.name = nullptr;
orig.data = nullptr;
orig._dataLen = 0;
orig._t = nullptr;
orig.leds = nullptr;
}
return *this;
}
@ -152,12 +150,7 @@ bool Segment::allocateData(size_t len) {
deallocateData();
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
// do not use SPI RAM on ESP32 since it is slow
//#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && defined(WLED_USE_PSRAM)
//if (psramFound())
// data = (byte*) ps_malloc(len);
//else
//#endif
data = (byte*) malloc(len);
data = (byte*) malloc(len);
if (!data) return false; //allocation failed
Segment::addUsedSegmentData(len);
_dataLen = len;
@ -182,28 +175,13 @@ void Segment::deallocateData() {
*/
void Segment::resetIfRequired() {
if (reset) {
if (leds) { free(leds); leds = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
if (_queuedChanges) setUp(_qStart, _qStop, grouping, spacing, offset, _qStartY, _qStopY, true);
reset = false; // setOption(SEG_OPTION_RESET, false);
}
}
void Segment::setUpLeds() {
// deallocation happens in resetIfRequired() as it is called when segment changes or in destructor
if (useGlobalLedBuffer) return; // TODO optional seg buffer for FX without lossy restore due to opacity
// no global buffer
if (leds == nullptr && length() > 0) { //softhack007 quickfix - avoid malloc(0) which is undefined behaviour (should not happen, but i've seen it)
//#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
//if (psramFound())
// leds = (CRGB*)ps_malloc(sizeof(CRGB)*length()); // softhack007 disabled; putting leds into psram leads to horrible slowdown on WROVER boards
//else
//#endif
leds = (uint32_t *)calloc(length(), sizeof(uint32_t));
}
}
CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
static unsigned long _lastPaletteChange = 0; // perhaps it should be per segment
static CRGBPalette16 randomPalette = CRGBPalette16(DEFAULT_COLOR);
@ -326,7 +304,7 @@ void Segment::startTransition(uint16_t dur) {
// transition progression between 0-65535
uint16_t Segment::progress() {
if (!transitional || !_t) return 0xFFFFU;
uint32_t timeNow = millis();
unsigned long timeNow = millis();
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
}
@ -355,7 +333,7 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
// blend palettes
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
// minimum blend time is 100ms maximum is 65535ms
uint32_t timeMS = millis() - _t->_start;
unsigned long timeMS = millis() - _t->_start;
uint16_t noOfBlends = (255U * timeMS / _t->_dur) - _t->_prevPaletteBlends;
for (int i=0; i<noOfBlends; i++, _t->_prevPaletteBlends++) nblendPaletteTowardPalette(_t->_palT, targetPalette, 48);
targetPalette = _t->_palT; // copy transitioning/temporary palette
@ -376,8 +354,9 @@ void Segment::handleTransition() {
}
}
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y) {
//return if neither bounds nor grouping have changed
void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t ofs, uint16_t i1Y, uint16_t i2Y, bool immediate) {
_queuedChanges = false; // cancel anything queued
// return if neither bounds nor grouping have changed
bool boundsUnchanged = (start == i1 && stop == i2);
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) boundsUnchanged &= (startY == i1Y && stopY == i2Y); // 2D
@ -386,29 +365,47 @@ void Segment::setUp(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t
&& (!grp || (grouping == grp && spacing == spc))
&& (ofs == UINT16_MAX || ofs == offset)) return;
if (stop) fill(BLACK); //turn old segment range off
if (i2 <= i1) { //disable segment
stop = 0;
markForReset();
return;
}
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
startY = 0;
stopY = 1;
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) { // 2D
if (i1Y < Segment::maxHeight) startY = i1Y;
stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y);
}
#endif
if (grp) {
if (stop) fill(BLACK); // turn old segment range off (clears pixels if changing spacing)
if (grp) { // prevent assignment of 0
grouping = grp;
spacing = spc;
} else {
grouping = 1;
spacing = 0;
}
if (ofs < UINT16_MAX) offset = ofs;
markForReset();
if (!boundsUnchanged) refreshLightCapabilities();
if (!boundsUnchanged) {
if (immediate) {
if (i2 <= i1) { //disable segment
stop = 0;
return;
}
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
startY = 0;
stopY = 1;
#ifndef WLED_DISABLE_2D
if (Segment::maxHeight>1) { // 2D
if (i1Y < Segment::maxHeight) startY = i1Y;
stopY = i2Y > Segment::maxHeight ? Segment::maxHeight : MAX(1,i2Y);
}
#endif
// safety check
if (start >= stop || startY >= stopY) {
stop = 0;
return;
}
if (!boundsUnchanged) refreshLightCapabilities();
} else {
_qStart = i1;
_qStop = i2;
_qStartY = i1Y;
_qStopY = i2Y;
_queuedChanges = true;
}
}
}
@ -455,8 +452,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
// if we have a valid mode & is not reserved
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
if (fx != mode) {
startTransition(strip.getTransition()); // set effect transitions
//markForReset(); // transition will handle this
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
mode = fx;
// load default values from effect string
@ -541,8 +537,7 @@ uint16_t Segment::virtualLength() const {
return vLen;
}
#endif
uint16_t groupLen = groupLength();
if (groupLen < 1) groupLen = 1; // prevent division by zero - better safe than sorry ...
uint16_t groupLen = groupLength(); // is always >= 1
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (mirror) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
@ -550,6 +545,7 @@ uint16_t Segment::virtualLength() const {
void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
{
if (!isActive()) return; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
#endif
@ -617,8 +613,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
}
#endif
if (leds) leds[i] = col;
uint16_t len = length();
uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) {
@ -660,6 +654,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
// anti-aliased normalized version of setPixelColor()
void Segment::setPixelColor(float i, uint32_t col, bool aa)
{
if (!isActive()) return; // not active
int vStrip = int(i/10.0f); // hack to allow running on virtual strips (2D segment columns/rows)
i -= int(i);
@ -691,6 +686,7 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
uint32_t Segment::getPixelColor(int i)
{
if (!isActive()) return 0; // not active
#ifndef WLED_DISABLE_2D
int vStrip = i>>16;
#endif
@ -718,8 +714,6 @@ uint32_t Segment::getPixelColor(int i)
}
#endif
if (leds) return leds[i];
if (reverse) i = virtualLength() - i - 1;
i *= groupLength();
i += start;
@ -760,6 +754,11 @@ void Segment::refreshLightCapabilities() {
uint16_t segStartIdx = 0xFFFFU;
uint16_t segStopIdx = 0;
if (!isActive()) {
_capabilities = 0;
return;
}
if (start < Segment::maxWidth * Segment::maxHeight) {
// we are withing 2D matrix (includes 1D segments)
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
@ -804,6 +803,7 @@ void Segment::refreshLightCapabilities() {
* Fills segment with color
*/
void Segment::fill(uint32_t c) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
@ -819,6 +819,7 @@ void Segment::blendPixelColor(int n, uint32_t color, uint8_t blend) {
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColor(int n, uint32_t color, bool fast) {
if (!isActive()) return; // not active
uint32_t col = getPixelColor(n);
uint8_t r = R(col);
uint8_t g = G(col);
@ -837,6 +838,7 @@ void Segment::addPixelColor(int n, uint32_t color, bool fast) {
}
void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
if (!isActive()) return; // not active
CRGB pix = CRGB(getPixelColor(n)).nscale8_video(fade);
setPixelColor(n, pix);
}
@ -845,6 +847,7 @@ void Segment::fadePixelColor(uint16_t n, uint8_t fade) {
* fade out function, higher rate = quicker fade
*/
void Segment::fade_out(uint8_t rate) {
if (!isActive()) return; // not active
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -882,7 +885,7 @@ void Segment::fade_out(uint8_t rate) {
// fades all pixels to black using nscale8()
void Segment::fadeToBlackBy(uint8_t fadeBy) {
if (fadeBy == 0) return; // optimization - no scaling to apply
if (!isActive() || fadeBy == 0) return; // optimization - no scaling to apply
const uint16_t cols = is2D() ? virtualWidth() : virtualLength();
const uint16_t rows = virtualHeight(); // will be 1 for 1D
@ -897,7 +900,7 @@ void Segment::fadeToBlackBy(uint8_t fadeBy) {
*/
void Segment::blur(uint8_t blur_amount)
{
if (blur_amount == 0) return; // optimization: 0 means "don't blur"
if (!isActive() || blur_amount == 0) return; // optimization: 0 means "don't blur"
#ifndef WLED_DISABLE_2D
if (is2D()) {
// compatibility with 2D
@ -1071,7 +1074,7 @@ void WS2812FX::finalizeInit(void)
}
void WS2812FX::service() {
uint32_t nowUp = millis(); // Be aware, millis() rolls over every 49 days
unsigned long nowUp = millis(); // Be aware, millis() rolls over every 49 days
now = nowUp + timebase;
if (nowUp - _lastShow < MIN_SHOW_DELAY) return;
bool doShow = false;
@ -1089,7 +1092,6 @@ void WS2812FX::service() {
// last condition ensures all solid segments are updated at the same time
if(nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{
if (seg.grouping == 0) seg.grouping = 1; // sanity check
doShow = true;
uint16_t delay = FRAMETIME;
@ -1117,12 +1119,19 @@ void WS2812FX::service() {
}
_virtualSegmentLength = 0;
busses.setSegmentCCT(-1);
if(doShow) {
_isServicing = false;
_triggered = false;
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow effects."));
#endif
if (doShow) {
yield();
show();
}
_triggered = false;
_isServicing = false;
#ifdef WLED_DEBUG
if (millis() - nowUp > _frametime) DEBUG_PRINTLN(F("Slow strip."));
#endif
}
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
@ -1159,31 +1168,25 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() {
bool useWackyWS2815PowerModel = false;
byte actualMilliampsPerLed = milliampsPerLed;
if(milliampsPerLed == 255) {
useWackyWS2815PowerModel = true;
actualMilliampsPerLed = 12; // from testing an actual strip
}
if (ablMilliampsMax < 150 || actualMilliampsPerLed == 0) { //0 mA per LED and too low numbers turn off calculation
currentMilliamps = 0;
return _brightness;
}
uint16_t pLen = getLengthPhysical();
uint32_t puPerMilliamp = 195075 / actualMilliampsPerLed;
uint32_t powerBudget = (ablMilliampsMax - MA_FOR_ESP) * puPerMilliamp; //100mA for ESP power
if (powerBudget > puPerMilliamp * pLen) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= puPerMilliamp * pLen;
} else {
powerBudget = 0;
if (milliampsPerLed == 255) {
useWackyWS2815PowerModel = true;
actualMilliampsPerLed = 12; // from testing an actual strip
}
uint32_t powerSum = 0; // could overflow if more than 22K LEDs (uint32_t MAX / 195075 PU per LED)
size_t powerBudget = (ablMilliampsMax - MA_FOR_ESP); //100mA for ESP power
size_t pLen = 0; //getLengthPhysical();
size_t powerSum = 0;
for (uint_fast8_t bNum = 0; bNum < busses.getNumBusses(); bNum++) {
Bus *bus = busses.getBus(bNum);
if (bus->getType() >= TYPE_NET_DDP_RGB) continue; //exclude non-physical network busses
if (!IS_DIGITAL(bus->getType())) continue; //exclude non-digital network busses
uint16_t len = bus->getLength();
pLen += len;
uint32_t busPowerSum = 0;
for (uint_fast16_t i = 0; i < len; i++) { //sum up the usage of each LED
uint32_t c = bus->getPixelColor(i); // always returns original or restored color without brightness scaling
@ -1198,38 +1201,44 @@ uint8_t WS2812FX::estimateCurrentAndLimitBri() {
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
busPowerSum *= 3;
busPowerSum = busPowerSum >> 2; //same as /= 4
busPowerSum >>= 2; //same as /= 4
}
powerSum += busPowerSum;
}
uint8_t newBri = _brightness;
uint32_t powerSumUnscaled = powerSum;
powerSum *= _brightness;
if (powerBudget > pLen) { //each LED uses about 1mA in standby, exclude that from power budget
powerBudget -= pLen;
} else {
powerBudget = 0;
}
if (powerSum > powerBudget) { //scale brightness down to stay in current limit
// powerSum has all the values of channels summed (max would be pLen*765 as white is excluded) so convert to milliAmps
powerSum = (powerSum * actualMilliampsPerLed) / 765;
uint8_t newBri = _brightness;
if (powerSum * _brightness / 255 > powerBudget) { //scale brightness down to stay in current limit
float scale = (float)powerBudget / (float)powerSum;
uint16_t scaleI = scale * 255;
uint8_t scaleB = (scaleI > 255) ? 255 : scaleI;
newBri = scale8(_brightness, scaleB);
}
currentMilliamps = (powerSumUnscaled * newBri) / puPerMilliamp;
currentMilliamps = (powerSum * newBri) / 255;
currentMilliamps += MA_FOR_ESP; //add power of ESP back to estimate
currentMilliamps += pLen; //add standby power back to estimate
currentMilliamps += pLen; //add standby power (1mA/LED) back to estimate
return newBri;
}
void WS2812FX::show(void) {
// avoid race condition, caputre _callback value
show_callback callback = _callback;
if (callback) callback();
#ifdef WLED_DEBUG
static unsigned long sumMicros = 0, sumCurrent = 0;
static size_t calls = 0;
unsigned long microsStart = micros();
#endif
// avoid race condition, caputre _callback value
show_callback callback = _callback;
if (callback) callback();
uint8_t busBrightness = estimateCurrentAndLimitBri();
busses.setBrightness(busBrightness);
#ifdef WLED_DEBUG
@ -1240,21 +1249,22 @@ void WS2812FX::show(void) {
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
busses.show();
unsigned long now = millis();
unsigned long diff = now - _lastShow;
uint16_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2;
_lastShow = now;
#ifdef WLED_DEBUG
sumMicros += micros() - microsStart;
if (++calls == 100) {
DEBUG_PRINTF("show calls: %d micros: %lu avg: %lu (current: %lu avg: %lu)\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls);
DEBUG_PRINTF("%d show calls: %lu[us] avg: %lu[us] (current: %lu[us] avg: %lu[us])\n", calls, sumMicros, sumMicros/calls, sumCurrent, sumCurrent/calls);
sumMicros = sumCurrent = 0;
calls = 0;
}
#endif
unsigned long now = millis();
size_t diff = now - _lastShow;
size_t fpsCurr = 200;
if (diff > 0) fpsCurr = 1000 / diff;
_cumulativeFps = (3 * _cumulativeFps + fpsCurr) >> 2;
_lastShow = now;
}
/**
@ -1431,6 +1441,7 @@ Segment& WS2812FX::getSegment(uint8_t id) {
return _segments[id >= _segments.size() ? getMainSegmentId() : id]; // vectors
}
// compatibility method (deprecated)
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset, uint16_t startY, uint16_t stopY) {
if (n >= _segments.size()) return;
_segments[n].setUp(i1, i2, grouping, spacing, offset, startY, stopY);

View File

@ -128,11 +128,6 @@ BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
void BusDigital::show() {
if (!_valid) return;
#ifdef WLED_DEBUG
static unsigned long sumMicros = 0;
static size_t calls = 0;
unsigned long microsStart = micros();
#endif
PolyBus::setBrightness(_busPtr, _iType, _bri);
if (buffering) { // should be _data != nullptr, but that causes ~20% FPS drop
size_t channels = Bus::hasWhite(_type) + 3*Bus::hasRGB(_type);
@ -159,14 +154,6 @@ void BusDigital::show() {
}
PolyBus::show(_busPtr, _iType);
PolyBus::setBrightness(_busPtr, _iType, 255); // restore full brightness at bus level (setting unscaled pixel color)
#ifdef WLED_DEBUG
sumMicros += micros() - microsStart;
if (++calls == 100) {
DEBUG_PRINTF("Bus calls: %d micros: %lu avg: %lu\n", calls, sumMicros, sumMicros/calls);
sumMicros = 0;
calls = 0;
}
#endif
}
bool BusDigital::canShow() {

View File

@ -22,7 +22,7 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
int stop = elem["stop"] | -1;
// if using vectors use this code to append segment
// append segment
if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return false; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal()));
@ -110,7 +110,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs;
}
if (stop > start && of > len -1) of = len -1;
seg.setUp(start, stop, grp, spc, of, startY, stopY);
// update segment (delete if necessary)
// we must not change segment dimensions during drawing of effects as that may produce undesired behaviour (crash)
seg.setUp(start, stop, grp, spc, of, startY, stopY, !strip.isServicing());
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
@ -468,12 +471,14 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
if (segmentBounds) {
root["start"] = seg.start;
root["stop"] = seg.stop;
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
root[F("startY")] = seg.startY;
root[F("stopY")] = seg.stopY;
}
#endif
}
if (!forPreset) root["len"] = (seg.stop >= seg.start) ? (seg.stop - seg.start) : 0;
if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping;
root[F("spc")] = seg.spacing;
root[F("of")] = seg.offset;

View File

@ -23,7 +23,7 @@ void WLED::reset()
#ifdef WLED_ENABLE_WEBSOCKETS
ws.closeAll(1012);
#endif
long dly = millis();
unsigned long dly = millis();
while (millis() - dly < 450) {
yield(); // enough time to send response to client
}
@ -36,13 +36,17 @@ void WLED::loop()
{
#ifdef WLED_DEBUG
static unsigned long lastRun = 0;
size_t loopDelay = (millis() - lastRun);
unsigned long loopMillis = millis();
size_t loopDelay = loopMillis - lastRun;
if (lastRun == 0) loopDelay=0; // startup - don't have valid data from last run.
if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %dms.\n", loopDelay);
if (loopDelay > 2) DEBUG_PRINTF("Loop delayed more than %ums.\n", loopDelay);
static unsigned long maxLoopMillis = 0;
static size_t avgLoopMillis = 0;
static unsigned long maxUsermodMillis = 0;
static size_t avgUsermodMillis = 0;
static unsigned long maxStripMillis = 0;
static size_t avgStripMillis = 0;
unsigned long stripMillis;
#endif
handleTime();
@ -84,6 +88,9 @@ void WLED::loop()
yield();
}
#ifdef WLED_DEBUG
stripMillis = millis();
#endif
if (!realtimeMode || realtimeOverride || (realtimeMode && useMainSegmentOnly)) // block stuff if WARLS/Adalight is enabled
{
if (apActive) dnsServer.processNextRequest();
@ -102,22 +109,18 @@ void WLED::loop()
handlePresets();
yield();
#ifdef WLED_DEBUG
unsigned long stripMillis = millis();
#endif
if (!offMode || strip.isOffRefreshRequired())
strip.service();
#ifdef ESP8266
else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
if (stripMillis > 50) DEBUG_PRINTLN("Slow strip.");
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
}
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
yield();
#ifdef ESP8266
@ -186,8 +189,31 @@ void WLED::loop()
handleWs();
handleStatusLED();
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
// DEBUG serial logging (every 30s)
#ifdef WLED_DEBUG
loopMillis = millis() - loopMillis;
if (loopMillis > 30) {
DEBUG_PRINTF("Loop took %lums.\n", loopMillis);
DEBUG_PRINTF("Usermods took %lums.\n", usermodMillis);
DEBUG_PRINTF("Strip took %lums.\n", stripMillis);
}
avgLoopMillis += loopMillis;
if (loopMillis > maxLoopMillis) maxLoopMillis = loopMillis;
if (millis() - debugTime > 29999) {
DEBUG_PRINTLN(F("---DEBUG INFO---"));
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
@ -210,11 +236,13 @@ void WLED::loop()
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
if (loops > 0) { // avoid division by zero
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("Loop time[ms]: ")); DEBUG_PRINT(avgLoopMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxLoopMillis);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
}
strip.printSize();
loops = 0;
maxLoopMillis = 0;
maxUsermodMillis = 0;
maxStripMillis = 0;
avgUsermodMillis = 0;
@ -224,20 +252,6 @@ void WLED::loop()
loops++;
lastRun = millis();
#endif // WLED_DEBUG
toki.resetTick();
#if WLED_WATCHDOG_TIMEOUT > 0
// we finished our mainloop, reset the watchdog timer
if (!strip.isUpdating())
#ifdef ARDUINO_ARCH_ESP32
esp_task_wdt_reset();
#else
ESP.wdtFeed();
#endif
#endif
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
reset();
}
void WLED::enableWatchdog() {

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2307090
#define VERSION 2307120
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -513,7 +513,7 @@ WLED_GLOBAL uint16_t userVar0 _INIT(0), userVar1 _INIT(0); //available for use i
// wifi
WLED_GLOBAL bool apActive _INIT(false);
WLED_GLOBAL bool forceReconnect _INIT(false);
WLED_GLOBAL uint32_t lastReconnectAttempt _INIT(0);
WLED_GLOBAL unsigned long lastReconnectAttempt _INIT(0);
WLED_GLOBAL bool interfacesInited _INIT(false);
WLED_GLOBAL bool wasConnected _INIT(false);