Fix for transitions.

This commit is contained in:
Blaz Kristan 2022-07-12 18:10:07 +02:00
parent d9f2c2b968
commit 588c7a81fc
6 changed files with 130 additions and 105 deletions

View File

@ -88,9 +88,10 @@ uint32_t color_add(uint32_t,uint32_t);
#define NUM_COLORS 3 /* number of colors per segment */ #define NUM_COLORS 3 /* number of colors per segment */
#define SEGMENT strip._segments[strip.getCurrSegmentId()] #define SEGMENT strip._segments[strip.getCurrSegmentId()]
#define SEGENV strip._segment_runtimes[strip.getCurrSegmentId()] #define SEGENV strip._segments[strip.getCurrSegmentId()].runtime
#define SEGCOLOR(x) strip.segColor(x) #define SEGCOLOR(x) strip.segColor(x)
#define SEGLEN strip._virtualSegmentLength //#define SEGLEN strip._segments[strip.getCurrSegmentId()].virtualLength()
#define SEGLEN strip._virtualSegmentLength /* saves us a few kbytes of code */
#define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN) #define SPEED_FORMULA_L (5U + (50U*(255U - SEGMENT.speed))/SEGLEN)
// some common colors // some common colors
@ -371,7 +372,7 @@ typedef struct ColorTransition { // 20 bytes
uint32_t transitionStart; uint32_t transitionStart;
uint16_t transitionDur; uint16_t transitionDur;
void startTransition(Segment *seg, uint16_t dur, uint8_t oldBri, uint8_t oldCct, uint32_t *oldCol); void startTransition(Segment *seg, uint16_t dur); // transition has to start before actual segment values change
void handleTransition(Segment *seg, uint8_t &newBri, uint8_t &newCct, uint32_t *newCol); void handleTransition(Segment *seg, uint8_t &newBri, uint8_t &newCct, uint32_t *newCol);
uint16_t progress(); //transition progression between 0-65535 uint16_t progress(); //transition progression between 0-65535
uint8_t currentBri(uint8_t briNew, bool useCct = false); uint8_t currentBri(uint8_t briNew, bool useCct = false);
@ -381,6 +382,31 @@ typedef struct ColorTransition { // 20 bytes
} color_transition; } color_transition;
// segment runtime parameters
typedef struct Segmentruntime { // 23 (24 in memory) bytes
unsigned long next_time; // millis() of next update
uint32_t step; // custom "step" var
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
byte* data = nullptr;
bool allocateData(uint16_t len);
void deallocateData();
void resetIfRequired();
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
inline void markForReset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
} segmentruntime;
// segment parameters // segment parameters
typedef struct Segment { // 40 (44 in memory) bytes typedef struct Segment { // 40 (44 in memory) bytes
uint16_t start; // start index / start X coordinate 2D (left) uint16_t start; // start index / start X coordinate 2D (left)
@ -400,6 +426,7 @@ typedef struct Segment { // 40 (44 in memory) bytes
uint16_t startY; // start Y coodrinate 2D (top) uint16_t startY; // start Y coodrinate 2D (top)
uint16_t stopY; // stop Y coordinate 2D (bottom) uint16_t stopY; // stop Y coordinate 2D (bottom)
char *name; char *name;
segmentruntime runtime;
color_transition transition; color_transition transition;
inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); } inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); }
@ -473,32 +500,6 @@ typedef struct Segment { // 40 (44 in memory) bytes
} segment; } segment;
// segment runtime parameters
typedef struct Segment_runtime { // 23 (24 in memory) bytes
unsigned long next_time; // millis() of next update
uint32_t step; // custom "step" var
uint32_t call; // call counter
uint16_t aux0; // custom var
uint16_t aux1; // custom var
byte* data = nullptr;
bool allocateData(uint16_t len);
void deallocateData();
void resetIfRequired();
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
* Safe to call from interrupts and network requests.
*/
inline void markForReset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
} segment_runtime;
// main "strip" class // main "strip" class
class WS2812FX { // 96 bytes class WS2812FX { // 96 bytes
typedef uint16_t (*mode_ptr)(void); // pointer to mode function typedef uint16_t (*mode_ptr)(void); // pointer to mode function
@ -562,6 +563,7 @@ class WS2812FX { // 96 bytes
inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments. inline void trigger(void) { _triggered = true; } // Forces the next frame to be computed on all active segments.
inline void setShowCallback(show_callback cb) { _callback = cb; } inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; } inline void setTransition(uint16_t t) { _transitionDur = t; }
inline void addUsedSegmentData(int16_t size) { _usedSegmentData += size; }
bool bool
gammaCorrectBri = false, gammaCorrectBri = false,
@ -605,6 +607,7 @@ class WS2812FX { // 96 bytes
inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; } inline uint16_t getMinShowDelay(void) { return MIN_SHOW_DELAY; }
inline uint16_t getLengthTotal(void) { return _length; } inline uint16_t getLengthTotal(void) { return _length; }
inline uint16_t getTransition(void) { return _transitionDur; } inline uint16_t getTransition(void) { return _transitionDur; }
inline uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
uint32_t uint32_t
now, now,
@ -626,7 +629,6 @@ class WS2812FX { // 96 bytes
inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; } inline Segment& getFirstSelectedSeg(void) { return _segments[getFirstSelectedSegId()]; }
inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; } inline Segment& getMainSegment(void) { return _segments[getMainSegmentId()]; }
inline Segment* getSegments(void) { return _segments; } inline Segment* getSegments(void) { return _segments; }
inline Segment_runtime& getSegmentRuntime(uint8_t id) { return _segment_runtimes[id >= MAX_NUM_SEGMENTS ? getMainSegmentId() : id]; }
// 2D support (panels) // 2D support (panels)
bool bool
@ -672,17 +674,19 @@ class WS2812FX { // 96 bytes
CRGBPalette16 currentPalette; CRGBPalette16 currentPalette;
CRGBPalette16 targetPalette; CRGBPalette16 targetPalette;
uint32_t _colors_t[3]; uint8_t _bri_t; // used for opacity transitions
uint32_t _colors_t[3]; // used for color transitions
uint16_t _virtualSegmentLength; uint16_t _virtualSegmentLength;
// using public array to reduce code size increase due to inline function getSegment() (with bounds checking)
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 27 bytes per element segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 27 bytes per element
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities, custom 1, custom 2, custom 3 // start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities, custom 1, custom 2, custom 3
{0, 7, 0, DEFAULT_SPEED, DEFAULT_INTENSITY, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0, DEFAULT_C1, DEFAULT_C2, DEFAULT_C3, 0, 1} {0, 7, 0, DEFAULT_SPEED, DEFAULT_INTENSITY, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0, DEFAULT_C1, DEFAULT_C2, DEFAULT_C3, 0, 1}
}; };
friend class Segment; friend class Segment;
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element //segmentruntime _segmentruntimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
friend class Segment_runtime; //friend class Segmentruntime;
private: private:
uint16_t _length; uint16_t _length;

