WLED 2D matrix support.

- Added 2 sample effects to UI.
- 2D segment details.
- 1D effect upscaling to 2D.
- 2D setPixelColorXY() and other utility functions
- Arbitrary panel position & oritentation.
- 2D settings with physical to logical mapping.
- Bump version.
This commit is contained in:
Blaz Kristan 2022-05-08 10:50:48 +02:00
parent 9f71a6ab18
commit f0d36fd769
19 changed files with 2836 additions and 1865 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.2-bl0",
"version": "0.14.0-bl0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.13.2-bl0",
"version": "0.14.0-bl0",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@ -366,6 +366,18 @@ writeChunks(
"</script><script src=\"/settings/s.js?p=8\"></script>"
)
},
{
file: "settings_2D.htm",
name: "PAGE_settings_2D",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=10\"></script>"
)
},
{
file: "settings_pin.htm",
name: "PAGE_settings_pin",

View File

@ -2346,7 +2346,7 @@ uint16_t WS2812FX::mode_ripple_rainbow(void) {
// incandescent bulbs change color as they get dim down.
#define COOL_LIKE_INCANDESCENT 1
CRGB IRAM_ATTR WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
CRGB WS2812FX::twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat)
{
// Overall twinkle speed (changed)
uint16_t ticks = ms / SEGENV.aux0;
@ -4226,4 +4226,71 @@ uint16_t WS2812FX::mode_aurora(void) {
}
return FRAMETIME;
}
}
///////////////////////////////////////////////////////////////////////////////
//*************************** 2D routines ***********************************
// sample 2D routine
uint16_t WS2812FX::mode_2DBlackHole_A() { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
uint16_t dataSize = sizeof(CRGB) * w * h;
if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed
CRGB *leds = reinterpret_cast<CRGB*>(SEGENV.data);
uint16_t x, y;
// initialize on first call
if (SEGENV.call == 0) {
for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
leds[x + y * w] = CRGB::Black;
}
}
fadeToBlackBy(32, leds); // create fading trails
float t = (float)(millis())/128;
for (byte i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.c1x>>3, 0, w - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(10 , 0, h - 1, 0, ((i % 2) ? 192 : 64) + t * i);
leds[x + y * w] += CHSV(i*32, 255, 255);
}
for (byte i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.c2x>>3, w/4, w - 1 - w/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.c3x>>3, h/4, h - 1 - h/4, 0, ((i % 2) ? 192 : 64) + t * i);
leds[x + y * w] += CHSV(i*32, 255, 255);
}
leds[w/2 * (1 + h)] = CHSV(0,0,255);
blur2d(16, leds);
for (y = 0; y < h; y++) for (x = 0; x < w; x++) {
uint16_t o = x + y * w;
setPixelColorXY(x, y, leds[o]);
}
return FRAMETIME;
} // mode_2DBlackHole()
// same as above not using leds[]
uint16_t WS2812FX::mode_2DBlackHole_B() { // By: Stepko https://editor.soulmatelights.com/gallery/1012 , Modified by: Andrew Tuline
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
uint16_t x, y;
fade_out2D(32); // create fading trails
float t = (float)(millis())/128;
for (byte i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.c1x>>3, 0, w - 1, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(10 , 0, h - 1, 0, ((i % 2) ? 192 : 64) + t * i);
setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)) + CHSV(i*32, 255, 255));
}
for (byte i = 0; i < 8; i++) {
x = beatsin8(SEGMENT.c2x>>3, w/4, w - 1 - w/4, 0, ((i % 2) ? 128 : 0) + t * i);
y = beatsin8(SEGMENT.c3x>>3, h/4, h - 1 - h/4, 0, ((i % 2) ? 192 : 64) + t * i);
setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x,y)) + CHSV(i*32, 255, 255));
}
setPixelColorXY(w/2, h/2, WHITE);
blur2d(16);
return FRAMETIME;
} // mode_2DBlackHole()

View File

