Merge pull request #4658 from wled/layers
Segment layers and better effect transitions (blending)
This commit is contained in:
229
wled00/cfg.cpp
229
wled00/cfg.cpp
@@ -6,6 +6,35 @@
|
||||
* The structure of the JSON is not to be considered an official API and may change without notice.
|
||||
*/
|
||||
|
||||
#ifndef PIXEL_COUNTS
|
||||
#define PIXEL_COUNTS DEFAULT_LED_COUNT
|
||||
#endif
|
||||
|
||||
#ifndef DATA_PINS
|
||||
#define DATA_PINS DEFAULT_LED_PIN
|
||||
#endif
|
||||
|
||||
#ifndef LED_TYPES
|
||||
#define LED_TYPES DEFAULT_LED_TYPE
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_LED_COLOR_ORDER
|
||||
#define DEFAULT_LED_COLOR_ORDER COL_ORDER_GRB //default to GRB
|
||||
#endif
|
||||
|
||||
static constexpr unsigned sumPinsRequired(const unsigned* current, size_t count) {
|
||||
return (count > 0) ? (Bus::getNumberOfPins(*current) + sumPinsRequired(current+1,count-1)) : 0;
|
||||
}
|
||||
|
||||
static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTypes, unsigned numPins ) {
|
||||
// Pins provided < pins required -> always invalid
|
||||
// Pins provided = pins required -> always valid
|
||||
// Pins provided > pins required -> valid if excess pins are a product of last type pins since it will be repeated
|
||||
return (sumPinsRequired(types, numTypes) > numPins) ? false :
|
||||
(numPins - sumPinsRequired(types, numTypes)) % Bus::getNumberOfPins(types[numTypes-1]) == 0;
|
||||
}
|
||||
|
||||
|
||||
//simple macro for ArduinoJSON's or syntax
|
||||
#define CJSON(a,b) a = b | a
|
||||
|
||||
@@ -20,7 +49,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
JsonObject ethernet = doc[F("eth")];
|
||||
CJSON(ethernetType, ethernet["type"]);
|
||||
// NOTE: Ethernet configuration takes priority over other use of pins
|
||||
@@ -136,7 +165,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
uint8_t cctBlending = hw_led[F("cb")] | Bus::getCCTBlend();
|
||||
Bus::setCCTBlend(cctBlending);
|
||||
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
|
||||
CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
CJSON(useParallelI2S, hw_led[F("prl")]);
|
||||
#endif
|
||||
@@ -146,12 +174,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject matrix = hw_led[F("matrix")];
|
||||
if (!matrix.isNull()) {
|
||||
strip.isMatrix = true;
|
||||
CJSON(strip.panels, matrix[F("mpc")]);
|
||||
unsigned numPanels = matrix[F("mpc")] | 1;
|
||||
numPanels = constrain(numPanels, 1, WLED_MAX_PANELS);
|
||||
strip.panel.clear();
|
||||
JsonArray panels = matrix[F("panels")];
|
||||
int s = 0;
|
||||
unsigned s = 0;
|
||||
if (!panels.isNull()) {
|
||||
strip.panel.reserve(max(1U,min((size_t)strip.panels,(size_t)WLED_MAX_PANELS))); // pre-allocate memory for panels
|
||||
strip.panel.reserve(numPanels); // pre-allocate default 8x8 panels
|
||||
for (JsonObject pnl : panels) {
|
||||
WS2812FX::Panel p;
|
||||
CJSON(p.bottomStart, pnl["b"]);
|
||||
@@ -163,30 +192,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(p.height, pnl["h"]);
|
||||
CJSON(p.width, pnl["w"]);
|
||||
strip.panel.push_back(p);
|
||||
if (++s >= WLED_MAX_PANELS || s >= strip.panels) break; // max panels reached
|
||||
if (++s >= numPanels) break; // max panels reached
|
||||
}
|
||||
} else {
|
||||
// fallback
|
||||
WS2812FX::Panel p;
|
||||
strip.panels = 1;
|
||||
p.height = p.width = 8;
|
||||
p.xOffset = p.yOffset = 0;
|
||||
p.options = 0;
|
||||
strip.panel.push_back(p);
|
||||
}
|
||||
// cannot call strip.setUpMatrix() here due to already locked JSON buffer
|
||||
strip.panel.shrink_to_fit(); // release unused memory (just in case)
|
||||
// cannot call strip.deserializeLedmap()/strip.setUpMatrix() here due to already locked JSON buffer
|
||||
//if (!fromFS) doInit2D = true; // if called at boot (fromFS==true), WLED::beginStrip() will take care of setting up matrix
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
|
||||
JsonArray ins = hw_led["ins"];
|
||||
|
||||
if (fromFS || !ins.isNull()) {
|
||||
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
|
||||
if (!ins.isNull()) {
|
||||
int s = 0; // bus iterator
|
||||
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
|
||||
|
||||
for (JsonObject elm : ins) {
|
||||
if (s >= WLED_MAX_BUSSES) break;
|
||||
if (s >= WLED_MAX_BUSSES) break; // only counts physical buses
|
||||
uint8_t pins[5] = {255, 255, 255, 255, 255};
|
||||
JsonArray pinArr = elm["pin"];
|
||||
if (pinArr.size() == 0) continue;
|
||||
@@ -215,11 +235,101 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
}
|
||||
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
|
||||
|
||||
//busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax)));
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
|
||||
busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, maPerLed, maMax);
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want
|
||||
}
|
||||
} else if (fromFS) {
|
||||
//if busses failed to load, add default (fresh install, FS issue, ...)
|
||||
BusManager::removeAll();
|
||||
busConfigs.clear();
|
||||
|
||||
DEBUG_PRINTLN(F("No busses, init default"));
|
||||
constexpr unsigned defDataTypes[] = {LED_TYPES};
|
||||
constexpr unsigned defDataPins[] = {DATA_PINS};
|
||||
constexpr unsigned defCounts[] = {PIXEL_COUNTS};
|
||||
constexpr unsigned defNumTypes = (sizeof(defDataTypes) / sizeof(defDataTypes[0]));
|
||||
constexpr unsigned defNumPins = (sizeof(defDataPins) / sizeof(defDataPins[0]));
|
||||
constexpr unsigned defNumCounts = (sizeof(defCounts) / sizeof(defCounts[0]));
|
||||
|
||||
static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins),
|
||||
"The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES");
|
||||
|
||||
unsigned mem = 0;
|
||||
unsigned pinsIndex = 0;
|
||||
unsigned digitalCount = 0;
|
||||
for (unsigned i = 0; i < WLED_MAX_BUSSES; i++) {
|
||||
uint8_t defPin[OUTPUT_MAX_PINS];
|
||||
// if we have less types than requested outputs and they do not align, use last known type to set current type
|
||||
unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1];
|
||||
unsigned busPins = Bus::getNumberOfPins(dataType);
|
||||
|
||||
// if we need more pins than available all outputs have been configured
|
||||
if (pinsIndex + busPins > defNumPins) break;
|
||||
|
||||
// Assign all pins first so we can check for conflicts on this bus
|
||||
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j];
|
||||
|
||||
for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) {
|
||||
bool validPin = true;
|
||||
// When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware
|
||||
// i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc.
|
||||
// Pin should not be already allocated, read/only or defined for current bus
|
||||
while (PinManager::isPinAllocated(defPin[j]) || !PinManager::isPinOk(defPin[j],true)) {
|
||||
if (validPin) {
|
||||
DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output."));
|
||||
defPin[j] = 1; // start with GPIO1 and work upwards
|
||||
validPin = false;
|
||||
} else if (defPin[j] < WLED_NUM_PINS) {
|
||||
defPin[j]++;
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("No available pins left! Can't configure output."));
|
||||
break;
|
||||
}
|
||||
// is the newly assigned pin already defined or used previously?
|
||||
// try next in line until there are no clashes or we run out of pins
|
||||
bool clash;
|
||||
do {
|
||||
clash = false;
|
||||
// check for conflicts on current bus
|
||||
for (const auto &pin : defPin) {
|
||||
if (&pin != &defPin[j] && pin == defPin[j]) {
|
||||
clash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We already have a clash on current bus, no point checking next buses
|
||||
if (!clash) {
|
||||
// check for conflicts in defined pins
|
||||
for (const auto &pin : defDataPins) {
|
||||
if (pin == defPin[j]) {
|
||||
clash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clash) defPin[j]++;
|
||||
if (defPin[j] >= WLED_NUM_PINS) break;
|
||||
} while (clash);
|
||||
}
|
||||
}
|
||||
pinsIndex += busPins;
|
||||
|
||||
// if we have less counts than pins and they do not align, use last known count to set current count
|
||||
unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1];
|
||||
unsigned start = 0;
|
||||
// analog always has length 1
|
||||
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
|
||||
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0);
|
||||
mem += defCfg.memUsage(Bus::isDigital(dataType) && !Bus::is2Pin(dataType) ? digitalCount++ : 0);
|
||||
if (mem > MAX_LED_MEMORY) {
|
||||
DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)dataType, (int)count, digitalCount);
|
||||
break;
|
||||
}
|
||||
busConfigs.push_back(defCfg); // use push_back for simplification as we needed defCfg to calculate memory usage
|
||||
doInitBusses = true; // finalization done in beginStrip()
|
||||
}
|
||||
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::memUsage());
|
||||
}
|
||||
if (hw_led["rev"] && BusManager::getNumBusses()) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
|
||||
|
||||
@@ -308,30 +418,28 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
}
|
||||
} else {
|
||||
} else if (fromFS) {
|
||||
// new install/missing configuration (button 0 has defaults)
|
||||
if (fromFS) {
|
||||
// relies upon only being called once with fromFS == true, which is currently true.
|
||||
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
|
||||
if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
|
||||
btnPin[s] = -1;
|
||||
buttonType[s] = BTN_TYPE_NONE;
|
||||
}
|
||||
if (btnPin[s] >= 0) {
|
||||
if (disablePullUp) {
|
||||
pinMode(btnPin[s], INPUT);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
macroButton[s] = 0;
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
// relies upon only being called once with fromFS == true, which is currently true.
|
||||
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
|
||||
if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
|
||||
btnPin[s] = -1;
|
||||
buttonType[s] = BTN_TYPE_NONE;
|
||||
}
|
||||
if (btnPin[s] >= 0) {
|
||||
if (disablePullUp) {
|
||||
pinMode(btnPin[s], INPUT);
|
||||
} else {
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
macroButton[s] = 0;
|
||||
macroLongPress[s] = 0;
|
||||
macroDoublePress[s] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,8 +516,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
|
||||
JsonObject light = doc[F("light")];
|
||||
CJSON(briMultiplier, light[F("scale-bri")]);
|
||||
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
||||
CJSON(paletteBlend, light[F("pal-mode")]);
|
||||
CJSON(strip.autoSegments, light[F("aseg")]);
|
||||
CJSON(useRainbowWheel, light[F("rw")]);
|
||||
|
||||
CJSON(gammaCorrectVal, light["gc"]["val"]); // default 2.8
|
||||
float light_gc_bri = light["gc"]["bri"];
|
||||
@@ -666,11 +775,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
static const char s_cfg_json[] PROGMEM = "/cfg.json";
|
||||
|
||||
void deserializeConfigFromFS() {
|
||||
bool success = deserializeConfigSec();
|
||||
[[maybe_unused]] bool success = deserializeConfigSec();
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
if (!success) { //if file does not exist, try reading from EEPROM
|
||||
deEEPSettings();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -679,23 +787,6 @@ void deserializeConfigFromFS() {
|
||||
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
|
||||
|
||||
success = readObjectFromFile(s_cfg_json, nullptr, pDoc);
|
||||
if (!success) { // if file does not exist, optionally try reading from EEPROM and then save defaults to FS
|
||||
releaseJSONBufferLock();
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
deEEPSettings();
|
||||
#endif
|
||||
|
||||
// save default values to /cfg.json
|
||||
// call readFromConfig() with an empty object so that usermods can initialize to defaults prior to saving
|
||||
JsonObject empty = JsonObject();
|
||||
UsermodManager::readFromConfig(empty);
|
||||
serializeConfigToFS();
|
||||
// init Ethernet (in case default type is set at compile time)
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
initEthernet();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: This routine deserializes *and* applies the configuration
|
||||
// Therefore, must also initialize ethernet from this function
|
||||
@@ -821,14 +912,13 @@ void serializeConfig(JsonObject root) {
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = strip.getLengthTotal(); //provided for compatibility on downgrade and per-output ABL
|
||||
hw_led[F("maxpwr")] = BusManager::ablMilliampsMax();
|
||||
hw_led[F("ledma")] = 0; // no longer used
|
||||
// hw_led[F("ledma")] = 0; // no longer used
|
||||
hw_led["cct"] = strip.correctWB;
|
||||
hw_led[F("cr")] = strip.cctFromRgb;
|
||||
hw_led[F("ic")] = cctICused;
|
||||
hw_led[F("cb")] = Bus::getCCTBlend();
|
||||
hw_led["fps"] = strip.getTargetFps();
|
||||
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
|
||||
hw_led[F("ld")] = useGlobalLedBuffer;
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
hw_led[F("prl")] = BusManager::hasParallelOutput();
|
||||
#endif
|
||||
@@ -837,7 +927,7 @@ void serializeConfig(JsonObject root) {
|
||||
// 2D Matrix Settings
|
||||
if (strip.isMatrix) {
|
||||
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
|
||||
matrix[F("mpc")] = strip.panels;
|
||||
matrix[F("mpc")] = strip.panel.size();
|
||||
JsonArray panels = matrix.createNestedArray(F("panels"));
|
||||
for (size_t i = 0; i < strip.panel.size(); i++) {
|
||||
JsonObject pnl = panels.createNestedObject();
|
||||
@@ -947,8 +1037,9 @@ void serializeConfig(JsonObject root) {
|
||||
|
||||
JsonObject light = root.createNestedObject(F("light"));
|
||||
light[F("scale-bri")] = briMultiplier;
|
||||
light[F("pal-mode")] = strip.paletteBlend;
|
||||
light[F("pal-mode")] = paletteBlend;
|
||||
light[F("aseg")] = strip.autoSegments;
|
||||
light[F("rw")] = useRainbowWheel;
|
||||
|
||||
JsonObject light_gc = light.createNestedObject("gc");
|
||||
light_gc["bri"] = (gammaCorrectBri) ? gammaCorrectVal : 1.0f; // keep compatibility
|
||||
|
||||
Reference in New Issue
Block a user