6262/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
6363 insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
6464#ifdef ESP8266
65- #define MAX_NUM_SEGMENTS 12
65+ #define MAX_NUM_SEGMENTS 12
66+ /* How many color transitions can run at once */
67+ #define MAX_NUM_TRANSITIONS 8
68+ /* How much data bytes all segments combined may allocate */
69+ #define MAX_SEGMENT_DATA 2048
6670#else
67- #define MAX_NUM_SEGMENTS 16
68- #endif
69-
70- /* How much data bytes all segments combined may allocate */
71- #ifdef ESP8266
72- #define MAX_SEGMENT_DATA 2048
73- #else
74- #define MAX_SEGMENT_DATA 8192
71+ #define MAX_NUM_SEGMENTS 16
72+ #define MAX_NUM_TRANSITIONS 16
73+ #define MAX_SEGMENT_DATA 8192
7574#endif
7675
7776#define LED_SKIP_AMOUNT 1
7877#define MIN_SHOW_DELAY 15
7978
8079#define NUM_COLORS 3 /* number of colors per segment */
8180#define SEGMENT _segments[_segment_index]
82- #define SEGCOLOR (x ) gamma32(_segments[_segment_index].colors[x])
81+ #define SEGCOLOR (x ) _colors_t [x]
8382#define SEGENV _segment_runtimes[_segment_index]
8483#define SEGLEN _virtualSegmentLength
8584#define SEGACT SEGMENT.stop
240239#define FX_MODE_TV_SIMULATOR 116
241240#define FX_MODE_DYNAMIC_SMOOTH 117
242241
242+
243243class WS2812FX {
244244 typedef uint16_t (WS2812FX::*mode_ptr)(void );
245245
246246 // pre show callback
247247 typedef void (*show_callback) (void );
248+
249+ static WS2812FX* instance;
248250
249251 // segment parameters
250252 public:
@@ -259,14 +261,39 @@ class WS2812FX {
259261 uint8_t grouping, spacing;
260262 uint8_t opacity;
261263 uint32_t colors[NUM_COLORS];
262- void setOption (uint8_t n, bool val)
264+ bool setColor (uint8_t slot, uint32_t c, uint8_t segn) { // returns true if changed
265+ if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false ;
266+ if (c == colors[slot]) return false ;
267+ ColorTransition::startTransition (opacity, colors[slot], instance->_transitionDur , segn, slot);
268+ colors[slot] = c; return true ;
269+ }
270+ void setOpacity (uint8_t o, uint8_t segn) {
271+ if (segn >= MAX_NUM_SEGMENTS) return ;
272+ if (opacity == o) return ;
273+ ColorTransition::startTransition (o, colors[0 ], instance->_transitionDur , segn, 0 );
274+ opacity = o;
275+ }
276+ uint8_t actualOpacity () { // respects On/Off state
277+ if (!getOption (SEG_OPTION_ON)) return 0 ;
278+ return opacity;
279+ }
280+ void setOption (uint8_t n, bool val, uint8_t segn = 255 )
263281 {
282+ bool prevOn = false ;
283+ if (n == SEG_OPTION_ON) prevOn = getOption (SEG_OPTION_ON);
264284 if (val) {
265285 options |= 0x01 << n;
266286 } else
267287 {
268288 options &= ~(0x01 << n);
269289 }
290+ if (n == SEG_OPTION_ON && segn < MAX_NUM_SEGMENTS && getOption (SEG_OPTION_ON) != prevOn) {
291+ if (getOption (SEG_OPTION_ON)) {
292+ ColorTransition::startTransition (0 , colors[0 ], instance->_transitionDur , segn, 0 );
293+ } else {
294+ ColorTransition::startTransition (opacity, colors[0 ], instance->_transitionDur , segn, 0 );
295+ }
296+ }
270297 }
271298 bool getOption (uint8_t n)
272299 {
@@ -309,18 +336,18 @@ class WS2812FX {
309336 bool allocateData (uint16_t len){
310337 if (data && _dataLen == len) return true ; // already allocated
311338 deallocateData ();
312- if (WS2812FX::_usedSegmentData + len > MAX_SEGMENT_DATA) return false ; // not enough memory
339+ if (WS2812FX::instance-> _usedSegmentData + len > MAX_SEGMENT_DATA) return false ; // not enough memory
313340 data = new (std::nothrow) byte[len];
314341 if (!data) return false ; // allocation failed
315- WS2812FX::_usedSegmentData += len;
342+ WS2812FX::instance-> _usedSegmentData += len;
316343 _dataLen = len;
317344 memset (data, 0 , len);
318345 return true ;
319346 }
320347 void deallocateData (){
321348 delete[] data;
322349 data = nullptr ;
323- WS2812FX::_usedSegmentData -= _dataLen;
350+ WS2812FX::instance-> _usedSegmentData -= _dataLen;
324351 _dataLen = 0 ;
325352 }
326353
@@ -350,7 +377,86 @@ class WS2812FX {
350377 bool _requiresReset = false ;
351378 } segment_runtime;
352379
380+ typedef struct ColorTransition { // 12 bytes
381+ uint32_t colorOld = 0 ;
382+ uint32_t transitionStart;
383+ uint16_t transitionDur;
384+ uint8_t segment = 0xFF ; // lower 6 bits: the segment this transition is for (255 indicates transition not in use/available) upper 2 bits: color channel
385+ uint8_t briOld = 0 ;
386+ static void startTransition (uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot) {
387+ Serial.printf (" Starting t: Bri %u Col %u Dur %u Seg %u Slot %u\n " , oldBri, oldCol, dur, segn, slot);
388+ if (segn >= MAX_NUM_SEGMENTS || slot >= NUM_COLORS || dur == 0 ) return ;
389+ uint8_t tIndex = 0xFF ; // none found
390+ uint16_t tProgression = 0 ;
391+ uint8_t s = segn + (slot << 6 ); // merge slot and segment into one byte
392+
393+ for (uint8_t i = 0 ; i < MAX_NUM_TRANSITIONS; i++) {
394+ uint8_t tSeg = instance->transitions [i].segment ;
395+ // see if this segment + color already has a running transition
396+ if (tSeg == s) {
397+ tIndex = i; break ;
398+ }
399+ if (tSeg == 0xFF ) { // free transition
400+ tIndex = i; tProgression = 0xFFFF ;
401+ }
402+ }
403+
404+ if (tIndex == 0xFF ) { // no slot found yet
405+ for (uint8_t i = 0 ; i < MAX_NUM_TRANSITIONS; i++) {
406+ // find most progressed transition to overwrite
407+ uint16_t prog = instance->transitions [i].progress ();
408+ if (prog > tProgression) {
409+ tIndex = i; tProgression = prog;
410+ }
411+ }
412+ }
413+
414+ ColorTransition& t = instance->transitions [tIndex];
415+ if (t.segment == s) // this is an active transition on the same segment+color
416+ {
417+ t.briOld = t.currentBri ();
418+ t.colorOld = t.currentColor (oldCol);
419+ } else {
420+ t.briOld = oldBri;
421+ t.colorOld = oldCol;
422+ uint8_t prevSeg = t.segment & 0x3F ;
423+ if (prevSeg < MAX_NUM_SEGMENTS) instance->_segments [prevSeg].setOption (SEG_OPTION_TRANSITIONAL, false );
424+ }
425+ t.transitionDur = dur;
426+ t.transitionStart = millis ();
427+ t.segment = s;
428+ instance->_segments [segn].setOption (SEG_OPTION_TRANSITIONAL, true );
429+ // Serial.printf("S: %u, TNr: %u, St: %u\n", s, tIndex, t.transitionStart);
430+ // instance->transitions[tIndex] = t;
431+ }
432+ uint16_t progress (bool allowEnd = false ) { // transition progression between 0-65535
433+ uint32_t timeNow = millis ();
434+ // Serial.printf("ProgressR %u, St: %u, S: %u\n",timeNow - transitionStart, transitionStart, segment);
435+ if (timeNow - transitionStart > transitionDur) return 0xFFFF ;
436+ uint32_t elapsed = timeNow - transitionStart;
437+ uint32_t prog = elapsed * 0xFFFF / transitionDur;
438+ // Serial.printf("Progress %u\n",prog);
439+ if (prog > 0xFFFF && allowEnd) {
440+ uint8_t segn = segment & 0x3F ;
441+ if (segn < MAX_NUM_SEGMENTS) instance->_segments [segn].setOption (SEG_OPTION_TRANSITIONAL, false );
442+ segment = 0xFF ;
443+ }
444+ return (prog > 0xFFFF ) ? 0xFFFF : prog;
445+ }
446+ uint32_t currentColor (uint32_t colorNew) {
447+ return instance->color_blend (colorOld, colorNew, progress (true ), true );
448+ }
449+ uint8_t currentBri () {
450+ uint8_t segn = segment & 0x3F ;
451+ if (segn >= MAX_NUM_SEGMENTS) return 0 ;
452+ uint8_t briNew = instance->_segments [segn].actualOpacity ();
453+ uint32_t prog = progress ();
454+ return ((briNew * prog) + (briOld * (0xFFFF - prog))) >> 16 ;
455+ }
456+ } color_transition;
457+
353458 WS2812FX () {
459+ WS2812FX::instance = this ;
354460 // assign each member of the _mode[] array to its respective function reference
355461 _mode[FX_MODE_STATIC] = &WS2812FX::mode_static;
356462 _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink;
@@ -493,6 +599,7 @@ class WS2812FX {
493599 setBrightness (uint8_t b),
494600 setRange (uint16_t i, uint16_t i2, uint32_t col),
495601 setShowCallback (show_callback cb),
602+ setTransition (uint16_t t),
496603 setTransitionMode (bool t),
497604 calcGammaTable (float ),
498605 trigger (void ),
@@ -547,7 +654,8 @@ class WS2812FX {
547654 timebase,
548655 color_wheel (uint8_t ),
549656 color_from_palette (uint16_t , bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255 ),
550- color_blend (uint32_t ,uint32_t ,uint8_t ),
657+ color_blend (uint32_t ,uint32_t ,uint16_t ,bool b16=false ),
658+ currentColor (uint32_t colorNew, uint8_t tNr),
551659 gamma32 (uint32_t ),
552660 getLastShow (void ),
553661 getPixelColor (uint16_t ),
@@ -695,7 +803,8 @@ class WS2812FX {
695803 uint16_t _length, _lengthRaw, _virtualSegmentLength;
696804 uint16_t _rand16seed;
697805 uint8_t _brightness;
698- static uint16_t _usedSegmentData;
806+ uint16_t _usedSegmentData = 0 ;
807+ uint16_t _transitionDur = 750 ;
699808
700809 void load_gradient_palette (uint8_t );
701810 void handle_palette (void );
@@ -735,10 +844,15 @@ class WS2812FX {
735844 CRGB twinklefox_one_twinkle (uint32_t ms, uint8_t salt, bool cat);
736845 CRGB pacifica_one_layer (uint16_t i, CRGBPalette16& p, uint16_t cistart, uint16_t wavescale, uint8_t bri, uint16_t ioff);
737846
738- void blendPixelColor (uint16_t n, uint32_t color, uint8_t blend);
847+ void
848+ blendPixelColor (uint16_t n, uint32_t color, uint8_t blend),
849+ startTransition (uint8_t oldBri, uint32_t oldCol, uint16_t dur, uint8_t segn, uint8_t slot);
739850
740851 uint32_t _lastPaletteChange = 0 ;
741852 uint32_t _lastShow = 0 ;
853+
854+ uint32_t _colors_t [3 ];
855+ uint8_t _bri_t ;
742856
743857 #ifdef WLED_USE_ANALOG_LEDS
744858 uint32_t _analogLastShow = 0 ;
@@ -755,7 +869,12 @@ class WS2812FX {
755869 segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
756870 friend class Segment_runtime ;
757871
758- uint16_t realPixelIndex (uint16_t i);
872+ ColorTransition transitions[MAX_NUM_TRANSITIONS]; // 12 bytes per element
873+ friend class ColorTransition ;
874+
875+ uint16_t
876+ realPixelIndex (uint16_t i),
877+ transitionProgress (uint8_t tNr);
759878};
760879
761880// 10 names per line
0 commit comments