@ -38,6 +38,9 @@
#define DEFAULT_SPEED (uint8_t)128
#define DEFAULT_INTENSITY (uint8_t)128
#define DEFAULT_COLOR (uint32_t)0xFFAA00
#define DEFAULT_C1 (uint8_t)128
#define DEFAULT_C2 (uint8_t)128
#define DEFAULT_C3 (uint8_t)128
#ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b))
@ -109,6 +112,9 @@
// bit 0: segment is selected
#define NO_OPTIONS (uint8_t)0x00
#define TRANSITIONAL (uint8_t)0x80
#define TRANSPOSED (uint8_t)0x40 // rotated 90deg & reversed
#define REVERSE_Y_2D (uint8_t)0x20
#define MIRROR_Y_2D (uint8_t)0x10
#define MIRROR (uint8_t)0x08
#define SEGMENT_ON (uint8_t)0x04
#define REVERSE (uint8_t)0x02
@ -118,8 +124,11 @@
#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 IS_REVERSE_Y_2D ((SEGMENT.options & REVERSE_Y_2D) == REVERSE_Y_2D)
#define IS_MIRROR_Y_2D ((SEGMENT.options & MIRROR_Y_2D ) == MIRROR_Y_2D )
#define IS_TRANSPOSED ((SEGMENT.options & TRANSPOSED ) == TRANSPOSED )
#define MODE_COUNT 118
#define MODE_COUNT 120
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@ -239,6 +248,8 @@
#define FX_MODE_BLENDS 115
#define FX_MODE_TV_SIMULATOR 116
#define FX_MODE_DYNAMIC_SMOOTH 117
#define FX_MODE_BLACK_HOLE_A 118
#define FX_MODE_BLACK_HOLE_B 119
class WS2812FX {
@ -249,24 +260,35 @@ class WS2812FX {
static WS2812FX* instance;
// segment parameters
public:
typedef struct Segment { // 34 (35 in memory) bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
// segment parameters
typedef struct Segment { // 35 (36 in memory) bytes
uint16_t start; // start index / start X coordinate 2D (left)
uint16_t stop; // stop index / stop X coordinate 2D (right); segment is invalid if stop == 0
uint16_t offset;
uint8_t speed;
uint8_t intensity;
uint8_t palette;
uint8_t mode;
uint8_t options; //bit pattern: msb first: transitional needspixelstate tbd tbd (paused) on reverse selected
uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
uint8_t cct; //0==1900K, 255==10091K
uint8_t _capabilities;
uint8_t c1x, c2x, c3x; // custom FX parameters
uint8_t c1x, c2x, c3x; // custom FX parameters
uint16_t startY; // start Y coodrinate 2D (top)
uint16_t stopY; // stop Y coordinate 2D (bottom)
char *name;
inline bool getOption(uint8_t n) { return ((options >> n) & 0x01); }
inline bool isSelected() { return getOption(0); }
inline bool isActive() { return stop > start; }
inline uint16_t width() { return options & TRANSPOSED ? stopY - startY : stop - start; }
inline uint16_t height() { return options & TRANSPOSED ? stop - start : stopY - startY; }
inline uint16_t length() { return width(); }
inline uint16_t groupLength() { return grouping + spacing; }
inline uint8_t getLightCapabilities() { return _capabilities; }
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;
if (c == colors[slot]) return false;
@ -291,8 +313,7 @@ class WS2812FX {
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
opacity = o;
}
void setOption(uint8_t n, bool val, uint8_t segn = 255)
{
void setOption(uint8_t n, bool val, uint8_t segn = 255) {
bool prevOn = false;
if (n == SEG_OPTION_ON) {
prevOn = getOption(SEG_OPTION_ON);
@ -300,48 +321,31 @@ class WS2812FX {
ColorTransition::startTransition(opacity, colors[0], instance->_transitionDur, segn, 0);
}
}
if (val) {
options |= 0x01 << n;
} else
{
options &= ~(0x01 << n);
}
if (val) options |= 0x01 << n;
else options &= ~(0x01 << n);
if (n == SEG_OPTION_ON && val && !prevOn) { //fade on
ColorTransition::startTransition(0, colors[0], instance->_transitionDur, segn, 0);
}
}
bool getOption(uint8_t n)
{
return ((options >> n) & 0x01);
uint16_t virtualWidth() {
uint16_t groupLen = groupLength();
uint16_t vWidth = (width() + groupLen - 1) / groupLen;
if (options & MIRROR) vWidth = (vWidth + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vWidth;
}
inline bool isSelected()
{
return getOption(0);
uint16_t virtualHeight() {
uint16_t groupLen = groupLength();
uint16_t vHeight = (height() + groupLen - 1) / groupLen;
if (options & MIRROR_Y_2D) vHeight = (vHeight + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vHeight;
}
inline bool isActive()
{
return stop > start;
}
inline uint16_t length()
{
return stop - start;
}
inline uint16_t groupLength()
{
return grouping + spacing;
}
uint16_t virtualLength()
{
uint16_t virtualLength() {
uint16_t groupLen = groupLength();
uint16_t vLength = (length() + groupLen - 1) / groupLen;
if (options & MIRROR)
vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
if (options & MIRROR) vLength = (vLength + 1) /2; // divide by 2 if mirror, leave at least a single LED
return vLength;
}
uint8_t differs(Segment& b);
inline uint8_t getLightCapabilities() {return _capabilities;}
void refreshLightCapabilities();
} segment;
@ -610,6 +614,8 @@ class WS2812FX {
_mode[FX_MODE_BLENDS] = &WS2812FX::mode_blends;
_mode[FX_MODE_TV_SIMULATOR] = &WS2812FX::mode_tv_simulator;
_mode[FX_MODE_DYNAMIC_SMOOTH] = &WS2812FX::mode_dynamic_smooth;
_mode[FX_MODE_BLACK_HOLE_A] = &WS2812FX::mode_2DBlackHole_A;
_mode[FX_MODE_BLACK_HOLE_B] = &WS2812FX::mode_2DBlackHole_B;
_brightness = DEFAULT_BRIGHTNESS;
currentPalette = CRGBPalette16(CRGB::Black);
@ -637,7 +643,7 @@ class WS2812FX {
setTransitionMode(bool t),
calcGammaTable(float),
trigger(void),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX),
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0, uint16_t offset = UINT16_MAX, uint16_t startY=0, uint16_t stopY=0),
setMainSegmentId(uint8_t n),
restartRuntime(),
resetSegments(),
@ -835,6 +841,62 @@ class WS2812FX {
mode_tv_simulator(void),
mode_dynamic_smooth(void);
// 2D support (panels)
bool
isMatrix = false;
uint8_t
hPanels = 1,
vPanels = 1;
uint16_t
panelH = 8,
panelW = 8,
matrixWidth = DEFAULT_LED_COUNT,
matrixHeight = 1;
#define WLED_MAX_PANELS 64
typedef struct panel_bitfield_t {
unsigned char
bottomStart : 1, // starts at bottom?
rightStart : 1, // starts on right?
vertical : 1, // is vertical?
serpentine : 1; // is serpentine?
} Panel;
Panel
matrix = {0,0,0,0},
panel[WLED_MAX_PANELS] = {{0,0,0,0}};
void
setUpMatrix(),
setPixelColorXY(uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0),
fill2D(uint32_t),
blur1d(fract8 blur_amount, CRGB* leds=nullptr),
blur2d(fract8 blur_amount, CRGB* leds=nullptr),
blurRows(fract8 blur_amount, CRGB* leds=nullptr),
blurColumns(fract8 blur_amount, CRGB* leds=nullptr),
fill_solid(const struct CRGB& color, CRGB* leds=nullptr),
fade_out2D(uint8_t r),
fadeToBlackBy(uint8_t fadeBy, CRGB* leds=nullptr),
nscale8(uint8_t scale, CRGB* leds=nullptr),
setPixels(CRGB* leds);
inline void setPixelColorXY(uint16_t x, uint16_t y, uint32_t c) { setPixelColorXY(x, y, byte(c>>16), byte(c>>8), byte(c), byte(c>>24)); }
inline void setPixelColorXY(uint16_t x, uint16_t y, CRGB &c) { setPixelColorXY(x, y, c.red, c.green, c.blue); }
uint16_t
XY(uint16_t x, uint16_t y, uint8_t seg=255);
uint32_t
getPixelColorXY(uint16_t, uint16_t);
// 2D modes
uint16_t
mode_2DBlackHole_A(),
mode_2DBlackHole_B();
// end 2D support
private:
uint32_t crgb_to_col(CRGB fastled);
CRGB col_to_crgb(uint32_t);
@ -905,9 +967,9 @@ class WS2812FX {
uint8_t _segment_index_palette_last = 99;
uint8_t _mainSegment;
segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 24 bytes per element
// start, stop, offset, speed, intensity, palette, mode, options, grouping, spacing, opacity (unused), color[], capabilities
{0, 7, 0, DEFAULT_SPEED, 128, 0, DEFAULT_MODE, NO_OPTIONS, 1, 0, 255, {DEFAULT_COLOR}, 0}
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
{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}
};
segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 28 bytes per element
friend class Segment_runtime;

349
wled00/FX_2Dfcn.cpp Normal file
View File

@ -0,0 +1,349 @@
/*
FX_2Dfcn.cpp contains all 2D utility functions
LICENSE
The MIT License (MIT)
Copyright (c) 2022 Blaz Kristan (https://blaz.at/home)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Parts of the code adapted from WLED Sound Reactive
*/
#include "wled.h"
#include "FX.h"
#include "palettes.h"
// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels
// this converts physical (possibly irregular) LED arrangement into well defined
// array of logical pixels: fist entry corresponds to left-topmost logical pixel
// followed by horizontal pixels, when matrixWidth logical pixels are added they
// are followed by next row (down) of matrixWidth pixels (and so forth)
// note: matrix may be comprised of multiple panels each with different orientation
// but ledmap takes care of that. ledmap is constructed upon initialization
// so matrix should disable regular ledmap processing
void WS2812FX::setUpMatrix() {
// erase old ledmap, just in case.
if (customMappingTable != nullptr) delete[] customMappingTable;
customMappingTable = nullptr;
customMappingSize = 0;
if (isMatrix) {
matrixWidth = hPanels * panelW;
matrixHeight = vPanels * panelH;
// safety check
if (matrixWidth * matrixHeight > MAX_LEDS) {
matrixWidth = getLengthTotal();
matrixHeight = 1;
isMatrix = false;
return;
}
customMappingSize = matrixWidth * matrixHeight;
customMappingTable = new uint16_t[customMappingSize];
if (customMappingTable != nullptr) {
uint16_t startL; // index in custom mapping array (logical strip)
uint16_t startP; // position of 1st pixel of panel on (virtual) strip
uint16_t x, y, offset;
uint8_t h = matrix.vertical ? vPanels : hPanels;
uint8_t v = matrix.vertical ? hPanels : vPanels;
for (uint8_t j=0, p=0; j<v; j++) {
for (uint8_t i=0; i<h; i++, p++) {
y = (matrix.vertical ? matrix.rightStart : matrix.bottomStart) ? v - j - 1 : j;
x = (matrix.vertical ? matrix.bottomStart : matrix.rightStart) ? h - i - 1 : i;
x = matrix.serpentine && j%2 ? h - x - 1 : x;
startL = (matrix.vertical ? y : x) * panelW + (matrix.vertical ? x : y) * matrixWidth * panelH; // logical index (top-left corner)
startP = p * panelW * panelH; // physical index (top-left corner)
for (uint16_t l=0; l<panelH; l++) {
y = panel[h*j + i].bottomStart ? (panelH - l - 1) : l;
for (uint16_t k=0; k<panelW; k++) {
x = panel[h*j + i].rightStart ? (panelW - k - 1) : k;
if (panel[h*j + i].vertical) {
y = (panel[h*j + i].serpentine && x%2) ? (panelH - y - 1) : y;
offset = y + x * panelH;
} else {
x = (panel[h*j + i].serpentine && y%2) ? (panelW - x - 1) : x;
offset = x + y * panelW;
}
customMappingTable[startL + k + l * matrixWidth] = startP + offset;
}
}
}
}
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) {
if (!(i%matrixWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]);
}
DEBUG_PRINTLN();
#endif
} else {
// memory allocation error
matrixWidth = getLengthTotal();
matrixHeight = 1;
isMatrix = false;
return;
}
} else {
// not a matrix set up
matrixWidth = getLengthTotal();
matrixHeight = 1;
}
}
// XY(x,y,seg) - returns an index of segment pixel in a matrix layout
// index still needs to undergo ledmap processing to represent actual physical pixel
// matrix is always organized by matrixHeight number of matrixWidth pixels from top to bottom, left to right
// so: pixel at XY(5,6) in a 2D segment with [start=10, stop=19, startY=20, stopY=29 : 10x10 pixels]
// corresponds to pixel with logical index of 847 (0 based) if a 2D segment belongs to a 32x32 matrix.
// math: (matrixWidth * (startY + y)) + start + x => (32 * (20+6)) + 10 + 5 = 847
uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y, uint8_t seg) {
if (seg == 255) seg = _segment_index;
x %= _segments[seg].width(); // just in case constrain x (wrap around)
y %= _segments[seg].height(); // just in case constrain y (wrap around)
return ((_segments[seg].startY + y) * matrixWidth) + _segments[seg].start + x;
}
void IRAM_ATTR WS2812FX::setPixelColorXY(uint16_t x, uint16_t y, byte r, byte g, byte b, byte w)
{
if (!isMatrix) return; // not a matrix set-up
uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
if (SEGLEN && _bri_t < 255) {
r = scale8(r, _bri_t);
g = scale8(g, _bri_t);
b = scale8(b, _bri_t);
w = scale8(w, _bri_t);
}
uint32_t col = RGBW32(r, g, b, w);
uint16_t width = _segments[segIdx].virtualWidth(); // segment width in logical pixels
uint16_t height = _segments[segIdx].virtualHeight(); // segment height in logical pixels
if (_segments[segIdx].options & MIRROR ) width = (width + 1) /2; // divide by 2 if mirror, leave at least a single LED
if (_segments[segIdx].options & MIRROR_Y_2D) height = (height + 1) /2; // divide by 2 if mirror, leave at least a single LED
if (_segments[segIdx].options & TRANSPOSED ) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= _segments[segIdx].groupLength();
y *= _segments[segIdx].groupLength();
if (x >= width || y >= height) return; // if pixel would fall out of segment just exit
for (uint8_t j = 0; j < _segments[segIdx].grouping; j++) { // groupping vertically
for (uint8_t g = 0; g < _segments[segIdx].grouping; g++) { // groupping horizontally
uint16_t index, xX = (x+g), yY = (y+j);
if (xX >= width || yY >= height) continue; // we have reached one dimension's end
if (_segments[segIdx].options & REVERSE ) xX = width - xX - 1;
if (_segments[segIdx].options & REVERSE_Y_2D) yY = height - yY - 1;
index = XY(xX, yY, segIdx);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
if (_segments[segIdx].options & MIRROR) { //set the corresponding horizontally mirrored pixel
index = XY(_segments[segIdx].stop - xX - 1, yY, segIdx);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
}
if (_segments[segIdx].options & MIRROR_Y_2D) { //set the corresponding vertically mirrored pixel
index = XY(xX, _segments[segIdx].stopY - yY - 1, segIdx);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
}
}
}
}
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y)
{
uint8_t segIdx = _segment_index;
uint16_t width = _segments[segIdx].virtualWidth(); // segment width in logical pixels
uint16_t height = _segments[segIdx].virtualHeight(); // segment height in logical pixels
if (_segments[segIdx].options & MIRROR ) width = (width + 1) /2; // divide by 2 if mirror, leave at least a single LED
if (_segments[segIdx].options & MIRROR_Y_2D) height = (height + 1) /2; // divide by 2 if mirror, leave at least a single LED
if (_segments[segIdx].options & TRANSPOSED ) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= _segments[segIdx].groupLength();
y *= _segments[segIdx].groupLength();
if (x >= width || y >= height) return 0;
if (_segments[segIdx].options & REVERSE ) x = width - x - 1;
if (_segments[segIdx].options & REVERSE_Y_2D) y = height - y - 1;
uint16_t index = XY(x, y, segIdx);
if (index < customMappingSize) index = customMappingTable[index];
return busses.getPixelColor(index);
}
// blurRows: perform a blur1d on every row of a rectangular matrix
void WS2812FX::blurRows(fract8 blur_amount, CRGB* leds)
{
uint16_t width = SEGMENT.virtualWidth();
uint16_t height = SEGMENT.virtualHeight();
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t y = 0; y < height; y++) for (uint16_t x = 0; x < width; x++) {
CRGB cur = leds ? leds[x + y * width] : col_to_crgb(getPixelColorXY(x,y));
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (x) {
if (leds) leds[(x-1) + y * width] += part;
else setPixelColorXY(x-1, y, col_to_crgb(getPixelColorXY(x-1, y)) + part);
}
if (leds) leds[x + y * width] = cur;
else setPixelColorXY(x, y, cur);
carryover = part;
}
}
// blurColumns: perform a blur1d on each column of a rectangular matrix
void WS2812FX::blurColumns(fract8 blur_amount, CRGB* leds)
{
uint16_t width = SEGMENT.virtualWidth();
uint16_t height = SEGMENT.virtualHeight();
// blur columns
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
for ( uint16_t x = 0; x < width; x++) {
CRGB carryover = CRGB::Black;
for ( uint16_t y = 0; y < height; y++) {
CRGB cur = leds ? leds[x + y * width] : col_to_crgb(getPixelColorXY(x,y));
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (y) {
if (leds) leds[x + (y-1) * width] += part;
else setPixelColorXY(x, y-1, col_to_crgb(getPixelColorXY(x,y-1)) + part);
}
if (leds) leds[x + y * width] = cur;
else setPixelColorXY(x, y, cur);
carryover = part;
}
}
}
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
//
// 0 = no spread at all
// 64 = moderate spreading
// 172 = maximum smooth, even spreading
//
// 173..255 = wider spreading, but increasing flicker
//
// Total light is NOT entirely conserved, so many repeated
// calls to 'blur' will also result in the light fading,
// eventually all the way to black; this is by design so that
// it can be used to (slowly) clear the LEDs to black.
void WS2812FX::blur1d(fract8 blur_amount, CRGB* leds)
{
blurRows(blur_amount, leds);
}
void WS2812FX::blur2d(fract8 blur_amount, CRGB* leds)
{
blurRows(blur_amount, leds);
blurColumns(blur_amount, leds);
}
//ewowi20210628: new functions moved from colorutils: add segment awareness
/*
* Fills segment with color
*/
void WS2812FX::fill2D(uint32_t c) {
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
setPixelColorXY(x, y, c);
}
}
void WS2812FX::fill_solid(const struct CRGB& color, CRGB* leds) {
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
if (leds) leds[x + y * w] = color;
else setPixelColorXY(x, y, color);
}
}
/*
* fade out function, higher rate = quicker fade
* TODO: may be better to use approach of nscale8()
*/
void WS2812FX::fade_out2D(uint8_t rate) {
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
rate = (255-rate) >> 1;
float mappedRate = float(rate) +1.1;
uint32_t color = SEGCOLOR(1); // target color
int w2 = W(color);
int r2 = R(color);
int g2 = G(color);
int b2 = B(color);
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
color = getPixelColorXY(x, y);
int w1 = W(color);
int r1 = R(color);
int g1 = G(color);
int b1 = B(color);
int wdelta = (w2 - w1) / mappedRate;
int rdelta = (r2 - r1) / mappedRate;
int gdelta = (g2 - g1) / mappedRate;
int bdelta = (b2 - b1) / mappedRate;
// if fade isn't complete, make sure delta is at least 1 (fixes rounding issues)
wdelta += (w2 == w1) ? 0 : (w2 > w1) ? 1 : -1;
rdelta += (r2 == r1) ? 0 : (r2 > r1) ? 1 : -1;
gdelta += (g2 == g1) ? 0 : (g2 > g1) ? 1 : -1;
bdelta += (b2 == b1) ? 0 : (b2 > b1) ? 1 : -1;
setPixelColorXY(x, y, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta);
}
}
void WS2812FX::fadeToBlackBy(uint8_t fadeBy, CRGB* leds) {
nscale8(255 - fadeBy, leds);
}
void WS2812FX::nscale8(uint8_t scale, CRGB* leds) {
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
for(uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) {
if (leds) leds[x + y * w].nscale8(scale);
else setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x, y)).nscale8(scale));
}
}
void WS2812FX::setPixels(CRGB* leds) {
uint16_t w = SEGMENT.virtualWidth();
uint16_t h = SEGMENT.virtualHeight();
for (uint16_t y = 0; y < h; y++) for (uint16_t x = 0; x < w; x++) setPixelColorXY(x, y, leds[x + y*w]);
}

