Skip to content

Commit 9551519

Browse files
committed
First working multisegment transitions
1 parent 0df6826 commit 9551519

File tree

10 files changed

+221
-104
lines changed

10 files changed

+221
-104
lines changed

wled00/FX.h

Lines changed: 137 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,23 @@
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
@@ -240,11 +239,14 @@
240239
#define FX_MODE_TV_SIMULATOR 116
241240
#define FX_MODE_DYNAMIC_SMOOTH 117
242241

242+
243243
class 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

Comments
 (0)