View File

@ -152,7 +152,7 @@ void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D
if (!strip.isMatrix) return; // not a matrix set-up if (!strip.isMatrix) return; // not a matrix set-up
uint8_t _bri_t = getOption(SEG_OPTION_TRANSITIONAL) ? transition.briOld : opacity; uint8_t _bri_t = strip._bri_t;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);

View File

@ -74,10 +74,12 @@
// ColorTransition class implementation // ColorTransition class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
void ColorTransition::startTransition(Segment *seg, uint16_t dur, uint8_t oldBri, uint8_t oldCct, uint32_t *oldCol) { void ColorTransition::startTransition(Segment *seg, uint16_t dur) {
currentBri(oldBri); // starting a transition has to occur before change
currentBri(oldCct, true); if (!seg->isActive()) return;
for (size_t i=0; i<NUM_COLORS; i++) colorOld[i] = currentColor(i, oldCol[i]); briOld = currentBri(seg->getOption(SEG_OPTION_ON) ? seg->opacity : 0);
cctOld = currentBri(seg->cct, true);
for (size_t i=0; i<NUM_COLORS; i++) colorOld[i] = currentColor(i, seg->colors[i]);
transitionDur = dur; transitionDur = dur;
transitionStart = millis(); transitionStart = millis();
seg->setOption(SEG_OPTION_TRANSITIONAL, true); seg->setOption(SEG_OPTION_TRANSITIONAL, true);
@ -92,16 +94,23 @@ uint16_t ColorTransition::progress() { //transition progression between 0-65535
uint8_t ColorTransition::currentBri(uint8_t briNew, bool useCct) { uint8_t ColorTransition::currentBri(uint8_t briNew, bool useCct) {
uint32_t prog = progress() + 1; uint32_t prog = progress() + 1;
if (useCct) return cctOld = ((briNew * prog) + cctOld * (0x10000 - prog)) >> 16; if (useCct) return ((briNew * prog) + cctOld * (0x10000 - prog)) >> 16;
else return briOld = ((briNew * prog) + briOld * (0x10000 - prog)) >> 16; else return ((briNew * prog) + briOld * (0x10000 - prog)) >> 16;
} }
void ColorTransition::handleTransition(Segment *seg, uint8_t &newBri, uint8_t &newCct, uint32_t *newCol) { void ColorTransition::handleTransition(Segment *seg, uint8_t &newBri, uint8_t &newCct, uint32_t *newCol) {
if (!seg->getOption(SEG_OPTION_TRANSITIONAL)) return; // if segment is not transitioning newBri = currentBri(newBri);
newBri = currentBri(seg->opacity); newCct = currentBri(newCct, true);
newCct = currentBri(seg->cct, true); for (size_t i=0; i<NUM_COLORS; i++) newCol[i] = currentColor(i, newCol[i]);
for (size_t i=0; i<NUM_COLORS; i++) newCol[i] = currentColor(i, seg->colors[i]); unsigned long maxWait = millis() + 22;
if (progress() == 0xFFFFU) seg->setOption(SEG_OPTION_TRANSITIONAL, false); if (seg->mode == FX_MODE_STATIC && seg->runtime.next_time > maxWait) seg->runtime.next_time = maxWait;
if (progress() == 0xFFFFU && seg->getOption(SEG_OPTION_TRANSITIONAL)) {
// finish transition
briOld = newBri;
cctOld = newCct;
for (size_t i=0; i<NUM_COLORS; i++) colorOld[i] = newCol[i];
seg->setOption(SEG_OPTION_TRANSITIONAL, false);
}
} }
@ -111,7 +120,7 @@ void ColorTransition::handleTransition(Segment *seg, uint8_t &newBri, uint8_t &n
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
if (slot >= NUM_COLORS || c == colors[slot]) return false; if (slot >= NUM_COLORS || c == colors[slot]) return false;
transition.startTransition(this, strip.getTransition(), opacity, cct, colors); transition.startTransition(this, strip.getTransition()); // start transition prior to change
colors[slot] = c; colors[slot] = c;
return true; return true;
} }
@ -123,26 +132,21 @@ void Segment::setCCT(uint16_t k) {
k = (k - 1900) >> 5; k = (k - 1900) >> 5;
} }
if (cct == k) return; if (cct == k) return;
transition.startTransition(this, strip.getTransition(), opacity, cct, colors); transition.startTransition(this, strip.getTransition()); // start transition prior to change
cct = k; cct = k;
} }
void Segment::setOpacity(uint8_t o) { void Segment::setOpacity(uint8_t o) {
if (opacity == o) return; if (opacity == o) return;
transition.startTransition(this, strip.getTransition(), opacity, cct, colors); transition.startTransition(this, strip.getTransition()); // start transition prior to change
opacity = o; opacity = o;
} }
void Segment::setOption(uint8_t n, bool val) { void Segment::setOption(uint8_t n, bool val) {
bool prevOn = getOption(SEG_OPTION_ON); bool prevOn = getOption(SEG_OPTION_ON);
if (n == SEG_OPTION_ON && !val && prevOn) { // fade out (off) if (n == SEG_OPTION_ON && val != prevOn) transition.startTransition(this, strip.getTransition()); // start transition prior to change
transition.startTransition(this, strip.getTransition(), opacity, cct, colors);
}
if (val) options |= 0x01 << n; if (val) options |= 0x01 << n;
else options &= ~(0x01 << n); else options &= ~(0x01 << n);
if (n == SEG_OPTION_ON && val && !prevOn) { // fade in (on)
transition.startTransition(this, strip.getTransition(), opacity, cct, colors);
}
} }
// 2D matrix // 2D matrix
@ -180,7 +184,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
} }
uint16_t len = length(); uint16_t len = length();
uint8_t _bri_t = getOption(SEG_OPTION_TRANSITIONAL) ? transition.briOld : opacity; uint8_t _bri_t = strip._bri_t;
if (_bri_t < 255) { if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t); byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t); byte g = scale8(G(col), _bri_t);
@ -484,14 +488,13 @@ uint32_t IRAM_ATTR Segment::color_from_palette(uint16_t i, bool mapping, bool wr
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Segment_runtime class implementation // Segmentruntime class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
bool Segment_runtime::allocateData(uint16_t len){ bool Segmentruntime::allocateData(uint16_t len){
if (data && _dataLen == len) return true; //already allocated if (data && _dataLen == len) return true; //already allocated
WS2812FX *instance = WS2812FX::getInstance();
deallocateData(); deallocateData();
if (instance->_usedSegmentData + len > MAX_SEGMENT_DATA) return false; //not enough memory if (strip.getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
// if possible use SPI RAM on ESP32 // if possible use SPI RAM on ESP32
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) if (psramFound())
@ -500,16 +503,16 @@ bool Segment_runtime::allocateData(uint16_t len){
#endif #endif
data = (byte*) malloc(len); data = (byte*) malloc(len);
if (!data) return false; //allocation failed if (!data) return false; //allocation failed
instance->_usedSegmentData += len; strip.addUsedSegmentData(len);
_dataLen = len; _dataLen = len;
memset(data, 0, len); memset(data, 0, len);
return true; return true;
} }
void Segment_runtime::deallocateData() { void Segmentruntime::deallocateData() {
free(data); free(data);
data = nullptr; data = nullptr;
WS2812FX::getInstance()->_usedSegmentData -= _dataLen; strip.addUsedSegmentData(-(int16_t)_dataLen);
_dataLen = 0; _dataLen = 0;
} }
@ -520,7 +523,7 @@ void Segment_runtime::deallocateData() {
* because it could access the data buffer and this method * because it could access the data buffer and this method
* may free that data buffer. * may free that data buffer.
*/ */
void Segment_runtime::resetIfRequired() { void Segmentruntime::resetIfRequired() {
if (_requiresReset) { if (_requiresReset) {
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
deallocateData(); deallocateData();
@ -538,8 +541,8 @@ void WS2812FX::finalizeInit(void)
{ {
//reset segment runtimes //reset segment runtimes
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segment_runtimes[i].markForReset(); _segments[i].runtime.markForReset();
_segment_runtimes[i].resetIfRequired(); _segments[i].runtime.resetIfRequired();
} }
_hasWhiteChannel = _isOffRefreshRequired = false; _hasWhiteChannel = _isOffRefreshRequired = false;
@ -598,29 +601,29 @@ void WS2812FX::service() {
//if (realtimeMode && useMainSegmentOnly && i == getMainSegmentId()) continue; //if (realtimeMode && useMainSegmentOnly && i == getMainSegmentId()) continue;
_segment_index = i; _segment_index = i;
Segment &seg = _segments[_segment_index];
// reset the segment runtime data if needed, called before isActive to ensure deleted // reset the segment runtime data if needed, called before isActive to ensure deleted
// segment's buffers are cleared // segment's buffers are cleared
_segment_runtimes[_segment_index].resetIfRequired(); seg.runtime.resetIfRequired();
if (!_segments[_segment_index].isActive()) continue; if (!seg.isActive()) continue;
// last condition ensures all solid segments are updated at the same time // last condition ensures all solid segments are updated at the same time
if(nowUp > _segment_runtimes[_segment_index].next_time || _triggered || (doShow && _segments[_segment_index].mode == 0)) if(nowUp > seg.runtime.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
{ {
if (_segments[_segment_index].grouping == 0) _segments[_segment_index].grouping = 1; //sanity check if (seg.grouping == 0) seg.grouping = 1; //sanity check
doShow = true; doShow = true;
uint16_t delay = FRAMETIME; uint16_t delay = FRAMETIME;
if (!_segments[_segment_index].getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen if (!seg.getOption(SEG_OPTION_FREEZE)) { //only run effect function if not frozen
Segment &seg = _segments[_segment_index];
_virtualSegmentLength = seg.virtualLength(); _virtualSegmentLength = seg.virtualLength();
uint8_t _bri_t = seg.getOption(SEG_OPTION_ON) ? seg.opacity : 0; _bri_t = seg.getOption(SEG_OPTION_ON) ? seg.opacity : 0;
uint8_t _cct_t = seg.cct; uint8_t _cct_t = seg.cct;
_colors_t[0] = seg.colors[0]; _colors_t[0] = seg.colors[0];
_colors_t[1] = seg.colors[1]; _colors_t[1] = seg.colors[1];
_colors_t[2] = seg.colors[2]; _colors_t[2] = seg.colors[2];
if (seg.getOption(SEG_OPTION_TRANSITIONAL)) seg.transition.handleTransition(&seg, _bri_t, _cct_t, _colors_t); seg.transition.handleTransition(&seg, _bri_t, _cct_t, _colors_t);
if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB); if (!cctFromRgb || correctWB) busses.setSegmentCCT(_cct_t, correctWB);
for (uint8_t c = 0; c < NUM_COLORS; c++) { for (uint8_t c = 0; c < NUM_COLORS; c++) {
@ -629,10 +632,10 @@ void WS2812FX::service() {
handle_palette(); handle_palette();
delay = (*_mode[seg.mode])(); delay = (*_mode[seg.mode])();
if (seg.mode != FX_MODE_HALLOWEEN_EYES) _segment_runtimes[_segment_index].call++; if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.runtime.call++;
} }
_segment_runtimes[_segment_index].next_time = nowUp + delay; seg.runtime.next_time = nowUp + delay;
} }
} }
_virtualSegmentLength = 0; _virtualSegmentLength = 0;
@ -650,34 +653,43 @@ void WS2812FX::setPixelColor(int i, uint32_t col)
// if realtime mode is active and applying to main segment // if realtime mode is active and applying to main segment
if (realtimeMode && useMainSegmentOnly) { if (realtimeMode && useMainSegmentOnly) {
uint16_t len = _segments[_mainSegment].length(); // length of segment in number of pixels Segment &seg = _segments[_mainSegment];
uint16_t len = seg.length(); // length of segment in number of pixels
if (seg.opacity < 255) {
byte r = scale8(R(col), seg.opacity);
byte g = scale8(G(col), seg.opacity);
byte b = scale8(B(col), seg.opacity);
byte w = scale8(W(col), seg.opacity);
col = RGBW32(r, g, b, w);
}
// get physical pixel address (taking into account start, grouping, spacing [and offset]) // get physical pixel address (taking into account start, grouping, spacing [and offset])
i = i * _segments[_mainSegment].groupLength(); i = i * seg.groupLength();
if (_segments[_mainSegment].getOption(SEG_OPTION_REVERSED)) { // is segment reversed? if (seg.getOption(SEG_OPTION_REVERSED)) { // is segment reversed?
if (_segments[_mainSegment].getOption(SEG_OPTION_MIRROR)) { // is segment mirrored? if (seg.getOption(SEG_OPTION_MIRROR)) { // is segment mirrored?
i = (len - 1) / 2 - i; //only need to index half the pixels i = (len - 1) / 2 - i; //only need to index half the pixels
} else { } else {
i = (len - 1) - i; i = (len - 1) - i;
} }
} }
i += _segments[_mainSegment].start; // starting pixel in a group i += seg.start; // starting pixel in a group
// set all the pixels in the group // set all the pixels in the group
for (uint16_t j = 0; j < _segments[_mainSegment].grouping; j++) { for (uint16_t j = 0; j < seg.grouping; j++) {
uint16_t indexSet = i + ((_segments[_mainSegment].getOption(SEG_OPTION_REVERSED)) ? -j : j); uint16_t indexSet = i + ((seg.getOption(SEG_OPTION_REVERSED)) ? -j : j);
if (indexSet >= _segments[_mainSegment].start && indexSet < _segments[_mainSegment].stop) { if (indexSet >= seg.start && indexSet < seg.stop) {
if (_segments[_mainSegment].getOption(SEG_OPTION_MIRROR)) { //set the corresponding mirrored pixel if (seg.getOption(SEG_OPTION_MIRROR)) { //set the corresponding mirrored pixel
uint16_t indexMir = _segments[_mainSegment].stop - indexSet + _segments[_mainSegment].start - 1; uint16_t indexMir = seg.stop - indexSet + seg.start - 1;
indexMir += _segments[_mainSegment].offset; // offset/phase indexMir += seg.offset; // offset/phase
if (indexMir >= _segments[_mainSegment].stop) indexMir -= len; // wrap if (indexMir >= seg.stop) indexMir -= len; // wrap
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir]; if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
busses.setPixelColor(indexMir, col); busses.setPixelColor(indexMir, col);
} }
indexSet += _segments[_mainSegment].offset; // offset/phase indexSet += seg.offset; // offset/phase
if (indexSet >= _segments[_mainSegment].stop) indexSet -= len; // wrap if (indexSet >= seg.stop) indexSet -= len; // wrap
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet]; if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col); busses.setPixelColor(indexSet, col);
@ -832,7 +844,7 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
if (_segments[segid].mode != m) if (_segments[segid].mode != m)
{ {
_segment_runtimes[segid].markForReset(); _segments[segid].runtime.markForReset();
_segments[segid].mode = m; _segments[segid].mode = m;
} }
} }
@ -873,7 +885,7 @@ void WS2812FX::setBrightness(uint8_t b, bool direct) {
busses.setBrightness(b); busses.setBrightness(b);
} else { } else {
unsigned long t = millis(); unsigned long t = millis();
if (_segment_runtimes[0].next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon if (_segments[0].runtime.next_time > t + 22 && t - _lastShow > MIN_SHOW_DELAY) show(); //apply brightness change immediately if no refresh soon
} }
} }
@ -1003,13 +1015,13 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
seg.spacing = spacing; seg.spacing = spacing;
} }
if (offset < UINT16_MAX) seg.offset = offset; if (offset < UINT16_MAX) seg.offset = offset;
_segment_runtimes[n].markForReset(); _segments[n].runtime.markForReset();
if (!boundsUnchanged) seg.refreshLightCapabilities(); if (!boundsUnchanged) seg.refreshLightCapabilities();
} }
void WS2812FX::restartRuntime() { void WS2812FX::restartRuntime() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) { for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) {
_segment_runtimes[i].markForReset(); _segments[i].runtime.markForReset();
} }
} }
@ -1017,7 +1029,7 @@ void WS2812FX::resetSegments() {
for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete[] _segments[i].name; for (uint8_t i = 0; i < MAX_NUM_SEGMENTS; i++) if (_segments[i].name) delete[] _segments[i].name;
_mainSegment = 0; _mainSegment = 0;
memset(_segments, 0, sizeof(_segments)); memset(_segments, 0, sizeof(_segments));
//memset(_segment_runtimes, 0, sizeof(_segment_runtimes)); //memset(_segmentruntimes, 0, sizeof(_segmentruntimes));
_segment_index = 0; _segment_index = 0;
_segments[0].mode = DEFAULT_MODE; _segments[0].mode = DEFAULT_MODE;
_segments[0].colors[0] = DEFAULT_COLOR; _segments[0].colors[0] = DEFAULT_COLOR;
@ -1046,9 +1058,9 @@ void WS2812FX::resetSegments() {
_segments[i].custom1 = DEFAULT_C1; _segments[i].custom1 = DEFAULT_C1;
_segments[i].custom2 = DEFAULT_C2; _segments[i].custom2 = DEFAULT_C2;
_segments[i].custom3 = DEFAULT_C3; _segments[i].custom3 = DEFAULT_C3;
_segment_runtimes[i].markForReset(); _segments[i].runtime.markForReset();
} }
_segment_runtimes[0].markForReset(); _segments[0].runtime.markForReset();
} }
void WS2812FX::makeAutoSegments(bool forceReset) { void WS2812FX::makeAutoSegments(bool forceReset) {
@ -1143,7 +1155,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
uint8_t prevSegId = _segment_index; uint8_t prevSegId = _segment_index;
if (n < MAX_NUM_SEGMENTS) { if (n < MAX_NUM_SEGMENTS) {
_segment_index = n; _segment_index = n;
//_virtualSegmentLength = _segments[_segment_index].virtualLength(); _virtualSegmentLength = _segments[_segment_index].virtualLength();
} }
return prevSegId; return prevSegId;
} }
@ -1161,13 +1173,19 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
void WS2812FX::setTransitionMode(bool t) void WS2812FX::setTransitionMode(bool t)
{ {
unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled unsigned long waitMax = millis() + 22; //refresh after 20 ms if transition enabled
for (uint16_t i = 0; i < MAX_NUM_SEGMENTS; i++) for (uint16_t i = 0; i < MAX_NUM_SEGMENTS; i++)
{ {
_segments[i].setOption(SEG_OPTION_TRANSITIONAL, t); if (_segments[i].isActive() && !_segments[i].getOption(SEG_OPTION_TRANSITIONAL)) {
if (t) {
if (t && _segments[i].mode == FX_MODE_STATIC && _segment_runtimes[i].next_time > waitMax) _segments[i].transition.transitionStart = millis();
_segment_runtimes[i].next_time = waitMax; _segments[i].transition.transitionDur = strip.getTransition();
}
_segments[i].setOption(SEG_OPTION_TRANSITIONAL, t);
if (t && _segments[i].mode == FX_MODE_STATIC && _segments[i].runtime.next_time > waitMax) {
_segments[i].runtime.next_time = waitMax;
}
}
} }
} }
@ -1295,7 +1313,7 @@ void WS2812FX::handle_palette(void)
load_gradient_palette(paletteIndex -13); load_gradient_palette(paletteIndex -13);
} }
if (singleSegmentMode && paletteFade && _segment_runtimes[_segment_index].call > 0) //only blend if just one segment uses FastLED mode if (singleSegmentMode && paletteFade && _segments[_segment_index].runtime.call > 0) //only blend if just one segment uses FastLED mode
{ {
nblendPaletteTowardPalette(currentPalette, targetPalette, 48); nblendPaletteTowardPalette(currentPalette, targetPalette, 48);
} else } else