View File

@ -184,23 +184,27 @@ void WS2812FX::service() {
void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
{
uint8_t segIdx;
uint8_t segIdx = SEGLEN ? _segment_index : _mainSegment;
if (isMatrix) {
// map linear pixel into 2D segment area (even for 1D segments, expanding vertically)
uint16_t h = _segments[segIdx].height(); // segment height in logical pixels
uint8_t l = _segments[segIdx].groupLength();
for (uint16_t y = 0; y < h; y += l) { // expand 1D effect vertically
setPixelColorXY(i, y, r, g, b, w);
}
return;
}
if (SEGLEN) { // SEGLEN!=0 -> from segment/FX
if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
//color_blend(getpixel, col, _bri_t); (pseudocode for future blending of segments)
if (_bri_t < 255) {
if (SEGLEN && _bri_t < 255) { // SEGLEN!=0 -> from segment/FX
r = scale8(r, _bri_t);
g = scale8(g, _bri_t);
b = scale8(b, _bri_t);
w = scale8(w, _bri_t);
}
segIdx = _segment_index;
} else // from live/realtime
segIdx = _mainSegment;
if (SEGLEN || (realtimeMode && useMainSegmentOnly)) {
uint32_t col = RGBW32(r, g, b, w);
uint16_t len = _segments[segIdx].length();
uint16_t len = _segments[segIdx].length(); // length of segment in number of pixels
// get physical pixel address (taking into account start, grouping, spacing [and offset])
i = i * _segments[segIdx].groupLength();
@ -211,7 +215,7 @@ void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte
i = (len - 1) - i;
}
}
i += _segments[segIdx].start;
i += _segments[segIdx].start; // starting pixel in a group
// set all the pixels in the group
for (uint16_t j = 0; j < _segments[segIdx].grouping; j++) {
@ -221,15 +225,13 @@ void IRAM_ATTR WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte
if (_segments[segIdx].options & MIRROR) { //set the corresponding mirrored pixel
uint16_t indexMir = _segments[segIdx].stop - indexSet + _segments[segIdx].start - 1;
indexMir += _segments[segIdx].offset; // offset/phase
if (indexMir >= _segments[segIdx].stop) indexMir -= len;
if (indexMir >= _segments[segIdx].stop) indexMir -= len; // wrap
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
busses.setPixelColor(indexMir, col);
}
indexSet += _segments[segIdx].offset; // offset/phase
if (indexSet >= _segments[segIdx].stop) indexSet -= len;
if (indexSet >= _segments[segIdx].stop) indexSet -= len; // wrap
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col);
@ -496,6 +498,8 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
uint32_t WS2812FX::getPixelColor(uint16_t i)
{
if (isMatrix) return getPixelColorXY(i, 0);
// get physical pixel
i = i * SEGMENT.groupLength();;
if (IS_REVERSE) {
@ -566,14 +570,13 @@ uint8_t WS2812FX::Segment::differs(Segment& b) {
if (c1x != b.c1x) d |= SEG_DIFFERS_FX;
if (c2x != b.c2x) d |= SEG_DIFFERS_FX;
if (c3x != b.c3x) d |= SEG_DIFFERS_FX;
if (startY != b.startY) d |= SEG_DIFFERS_BOUNDS;
if (stopY != b.stopY) d |= SEG_DIFFERS_BOUNDS;
if ((options & 0b00101110) != (b.options & 0b00101110)) d |= SEG_DIFFERS_OPT;
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
if ((options & 0x01) != (b.options & 0x01)) d |= SEG_DIFFERS_SEL;
for (uint8_t i = 0; i < NUM_COLORS; i++)
{
if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
}
for (uint8_t i = 0; i < NUM_COLORS; i++) if (colors[i] != b.colors[i]) d |= SEG_DIFFERS_COL;
return d;
}
@ -640,12 +643,15 @@ bool WS2812FX::hasCCTBus(void) {
return false;
}
void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping, uint8_t spacing, uint16_t offset) {
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 >= MAX_NUM_SEGMENTS) return;
Segment& seg = _segments[n];
//return if neither bounds nor grouping have changed
bool boundsUnchanged = (seg.start == i1 && seg.stop == i2);
if (isMatrix) {
boundsUnchanged &= (seg.startY == startY && seg.stopY == stopY);
}
if (boundsUnchanged
&& (!grouping || (seg.grouping == grouping && seg.spacing == spacing))
&& (offset == UINT16_MAX || offset == seg.offset)) return;
@ -662,9 +668,17 @@ void WS2812FX::setSegment(uint8_t n, uint16_t i1, uint16_t i2, uint8_t grouping,
if (n == _mainSegment) setMainSegmentId(0);
return;
}
if (i1 < _length) seg.start = i1;
seg.stop = i2;
if (i2 > _length) seg.stop = _length;
if (isMatrix) {
if (i1 < matrixWidth) seg.start = i1;
seg.stop = i2 > matrixWidth ? matrixWidth : i2;
if (startY < matrixHeight) seg.startY = startY;
seg.stopY = stopY > matrixHeight ? matrixHeight : MAX(1,stopY);
} else {
if (i1 < _length) seg.start = i1;
seg.stop = i2 > _length ? _length : i2;
seg.startY = 0;
seg.stopY = 1;
}
if (grouping) {
seg.grouping = grouping;
seg.spacing = spacing;
@ -1157,6 +1171,8 @@ uint32_t IRAM_ATTR WS2812FX::color_from_palette(uint16_t i, bool mapping, bool w
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
void WS2812FX::deserializeMap(uint8_t n) {
if (isMatrix) return; // 2D support creates its own ledmap
char fileName[32];
strcpy_P(fileName, PSTR("/ledmap"));
if (n) sprintf(fileName +7, "%d", n);
@ -1399,7 +1415,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Candy Cane@!,Width;;",
"Blends@Shift speed,Blend speed;1,2,3,!",
"TV Simulator",
"Dynamic Smooth"
"Dynamic Smooth",
"2D Black Hole A@!,!,C1,C2,C3;!;!",
"2D Black Hole B@!,!,C1,C2,C3;!;!"
])=====";
const char JSON_palette_names[] PROGMEM = R"=====([

View File

@ -91,6 +91,41 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
// 2D Matrix Settings
JsonObject matrix = hw_led[F("matrix")];
if (!matrix.isNull()) {
strip.isMatrix = true;
CJSON(strip.panelH, matrix[F("ph")]);
CJSON(strip.panelW, matrix[F("pw")]);
CJSON(strip.hPanels, matrix[F("mph")]);
CJSON(strip.vPanels, matrix[F("mpv")]);
CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
CJSON(strip.matrix.rightStart, matrix[F("pr")]);
CJSON(strip.matrix.vertical, matrix[F("pv")]);
CJSON(strip.matrix.serpentine, matrix[F("ps")]);
JsonArray panels = matrix[F("panels")];
uint8_t s = 0;
if (!panels.isNull()) {
for (JsonObject pnl : panels) {
CJSON(strip.panel[s].bottomStart, pnl["b"]);
CJSON(strip.panel[s].rightStart, pnl["r"]);
CJSON(strip.panel[s].vertical, pnl["v"]);
CJSON(strip.panel[s].serpentine, pnl["s"]);
if (++s >= WLED_MAX_PANELS) break; // max panels reached
}
}
// clear remaining panels
for (; s<WLED_MAX_PANELS; s++) {
strip.panel[s].bottomStart = 0;
strip.panel[s].rightStart = 0;
strip.panel[s].vertical = 0;
strip.panel[s].serpentine = 0;
}
strip.setUpMatrix();
}
JsonArray ins = hw_led["ins"];
if (fromFS || !ins.isNull()) {
@ -583,6 +618,28 @@ void serializeConfig() {
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override
// 2D Matrix Settings
if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
matrix[F("ph")] = strip.panelH;
matrix[F("pw")] = strip.panelW;
matrix[F("mph")] = strip.hPanels;
matrix[F("mpv")] = strip.vPanels;
matrix[F("pb")] = strip.matrix.bottomStart;
matrix[F("pr")] = strip.matrix.rightStart;
matrix[F("pv")] = strip.matrix.vertical;
matrix[F("ps")] = strip.matrix.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels"));
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
JsonObject pnl = panels.createNestedObject();
pnl["b"] = strip.panel[i].bottomStart;
pnl["r"] = strip.panel[i].rightStart;
pnl["v"] = strip.panel[i].vertical;
pnl["s"] = strip.panel[i].serpentine;
}
}
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {

View File

@ -223,6 +223,9 @@
#define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
#define SEG_OPTION_TRANSITIONAL 7
#define SEG_OPTION_REVERSED_Y 8
#define SEG_OPTION_MIRROR_Y 9
#define SEG_OPTION_TRANSPOSE 10
//Segment differs return byte
#define SEG_DIFFERS_BRI 0x01

View File

@ -972,6 +972,11 @@ textarea {
margin-top: 8px;
}
TD .revchkl {
padding: 0 0 0 32px;
margin-top: 0;
}
.check input, .radio input {
position: absolute;
opacity: 0;
@ -1041,6 +1046,10 @@ textarea {
background: var(--c-f);
}
TD .checkmark, TD .radiomark {
top: -6px;
}
.h {
font-size: 13px;
text-align: center;

View File

@ -20,6 +20,7 @@ var pJson = {}, eJson = {}, lJson = {};
var pN = "", pI = 0, pNum = 0;
var pmt = 1, pmtLS = 0, pmtLast = 0;
var lastinfo = {};
var isM = false, mw = 0, mh=0;
var ws, cpick, ranges;
var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
@ -599,6 +600,10 @@ function parseInfo(i) {
syncTglRecv = i.str;
maxSeg = i.leds.maxseg;
pmt = i.fs.pmt;
// do we have a matrix set-up
mw = i.leds.matrix ? i.leds.matrix.w : 0;
mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0;
}
function populateInfo(i)
@ -665,6 +670,13 @@ function populateSegments(s)
<div class="sliderdisplay"></div>
</div>
</div>`;
let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark schk"></span></label>`;
let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark schk"></span></label>`;
let rvYck = "", miYck ="";
if (isM) {
rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark schk"></span></label>`;
miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark schk"></span></label>`;
}
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}">
<label class="check schkl">
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
@ -681,19 +693,25 @@ function populateSegments(s)
<input type="text" class="ptxt noslide" id="seg${i}t" autocomplete="off" maxlength=32 value="${inst.n?inst.n:""}" placeholder="Enter name..."/>
<table class="infot segt">
<tr>
<td>Start LED</td>
<td>${cfg.comp.seglen?"LED count":"Stop LED"}</td>
<td>Offset</td>
<td>${isM?'Start X':'Start LED'}</td>
<td>${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
<td>${isM?'':'Offset'}</td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${ledCount-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${ledCount-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
<td><input class="noslide segn" id="seg${i}s" type="number" min="0" max="${(isM?mw:ledCount)-1}" value="${inst.start}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td><input class="noslide segn" id="seg${i}e" type="number" min="0" max="${(isM?mw:ledCount)-(cfg.comp.seglen?inst.start:0)}" value="${inst.stop-(cfg.comp.seglen?inst.start:0)}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
<td style="text-align:revert;">${isM?miXck+'<br>'+rvXck:''}<input class="noslide segn ${isM?'hide':''}" id="seg${i}of" type="number" value="${inst.of}" oninput="updateLen(${i})"></td>
</tr>
${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td><td></td></tr>'+
'<tr>'+
'<td><input class="noslide segn" id="seg'+i+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+inst.startY+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td><input class="noslide segn" id="seg'+i+'eY" type="number" min="0" max="'+(mh-(cfg.comp.seglen?inst.startY:0))+'" value="'+(inst.stopY-(cfg.comp.seglen?inst.start:0))+'" oninput="updateLen('+i+')" onkeydown="segEnter('+i+')"></td>'+
'<td style="text-align:revert;">'+miYck+'<br>'+rvYck+'</td>'+
'</tr>':''}
<tr>
<td>Grouping</td>
<td>Spacing</td>
<td>Apply</td>
<td><!--Apply--></td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${i}grp" type="number" min="1" max="255" value="${inst.grp}" oninput="updateLen(${i})" onkeydown="segEnter(${i})"></td>
@ -702,14 +720,10 @@ function populateSegments(s)
</tr>
</table>
<div class="h bp" id="seg${i}len"></div>
${!isM?rvXck:''}
<label class="check revchkl">
Reverse direction
<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev ? "checked":""}>
<span class="checkmark schk"></span>
</label>
<label class="check revchkl">
Mirror effect
<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi ? "checked":""}>
${isM?'Transpose':'Mirror effect'}
<input type="checkbox" id="seg${i}${isM?'tp':'mi'}" onchange="${(isM?'setTp(':'setMi(')+i})" ${isM?(inst.tp?"checked":""):(inst.mi?"checked":"")}>
<span class="checkmark schk"></span>
</label>
<div class="del">
@ -736,7 +750,7 @@ function populateSegments(s)
if (!gId(`seg${i}sel`).checked) gId(`selall`).checked = false;
}
if (segCount < 2) gId(`segd${lSeg}`).style.display = "none";
if (!noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).style.display = "inline";
gId('rsbtn').style.display = (segCount > 1) ? "inline":"none";
}
@ -966,6 +980,11 @@ function updateLen(s)
var start = parseInt(gId(`seg${s}s`).value);
var stop = parseInt(gId(`seg${s}e`).value);
var len = stop - (cfg.comp.seglen?0:start);
if (isM) {
start = parseInt(gId(`seg${s}sY`).value);
stop = parseInt(gId(`seg${s}eY`).value);
len *= (stop-(cfg.comp.seglen?0:start));
}
var out = "(delete)";
if (len > 1) {
out = `${len} LEDs`;
@ -1280,7 +1299,7 @@ function setSliderAndColorControl(idx, applyDef=false)
var v = Math.max(0,Math.min(255,parseInt(slOnOff[i].substr(dPos+1))));
if (i==0) { if (applyDef) gId("sliderSpeed").value = v; obj.seg.sx = v; }
else if (i==1) { if (applyDef) gId("sliderIntensity").value = v; obj.seg.ix = v; }
else { if (applyDef) gId("sliderC"+(i-1)).value = v; obj.seg["C"+(i-1)] = v}
else { if (applyDef) gId("sliderC"+(i-1)).value = v; obj.seg["c"+(i-1)+"x"] = v}
slOnOff[i] = slOnOff[i].substring(0,dPos);
}
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
@ -1501,6 +1520,7 @@ function makeSeg()
{
var ns = 0;
var lu = lowestUnused;
let li = lastinfo;
if (lu > 0) {
var pend = parseInt(gId(`seg${lu -1}e`).value,10) + (cfg.comp.seglen?parseInt(gId(`seg${lu -1}s`).value,10):0);
if (pend < ledCount) ns = pend;
@ -1515,14 +1535,19 @@ function makeSeg()
<input type="text" class="noslide" id="seg${lu}t" autocomplete="off" maxlength=32 value="" placeholder="New segment ${lu}"/>
<table class="segt">
<tr>
<td width="38%">Start LED</td>
<td width="38%">${cfg.comp.seglen?"LED count":"Stop LED"}</td>
<td width="38%">${isM?'Start X':'Start LED'}</td>
<td width="38%">${isM?(cfg.comp.seglen?"Width":"Stop X"):(cfg.comp.seglen?"LED count":"Stop LED")}</td>
</tr>
<tr>
<td><input class="noslide segn" id="seg${lu}s" type="number" min="0" max="${ledCount-1}" value="${ns}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><input class="noslide segn" id="seg${lu}e" type="number" min="0" max="${ct}" value="${ct}" oninput="updateLen(${lu})" onkeydown="segEnter(${lu})"></td>
<td><button class="btn btn-xs" onclick="setSeg(${lu});resetUtil();"><i class="icons bth-icon" id="segc${lu}">&#xe390;</i></button></td>
</tr>
${isM ? '<tr><td>Start Y</td><td>'+(cfg.comp.seglen?'Height':'Stop Y')+'</td></tr>'+
'<tr>'+
'<td><input class="noslide segn" id="seg'+lu+'sY" type="number" min="0" max="'+(mh-1)+'" value="'+0+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+
'<td><input class="noslide segn" id="seg'+lu+'eY" type="number" min="0" max="'+mh+'" value="'+mh+'" oninput="updateLen('+lu+')" onkeydown="segEnter('+lu+')"></td>'+
'</tr>':''}
</table>
<div class="h" id="seg${lu}len">${ledCount - ns} LEDs</div>
<div class="c"><button class="btn btn-p" onclick="resetUtil()">Cancel</button></div>
@ -1813,6 +1838,7 @@ function selSeg(s)
function rptSeg(s)
{
//TODO: 2D support
var name = gId(`seg${s}t`).value;
var start = parseInt(gId(`seg${s}s`).value);
var stop = parseInt(gId(`seg${s}e`).value);
@ -1842,6 +1868,12 @@ function setSeg(s)
var stop = parseInt(gId(`seg${s}e`).value);
if ((cfg.comp.seglen && stop == 0) || (!cfg.comp.seglen && stop <= start)) {delSeg(s); return;}
var obj = {"seg": {"id": s, "n": name, "start": start, "stop": (cfg.comp.seglen?start:0)+stop}};
if (isM) {
var startY = parseInt(gId(`seg${s}sY`).value);
var stopY = parseInt(gId(`seg${s}eY`).value);
obj.seg.startY = startY;
obj.seg.stopY = (cfg.comp.seglen?startY:0)+stopY;
}
if (gId(`seg${s}grp`)) {
var grp = parseInt(gId(`seg${s}grp`).value);
var spc = parseInt(gId(`seg${s}spc`).value);
@ -1871,6 +1903,13 @@ function setRev(s)
requestJson(obj);
}
function setRevY(s)
{
var rev = gId(`seg${s}rY`).checked;
var obj = {"seg": {"id": s, "rY": rev}};
requestJson(obj);
}
function setMi(s)
{
var mi = gId(`seg${s}mi`).checked;
@ -1878,6 +1917,20 @@ function setMi(s)
requestJson(obj);
}
function setMiY(s)
{
var mi = gId(`seg${s}mY`).checked;
var obj = {"seg": {"id": s, "mY": mi}};
requestJson(obj);
}
function setTp(s)
{
var tp = gId(`seg${s}tp`).checked;
var obj = {"seg": {"id": s, "tp": tp}};
requestJson(obj);
}
function setSegPwr(s)
{
var pwr = gId(`seg${s}pwr`).classList.contains("act");

View File

@ -18,31 +18,33 @@
margin: 0;
}
html {
--h: 10.2vh;
--h: 9vh;
}
button {
background: #333;
color: #fff;
font-family: Verdana, Helvetica, sans-serif;
display: inline-block;
display: block;
border: 1px solid #333;
border-radius: var(--h);
font-size: 6vmin;
height: var(--h);
width: calc(100% - 40px);
margin-top: 2vh;
cursor: pointer;
}
</style>
</head>
<body onload="S()">
<form action="/"><button type=submit id="b">Back</button></form>
<form action="/settings/wifi"><button type="submit">WiFi Setup</button></form>
<form action="/settings/leds"><button type="submit">LED Preferences</button></form>
<form action="/settings/ui"><button type="submit">User Interface</button></form>
<form action="/settings/dmx"><button id="dmxbtn" style="display: none;" type="submit">DMX Output</button></form>
<form action="/settings/sync"><button type="submit">Sync Interfaces</button></form>
<form action="/settings/time"><button type="submit">Time & Macros</button></form>
<form action="/settings/um"><button type="submit">Usermods</button></form>
<form action="/settings/sec"><button type="submit">Security & Updates</button></form>
<button type=submit id="b" onclick="window.location='/'">Back</button>
<button type="submit" onclick="window.location='./settings/wifi'">WiFi Setup</button>
<button type="submit" onclick="window.location='./settings/leds'">LED Preferences</button>
<button type="submit" onclick="window.location='./settings/2D'">2D Configuration</button>
<button type="submit" onclick="window.location='./settings/ui'">User Interface</button>
<button id="dmxbtn" style="display: none;" type="submit" onclick="window.location='./settings/dmx'">DMX Output</button>
<button type="submit" onclick="window.location='./settings/sync'">Sync Interfaces</button>
<button type="submit" onclick="window.location='./settings/time'">Time & Macros</button>
<button type="submit" onclick="window.location='./settings/um'">Usermods</button>
<button type="submit" onclick="window.location='./settings/sec'">Security & Updates</button>
</body>
</html>

126
wled00/data/settings_2D.htm Normal file
View File

@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=500">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>2D Set-up</title>
<script>
var d=document;
function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);}
var maxPanels=64;
function UI(change=false)
{
if (gId("somp").value === "0") {
gId("mpdiv").style.display = "none";
resetPanels();
return;
}
gId("mpdiv").style.display = "block";
maxPanels = parseInt(d.Sf.MPH.value) * parseInt(d.Sf.MPV.value);
let i = gId("panels").children.length;
if (i<maxPanels) for (let j=i; j<maxPanels; j++) addPanel(j);
if (i>maxPanels) for (let j=i; j>maxPanels; j--) remPanel();
//btnPanel(gId("panels").children.length);
}
function addPanels() {
let h = parseInt(d.Sf.MPH.value);
let v = parseInt(d.Sf.MPV.value);
for (let i=0; i<h*v; i++) addPanel(i);
}
function addPanel(i=0) {
let p = gId("panels");
if (p.children.length >= maxPanels) return;
let b = `<div id="pnl${i}">${i===0?"":'<hr style="width:260px">'}Panel ${i}<br>1<sup>st</sup> LED: <select name="P${i}B">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="P${i}R">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="P${i}V">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="P${i}S"></div>`;
p.insertAdjacentHTML("beforeend", b);
}
function remPanel() {
let p = gId("panels").children;
var i = p.length;
if (i <= 1) return;
p[i-1].remove();
}
function resetPanels() {
d.Sf.MPH.value = 1;
d.Sf.MPV.value = 1;
for (let e of gId("panels").children) e.remove();
}
function btnPanel(i) {
gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
gId("pnl_rem").style.display = (i>1) ? "inline":"none";
}
function S() {GetV();UI()}
//values injected by server while sending HTML
function GetV() {}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>2D setup</h2>
Strip or panel:
<select id="somp" name="SOMP" onchange="resetPanels();addPanels();UI();" >
<option value="0">1D Strip</option>
<option value="1">2D Matrix</option>
</select><br>
<div id="mpdiv" style="display:none;">
<h3>2D Matrix</h3>
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1" oninput="UI()">
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1" oninput="UI()"><br>
1<sup>st</sup> panel: <select name="PB">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="PR">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="PV">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="PS">
<hr style="width:260px">
<i>A matrix is made of 1 or more physical led panels of the same dimensions.<br>
Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>
Each panel can have different orientation and/or starting point and/or layout.</i><br>
<hr style="width:260px">
<h3>2D Panel layout</h3>
<div id="panels">
</div>
<!--
<button type="button" class="xs" id="pnl_add" onclick="addPanel()">+</button>
<button type="button" class="xs" id="pnl_rem" onclick="remPanel()">-</button>
-->
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
</html>

View File

@ -64,50 +64,52 @@ const uint8_t PAGE_settingsCss[] PROGMEM = {
// Autogenerated from wled00/data/settings.htm, do not edit!!
const uint16_t PAGE_settings_length = 669;
const uint16_t PAGE_settings_length = 694;
const uint8_t PAGE_settings[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x8d, 0x94, 0xdb, 0x6e, 0xdb, 0x30,
0x0c, 0x86, 0xef, 0xfd, 0x14, 0x9a, 0x8b, 0x0d, 0x09, 0x10, 0x37, 0x6d, 0x3a, 0x0c, 0x83, 0x0f,
0x19, 0xd0, 0xb5, 0xdd, 0x0a, 0xac, 0x68, 0x81, 0x9e, 0xb6, 0x4b, 0x59, 0xa2, 0x13, 0xae, 0x96,
0x64, 0x48, 0x74, 0x1a, 0x2f, 0xc8, 0xbb, 0x4f, 0x76, 0xb2, 0xb4, 0x6b, 0x17, 0x2c, 0x37, 0x06,
0x48, 0x51, 0x3f, 0xc9, 0x8f, 0x94, 0xd3, 0x37, 0x27, 0x97, 0x9f, 0x6f, 0x7e, 0x5c, 0x9d, 0xb2,
0x29, 0xa9, 0x72, 0x9c, 0xb6, 0x5f, 0x56, 0x72, 0x3d, 0xc9, 0x42, 0xd0, 0xa1, 0xb7, 0x81, 0xcb,
0x71, 0xaa, 0x80, 0x38, 0x13, 0x53, 0x6e, 0x1d, 0x50, 0x16, 0xde, 0xde, 0x9c, 0x45, 0x1f, 0xc3,
0xb5, 0x37, 0x10, 0x46, 0x13, 0x68, 0xef, 0x7e, 0x44, 0x49, 0xd3, 0x4c, 0xc2, 0x0c, 0x05, 0x44,
0x9d, 0x31, 0x40, 0x8d, 0x84, 0xbc, 0x8c, 0x9c, 0xe0, 0x25, 0x64, 0x87, 0x03, 0xc5, 0xe7, 0xa8,
0x6a, 0xb5, 0xb1, 0x6b, 0x07, 0xb6, 0x33, 0x78, 0xee, 0x6d, 0x6d, 0x42, 0x16, 0x68, 0xae, 0x20,
0x0b, 0x67, 0x08, 0x8f, 0x95, 0xb1, 0xe4, 0xb3, 0x10, 0x52, 0x09, 0xe3, 0xfb, 0x6f, 0xa7, 0x27,
0xec, 0x1a, 0x88, 0x50, 0x4f, 0x5c, 0x3a, 0x5c, 0x39, 0x53, 0x27, 0x2c, 0x56, 0x34, 0x0e, 0x66,
0xdc, 0x32, 0x99, 0x49, 0x23, 0x6a, 0xe5, 0x4b, 0x49, 0x8a, 0x5a, 0x0b, 0x42, 0xa3, 0xd9, 0xe4,
0x5c, 0xf6, 0x74, 0x7f, 0x61, 0x81, 0x6a, 0xab, 0x99, 0xdc, 0x9f, 0x00, 0x9d, 0x96, 0xd0, 0xc6,
0x1c, 0x37, 0xdd, 0xd1, 0x72, 0x13, 0x7a, 0xdd, 0xeb, 0x2f, 0xbe, 0x00, 0xdd, 0xf5, 0xfa, 0xcb,
0x20, 0x1d, 0xae, 0x85, 0xd7, 0x09, 0x98, 0xb3, 0x22, 0x0b, 0x87, 0x6e, 0x9d, 0x7e, 0xe8, 0xf6,
0x7f, 0xba, 0x4f, 0x55, 0x76, 0xe0, 0xab, 0x7b, 0x8a, 0xa4, 0xc6, 0x57, 0x14, 0xe4, 0x46, 0x36,
0x0b, 0x82, 0x39, 0x45, 0xbc, 0xc4, 0x89, 0x8e, 0x85, 0xcf, 0x05, 0x36, 0xc9, 0xb9, 0x78, 0x98,
0x58, 0x53, 0x6b, 0x19, 0xef, 0x8d, 0x46, 0xa3, 0x64, 0x0a, 0x38, 0x99, 0x52, 0x7c, 0x78, 0x70,
0x50, 0xcd, 0x13, 0xc5, 0xed, 0x04, 0x75, 0x7c, 0xb0, 0x6c, 0xe1, 0x2f, 0xa2, 0x68, 0xea, 0xfd,
0xfb, 0xa3, 0xd9, 0x74, 0x99, 0xd7, 0x44, 0x46, 0x2f, 0x9e, 0x5f, 0x3e, 0x3a, 0x3a, 0x4a, 0x84,
0x29, 0x8d, 0x8d, 0xf7, 0x8a, 0xa2, 0x48, 0x0a, 0x0f, 0x3f, 0x2a, 0xb8, 0xc2, 0xb2, 0x89, 0xef,
0xc0, 0x4a, 0xae, 0xf9, 0xe0, 0x2b, 0x94, 0x33, 0x20, 0x14, 0x7c, 0xe0, 0xb8, 0x76, 0x91, 0x47,
0x8c, 0x45, 0x22, 0xd1, 0x55, 0x25, 0x6f, 0x62, 0xd4, 0x25, 0x6a, 0x88, 0xf2, 0xd2, 0x88, 0x87,
0x24, 0x37, 0x56, 0x82, 0x8d, 0x0f, 0xab, 0x39, 0x73, 0xa6, 0x44, 0xc9, 0x3a, 0xf9, 0x95, 0x37,
0xb2, 0x5c, 0x62, 0xed, 0x62, 0x8f, 0xb6, 0xe7, 0x4b, 0xea, 0xaf, 0x52, 0x39, 0xfc, 0x05, 0xf1,
0x87, 0x99, 0x42, 0xfd, 0xa7, 0x85, 0xcd, 0x79, 0x37, 0xf1, 0xd8, 0xcf, 0x52, 0xf4, 0x7c, 0x5b,
0x6f, 0x59, 0xc4, 0xde, 0xfb, 0xe6, 0xfa, 0xeb, 0xee, 0x22, 0x32, 0x55, 0xdc, 0xf6, 0xd4, 0xc2,
0xed, 0x50, 0xa5, 0xc3, 0xd5, 0x6e, 0xb5, 0xc4, 0x98, 0xd1, 0xa5, 0xe1, 0x32, 0x0b, 0xfd, 0x14,
0x3c, 0xd4, 0xc2, 0x58, 0xc5, 0x78, 0x37, 0x17, 0x8f, 0xdd, 0x3b, 0x56, 0x20, 0x18, 0x35, 0x95,
0xdf, 0x0d, 0x57, 0xe7, 0x0a, 0x29, 0x64, 0xe8, 0xe3, 0xf3, 0x70, 0x1c, 0x1c, 0x7b, 0x3c, 0xe9,
0x70, 0x15, 0xe2, 0x55, 0xdb, 0xcb, 0x2f, 0x25, 0x36, 0x93, 0x7b, 0xc4, 0x02, 0xb7, 0xe8, 0x8d,
0x83, 0x7b, 0x3c, 0xc3, 0x76, 0xc7, 0xea, 0x6a, 0x57, 0xb9, 0x12, 0xa4, 0xdb, 0x2a, 0xd7, 0x6e,
0xec, 0x95, 0x85, 0x02, 0x2c, 0x68, 0x01, 0x6e, 0x57, 0xcd, 0xfa, 0x59, 0x81, 0xc1, 0xdf, 0x92,
0xb7, 0x7e, 0x96, 0xec, 0xbc, 0x5d, 0xa8, 0x82, 0x0b, 0xd8, 0x55, 0x50, 0xaa, 0xf9, 0x46, 0x31,
0xe8, 0xa8, 0x79, 0x4f, 0x4e, 0x3a, 0x64, 0xdd, 0x20, 0xbc, 0xb9, 0xde, 0x0d, 0x6d, 0x34, 0x84,
0x2f, 0xba, 0x38, 0xb9, 0xf8, 0xce, 0x2e, 0x6b, 0xaa, 0x6a, 0xfa, 0x77, 0xba, 0xe0, 0x75, 0x3e,
0xd7, 0x68, 0xb1, 0x0d, 0xca, 0xb5, 0x3f, 0x7b, 0xea, 0xe0, 0x15, 0x93, 0x60, 0x5b, 0x0f, 0x84,
0x0a, 0xb6, 0x69, 0xde, 0xf8, 0x33, 0xf6, 0x8e, 0x5d, 0x70, 0x61, 0xcd, 0x93, 0x62, 0xf0, 0x3f,
0xcc, 0x6a, 0x9b, 0x5e, 0x4b, 0x59, 0x19, 0xb9, 0xbb, 0x94, 0x83, 0xed, 0xfd, 0x82, 0xa8, 0x2d,
0x52, 0xe3, 0xeb, 0xbb, 0xad, 0x24, 0x27, 0x70, 0xc1, 0x2b, 0x8e, 0xc3, 0xf6, 0x05, 0xb4, 0xcf,
0xa1, 0xfd, 0x01, 0xff, 0x06, 0x3e, 0x6a, 0x2c, 0x53, 0x90, 0x05, 0x00, 0x00
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x55, 0x6d, 0x6f, 0xd3, 0x30,
0x10, 0xfe, 0xde, 0x5f, 0x61, 0x32, 0xc1, 0x5a, 0xa9, 0x69, 0xbb, 0x0e, 0x21, 0xc8, 0x4b, 0x91,
0xb6, 0x6e, 0x30, 0x89, 0x69, 0x93, 0xf6, 0x06, 0x1f, 0x1d, 0xfb, 0x92, 0x1c, 0x4b, 0xec, 0xc8,
0x76, 0xfa, 0x42, 0xd5, 0xff, 0xce, 0x25, 0x2d, 0xdb, 0x10, 0x13, 0x4c, 0xa8, 0x5f, 0xe2, 0xdc,
0xd9, 0x79, 0xee, 0xb9, 0xe7, 0x39, 0x2b, 0xd1, 0xab, 0xe9, 0xc5, 0xf1, 0xf5, 0xb7, 0xcb, 0x13,
0x96, 0xbb, 0xb2, 0x98, 0x44, 0xcd, 0x93, 0x15, 0x5c, 0x65, 0xb1, 0x07, 0xca, 0xa3, 0x18, 0xb8,
0x9c, 0x44, 0x25, 0x38, 0xce, 0x44, 0xce, 0x8d, 0x05, 0x17, 0x7b, 0x37, 0xd7, 0xa7, 0xfe, 0x7b,
0x6f, 0x9b, 0xed, 0x08, 0xad, 0x1c, 0x28, 0x4a, 0xcf, 0x51, 0xba, 0x3c, 0x96, 0x30, 0x43, 0x01,
0x7e, 0x1b, 0xf4, 0x51, 0xa1, 0x43, 0x5e, 0xf8, 0x56, 0xf0, 0x02, 0xe2, 0x83, 0x7e, 0xc9, 0x17,
0x58, 0xd6, 0xe5, 0x43, 0x5c, 0x5b, 0x30, 0x6d, 0xc0, 0x13, 0x8a, 0x95, 0xf6, 0x58, 0x47, 0xf1,
0x12, 0x62, 0x6f, 0x86, 0x30, 0xaf, 0xb4, 0x71, 0x54, 0xc5, 0xa1, 0x2b, 0x60, 0x72, 0xf7, 0xe5,
0x64, 0xca, 0xae, 0xc0, 0x39, 0x54, 0x99, 0x8d, 0x86, 0x9b, 0x64, 0x64, 0x85, 0xc1, 0xca, 0x4d,
0x3a, 0x33, 0x6e, 0x98, 0x8c, 0xa5, 0x16, 0x75, 0x49, 0x54, 0xc2, 0xb4, 0x56, 0xc2, 0xa1, 0x56,
0x2c, 0x3b, 0x93, 0x5d, 0xd5, 0x5b, 0x19, 0x70, 0xb5, 0x51, 0x4c, 0x0e, 0x32, 0x70, 0x27, 0x05,
0x34, 0x67, 0x8e, 0x96, 0xed, 0xd6, 0xfa, 0xe1, 0xe8, 0x55, 0xb7, 0xb7, 0xfa, 0x04, 0xee, 0xb6,
0xdb, 0x5b, 0x77, 0xa2, 0xe1, 0x16, 0x78, 0x5b, 0x80, 0x59, 0x23, 0x62, 0x6f, 0x68, 0xb7, 0xe5,
0x87, 0x76, 0xf0, 0xdd, 0x7e, 0xac, 0xe2, 0x11, 0xb1, 0x7b, 0x3c, 0xe9, 0x96, 0xc4, 0xa8, 0x93,
0x68, 0xb9, 0x5c, 0x39, 0x58, 0x38, 0x9f, 0x17, 0x98, 0xa9, 0x40, 0x50, 0x2d, 0x30, 0x61, 0xc2,
0xc5, 0x7d, 0x66, 0x74, 0xad, 0x64, 0xb0, 0x37, 0x1e, 0x8f, 0xc3, 0x1c, 0x30, 0xcb, 0x5d, 0x70,
0x30, 0x1a, 0x55, 0x8b, 0xb0, 0xe4, 0x26, 0x43, 0x15, 0x8c, 0xd6, 0x8d, 0xf8, 0x2b, 0xdf, 0xcf,
0x83, 0x0f, 0xb3, 0x7c, 0x9d, 0xd4, 0xce, 0x69, 0xb5, 0x7a, 0xfa, 0xe5, 0xe1, 0xe1, 0x61, 0x28,
0x74, 0xa1, 0x4d, 0xb0, 0x97, 0xa6, 0x69, 0x98, 0x92, 0xf2, 0x7e, 0xca, 0x4b, 0x2c, 0x96, 0xc1,
0x2d, 0x18, 0xc9, 0x15, 0xef, 0x7f, 0x86, 0x62, 0x06, 0x0e, 0x05, 0xef, 0x5b, 0xae, 0xac, 0x4f,
0xfa, 0x62, 0x1a, 0x4a, 0xb4, 0x55, 0xc1, 0x97, 0x41, 0x52, 0x68, 0x71, 0x1f, 0x26, 0xda, 0x48,
0x30, 0xc1, 0x41, 0xb5, 0x60, 0x56, 0x17, 0x28, 0x59, 0x8b, 0xbb, 0xc9, 0xfa, 0x86, 0x4b, 0xac,
0x6d, 0x40, 0x82, 0x76, 0x89, 0x48, 0x6f, 0x53, 0xc3, 0xe2, 0x0f, 0x08, 0xde, 0xcd, 0x4a, 0x54,
0xbf, 0x88, 0x3f, 0xec, 0xb7, 0x3e, 0x07, 0xe4, 0xa0, 0xe8, 0x52, 0x33, 0xaf, 0x99, 0xcf, 0xde,
0x52, 0x4b, 0xbd, 0x6d, 0x4f, 0xbe, 0xd3, 0x55, 0x30, 0x9e, 0xe5, 0xa1, 0xa8, 0x8d, 0x25, 0xda,
0x95, 0xc6, 0x46, 0x8e, 0x56, 0xe1, 0x56, 0xaf, 0x68, 0xb8, 0x19, 0xb0, 0x46, 0x36, 0xa6, 0x55,
0xa1, 0xb9, 0x8c, 0x3d, 0xb2, 0x82, 0x94, 0xdd, 0xf4, 0xcf, 0xdc, 0xb2, 0xa2, 0x79, 0xb0, 0x75,
0x52, 0xa2, 0xf3, 0x18, 0xd2, 0x76, 0x42, 0x53, 0xa2, 0x95, 0x28, 0x50, 0xdc, 0xc7, 0xfb, 0x73,
0x54, 0x52, 0xcf, 0x07, 0xd4, 0x18, 0x6f, 0x6c, 0x24, 0x97, 0xbc, 0xfd, 0xc9, 0x11, 0x69, 0x16,
0x0d, 0x37, 0x00, 0x13, 0xf6, 0x3c, 0xd2, 0x5f, 0x20, 0x06, 0x8f, 0x4e, 0xcf, 0x31, 0x45, 0x02,
0xbc, 0xc3, 0x53, 0x6c, 0xa6, 0xaf, 0xae, 0xfe, 0x84, 0xed, 0xfc, 0x8e, 0xfb, 0x22, 0xd8, 0x02,
0xa4, 0x25, 0xd8, 0x66, 0xa6, 0x2f, 0x0d, 0xa4, 0x60, 0x40, 0x09, 0xb0, 0x9d, 0x7f, 0x71, 0x7e,
0x11, 0xf6, 0x78, 0x4a, 0xc8, 0x9d, 0xf1, 0x94, 0x1d, 0x6b, 0x95, 0x62, 0x56, 0x9b, 0xf6, 0xc8,
0x6e, 0xe4, 0xa8, 0x1b, 0x31, 0x6e, 0x68, 0xa8, 0xd8, 0x59, 0xe3, 0x63, 0xca, 0x05, 0x3c, 0x23,
0x48, 0x63, 0x92, 0x2c, 0x17, 0x89, 0x53, 0x1e, 0x6b, 0x6d, 0xa6, 0x70, 0x3b, 0x81, 0x4a, 0x2b,
0xf0, 0xfe, 0xab, 0x34, 0x01, 0x52, 0xed, 0xe9, 0xf9, 0x57, 0x76, 0x51, 0xbb, 0xaa, 0x76, 0xbb,
0x31, 0xc2, 0x2e, 0x95, 0x20, 0xd8, 0x2b, 0x5a, 0x1e, 0x5b, 0xda, 0x91, 0x11, 0x0e, 0x4b, 0x68,
0xac, 0xb8, 0xa6, 0x95, 0xbd, 0x61, 0xe7, 0x5c, 0x18, 0x6d, 0x77, 0xe4, 0x43, 0xb9, 0xf5, 0xa1,
0xd4, 0xd2, 0xee, 0x48, 0x09, 0x68, 0x85, 0x00, 0xba, 0xa9, 0xe8, 0x96, 0xc4, 0xf7, 0xa6, 0x92,
0xdc, 0x3d, 0xd5, 0x82, 0x5e, 0xe8, 0x96, 0x36, 0x57, 0xb6, 0xf9, 0x53, 0xfc, 0x04, 0xe7, 0xc3,
0x64, 0x44, 0x39, 0x06, 0x00, 0x00
};
@ -1548,6 +1550,103 @@ const uint8_t PAGE_settings_um[] PROGMEM = {
};
// Autogenerated from wled00/data/settings_2D.htm, do not edit!!
const uint16_t PAGE_settings_2D_length = 1453;
const uint8_t PAGE_settings_2D[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x8d, 0x57, 0x6d, 0x6f, 0xdb, 0x36,
0x10, 0xfe, 0xae, 0x5f, 0xc1, 0x12, 0x05, 0x6a, 0xb5, 0xb6, 0x15, 0x67, 0x5b, 0x51, 0xc4, 0x92,
0xb2, 0x7a, 0x09, 0xe6, 0x02, 0x0d, 0x1a, 0xd4, 0x5d, 0x8a, 0x61, 0x1d, 0x5a, 0x59, 0x3a, 0x47,
0x6c, 0x28, 0x52, 0x20, 0x29, 0x3b, 0x99, 0x9b, 0xff, 0xbe, 0x23, 0x25, 0x5b, 0xb6, 0x63, 0xa7,
0xf9, 0x12, 0x99, 0x2f, 0xf7, 0x7e, 0xf7, 0x3c, 0x4c, 0xf8, 0xec, 0xec, 0xc3, 0x1f, 0x9f, 0xfe,
0xbe, 0x3c, 0x27, 0xb9, 0x29, 0x78, 0x1c, 0xda, 0xbf, 0x84, 0x27, 0xe2, 0x3a, 0xa2, 0x20, 0x28,
0xae, 0x21, 0xc9, 0xe2, 0xb0, 0x00, 0x93, 0x90, 0x34, 0x4f, 0x94, 0x06, 0x13, 0xd1, 0xca, 0xcc,
0x7a, 0x6f, 0x68, 0xb3, 0xeb, 0x89, 0xa4, 0x80, 0x88, 0xce, 0x19, 0x2c, 0x4a, 0xa9, 0x0c, 0x25,
0xa9, 0x14, 0x06, 0x04, 0x5e, 0x5b, 0xb0, 0xcc, 0xe4, 0xd1, 0x6f, 0x47, 0x47, 0xeb, 0xab, 0x3b,
0x47, 0x19, 0xcc, 0x59, 0x0a, 0x3d, 0xb7, 0xe8, 0x32, 0xc1, 0x0c, 0x4b, 0x78, 0x4f, 0xa7, 0x09,
0x87, 0x68, 0xd0, 0x2d, 0x92, 0x5b, 0x56, 0x54, 0xc5, 0x7a, 0x5d, 0x69, 0x50, 0x6e, 0x91, 0x4c,
0x71, 0x2d, 0x24, 0x7d, 0x60, 0x39, 0x0e, 0x0d, 0x33, 0x1c, 0xe2, 0xe3, 0x33, 0x32, 0x01, 0xd3,
0xab, 0xca, 0x30, 0xa8, 0x37, 0x42, 0x9d, 0x2a, 0x56, 0x9a, 0xd8, 0x9b, 0x27, 0x8a, 0x64, 0x51,
0x26, 0xd3, 0xaa, 0x40, 0x37, 0x86, 0xb3, 0x4a, 0xa4, 0x86, 0x49, 0x41, 0xc6, 0x1d, 0x7f, 0xb9,
0x60, 0x22, 0x93, 0x8b, 0xbe, 0x2c, 0x41, 0x74, 0x68, 0x6e, 0x4c, 0xa9, 0x4f, 0x82, 0xe0, 0x46,
0xc8, 0xfe, 0x82, 0x43, 0xd6, 0xbf, 0x86, 0x60, 0x06, 0x89, 0xa9, 0x14, 0xe8, 0xe0, 0xf8, 0x8c,
0xfa, 0xf7, 0x6b, 0xd9, 0xd1, 0xae, 0x6c, 0x80, 0x39, 0x32, 0x4c, 0x5c, 0x6b, 0xda, 0xa5, 0x5f,
0x35, 0xf0, 0xd9, 0xe6, 0xed, 0xeb, 0x77, 0x59, 0x07, 0xfc, 0xa5, 0x02, 0x54, 0x25, 0x88, 0xd5,
0x6b, 0xce, 0x39, 0x58, 0x6f, 0x46, 0x77, 0xee, 0xe8, 0xde, 0xfa, 0x88, 0xb1, 0x5f, 0x26, 0x02,
0xb8, 0x8e, 0x5e, 0xff, 0xda, 0x7a, 0xf9, 0xd7, 0xbb, 0x0e, 0x44, 0xcf, 0x06, 0xfe, 0x92, 0xcd,
0x3a, 0xf4, 0x88, 0x46, 0x51, 0x64, 0xb5, 0x51, 0x2d, 0x8b, 0x92, 0xfa, 0xfd, 0x79, 0xc2, 0x2b,
0xf0, 0x1b, 0xc5, 0xee, 0xa0, 0x28, 0x33, 0x36, 0xc7, 0x13, 0x6d, 0xee, 0x38, 0xf4, 0x33, 0xa6,
0x4b, 0x9e, 0xdc, 0x45, 0x54, 0x48, 0x01, 0xb4, 0x3b, 0x97, 0x2c, 0x23, 0x18, 0x0d, 0x98, 0xda,
0x52, 0xc7, 0x1f, 0x3e, 0x26, 0x34, 0xe5, 0x32, 0xbd, 0xa1, 0xdd, 0xd6, 0xb1, 0xd2, 0xf6, 0xc2,
0x3b, 0x61, 0x3a, 0x59, 0x7f, 0x32, 0xeb, 0x5f, 0x5c, 0x8e, 0x1b, 0x07, 0x5e, 0xee, 0x1e, 0x5c,
0x35, 0x07, 0x43, 0x0e, 0x86, 0x88, 0xda, 0xe5, 0xd2, 0xe9, 0x40, 0x2b, 0x69, 0xce, 0x78, 0xa6,
0x40, 0xf4, 0x39, 0x88, 0x6b, 0x93, 0x0f, 0x31, 0x32, 0x11, 0xae, 0x8d, 0xf8, 0x33, 0xa9, 0x3a,
0x56, 0x0c, 0xeb, 0x3d, 0x84, 0x76, 0x7f, 0x08, 0xaf, 0x5e, 0xf9, 0x49, 0x96, 0xb9, 0x15, 0xe6,
0xcc, 0x89, 0xc5, 0x07, 0xc4, 0xe2, 0x0d, 0xb1, 0x5e, 0x0f, 0x13, 0x54, 0xd4, 0x62, 0x1b, 0x45,
0x59, 0xa9, 0xc2, 0x24, 0x2c, 0x6b, 0xb9, 0x43, 0xd1, 0x75, 0x45, 0x74, 0x30, 0xbe, 0x95, 0x55,
0x13, 0x1d, 0x0d, 0x4d, 0x08, 0x2f, 0xc5, 0xd0, 0x6c, 0xba, 0x69, 0xf6, 0x18, 0xc4, 0x7a, 0x1e,
0xd5, 0x26, 0x77, 0x12, 0xe3, 0x22, 0xda, 0xcd, 0x4e, 0x1c, 0xb5, 0x21, 0xd6, 0x75, 0x1e, 0xd6,
0xf6, 0xbe, 0x85, 0x58, 0x33, 0xc2, 0xb2, 0x88, 0x96, 0x82, 0x3f, 0x5f, 0xc2, 0x3d, 0x8d, 0x9f,
0x2f, 0x8f, 0xb0, 0x3f, 0xe0, 0x94, 0xd2, 0x93, 0x17, 0x61, 0xae, 0x88, 0x2b, 0x67, 0x33, 0x77,
0x27, 0xc7, 0xaf, 0x8f, 0xca, 0x5b, 0x1a, 0xbf, 0xb8, 0x77, 0xca, 0x88, 0x95, 0x08, 0xa7, 0x2a,
0x1e, 0x84, 0xba, 0x2a, 0x63, 0x6d, 0xc2, 0xc0, 0x7e, 0xc9, 0xfb, 0xf3, 0xb3, 0x13, 0x12, 0x62,
0x03, 0x43, 0x8a, 0xfe, 0xb9, 0x51, 0xbb, 0xb4, 0x57, 0x47, 0x34, 0xfe, 0x22, 0xbe, 0x98, 0x50,
0x96, 0x2e, 0x16, 0x17, 0x7d, 0x84, 0xfd, 0x18, 0x7f, 0x92, 0x38, 0x71, 0xf5, 0xee, 0xbe, 0x1b,
0x03, 0x1a, 0x8f, 0xa4, 0x31, 0xb2, 0xd8, 0xb8, 0x84, 0xa6, 0x9c, 0xfe, 0x78, 0x8f, 0x9d, 0x8f,
0x87, 0xec, 0xbc, 0x87, 0x99, 0xf9, 0x89, 0xa1, 0x8f, 0xec, 0x3a, 0x37, 0x7b, 0xed, 0x60, 0x9c,
0x5f, 0xc4, 0x07, 0xc5, 0x70, 0xe2, 0x12, 0x7b, 0xb8, 0x37, 0xc4, 0xab, 0x43, 0xa6, 0xc7, 0x52,
0xb1, 0xff, 0x10, 0xc4, 0x12, 0xfe, 0x13, 0x07, 0xae, 0x40, 0x19, 0x96, 0x6e, 0x5d, 0xdb, 0xf1,
0x61, 0x02, 0x0a, 0xe1, 0x02, 0x81, 0x02, 0xd0, 0x05, 0x26, 0xca, 0x0a, 0x2b, 0x79, 0x57, 0xa2,
0x74, 0x9a, 0x43, 0x7a, 0x33, 0x95, 0xb7, 0x74, 0xd3, 0xa3, 0x09, 0xa2, 0x5b, 0x80, 0x55, 0x8e,
0xbf, 0x0d, 0x45, 0x9f, 0x09, 0xc4, 0x42, 0xf3, 0x36, 0xfb, 0x9e, 0xa4, 0xa8, 0x61, 0xfc, 0xe9,
0xe2, 0x7d, 0x87, 0x4e, 0x01, 0xfb, 0x0f, 0x40, 0x64, 0xb4, 0xbb, 0xd9, 0x69, 0x6d, 0xbb, 0x37,
0x9d, 0xbd, 0x7f, 0xfe, 0x86, 0x16, 0x75, 0x44, 0x04, 0xab, 0x39, 0x14, 0x61, 0x34, 0xf8, 0xf1,
0x03, 0xfe, 0x11, 0xbd, 0xc1, 0xbf, 0x7d, 0xd4, 0x21, 0xe7, 0xd0, 0xd9, 0xd2, 0xba, 0x81, 0x1b,
0xcb, 0xed, 0x19, 0x41, 0xa8, 0xde, 0x9e, 0x8c, 0x68, 0xb0, 0x1e, 0x0d, 0x20, 0x72, 0x46, 0xf6,
0x7b, 0xe0, 0xc3, 0x1e, 0x3b, 0x53, 0x23, 0x56, 0x33, 0xbe, 0xac, 0xc5, 0x04, 0xff, 0x8a, 0xc3,
0xf3, 0x00, 0x9f, 0x36, 0xc0, 0xe1, 0x94, 0x32, 0xc1, 0x31, 0xab, 0xf4, 0xa4, 0x41, 0xba, 0xb5,
0x20, 0xea, 0x7f, 0x28, 0x18, 0x0f, 0x76, 0x05, 0x5a, 0xf3, 0x13, 0x0c, 0xee, 0x4f, 0x30, 0x57,
0x1d, 0xbf, 0x8b, 0xd8, 0xeb, 0xdf, 0x7b, 0x58, 0xc2, 0x9a, 0x49, 0x1a, 0x46, 0x21, 0x5a, 0xa5,
0x51, 0x0b, 0xf8, 0x81, 0xee, 0x7f, 0xd7, 0xa7, 0x65, 0x34, 0xb0, 0x8c, 0xd7, 0x5e, 0xb5, 0x06,
0x63, 0xef, 0x77, 0x56, 0x58, 0x96, 0x22, 0x95, 0xe2, 0x88, 0xd9, 0xce, 0x89, 0x54, 0xdb, 0x51,
0xc7, 0x9b, 0xee, 0x46, 0x18, 0xd4, 0x5c, 0x3b, 0x95, 0xd9, 0x1d, 0x91, 0x82, 0xcb, 0x04, 0x07,
0x1a, 0x5d, 0x40, 0x5d, 0x98, 0xbf, 0xc2, 0xcd, 0xb7, 0xfd, 0xf1, 0x55, 0xaf, 0xa9, 0x6f, 0x32,
0xa3, 0x04, 0x99, 0x35, 0x97, 0x76, 0xf4, 0xa5, 0xb6, 0x14, 0x68, 0x91, 0x20, 0xe5, 0x89, 0xd6,
0x11, 0x35, 0xb2, 0x54, 0x72, 0xb1, 0xbd, 0x97, 0x03, 0x2f, 0x71, 0x80, 0xc3, 0x69, 0x85, 0x93,
0x28, 0x88, 0x57, 0xb7, 0x5c, 0xbd, 0xa2, 0x68, 0x35, 0xe5, 0x2c, 0xbd, 0x89, 0xe8, 0xd8, 0x9a,
0x3d, 0x0d, 0x83, 0xfa, 0xa0, 0x69, 0xbd, 0x95, 0xd0, 0x01, 0x99, 0x91, 0x95, 0xf1, 0x46, 0x49,
0x7a, 0xd3, 0xca, 0x6d, 0x49, 0xe8, 0x6a, 0x5a, 0x30, 0xf4, 0x71, 0x92, 0xcc, 0xa1, 0xbd, 0x92,
0xab, 0x95, 0xfa, 0xfc, 0xd8, 0x52, 0x37, 0x26, 0xd3, 0x32, 0x37, 0x2e, 0xbc, 0x89, 0xc1, 0x0c,
0x12, 0xa9, 0x88, 0xeb, 0x96, 0x76, 0x52, 0x6d, 0x26, 0x1c, 0xeb, 0x35, 0x13, 0x32, 0xf9, 0x70,
0x71, 0x89, 0x39, 0x41, 0x4f, 0x72, 0x7c, 0xb8, 0xe0, 0xc6, 0x56, 0x7f, 0x76, 0x37, 0xe0, 0xdd,
0xd5, 0x11, 0xc3, 0x7f, 0x30, 0xd8, 0x03, 0x7c, 0x33, 0x58, 0x6b, 0xeb, 0x79, 0xf5, 0x1e, 0xce,
0x34, 0x3a, 0x77, 0x91, 0xe0, 0xa5, 0xdb, 0xf5, 0xa5, 0xad, 0x91, 0x5e, 0x83, 0x70, 0x4d, 0xa1,
0xc4, 0x6b, 0x30, 0xb7, 0x69, 0xb5, 0x13, 0xd7, 0x5c, 0x18, 0xe5, 0x2f, 0x9b, 0x8a, 0x70, 0x55,
0x83, 0x70, 0xc6, 0xf0, 0x11, 0xa0, 0x51, 0xa9, 0x26, 0x9d, 0xcf, 0xb7, 0x63, 0x7f, 0x0d, 0x0a,
0x0d, 0x08, 0x7c, 0xa6, 0xab, 0x62, 0x89, 0xaa, 0x98, 0x82, 0xc2, 0xc2, 0x33, 0x61, 0xdd, 0xb2,
0x8f, 0x05, 0xfc, 0x1e, 0xbf, 0xa1, 0x2b, 0x57, 0xf1, 0x69, 0x46, 0x6e, 0x77, 0xc4, 0xc7, 0x94,
0x6c, 0x4b, 0x7b, 0x8f, 0x89, 0xdb, 0x70, 0x5a, 0xa8, 0xab, 0xd3, 0xaf, 0x77, 0x3c, 0xba, 0xb0,
0x3a, 0x1f, 0x73, 0xa9, 0xd5, 0x38, 0xb0, 0x5d, 0xe2, 0x64, 0x23, 0x5a, 0x17, 0x80, 0xac, 0xf0,
0x71, 0x57, 0xb7, 0xb7, 0x52, 0x7e, 0xb5, 0xeb, 0xf0, 0x93, 0x75, 0x6f, 0x52, 0x99, 0xd7, 0x70,
0xd9, 0x4e, 0x03, 0x35, 0x49, 0x19, 0xed, 0xeb, 0x84, 0x4d, 0x16, 0x7b, 0xd8, 0x03, 0xde, 0x0e,
0x85, 0x1d, 0x22, 0xb0, 0x8f, 0xfb, 0x54, 0x6f, 0x11, 0x57, 0x73, 0xec, 0x1d, 0x64, 0xad, 0xad,
0xe6, 0x7a, 0x8c, 0xb1, 0xae, 0x5a, 0x5b, 0xde, 0xa3, 0x54, 0xf5, 0x04, 0x9a, 0xda, 0x32, 0xea,
0x3d, 0x9d, 0xa3, 0x2c, 0x3f, 0x1d, 0x78, 0x66, 0x84, 0x2c, 0xf6, 0xde, 0x62, 0xd9, 0x6c, 0xc3,
0x13, 0xa6, 0xf1, 0x57, 0xe6, 0x78, 0x60, 0x60, 0x27, 0xbb, 0x40, 0xd2, 0x22, 0x65, 0x7e, 0xa7,
0x5d, 0x33, 0xe0, 0x4b, 0xbb, 0x69, 0x08, 0x7b, 0xc1, 0xe4, 0x40, 0x34, 0xaa, 0xdf, 0x98, 0x8d,
0xbe, 0x73, 0xab, 0x9e, 0x66, 0xa2, 0x73, 0x59, 0xf1, 0x8c, 0x4c, 0x81, 0x24, 0x4a, 0xd9, 0xc1,
0xcf, 0xc8, 0x4c, 0xc9, 0x82, 0x20, 0xee, 0xf5, 0x38, 0xa6, 0x1a, 0x7f, 0x90, 0xa9, 0x2b, 0x57,
0x4f, 0xd9, 0xcc, 0xa2, 0xbd, 0x0c, 0x54, 0x17, 0x9d, 0x4c, 0x94, 0xc5, 0x6b, 0xb2, 0x60, 0x26,
0x27, 0x5c, 0x2e, 0xa0, 0x41, 0x18, 0x52, 0xf7, 0x1a, 0xb1, 0x88, 0x85, 0xb6, 0x9d, 0x8e, 0x0e,
0x3a, 0x89, 0x0a, 0x09, 0x43, 0x7f, 0xd0, 0x88, 0x46, 0x98, 0x85, 0xcc, 0xef, 0x7b, 0xce, 0x91,
0xf3, 0x24, 0xcd, 0x1b, 0xd1, 0x34, 0x11, 0x24, 0x47, 0x60, 0x43, 0x67, 0x67, 0x33, 0x40, 0x42,
0xb3, 0xe6, 0xd6, 0x35, 0x23, 0x89, 0xc8, 0x02, 0xa9, 0x5a, 0xd3, 0xa5, 0x64, 0x78, 0xa3, 0xd9,
0x45, 0x74, 0x90, 0x95, 0x41, 0x9d, 0x01, 0xab, 0x91, 0xe4, 0x50, 0x26, 0x6b, 0xec, 0xa8, 0x01,
0xa3, 0x16, 0x72, 0x08, 0xd2, 0x3e, 0xff, 0x6a, 0x56, 0x6d, 0x00, 0xd5, 0x5b, 0xe1, 0xaa, 0x7a,
0x12, 0x74, 0xef, 0x45, 0x6e, 0xef, 0x31, 0xe8, 0x0e, 0x2c, 0x1d, 0xe1, 0xc7, 0x32, 0x96, 0xa5,
0x2f, 0xfb, 0x0f, 0xe4, 0xff, 0x77, 0x44, 0x75, 0x1f, 0x50, 0x0e, 0x00, 0x00
};
// Autogenerated from wled00/data/settings_pin.htm, do not edit!!
const uint16_t PAGE_settings_pin_length = 471;
const uint8_t PAGE_settings_pin[] PROGMEM = {

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,9 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t len = elem["len"];
stop = (len > 0) ? start + len : seg.stop;
}
// 2D segments
uint16_t startY = elem["startY"] | seg.startY;
uint16_t stopY = elem["stopY"] | seg.stopY;
//repeat, multiplies segment until all LEDs are used, or max segments reached
bool repeat = elem["rpt"] | false;
@ -31,6 +34,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
for (byte i=id+1; i<strip.getMaxSegments(); i++) {
start = start + len;
if (start >= strip.getLengthTotal()) break;
//TODO: add support for 2D
elem["start"] = start;
elem["stop"] = start + len;
elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments
@ -78,7 +82,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs;
}
if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of);
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
byte segbri = 0;
if (getVal(elem["bri"], &segbri)) {
@ -146,6 +150,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
// 2D options
seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y));
seg.setOption(SEG_OPTION_MIRROR_Y , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y ));
seg.setOption(SEG_OPTION_TRANSPOSE , elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSE ));
byte fx = seg.mode;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
@ -189,7 +197,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
set = 2;
}
} else { //color
int rgbw[] = {0,0,0,0};
uint8_t rgbw[] = {0,0,0,0};
JsonArray icol = iarr[i];
if (!icol.isNull()) { //array, e.g. [255,0,0]
byte sz = icol.size();
@ -386,6 +394,10 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
if (segmentBounds) {
root["start"] = seg.start;
root["stop"] = seg.stop;
if (strip.isMatrix) {
root[F("startY")] = seg.startY;
root[F("stopY")] = seg.stopY;
}
}
if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping;
@ -427,6 +439,11 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
if (strip.isMatrix) {
root[F("rY")] = seg.getOption(SEG_OPTION_REVERSED_Y);
root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y);
root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSE);
}
}
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
@ -494,7 +511,13 @@ void serializeInfo(JsonObject root)
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
if (strip.isMatrix) {
JsonObject matrix = leds.createNestedObject("matrix");
matrix["w"] = strip.matrixWidth;
matrix["h"] = strip.matrixHeight;
}
uint8_t totalLC = 0;
JsonArray lcarr = leds.createNestedArray(F("seglc"));
uint8_t nSegs = strip.getLastActiveSegmentId();

