diff --git a/CHANGELOG.md b/CHANGELOG.md index 1277b32e1..1626051d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ ### Development versions after 0.9.1 release +#### Build 2004230 + +- Added brightness and power for individual segments +- Added `on` and `bri` properties to Segment object in JSON API +- Added `C3` an `SB` commands to HTTP get API +- Merged pull request #865 for 5CH_Shojo_PCB environment + +#### Build 2004220 + +- Added Candle Multi effect +- Added Palette capability to Pacifica effect + +#### Build 2004190 + +- Added TM1814 type LED defines + #### Build 2004120 - Added Art-Net support diff --git a/platformio.ini b/platformio.ini index 905ba2c59..1bfeae1dd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -18,7 +18,7 @@ extra_configs = # Release binaries follow default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_WS2801, custom_APA102, custom_LEDPIN_16, custom_LEDPIN_4, custom32_LEDPIN_16 -# Single binaries +# Single binaries (uncomment your board) ; default_envs = nodemcuv2 ; default_envs = esp01 ; default_envs = esp01_1m_ota @@ -33,6 +33,7 @@ default_envs = nodemcuv2, esp01, esp01_1m_ota, esp01_1m_full, esp32dev, custom_W ; default_envs = esp8285_4CH_MagicHome ; default_envs = esp8285_4CH_H801 ; default_envs = esp8285_5CH_H801 +; default_envs = d1_mini_5CH_Shojo_PCB [common] # ------------------------------------------------------------------------------ @@ -246,6 +247,12 @@ platform = ${common.platform_latest} board_build.ldscript = ${common.ldscript_1m0m} build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_HUESYNC -D WLED_USE_ANALOG_LEDS -D WLED_USE_H801 -D WLED_ENABLE_5CH_LEDS +[env:d1_mini_5CH_Shojo_PCB] +board = d1_mini +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_4m1m} +build_flags = ${common.build_flags_esp8266} -D WLED_USE_ANALOG_LEDS -D SHOJO_PCB -D WLED_ENABLE_5CH_LEDS + # ------------------------------------------------------------------------------ # DEVELOPMENT BOARDS # ------------------------------------------------------------------------------ diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 7230a9b3e..14fee5cb8 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -35,7 +35,7 @@ */ uint16_t WS2812FX::mode_static(void) { fill(SEGCOLOR(0)); - return (SEGMENT.getOption(7)) ? FRAMETIME : 500; //update faster if in transition + return (SEGMENT.getOption(SEG_OPTION_TRANSITIONAL)) ? FRAMETIME : 500; //update faster if in transition } @@ -2719,50 +2719,92 @@ uint16_t WS2812FX::mode_popcorn(void) { //Inspired by https://github.com/avanhanegem/ArduinoCandleEffectNeoPixel //and https://cpldcpu.wordpress.com/2016/01/05/reverse-engineering-a-real-candle/ -uint16_t WS2812FX::mode_candle() +uint16_t WS2812FX::candle(bool multi) { - if (SEGENV.call == 0) { - SEGENV.aux0 = 128; SEGENV.aux1 = 132; SEGENV.step = 1; - } - bool newTarget = false; - - uint8_t s = SEGENV.aux0, target = SEGENV.aux1, fadeStep = SEGENV.step; - - if (target > s) { //fade up - s = qadd8(s, fadeStep); - if (s >= target) newTarget = true; - } else { - s = qsub8(s, fadeStep); - if (s <= target) newTarget = true; - } - SEGENV.aux0 = s; - - for (uint16_t i = 0; i < SEGLEN; i++) { - setPixelColor(i, color_blend(color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), SEGCOLOR(1), 255-s)); - } - - if (newTarget) + if (multi) { - uint8_t valrange = SEGMENT.intensity; - uint8_t rndval = valrange >> 1; - target = random8(rndval) + random8(rndval); - if (target < (rndval >> 1)) target = (rndval >> 1) + random8(rndval); - uint8_t offset = (255 - valrange) >> 1; - target += offset; + //allocate segment data + uint16_t dataSize = (SEGLEN -1) *3; + if (!SEGENV.allocateData(dataSize)) return candle(false); //allocation failed + } - uint8_t dif = (target > s) ? target - s : s - target; - - //how much to move closer to target per frame - fadeStep = dif >> 2; //mode called every ~25 ms, so 4 frames to have a new target every 100ms - if (fadeStep == 0) fadeStep = 1; + //max. flicker range controlled by intensity + uint8_t valrange = SEGMENT.intensity; + uint8_t rndval = valrange >> 1; + + //step (how much to move closer to target per frame) coarsely set by speed + uint8_t speedFactor = 4; + if (SEGMENT.speed > 252) { //epilepsy + speedFactor = 1; + } else if (SEGMENT.speed > 99) { //regular candle (mode called every ~25 ms, so 4 frames to have a new target every 100ms) + speedFactor = 2; + } else if (SEGMENT.speed > 49) { //slower fade + speedFactor = 3; + } //else 4 (slowest) + + uint16_t numCandles = (multi) ? SEGLEN : 1; + + for (uint16_t i = 0; i < numCandles; i++) + { + uint16_t d = 0; //data location + + uint8_t s = SEGENV.aux0, s_target = SEGENV.aux1, fadeStep = SEGENV.step; + if (i > 0) { + d = (i-1) *3; + s = SEGENV.data[d]; s_target = SEGENV.data[d+1]; fadeStep = SEGENV.data[d+2]; + } + if (fadeStep == 0) { //init vals + s = 128; s_target = 130 + random8(4); fadeStep = 1; + } + + bool newTarget = false; + if (s_target > s) { //fade up + s = qadd8(s, fadeStep); + if (s >= s_target) newTarget = true; + } else { + s = qsub8(s, fadeStep); + if (s <= s_target) newTarget = true; + } + + if (newTarget) { + s_target = random8(rndval) + random8(rndval); + if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval); + uint8_t offset = (255 - valrange) >> 1; + s_target += offset; + + uint8_t dif = (s_target > s) ? s_target - s : s - s_target; - SEGENV.step = fadeStep; - SEGENV.aux1 = target; + fadeStep = dif >> speedFactor; + if (fadeStep == 0) fadeStep = 1; + } + + if (i > 0) { + setPixelColor(i, color_blend(SEGCOLOR(1), color_from_palette(i, true, PALETTE_SOLID_WRAP, 0), s)); + + SEGENV.data[d] = s; SEGENV.data[d+1] = s_target; SEGENV.data[d+2] = fadeStep; + } else { + for (uint16_t j = 0; j < SEGLEN; j++) { + setPixelColor(j, color_blend(SEGCOLOR(1), color_from_palette(j, true, PALETTE_SOLID_WRAP, 0), s)); + } + + SEGENV.aux0 = s; SEGENV.aux1 = s_target; SEGENV.step = fadeStep; + } } return FRAMETIME; } +uint16_t WS2812FX::mode_candle() +{ + return candle(false); +} + + +uint16_t WS2812FX::mode_candle_multi() +{ + return candle(true); +} + /* / Fireworks in starburst effect @@ -2901,9 +2943,9 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) fill(BLACK); - bool actuallyReverse = SEGMENT.getOption(1); + bool actuallyReverse = SEGMENT.getOption(SEG_OPTION_REVERSED); //have fireworks start in either direction based on intensity - SEGMENT.setOption(1, SEGENV.step); + SEGMENT.setOption(SEG_OPTION_REVERSED, SEGENV.step); Spark* sparks = reinterpret_cast(SEGENV.data); Spark* flare = sparks; //first spark is flare data @@ -2994,7 +3036,7 @@ uint16_t WS2812FX::mode_exploding_fireworks(void) } } - SEGMENT.setOption(1, actuallyReverse); + SEGMENT.setOption(SEG_OPTION_REVERSED, actuallyReverse); return FRAMETIME; } @@ -3205,6 +3247,13 @@ uint16_t WS2812FX::mode_pacifica() CRGBPalette16 pacifica_palette_3 = { 0x000208, 0x00030E, 0x000514, 0x00061A, 0x000820, 0x000927, 0x000B2D, 0x000C33, 0x000E39, 0x001040, 0x001450, 0x001860, 0x001C70, 0x002080, 0x1040BF, 0x2060FF }; + + if (SEGMENT.palette) { + pacifica_palette_1 = currentPalette; + pacifica_palette_2 = currentPalette; + pacifica_palette_3 = currentPalette; + } + // Increment the four "color index start" counters, one for each wave layer. // Each is incremented at a different speed, and the speeds vary over time. uint16_t sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16; diff --git a/wled00/FX.h b/wled00/FX.h index a38a1f542..7022fede4 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -84,18 +84,21 @@ // options // bit 7: segment is in transition mode -// bits 2-6: TBD +// bits 3-6: TBD +// bit 2: segment is on // bit 1: reverse segment // bit 0: segment is selected #define NO_OPTIONS (uint8_t)0x00 #define TRANSITIONAL (uint8_t)0x80 +#define SEGMENT_ON (uint8_t)0x04 #define REVERSE (uint8_t)0x02 #define SELECTED (uint8_t)0x01 #define IS_TRANSITIONAL ((SEGMENT.options & TRANSITIONAL) == TRANSITIONAL) -#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) -#define IS_SELECTED ((SEGMENT.options & SELECTED) == SELECTED ) +#define IS_SEGMENT_ON ((SEGMENT.options & SEGMENT_ON ) == SEGMENT_ON ) +#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) +#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) -#define MODE_COUNT 102 +#define MODE_COUNT 103 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -199,6 +202,7 @@ #define FX_MODE_RIPPLE_RAINBOW 99 #define FX_MODE_HEARTBEAT 100 #define FX_MODE_PACIFICA 101 +#define FX_MODE_CANDLE_MULTI 102 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -389,6 +393,7 @@ class WS2812FX { _mode[FX_MODE_RIPPLE_RAINBOW] = &WS2812FX::mode_ripple_rainbow; _mode[FX_MODE_HEARTBEAT] = &WS2812FX::mode_heartbeat; _mode[FX_MODE_PACIFICA] = &WS2812FX::mode_pacifica; + _mode[FX_MODE_CANDLE_MULTI] = &WS2812FX::mode_candle_multi; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -574,7 +579,8 @@ class WS2812FX { mode_percent(void), mode_ripple_rainbow(void), mode_heartbeat(void), - mode_pacifica(void); + mode_pacifica(void), + mode_candle_multi(void); private: @@ -607,6 +613,7 @@ class WS2812FX { // mode helper functions uint16_t blink(uint32_t, uint32_t, bool strobe, bool), + candle(bool), color_wipe(bool, bool), scan(bool), theater_chase(uint32_t, uint32_t, bool), @@ -660,7 +667,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Noise 1","Noise 2","Noise 3","Noise 4","Colortwinkles","Lake","Meteor","Meteor Smooth","Railway","Ripple", "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", -"Heartbeat","Pacifica" +"Heartbeat","Pacifica","Candle Multi" ])====="; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 3614db492..e25ffb0ca 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -146,11 +146,23 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w) default: col.G = g; col.R = b; col.B = r; break; //5 = GBR } col.W = w; - - //color_blend(BLACK, (uint32_t)col, SEGMENT.opacity); uint16_t skip = _skipFirstMode ? LED_SKIP_AMOUNT : 0; if (SEGLEN) {//from segment + + //color_blend(getpixel, col, SEGMENT.opacity); (pseudocode for future blending of segments) + if (IS_SEGMENT_ON) + { + if (SEGMENT.opacity < 255) { + col.R = scale8(col.R, SEGMENT.opacity); + col.G = scale8(col.G, SEGMENT.opacity); + col.B = scale8(col.B, SEGMENT.opacity); + col.W = scale8(col.W, SEGMENT.opacity); + } + } else { + col = BLACK; + } + /* Set all the pixels in the group, ensuring _skipFirstMode is honored */ bool reversed = reverseMode ^ IS_REVERSE; uint16_t realIndex = realPixelIndex(i); @@ -483,11 +495,16 @@ void WS2812FX::resetSegments() { _segments[0].speed = DEFAULT_SPEED; _segments[0].stop = _length; _segments[0].grouping = 1; - _segments[0].setOption(0, 1); //select + _segments[0].setOption(SEG_OPTION_SELECTED, 1); + _segments[0].setOption(SEG_OPTION_ON, 1); + _segments[0].opacity = 255; + for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++) { _segments[i].colors[0] = color_wheel(i*51); _segments[i].grouping = 1; + _segments[i].setOption(SEG_OPTION_ON, 1); + _segments[i].opacity = 255; _segment_runtimes[i].reset(); } _segment_runtimes[0].reset(); @@ -512,7 +529,7 @@ void WS2812FX::setShowCallback(show_callback cb) void WS2812FX::setTransitionMode(bool t) { _segment_index = getMainSegmentId(); - SEGMENT.setOption(7,t); + SEGMENT.setOption(SEG_OPTION_TRANSITIONAL, t); if (!t) return; unsigned long waitMax = millis() + 20; //refresh after 20 ms if transition enabled if (SEGMENT.mode == FX_MODE_STATIC && SEGENV.next_time > waitMax) SEGENV.next_time = waitMax; @@ -795,7 +812,7 @@ bool WS2812FX::segmentsAreIdentical(Segment* a, Segment* b) if (a->speed != b->speed) return false; if (a->intensity != b->intensity) return false; if (a->palette != b->palette) return false; - //if (a->getOption(1) != b->getOption(1)) return false; //reverse + //if (a->getOption(SEG_OPTION_REVERSED) != b->getOption(SEG_OPTION_REVERSED)) return false; return true; } diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index f3bcfd855..faee293a3 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -9,7 +9,7 @@ //#define USE_APA102 // Uncomment for using APA102 LEDs. //#define USE_WS2801 // Uncomment for using WS2801 LEDs (make sure you have NeoPixelBus v2.5.6 or newer) //#define USE_LPD8806 // Uncomment for using LPD8806 -//#define USE_TM1814 // Uncomment for using TM1814 LEDs (make sure you have NeoPixelBus v2.5.7 or newer) +//#define USE_TM1814 // Uncomment for using TM1814 LEDs (make sure you have NeoPixelBus v2.5.7 or newer) //#define USE_P9813 // Uncomment for using P9813 LEDs (make sure you have NeoPixelBus v2.5.8 or newer) //#define WLED_USE_ANALOG_LEDS //Uncomment for using "dumb" PWM controlled LEDs (see pins below, default R: gpio5, G: 12, B: 15, W: 13) //#define WLED_USE_H801 //H801 controller. Please uncomment #define WLED_USE_ANALOG_LEDS as well @@ -62,6 +62,13 @@ #define GPIN 4 //G pin for analog LED strip #define BPIN 14 //B pin for analog LED strip #define WPIN 5 //W pin for analog LED strip + #elif defined(SHOJO_PCB) + //PWM pins - to use with Shojo PCB (https://www.bastelbunker.de/esp-rgbww-wifi-led-controller-vbs-edition/) + #define RPIN 14 //R pin for analog LED strip + #define GPIN 4 //G pin for analog LED strip + #define BPIN 5 //B pin for analog LED strip + #define WPIN 15 //W pin for analog LED strip + #define W2PIN 12 //W2 pin for analog LED strip #else //PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller #define RPIN 5 //R pin for analog LED strip diff --git a/wled00/const.h b/wled00/const.h index 5cd4b02f4..176043781 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -77,6 +77,12 @@ #define HUE_ERROR_TIMEOUT 251 #define HUE_ERROR_ACTIVE 255 +//Segment option byte bits +#define SEG_OPTION_SELECTED 0 +#define SEG_OPTION_REVERSED 1 +#define SEG_OPTION_ON 2 +#define SEG_OPTION_TRANSITIONAL 7 + //EEPROM size #define EEPSIZE 2560 //Maximum is 4096 diff --git a/wled00/json.cpp b/wled00/json.cpp index 31208bf5b..72d60738e 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -20,6 +20,16 @@ void deserializeSegment(JsonObject elem, byte it) uint16_t grp = elem["grp"] | seg.grouping; uint16_t spc = elem["spc"] | seg.spacing; strip.setSegment(id, start, stop, grp, spc); + + int segbri = elem["bri"] | -1; + if (segbri == 0) { + seg.setOption(SEG_OPTION_ON, 0); + } else if (segbri > 0) { + seg.opacity = segbri; + seg.setOption(SEG_OPTION_ON, 1); + } + + seg.setOption(SEG_OPTION_ON, elem["on"] | seg.getOption(SEG_OPTION_ON)); JsonArray colarr = elem["col"]; if (!colarr.isNull()) @@ -47,9 +57,9 @@ void deserializeSegment(JsonObject elem, byte it) } //if (pal != seg.palette && pal < strip.getPaletteCount()) strip.setPalette(pal); - seg.setOption(0, elem["sel"] | seg.getOption(0)); //selected - seg.setOption(1, elem["rev"] | seg.getOption(1)); //reverse - //int cln = seg_0["cln"]; + seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED)); + seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED)); + //temporary, strip object gets updated via colorUpdated() if (id == strip.getMainSegmentId()) { effectCurrent = elem["fx"] | effectCurrent; @@ -177,6 +187,9 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["len"] = seg.stop - seg.start; root["grp"] = seg.grouping; root["spc"] = seg.spacing; + root["on"] = seg.getOption(SEG_OPTION_ON); + byte segbri = seg.opacity; + root["bri"] = (segbri) ? segbri : 255; JsonArray colarr = root.createNestedArray("col"); @@ -204,7 +217,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id) root["ix"] = seg.intensity; root["pal"] = seg.palette; root["sel"] = seg.isSelected(); - root["rev"] = seg.getOption(1); + root["rev"] = seg.getOption(SEG_OPTION_REVERSED); } diff --git a/wled00/set.cpp b/wled00/set.cpp index b568dff22..d04c0cb2a 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -420,7 +420,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) WS2812FX::Segment& mainseg = strip.getSegment(main); pos = req.indexOf("SV="); //segment selected - if (pos > 0) mainseg.setOption(0, (req.charAt(pos+3) != '0')); + if (pos > 0) mainseg.setOption(SEG_OPTION_SELECTED, (req.charAt(pos+3) != '0')); uint16_t startI = mainseg.start; uint16_t stopI = mainseg.stop; @@ -513,6 +513,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) if (pos > 0) { colorFromDecOrHexString(colSec, (char*)req.substring(pos + 3).c_str()); } + pos = req.indexOf("C3="); + if (pos > 0) { + byte t[4]; + colorFromDecOrHexString(t, (char*)req.substring(pos + 3).c_str()); + strip.setColor(2, t[0], t[1], t[2], t[3]); + } //set to random hue SR=0->1st SR=1->2nd pos = req.indexOf("SR"); @@ -626,7 +632,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req) //Segment reverse pos = req.indexOf("RV="); - if (pos > 0) strip.getSegment(main).setOption(1, req.charAt(pos+3) != '0'); + if (pos > 0) strip.getSegment(main).setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0'); + + //Segment brightness/opacity + pos = req.indexOf("SB="); + if (pos > 0) { + byte segbri = getNumVal(&req, pos); + strip.getSegment(main).setOption(SEG_OPTION_ON, segbri); + if (segbri) { + strip.getSegment(main).opacity = segbri; + } + } //deactivate nightlight if target brightness is reached if (bri == nightlightTargetBri) nightlightActive = false; diff --git a/wled00/wled.h b/wled00/wled.h index 10343cc00..64210d429 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2004190 +#define VERSION 2004230 // ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS). diff --git a/wled00/wled_eeprom.cpp b/wled00/wled_eeprom.cpp index 2bf7eeffb..807d3a447 100644 --- a/wled00/wled_eeprom.cpp +++ b/wled00/wled_eeprom.cpp @@ -611,8 +611,10 @@ bool applyPreset(byte index, bool loadBri) } if (index > 16 || index < 1) return false; uint16_t i = 380 + index*20; + byte ver = EEPROM.read(i); + if (index < 16) { - if (EEPROM.read(i) != 1) return false; + if (ver != 1) return false; strip.applyToAllSelected = true; if (loadBri) bri = EEPROM.read(i+1); @@ -628,11 +630,18 @@ bool applyPreset(byte index, bool loadBri) effectIntensity = EEPROM.read(i+16); effectPalette = EEPROM.read(i+17); } else { - if (EEPROM.read(i) != 2) return false; + if (ver != 2 && ver != 3) return false; strip.applyToAllSelected = false; if (loadBri) bri = EEPROM.read(i+1); WS2812FX::Segment* seg = strip.getSegments(); memcpy(seg, EEPROM.getDataPtr() +i+2, 240); + if (ver == 2) { //versions before 2004230 did not have opacity + for (byte j = 0; j < strip.getMaxSegments(); j++) + { + strip.getSegment(j).opacity = 255; + strip.getSegment(j).setOption(SEG_OPTION_ON, 1); + } + } setValuesFromMainSeg(); } currentPreset = index; @@ -666,7 +675,7 @@ void savePreset(byte index, bool persist) EEPROM.write(i+16, effectIntensity); EEPROM.write(i+17, effectPalette); } else { //segment 16 can save segments - EEPROM.write(i, 2); + EEPROM.write(i, 3); EEPROM.write(i+1, bri); WS2812FX::Segment* seg = strip.getSegments(); memcpy(EEPROM.getDataPtr() +i+2, seg, 240);