commit
9249c74b7b
@ -375,6 +375,7 @@ public:
|
|||||||
* onStateChanged() is used to detect WLED state change
|
* onStateChanged() is used to detect WLED state change
|
||||||
*/
|
*/
|
||||||
void onStateChange(uint8_t mode) {
|
void onStateChange(uint8_t mode) {
|
||||||
|
if (!initDone) return;
|
||||||
DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart);
|
DEBUG_PRINT(F("PIR: offTimerStart=")); DEBUG_PRINTLN(offTimerStart);
|
||||||
if (PIRtriggered && offTimerStart) {
|
if (PIRtriggered && offTimerStart) {
|
||||||
// checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger
|
// checking PIRtriggered and offTimerStart will prevent cancellation upon On trigger
|
||||||
|
14
wled00/FX.h
14
wled00/FX.h
@ -460,7 +460,7 @@ typedef struct Segment {
|
|||||||
_dataLen(0),
|
_dataLen(0),
|
||||||
_t(nullptr)
|
_t(nullptr)
|
||||||
{
|
{
|
||||||
refreshLightCapabilities();
|
//refreshLightCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
|
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
|
||||||
@ -659,7 +659,6 @@ class WS2812FX { // 96 bytes
|
|||||||
isMatrix(false),
|
isMatrix(false),
|
||||||
#ifndef WLED_DISABLE_2D
|
#ifndef WLED_DISABLE_2D
|
||||||
panels(1),
|
panels(1),
|
||||||
matrix{0,0,0,0},
|
|
||||||
#endif
|
#endif
|
||||||
// semi-private (just obscured) used in effect functions through macros
|
// semi-private (just obscured) used in effect functions through macros
|
||||||
_currentPalette(CRGBPalette16(CRGB::Black)),
|
_currentPalette(CRGBPalette16(CRGB::Black)),
|
||||||
@ -727,8 +726,7 @@ class WS2812FX { // 96 bytes
|
|||||||
fixInvalidSegments(),
|
fixInvalidSegments(),
|
||||||
setPixelColor(int n, uint32_t c),
|
setPixelColor(int n, uint32_t c),
|
||||||
show(void),
|
show(void),
|
||||||
setTargetFps(uint8_t fps),
|
setTargetFps(uint8_t fps);
|
||||||
deserializeMap(uint8_t n=0);
|
|
||||||
|
|
||||||
void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline)
|
void fill(uint32_t c) { for (int i = 0; i < _length; i++) setPixelColor(i, c); } // fill whole strip with color (inline)
|
||||||
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
|
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
|
||||||
@ -748,6 +746,7 @@ class WS2812FX { // 96 bytes
|
|||||||
hasCCTBus(void),
|
hasCCTBus(void),
|
||||||
// return true if the strip is being sent pixel updates
|
// return true if the strip is being sent pixel updates
|
||||||
isUpdating(void),
|
isUpdating(void),
|
||||||
|
deserializeMap(uint8_t n=0),
|
||||||
useLedsArray = false;
|
useLedsArray = false;
|
||||||
|
|
||||||
inline bool isServicing(void) { return _isServicing; }
|
inline bool isServicing(void) { return _isServicing; }
|
||||||
@ -813,13 +812,6 @@ class WS2812FX { // 96 bytes
|
|||||||
uint8_t
|
uint8_t
|
||||||
panels;
|
panels;
|
||||||
|
|
||||||
struct {
|
|
||||||
bool bottomStart : 1;
|
|
||||||
bool rightStart : 1;
|
|
||||||
bool vertical : 1;
|
|
||||||
bool serpentine : 1;
|
|
||||||
} matrix;
|
|
||||||
|
|
||||||
typedef struct panel_t {
|
typedef struct panel_t {
|
||||||
uint16_t xOffset; // x offset relative to the top left of matrix in LEDs
|
uint16_t xOffset; // x offset relative to the top left of matrix in LEDs
|
||||||
uint16_t yOffset; // y offset relative to the top left of matrix in LEDs
|
uint16_t yOffset; // y offset relative to the top left of matrix in LEDs
|
||||||
|
@ -78,21 +78,60 @@ void WS2812FX::setUpMatrix() {
|
|||||||
customMappingTable[i] = (uint16_t)-1;
|
customMappingTable[i] = (uint16_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we will try to load a "gap" array (a JSON file)
|
||||||
|
// the array has to have the same amount of values as mapping array (or larger)
|
||||||
|
// "gap" array is used while building ledmap (mapping array)
|
||||||
|
// and discarded afterwards as it has no meaning after the process
|
||||||
|
// content of the file is just raw JSON array in the form of [val1,val2,val3,...]
|
||||||
|
// there are no other "key":"value" pairs in it
|
||||||
|
// allowed values are: -1 (missing pixel/no LED attached), 0 (inactive/unused pixel), 1 (active/used pixel)
|
||||||
|
char fileName[32]; strcpy_P(fileName, PSTR("/2d-gaps.json")); // reduce flash footprint
|
||||||
|
bool isFile = WLED_FS.exists(fileName);
|
||||||
|
size_t gapSize = 0;
|
||||||
|
int8_t *gapTable = nullptr;
|
||||||
|
|
||||||
|
if (isFile && requestJSONBufferLock(20)) {
|
||||||
|
DEBUG_PRINT(F("Reading LED gap from "));
|
||||||
|
DEBUG_PRINTLN(fileName);
|
||||||
|
// read the array into global JSON buffer
|
||||||
|
if (readObjectFromFile(fileName, nullptr, &doc)) {
|
||||||
|
// the array is similar to ledmap, except it has only 3 values:
|
||||||
|
// -1 ... missing pixel (do not increase pixel count)
|
||||||
|
// 0 ... inactive pixel (it does count, but should be mapped out (-1))
|
||||||
|
// 1 ... active pixel (it will count and will be mapped)
|
||||||
|
JsonArray map = doc.as<JsonArray>();
|
||||||
|
gapSize = map.size();
|
||||||
|
if (!map.isNull() && gapSize >= customMappingSize) { // not an empty map
|
||||||
|
gapTable = new int8_t[gapSize];
|
||||||
|
if (gapTable) for (size_t i = 0; i < gapSize; i++) {
|
||||||
|
gapTable[i] = constrain(map[i], -1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_PRINTLN(F("Gaps loaded."));
|
||||||
|
releaseJSONBufferLock();
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t x, y, pix=0; //pixel
|
uint16_t x, y, pix=0; //pixel
|
||||||
for (size_t pan = 0; pan < panel.size(); pan++) {
|
for (size_t pan = 0; pan < panel.size(); pan++) {
|
||||||
Panel &p = panel[pan];
|
Panel &p = panel[pan];
|
||||||
uint16_t h = p.vertical ? p.height : p.width;
|
uint16_t h = p.vertical ? p.height : p.width;
|
||||||
uint16_t v = p.vertical ? p.width : p.height;
|
uint16_t v = p.vertical ? p.width : p.height;
|
||||||
for (size_t j = 0; j < v; j++){
|
for (size_t j = 0; j < v; j++){
|
||||||
for(size_t i = 0; i < h; i++, pix++) {
|
for(size_t i = 0; i < h; i++) {
|
||||||
y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j;
|
y = (p.vertical?p.rightStart:p.bottomStart) ? v-j-1 : j;
|
||||||
x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i;
|
x = (p.vertical?p.bottomStart:p.rightStart) ? h-i-1 : i;
|
||||||
x = p.serpentine && j%2 ? h-x-1 : x;
|
x = p.serpentine && j%2 ? h-x-1 : x;
|
||||||
customMappingTable[(p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x)] = pix;
|
size_t index = (p.yOffset + (p.vertical?x:y)) * Segment::maxWidth + p.xOffset + (p.vertical?y:x);
|
||||||
|
if (!gapTable || (gapTable && gapTable[index] > 0)) customMappingTable[index] = pix; // a useful pixel (otherwise -1 is retained)
|
||||||
|
if (!gapTable || (gapTable && gapTable[index] >= 0)) pix++; // not a missing pixel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete gap array as we no longer need it
|
||||||
|
if (gapTable) delete[] gapTable;
|
||||||
|
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
DEBUG_PRINT(F("Matrix ledmap:"));
|
DEBUG_PRINT(F("Matrix ledmap:"));
|
||||||
for (uint16_t i=0; i<customMappingSize; i++) {
|
for (uint16_t i=0; i<customMappingSize; i++) {
|
||||||
@ -108,9 +147,9 @@ void WS2812FX::setUpMatrix() {
|
|||||||
panel.clear();
|
panel.clear();
|
||||||
Segment::maxWidth = _length;
|
Segment::maxWidth = _length;
|
||||||
Segment::maxHeight = 1;
|
Segment::maxHeight = 1;
|
||||||
}
|
|
||||||
resetSegments();
|
resetSegments();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
isMatrix = false; // no matter what config says
|
isMatrix = false; // no matter what config says
|
||||||
#endif
|
#endif
|
||||||
|
@ -750,15 +750,33 @@ uint8_t Segment::differs(Segment& b) const {
|
|||||||
|
|
||||||
void Segment::refreshLightCapabilities() {
|
void Segment::refreshLightCapabilities() {
|
||||||
uint8_t capabilities = 0;
|
uint8_t capabilities = 0;
|
||||||
|
uint16_t segStartIdx = 0xFFFFU;
|
||||||
|
uint16_t segStopIdx = 0;
|
||||||
|
|
||||||
|
if (start < Segment::maxWidth * Segment::maxHeight) {
|
||||||
|
// we are withing 2D matrix (includes 1D segments)
|
||||||
|
for (int y = startY; y < stopY; y++) for (int x = start; x < stop; x++) {
|
||||||
|
uint16_t index = x + Segment::maxWidth * y;
|
||||||
|
if (index < strip.customMappingSize) index = strip.customMappingTable[index]; // convert logical address to physical
|
||||||
|
if (index < 0xFFFFU) {
|
||||||
|
if (segStartIdx > index) segStartIdx = index;
|
||||||
|
if (segStopIdx < index) segStopIdx = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are on the strip located after the matrix
|
||||||
|
segStartIdx = start;
|
||||||
|
segStopIdx = stop;
|
||||||
|
}
|
||||||
|
|
||||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||||
Bus *bus = busses.getBus(b);
|
Bus *bus = busses.getBus(b);
|
||||||
if (bus == nullptr || bus->getLength()==0) break;
|
if (bus == nullptr || bus->getLength()==0) break;
|
||||||
if (!bus->isOk()) continue;
|
if (!bus->isOk()) continue;
|
||||||
if (bus->getStart() >= stop) continue;
|
if (bus->getStart() >= segStopIdx) continue;
|
||||||
if (bus->getStart() + bus->getLength() <= start) continue;
|
if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
|
||||||
|
|
||||||
uint8_t type = bus->getType();
|
//uint8_t type = bus->getType();
|
||||||
if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
|
if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
|
||||||
if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
|
if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
|
||||||
if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
|
if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
|
||||||
@ -1012,7 +1030,8 @@ void WS2812FX::finalizeInit(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up
|
if (isMatrix) setUpMatrix();
|
||||||
|
else {
|
||||||
Segment::maxWidth = _length;
|
Segment::maxWidth = _length;
|
||||||
Segment::maxHeight = 1;
|
Segment::maxHeight = 1;
|
||||||
}
|
}
|
||||||
@ -1024,17 +1043,20 @@ void WS2812FX::finalizeInit(void)
|
|||||||
Segment::_globalLeds = nullptr;
|
Segment::_globalLeds = nullptr;
|
||||||
}
|
}
|
||||||
if (useLedsArray) {
|
if (useLedsArray) {
|
||||||
|
size_t arrSize = sizeof(CRGB) * MAX(_length, Segment::maxWidth*Segment::maxHeight);
|
||||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||||
if (psramFound())
|
if (psramFound())
|
||||||
Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length);
|
Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length);
|
Segment::_globalLeds = (CRGB*) malloc(arrSize);
|
||||||
memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length);
|
memset(Segment::_globalLeds, 0, arrSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
//segments are created in makeAutoSegments();
|
//segments are created in makeAutoSegments();
|
||||||
|
DEBUG_PRINTLN(F("Loading custom palettes"));
|
||||||
loadCustomPalettes(); // (re)load all custom palettes
|
loadCustomPalettes(); // (re)load all custom palettes
|
||||||
|
DEBUG_PRINTLN(F("Loading custom ledmaps"));
|
||||||
deserializeMap(); // (re)load default ledmap
|
deserializeMap(); // (re)load default ledmap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1409,9 +1431,9 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
|||||||
}
|
}
|
||||||
// do we have LEDs after the matrix? (ignore buses)
|
// do we have LEDs after the matrix? (ignore buses)
|
||||||
if (autoSegments && _length > Segment::maxWidth*Segment::maxHeight /*&& getActiveSegmentsNum() == 2*/) {
|
if (autoSegments && _length > Segment::maxWidth*Segment::maxHeight /*&& getActiveSegmentsNum() == 2*/) {
|
||||||
if (_segments.size() == getLastActiveSegmentId()+1)
|
if (_segments.size() == getLastActiveSegmentId()+1U) {
|
||||||
_segments.push_back(Segment(Segment::maxWidth*Segment::maxHeight, _length));
|
_segments.push_back(Segment(Segment::maxWidth*Segment::maxHeight, _length));
|
||||||
else {
|
} else {
|
||||||
size_t i = getLastActiveSegmentId() + 1;
|
size_t i = getLastActiveSegmentId() + 1;
|
||||||
_segments[i].start = Segment::maxWidth*Segment::maxHeight;
|
_segments[i].start = Segment::maxWidth*Segment::maxHeight;
|
||||||
_segments[i].stop = _length;
|
_segments[i].stop = _length;
|
||||||
@ -1468,14 +1490,30 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
|||||||
void WS2812FX::fixInvalidSegments() {
|
void WS2812FX::fixInvalidSegments() {
|
||||||
//make sure no segment is longer than total (sanity check)
|
//make sure no segment is longer than total (sanity check)
|
||||||
for (size_t i = getSegmentsNum()-1; i > 0; i--) {
|
for (size_t i = getSegmentsNum()-1; i > 0; i--) {
|
||||||
|
if (isMatrix) {
|
||||||
|
#ifndef WLED_DISABLE_2D
|
||||||
|
if (_segments[i].start >= Segment::maxWidth * Segment::maxHeight) {
|
||||||
|
// 1D segment at the end of matrix
|
||||||
|
if (_segments[i].start >= _length || _segments[i].startY > 0 || _segments[i].stopY > 1) { _segments.erase(_segments.begin()+i); continue; }
|
||||||
|
if (_segments[i].stop > _length) _segments[i].stop = _length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_segments[i].start >= Segment::maxWidth || _segments[i].startY >= Segment::maxHeight) { _segments.erase(_segments.begin()+i); continue; }
|
||||||
|
if (_segments[i].stop > Segment::maxWidth) _segments[i].stop = Segment::maxWidth;
|
||||||
|
if (_segments[i].stopY > Segment::maxHeight) _segments[i].stopY = Segment::maxHeight;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
|
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
|
||||||
if (_segments[i].stop > _length) _segments[i].stop = _length;
|
if (_segments[i].stop > _length) _segments[i].stop = _length;
|
||||||
// this is always called as the last step after finalizeInit(), update covered bus types
|
|
||||||
_segments[i].refreshLightCapabilities();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// this is always called as the last step after finalizeInit(), update covered bus types
|
||||||
|
for (segment &seg : _segments)
|
||||||
|
seg.refreshLightCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
//true if all segments align with a bus, or if a segment covers the total length
|
//true if all segments align with a bus, or if a segment covers the total length
|
||||||
|
//irrelevant in 2D set-up
|
||||||
bool WS2812FX::checkSegmentAlignment() {
|
bool WS2812FX::checkSegmentAlignment() {
|
||||||
bool aligned = false;
|
bool aligned = false;
|
||||||
for (segment &seg : _segments) {
|
for (segment &seg : _segments) {
|
||||||
@ -1575,8 +1613,8 @@ void WS2812FX::loadCustomPalettes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
||||||
void WS2812FX::deserializeMap(uint8_t n) {
|
bool WS2812FX::deserializeMap(uint8_t n) {
|
||||||
if (isMatrix) return; // 2D support creates its own ledmap
|
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
|
||||||
|
|
||||||
char fileName[32];
|
char fileName[32];
|
||||||
strcpy_P(fileName, PSTR("/ledmap"));
|
strcpy_P(fileName, PSTR("/ledmap"));
|
||||||
@ -1586,24 +1624,24 @@ void WS2812FX::deserializeMap(uint8_t n) {
|
|||||||
|
|
||||||
if (!isFile) {
|
if (!isFile) {
|
||||||
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
|
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
|
||||||
if (!n && customMappingTable != nullptr) {
|
if (!isMatrix && !n && customMappingTable != nullptr) {
|
||||||
customMappingSize = 0;
|
customMappingSize = 0;
|
||||||
delete[] customMappingTable;
|
delete[] customMappingTable;
|
||||||
customMappingTable = nullptr;
|
customMappingTable = nullptr;
|
||||||
}
|
}
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requestJSONBufferLock(7)) return;
|
if (!requestJSONBufferLock(7)) return false;
|
||||||
|
|
||||||
DEBUG_PRINT(F("Reading LED map from "));
|
|
||||||
DEBUG_PRINTLN(fileName);
|
|
||||||
|
|
||||||
if (!readObjectFromFile(fileName, nullptr, &doc)) {
|
if (!readObjectFromFile(fileName, nullptr, &doc)) {
|
||||||
releaseJSONBufferLock();
|
releaseJSONBufferLock();
|
||||||
return; //if file does not exist just exit
|
return false; //if file does not exist just exit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINT(F("Reading LED map from "));
|
||||||
|
DEBUG_PRINTLN(fileName);
|
||||||
|
|
||||||
// erase old custom ledmap
|
// erase old custom ledmap
|
||||||
if (customMappingTable != nullptr) {
|
if (customMappingTable != nullptr) {
|
||||||
customMappingSize = 0;
|
customMappingSize = 0;
|
||||||
@ -1621,6 +1659,7 @@ void WS2812FX::deserializeMap(uint8_t n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
releaseJSONBufferLock();
|
releaseJSONBufferLock();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,11 +98,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
if (!matrix.isNull()) {
|
if (!matrix.isNull()) {
|
||||||
strip.isMatrix = true;
|
strip.isMatrix = true;
|
||||||
CJSON(strip.panels, matrix[F("mpc")]);
|
CJSON(strip.panels, matrix[F("mpc")]);
|
||||||
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["ps"]);
|
|
||||||
|
|
||||||
strip.panel.clear();
|
strip.panel.clear();
|
||||||
JsonArray panels = matrix[F("panels")];
|
JsonArray panels = matrix[F("panels")];
|
||||||
uint8_t s = 0;
|
uint8_t s = 0;
|
||||||
@ -130,8 +125,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
p.options = 0;
|
p.options = 0;
|
||||||
strip.panel.push_back(p);
|
strip.panel.push_back(p);
|
||||||
}
|
}
|
||||||
|
// cannot call strip.setUpMatrix() here due to already locked JSON buffer
|
||||||
strip.setUpMatrix();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -333,12 +327,20 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
||||||
CJSON(autoSegments, light[F("aseg")]);
|
CJSON(autoSegments, light[F("aseg")]);
|
||||||
|
|
||||||
|
CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.8
|
||||||
float light_gc_bri = light["gc"]["bri"];
|
float light_gc_bri = light["gc"]["bri"];
|
||||||
float light_gc_col = light["gc"]["col"]; // 2.8
|
float light_gc_col = light["gc"]["col"];
|
||||||
if (light_gc_bri > 1.5) gammaCorrectBri = true;
|
if (light_gc_bri > 1.0f) gammaCorrectBri = true;
|
||||||
else if (light_gc_bri > 0.5) gammaCorrectBri = false;
|
else gammaCorrectBri = false;
|
||||||
if (light_gc_col > 1.5) gammaCorrectCol = true;
|
if (light_gc_col > 1.0f) gammaCorrectCol = true;
|
||||||
else if (light_gc_col > 0.5) gammaCorrectCol = false;
|
else gammaCorrectCol = false;
|
||||||
|
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3) {
|
||||||
|
if (gammaCorrectVal != 2.8f) calcGammaTable(gammaCorrectVal);
|
||||||
|
} else {
|
||||||
|
gammaCorrectVal = 1.0f; // no gamma correction
|
||||||
|
gammaCorrectBri = false;
|
||||||
|
gammaCorrectCol = false;
|
||||||
|
}
|
||||||
|
|
||||||
JsonObject light_tr = light["tr"];
|
JsonObject light_tr = light["tr"];
|
||||||
CJSON(fadeTransition, light_tr["mode"]);
|
CJSON(fadeTransition, light_tr["mode"]);
|
||||||
@ -708,11 +710,6 @@ void serializeConfig() {
|
|||||||
if (strip.isMatrix) {
|
if (strip.isMatrix) {
|
||||||
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
|
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
|
||||||
matrix[F("mpc")] = strip.panels;
|
matrix[F("mpc")] = strip.panels;
|
||||||
matrix[F("pb")] = strip.matrix.bottomStart;
|
|
||||||
matrix[F("pr")] = strip.matrix.rightStart;
|
|
||||||
matrix[F("pv")] = strip.matrix.vertical;
|
|
||||||
matrix["ps"] = strip.matrix.serpentine;
|
|
||||||
|
|
||||||
JsonArray panels = matrix.createNestedArray(F("panels"));
|
JsonArray panels = matrix.createNestedArray(F("panels"));
|
||||||
for (uint8_t i=0; i<strip.panel.size(); i++) {
|
for (uint8_t i=0; i<strip.panel.size(); i++) {
|
||||||
JsonObject pnl = panels.createNestedObject();
|
JsonObject pnl = panels.createNestedObject();
|
||||||
@ -810,8 +807,9 @@ void serializeConfig() {
|
|||||||
light[F("aseg")] = autoSegments;
|
light[F("aseg")] = autoSegments;
|
||||||
|
|
||||||
JsonObject light_gc = light.createNestedObject("gc");
|
JsonObject light_gc = light.createNestedObject("gc");
|
||||||
light_gc["bri"] = (gammaCorrectBri) ? 2.8 : 1.0;
|
light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility
|
||||||
light_gc["col"] = (gammaCorrectCol) ? 2.8 : 1.0;
|
light_gc["col"] = (gammaCorrectCol) ? gammaCorrectVal : 1.0f; // keep compatibility
|
||||||
|
light_gc["val"] = gammaCorrectVal;
|
||||||
|
|
||||||
JsonObject light_tr = light.createNestedObject("tr");
|
JsonObject light_tr = light.createNestedObject("tr");
|
||||||
light_tr["mode"] = fadeTransition;
|
light_tr["mode"] = fadeTransition;
|
||||||
|
@ -79,6 +79,17 @@
|
|||||||
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(WLED_MAX_LEDMAPS) && (WLED_MAX_LEDMAPS > 32 || WLED_MAX_LEDMAPS < 10)
|
||||||
|
#undef WLED_MAX_LEDMAPS
|
||||||
|
#endif
|
||||||
|
#ifndef WLED_MAX_LEDMAPS
|
||||||
|
#ifdef ESP8266
|
||||||
|
#define WLED_MAX_LEDMAPS 10
|
||||||
|
#else
|
||||||
|
#define WLED_MAX_LEDMAPS 16
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
//Usermod IDs
|
//Usermod IDs
|
||||||
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
|
||||||
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
|
||||||
|
@ -808,9 +808,9 @@ function populateSegments(s)
|
|||||||
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";
|
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('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
|
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
|
||||||
|
|
||||||
if (!isM && Array.isArray(li.maps) && li.maps.length>1) {
|
if (Array.isArray(li.maps) && li.maps.length>1) {
|
||||||
let cont = `Ledmap: <select class="sel-sg" onchange="requestJson({'ledmap':parseInt(this.value)})"><option value="" selected>Unchanged</option>`;
|
let cont = `Ledmap: <select class="sel-sg" onchange="requestJson({'ledmap':parseInt(this.value)})"><option value="" selected>Unchanged</option>`;
|
||||||
for (const k of (li.maps||[])) cont += `<option value="${k}">${k==0?'Default':'ledmap'+k+'.json'}</option>`;
|
for (const k of (li.maps||[])) cont += `<option value="${k.id}">${k.id==0?'Default':(k.n?k.n:'ledmap'+k.id+'.json')}</option>`;
|
||||||
cont += "</select></div>";
|
cont += "</select></div>";
|
||||||
gId("ledmap").innerHTML = cont;
|
gId("ledmap").innerHTML = cont;
|
||||||
gId("ledmap").classList.remove('hide');
|
gId("ledmap").classList.remove('hide');
|
||||||
@ -1875,9 +1875,9 @@ ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
|
|||||||
<input type="checkbox" id="p${i}sbchk">
|
<input type="checkbox" id="p${i}sbchk">
|
||||||
<span class="checkmark"></span>
|
<span class="checkmark"></span>
|
||||||
</label>`;
|
</label>`;
|
||||||
if (!isM && Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
|
if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>1) {
|
||||||
content += `<div class="lbl-l">Ledmap: <div class="sel-p"><select class="sel-p" id="p${i}lmp"><option value="">Unchanged</option>`;
|
content += `<div class="lbl-l">Ledmap: <div class="sel-p"><select class="sel-p" id="p${i}lmp"><option value="">Unchanged</option>`;
|
||||||
for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k==0?'Default':'ledmap'+k+'.json'}</option>`;
|
for (const k of (lastinfo.maps||[])) content += `<option value="${k.id}"${(i>0 && pJson[i].ledmap==k.id)?" selected":""}>${k.id==0?'Default':(k.n?k.n:'ledmap'+k.id+'.json')}</option>`;
|
||||||
content += "</select></div></div>";
|
content += "</select></div></div>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2072,7 +2072,7 @@ function setSeg(s)
|
|||||||
obj.seg.grp = grp;
|
obj.seg.grp = grp;
|
||||||
obj.seg.spc = spc;
|
obj.seg.spc = spc;
|
||||||
obj.seg.of = ofs;
|
obj.seg.of = ofs;
|
||||||
if (isM) obj.seg.tp = gId(`seg${s}tp`).checked;
|
if (isM && gId(`seg${s}tp`)) obj.seg.tp = gId(`seg${s}tp`).checked;
|
||||||
}
|
}
|
||||||
resetUtil(); // close add segment dialog just in case
|
resetUtil(); // close add segment dialog just in case
|
||||||
requestJson(obj);
|
requestJson(obj);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
var d=document;
|
var d=document;
|
||||||
var loc = false, locip;
|
var loc = false, locip;
|
||||||
var maxPanels=64;
|
var maxPanels=64;
|
||||||
|
var ctx = null; // WLEDMM
|
||||||
function H(){window.open("https://kno.wled.ge/features/2D");}
|
function H(){window.open("https://kno.wled.ge/features/2D");}
|
||||||
function B(){window.open("/settings","_self");}
|
function B(){window.open("/settings","_self");}
|
||||||
function gId(n){return d.getElementById(n);}
|
function gId(n){return d.getElementById(n);}
|
||||||
@ -52,6 +53,30 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gId("mpdiv").style.display = "block";
|
gId("mpdiv").style.display = "block";
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout;
|
||||||
|
function showToast(text, error = false)
|
||||||
|
{
|
||||||
|
var x = gId("toast");
|
||||||
|
x.innerHTML = text;
|
||||||
|
x.className = error ? "error":"show";
|
||||||
|
clearTimeout(timeout);
|
||||||
|
x.style.animation = 'none';
|
||||||
|
timeout = setTimeout(function(){ x.className = x.className.replace("show", ""); }, 2900);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFile(name) {
|
||||||
|
var req = new XMLHttpRequest();
|
||||||
|
req.addEventListener('load', function(){showToast(this.responseText,this.status >= 400)});
|
||||||
|
req.addEventListener('error', function(e){showToast(e.stack,true);});
|
||||||
|
req.open("POST", "/upload");
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append("data", d.Sf.data.files[0], name);
|
||||||
|
req.send(formData);
|
||||||
|
d.Sf.data.value = '';
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPanels() {
|
function addPanels() {
|
||||||
@ -67,21 +92,21 @@
|
|||||||
var pw = parseInt(d.Sf.PW.value);
|
var pw = parseInt(d.Sf.PW.value);
|
||||||
var ph = parseInt(d.Sf.PH.value);
|
var ph = parseInt(d.Sf.PH.value);
|
||||||
let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br>
|
let b = `<div id="pnl${i}"><hr class="sml">Panel ${i}<br>
|
||||||
1<sup>st</sup> LED: <select name="P${i}B">
|
1<sup>st</sup> LED: <select name="P${i}B" oninput="UI()">
|
||||||
<option value="0">Top</option>
|
<option value="0">Top</option>
|
||||||
<option value="1">Bottom</option>
|
<option value="1">Bottom</option>
|
||||||
</select><select name="P${i}R">
|
</select><select name="P${i}R" oninput="UI()">
|
||||||
<option value="0">Left</option>
|
<option value="0">Left</option>
|
||||||
<option value="1">Right</option>
|
<option value="1">Right</option>
|
||||||
</select><br>
|
</select><br>
|
||||||
Orientation: <select name="P${i}V">
|
Orientation: <select name="P${i}V" oninput="UI()">
|
||||||
<option value="0">Horizontal</option>
|
<option value="0">Horizontal</option>
|
||||||
<option value="1">Vertical</option>
|
<option value="1">Vertical</option>
|
||||||
</select><br>
|
</select><br>
|
||||||
Serpentine: <input type="checkbox" name="P${i}S"><br>
|
Serpentine: <input type="checkbox" name="P${i}S" oninput="UI()"><br>
|
||||||
Dimensions (WxH): <input id="P${i}W" name="P${i}W" type="number" min="1" max="128" value="${pw}"> x <input id="P${i}H" name="P${i}H" type="number" min="1" max="128" value="${ph}"><br>
|
Dimensions (WxH): <input name="P${i}W" type="number" min="1" max="255" value="${pw}" oninput="UI()"> x <input name="P${i}H" type="number" min="1" max="255" value="${ph}" oninput="UI()"><br>
|
||||||
Offset X:<input id="P${i}X" name="P${i}X" type="number" min="0" max="256" value="0">
|
Offset X:<input name="P${i}X" type="number" min="0" max="255" value="0" oninput="UI()">
|
||||||
Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br><i>(offset from top-left corner in # LEDs)</i>
|
Y:<input name="P${i}Y" type="number" min="0" max="255" value="0" oninput="UI()"><br><i>(offset from top-left corner in # LEDs)</i>
|
||||||
</div>`;
|
</div>`;
|
||||||
p.insertAdjacentHTML("beforeend", b);
|
p.insertAdjacentHTML("beforeend", b);
|
||||||
}
|
}
|
||||||
@ -107,17 +132,17 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
|||||||
function gen() {
|
function gen() {
|
||||||
resetPanels();
|
resetPanels();
|
||||||
|
|
||||||
var pansH = parseInt(d.Sf.MPH.value);
|
var pansH = parseInt(Sf.MPH.value);
|
||||||
var pansV = parseInt(d.Sf.MPV.value);
|
var pansV = parseInt(Sf.MPV.value);
|
||||||
var c = pansH*pansV;
|
var c = pansH*pansV;
|
||||||
d.Sf.MPC.value = c; // number of panels
|
Sf.MPC.value = c; // number of panels
|
||||||
|
|
||||||
var ps = d.Sf.PS.checked;
|
var ps = Sf.PS.checked;
|
||||||
var pv = d.Sf.PV.value==="1";
|
var pv = Sf.PV.value==="1";
|
||||||
var pb = d.Sf.PB.value==="1";
|
var pb = Sf.PB.value==="1";
|
||||||
var pr = d.Sf.PR.value==="1";
|
var pr = Sf.PR.value==="1";
|
||||||
var pw = parseInt(d.Sf.PW.value);
|
var pw = parseInt(Sf.PW.value);
|
||||||
var ph = parseInt(d.Sf.PH.value);
|
var ph = parseInt(Sf.PH.value);
|
||||||
|
|
||||||
var h = pv ? pansV : pansH;
|
var h = pv ? pansV : pansH;
|
||||||
var v = pv ? pansH : pansV;
|
var v = pv ? pansH : pansV;
|
||||||
@ -127,13 +152,141 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
|||||||
var y = (pv?pr:pb) ? v-j-1: j;
|
var y = (pv?pr:pb) ? v-j-1: j;
|
||||||
var x = (pv?pb:pr) ? h-i-1 : i;
|
var x = (pv?pb:pr) ? h-i-1 : i;
|
||||||
x = ps && j%2 ? h-x-1 : x;
|
x = ps && j%2 ? h-x-1 : x;
|
||||||
gId("P"+p+"X").value = (pv?y:x) * pw;
|
Sf[`P${p}X`].value = (pv?y:x) * pw;
|
||||||
gId("P"+p+"Y").value = (pv?x:y) * ph
|
Sf[`P${p}Y`].value = (pv?x:y) * ph
|
||||||
gId("P"+p+"W").value = pw;
|
Sf[`P${p}W`].value = pw;
|
||||||
gId("P"+p+"H").value = ph;
|
Sf[`P${p}H`].value = ph;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function expand(o,i)
|
||||||
|
{
|
||||||
|
i.style.display = i.style.display!=="none" ? "none" : "";
|
||||||
|
o.style.rotate = i.style.display==="none" ? "none" : "90deg";
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
|
||||||
|
if (!ctx) {
|
||||||
|
//WLEDMM: add canvas, initialize and set UI
|
||||||
|
var canvas = gId("canvas");
|
||||||
|
canvas.width = window.innerWidth > 640?640:400; //Mobile gets 400, pc 640
|
||||||
|
canvas.height = canvas.width;
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// window.requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
//calc max height and width
|
||||||
|
var maxWidth = 0;
|
||||||
|
var maxHeight = 0;
|
||||||
|
for (let p=0; p<gId("panels").children.length; p++) {
|
||||||
|
var px = parseInt(Sf[`P${p}X`].value); //first led x
|
||||||
|
var py = parseInt(Sf[`P${p}Y`].value); //first led y
|
||||||
|
var pw = parseInt(Sf[`P${p}W`].value); //width
|
||||||
|
var ph = parseInt(Sf[`P${p}H`].value); //height
|
||||||
|
maxWidth = Math.max(maxWidth, px + pw);
|
||||||
|
maxHeight = Math.max(maxHeight, py + ph);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.canvas.height = ctx.canvas.width / maxWidth * maxHeight;
|
||||||
|
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
var space=0; // space between panels + margin
|
||||||
|
var ppL = (ctx.canvas.width - space * 2) / maxWidth; //pixels per led
|
||||||
|
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.strokeStyle="yellow";
|
||||||
|
ctx.strokeRect(0, 0, ctx.canvas.width, ctx.canvas.height); // add space between panels
|
||||||
|
|
||||||
|
for (let p=0; p<gId("panels").children.length; p++) {
|
||||||
|
var px = parseInt(Sf[`P${p}X`].value); //first led x
|
||||||
|
var py = parseInt(Sf[`P${p}Y`].value); //first led y
|
||||||
|
var pw = parseInt(Sf[`P${p}W`].value); //width
|
||||||
|
var ph = parseInt(Sf[`P${p}H`].value); //height
|
||||||
|
|
||||||
|
var pb = Sf[`P${p}B`].value == "1"; //bottom
|
||||||
|
var pr = Sf[`P${p}R`].value == "1"; //right
|
||||||
|
var pv = Sf[`P${p}V`].value == "1"; //vertical
|
||||||
|
var ps = Sf[`P${p}S`].checked; //serpentine
|
||||||
|
|
||||||
|
var topLeftX = px*ppL + space; //left margin
|
||||||
|
var topLeftY = py*ppL + space; //top margin
|
||||||
|
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle="white";
|
||||||
|
ctx.strokeRect(topLeftX, topLeftY, pw*ppL, ph*ppL); // add space between panels
|
||||||
|
|
||||||
|
var lnX;
|
||||||
|
var lnY;
|
||||||
|
|
||||||
|
//find start led
|
||||||
|
if (pb) //bottom
|
||||||
|
lnY = topLeftY + ph*ppL - ppL/2;
|
||||||
|
else //top
|
||||||
|
lnY = topLeftY + ppL/2;
|
||||||
|
if (pr) //right
|
||||||
|
lnX = topLeftX + pw*ppL - ppL/2;
|
||||||
|
else //left
|
||||||
|
lnX = topLeftX + ppL/2;
|
||||||
|
|
||||||
|
//first led
|
||||||
|
ctx.fillStyle = "green";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
//start line
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(lnX, lnY);
|
||||||
|
|
||||||
|
var longLineLength = (pv?ph:pw)*ppL - ppL;
|
||||||
|
for (let ln=0; ln<(pv?pw:ph); ln++) { //loop over panelwidth (or height of vertical?)
|
||||||
|
|
||||||
|
var serpLine = ps && ln%2!=0; //serp: turn around if even line
|
||||||
|
|
||||||
|
if (pv) //if vertical
|
||||||
|
lnY += (pb?-1:1) * longLineLength * (serpLine?-1:1); //if vertical change the Y
|
||||||
|
else
|
||||||
|
lnX += (pr?-1:1) * longLineLength * (serpLine?-1:1); //if horizontal change the X
|
||||||
|
|
||||||
|
ctx.lineTo(lnX, lnY); //draw the long line
|
||||||
|
|
||||||
|
if (ln<(pv?pw:ph)-1) { //not the last
|
||||||
|
//find the small line end point
|
||||||
|
if (pv) //vertical
|
||||||
|
lnX += (pr?-1:1) * ppL;
|
||||||
|
else //horizontal
|
||||||
|
lnY += (pb?-1:1) * ppL;
|
||||||
|
|
||||||
|
//if serpentine go next else go down
|
||||||
|
if (ps) { //serpentine
|
||||||
|
ctx.lineTo(lnX, lnY); //draw the serpentine line
|
||||||
|
} else {
|
||||||
|
//find the other end of the long line
|
||||||
|
if (pv) //vertical
|
||||||
|
lnY += (pb?1:-1) * longLineLength * (serpLine?-1:1); //min as we go back
|
||||||
|
else //horizontal
|
||||||
|
lnX += (pr?1:-1) * longLineLength * (serpLine?-1:1);
|
||||||
|
ctx.moveTo(lnX, lnY); //move to the start point of the next long line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
//last led
|
||||||
|
ctx.fillStyle = "red";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(lnX, lnY, ppL*0.5, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.font = '40px Arial';
|
||||||
|
ctx.fillStyle = "orange";
|
||||||
|
ctx.fillText(p, topLeftX + pw/2*ppL - 10, topLeftY + ph/2*ppL + 10);
|
||||||
|
}
|
||||||
|
gId("MD").innerHTML = "Matrix Dimensions (W*H=LC): " + maxWidth + " x " + maxHeight + " = " + maxWidth * maxHeight;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>@import url("style.css");</style>
|
<style>@import url("style.css");</style>
|
||||||
</head>
|
</head>
|
||||||
@ -151,7 +304,8 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
|||||||
</select><br>
|
</select><br>
|
||||||
<div id="mpdiv" style="display:none;">
|
<div id="mpdiv" style="display:none;">
|
||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
<h3>Matrix Generator</h3>
|
<h3>Matrix Generator <button type="button" id="expGen" onclick="expand(this,gId('mxGen'));">></button></h3>
|
||||||
|
<div id="mxGen" style="display:none;">
|
||||||
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>
|
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">
|
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1">
|
||||||
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br>
|
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1"><br>
|
||||||
@ -167,20 +321,28 @@ Y:<input id="P${i}Y" name="P${i}Y" type="number" min="0" max="256" value="0"><br
|
|||||||
<option value="1">Vertical</option>
|
<option value="1">Vertical</option>
|
||||||
</select><br>
|
</select><br>
|
||||||
Serpentine: <input type="checkbox" name="PS"><br>
|
Serpentine: <input type="checkbox" name="PS"><br>
|
||||||
<i style="color:#fa0;">Can populate LED panel layout with pre-arranged matrix.<br>These values do not affect final layout.</i><br>
|
<i style="color:#fa0;">Pressing Populate will create LED panel layout with pre-arranged matrix.<br>Values above <i>will not</i> affect final layout.<br>
|
||||||
<button type="button" onclick="gen()">Populate</button>
|
WARNING: You may need to update each panel parameters after they are generated.</i><br>
|
||||||
|
<button type="button" onclick="gen();expand(gId('expGen'),gId('mxGen'));">Populate</button>
|
||||||
|
</div>
|
||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
<h3>Panel set-up</h3>
|
<h3>Panel set-up</h3>
|
||||||
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels()"><br>
|
Number of panels: <input name="MPC" type="number" min="1" max="64" value="1" oninput="addPanels();UI();"><br>
|
||||||
<i>A matrix is made of 1 or more physical LED panels.<br>
|
<i>A matrix is made of 1 or more physical LED panels.<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 be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br>
|
Each panel can be of different size and/or have different LED orientation and/or starting point and/or layout.</i><br>
|
||||||
<h3>LED panel layout</h3>
|
<h3>LED panel layout</h3>
|
||||||
<div id="panels">
|
<div id="panels">
|
||||||
</div>
|
</div>
|
||||||
|
<hr class="sml">
|
||||||
|
<div id="MD"></div>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<div id="json" >Gap file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/2d-gaps.json')">Upload</button></div>
|
||||||
|
<i>Note: Gap file is a <b>.json</b> file containing an array with number of elements equal to the matrix size.<br>
|
||||||
|
A value of -1 means that pixel at that position is missing, a value of 0 means never paint that pixel, and 1 means regular pixel.</i>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||||
</form>
|
</form>
|
||||||
|
<div id="toast"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -642,7 +642,8 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
|||||||
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
|
Apply preset <input name="BP" type="number" class="m" min="0" max="250" required> at boot (0 uses defaults)
|
||||||
<br><br>
|
<br><br>
|
||||||
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
Use Gamma correction for color: <input type="checkbox" name="GC"> (strongly recommended)<br>
|
||||||
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br><br>
|
Use Gamma correction for brightness: <input type="checkbox" name="GB"> (not recommended)<br>
|
||||||
|
Use Gamma value: <input name="GV" type="number" class="m" placeholder="2.8" min="1" max="3" step="0.1" required><br><br>
|
||||||
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
|
Brightness factor: <input name="BF" type="number" class="m" min="1" max="255" required> %
|
||||||
<h3>Transitions</h3>
|
<h3>Transitions</h3>
|
||||||
Crossfade: <input type="checkbox" name="TF"><br>
|
Crossfade: <input type="checkbox" name="TF"><br>
|
||||||
|
1498
wled00/html_other.h
1498
wled00/html_other.h
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2241
wled00/html_simple.h
2241
wled00/html_simple.h
File diff suppressed because it is too large
Load Diff
1907
wled00/html_ui.h
1907
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -643,8 +643,14 @@ void serializeInfo(JsonObject root)
|
|||||||
root[F("cpalcount")] = strip.customPalettes.size(); //number of custom palettes
|
root[F("cpalcount")] = strip.customPalettes.size(); //number of custom palettes
|
||||||
|
|
||||||
JsonArray ledmaps = root.createNestedArray(F("maps"));
|
JsonArray ledmaps = root.createNestedArray(F("maps"));
|
||||||
for (size_t i=0; i<10; i++) {
|
for (size_t i=0; i<WLED_MAX_LEDMAPS; i++) {
|
||||||
if ((ledMaps>>i) & 0x0001) ledmaps.add(i);
|
if ((ledMaps>>i) & 0x00000001U) {
|
||||||
|
JsonObject ledmaps0 = ledmaps.createNestedObject();
|
||||||
|
ledmaps0["id"] = i;
|
||||||
|
#ifndef ESP8266
|
||||||
|
if (i && ledmapNames[i-1]) ledmaps0["n"] = ledmapNames[i-1];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject wifi_info = root.createNestedObject("wifi");
|
JsonObject wifi_info = root.createNestedObject("wifi");
|
||||||
|
@ -208,6 +208,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
if (t <= 250) bootPreset = t;
|
if (t <= 250) bootPreset = t;
|
||||||
gammaCorrectBri = request->hasArg(F("GB"));
|
gammaCorrectBri = request->hasArg(F("GB"));
|
||||||
gammaCorrectCol = request->hasArg(F("GC"));
|
gammaCorrectCol = request->hasArg(F("GC"));
|
||||||
|
gammaCorrectVal = request->arg(F("GV")).toFloat();
|
||||||
|
if (gammaCorrectVal > 1.0f && gammaCorrectVal <= 3)
|
||||||
|
calcGammaTable(gammaCorrectVal);
|
||||||
|
else {
|
||||||
|
gammaCorrectVal = 1.0f; // no gamma correction
|
||||||
|
gammaCorrectBri = false;
|
||||||
|
gammaCorrectCol = false;
|
||||||
|
}
|
||||||
|
|
||||||
fadeTransition = request->hasArg(F("TF"));
|
fadeTransition = request->hasArg(F("TF"));
|
||||||
t = request->arg(F("TD")).toInt();
|
t = request->arg(F("TD")).toInt();
|
||||||
@ -240,6 +248,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
}
|
}
|
||||||
simplifiedUI = request->hasArg(F("SU"));
|
simplifiedUI = request->hasArg(F("SU"));
|
||||||
#endif
|
#endif
|
||||||
|
DEBUG_PRINTLN(F("Enumerating ledmaps"));
|
||||||
|
enumerateLedmaps();
|
||||||
|
DEBUG_PRINTLN(F("Loading custom palettes"));
|
||||||
|
strip.loadCustomPalettes(); // (re)load all custom palettes
|
||||||
}
|
}
|
||||||
|
|
||||||
//SYNC
|
//SYNC
|
||||||
@ -645,15 +657,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
if (subPage == 10)
|
if (subPage == 10)
|
||||||
{
|
{
|
||||||
strip.isMatrix = request->arg(F("SOMP")).toInt();
|
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.panel.clear(); // release memory if allocated
|
strip.panel.clear(); // release memory if allocated
|
||||||
if (strip.isMatrix) {
|
if (strip.isMatrix) {
|
||||||
strip.panels = MAX(1,MIN(WLED_MAX_PANELS,request->arg(F("MPC")).toInt()));
|
strip.panels = MAX(1,MIN(WLED_MAX_PANELS,request->arg(F("MPC")).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"));
|
|
||||||
strip.panel.reserve(strip.panels); // pre-allocate memory
|
strip.panel.reserve(strip.panels); // pre-allocate memory
|
||||||
for (uint8_t i=0; i<strip.panels; i++) {
|
for (uint8_t i=0; i<strip.panels; i++) {
|
||||||
WS2812FX::Panel p;
|
WS2812FX::Panel p;
|
||||||
@ -673,11 +679,13 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
pO[l] = 'H'; p.height = request->arg(pO).toInt();
|
pO[l] = 'H'; p.height = request->arg(pO).toInt();
|
||||||
strip.panel.push_back(p);
|
strip.panel.push_back(p);
|
||||||
}
|
}
|
||||||
|
strip.setUpMatrix(); // will check limits
|
||||||
|
strip.makeAutoSegments(true);
|
||||||
|
strip.deserializeMap();
|
||||||
} else {
|
} else {
|
||||||
Segment::maxWidth = strip.getLengthTotal();
|
Segment::maxWidth = strip.getLengthTotal();
|
||||||
Segment::maxHeight = 1;
|
Segment::maxHeight = 1;
|
||||||
}
|
}
|
||||||
strip.setUpMatrix(); // will check limits
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -503,12 +503,49 @@ um_data_t* simulateSound(uint8_t simulationId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
|
||||||
void enumerateLedmaps() {
|
void enumerateLedmaps() {
|
||||||
ledMaps = 1;
|
ledMaps = 1;
|
||||||
for (size_t i=1; i<10; i++) {
|
for (size_t i=1; i<WLED_MAX_LEDMAPS; i++) {
|
||||||
char fileName[16];
|
char fileName[33];
|
||||||
sprintf_P(fileName, PSTR("/ledmap%d.json"), i);
|
sprintf_P(fileName, PSTR("/ledmap%d.json"), i);
|
||||||
bool isFile = WLED_FS.exists(fileName);
|
bool isFile = WLED_FS.exists(fileName);
|
||||||
if (isFile) ledMaps |= 1 << i;
|
|
||||||
|
#ifndef ESP8266
|
||||||
|
if (ledmapNames[i-1]) { //clear old name
|
||||||
|
delete[] ledmapNames[i-1];
|
||||||
|
ledmapNames[i-1] = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (isFile) {
|
||||||
|
ledMaps |= 1 << i;
|
||||||
|
|
||||||
|
#ifndef ESP8266
|
||||||
|
if (requestJSONBufferLock(21)) {
|
||||||
|
if (readObjectFromFile(fileName, nullptr, &doc)) {
|
||||||
|
size_t len = 0;
|
||||||
|
if (!doc["n"].isNull()) {
|
||||||
|
// name field exists
|
||||||
|
const char *name = doc["n"].as<const char*>();
|
||||||
|
if (name != nullptr) len = strlen(name);
|
||||||
|
if (len > 0 && len < 33) {
|
||||||
|
ledmapNames[i-1] = new char[len+1];
|
||||||
|
if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], name, 33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ledmapNames[i-1]) {
|
||||||
|
char tmp[33];
|
||||||
|
snprintf_P(tmp, 32, PSTR("ledmap%d.json"), i);
|
||||||
|
len = strlen(tmp);
|
||||||
|
ledmapNames[i-1] = new char[len+1];
|
||||||
|
if (ledmapNames[i-1]) strlcpy(ledmapNames[i-1], tmp, 33);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
releaseJSONBufferLock();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -171,15 +171,14 @@ void WLED::loop()
|
|||||||
}
|
}
|
||||||
delete busConfigs[i]; busConfigs[i] = nullptr;
|
delete busConfigs[i]; busConfigs[i] = nullptr;
|
||||||
}
|
}
|
||||||
strip.finalizeInit();
|
strip.finalizeInit(); // also loads default ledmap if present
|
||||||
loadLedmap = 0;
|
|
||||||
if (aligned) strip.makeAutoSegments();
|
if (aligned) strip.makeAutoSegments();
|
||||||
else strip.fixInvalidSegments();
|
else strip.fixInvalidSegments();
|
||||||
yield();
|
yield();
|
||||||
serializeConfig();
|
serializeConfig();
|
||||||
}
|
}
|
||||||
if (loadLedmap >= 0) {
|
if (loadLedmap >= 0) {
|
||||||
strip.deserializeMap(loadLedmap);
|
if (!strip.deserializeMap(loadLedmap) && strip.isMatrix && loadLedmap == 0) strip.setUpMatrix();
|
||||||
loadLedmap = -1;
|
loadLedmap = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// version code in format yymmddb (b = daily build)
|
// version code in format yymmddb (b = daily build)
|
||||||
#define VERSION 2302120
|
#define VERSION 2302150
|
||||||
|
|
||||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||||
//#define WLED_USE_MY_CONFIG
|
//#define WLED_USE_MY_CONFIG
|
||||||
@ -319,6 +319,7 @@ WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB co
|
|||||||
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
|
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
|
||||||
WLED_GLOBAL bool gammaCorrectCol _INIT(true ); // use gamma correction on colors
|
WLED_GLOBAL bool gammaCorrectCol _INIT(true ); // use gamma correction on colors
|
||||||
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
|
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
|
||||||
|
WLED_GLOBAL float gammaCorrectVal _INIT(2.8f); // gamma correction value
|
||||||
|
|
||||||
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
|
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
|
||||||
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
|
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color
|
||||||
@ -677,7 +678,14 @@ WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
|
|||||||
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
|
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
|
||||||
WLED_GLOBAL bool doInitBusses _INIT(false);
|
WLED_GLOBAL bool doInitBusses _INIT(false);
|
||||||
WLED_GLOBAL int8_t loadLedmap _INIT(-1);
|
WLED_GLOBAL int8_t loadLedmap _INIT(-1);
|
||||||
|
#ifndef ESP8266
|
||||||
|
WLED_GLOBAL char *ledmapNames[WLED_MAX_LEDMAPS-1] _INIT_N(({nullptr}));
|
||||||
|
#endif
|
||||||
|
#if WLED_MAX_LEDMAPS>16
|
||||||
|
WLED_GLOBAL uint32_t ledMaps _INIT(0); // bitfield representation of available ledmaps
|
||||||
|
#else
|
||||||
WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps
|
WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps
|
||||||
|
#endif
|
||||||
|
|
||||||
// Usermod manager
|
// Usermod manager
|
||||||
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
||||||
|
@ -364,7 +364,7 @@ void getSettingsJS(byte subPage, char* dest)
|
|||||||
|
|
||||||
if (subPage == 2)
|
if (subPage == 2)
|
||||||
{
|
{
|
||||||
char nS[8];
|
char nS[32];
|
||||||
|
|
||||||
appendGPIOinfo();
|
appendGPIOinfo();
|
||||||
|
|
||||||
@ -445,6 +445,7 @@ void getSettingsJS(byte subPage, char* dest)
|
|||||||
|
|
||||||
sappend('c',SET_F("GB"),gammaCorrectBri);
|
sappend('c',SET_F("GB"),gammaCorrectBri);
|
||||||
sappend('c',SET_F("GC"),gammaCorrectCol);
|
sappend('c',SET_F("GC"),gammaCorrectCol);
|
||||||
|
dtostrf(gammaCorrectVal,3,1,nS); sappends('s',SET_F("GV"),nS);
|
||||||
sappend('c',SET_F("TF"),fadeTransition);
|
sappend('c',SET_F("TF"),fadeTransition);
|
||||||
sappend('v',SET_F("TD"),transitionDelayDefault);
|
sappend('v',SET_F("TD"),transitionDelayDefault);
|
||||||
sappend('c',SET_F("PF"),strip.paletteFade);
|
sappend('c',SET_F("PF"),strip.paletteFade);
|
||||||
@ -738,10 +739,6 @@ void getSettingsJS(byte subPage, char* dest)
|
|||||||
sappend('v',SET_F("PH"),strip.panel[0].height);
|
sappend('v',SET_F("PH"),strip.panel[0].height);
|
||||||
}
|
}
|
||||||
sappend('v',SET_F("MPC"),strip.panels);
|
sappend('v',SET_F("MPC"),strip.panels);
|
||||||
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
|
// panels
|
||||||
for (uint8_t i=0; i<strip.panels; i++) {
|
for (uint8_t i=0; i<strip.panels; i++) {
|
||||||
char n[5];
|
char n[5];
|
||||||
|
Loading…
Reference in New Issue
Block a user