View File

@ -15,8 +15,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
return;
}
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods
if (subPage <1 || subPage >8 || !correctPIN) return;
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods 9: N/A 10: 2D
if (subPage <1 || subPage >10 || !correctPIN) return;
//WIFI SETTINGS
if (subPage == 1)
@ -547,6 +547,30 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
releaseJSONBufferLock();
}
//2D panels
if (subPage == 10)
{
strip.isMatrix = request->arg(F("SOMP")).toInt();
strip.panelH = MAX(1,MIN(128,request->arg(F("PH")).toInt()));
strip.panelW = MAX(1,MIN(128,request->arg(F("PW")).toInt()));
strip.hPanels = MAX(1,MIN(8,request->arg(F("MPH")).toInt()));
strip.vPanels = MAX(1,MIN(8,request->arg(F("MPV")).toInt()));
strip.matrix.bottomStart = request->arg(F("PB")).toInt();
strip.matrix.rightStart = request->arg(F("PR")).toInt();
strip.matrix.vertical = request->arg(F("PV")).toInt();
strip.matrix.serpentine = request->hasArg(F("PS"));
for (uint8_t i=0; i<WLED_MAX_PANELS; i++) {
char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
uint8_t l = strlen(pO); pO[l+1] = 0;
pO[l] = 'B'; if (!request->hasArg(pO)) break;
pO[l] = 'B'; strip.panel[i].bottomStart = request->arg(pO).toInt();
pO[l] = 'R'; strip.panel[i].rightStart = request->arg(pO).toInt();
pO[l] = 'V'; strip.panel[i].vertical = request->arg(pO).toInt();
pO[l] = 'S'; strip.panel[i].serpentine = request->hasArg(pO);
}
strip.setUpMatrix(); // will check limits
}
lastEditTime = millis();
if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
if (subPage == 4) alexaInit();

