Merge branch 'mdev' into audio-palette-updates
This commit is contained in:
@@ -81,7 +81,7 @@ uint16_t Segment::maxHeight = 1;
|
||||
// copy constructor
|
||||
Segment::Segment(const Segment &orig) {
|
||||
//DEBUG_PRINTLN(F("-- Copy segment constructor --"));
|
||||
memcpy(this, &orig, sizeof(Segment));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
name = nullptr;
|
||||
data = nullptr;
|
||||
_dataLen = 0;
|
||||
@@ -97,7 +97,7 @@ Segment::Segment(const Segment &orig) {
|
||||
// move constructor
|
||||
Segment::Segment(Segment &&orig) noexcept {
|
||||
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
|
||||
memcpy(this, &orig, sizeof(Segment));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.name = nullptr;
|
||||
orig.data = nullptr;
|
||||
orig._dataLen = 0;
|
||||
@@ -116,7 +116,7 @@ Segment& Segment::operator= (const Segment &orig) {
|
||||
if (leds && !Segment::_globalLeds) free(leds);
|
||||
deallocateData();
|
||||
// copy source
|
||||
memcpy(this, &orig, sizeof(Segment));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
// erase pointers to allocated data
|
||||
name = nullptr;
|
||||
data = nullptr;
|
||||
@@ -141,7 +141,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
|
||||
deallocateData(); // free old runtime data
|
||||
if (_t) delete _t;
|
||||
if (leds && !Segment::_globalLeds) free(leds);
|
||||
memcpy(this, &orig, sizeof(Segment));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.name = nullptr;
|
||||
orig.data = nullptr;
|
||||
orig._dataLen = 0;
|
||||
@@ -188,7 +188,8 @@ void Segment::deallocateData() {
|
||||
void Segment::resetIfRequired() {
|
||||
if (reset) {
|
||||
if (leds && !Segment::_globalLeds) { free(leds); leds = nullptr; }
|
||||
//if (_t) { delete _t; _t = nullptr; transitional = false; }
|
||||
if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
|
||||
deallocateData();
|
||||
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
|
||||
reset = false; // setOption(SEG_OPTION_RESET, false);
|
||||
}
|
||||
@@ -230,14 +231,34 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
case FX_MODE_NOISE16_4 : pal = 26; break; // landscape 33
|
||||
case FX_MODE_GLITTER : pal = 11; break; // rainbow colors
|
||||
case FX_MODE_SUNRISE : pal = 35; break; // heat palette
|
||||
case FX_MODE_FLOW : pal = 6; break; // party
|
||||
case FX_MODE_RAILWAY : pal = 3; break; // prim + sec
|
||||
}
|
||||
switch (pal) {
|
||||
case 0: //default palette. Exceptions for specific effects above
|
||||
targetPalette = PartyColors_p; break;
|
||||
case 1: {//periodically replace palette with a random one. Transition palette change in 500ms
|
||||
uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
|
||||
if (timeSinceLastChange > 5000 /*+ ((uint32_t)(255-intensity))*100*/) {
|
||||
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
|
||||
prevRandomPalette = randomPalette;
|
||||
randomPalette = CRGBPalette16(
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)),
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)),
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)),
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)));
|
||||
_lastPaletteChange = millis();
|
||||
timeSinceLastChange = 0;
|
||||
}
|
||||
|
||||
//WLEDMM: smooth transitions of palettes instead of every 5 sec with short transition
|
||||
for (int i=0; i< 16; i++) {
|
||||
targetPalette[i].r = prevRandomPalette[i].r*(5000-timeSinceLastChange)/5000 + randomPalette[i].r*timeSinceLastChange/5000;
|
||||
targetPalette[i].g = prevRandomPalette[i].g*(5000-timeSinceLastChange)/5000 + randomPalette[i].g*timeSinceLastChange/5000;
|
||||
targetPalette[i].b = prevRandomPalette[i].b*(5000-timeSinceLastChange)/5000 + randomPalette[i].b*timeSinceLastChange/5000;
|
||||
}
|
||||
break;}
|
||||
case 74: {//periodically replace palette with a random one. Transition palette change in 500ms
|
||||
uint32_t timeSinceLastChange = millis() - _lastPaletteChange;
|
||||
if (timeSinceLastChange > randomPaletteChangeTime * 1000U) {
|
||||
prevRandomPalette = randomPalette;
|
||||
randomPalette = CRGBPalette16(
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)),
|
||||
@@ -401,8 +422,8 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
|
||||
markForReset();
|
||||
return;
|
||||
}
|
||||
if (i1 < Segment::maxWidth) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
|
||||
stop = i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2);
|
||||
if (i1 < Segment::maxWidth || (i1 >= Segment::maxWidth*Segment::maxHeight && i1 < strip.getLengthTotal())) start = i1; // Segment::maxWidth equals strip.getLengthTotal() for 1D
|
||||
stop = i2 > Segment::maxWidth*Segment::maxHeight ? MIN(i2,strip.getLengthTotal()) : (i2 > Segment::maxWidth ? Segment::maxWidth : MAX(1,i2));
|
||||
startY = 0;
|
||||
stopY = 1;
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -423,6 +444,10 @@ void Segment::set(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, uint16_t o
|
||||
|
||||
bool Segment::setColor(uint8_t slot, uint32_t c) { //returns true if changed
|
||||
if (slot >= NUM_COLORS || c == colors[slot]) return false;
|
||||
if (!_isRGB && !_hasW) {
|
||||
if (slot == 0 && c == BLACK) return false; // on/off segment cannot have primary color black
|
||||
if (slot == 1 && c != BLACK) return false; // on/off segment cannot have secondary color non black
|
||||
}
|
||||
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
|
||||
colors[slot] = c;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
@@ -556,12 +581,12 @@ class JMapC {
|
||||
char previousSegmentName[50] = "";
|
||||
|
||||
~JMapC() {
|
||||
Serial.println("~JMapC");
|
||||
USER_PRINTLN("~JMapC");
|
||||
deletejVectorMap();
|
||||
}
|
||||
void deletejVectorMap() {
|
||||
if (jVectorMap.size() > 0) {
|
||||
Serial.println("delete jVectorMap");
|
||||
USER_PRINTLN("delete jVectorMap");
|
||||
for (size_t i=0; i<jVectorMap.size(); i++)
|
||||
delete jVectorMap[i].array;
|
||||
jVectorMap.clear();
|
||||
@@ -605,11 +630,11 @@ class JMapC {
|
||||
else if (SEGMENT.name != nullptr && strcmp(SEGMENT.name, previousSegmentName) != 0) {
|
||||
uint32_t dataSize = 0;
|
||||
deletejVectorMap();
|
||||
Serial.print("New "); Serial.println(SEGMENT.name);
|
||||
USER_PRINT("New "); USER_PRINTLN(SEGMENT.name);
|
||||
char jMapFileName[50];
|
||||
strcpy(jMapFileName, "/");
|
||||
strcat(jMapFileName, SEGMENT.name);
|
||||
strcat(jMapFileName, ".jmap");
|
||||
strcat(jMapFileName, ".json");
|
||||
File jMapFile;
|
||||
jMapFile = WLED_FS.open(jMapFileName, "r");
|
||||
|
||||
@@ -620,11 +645,11 @@ class JMapC {
|
||||
jMapFile.find("[");
|
||||
do {
|
||||
DeserializationError err = deserializeJson(docChunk, jMapFile);
|
||||
// serializeJson(docChunk, Serial); Serial.println();
|
||||
// Serial.printf("docChunk %u / %u%% (%u %u %u) %u\n", (unsigned int)docChunk.memoryUsage(), 100 * docChunk.memoryUsage() / docChunk.capacity(), (unsigned int)docChunk.size(), docChunk.overflowed(), (unsigned int)docChunk.nesting(), jMapFile.size());
|
||||
// serializeJson(docChunk, Serial); USER_PRINTLN();
|
||||
// USER_PRINTF("docChunk %u / %u%% (%u %u %u) %u\n", (unsigned int)docChunk.memoryUsage(), 100 * docChunk.memoryUsage() / docChunk.capacity(), (unsigned int)docChunk.size(), docChunk.overflowed(), (unsigned int)docChunk.nesting(), jMapFile.size());
|
||||
if (err)
|
||||
{
|
||||
Serial.printf("deserializeJson() of parseTree failed with code %s\n", err.c_str());
|
||||
USER_PRINTF("deserializeJson() of parseTree failed with code %s\n", err.c_str());
|
||||
delete[] SEGMENT.name; SEGMENT.name = nullptr; //need to clear the name as otherwise continuously loaded
|
||||
return;
|
||||
}
|
||||
@@ -665,10 +690,10 @@ class JMapC {
|
||||
scale = MIN(SEGMENT.virtualWidth() / maxWidth, SEGMENT.virtualHeight() / maxHeight);
|
||||
|
||||
dataSize += sizeof(jVectorMap);
|
||||
Serial.print("dataSize ");
|
||||
Serial.print(dataSize);
|
||||
Serial.print(" scale ");
|
||||
Serial.println(scale);
|
||||
USER_PRINT("dataSize ");
|
||||
USER_PRINT(dataSize);
|
||||
USER_PRINT(" scale ");
|
||||
USER_PRINTLN(scale);
|
||||
strcpy(previousSegmentName, SEGMENT.name);
|
||||
}
|
||||
} //updatejMapDoc
|
||||
@@ -677,7 +702,7 @@ class JMapC {
|
||||
//WLEDMM jMap
|
||||
void Segment::createjMap() {
|
||||
if (!jMap) {
|
||||
Serial.println("createjMap");
|
||||
USER_PRINTLN("createjMap");
|
||||
jMap = new JMapC();
|
||||
}
|
||||
}
|
||||
@@ -686,7 +711,7 @@ void Segment::createjMap() {
|
||||
void Segment::deletejMap() {
|
||||
//Should be called from ~Segment but causes crash (and ~Segment is called quite often...)
|
||||
if (jMap) {
|
||||
Serial.println("deletejMap");
|
||||
USER_PRINTLN("deletejMap");
|
||||
delete (JMapC *)jMap; jMap = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -758,7 +783,9 @@ void xyFromBlock(uint16_t &x,uint16_t &y, uint16_t i, uint16_t vW, uint16_t vH,
|
||||
|
||||
void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATTR conditionaly
|
||||
{
|
||||
#ifndef WLED_DISABLE_2D
|
||||
int vStrip = i>>16; // hack to allow running on virtual strips (2D segment columns/rows)
|
||||
#endif
|
||||
i &= 0xFFFF;
|
||||
|
||||
if (i >= virtualLength() || i<0) return; // if pixel would fall out of segment just exit
|
||||
@@ -847,12 +874,14 @@ void IRAM_ATTR_YN Segment::setPixelColor(int i, uint32_t col) //WLEDMM: IRAM_ATT
|
||||
}
|
||||
return;
|
||||
} else if (Segment::maxHeight!=1 && (width()==1 || height()==1)) {
|
||||
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
|
||||
int x = 0, y = 0;
|
||||
if (virtualHeight()>1) y = i;
|
||||
if (virtualWidth() >1) x = i;
|
||||
setPixelColorXY(x, y, col);
|
||||
return;
|
||||
if (start < Segment::maxWidth*Segment::maxHeight) {
|
||||
// we have a vertical or horizontal 1D segment (WARNING: virtual...() may be transposed)
|
||||
int x = 0, y = 0;
|
||||
if (virtualHeight()>1) y = i;
|
||||
if (virtualWidth() >1) x = i;
|
||||
setPixelColorXY(x, y, col);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -931,7 +960,9 @@ void Segment::setPixelColor(float i, uint32_t col, bool aa)
|
||||
|
||||
uint32_t Segment::getPixelColor(int i)
|
||||
{
|
||||
#ifndef WLED_DISABLE_2D
|
||||
int vStrip = i>>16;
|
||||
#endif
|
||||
i &= 0xFFFF;
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -1018,29 +1049,46 @@ uint8_t Segment::differs(Segment& b) const {
|
||||
}
|
||||
|
||||
void Segment::refreshLightCapabilities() {
|
||||
uint8_t capabilities = 0x01;
|
||||
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;
|
||||
}
|
||||
if (segStartIdx == segStopIdx) segStopIdx++; // we only have 1 pixel segment
|
||||
}
|
||||
} else {
|
||||
// we are on the strip located after the matrix
|
||||
segStartIdx = start;
|
||||
segStopIdx = stop;
|
||||
}
|
||||
|
||||
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
if (!bus->isOk()) continue;
|
||||
if (bus->getStart() >= stop) continue;
|
||||
if (bus->getStart() + bus->getLength() <= start) continue;
|
||||
if (bus->getStart() >= segStopIdx) continue;
|
||||
if (bus->getStart() + bus->getLength() <= segStartIdx) continue;
|
||||
|
||||
uint8_t type = bus->getType();
|
||||
if (type == TYPE_ONOFF || type == TYPE_ANALOG_1CH || (!cctFromRgb && type == TYPE_ANALOG_2CH)) capabilities &= 0xFE; // does not support RGB
|
||||
if (bus->isRgbw()) capabilities |= 0x02; // segment supports white channel
|
||||
if (!cctFromRgb) {
|
||||
switch (type) {
|
||||
case TYPE_ANALOG_5CH:
|
||||
case TYPE_ANALOG_2CH:
|
||||
capabilities |= 0x04; //segment supports white CCT
|
||||
}
|
||||
//uint8_t type = bus->getType();
|
||||
if (bus->hasRGB() || (cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB;
|
||||
if (!cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT;
|
||||
if (correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider)
|
||||
if (bus->hasWhite()) {
|
||||
uint8_t aWM = Bus::getGlobalAWMode() == AW_GLOBAL_DISABLED ? bus->getAutoWhiteMode() : Bus::getGlobalAWMode();
|
||||
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
|
||||
// if auto white calculation from RGB is active (Accurate/Brighter), force RGB controls even if there are no RGB busses
|
||||
if (!whiteSlider) capabilities |= SEG_CAPABILITY_RGB;
|
||||
// if auto white calculation from RGB is disabled/optional (None/Dual), allow white channel adjustments
|
||||
if ( whiteSlider) capabilities |= SEG_CAPABILITY_W;
|
||||
}
|
||||
if (correctWB && !(type == TYPE_ANALOG_1CH || type == TYPE_ONOFF)) capabilities |= 0x04; //white balance correction (uses CCT slider)
|
||||
uint8_t aWM = Bus::getAutoWhiteMode()<255 ? Bus::getAutoWhiteMode() : bus->getAWMode();
|
||||
bool whiteSlider = (aWM == RGBW_MODE_DUAL || aWM == RGBW_MODE_MANUAL_ONLY); // white slider allowed
|
||||
if (bus->isRgbw() && (whiteSlider || !(capabilities & 0x01))) capabilities |= 0x08; // allow white channel adjustments (AWM allows or is not RGB)
|
||||
}
|
||||
_capabilities = capabilities;
|
||||
}
|
||||
@@ -1164,7 +1212,7 @@ void Segment::blur(uint8_t blur_amount)
|
||||
* The colours are a transition r -> g -> b -> back to r
|
||||
* Inspired by the Adafruit examples.
|
||||
*/
|
||||
uint32_t Segment::color_wheel(uint8_t pos) { // TODO
|
||||
uint32_t Segment::color_wheel(uint8_t pos) {
|
||||
if (palette) return color_from_palette(pos, false, true, 0);
|
||||
pos = 255 - pos;
|
||||
if(pos < 85) {
|
||||
@@ -1268,6 +1316,65 @@ uint8_t * Segment::getAudioPalette(int pal) {
|
||||
// WS2812FX class implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//WLEDMM from util.cpp
|
||||
// enumerate all ledmapX.json files on FS and extract ledmap names if existing
|
||||
void WS2812FX::enumerateLedmaps() {
|
||||
ledMaps = 1;
|
||||
for (int i=1; i<10; i++) {
|
||||
char fileName[33];
|
||||
snprintf_P(fileName, sizeof(fileName), PSTR("/ledmap%d.json"), i);
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
||||
//WLEDMM add segment names to be used as ledmap names
|
||||
uint8_t segment_index = 0;
|
||||
for (segment &seg : _segments) {
|
||||
if (seg.name != nullptr && strcmp(seg.name, "") != 0) {
|
||||
char fileName[33];
|
||||
snprintf_P(fileName, sizeof(fileName), PSTR("/lm%s.json"), seg.name);
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
if (isFile) ledMaps |= 1 << (10+segment_index);
|
||||
}
|
||||
segment_index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//do not call this method from system context (network callback)
|
||||
void WS2812FX::finalizeInit(void)
|
||||
{
|
||||
@@ -1308,7 +1415,7 @@ void WS2812FX::finalizeInit(void)
|
||||
if (bus == nullptr) continue;
|
||||
if (bus->getStart() + bus->getLength() > MAX_LEDS) break;
|
||||
//RGBW mode is enabled if at least one of the strips is RGBW
|
||||
_hasWhiteChannel |= bus->isRgbw();
|
||||
_hasWhiteChannel |= bus->hasWhite();
|
||||
//refresh is required to remain off if at least one of the strips requires the refresh.
|
||||
_isOffRefreshRequired |= bus->isOffRefreshRequired();
|
||||
uint16_t busEnd = bus->getStart() + bus->getLength();
|
||||
@@ -1322,10 +1429,11 @@ void WS2812FX::finalizeInit(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!isMatrix) { // if 2D then max values defined in setUpMatrix() using panel set-up
|
||||
if (isMatrix) setUpMatrix();
|
||||
else {
|
||||
Segment::maxWidth = _length;
|
||||
Segment::maxHeight = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//initialize leds array. TBD: realloc if nr of leds change
|
||||
if (Segment::_globalLeds) {
|
||||
@@ -1334,17 +1442,20 @@ void WS2812FX::finalizeInit(void)
|
||||
Segment::_globalLeds = nullptr;
|
||||
}
|
||||
if (useLedsArray) {
|
||||
size_t arrSize = sizeof(CRGB) * getLengthTotal();
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound())
|
||||
Segment::_globalLeds = (CRGB*) ps_malloc(sizeof(CRGB) * _length);
|
||||
Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
|
||||
else
|
||||
#endif
|
||||
Segment::_globalLeds = (CRGB*) malloc(sizeof(CRGB) * _length);
|
||||
memset(Segment::_globalLeds, 0, sizeof(CRGB) * _length);
|
||||
Segment::_globalLeds = (CRGB*) malloc(arrSize);
|
||||
memset(Segment::_globalLeds, 0, arrSize);
|
||||
}
|
||||
|
||||
//segments are created in makeAutoSegments();
|
||||
DEBUG_PRINTLN(F("Loading custom palettes"));
|
||||
loadCustomPalettes(); // (re)load all custom palettes
|
||||
DEBUG_PRINTLN(F("Loading custom ledmaps"));
|
||||
deserializeMap(); // (re)load default ledmap
|
||||
}
|
||||
|
||||
@@ -1405,15 +1516,15 @@ void WS2812FX::service() {
|
||||
|
||||
void IRAM_ATTR WS2812FX::setPixelColor(int i, uint32_t col)
|
||||
{
|
||||
if (i >= _length) return;
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return;
|
||||
busses.setPixelColor(i, col);
|
||||
}
|
||||
|
||||
uint32_t WS2812FX::getPixelColor(uint16_t i)
|
||||
{
|
||||
if (i >= _length) return 0;
|
||||
if (i < customMappingSize) i = customMappingTable[i];
|
||||
if (i >= _length) return 0;
|
||||
return busses.getPixelColor(i);
|
||||
}
|
||||
|
||||
@@ -1475,7 +1586,7 @@ void WS2812FX::estimateCurrentAndLimitBri() {
|
||||
}
|
||||
}
|
||||
|
||||
if (bus->isRgbw()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
if (bus->hasWhite()) { //RGBW led total output with white LEDs enabled is still 50mA, so each channel uses less
|
||||
busPowerSum *= 3;
|
||||
busPowerSum = busPowerSum >> 2; //same as /= 4
|
||||
}
|
||||
@@ -1626,6 +1737,12 @@ uint8_t WS2812FX::getActiveSegmentsNum(void) {
|
||||
return c;
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::getLengthTotal(void) {
|
||||
uint16_t len = Segment::maxWidth * Segment::maxHeight; // will be _length for 1D (see finalizeInit()) but should cover whole matrix for 2D
|
||||
if (isMatrix && _length > len) len = _length; // for 2D with trailing strip
|
||||
return len;
|
||||
}
|
||||
|
||||
uint16_t WS2812FX::getLengthPhysical(void) {
|
||||
uint16_t len = 0;
|
||||
for (size_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
@@ -1643,12 +1760,7 @@ bool WS2812FX::hasRGBWBus(void) {
|
||||
for (size_t b = 0; b < busses.getNumBusses(); b++) {
|
||||
Bus *bus = busses.getBus(b);
|
||||
if (bus == nullptr || bus->getLength()==0) break;
|
||||
switch (bus->getType()) {
|
||||
case TYPE_SK6812_RGBW:
|
||||
case TYPE_TM1814:
|
||||
case TYPE_ANALOG_4CH:
|
||||
return true;
|
||||
}
|
||||
if (bus->hasRGB() && bus->hasWhite()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1673,7 +1785,6 @@ void WS2812FX::purgeSegments(bool force) {
|
||||
if (_segments.size() <= 1) return;
|
||||
for (size_t i = _segments.size()-1; i > 0; i--)
|
||||
if (_segments[i].stop == 0 || force) {
|
||||
DEBUG_PRINT(F("Purging segment segment: ")); DEBUG_PRINTLN(i);
|
||||
deleted++;
|
||||
_segments.erase(_segments.begin() + i);
|
||||
}
|
||||
@@ -1696,43 +1807,58 @@ void WS2812FX::restartRuntime() {
|
||||
for (segment &seg : _segments) seg.markForReset();
|
||||
}
|
||||
|
||||
void WS2812FX::resetSegments() {
|
||||
_segments.clear(); // destructs all Segment as part of clearing
|
||||
#ifndef WLED_DISABLE_2D
|
||||
segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length);
|
||||
#else
|
||||
segment seg = Segment(0, _length);
|
||||
#endif
|
||||
_segments.push_back(seg);
|
||||
_mainSegment = 0;
|
||||
void WS2812FX::resetSegments(bool boundsOnly) { //WLEDMM add boundsonly
|
||||
if (!boundsOnly) {
|
||||
_segments.clear(); // destructs all Segment as part of clearing
|
||||
#ifndef WLED_DISABLE_2D
|
||||
segment seg = isMatrix ? Segment(0, Segment::maxWidth, 0, Segment::maxHeight) : Segment(0, _length);
|
||||
#else
|
||||
segment seg = Segment(0, _length);
|
||||
#endif
|
||||
_segments.push_back(seg);
|
||||
_mainSegment = 0;
|
||||
} else { //WLEDMM boundsonly
|
||||
for (segment &seg : _segments) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
seg.start = 0;
|
||||
seg.stop = Segment::maxWidth;
|
||||
seg.startY = 0;
|
||||
seg.stopY = Segment::maxHeight;
|
||||
#else
|
||||
seg.start = 0;
|
||||
seg.stop = _length;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
if (isMatrix) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
// only create 1 2D segment
|
||||
if (forceReset || getSegmentsNum() == 0) resetSegments(); // initialises 1 segment
|
||||
else if (getActiveSegmentsNum() == 1) {
|
||||
size_t i = getLastActiveSegmentId();
|
||||
_segments[i].start = 0;
|
||||
_segments[i].stop = Segment::maxWidth;
|
||||
_segments[i].startY = 0;
|
||||
_segments[i].stopY = Segment::maxHeight;
|
||||
_segments[i].grouping = 1;
|
||||
_segments[i].spacing = 0;
|
||||
_mainSegment = i;
|
||||
}
|
||||
#endif
|
||||
} else if (autoSegments) { //make one segment per bus
|
||||
if (autoSegments) { //make one segment per bus
|
||||
uint16_t segStarts[MAX_NUM_SEGMENTS] = {0};
|
||||
uint16_t segStops [MAX_NUM_SEGMENTS] = {0};
|
||||
uint8_t s = 0;
|
||||
for (uint8_t i = 0; i < busses.getNumBusses(); i++) {
|
||||
size_t s = 0;
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
// 2D segment is the 1st one using entire matrix
|
||||
if (isMatrix) {
|
||||
segStarts[0] = 0;
|
||||
segStops[0] = Segment::maxWidth*Segment::maxHeight;
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (size_t i = s; i < busses.getNumBusses(); i++) {
|
||||
Bus* b = busses.getBus(i);
|
||||
|
||||
segStarts[s] = b->getStart();
|
||||
segStops[s] = segStarts[s] + b->getLength();
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (isMatrix && segStops[s] < Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix
|
||||
if (isMatrix && segStarts[s] < Segment::maxWidth*Segment::maxHeight) segStarts[s] = Segment::maxWidth*Segment::maxHeight;
|
||||
#endif
|
||||
|
||||
//check for overlap with previous segments
|
||||
for (size_t j = 0; j < s; j++) {
|
||||
if (segStops[j] > segStarts[s] && segStarts[j] < segStops[s]) {
|
||||
@@ -1744,23 +1870,40 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
_segments.clear();
|
||||
for (size_t i = 0; i < s; i++) {
|
||||
Segment seg = Segment(segStarts[i], segStops[i]);
|
||||
seg.selected = true;
|
||||
_segments.push_back(seg);
|
||||
_segments.reserve(s); // prevent reallocations
|
||||
// there is always at least one segment (but we need to differentiate between 1D and 2D)
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (isMatrix)
|
||||
_segments.push_back(Segment(0, Segment::maxWidth, 0, Segment::maxHeight));
|
||||
else
|
||||
#endif
|
||||
_segments.push_back(Segment(segStarts[0], segStops[0]));
|
||||
for (size_t i = 1; i < s; i++) {
|
||||
_segments.push_back(Segment(segStarts[i], segStops[i]));
|
||||
}
|
||||
_mainSegment = 0;
|
||||
|
||||
} else {
|
||||
|
||||
if (forceReset || getSegmentsNum() == 0) resetSegments();
|
||||
//expand the main seg to the entire length, but only if there are no other segments, or reset is forced
|
||||
else if (getActiveSegmentsNum() == 1) {
|
||||
size_t i = getLastActiveSegmentId();
|
||||
#ifndef WLED_DISABLE_2D
|
||||
_segments[i].start = 0;
|
||||
_segments[i].stop = Segment::maxWidth;
|
||||
_segments[i].startY = 0;
|
||||
_segments[i].stopY = Segment::maxHeight;
|
||||
_segments[i].grouping = 1;
|
||||
_segments[i].spacing = 0;
|
||||
#else
|
||||
_segments[i].start = 0;
|
||||
_segments[i].stop = _length;
|
||||
_mainSegment = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
_mainSegment = 0;
|
||||
|
||||
fixInvalidSegments();
|
||||
}
|
||||
@@ -1768,14 +1911,30 @@ void WS2812FX::makeAutoSegments(bool forceReset) {
|
||||
void WS2812FX::fixInvalidSegments() {
|
||||
//make sure no segment is longer than total (sanity check)
|
||||
for (size_t i = getSegmentsNum()-1; i > 0; i--) {
|
||||
if (_segments[i].start >= _length) { _segments.erase(_segments.begin()+i); continue; }
|
||||
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();
|
||||
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].stop > _length) _segments[i].stop = _length;
|
||||
}
|
||||
}
|
||||
// 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
|
||||
//irrelevant in 2D set-up
|
||||
bool WS2812FX::checkSegmentAlignment() {
|
||||
bool aligned = false;
|
||||
for (segment &seg : _segments) {
|
||||
@@ -1792,8 +1951,7 @@ bool WS2812FX::checkSegmentAlignment() {
|
||||
//After this function is called, setPixelColor() will use that segment (offsets, grouping, ... will apply)
|
||||
//Note: If called in an interrupt (e.g. JSON API), original segment must be restored,
|
||||
//otherwise it can lead to a crash on ESP32 because _segment_index is modified while in use by the main thread
|
||||
uint8_t WS2812FX::setPixelSegment(uint8_t n)
|
||||
{
|
||||
uint8_t WS2812FX::setPixelSegment(uint8_t n) {
|
||||
uint8_t prevSegId = _segment_index;
|
||||
if (n < _segments.size()) {
|
||||
_segment_index = n;
|
||||
@@ -1802,8 +1960,7 @@ uint8_t WS2812FX::setPixelSegment(uint8_t n)
|
||||
return prevSegId;
|
||||
}
|
||||
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
|
||||
{
|
||||
void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col) {
|
||||
if (i2 >= i)
|
||||
{
|
||||
for (uint16_t x = i; x <= i2; x++) setPixelColor(x, col);
|
||||
@@ -1813,26 +1970,24 @@ void WS2812FX::setRange(uint16_t i, uint16_t i2, uint32_t col)
|
||||
}
|
||||
}
|
||||
|
||||
void WS2812FX::setTransitionMode(bool t)
|
||||
{
|
||||
void WS2812FX::setTransitionMode(bool t) {
|
||||
for (segment &seg : _segments) if (!seg.transitional) seg.startTransition(t ? _transitionDur : 0);
|
||||
}
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
void WS2812FX::printSize()
|
||||
{
|
||||
void WS2812FX::printSize() {
|
||||
size_t size = 0;
|
||||
for (const Segment &seg : _segments) size += seg.getSize();
|
||||
DEBUG_PRINTF("Segments: %d -> %uB\n", _segments.size(), size);
|
||||
DEBUG_PRINTF("Modes: %d*%d=%uB\n", sizeof(mode_ptr), _mode.size(), (_mode.capacity()*sizeof(mode_ptr)));
|
||||
DEBUG_PRINTF("Data: %d*%d=%uB\n", sizeof(const char *), _modeData.size(), (_modeData.capacity()*sizeof(const char *)));
|
||||
DEBUG_PRINTF("Map: %d*%d=%uB\n", sizeof(uint16_t), (int)customMappingSize, customMappingSize*sizeof(uint16_t));
|
||||
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%d=%uB\n", sizeof(CRGB), (int)_length, _length*sizeof(CRGB));
|
||||
size = getLengthTotal();
|
||||
if (useLedsArray) DEBUG_PRINTF("Buffer: %d*%u=%uB\n", sizeof(CRGB), size, size*sizeof(CRGB));
|
||||
}
|
||||
#endif
|
||||
|
||||
void WS2812FX::loadCustomPalettes()
|
||||
{
|
||||
void WS2812FX::loadCustomPalettes() {
|
||||
byte tcp[72]; //support gradient palettes with up to 18 entries
|
||||
CRGBPalette16 targetPalette;
|
||||
customPalettes.clear(); // start fresh
|
||||
@@ -1880,85 +2035,103 @@ void WS2812FX::loadCustomPalettes()
|
||||
}
|
||||
|
||||
//load custom mapping table from JSON file (called from finalizeInit() or deserializeState())
|
||||
void WS2812FX::deserializeMap(uint8_t n) {
|
||||
// WLEDMM: also supports isMatrix
|
||||
bool WS2812FX::deserializeMap(uint8_t n) {
|
||||
// 2D support creates its own ledmap (on the fly) if a ledmap.json exists it will overwrite built one.
|
||||
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
if (n) sprintf(fileName +7, "%d", n);
|
||||
strcat(fileName, ".json");
|
||||
bool isFile = WLED_FS.exists(fileName);
|
||||
//WLEDMM: als support segment name ledmaps
|
||||
bool isFile = false;;
|
||||
if (n<10) {
|
||||
strcpy_P(fileName, PSTR("/ledmap"));
|
||||
if (n) sprintf(fileName +7, "%d", n); //WLEDMM: trick to not include 0 in ledmap.json
|
||||
strcat(fileName, ".json");
|
||||
isFile = WLED_FS.exists(fileName);
|
||||
} else { //WLEDM add segment name as ledmap.name
|
||||
uint8_t segment_index = 0;
|
||||
for (segment &seg : _segments) {
|
||||
if (n == 10 + segment_index && !isFile && seg.name != nullptr) {
|
||||
sprintf_P(fileName, PSTR("/%s.json"), seg.name);
|
||||
isFile = WLED_FS.exists(fileName);
|
||||
}
|
||||
if (isFile) break;
|
||||
segment_index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFile) {
|
||||
// erase custom mapping if selecting nonexistent ledmap.json (n==0)
|
||||
if (!n && customMappingTable != nullptr) {
|
||||
//WLEDMM: if isMatrix then not erase but back to matrix default
|
||||
if (isMatrix)
|
||||
setUpMatrix(true);
|
||||
else {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
}
|
||||
//WLEDM: doubt this is necessary as return false causes setupMatrix to deal with this
|
||||
if (!isMatrix && !n && customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
loadedLedmap = 0; //WLEDMM
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!requestJSONBufferLock(7)) return;
|
||||
|
||||
DEBUG_PRINT(F("Reading LED map from "));
|
||||
DEBUG_PRINTLN(fileName);
|
||||
if (!requestJSONBufferLock(7)) return false;
|
||||
|
||||
if (!readObjectFromFile(fileName, nullptr, &doc)) {
|
||||
releaseJSONBufferLock();
|
||||
return; //if file does not exist just exit
|
||||
return false; //if file does not exist just exit
|
||||
}
|
||||
|
||||
USER_PRINT(F("Reading LED map from ")); //WLEDMM use USER_PRINT
|
||||
USER_PRINTLN(fileName);
|
||||
|
||||
// erase old custom ledmap
|
||||
if (customMappingTable != nullptr) {
|
||||
customMappingSize = 0;
|
||||
delete[] customMappingTable;
|
||||
customMappingTable = nullptr;
|
||||
loadedLedmap = 0;
|
||||
}
|
||||
|
||||
JsonArray map = doc[F("map")];
|
||||
if (!map.isNull() && map.size()) { // not an empty map
|
||||
//WLEDMM: if isMatrix then customMap size is whole matrix
|
||||
#ifndef WLED_DISABLE_2D
|
||||
if (isMatrix)
|
||||
customMappingSize = Segment::maxWidth * Segment::maxHeight;
|
||||
else
|
||||
#endif
|
||||
customMappingSize = map.size();
|
||||
customMappingTable = new uint16_t[customMappingSize];
|
||||
for (uint16_t i=0; i<MIN(map.size(),customMappingSize); i++) {
|
||||
customMappingTable[i] = (uint16_t) map[i];
|
||||
|
||||
//WLEDMM: support ledmap file properties width and height
|
||||
if (doc[F("width")]>0 && doc[F("height")]>0) {
|
||||
Segment::maxWidth = doc[F("width")];;
|
||||
Segment::maxHeight = doc[F("height")];;
|
||||
resetSegments(true); //WLEDMM not makeAutoSegments() as we only want to change bounds
|
||||
}
|
||||
setUpMatrix(false); //WLEDMM: apply logical to physical mapping after the ledmap
|
||||
|
||||
customMappingSize = map.size();
|
||||
customMappingTable = new uint16_t[customMappingSize];
|
||||
|
||||
for (uint16_t i=0; i<customMappingSize; i++)
|
||||
customMappingTable[i] = (uint16_t) (map[i]<0 ? 0xFFFFU : map[i]);
|
||||
|
||||
loadedLedmap = n;
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINTF("Custom ledmap: %d\n", loadedLedmap);
|
||||
for (uint16_t i=0; i<customMappingSize; i++) {
|
||||
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
|
||||
DEBUG_PRINTF("%4d,", customMappingTable[i]);
|
||||
}
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
setUpMatrix(true); //WLEDMM: if no map then back to matrix default
|
||||
|
||||
releaseJSONBufferLock();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
WS2812FX* WS2812FX::instance = nullptr;
|
||||
|
||||
//Bus static member definition, would belong in bus_manager.cpp
|
||||
int16_t Bus::_cct = -1;
|
||||
uint8_t Bus::_cctBlend = 0;
|
||||
uint8_t Bus::_gAWM = 255;
|
||||
|
||||
const char JSON_mode_names[] PROGMEM = R"=====(["FX names moved"])=====";
|
||||
//WLEDMM netmindz ar palette add Audio responsive
|
||||
const char JSON_palette_names[] PROGMEM = R"=====([
|
||||
"Default","* Random Cycle","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",
|
||||
"Default","* Random Smooth ☾","* Color 1","* Colors 1&2","* Color Gradient","* Colors Only","Party","Cloud","Lava","Ocean",
|
||||
"Forest","Rainbow","Rainbow Bands","Sunset","Rivendell","Breeze","Red & Blue","Yellowout","Analogous","Splash",
|
||||
"Pastel","Sunset 2","Beach","Vintage","Departure","Landscape","Beech","Sherbet","Hult","Hult 64",
|
||||
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
|
||||
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
|
||||
"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf",
|
||||
"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide",
|
||||
"Candy2","Audio Responsive Ratio","Audio Responsive Hue","Audio Responsive Ramp"
|
||||
"Candy2","Audio Responsive Ratio ☾","Audio Responsive Hue ☾","Audio Responsive Ramp","* Random Cycle"
|
||||
])=====";
|
||||
|
||||
Reference in New Issue
Block a user