View File

@ -712,7 +712,7 @@ void initIR()
void handleIR() void handleIR()
{ {
if (irEnabled > 0 && millis() - irCheckedTime > 120) if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating())
{ {
irCheckedTime = millis(); irCheckedTime = millis();
if (irEnabled > 0) if (irEnabled > 0)

View File

@ -154,6 +154,7 @@ void initServer()
}); });
server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request){ server.on("/json", HTTP_GET, [](AsyncWebServerRequest *request){
while (strip.isUpdating()) delay(1);
serveJson(request); serveJson(request);
}); });
@ -161,6 +162,8 @@ void initServer()
bool verboseResponse = false; bool verboseResponse = false;
bool isConfig = false; bool isConfig = false;
while (strip.isUpdating()) delay(1);
if (!requestJSONBufferLock(14)) return; if (!requestJSONBufferLock(14)) return;
DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject)); DeserializationError error = deserializeJson(doc, (uint8_t*)(request->_tempObject));

View File

@ -96,7 +96,7 @@ void sendDataWs(AsyncWebSocketClient * client)
if (!ws.count()) return; if (!ws.count()) return;
AsyncWebSocketMessageBuffer * buffer; AsyncWebSocketMessageBuffer * buffer;
while (strip.isUpdating()) yield(); while (strip.isUpdating()) delay(1);
if (!requestJSONBufferLock(12)) return; if (!requestJSONBufferLock(12)) return;