View File

@ -476,7 +476,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
char buf[SETTINGS_STACK_BUF_SIZE+37];
buf[0] = 0;
byte subPage = request->arg(F("p")).toInt();
if (subPage > 9) {
if (subPage > 10) {
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
request->send(501, "application/javascript", buf);
return;
@ -510,6 +510,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
else if (url.indexOf("sec") > 0) subPage = 6;
else if (url.indexOf("dmx") > 0) subPage = 7;
else if (url.indexOf("um") > 0) subPage = 8;
else if (url.indexOf("2D") > 0) subPage = 10;
else if (url.indexOf("lock") > 0) subPage = 251;
}
else if (url.indexOf("/update") >= 0) subPage = 9; // update page, for PIN check
@ -542,6 +543,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
case 6: strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
case 7: strcpy_P(s, PSTR("DMX")); break;
case 8: strcpy_P(s, PSTR("Usermods")); break;
case 10: strcpy_P(s, PSTR("2D")); break;
case 252: strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break;
}
@ -572,6 +574,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
case 7: response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break;
case 8: response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break;
case 9: response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break;
case 10: response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break;
case 251: {
correctPIN = !strlen(settingsPIN); // lock if a pin is set
createEditHandler(correctPIN);

View File

@ -177,7 +177,7 @@ void getSettingsJS(byte subPage, char* dest)
obuf = dest;
olen = 0;
if (subPage <0 || subPage >9) return;
if (subPage <0 || subPage >10) return;
if (subPage == 0)
{
@ -631,4 +631,33 @@ void getSettingsJS(byte subPage, char* dest)
oappendi(VERSION);
oappend(SET_F(")\";"));
}
if (subPage == 10) // 2D matrices
{
sappend('v',SET_F("SOMP"),strip.isMatrix);
oappend(SET_F("resetPanels();"));
if (strip.isMatrix) {
sappend('v',SET_F("PH"),strip.panelH);
sappend('v',SET_F("PW"),strip.panelW);
sappend('v',SET_F("MPH"),strip.hPanels);
sappend('v',SET_F("MPV"),strip.vPanels);
sappend('v',SET_F("PB"),strip.matrix.bottomStart);
sappend('v',SET_F("PR"),strip.matrix.rightStart);
sappend('v',SET_F("PV"),strip.matrix.vertical);
sappend('c',SET_F("PS"),strip.matrix.serpentine);
// panels
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
char n[5];
oappend(SET_F("addPanel("));
oappend(itoa(i,n,10));
oappend(SET_F(");"));
char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
uint8_t l = strlen(pO); pO[l+1] = 0;
pO[l] = 'B'; sappend('v',pO,strip.panel[i].bottomStart);
pO[l] = 'R'; sappend('v',pO,strip.panel[i].rightStart);
pO[l] = 'V'; sappend('v',pO,strip.panel[i].vertical);
pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine);
}
}
}
}