Merge branch 'filesystem' into mergefs-201112
This commit is contained in:
commit
e9d9760e16
@ -124,7 +124,7 @@ build_flags = -g -w -DMQTT_MAX_PACKET_SIZE=1024 -DPIO_FRAMEWORK_ARDUINO_LWIP_HIG
|
||||
-D DECODE_LG=true
|
||||
|
||||
build_flags_esp8266 = ${common.build_flags} -DESP8266
|
||||
build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32
|
||||
build_flags_esp32 = ${common.build_flags} -DARDUINO_ARCH_ESP32 -DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
|
||||
# enables all features for travis CI
|
||||
build_flags_all_features =
|
||||
@ -138,6 +138,8 @@ build_flags_all_features =
|
||||
|
||||
ldscript_512k = eagle.flash.512k.ld ;for older versions change this to eagle.flash.512k0.ld
|
||||
ldscript_1m0m = eagle.flash.1m.ld ;for older versions change this to eagle.flash.1m0.ld
|
||||
ldscript_1m128k = eagle.flash.1m128.ld
|
||||
ldscript_2m512k = eagle.flash.2m512.ld
|
||||
ldscript_2m1m = eagle.flash.2m1m.ld
|
||||
ldscript_4m1m = eagle.flash.4m1m.ld
|
||||
ldscript_4m3m = eagle.flash.4m3m.ld
|
||||
@ -165,19 +167,20 @@ lib_extra_dirs =
|
||||
lib_compat_mode = strict
|
||||
lib_deps =
|
||||
FastLED@3.3.2
|
||||
#NeoPixelBus@2.5.7
|
||||
https://github.com/Makuna/NeoPixelBus
|
||||
NeoPixelBus@2.6.0
|
||||
ESPAsyncTCP@1.2.0
|
||||
ESPAsyncUDP
|
||||
AsyncTCP@1.0.3
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer
|
||||
IRremoteESP8266@2.7.3
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
https://github.com/Aircoookie/ESPAsyncWebServer.git@~2.0.0
|
||||
#For use of the TTGO T-Display ESP32 Module with integrated TFT display uncomment the following line
|
||||
#TFT_eSPI
|
||||
#For use SSD1306 OLED display uncomment following
|
||||
#U8g2@~2.27.2
|
||||
#For Dallas sensor uncomment following 2 lines
|
||||
#OneWire@~2.3.5
|
||||
#milesburton/DallasTemperature@^3.9.0
|
||||
#For BME280 sensor uncomment following
|
||||
#BME280@~3.0.0
|
||||
lib_ignore =
|
||||
@ -212,7 +215,7 @@ build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_ALEXA -D WLED_DISABL
|
||||
[env:esp01_1m_full]
|
||||
board = esp01_1m
|
||||
platform = ${common.platform_wled_default}
|
||||
board_build.ldscript = ${common.ldscript_1m0m}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_flags = ${common.build_flags_esp8266} -D WLED_DISABLE_OTA
|
||||
|
||||
[env:esp07]
|
||||
@ -227,6 +230,7 @@ platform = ${common.platform_wled_default}
|
||||
upload_speed = 921600
|
||||
board_build.ldscript = ${common.ldscript_4m1m}
|
||||
build_flags = ${common.build_flags_esp8266}
|
||||
monitor_filters = esp8266_exception_decoder
|
||||
|
||||
[env:heltec_wifi_kit_8]
|
||||
board = d1_mini
|
||||
@ -379,7 +383,7 @@ build_flags = ${common.build_flags_esp8266} ${common.debug_flags} ${common.build
|
||||
|
||||
[env:travis_esp32]
|
||||
extends = env:esp32dev
|
||||
build_type = debug
|
||||
; build_type = debug
|
||||
build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_flags_all_features}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -103,7 +103,43 @@ class MyExampleUsermod : public Usermod {
|
||||
userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value
|
||||
//if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
||||
*
|
||||
* CAUTION: serializeConfig() will initiate a filesystem write operation.
|
||||
* It might cause the LEDs to stutter and will cause flash wear if called too often.
|
||||
* Use it sparingly and always in the loop, never in network callbacks!
|
||||
*
|
||||
* addToConfig() will also not yet add your setting to one of the settings pages automatically.
|
||||
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
|
||||
*
|
||||
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
|
||||
*/
|
||||
void addToConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject("exampleUsermod");
|
||||
top["great"] = userVar0; //save this var persistently whenever settings are saved
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
||||
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
|
||||
*
|
||||
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
|
||||
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
|
||||
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
|
||||
*/
|
||||
void readFromConfig(JsonObject& root)
|
||||
{
|
||||
JsonObject top = root["top"];
|
||||
userVar0 = top["great"] | 42; //The value right of the pipe "|" is the default value in case your setting was not present in cfg.json (e.g. first boot)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
||||
|
@ -2726,7 +2726,7 @@ uint16_t WS2812FX::candle(bool multi)
|
||||
|
||||
//max. flicker range controlled by intensity
|
||||
uint8_t valrange = SEGMENT.intensity;
|
||||
uint8_t rndval = valrange >> 1;
|
||||
uint8_t rndval = valrange >> 1; //max 127
|
||||
|
||||
//step (how much to move closer to target per frame) coarsely set by speed
|
||||
uint8_t speedFactor = 4;
|
||||
@ -2763,9 +2763,9 @@ uint16_t WS2812FX::candle(bool multi)
|
||||
}
|
||||
|
||||
if (newTarget) {
|
||||
s_target = random8(rndval) + random8(rndval);
|
||||
s_target = random8(rndval) + random8(rndval); //between 0 and rndval*2 -2 = 252
|
||||
if (s_target < (rndval >> 1)) s_target = (rndval >> 1) + random8(rndval);
|
||||
uint8_t offset = (255 - valrange) >> 1;
|
||||
uint8_t offset = (255 - valrange);
|
||||
s_target += offset;
|
||||
|
||||
uint8_t dif = (s_target > s) ? s_target - s : s - s_target;
|
||||
|
@ -50,9 +50,9 @@
|
||||
/* each segment uses 52 bytes of SRAM memory, so if you're application fails because of
|
||||
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
|
||||
#ifdef ESP8266
|
||||
#define MAX_NUM_SEGMENTS 10
|
||||
#define MAX_NUM_SEGMENTS 12
|
||||
#else
|
||||
#define MAX_NUM_SEGMENTS 10
|
||||
#define MAX_NUM_SEGMENTS 16
|
||||
#endif
|
||||
|
||||
/* How much data bytes all segments combined may allocate */
|
||||
@ -452,6 +452,7 @@ class WS2812FX {
|
||||
setRange(uint16_t i, uint16_t i2, uint32_t col),
|
||||
setShowCallback(show_callback cb),
|
||||
setTransitionMode(bool t),
|
||||
calcGammaTable(float),
|
||||
trigger(void),
|
||||
setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t grouping = 0, uint8_t spacing = 0),
|
||||
resetSegments(),
|
||||
@ -485,6 +486,7 @@ class WS2812FX {
|
||||
//getFirstSelectedSegment(void),
|
||||
getMainSegmentId(void),
|
||||
gamma8(uint8_t),
|
||||
gamma8_cal(uint8_t, float),
|
||||
get_random_wheel_index(uint8_t);
|
||||
|
||||
int8_t
|
||||
|
@ -965,8 +965,8 @@ void WS2812FX::setRgbwPwm(void) {
|
||||
void WS2812FX::setRgbwPwm() {}
|
||||
#endif
|
||||
|
||||
//gamma 2.4 lookup table used for color correction
|
||||
const byte gammaT[] = {
|
||||
//gamma 2.8 lookup table used for color correction
|
||||
byte gammaT[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
|
||||
@ -984,6 +984,17 @@ const byte gammaT[] = {
|
||||
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
|
||||
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
|
||||
|
||||
uint8_t WS2812FX::gamma8_cal(uint8_t b, float gamma) {
|
||||
return (int)(pow((float)b / 255.0, gamma) * 255 + 0.5);
|
||||
}
|
||||
|
||||
void WS2812FX::calcGammaTable(float gamma)
|
||||
{
|
||||
for (uint16_t i = 0; i < 256; i++) {
|
||||
gammaT[i] = gamma8_cal(i, gamma);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t WS2812FX::gamma8(uint8_t b)
|
||||
{
|
||||
return gammaT[b];
|
||||
|
@ -46,7 +46,7 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
bri = briLast;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
}
|
||||
} else applyMacro(macroAlexaOn);
|
||||
} else applyPreset(macroAlexaOn);
|
||||
} else if (m == EspalexaDeviceProperty::off)
|
||||
{
|
||||
if (!macroAlexaOff)
|
||||
@ -57,7 +57,7 @@ void onAlexaChange(EspalexaDevice* dev)
|
||||
bri = 0;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_ALEXA);
|
||||
}
|
||||
} else applyMacro(macroAlexaOff);
|
||||
} else applyPreset(macroAlexaOff);
|
||||
} else if (m == EspalexaDeviceProperty::bri)
|
||||
{
|
||||
bri = espalexaDevice->getValue();
|
||||
|
@ -11,7 +11,7 @@ void shortPressAction()
|
||||
toggleOnOff();
|
||||
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
|
||||
} else {
|
||||
applyMacro(macroButton);
|
||||
applyPreset(macroButton);
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ void handleButton()
|
||||
{
|
||||
if (!buttonLongPressed)
|
||||
{
|
||||
if (macroLongPress) {applyMacro(macroLongPress);}
|
||||
if (macroLongPress) {applyPreset(macroLongPress);}
|
||||
else _setRandomColor(false,true);
|
||||
|
||||
buttonLongPressed = true;
|
||||
@ -62,7 +62,7 @@ void handleButton()
|
||||
else if (!buttonLongPressed) { //short press
|
||||
if (macroDoublePress)
|
||||
{
|
||||
if (doublePress) applyMacro(macroDoublePress);
|
||||
if (doublePress) applyPreset(macroDoublePress);
|
||||
else buttonWaitTime = millis();
|
||||
} else shortPressAction();
|
||||
}
|
||||
|
674
wled00/cfg.cpp
Normal file
674
wled00/cfg.cpp
Normal file
@ -0,0 +1,674 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Serializes and parses the cfg.json and wsec.json settings files, stored in internal FS.
|
||||
* The structure of the JSON is not to be considered an official API and may change without notice.
|
||||
*/
|
||||
|
||||
//simple macro for ArduinoJSON's or syntax
|
||||
#define CJSON(a,b) a = b | a
|
||||
|
||||
void getStringFromJson(char* dest, const char* src, size_t len) {
|
||||
if (src != nullptr) strlcpy(dest, src, len);
|
||||
}
|
||||
|
||||
void deserializeConfig() {
|
||||
bool fromeep = false;
|
||||
bool success = deserializeConfigSec();
|
||||
if (!success) { //if file does not exist, try reading from EEPROM
|
||||
deEEPSettings();
|
||||
fromeep = true;
|
||||
}
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
|
||||
|
||||
success = readObjectFromFile("/cfg.json", nullptr, &doc);
|
||||
if (!success) { //if file does not exist, try reading from EEPROM
|
||||
if (!fromeep) deEEPSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
//deserializeJson(doc, json);
|
||||
|
||||
//int rev_major = doc[F("rev")][0]; // 1
|
||||
//int rev_minor = doc[F("rev")][1]; // 0
|
||||
|
||||
//long vid = doc[F("vid")]; // 2010020
|
||||
|
||||
JsonObject id = doc[F("id")];
|
||||
getStringFromJson(cmDNS, id[F("mdns")], 33);
|
||||
getStringFromJson(serverDescription, id[F("name")], 33);
|
||||
getStringFromJson(alexaInvocationName, id[F("inv")], 33);
|
||||
|
||||
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
|
||||
getStringFromJson(clientSSID, nw_ins_0[F("ssid")], 33);
|
||||
//int nw_ins_0_pskl = nw_ins_0[F("pskl")];
|
||||
//The WiFi PSK is normally not contained in the regular file for security reasons.
|
||||
//If it is present however, we will use it
|
||||
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
|
||||
|
||||
JsonArray nw_ins_0_ip = nw_ins_0[F("ip")];
|
||||
JsonArray nw_ins_0_gw = nw_ins_0[F("gw")];
|
||||
JsonArray nw_ins_0_sn = nw_ins_0[F("sn")];
|
||||
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
CJSON(staticIP[i], nw_ins_0_ip[i]);
|
||||
CJSON(staticGateway[i], nw_ins_0_gw[i]);
|
||||
CJSON(staticSubnet[i], nw_ins_0_sn[i]);
|
||||
}
|
||||
|
||||
JsonObject ap = doc[F("ap")];
|
||||
getStringFromJson(apSSID, ap[F("ssid")], 33);
|
||||
getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security
|
||||
//int ap_pskl = ap[F("pskl")];
|
||||
|
||||
CJSON(apChannel, ap[F("chan")]);
|
||||
if (apChannel > 13 || apChannel < 1) apChannel = 1;
|
||||
|
||||
CJSON(apHide, ap[F("hide")]);
|
||||
if (apHide > 1) apHide = 1;
|
||||
|
||||
CJSON(apBehavior, ap[F("behav")]);
|
||||
|
||||
/*
|
||||
JsonArray ap_ip = ap[F("ip")];
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
apIP[i] = ap_ip;
|
||||
}*/
|
||||
|
||||
noWifiSleep = doc[F("wifi")][F("sleep")] | !noWifiSleep; // inverted
|
||||
noWifiSleep = !noWifiSleep;
|
||||
//int wifi_phy = doc[F("wifi")][F("phy")]; //force phy mode n?
|
||||
|
||||
JsonObject hw = doc[F("hw")];
|
||||
|
||||
JsonObject hw_led = hw[F("led")];
|
||||
CJSON(ledCount, hw_led[F("total")]);
|
||||
if (ledCount > MAX_LEDS) ledCount = MAX_LEDS;
|
||||
|
||||
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
|
||||
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
|
||||
CJSON(strip.reverseMode, hw_led[F("rev")]);
|
||||
|
||||
JsonObject hw_led_ins_0 = hw_led[F("ins")][0];
|
||||
//bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true
|
||||
//int hw_led_ins_0_start = hw_led_ins_0[F("start")]; // 0
|
||||
//int hw_led_ins_0_len = hw_led_ins_0[F("len")]; // 1200
|
||||
|
||||
//int hw_led_ins_0_pin_0 = hw_led_ins_0[F("pin")][0]; // 2
|
||||
|
||||
strip.colorOrder = hw_led_ins_0[F("order")];
|
||||
//bool hw_led_ins_0_rev = hw_led_ins_0[F("rev")]; // false
|
||||
skipFirstLed = hw_led_ins_0[F("skip")]; // 0
|
||||
//int hw_led_ins_0_type = hw_led_ins_0[F("type")]; // 2*/
|
||||
|
||||
JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
|
||||
buttonEnabled = hw_btn_ins_0[F("en")] | buttonEnabled;
|
||||
|
||||
//int hw_btn_ins_0_pin_0 = hw_btn_ins_0[F("pin")][0]; // 0
|
||||
|
||||
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0[F("macros")];
|
||||
CJSON(macroButton, hw_btn_ins_0_macros[0]);
|
||||
CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
|
||||
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
|
||||
|
||||
//int hw_btn_ins_0_type = hw_btn_ins_0[F("type")]; // 0
|
||||
|
||||
//int hw_ir_pin = hw[F("ir")][F("pin")]; // 4
|
||||
CJSON(irEnabled, hw[F("ir")][F("type")]); // 0
|
||||
|
||||
//int hw_relay_pin = hw[F("relay")][F("pin")]; // 12
|
||||
//bool hw_relay_rev = hw[F("relay")][F("rev")]; // false
|
||||
|
||||
//int hw_status_pin = hw[F("status")][F("pin")]; // -1
|
||||
|
||||
JsonObject light = doc[F("light")];
|
||||
CJSON(briMultiplier, light[F("scale-bri")]);
|
||||
CJSON(strip.paletteBlend, light[F("pal-mode")]);
|
||||
|
||||
float light_gc_bri = light[F("gc")]["bri"];
|
||||
float light_gc_col = light[F("gc")][F("col")]; // 2.8
|
||||
if (light_gc_bri > 1.5) strip.gammaCorrectBri = true;
|
||||
else if (light_gc_bri > 0.5) strip.gammaCorrectBri = false;
|
||||
if (light_gc_col > 1.5) strip.gammaCorrectCol = true;
|
||||
else if (light_gc_col > 0.5) strip.gammaCorrectCol = false;
|
||||
|
||||
JsonObject light_tr = light[F("tr")];
|
||||
CJSON(fadeTransition, light_tr[F("mode")]);
|
||||
int tdd = light_tr[F("dur")] | -1;
|
||||
if (tdd >= 0) transitionDelayDefault = tdd * 100;
|
||||
CJSON(strip.paletteFade, light_tr[F("pal")]);
|
||||
|
||||
JsonObject light_nl = light["nl"];
|
||||
CJSON(nightlightMode, light_nl[F("mode")]);
|
||||
CJSON(nightlightDelayMinsDefault, light_nl[F("dur")]);
|
||||
nightlightDelayMins = nightlightDelayMinsDefault;
|
||||
|
||||
CJSON(nightlightTargetBri, light_nl[F("tbri")]);
|
||||
CJSON(macroNl, light_nl[F("macro")]);
|
||||
|
||||
JsonObject def = doc[F("def")];
|
||||
CJSON(bootPreset, def[F("ps")]);
|
||||
CJSON(turnOnAtBoot, def["on"]); // true
|
||||
CJSON(briS, def["bri"]); // 128
|
||||
if (briS == 0) briS = 255;
|
||||
|
||||
JsonObject def_cy = def[F("cy")];
|
||||
CJSON(presetCyclingEnabled, def_cy["on"]);
|
||||
|
||||
CJSON(presetCycleMin, def_cy[F("range")][0]);
|
||||
CJSON(presetCycleMax, def_cy[F("range")][1]);
|
||||
|
||||
tdd = def_cy[F("dur")] | -1;
|
||||
if (tdd >= 0) presetCycleTime = tdd * 100;
|
||||
|
||||
JsonObject interfaces = doc["if"];
|
||||
|
||||
JsonObject if_sync = interfaces[F("sync")];
|
||||
CJSON(udpPort, if_sync[F("port0")]); // 21324
|
||||
CJSON(udpPort2, if_sync[F("port1")]); // 65506
|
||||
|
||||
JsonObject if_sync_recv = if_sync[F("recv")];
|
||||
CJSON(receiveNotificationBrightness, if_sync_recv["bri"]);
|
||||
CJSON(receiveNotificationColor, if_sync_recv[F("col")]);
|
||||
CJSON(receiveNotificationEffects, if_sync_recv[F("fx")]);
|
||||
receiveNotifications = (receiveNotificationBrightness || receiveNotificationColor || receiveNotificationEffects);
|
||||
|
||||
JsonObject if_sync_send = if_sync[F("send")];
|
||||
CJSON(notifyDirectDefault, if_sync_send[F("dir")]);
|
||||
notifyDirect = notifyDirectDefault;
|
||||
CJSON(notifyButton, if_sync_send[F("btn")]);
|
||||
CJSON(notifyAlexa, if_sync_send[F("va")]);
|
||||
CJSON(notifyHue, if_sync_send[F("hue")]);
|
||||
CJSON(notifyMacro, if_sync_send[F("macro")]);
|
||||
CJSON(notifyTwice, if_sync_send[F("twice")]);
|
||||
|
||||
JsonObject if_live = interfaces[F("live")];
|
||||
CJSON(receiveDirect, if_live[F("en")]);
|
||||
CJSON(e131Port, if_live[F("port")]); // 5568
|
||||
CJSON(e131Multicast, if_live[F("mc")]);
|
||||
|
||||
JsonObject if_live_dmx = if_live[F("dmx")];
|
||||
CJSON(e131Universe, if_live_dmx[F("uni")]);
|
||||
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
|
||||
CJSON(DMXAddress, if_live_dmx[F("addr")]);
|
||||
CJSON(DMXMode, if_live_dmx[F("mode")]);
|
||||
|
||||
tdd = if_live[F("timeout")] | -1;
|
||||
if (tdd >= 0) realtimeTimeoutMs = tdd * 100;
|
||||
CJSON(arlsForceMaxBri, if_live[F("maxbri")]);
|
||||
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
|
||||
CJSON(arlsOffset, if_live[F("offset")]); // 0
|
||||
|
||||
CJSON(alexaEnabled, interfaces[F("va")][F("alexa")]); // false
|
||||
|
||||
CJSON(macroAlexaOn, interfaces[F("va")][F("macros")][0]);
|
||||
CJSON(macroAlexaOff, interfaces[F("va")][F("macros")][1]);
|
||||
|
||||
const char* apikey = interfaces[F("blynk")][F("token")] | "Hidden";
|
||||
tdd = strnlen(apikey, 36);
|
||||
if (tdd > 20 || tdd == 0)
|
||||
getStringFromJson(blynkApiKey, apikey, 36); //normally not present due to security
|
||||
|
||||
JsonObject if_mqtt = interfaces[F("mqtt")];
|
||||
CJSON(mqttEnabled, if_mqtt[F("en")]);
|
||||
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
|
||||
CJSON(mqttPort, if_mqtt[F("port")]); // 1883
|
||||
getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
|
||||
getStringFromJson(mqttPass, if_mqtt["psk"], 41); //normally not present due to security
|
||||
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
|
||||
|
||||
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
|
||||
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
|
||||
|
||||
JsonObject if_hue = interfaces[F("hue")];
|
||||
CJSON(huePollingEnabled, if_hue[F("en")]);
|
||||
CJSON(huePollLightId, if_hue[F("id")]);
|
||||
tdd = if_hue[F("iv")] | -1;
|
||||
if (tdd >= 2) huePollIntervalMs = tdd * 100;
|
||||
|
||||
JsonObject if_hue_recv = if_hue[F("recv")];
|
||||
CJSON(hueApplyOnOff, if_hue_recv["on"]);
|
||||
CJSON(hueApplyBri, if_hue_recv["bri"]);
|
||||
CJSON(hueApplyColor, if_hue_recv[F("col")]);
|
||||
|
||||
JsonArray if_hue_ip = if_hue[F("ip")];
|
||||
|
||||
for (byte i = 0; i < 4; i++)
|
||||
CJSON(hueIP[i], if_hue_ip[i]);
|
||||
|
||||
JsonObject if_ntp = interfaces[F("ntp")];
|
||||
CJSON(ntpEnabled, if_ntp[F("en")]);
|
||||
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
|
||||
CJSON(currentTimezone, if_ntp[F("tz")]);
|
||||
CJSON(utcOffsetSecs, if_ntp[F("offset")]);
|
||||
CJSON(useAMPM, if_ntp[F("ampm")]);
|
||||
|
||||
JsonObject ol = doc[F("ol")];
|
||||
CJSON(overlayDefault ,ol[F("clock")]); // 0
|
||||
CJSON(countdownMode, ol[F("cntdwn")]);
|
||||
overlayCurrent = overlayDefault;
|
||||
|
||||
JsonArray ol_cntdwn = ol[F("cntdwn")]; //[20,12,31,23,59,59]
|
||||
|
||||
//timed macro rules
|
||||
JsonObject tm = doc[F("timers")];
|
||||
JsonObject cntdwn = tm[F("cntdwn")];
|
||||
JsonArray cntdwn_goal = cntdwn[F("goal")];
|
||||
CJSON(countdownYear, cntdwn_goal[0]);
|
||||
CJSON(countdownMonth, cntdwn_goal[1]);
|
||||
CJSON(countdownDay, cntdwn_goal[2]);
|
||||
CJSON(countdownHour, cntdwn_goal[3]);
|
||||
CJSON(countdownMin, cntdwn_goal[4]);
|
||||
CJSON(countdownSec, cntdwn_goal[5]);
|
||||
CJSON(macroCountdown, cntdwn[F("macro")]);
|
||||
|
||||
JsonArray timers = tm[F("ins")];
|
||||
uint8_t it = 0;
|
||||
for (JsonObject timer : timers) {
|
||||
if (it > 7) break;
|
||||
CJSON(timerHours[it], timer[F("hour")]);
|
||||
CJSON(timerMinutes[it], timer[F("min")]);
|
||||
CJSON(timerMacro[it], timer[F("macro")]);
|
||||
|
||||
byte dowPrev = timerWeekday[it];
|
||||
bool actPrev = timerWeekday[it] & 0x01;
|
||||
CJSON(timerWeekday[it], timer[F("dow")]);
|
||||
if (timerWeekday[it] != dowPrev) { //present in JSON
|
||||
timerWeekday[it] <<= 1; //add active bit
|
||||
bool act = timer[F("en")] | actPrev;
|
||||
if (act) timerWeekday[it]++;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
JsonObject ota = doc["ota"];
|
||||
const char* pwd = ota["psk"]; //normally not present due to security
|
||||
|
||||
bool pwdCorrect = !otaLock; //always allow access if ota not locked
|
||||
if (pwd != nullptr && strncmp(otaPass, pwd, 33) == 0) pwdCorrect = true;
|
||||
|
||||
if (pwdCorrect) { //only accept these values from cfg.json if ota is unlocked (else from wsec.json)
|
||||
CJSON(otaLock, ota[F("lock")]);
|
||||
CJSON(wifiLock, ota[F("lock-wifi")]);
|
||||
CJSON(aOtaEnabled, ota[F("aota")]);
|
||||
getStringFromJson(otaPass, pwd, 33); //normally not present due to security
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
JsonObject dmx = doc["dmx"];
|
||||
CJSON(DMXChannels, dmx[F("chan")]);
|
||||
CJSON(DMXGap,dmx[F("gap")]);
|
||||
CJSON(DMXStart, dmx[F("start")]);
|
||||
CJSON(DMXStartLED,dmx[F("start-led")]);
|
||||
|
||||
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
|
||||
it = 0;
|
||||
for (int i : dmx_fixmap) {
|
||||
if (it > 14) break;
|
||||
DMXFixtureMap[i] = i;
|
||||
it++;
|
||||
}
|
||||
#endif
|
||||
|
||||
JsonObject usermods_settings = doc["um"];
|
||||
usermods.readFromConfig(usermods_settings);
|
||||
}
|
||||
|
||||
void serializeConfig() {
|
||||
serializeConfigSec();
|
||||
|
||||
DEBUG_PRINTLN(F("Writing settings to /cfg.json..."));
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
//{ //scope this to reduce stack size
|
||||
JsonArray rev = doc.createNestedArray("rev");
|
||||
rev.add(1); //major settings revision
|
||||
rev.add(0); //minor settings revision
|
||||
|
||||
doc[F("vid")] = VERSION;
|
||||
|
||||
JsonObject id = doc.createNestedObject("id");
|
||||
id[F("mdns")] = cmDNS;
|
||||
id[F("name")] = serverDescription;
|
||||
id[F("inv")] = alexaInvocationName;
|
||||
|
||||
JsonObject nw = doc.createNestedObject("nw");
|
||||
|
||||
JsonArray nw_ins = nw.createNestedArray("ins");
|
||||
|
||||
JsonObject nw_ins_0 = nw_ins.createNestedObject();
|
||||
nw_ins_0[F("ssid")] = clientSSID;
|
||||
nw_ins_0[F("pskl")] = strlen(clientPass);
|
||||
|
||||
JsonArray nw_ins_0_ip = nw_ins_0.createNestedArray("ip");
|
||||
JsonArray nw_ins_0_gw = nw_ins_0.createNestedArray("gw");
|
||||
JsonArray nw_ins_0_sn = nw_ins_0.createNestedArray("sn");
|
||||
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
nw_ins_0_ip.add(staticIP[i]);
|
||||
nw_ins_0_gw.add(staticGateway[i]);
|
||||
nw_ins_0_sn.add(staticSubnet[i]);
|
||||
}
|
||||
|
||||
JsonObject ap = doc.createNestedObject("ap");
|
||||
ap[F("ssid")] = apSSID;
|
||||
ap[F("pskl")] = strlen(apPass);
|
||||
ap[F("chan")] = apChannel;
|
||||
ap[F("behav")] = apBehavior;
|
||||
|
||||
JsonArray ap_ip = ap.createNestedArray("ip");
|
||||
ap_ip.add(4);
|
||||
ap_ip.add(3);
|
||||
ap_ip.add(2);
|
||||
ap_ip.add(1);
|
||||
|
||||
JsonObject wifi = doc.createNestedObject("wifi");
|
||||
wifi[F("sleep")] = !noWifiSleep;
|
||||
wifi[F("phy")] = 1;
|
||||
|
||||
JsonObject hw = doc.createNestedObject("hw");
|
||||
|
||||
JsonObject hw_led = hw.createNestedObject("led");
|
||||
hw_led[F("total")] = ledCount;
|
||||
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
|
||||
hw_led[F("ledma")] = strip.milliampsPerLed;
|
||||
hw_led[F("rev")] = strip.reverseMode;
|
||||
|
||||
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
|
||||
|
||||
JsonObject hw_led_ins_0 = hw_led_ins.createNestedObject();
|
||||
hw_led_ins_0[F("en")] = true;
|
||||
hw_led_ins_0[F("start")] = 0;
|
||||
hw_led_ins_0[F("len")] = ledCount;
|
||||
JsonArray hw_led_ins_0_pin = hw_led_ins_0.createNestedArray("pin");
|
||||
hw_led_ins_0_pin.add(LEDPIN);
|
||||
#ifdef DATAPIN
|
||||
hw_led_ins_0_pin.add(DATAPIN);
|
||||
#endif
|
||||
hw_led_ins_0[F("order")] = strip.colorOrder; //color order
|
||||
hw_led_ins_0[F("rev")] = false;
|
||||
hw_led_ins_0[F("skip")] = skipFirstLed ? 1 : 0;
|
||||
|
||||
//this is very crude and temporary
|
||||
byte ledType = TYPE_WS2812_RGB;
|
||||
if (strip.rgbwMode) ledType = TYPE_SK6812_RGBW;
|
||||
#ifdef USE_WS2801
|
||||
ledType = TYPE_WS2801;
|
||||
#endif
|
||||
#ifdef USE_APA102
|
||||
ledType = TYPE_APA102;
|
||||
#endif
|
||||
#ifdef USE_LPD8806
|
||||
ledType = TYPE_LPD8806;
|
||||
#endif
|
||||
#ifdef USE_P9813
|
||||
ledType = TYPE_P9813;
|
||||
#endif
|
||||
#ifdef USE_TM1814
|
||||
ledType = TYPE_TM1814;
|
||||
#endif
|
||||
|
||||
hw_led_ins_0[F("type")] = ledType;
|
||||
|
||||
JsonObject hw_btn = hw.createNestedObject("btn");
|
||||
|
||||
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
|
||||
|
||||
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
|
||||
hw_btn_ins_0[F("type")] = (buttonEnabled) ? BTN_TYPE_PUSH : BTN_TYPE_NONE;
|
||||
|
||||
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
|
||||
hw_btn_ins_0_pin.add(BTNPIN);
|
||||
|
||||
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
|
||||
hw_btn_ins_0_macros.add(macroButton);
|
||||
hw_btn_ins_0_macros.add(macroLongPress);
|
||||
hw_btn_ins_0_macros.add(macroDoublePress);
|
||||
|
||||
JsonObject hw_ir = hw.createNestedObject("ir");
|
||||
hw_ir[F("pin")] = IR_PIN;
|
||||
hw_ir[F("type")] = 0;
|
||||
|
||||
JsonObject hw_relay = hw.createNestedObject("relay");
|
||||
hw_relay[F("pin")] = RLYPIN;
|
||||
hw_relay[F("rev")] = (RLYMDE) ? false : true;
|
||||
JsonObject hw_status = hw.createNestedObject("status");
|
||||
hw_status[F("pin")] = -1;
|
||||
|
||||
JsonObject light = doc.createNestedObject("light");
|
||||
light[F("scale-bri")] = briMultiplier;
|
||||
light[F("pal-mode")] = strip.paletteBlend;
|
||||
|
||||
JsonObject light_gc = light.createNestedObject("gc");
|
||||
light_gc["bri"] = (strip.gammaCorrectBri) ? 2.8 : 1.0;
|
||||
light_gc[F("col")] = (strip.gammaCorrectCol) ? 2.8 : 1.0;
|
||||
|
||||
JsonObject light_tr = light.createNestedObject("tr");
|
||||
light_tr[F("mode")] = fadeTransition;
|
||||
light_tr[F("dur")] = transitionDelayDefault / 100;
|
||||
light_tr[F("pal")] = strip.paletteFade;
|
||||
|
||||
JsonObject light_nl = light.createNestedObject("nl");
|
||||
light_nl[F("mode")] = nightlightMode;
|
||||
light_nl[F("dur")] = nightlightDelayMinsDefault;
|
||||
light_nl[F("tbri")] = nightlightTargetBri;
|
||||
light_nl[F("macro")] = macroNl;
|
||||
|
||||
JsonObject def = doc.createNestedObject("def");
|
||||
def[F("ps")] = bootPreset;
|
||||
def["on"] = turnOnAtBoot;
|
||||
def["bri"] = briS;
|
||||
|
||||
//to be removed once preset cycles are presets
|
||||
if (saveCurrPresetCycConf) {
|
||||
JsonObject def_cy = def.createNestedObject("cy");
|
||||
def_cy["on"] = presetCyclingEnabled;
|
||||
|
||||
JsonArray def_cy_range = def_cy.createNestedArray("range");
|
||||
def_cy_range.add(presetCycleMin);
|
||||
def_cy_range.add(presetCycleMax);
|
||||
def_cy[F("dur")] = presetCycleTime / 100;
|
||||
}
|
||||
|
||||
JsonObject interfaces = doc.createNestedObject("if");
|
||||
|
||||
JsonObject if_sync = interfaces.createNestedObject("sync");
|
||||
if_sync[F("port0")] = udpPort;
|
||||
if_sync[F("port1")] = udpPort2;
|
||||
|
||||
JsonObject if_sync_recv = if_sync.createNestedObject("recv");
|
||||
if_sync_recv["bri"] = receiveNotificationBrightness;
|
||||
if_sync_recv[F("col")] = receiveNotificationColor;
|
||||
if_sync_recv[F("fx")] = receiveNotificationEffects;
|
||||
|
||||
JsonObject if_sync_send = if_sync.createNestedObject("send");
|
||||
if_sync_send[F("dir")] = notifyDirect;
|
||||
if_sync_send[F("btn")] = notifyButton;
|
||||
if_sync_send[F("va")] = notifyAlexa;
|
||||
if_sync_send[F("hue")] = notifyHue;
|
||||
if_sync_send[F("macro")] = notifyMacro;
|
||||
if_sync_send[F("twice")] = notifyTwice;
|
||||
|
||||
JsonObject if_live = interfaces.createNestedObject("live");
|
||||
if_live[F("en")] = receiveDirect;
|
||||
if_live[F("port")] = e131Port;
|
||||
if_live[F("mc")] = e131Multicast;
|
||||
|
||||
JsonObject if_live_dmx = if_live.createNestedObject("dmx");
|
||||
if_live_dmx[F("uni")] = e131Universe;
|
||||
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
|
||||
if_live_dmx[F("addr")] = DMXAddress;
|
||||
if_live_dmx[F("mode")] = DMXMode;
|
||||
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
||||
if_live[F("maxbri")] = arlsForceMaxBri;
|
||||
if_live[F("no-gc")] = arlsDisableGammaCorrection;
|
||||
if_live[F("offset")] = arlsOffset;
|
||||
|
||||
JsonObject if_va = interfaces.createNestedObject("va");
|
||||
if_va[F("alexa")] = alexaEnabled;
|
||||
|
||||
JsonArray if_va_macros = if_va.createNestedArray("macros");
|
||||
if_va_macros.add(macroAlexaOn);
|
||||
if_va_macros.add(macroAlexaOff);
|
||||
JsonObject if_blynk = interfaces.createNestedObject("blynk");
|
||||
if_blynk[F("token")] = strlen(blynkApiKey) ? "Hidden":"";
|
||||
|
||||
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
|
||||
if_mqtt[F("en")] = mqttEnabled;
|
||||
if_mqtt[F("broker")] = mqttServer;
|
||||
if_mqtt[F("port")] = mqttPort;
|
||||
if_mqtt[F("user")] = mqttUser;
|
||||
if_mqtt[F("pskl")] = strlen(mqttPass);
|
||||
if_mqtt[F("cid")] = mqttClientID;
|
||||
|
||||
JsonObject if_mqtt_topics = if_mqtt.createNestedObject("topics");
|
||||
if_mqtt_topics[F("device")] = mqttDeviceTopic;
|
||||
if_mqtt_topics[F("group")] = mqttGroupTopic;
|
||||
|
||||
JsonObject if_hue = interfaces.createNestedObject("hue");
|
||||
if_hue[F("en")] = huePollingEnabled;
|
||||
if_hue[F("id")] = huePollLightId;
|
||||
if_hue[F("iv")] = huePollIntervalMs / 100;
|
||||
|
||||
JsonObject if_hue_recv = if_hue.createNestedObject("recv");
|
||||
if_hue_recv["on"] = hueApplyOnOff;
|
||||
if_hue_recv["bri"] = hueApplyBri;
|
||||
if_hue_recv[F("col")] = hueApplyColor;
|
||||
|
||||
JsonArray if_hue_ip = if_hue.createNestedArray("ip");
|
||||
for (byte i = 0; i < 4; i++) {
|
||||
if_hue_ip.add(hueIP[i]);
|
||||
}
|
||||
|
||||
JsonObject if_ntp = interfaces.createNestedObject("ntp");
|
||||
if_ntp[F("en")] = ntpEnabled;
|
||||
if_ntp[F("host")] = ntpServerName;
|
||||
if_ntp[F("tz")] = currentTimezone;
|
||||
if_ntp[F("offset")] = utcOffsetSecs;
|
||||
if_ntp[F("ampm")] = useAMPM;
|
||||
|
||||
JsonObject ol = doc.createNestedObject("ol");
|
||||
ol[F("clock")] = overlayDefault;
|
||||
ol[F("cntdwn")] = countdownMode;
|
||||
|
||||
JsonObject timers = doc.createNestedObject("timers");
|
||||
|
||||
JsonObject cntdwn = timers.createNestedObject("cntdwn");
|
||||
JsonArray goal = cntdwn.createNestedArray("goal");
|
||||
goal.add(countdownYear); goal.add(countdownMonth); goal.add(countdownDay);
|
||||
goal.add(countdownHour); goal.add(countdownMin); goal.add(countdownSec);
|
||||
cntdwn[F("macro")] = macroCountdown;
|
||||
|
||||
JsonArray timers_ins = timers.createNestedArray("ins");
|
||||
|
||||
for (byte i = 0; i < 8; i++) {
|
||||
if (timerMacro[i] == 0 && timerHours[i] == 0 && timerMinutes[i] == 0) continue;
|
||||
JsonObject timers_ins0 = timers_ins.createNestedObject();
|
||||
timers_ins0[F("en")] = (timerWeekday[i] & 0x01);
|
||||
timers_ins0[F("hour")] = timerHours[i];
|
||||
timers_ins0[F("min")] = timerMinutes[i];
|
||||
timers_ins0[F("macro")] = timerMacro[i];
|
||||
timers_ins0[F("dow")] = timerWeekday[i] >> 1;
|
||||
}
|
||||
|
||||
JsonObject ota = doc.createNestedObject("ota");
|
||||
ota[F("lock")] = otaLock;
|
||||
ota[F("lock-wifi")] = wifiLock;
|
||||
ota[F("pskl")] = strlen(otaPass);
|
||||
ota[F("aota")] = aOtaEnabled;
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
JsonObject dmx = doc.createNestedObject("dmx");
|
||||
dmx[F("chan")] = DMXChannels;
|
||||
dmx[F("gap")] = DMXGap;
|
||||
dmx[F("start")] = DMXStart;
|
||||
dmx[F("start-led")] = DMXStartLED;
|
||||
|
||||
JsonArray dmx_fixmap = dmx.createNestedArray("fixmap");
|
||||
for (byte i = 0; i < 15; i++)
|
||||
dmx_fixmap.add(DMXFixtureMap[i]);
|
||||
#endif
|
||||
//}
|
||||
|
||||
JsonObject usermods_settings = doc.createNestedObject("um");
|
||||
usermods.addToConfig(usermods_settings);
|
||||
|
||||
File f = WLED_FS.open("/cfg.json", "w");
|
||||
if (f) serializeJson(doc, f);
|
||||
f.close();
|
||||
}
|
||||
|
||||
//settings in /wsec.json, not accessible via webserver, for passwords and tokens
|
||||
bool deserializeConfigSec() {
|
||||
DEBUG_PRINTLN(F("Reading settings from /wsec.json..."));
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
bool success = readObjectFromFile("/wsec.json", nullptr, &doc);
|
||||
if (!success) return false;
|
||||
|
||||
JsonObject nw_ins_0 = doc["nw"][F("ins")][0];
|
||||
getStringFromJson(clientPass, nw_ins_0["psk"], 65);
|
||||
|
||||
JsonObject ap = doc[F("ap")];
|
||||
getStringFromJson(apPass, ap["psk"] , 65);
|
||||
|
||||
JsonObject interfaces = doc["if"];
|
||||
|
||||
const char* apikey = interfaces["blynk"][F("token")] | "Hidden";
|
||||
int tdd = strnlen(apikey, 36);
|
||||
if (tdd > 20 || tdd == 0)
|
||||
getStringFromJson(blynkApiKey, apikey, 36);
|
||||
|
||||
JsonObject if_mqtt = interfaces[F("mqtt")];
|
||||
getStringFromJson(mqttPass, if_mqtt["psk"], 41);
|
||||
|
||||
getStringFromJson(hueApiKey, interfaces[F("hue")][F("key")], 47);
|
||||
|
||||
JsonObject ota = doc["ota"];
|
||||
getStringFromJson(otaPass, ota[F("pwd")], 33);
|
||||
CJSON(otaLock, ota[F("lock")]);
|
||||
CJSON(wifiLock, ota[F("lock-wifi")]);
|
||||
CJSON(aOtaEnabled, ota[F("aota")]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void serializeConfigSec() {
|
||||
DEBUG_PRINTLN(F("Writing settings to /wsec.json..."));
|
||||
|
||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||
|
||||
JsonObject nw = doc.createNestedObject("nw");
|
||||
|
||||
JsonArray nw_ins = nw.createNestedArray("ins");
|
||||
|
||||
JsonObject nw_ins_0 = nw_ins.createNestedObject();
|
||||
nw_ins_0["psk"] = clientPass;
|
||||
|
||||
JsonObject ap = doc.createNestedObject("ap");
|
||||
ap["psk"] = apPass;
|
||||
|
||||
JsonObject interfaces = doc.createNestedObject("if");
|
||||
JsonObject if_blynk = interfaces.createNestedObject("blynk");
|
||||
if_blynk[F("token")] = blynkApiKey;
|
||||
JsonObject if_mqtt = interfaces.createNestedObject("mqtt");
|
||||
if_mqtt["psk"] = mqttPass;
|
||||
JsonObject if_hue = interfaces.createNestedObject("hue");
|
||||
if_hue[F("key")] = hueApiKey;
|
||||
|
||||
JsonObject ota = doc.createNestedObject("ota");
|
||||
ota[F("pwd")] = otaPass;
|
||||
ota[F("lock")] = otaLock;
|
||||
ota[F("lock-wifi")] = wifiLock;
|
||||
ota[F("aota")] = aOtaEnabled;
|
||||
|
||||
File f = WLED_FS.open("/wsec.json", "w");
|
||||
if (f) serializeJson(doc, f);
|
||||
f.close();
|
||||
}
|
@ -72,20 +72,46 @@
|
||||
#define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels)
|
||||
#define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels)
|
||||
|
||||
//Light capability byte (unused)
|
||||
//Light capability byte (unused) 0bRRCCTTTT
|
||||
//bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior
|
||||
//bits 4/5: specifies the class of LED driver - 0b00 (dec. 0-15) unconfigured/reserved
|
||||
// - 0b01 (dec. 16-31) digital (data pin only)
|
||||
// - 0b10 (dec. 32-47) analog (PWM)
|
||||
// - 0b11 (dec. 48-63) digital (data + clock / SPI)
|
||||
//bits 6/7 are reserved and set to 0b00
|
||||
|
||||
#define TYPE_NONE 0 //light is not configured
|
||||
#define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light
|
||||
#define TYPE_WS2812_RGB 2
|
||||
#define TYPE_SK6812_RGBW 3
|
||||
#define TYPE_WS2812_WWA 4 //amber + warm + cold white
|
||||
#define TYPE_WS2801 5
|
||||
#define TYPE_ANALOG_1CH 6 //single channel PWM. Uses value of brightest RGBW channel
|
||||
#define TYPE_ANALOG_2CH 7 //analog WW + CW
|
||||
#define TYPE_ANALOG_3CH 8 //analog RGB
|
||||
#define TYPE_ANALOG_4CH 9 //analog RGBW
|
||||
#define TYPE_ANALOG_5CH 10 //analog RGB + WW + CW
|
||||
#define TYPE_APA102 11
|
||||
#define TYPE_LPD8806 12
|
||||
//Digital types (data pin only) (16-31)
|
||||
#define TYPE_WS2812_1CH 20 //white-only chips
|
||||
#define TYPE_WS2812_WWA 21 //amber + warm + cold white
|
||||
#define TYPE_WS2812_RGB 22
|
||||
#define TYPE_GS8608 23 //same driver as WS2812, but will require signal 2x per second (else displays test pattern)
|
||||
#define TYPE_WS2811_400KHZ 24 //half-speed WS2812 protocol, used by very old WS2811 units
|
||||
#define TYPE_SK6812_RGBW 30
|
||||
//"Analog" types (PWM) (32-47)
|
||||
#define TYPE_ONOFF 40 //binary output (relays etc.)
|
||||
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
|
||||
#define TYPE_ANALOG_2CH 42 //analog WW + CW
|
||||
#define TYPE_ANALOG_3CH 43 //analog RGB
|
||||
#define TYPE_ANALOG_4CH 44 //analog RGBW
|
||||
#define TYPE_ANALOG_5CH 45 //analog RGB + WW + CW
|
||||
//Digital types (data + clock / SPI) (48-63)
|
||||
#define TYPE_WS2801 50
|
||||
#define TYPE_APA102 51
|
||||
#define TYPE_LPD8806 52
|
||||
#define TYPE_P9813 53
|
||||
#define TYPE_TM1814 54
|
||||
|
||||
|
||||
//Button type
|
||||
#define BTN_TYPE_NONE 0
|
||||
#define BTN_TYPE_RESERVED 1
|
||||
#define BTN_TYPE_PUSH 2
|
||||
#define BTN_TYPE_PUSH_ACT_HIGH 3 //not implemented
|
||||
#define BTN_TYPE_SWITCH 4 //not implemented
|
||||
#define BTN_TYPE_SWITCH_ACT_HIGH 5 //not implemented
|
||||
|
||||
|
||||
//Hue error codes
|
||||
#define HUE_ERROR_INACTIVE 0
|
||||
@ -105,14 +131,21 @@
|
||||
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
|
||||
#define SEG_OPTION_TRANSITIONAL 7
|
||||
|
||||
// WLED Error modes
|
||||
#define ERR_NONE 0 // All good :)
|
||||
#define ERR_EEP_COMMIT 2 // Could not commit to EEPROM (wrong flash layout?)
|
||||
#define ERR_JSON 9 // JSON parsing failed (input too large?)
|
||||
#define ERR_FS_BEGIN 10 // Could not init filesystem (no partition?)
|
||||
#define ERR_FS_QUOTA 11 // The FS is full or the maximum file size is reached
|
||||
#define ERR_FS_PLOAD 12 // It was attempted to load a preset that does not exist
|
||||
#define ERR_FS_GENERAL 19 // A general unspecified filesystem error occured
|
||||
|
||||
//Timer mode types
|
||||
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness
|
||||
#define NL_MODE_FADE 1 //Fade to target brightness gradually
|
||||
#define NL_MODE_COLORFADE 2 //Fade to target brightness and secondary color gradually
|
||||
#define NL_MODE_SUN 3 //Sunrise/sunset. Target brightness is set immediately, then Sunrise effect is started. Max 60 min.
|
||||
|
||||
//EEPROM size
|
||||
#define EEPSIZE 2560 //Maximum is 4096
|
||||
|
||||
#define NTP_PACKET_SIZE 48
|
||||
|
||||
@ -127,6 +160,7 @@
|
||||
|
||||
#define ABL_MILLIAMPS_DEFAULT 850; // auto lower brightness to stay close to milliampere limit
|
||||
|
||||
|
||||
#define TOUCH_THRESHOLD 32 // limit to recognize a touch, higher value means more sensitive
|
||||
|
||||
// Size of buffer for API JSON object (increase for more segments)
|
||||
|
File diff suppressed because one or more lines are too long
@ -43,10 +43,10 @@
|
||||
}
|
||||
function BTa()
|
||||
{
|
||||
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
|
||||
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
|
||||
for (i=0;i<8;i++)
|
||||
{
|
||||
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"16\"></td>";
|
||||
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>";
|
||||
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
|
||||
}
|
||||
gId("TMT").innerHTML=ih;
|
||||
@ -134,32 +134,17 @@
|
||||
Countdown Goal:<br>
|
||||
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br>
|
||||
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br>
|
||||
<h3>Advanced Macros</h3>
|
||||
Define API macros here:<br>
|
||||
1: <input name="M1" maxlength="64"><br>
|
||||
2: <input name="M2" maxlength="64"><br>
|
||||
3: <input name="M3" maxlength="64"><br>
|
||||
4: <input name="M4" maxlength="64"><br>
|
||||
5: <input name="M5" maxlength="64"><br>
|
||||
6: <input name="M6" maxlength="64"><br>
|
||||
7: <input name="M7" maxlength="64"><br>
|
||||
8: <input name="M8" maxlength="64"><br>
|
||||
9: <input name="M9" maxlength="64"><br>
|
||||
10: <input name="M10" maxlength="64"><br>
|
||||
11: <input name="M11" maxlength="64"><br>
|
||||
12: <input name="M12" maxlength="64"><br>
|
||||
13: <input name="M13" maxlength="64"><br>
|
||||
14: <input name="M14" maxlength="64"><br>
|
||||
15: <input name="M15" maxlength="64"><br>
|
||||
16: <input name="M16" maxlength="64"><br><br>
|
||||
<i>Use 0 for the default action instead of a macro</i><br>
|
||||
Boot Macro: <input name="MB" type="number" min="0" max="16" required><br>
|
||||
Alexa On/Off Macros: <input name="A0" type="number" min="0" max="16" required> <input name="A1" type="number" min="0" max="16" required><br>
|
||||
Button short press macro: Macro: <input name="MP" type="number" min="0" max="16" required><br>
|
||||
Long Press: <input name="ML" type="number" min="0" max="16" required> Double press: <input name="MD" type="number" min="0" max="16" required><br>
|
||||
Countdown-Over Macro: <input name="MC" type="number" min="0" max="16" required><br>
|
||||
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16" required><br>
|
||||
Time-Controlled Macros:<br>
|
||||
<h3>Macro presets</h3>
|
||||
<b>Macros have moved!</b><br>
|
||||
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
|
||||
Just enter the preset id below!</i>
|
||||
<i>Use 0 for the default action instead of a preset</i><br>
|
||||
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br>
|
||||
Button short press Preset: Macro: <input name="MP" type="number" min="0" max="250" required><br>
|
||||
Long Press: <input name="ML" type="number" min="0" max="250" required> Double press: <input name="MD" type="number" min="0" max="250" required><br>
|
||||
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br>
|
||||
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br>
|
||||
<h3>Time-controlled presets</h3>
|
||||
<div style="display: inline-block">
|
||||
<table id="TMT">
|
||||
</table></div><hr>
|
||||
|
@ -24,15 +24,16 @@ void handleDDPPacket(e131_packet_t* p) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t offsetLeds = htonl(p->channelOffset) /3;
|
||||
uint16_t packetLeds = htons(p->dataLen) /3;
|
||||
uint32_t start = htonl(p->channelOffset) /3;
|
||||
start += DMXAddress /3;
|
||||
uint16_t stop = start + htons(p->dataLen) /3;
|
||||
uint8_t* data = p->data;
|
||||
uint16_t c = 0;
|
||||
if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP);
|
||||
|
||||
for (uint16_t i = offsetLeds; i < offsetLeds + packetLeds; i++) {
|
||||
for (uint16_t i = start; i < stop; i++) {
|
||||
setRealtimePixel(i, data[c++], data[c++], data[c++], 0);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,12 @@ bool isButtonPressed();
|
||||
void handleButton();
|
||||
void handleIO();
|
||||
|
||||
//cfg.cpp
|
||||
void deserializeConfig();
|
||||
bool deserializeConfigSec();
|
||||
void serializeConfig();
|
||||
void serializeConfigSec();
|
||||
|
||||
//colors.cpp
|
||||
void colorFromUint32(uint32_t in, bool secondary = false);
|
||||
void colorFromUint24(uint32_t in, bool secondary = false);
|
||||
@ -48,6 +54,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
|
||||
|
||||
//file.cpp
|
||||
bool handleFileRead(AsyncWebServerRequest*, String path);
|
||||
bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content);
|
||||
bool writeObjectToFile(const char* file, const char* key, JsonDocument* content);
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
|
||||
void updateFSInfo();
|
||||
void closeFile();
|
||||
|
||||
//hue.cpp
|
||||
void handleHue();
|
||||
@ -85,8 +97,8 @@ void handleIR();
|
||||
|
||||
void deserializeSegment(JsonObject elem, byte it);
|
||||
bool deserializeState(JsonObject root);
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id);
|
||||
void serializeState(JsonObject root);
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
|
||||
void serializeInfo(JsonObject root);
|
||||
void serveJson(AsyncWebServerRequest* request);
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
@ -135,11 +147,20 @@ void setCronixie();
|
||||
void _overlayCronixie();
|
||||
void _drawOverlayCronixie();
|
||||
|
||||
//playlist.cpp
|
||||
void loadPlaylist(JsonObject playlistObject);
|
||||
void handlePlaylist();
|
||||
|
||||
//presets.cpp
|
||||
bool applyPreset(byte index);
|
||||
void savePreset(byte index, bool persist = true, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
void deletePreset(byte index);
|
||||
|
||||
//set.cpp
|
||||
void _setRandomColor(bool _sec,bool fromButton=false);
|
||||
bool isAsterisksOnly(const char* str, byte maxLen);
|
||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req);
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=true);
|
||||
int getNumVal(const String* req, uint16_t pos);
|
||||
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
|
||||
|
||||
@ -158,6 +179,8 @@ class Usermod {
|
||||
virtual void addToJsonState(JsonObject& obj) {}
|
||||
virtual void addToJsonInfo(JsonObject& obj) {}
|
||||
virtual void readFromJsonState(JsonObject& obj) {}
|
||||
virtual void addToConfig(JsonObject& obj) {}
|
||||
virtual void readFromConfig(JsonObject& obj) {}
|
||||
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
||||
};
|
||||
|
||||
@ -176,6 +199,9 @@ class UsermodManager {
|
||||
void addToJsonInfo(JsonObject& obj);
|
||||
void readFromJsonState(JsonObject& obj);
|
||||
|
||||
void addToConfig(JsonObject& obj);
|
||||
void readFromConfig(JsonObject& obj);
|
||||
|
||||
bool add(Usermod* um);
|
||||
byte getModCount();
|
||||
};
|
||||
@ -189,18 +215,9 @@ void userConnected();
|
||||
void userLoop();
|
||||
|
||||
//wled_eeprom.cpp
|
||||
void commit();
|
||||
void clearEEPROM();
|
||||
void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len);
|
||||
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len);
|
||||
void saveSettingsToEEPROM();
|
||||
void loadSettingsFromEEPROM(bool first);
|
||||
void savedToPresets();
|
||||
bool applyPreset(byte index, bool loadBri = true);
|
||||
void savePreset(byte index, bool persist = true);
|
||||
void loadMacro(byte index, char* m);
|
||||
void applyMacro(byte index);
|
||||
void saveMacro(byte index, const String& mc, bool persist = true); //only commit on single save, not in settings
|
||||
void deEEP();
|
||||
void deEEPSettings();
|
||||
|
||||
//wled_serial.cpp
|
||||
void handleSerial();
|
||||
|
386
wled00/file.cpp
386
wled00/file.cpp
@ -4,15 +4,378 @@
|
||||
* Utility for SPIFFS filesystem
|
||||
*/
|
||||
|
||||
//filesystem
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
#include <FS.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "SPIFFS.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32 //FS info bare IDF function until FS wrapper is available for ESP32
|
||||
#if WLED_FS != LITTLEFS
|
||||
#include "esp_spiffs.h"
|
||||
#endif
|
||||
#include "SPIFFSEditor.h"
|
||||
#endif
|
||||
|
||||
#define FS_BUFSIZE 256
|
||||
|
||||
/*
|
||||
* Structural requirements for files managed by writeObjectToFile() and readObjectFromFile() utilities:
|
||||
* 1. File must be a string representation of a valid JSON object
|
||||
* 2. File must have '{' as first character
|
||||
* 3. There must not be any additional characters between a root-level key and its value object (e.g. space, tab, newline)
|
||||
* 4. There must not be any characters between an root object-separating ',' and the next object key string
|
||||
* 5. There may be any number of spaces, tabs, and/or newlines before such object-separating ','
|
||||
* 6. There must not be more than 5 consecutive spaces at any point except for those permitted in condition 5
|
||||
* 7. If it is desired to delete the first usable object (e.g. preset file), a dummy object '"0":{}' is inserted at the beginning.
|
||||
* It shall be disregarded by receiving software.
|
||||
* The reason for it is that deleting the first preset would require special code to handle commas between it and the 2nd preset
|
||||
*/
|
||||
|
||||
// There are no consecutive spaces longer than this in the file, so if more space is required, findSpace() can return false immediately
|
||||
// Actual space may be lower
|
||||
uint16_t knownLargestSpace = UINT16_MAX;
|
||||
|
||||
File f;
|
||||
|
||||
//wrapper to find out how long closing takes
|
||||
void closeFile() {
|
||||
DEBUGFS_PRINT(F("Close -> "));
|
||||
uint32_t s = millis();
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
|
||||
doCloseFile = false;
|
||||
}
|
||||
|
||||
//find() that reads and buffers data from file stream in 256-byte blocks.
|
||||
//Significantly faster, f.find(key) can take SECONDS for multi-kB files
|
||||
bool bufferedFind(const char *target, bool fromStart = true) {
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINT("Find ");
|
||||
DEBUGFS_PRINTLN(target);
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
|
||||
if (!f || !f.size()) return false;
|
||||
size_t targetLen = strlen(target);
|
||||
|
||||
size_t index = 0;
|
||||
byte c;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[FS_BUFSIZE];
|
||||
if (fromStart) f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
while (count < bufsize) {
|
||||
if(buf[count] != target[index])
|
||||
index = 0; // reset index if any char does not match
|
||||
|
||||
if(buf[count] == target[index]) {
|
||||
if(++index >= targetLen) { // return true if all chars in the target match
|
||||
f.seek((f.position() - bufsize) + count +1);
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//find empty spots in file stream in 256-byte blocks.
|
||||
bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
|
||||
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Find %d spaces\n", targetLen);
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
|
||||
if (knownLargestSpace < targetLen) {
|
||||
DEBUGFS_PRINT(F("No match, KLS "));
|
||||
DEBUGFS_PRINTLN(knownLargestSpace);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f || !f.size()) return false;
|
||||
|
||||
uint16_t index = 0;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[FS_BUFSIZE];
|
||||
if (fromStart) f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
|
||||
while (count < bufsize) {
|
||||
if(buf[count] == ' ') {
|
||||
if(++index >= targetLen) { // return true if space long enough
|
||||
if (fromStart) {
|
||||
f.seek((f.position() - bufsize) + count +1 - targetLen);
|
||||
knownLargestSpace = UINT16_MAX; //there may be larger spaces after, so we don't know
|
||||
}
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!fromStart) return false;
|
||||
if (index) {
|
||||
if (knownLargestSpace < index || knownLargestSpace == UINT16_MAX) knownLargestSpace = index;
|
||||
index = 0; // reset index if not space
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//find the closing bracket corresponding to the opening bracket at the file pos when calling this function
|
||||
bool bufferedFindObjectEnd() {
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN(F("Find obj end"));
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
|
||||
if (!f || !f.size()) return false;
|
||||
|
||||
uint16_t objDepth = 0; //num of '{' minus num of '}'. return once 0
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
//size_t start = f.position();
|
||||
byte buf[FS_BUFSIZE];
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
count = 0;
|
||||
|
||||
while (count < bufsize) {
|
||||
if (buf[count] == '{') objDepth++;
|
||||
if (buf[count] == '}') objDepth--;
|
||||
if (objDepth == 0) {
|
||||
f.seek((f.position() - bufsize) + count +1);
|
||||
DEBUGFS_PRINTF("} at pos %d, took %d ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
DEBUGFS_PRINTF("No match, took %d ms\n", millis() - s);
|
||||
return false;
|
||||
}
|
||||
|
||||
//fills n bytes from current file pos with ' ' characters
|
||||
void writeSpace(uint16_t l)
|
||||
{
|
||||
byte buf[FS_BUFSIZE];
|
||||
memset(buf, ' ', FS_BUFSIZE);
|
||||
|
||||
while (l > 0) {
|
||||
uint16_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l;
|
||||
f.write(buf, block);
|
||||
l -= block;
|
||||
}
|
||||
|
||||
if (knownLargestSpace < l) knownLargestSpace = l;
|
||||
}
|
||||
|
||||
bool appendObjectToFile(const char* key, JsonDocument* content, uint32_t s, uint32_t contentLen = 0)
|
||||
{
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN(F("Append"));
|
||||
uint32_t s1 = millis();
|
||||
#endif
|
||||
uint32_t pos = 0;
|
||||
if (!f) return false;
|
||||
|
||||
if (f.size() < 3) {
|
||||
char init[10];
|
||||
strcpy_P(init, PSTR("{\"0\":{}}"));
|
||||
f.print(init);
|
||||
}
|
||||
|
||||
if (content->isNull()) {
|
||||
doCloseFile = true;
|
||||
return true; //nothing to append
|
||||
}
|
||||
|
||||
//if there is enough empty space in file, insert there instead of appending
|
||||
if (!contentLen) contentLen = measureJson(*content);
|
||||
DEBUGFS_PRINTF("CLen %d\n", contentLen);
|
||||
if (bufferedFindSpace(contentLen + strlen(key) + 1)) {
|
||||
if (f.position() > 2) f.write(','); //add comma if not first object
|
||||
f.print(key);
|
||||
serializeJson(*content, f);
|
||||
DEBUGFS_PRINTF("Inserted, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
doCloseFile = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
//not enough space, append at end
|
||||
|
||||
//permitted space for presets exceeded
|
||||
updateFSInfo();
|
||||
|
||||
if (f.size() + 9000 > (fsBytesTotal - fsBytesUsed)) { //make sure there is enough space to at least copy the file once
|
||||
errorFlag = ERR_FS_QUOTA;
|
||||
doCloseFile = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//check if last character in file is '}' (typical)
|
||||
f.seek(1, SeekEnd);
|
||||
if (f.read() == '}') pos = f.size() -1;
|
||||
|
||||
if (pos == 0) //not found
|
||||
{
|
||||
DEBUGFS_PRINTLN("not }");
|
||||
f.seek(0);
|
||||
while (bufferedFind("}",false)) //find last closing bracket in JSON if not last char
|
||||
{
|
||||
pos = f.position();
|
||||
}
|
||||
if (pos > 0) pos--;
|
||||
}
|
||||
DEBUGFS_PRINT("pos "); DEBUGFS_PRINTLN(pos);
|
||||
if (pos > 2)
|
||||
{
|
||||
f.seek(pos, SeekSet);
|
||||
f.write(',');
|
||||
} else { //file content is not valid JSON object
|
||||
f.seek(0, SeekSet);
|
||||
f.print('{'); //start JSON
|
||||
}
|
||||
|
||||
f.print(key);
|
||||
|
||||
//Append object
|
||||
serializeJson(*content, f);
|
||||
f.write('}');
|
||||
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Appended, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeObjectToFileUsingId(const char* file, uint16_t id, JsonDocument* content)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
return writeObjectToFile(file, objKey, content);
|
||||
}
|
||||
|
||||
bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
{
|
||||
uint32_t s = 0; //timing
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Write to %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key);
|
||||
serializeJson(*content, Serial); DEBUGFS_PRINTLN();
|
||||
s = millis();
|
||||
#endif
|
||||
|
||||
uint32_t pos = 0;
|
||||
f = WLED_FS.open(file, "r+");
|
||||
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
|
||||
if (!f) {
|
||||
DEBUGFS_PRINTLN(F("Failed to open!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bufferedFind(key)) //key does not exist in file
|
||||
{
|
||||
return appendObjectToFile(key, content, s);
|
||||
}
|
||||
|
||||
//an object with this key already exists, replace or delete it
|
||||
pos = f.position();
|
||||
//measure out end of old object
|
||||
bufferedFindObjectEnd();
|
||||
uint32_t pos2 = f.position();
|
||||
|
||||
uint32_t oldLen = pos2 - pos;
|
||||
DEBUGFS_PRINTF("Old obj len %d\n", oldLen);
|
||||
|
||||
//Three cases:
|
||||
//1. The new content is null, overwrite old obj with spaces
|
||||
//2. The new content is smaller than the old, overwrite and fill diff with spaces
|
||||
//3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new
|
||||
//4. The new content is larger than old + trailing spaces, delete old and append
|
||||
|
||||
uint32_t contentLen = 0;
|
||||
if (!content->isNull()) contentLen = measureJson(*content);
|
||||
|
||||
if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces
|
||||
DEBUGFS_PRINTLN(F("replace"));
|
||||
f.seek(pos);
|
||||
serializeJson(*content, f);
|
||||
writeSpace(pos2 - f.position());
|
||||
} else if (contentLen && bufferedFindSpace(contentLen - oldLen, false)) { //enough leading spaces to replace
|
||||
DEBUGFS_PRINTLN(F("replace (trailing)"));
|
||||
f.seek(pos);
|
||||
serializeJson(*content, f);
|
||||
} else {
|
||||
DEBUGFS_PRINTLN(F("delete"));
|
||||
pos -= strlen(key);
|
||||
if (pos > 3) pos--; //also delete leading comma if not first object
|
||||
f.seek(pos);
|
||||
writeSpace(pos2 - pos);
|
||||
if (contentLen) return appendObjectToFile(key, content, s, contentLen);
|
||||
}
|
||||
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Replaced/deleted, took %d ms\n", millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest)
|
||||
{
|
||||
char objKey[10];
|
||||
sprintf(objKey, "\"%d\":", id);
|
||||
return readObjectFromFile(file, objKey, dest);
|
||||
}
|
||||
|
||||
//if the key is a nullptr, deserialize entire object
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
{
|
||||
if (doCloseFile) closeFile();
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Read from %s with key %s >>>\n", file, (key==nullptr)?"nullptr":key);
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
f = WLED_FS.open(file, "r");
|
||||
if (!f) return false;
|
||||
|
||||
if (key != nullptr && !bufferedFind(key)) //key does not exist in file
|
||||
{
|
||||
f.close();
|
||||
dest->clear();
|
||||
DEBUGFS_PRINTLN(F("Obj not found."));
|
||||
return false;
|
||||
}
|
||||
|
||||
deserializeJson(*dest, f);
|
||||
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("Read, took %d ms\n", millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateFSInfo() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if WLED_FS == LITTLEFS
|
||||
fsBytesTotal = LITTLEFS.totalBytes();
|
||||
fsBytesUsed = LITTLEFS.usedBytes();
|
||||
#else
|
||||
esp_spiffs_info(nullptr, &fsBytesTotal, &fsBytesUsed);
|
||||
#endif
|
||||
#else
|
||||
FSInfo fsi;
|
||||
WLED_FS.info(fsi);
|
||||
fsBytesUsed = fsi.usedBytes;
|
||||
fsBytesTotal = fsi.totalBytes;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING
|
||||
//Un-comment any file types you need
|
||||
@ -37,14 +400,15 @@ String getContentType(AsyncWebServerRequest* request, String filename){
|
||||
bool handleFileRead(AsyncWebServerRequest* request, String path){
|
||||
DEBUG_PRINTLN("FileRead: " + path);
|
||||
if(path.endsWith("/")) path += "index.htm";
|
||||
if(path.indexOf("sec") > -1) return false;
|
||||
String contentType = getContentType(request, path);
|
||||
String pathWithGz = path + ".gz";
|
||||
if(SPIFFS.exists(pathWithGz)){
|
||||
request->send(SPIFFS, pathWithGz, contentType);
|
||||
/*String pathWithGz = path + ".gz";
|
||||
if(WLED_FS.exists(pathWithGz)){
|
||||
request->send(WLED_FS, pathWithGz, contentType);
|
||||
return true;
|
||||
}
|
||||
if(SPIFFS.exists(path)) {
|
||||
request->send(SPIFFS, path, contentType);
|
||||
}*/
|
||||
if(WLED_FS.exists(path)) {
|
||||
request->send(WLED_FS, path, contentType);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -283,7 +283,7 @@ type="submit">Save</button></form></body></html>)=====";
|
||||
// Autogenerated from wled00/data/settings_time.htm, do not edit!!
|
||||
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html><head><meta name="viewport" content="width=500"><meta
|
||||
charset="utf-8"><title>Time Settings</title><script>
|
||||
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Macro</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="16"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
|
||||
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<8;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0],i=0;i<8;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
|
||||
%CSS%%SCSS%</head><body onload="S()"><form
|
||||
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="helpB"><button
|
||||
type="button" onclick="H()">?</button></div><button type="button" onclick="B()">
|
||||
@ -318,28 +318,20 @@ Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input
|
||||
type="number" min="1" max="31" required><br>Hour: <input name="CH"
|
||||
type="number" min="0" max="23" required> Minute: <input name="CM" type="number"
|
||||
min="0" max="59" required> Second: <input name="CS" type="number" min="0"
|
||||
max="59" required><br><h3>Advanced Macros</h3>Define API macros here:<br>1:
|
||||
<input name="M1" maxlength="64"><br>2: <input name="M2" maxlength="64"><br>3:
|
||||
<input name="M3" maxlength="64"><br>4: <input name="M4" maxlength="64"><br>5:
|
||||
<input name="M5" maxlength="64"><br>6: <input name="M6" maxlength="64"><br>7:
|
||||
<input name="M7" maxlength="64"><br>8: <input name="M8" maxlength="64"><br>9:
|
||||
<input name="M9" maxlength="64"><br>10: <input name="M10" maxlength="64"><br>
|
||||
11: <input name="M11" maxlength="64"><br>12: <input name="M12" maxlength="64">
|
||||
<br>13: <input name="M13" maxlength="64"><br>14: <input name="M14"
|
||||
maxlength="64"><br>15: <input name="M15" maxlength="64"><br>16: <input
|
||||
name="M16" maxlength="64"><br><br><i>
|
||||
Use 0 for the default action instead of a macro</i><br>Boot Macro: <input
|
||||
name="MB" type="number" min="0" max="16" required><br>Alexa On/Off Macros:
|
||||
<input name="A0" type="number" min="0" max="16" required> <input name="A1"
|
||||
type="number" min="0" max="16" required><br>Button short press macro: Macro:
|
||||
<input name="MP" type="number" min="0" max="16" required><br>Long Press: <input
|
||||
name="ML" type="number" min="0" max="16" required> Double press: <input
|
||||
name="MD" type="number" min="0" max="16" required><br>Countdown-Over Macro:
|
||||
<input name="MC" type="number" min="0" max="16" required><br>
|
||||
Timed-Light-Over Macro: <input name="MN" type="number" min="0" max="16"
|
||||
required><br>Time-Controlled Macros:<br><div style="display:inline-block"><table
|
||||
id="TMT"></table></div><hr><button type="button" onclick="B()">Back</button>
|
||||
<button type="submit">Save</button></form></body></html>)=====";
|
||||
max="59" required><br><h3>Macro presets</h3><b>Macros have moved!</b><br><i>
|
||||
Presets now also can be used as macros to save both JSON and HTTP API commands.
|
||||
<br>Just enter the preset id below!</i> <i>
|
||||
Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset:
|
||||
<input name="A0" type="number" min="0" max="250" required> <input name="A1"
|
||||
type="number" min="0" max="250" required><br>Button short press Preset: Macro:
|
||||
<input name="MP" type="number" min="0" max="250" required><br>Long Press: <input
|
||||
name="ML" type="number" min="0" max="250" required> Double press: <input
|
||||
name="MD" type="number" min="0" max="250" required><br>Countdown-Over Preset:
|
||||
<input name="MC" type="number" min="0" max="250" required><br>
|
||||
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250"
|
||||
required><br><h3>Time-controlled presets</h3><div style="display:inline-block">
|
||||
<table id="TMT"></table></div><hr><button type="button" onclick="B()">Back
|
||||
</button><button type="submit">Save</button></form></body></html>)=====";
|
||||
|
||||
|
||||
// Autogenerated from wled00/data/settings_sec.htm, do not edit!!
|
||||
|
3934
wled00/html_ui.h
3934
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ void handleHue()
|
||||
colorUpdated(NOTIFIER_CALL_MODE_HUE); hueReceived = false;
|
||||
if (hueStoreAllowed && hueNewKey)
|
||||
{
|
||||
saveSettingsToEEPROM(); //save api key
|
||||
serializeConfigSec(); //save api key
|
||||
hueStoreAllowed = false;
|
||||
hueNewKey = false;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ bool decodeIRCustom(uint32_t code)
|
||||
{
|
||||
//just examples, feel free to modify or remove
|
||||
case IRCUSTOM_ONOFF : toggleOnOff(); break;
|
||||
case IRCUSTOM_MACRO1 : applyMacro(1); break;
|
||||
case IRCUSTOM_MACRO1 : applyPreset(1); break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
|
140
wled00/json.cpp
140
wled00/json.cpp
@ -11,7 +11,7 @@ void deserializeSegment(JsonObject elem, byte it)
|
||||
{
|
||||
WS2812FX::Segment& seg = strip.getSegment(id);
|
||||
uint16_t start = elem[F("start")] | seg.start;
|
||||
int stop = elem[F("stop")] | -1;
|
||||
int stop = elem["stop"] | -1;
|
||||
|
||||
if (stop < 0) {
|
||||
uint16_t len = elem[F("len")];
|
||||
@ -147,9 +147,6 @@ bool deserializeState(JsonObject root)
|
||||
{
|
||||
strip.applyToAllSelected = false;
|
||||
bool stateResponse = root[F("v")] | false;
|
||||
|
||||
int ps = root[F("ps")] | -1;
|
||||
if (ps >= 0) applyPreset(ps);
|
||||
|
||||
bri = root["bri"] | bri;
|
||||
|
||||
@ -173,26 +170,26 @@ bool deserializeState(JsonObject root)
|
||||
|
||||
int cy = root[F("pl")] | -2;
|
||||
if (cy > -2) presetCyclingEnabled = (cy >= 0);
|
||||
JsonObject ccnf = root[F("ccnf")];
|
||||
JsonObject ccnf = root["ccnf"];
|
||||
presetCycleMin = ccnf[F("min")] | presetCycleMin;
|
||||
presetCycleMax = ccnf[F("max")] | presetCycleMax;
|
||||
tr = ccnf[F("time")] | -1;
|
||||
if (tr >= 2) presetCycleTime = tr;
|
||||
|
||||
JsonObject nl = root[F("nl")];
|
||||
JsonObject nl = root["nl"];
|
||||
nightlightActive = nl["on"] | nightlightActive;
|
||||
nightlightDelayMins = nl[F("dur")] | nightlightDelayMins;
|
||||
nightlightMode = nl[F("fade")] | nightlightMode; //deprecated
|
||||
nightlightMode = nl[F("mode")] | nightlightMode;
|
||||
nightlightTargetBri = nl[F("tbri")] | nightlightTargetBri;
|
||||
|
||||
JsonObject udpn = root[F("udpn")];
|
||||
JsonObject udpn = root["udpn"];
|
||||
notifyDirect = udpn[F("send")] | notifyDirect;
|
||||
receiveNotifications = udpn[F("recv")] | receiveNotifications;
|
||||
bool noNotification = udpn[F("nn")]; //send no notification just for this request
|
||||
|
||||
int timein = root[F("time")] | -1;
|
||||
if (timein != -1) setTime(timein);
|
||||
if (timein != -1 && millis() - ntpLastSyncTime > 50000000L) setTime(timein);
|
||||
doReboot = root[F("rb")] | doReboot;
|
||||
|
||||
realtimeOverride = root[F("lor")] | realtimeOverride;
|
||||
@ -203,7 +200,7 @@ bool deserializeState(JsonObject root)
|
||||
if (strip.getMainSegmentId() != prevMain) setValuesFromMainSeg();
|
||||
|
||||
int it = 0;
|
||||
JsonVariant segVar = root[F("seg")];
|
||||
JsonVariant segVar = root["seg"];
|
||||
if (segVar.is<JsonObject>())
|
||||
{
|
||||
int id = segVar[F("id")] | -1;
|
||||
@ -238,23 +235,44 @@ bool deserializeState(JsonObject root)
|
||||
|
||||
usermods.readFromJsonState(root);
|
||||
|
||||
int ps = root[F("psave")] | -1;
|
||||
if (ps > 0) {
|
||||
savePreset(ps, true, nullptr, root);
|
||||
} else {
|
||||
ps = root[F("pdel")] | -1; //deletion
|
||||
if (ps > 0) {
|
||||
deletePreset(ps);
|
||||
}
|
||||
ps = root["ps"] | -1; //load preset (clears state request!)
|
||||
if (ps >= 0) {applyPreset(ps); return stateResponse;}
|
||||
|
||||
//HTTP API commands
|
||||
const char* httpwin = root["win"];
|
||||
if (httpwin) {
|
||||
String apireq = "win&";
|
||||
apireq += httpwin;
|
||||
handleSet(nullptr, apireq, false);
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject playlist = root[F("playlist")];
|
||||
if (!playlist.isNull()) {
|
||||
loadPlaylist(playlist); return stateResponse;
|
||||
}
|
||||
|
||||
colorUpdated(noNotification ? NOTIFIER_CALL_MODE_NO_NOTIFY : NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||
|
||||
//write presets to flash directly?
|
||||
bool persistSaves = !(root[F("np")] | false);
|
||||
|
||||
ps = root[F("psave")] | -1;
|
||||
if (ps >= 0) savePreset(ps, persistSaves);
|
||||
|
||||
return stateResponse;
|
||||
}
|
||||
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
|
||||
{
|
||||
root[F("id")] = id;
|
||||
root[F("start")] = seg.start;
|
||||
root[F("stop")] = seg.stop;
|
||||
root[F("len")] = seg.stop - seg.start;
|
||||
if (segmentBounds) {
|
||||
root[F("start")] = seg.start;
|
||||
root["stop"] = seg.stop;
|
||||
}
|
||||
if (!forPreset) root[F("len")] = seg.stop - seg.start;
|
||||
root[F("grp")] = seg.grouping;
|
||||
root[F("spc")] = seg.spacing;
|
||||
root["on"] = seg.getOption(SEG_OPTION_ON);
|
||||
@ -291,44 +309,47 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id)
|
||||
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
|
||||
}
|
||||
|
||||
|
||||
void serializeState(JsonObject root)
|
||||
{
|
||||
if (errorFlag) root[F("error")] = errorFlag;
|
||||
|
||||
root["on"] = (bri > 0);
|
||||
root["bri"] = briLast;
|
||||
root[F("transition")] = transitionDelay/100; //in 100ms
|
||||
|
||||
root[F("ps")] = currentPreset;
|
||||
root[F("pss")] = savedPresets;
|
||||
root[F("pl")] = (presetCyclingEnabled) ? 0: -1;
|
||||
|
||||
usermods.addToJsonState(root);
|
||||
|
||||
//temporary for preset cycle
|
||||
JsonObject ccnf = root.createNestedObject("ccnf");
|
||||
ccnf[F("min")] = presetCycleMin;
|
||||
ccnf[F("max")] = presetCycleMax;
|
||||
ccnf[F("time")] = presetCycleTime;
|
||||
|
||||
JsonObject nl = root.createNestedObject("nl");
|
||||
nl["on"] = nightlightActive;
|
||||
nl[F("dur")] = nightlightDelayMins;
|
||||
nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated
|
||||
nl[F("mode")] = nightlightMode;
|
||||
nl[F("tbri")] = nightlightTargetBri;
|
||||
if (nightlightActive) {
|
||||
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
|
||||
} else {
|
||||
nl[F("rem")] = -1;
|
||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
|
||||
{
|
||||
if (includeBri) {
|
||||
root["on"] = (bri > 0);
|
||||
root["bri"] = briLast;
|
||||
root[F("transition")] = transitionDelay/100; //in 100ms
|
||||
}
|
||||
|
||||
JsonObject udpn = root.createNestedObject("udpn");
|
||||
udpn[F("send")] = notifyDirect;
|
||||
udpn[F("recv")] = receiveNotifications;
|
||||
if (!forPreset) {
|
||||
if (errorFlag) root[F("error")] = errorFlag;
|
||||
|
||||
root[F("ps")] = currentPreset;
|
||||
root[F("pss")] = savedPresets;
|
||||
root[F("pl")] = (presetCyclingEnabled) ? 0: -1;
|
||||
|
||||
usermods.addToJsonState(root);
|
||||
|
||||
root[F("lor")] = realtimeOverride;
|
||||
//temporary for preset cycle
|
||||
JsonObject ccnf = root.createNestedObject("ccnf");
|
||||
ccnf[F("min")] = presetCycleMin;
|
||||
ccnf[F("max")] = presetCycleMax;
|
||||
ccnf[F("time")] = presetCycleTime;
|
||||
|
||||
JsonObject nl = root.createNestedObject("nl");
|
||||
nl["on"] = nightlightActive;
|
||||
nl[F("dur")] = nightlightDelayMins;
|
||||
nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated
|
||||
nl[F("mode")] = nightlightMode;
|
||||
nl[F("tbri")] = nightlightTargetBri;
|
||||
if (nightlightActive) {
|
||||
nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining
|
||||
} else {
|
||||
nl[F("rem")] = -1;
|
||||
}
|
||||
|
||||
JsonObject udpn = root.createNestedObject("udpn");
|
||||
udpn[F("send")] = notifyDirect;
|
||||
udpn[F("recv")] = receiveNotifications;
|
||||
|
||||
root[F("lor")] = realtimeOverride;
|
||||
}
|
||||
|
||||
root[F("mainseg")] = strip.getMainSegmentId();
|
||||
|
||||
@ -339,7 +360,10 @@ void serializeState(JsonObject root)
|
||||
if (sg.isActive())
|
||||
{
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
serializeSegment(seg0, sg, s);
|
||||
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
|
||||
} else if (forPreset && segmentBounds) { //disable segments not part of preset
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
seg0["stop"] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -422,6 +446,11 @@ void serializeInfo(JsonObject root)
|
||||
wifi_info[F("rssi")] = qrssi;
|
||||
wifi_info[F("signal")] = getSignalQuality(qrssi);
|
||||
wifi_info[F("channel")] = WiFi.channel();
|
||||
|
||||
JsonObject fs_info = root.createNestedObject("fs");
|
||||
fs_info["u"] = fsBytesUsed / 1000;
|
||||
fs_info["t"] = fsBytesTotal / 1000;
|
||||
fs_info[F("pmt")] = presetsModifiedTime;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#ifdef WLED_DEBUG
|
||||
@ -449,6 +478,7 @@ void serializeInfo(JsonObject root)
|
||||
root[F("freeheap")] = ESP.getFreeHeap();
|
||||
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
|
||||
|
||||
|
||||
usermods.addToJsonInfo(root);
|
||||
|
||||
byte os = 0;
|
||||
|
@ -282,7 +282,7 @@ void handleNightlight()
|
||||
}
|
||||
updateBlynk();
|
||||
if (macroNl > 0)
|
||||
applyMacro(macroNl);
|
||||
applyPreset(macroNl);
|
||||
nightlightActiveOld = false;
|
||||
}
|
||||
} else if (nightlightActiveOld) //early de-init
|
||||
@ -303,7 +303,7 @@ void handleNightlight()
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
if (presetCycCurr < presetCycleMin || presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin;
|
||||
applyPreset(presetCycCurr,presetApplyBri);
|
||||
applyPreset(presetCycCurr);
|
||||
presetCycCurr++;
|
||||
if (presetCycCurr > 16) presetCycCurr = 1;
|
||||
colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE);
|
||||
|
@ -266,7 +266,7 @@ void checkTimers()
|
||||
&& (timerWeekday[i] & 0x01) //timer is enabled
|
||||
&& timerWeekday[i] >> weekdayMondayFirst() & 0x01) //timer should activate at current day of week
|
||||
{
|
||||
applyMacro(timerMacro[i]);
|
||||
applyPreset(timerMacro[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
106
wled00/playlist.cpp
Normal file
106
wled00/playlist.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Handles playlists, timed sequences of presets
|
||||
*/
|
||||
|
||||
typedef struct PlaylistEntry {
|
||||
uint8_t preset;
|
||||
uint16_t dur;
|
||||
uint16_t tr;
|
||||
} ple;
|
||||
|
||||
byte playlistRepeat = 1;
|
||||
byte playlistEndPreset = 0;
|
||||
|
||||
uint8_t* playlistEntries;
|
||||
|
||||
byte playlistLen;
|
||||
int8_t playlistIndex = -1;
|
||||
|
||||
uint16_t playlistEntryDur = 0;
|
||||
|
||||
void loadPlaylist(JsonObject playlistObj) {
|
||||
delete playlistEntries;
|
||||
playlistIndex = -1; playlistEntryDur = 0;
|
||||
JsonArray presets = playlistObj["ps"];
|
||||
playlistLen = presets.size();
|
||||
if (playlistLen == 0) return;
|
||||
if (playlistLen > 100) playlistLen = 100;
|
||||
uint16_t dataSize = sizeof(ple) * playlistLen;
|
||||
playlistEntries = new byte[dataSize];
|
||||
PlaylistEntry* entries = reinterpret_cast<PlaylistEntry*>(playlistEntries);
|
||||
|
||||
byte it = 0;
|
||||
for (int ps : presets) {
|
||||
if (it >= playlistLen) break;
|
||||
entries[it].preset = ps;
|
||||
it++;
|
||||
}
|
||||
|
||||
it = 0;
|
||||
JsonArray durations = playlistObj["dur"];
|
||||
if (durations.isNull()) {
|
||||
entries[0].dur = playlistObj["dur"] | 100;
|
||||
it = 1;
|
||||
} else {
|
||||
for (int dur : durations) {
|
||||
if (it >= playlistLen) break;
|
||||
entries[it].dur = dur;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (int i = it; i < playlistLen; i++) entries[i].dur = entries[it -1].dur;
|
||||
|
||||
it = 0;
|
||||
JsonArray tr = playlistObj["transition"];
|
||||
if (tr.isNull()) {
|
||||
entries[0].tr = playlistObj["transition"] | (transitionDelay / 100);
|
||||
it = 1;
|
||||
} else {
|
||||
for (int transition : tr) {
|
||||
if (it >= playlistLen) break;
|
||||
entries[it].tr = transition;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
for (int i = it; i < playlistLen; i++) entries[i].tr = entries[it -1].tr;
|
||||
|
||||
playlistRepeat = playlistObj[F("repeat")] | 0;
|
||||
playlistEndPreset = playlistObj[F("end")] | 0;
|
||||
|
||||
currentPlaylist = 0; //TODO here we need the preset ID where the playlist is saved
|
||||
}
|
||||
|
||||
void handlePlaylist()
|
||||
{
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr || presetCyclingEnabled) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100*playlistEntryDur))
|
||||
{
|
||||
presetCycledTime = millis();
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
playlistIndex++;
|
||||
if (playlistIndex >= playlistLen) {
|
||||
playlistIndex = 0;
|
||||
if (playlistRepeat == 1) { //stop
|
||||
currentPlaylist = -1;
|
||||
delete playlistEntries;
|
||||
playlistEntries = nullptr;
|
||||
if (playlistEndPreset) applyPreset(playlistEndPreset);
|
||||
return;
|
||||
}
|
||||
if (playlistRepeat > 1) playlistRepeat--;
|
||||
}
|
||||
|
||||
PlaylistEntry* entries = reinterpret_cast<PlaylistEntry*>(playlistEntries);
|
||||
|
||||
jsonTransitionOnce = true;
|
||||
transitionDelayTemp = entries[playlistIndex].tr * 100;
|
||||
|
||||
applyPreset(entries[playlistIndex].preset);
|
||||
playlistEntryDur = entries[playlistIndex].dur;
|
||||
if (playlistEntryDur == 0) playlistEntryDur = 10;
|
||||
}
|
||||
}
|
76
wled00/presets.cpp
Normal file
76
wled00/presets.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* Methods to handle saving and loading presets to/from the filesystem
|
||||
*/
|
||||
|
||||
bool applyPreset(byte index)
|
||||
{
|
||||
if (fileDoc) {
|
||||
errorFlag = readObjectFromFileUsingId("/presets.json", index, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
JsonObject fdo = fileDoc->as<JsonObject>();
|
||||
if (fdo["ps"] == index) fdo.remove("ps"); //remove load request for same presets to prevent recursive crash
|
||||
#ifdef WLED_DEBUG_FS
|
||||
serializeJson(*fileDoc, Serial);
|
||||
#endif
|
||||
deserializeState(fdo);
|
||||
} else {
|
||||
DEBUGFS_PRINTLN(F("Make read buf"));
|
||||
DynamicJsonDocument fDoc(JSON_BUFFER_SIZE);
|
||||
errorFlag = readObjectFromFileUsingId("/presets.json", index, &fDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
JsonObject fdo = fDoc.as<JsonObject>();
|
||||
if (fdo["ps"] == index) fdo.remove("ps");
|
||||
#ifdef WLED_DEBUG_FS
|
||||
serializeJson(fDoc, Serial);
|
||||
#endif
|
||||
deserializeState(fdo);
|
||||
}
|
||||
|
||||
if (!errorFlag) {
|
||||
currentPreset = index;
|
||||
isPreset = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
|
||||
{
|
||||
if (index == 0 || index > 250) return;
|
||||
bool docAlloc = fileDoc;
|
||||
JsonObject sObj = saveobj;
|
||||
|
||||
if (!docAlloc) {
|
||||
DEBUGFS_PRINTLN(F("Allocating saving buffer"));
|
||||
fileDoc = new DynamicJsonDocument(JSON_BUFFER_SIZE);
|
||||
sObj = fileDoc->to<JsonObject>();
|
||||
if (pname) sObj["n"] = pname;
|
||||
} else {
|
||||
DEBUGFS_PRINTLN(F("Reuse recv buffer"));
|
||||
sObj.remove(F("psave"));
|
||||
sObj.remove(F("v"));
|
||||
}
|
||||
|
||||
if (!sObj["o"]) {
|
||||
DEBUGFS_PRINTLN(F("Save current state"));
|
||||
serializeState(sObj, true, sObj["ib"], sObj["sb"]);
|
||||
currentPreset = index;
|
||||
}
|
||||
sObj.remove("o");
|
||||
sObj.remove("ib");
|
||||
sObj.remove("sb");
|
||||
sObj.remove(F("error"));
|
||||
sObj.remove(F("time"));
|
||||
|
||||
writeObjectToFileUsingId("/presets.json", index, fileDoc);
|
||||
if (!docAlloc) delete fileDoc;
|
||||
presetsModifiedTime = now(); //unix time
|
||||
updateFSInfo();
|
||||
}
|
||||
|
||||
void deletePreset(byte index) {
|
||||
StaticJsonDocument<24> empty;
|
||||
writeObjectToFileUsingId("/presets.json", index, &empty);
|
||||
presetsModifiedTime = now(); //unix time
|
||||
updateFSInfo();
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#include "wled.h"
|
||||
|
||||
|
||||
/*
|
||||
* Receives client input
|
||||
*/
|
||||
@ -234,13 +233,6 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
countdownMin = request->arg(F("CM")).toInt();
|
||||
countdownSec = request->arg(F("CS")).toInt();
|
||||
|
||||
for (int i=1;i<17;i++)
|
||||
{
|
||||
String a = "M"+String(i);
|
||||
if (request->hasArg(a.c_str())) saveMacro(i,request->arg(a),false);
|
||||
}
|
||||
|
||||
macroBoot = request->arg(F("MB")).toInt();
|
||||
macroAlexaOn = request->arg(F("A0")).toInt();
|
||||
macroAlexaOff = request->arg(F("A1")).toInt();
|
||||
macroButton = request->arg(F("MP")).toInt();
|
||||
@ -273,7 +265,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
{
|
||||
if (request->hasArg(F("RS"))) //complete factory reset
|
||||
{
|
||||
clearEEPROM();
|
||||
WLED_FS.format();
|
||||
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
|
||||
doReboot = true;
|
||||
}
|
||||
@ -328,7 +320,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
|
||||
#endif
|
||||
if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset
|
||||
if (subPage != 6 || !doReboot) serializeConfig(); //do not save if factory reset
|
||||
if (subPage == 2) {
|
||||
strip.init(useRGBW,ledCount,skipFirstLed);
|
||||
}
|
||||
@ -375,7 +367,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv, byte ma
|
||||
|
||||
|
||||
//HTTP API request parser
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
{
|
||||
if (!(req.indexOf("win") >= 0)) return false;
|
||||
|
||||
@ -383,31 +375,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
DEBUG_PRINT(F("API req: "));
|
||||
DEBUG_PRINTLN(req);
|
||||
|
||||
//write presets and macros saved to flash directly?
|
||||
bool persistSaves = true;
|
||||
pos = req.indexOf(F("NP"));
|
||||
if (pos > 0) {
|
||||
persistSaves = false;
|
||||
}
|
||||
|
||||
//save macro, requires &MS=<slot>(<macro>) format
|
||||
pos = req.indexOf(F("&MS="));
|
||||
if (pos > 0) {
|
||||
int i = req.substring(pos + 4).toInt();
|
||||
pos = req.indexOf('(') +1;
|
||||
if (pos > 0) {
|
||||
int en = req.indexOf(')');
|
||||
String mc = req.substring(pos);
|
||||
if (en > 0) mc = req.substring(pos, en);
|
||||
saveMacro(i, mc, persistSaves);
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("IN"));
|
||||
if (pos < 1) XML_response(request);
|
||||
return true;
|
||||
//if you save a macro in one request, other commands in that request are ignored due to unwanted behavior otherwise
|
||||
}
|
||||
|
||||
strip.applyToAllSelected = true;
|
||||
|
||||
//segment select (sets main segment)
|
||||
@ -486,15 +453,12 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
if (v > 100) presetCycleTime = v/100;
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("PA=")); //apply brightness from preset
|
||||
if (pos > 0) presetApplyBri = (req.charAt(pos+3) != '0');
|
||||
|
||||
pos = req.indexOf(F("PS=")); //saves current in preset
|
||||
if (pos > 0) savePreset(getNumVal(&req, pos), persistSaves);
|
||||
if (pos > 0) savePreset(getNumVal(&req, pos));
|
||||
|
||||
//apply preset
|
||||
if (updateVal(&req, "PL=", &presetCycCurr, presetCycleMin, presetCycleMax)) {
|
||||
applyPreset(presetCycCurr, presetApplyBri);
|
||||
applyPreset(presetCycCurr);
|
||||
}
|
||||
|
||||
//set brightness
|
||||
@ -595,10 +559,10 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
overlayCurrent = getNumVal(&req, pos);
|
||||
}
|
||||
|
||||
//apply macro
|
||||
//apply macro (deprecated, added for compatibility with pre-0.11 automations)
|
||||
pos = req.indexOf(F("&M="));
|
||||
if (pos > 0) {
|
||||
applyMacro(getNumVal(&req, pos));
|
||||
applyPreset(getNumVal(&req, pos) + 16);
|
||||
}
|
||||
|
||||
//toggle send UDP direct notifications
|
||||
@ -746,6 +710,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req)
|
||||
}
|
||||
//you can add more if you need
|
||||
|
||||
if (!apply) return true; //when called by JSON API, do not call colorUpdated() here
|
||||
|
||||
//internal call, does not send XML response
|
||||
pos = req.indexOf(F("IN"));
|
||||
if (pos < 1) XML_response(request);
|
||||
|
@ -12,6 +12,8 @@ void UsermodManager::connected() { for (byte i = 0; i < numMods; i++) ums[i]->co
|
||||
void UsermodManager::addToJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonState(obj); }
|
||||
void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToJsonInfo(obj); }
|
||||
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
|
||||
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
|
||||
void UsermodManager::readFromConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromConfig(obj); }
|
||||
|
||||
bool UsermodManager::add(Usermod* um)
|
||||
{
|
||||
|
@ -70,6 +70,10 @@ void WLED::loop()
|
||||
|
||||
if (doReboot)
|
||||
reset();
|
||||
if (doCloseFile) {
|
||||
closeFile();
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!realtimeMode || realtimeOverride) // block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
@ -80,10 +84,17 @@ void WLED::loop()
|
||||
ArduinoOTA.handle();
|
||||
#endif
|
||||
handleNightlight();
|
||||
handlePlaylist();
|
||||
yield();
|
||||
|
||||
handleHue();
|
||||
handleBlynk();
|
||||
|
||||
/*if (presetToApply) {
|
||||
applyPreset(presetToApply);
|
||||
presetToApply = 0;
|
||||
}*/
|
||||
|
||||
yield();
|
||||
|
||||
if (!offMode)
|
||||
@ -131,17 +142,6 @@ void WLED::loop()
|
||||
|
||||
void WLED::setup()
|
||||
{
|
||||
EEPROM.begin(EEPSIZE);
|
||||
ledCount = EEPROM.read(229) + ((EEPROM.read(398) << 8) & 0xFF00);
|
||||
if (ledCount > MAX_LEDS || ledCount == 0)
|
||||
ledCount = 30;
|
||||
|
||||
#ifdef ESP8266
|
||||
#if LEDPIN == 3
|
||||
if (ledCount > MAX_LEDS_DMA)
|
||||
ledCount = MAX_LEDS_DMA; // DMA method uses too much ram
|
||||
#endif
|
||||
#endif
|
||||
Serial.begin(115200);
|
||||
Serial.setTimeout(50);
|
||||
DEBUG_PRINTLN();
|
||||
@ -162,25 +162,33 @@ void WLED::setup()
|
||||
DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
registerUsermods();
|
||||
|
||||
strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly
|
||||
strip.setBrightness(0);
|
||||
//strip.init(EEPROM.read(372), ledCount, EEPROM.read(2204)); // init LEDs quickly
|
||||
//strip.setBrightness(0);
|
||||
|
||||
DEBUG_PRINT(F("LEDs inited. heap usage ~"));
|
||||
DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
|
||||
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
|
||||
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
|
||||
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
SPIFFS.begin(true);
|
||||
#endif
|
||||
SPIFFS.begin();
|
||||
|
||||
bool fsinit = false;
|
||||
DEBUGFS_PRINTLN(F("Mount FS"));
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
fsinit = WLED_FS.begin(true);
|
||||
#else
|
||||
fsinit = WLED_FS.begin();
|
||||
#endif
|
||||
if (!fsinit) {
|
||||
DEBUGFS_PRINTLN(F("FS failed!"));
|
||||
errorFlag = ERR_FS_BEGIN;
|
||||
} else deEEP();
|
||||
updateFSInfo();
|
||||
deserializeConfig();
|
||||
|
||||
#if STATUSLED && STATUSLED != LEDPIN
|
||||
pinMode(STATUSLED, OUTPUT);
|
||||
#endif
|
||||
|
||||
DEBUG_PRINTLN(F("Load EEPROM"));
|
||||
loadSettingsFromEEPROM(true);
|
||||
//DEBUG_PRINTLN(F("Load EEPROM"));
|
||||
//loadSettingsFromEEPROM();
|
||||
beginStrip();
|
||||
userSetup();
|
||||
usermods.setup();
|
||||
@ -188,8 +196,6 @@ void WLED::setup()
|
||||
showWelcomePage = true;
|
||||
WiFi.persistent(false);
|
||||
|
||||
if (macroBoot > 0)
|
||||
applyMacro(macroBoot);
|
||||
Serial.println(F("Ada"));
|
||||
|
||||
// generate module IDs
|
||||
@ -234,14 +240,25 @@ void WLED::setup()
|
||||
void WLED::beginStrip()
|
||||
{
|
||||
// Initialize NeoPixel Strip and button
|
||||
#ifdef ESP8266
|
||||
#if LEDPIN == 3
|
||||
if (ledCount > MAX_LEDS_DMA)
|
||||
ledCount = MAX_LEDS_DMA; // DMA method uses too much ram
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ledCount > MAX_LEDS || ledCount == 0)
|
||||
ledCount = 30;
|
||||
|
||||
strip.init(useRGBW, ledCount, skipFirstLed);
|
||||
strip.setBrightness(0);
|
||||
strip.setShowCallback(handleOverlayDraw);
|
||||
|
||||
#ifdef BTNPIN
|
||||
pinMode(BTNPIN, INPUT_PULLUP);
|
||||
#endif
|
||||
|
||||
if (bootPreset > 0)
|
||||
applyPreset(bootPreset, turnOnAtBoot);
|
||||
if (bootPreset > 0) applyPreset(bootPreset);
|
||||
colorUpdated(NOTIFIER_CALL_MODE_INIT);
|
||||
|
||||
// init relay pin
|
||||
|
@ -3,16 +3,16 @@
|
||||
/*
|
||||
Main sketch, global variable declarations
|
||||
@title WLED project sketch
|
||||
@version 0.10.2
|
||||
@version 0.11.0p
|
||||
@author Christian Schwinne
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2011120
|
||||
#define VERSION 2011121
|
||||
|
||||
// ESP8266-01 (blue) got too little storage space to work with all features of WLED. To use it, you must use ESP8266 Arduino Core v2.4.2 and the setting 512K(No SPIFFS).
|
||||
// ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.
|
||||
|
||||
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program. Use 1M(64K SPIFFS).
|
||||
// ESP8266-01 (black) has 1MB flash and can thus fit the whole program, although OTA update is not possible. Use 1M(128K SPIFFS).
|
||||
// Uncomment some of the following lines to disable features to compile for ESP8266-01 (max flash size 434kB):
|
||||
// Alternatively, with platformio pass your chosen flags to your custom build target in platformio.ini.override
|
||||
|
||||
@ -35,19 +35,23 @@
|
||||
#define WLED_ENABLE_WEBSOCKETS
|
||||
#endif
|
||||
|
||||
#define WLED_DISABLE_FILESYSTEM // SPIFFS is not used by any WLED feature yet
|
||||
//#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
|
||||
//#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
|
||||
//#define WLED_DISABLE_FILESYSTEM // FS used by new preset functionality
|
||||
#define WLED_ENABLE_FS_SERVING // Enable sending html file from SPIFFS before serving progmem version
|
||||
#define WLED_ENABLE_FS_EDITOR // enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
|
||||
|
||||
// to toggle usb serial debug (un)comment the following line
|
||||
//#define WLED_DEBUG
|
||||
|
||||
// filesystem specific debugging
|
||||
//#define WLED_DEBUG_FS
|
||||
|
||||
// Library inclusions.
|
||||
#include <Arduino.h>
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#include <LittleFS.h>
|
||||
extern "C"
|
||||
{
|
||||
#include <user_interface.h>
|
||||
@ -57,7 +61,9 @@
|
||||
#include "esp_wifi.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include "SPIFFS.h"
|
||||
//#include "SPIFFS.h"
|
||||
#define CONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
#include <LITTLEFS.h>
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
@ -109,6 +115,12 @@
|
||||
#define CLIENT_PASS ""
|
||||
#endif
|
||||
|
||||
#ifndef SPIFFS_EDITOR_AIRCOOOKIE
|
||||
#error You are not using the Aircoookie fork of the ESPAsyncWebserver library.\
|
||||
Using upstream puts your WiFi password at risk of being served by the filesystem.\
|
||||
Comment out this error message to build regardless.
|
||||
#endif
|
||||
|
||||
#if IR_PIN < 0
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#define WLED_DISABLE_INFRARED
|
||||
@ -121,25 +133,18 @@
|
||||
#include <IRutils.h>
|
||||
#endif
|
||||
|
||||
//Filesystem to use for preset and config files. SPIFFS or LittleFS on ESP8266, SPIFFS only on ESP32 (now using LITTLEFS port by lorol)
|
||||
#ifdef ESP8266
|
||||
#define WLED_FS LittleFS
|
||||
#else
|
||||
#define WLED_FS LITTLEFS
|
||||
#endif
|
||||
|
||||
// remove flicker because PWM signal of RGB channels can become out of phase (part of core as of Arduino core v2.7.0)
|
||||
//#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266)
|
||||
// #include "src/dependencies/arduino/core_esp8266_waveform.h"
|
||||
//#endif
|
||||
|
||||
// enable additional debug output
|
||||
#ifdef WLED_DEBUG
|
||||
#ifndef ESP8266
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#define DEBUG_PRINT(x) Serial.print(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUG_PRINTF(x) Serial.printf(x)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x)
|
||||
#endif
|
||||
|
||||
// GLOBAL VARIABLES
|
||||
// both declared and defined in header (solution from http://www.keil.com/support/docs/1868.htm)
|
||||
//
|
||||
@ -160,8 +165,8 @@
|
||||
#endif
|
||||
|
||||
// Global Variable definitions
|
||||
WLED_GLOBAL char versionString[] _INIT("0.10.2");
|
||||
#define WLED_CODENAME "Fumikiri"
|
||||
WLED_GLOBAL char versionString[] _INIT("0.11.0p");
|
||||
#define WLED_CODENAME "Mirai"
|
||||
|
||||
// AP and OTA default passwords (for maximum security change them!)
|
||||
WLED_GLOBAL char apPass[65] _INIT(DEFAULT_AP_PASS);
|
||||
@ -170,8 +175,12 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
|
||||
// Hardware CONFIG (only changeble HERE, not at runtime)
|
||||
// LED strip pin, button pin and IR pin changeable in NpbWrapper.h!
|
||||
|
||||
//WLED_GLOBAL byte presetToApply _INIT(0);
|
||||
|
||||
#if AUXPIN >= 0
|
||||
WLED_GLOBAL byte auxDefaultState _INIT(0); // 0: input 1: high 2: low
|
||||
WLED_GLOBAL byte auxTriggeredState _INIT(0); // 0: input 1: high 2: low
|
||||
#endif
|
||||
WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use
|
||||
|
||||
// WiFi CONFIG (all these can be changed via web UI, no need to set them here)
|
||||
@ -291,7 +300,6 @@ WLED_GLOBAL byte countdownYear _INIT(20), countdownMonth _INIT(1); // Countdow
|
||||
WLED_GLOBAL byte countdownDay _INIT(1) , countdownHour _INIT(0);
|
||||
WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0);
|
||||
|
||||
WLED_GLOBAL byte macroBoot _INIT(0); // macro loaded after startup
|
||||
WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over
|
||||
WLED_GLOBAL byte macroCountdown _INIT(0);
|
||||
WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0);
|
||||
@ -426,9 +434,10 @@ WLED_GLOBAL byte presetCycleMin _INIT(1), presetCycleMax _INIT(5);
|
||||
WLED_GLOBAL uint16_t presetCycleTime _INIT(12);
|
||||
WLED_GLOBAL unsigned long presetCycledTime _INIT(0);
|
||||
WLED_GLOBAL byte presetCycCurr _INIT(presetCycleMin);
|
||||
WLED_GLOBAL bool presetApplyBri _INIT(true);
|
||||
WLED_GLOBAL bool saveCurrPresetCycConf _INIT(false);
|
||||
|
||||
WLED_GLOBAL int16_t currentPlaylist _INIT(0);
|
||||
|
||||
// realtime
|
||||
WLED_GLOBAL byte realtimeMode _INIT(REALTIME_MODE_INACTIVE);
|
||||
WLED_GLOBAL byte realtimeOverride _INIT(REALTIME_OVERRIDE_NONE);
|
||||
@ -473,9 +482,16 @@ WLED_GLOBAL uint16_t rolloverMillis _INIT(0);
|
||||
WLED_GLOBAL char* obuf;
|
||||
WLED_GLOBAL uint16_t olen _INIT(0);
|
||||
|
||||
// General filesystem
|
||||
WLED_GLOBAL size_t fsBytesUsed _INIT(0);
|
||||
WLED_GLOBAL size_t fsBytesTotal _INIT(0);
|
||||
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
|
||||
WLED_GLOBAL JsonDocument* fileDoc;
|
||||
WLED_GLOBAL bool doCloseFile _INIT(false);
|
||||
|
||||
// presets
|
||||
WLED_GLOBAL uint16_t savedPresets _INIT(0);
|
||||
WLED_GLOBAL int8_t currentPreset _INIT(-1);
|
||||
WLED_GLOBAL int16_t currentPreset _INIT(-1);
|
||||
WLED_GLOBAL bool isPreset _INIT(false);
|
||||
|
||||
WLED_GLOBAL byte errorFlag _INIT(0);
|
||||
@ -513,6 +529,30 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
||||
WLED_GLOBAL bool ledStatusState _INIT(0); // the current LED state
|
||||
#endif
|
||||
|
||||
// enable additional debug output
|
||||
#ifdef WLED_DEBUG
|
||||
#ifndef ESP8266
|
||||
#include <rom/rtc.h>
|
||||
#endif
|
||||
#define DEBUG_PRINT(x) Serial.print(x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUG_PRINTF(x...) Serial.printf(x)
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x)
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG_FS
|
||||
#define DEBUGFS_PRINT(x) Serial.print(x)
|
||||
#define DEBUGFS_PRINTLN(x) Serial.println(x)
|
||||
#define DEBUGFS_PRINTF(x...) Serial.printf(x)
|
||||
#else
|
||||
#define DEBUGFS_PRINT(x)
|
||||
#define DEBUGFS_PRINTLN(x)
|
||||
#define DEBUGFS_PRINTF(x...)
|
||||
#endif
|
||||
|
||||
// debug macro variable definitions
|
||||
#ifdef WLED_DEBUG
|
||||
WLED_GLOBAL unsigned long debugTime _INIT(0);
|
||||
@ -521,7 +561,6 @@ WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
|
||||
WLED_GLOBAL int loops _INIT(0);
|
||||
#endif
|
||||
|
||||
|
||||
#define WLED_CONNECTED (WiFi.status() == WL_CONNECTED)
|
||||
#define WLED_WIFI_CONFIGURED (strlen(clientSSID) >= 1 && strcmp(clientSSID, DEFAULT_CLIENT_SSID) != 0)
|
||||
#define WLED_MQTT_CONNECTED (mqtt != nullptr && mqtt->connected())
|
||||
|
@ -2,12 +2,16 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* DEPRECATED, do not use for new settings
|
||||
* Only used to restore config from pre-0.11 installations using the deEEP() methods
|
||||
*
|
||||
* Methods to handle saving and loading to non-volatile memory
|
||||
* EEPROM Map: https://github.com/Aircoookie/WLED/wiki/EEPROM-Map
|
||||
*/
|
||||
|
||||
//eeprom Version code, enables default settings instead of 0 init on update
|
||||
#define EEPVER 22
|
||||
#define EEPSIZE 2560 //Maximum is 4096
|
||||
//0 -> old version, default
|
||||
//1 -> 0.4p 1711272 and up
|
||||
//2 -> 0.4p 1711302 and up
|
||||
@ -32,34 +36,6 @@
|
||||
//21-> 0.10.1p
|
||||
//22-> 2009260
|
||||
|
||||
void commit()
|
||||
{
|
||||
if (!EEPROM.commit()) errorFlag = 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase all configuration data
|
||||
*/
|
||||
void clearEEPROM()
|
||||
{
|
||||
for (int i = 0; i < EEPSIZE; i++)
|
||||
{
|
||||
EEPROM.write(i, 0);
|
||||
}
|
||||
commit();
|
||||
}
|
||||
|
||||
|
||||
void writeStringToEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
{
|
||||
EEPROM.write(pos + i, str[i]);
|
||||
if (str[i] == 0) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
{
|
||||
for (int i = 0; i < len; ++i)
|
||||
@ -70,238 +46,14 @@ void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
str[len] = 0; //make sure every string is properly terminated. str must be at least len +1 big.
|
||||
}
|
||||
|
||||
/*
|
||||
* Write configuration to flash
|
||||
*/
|
||||
void saveSettingsToEEPROM()
|
||||
{
|
||||
if (EEPROM.read(233) != 233) //set no first boot flag
|
||||
{
|
||||
clearEEPROM();
|
||||
EEPROM.write(233, 233);
|
||||
}
|
||||
|
||||
writeStringToEEPROM( 0, clientSSID, 32);
|
||||
writeStringToEEPROM( 32, clientPass, 64);
|
||||
writeStringToEEPROM( 96, cmDNS, 32);
|
||||
writeStringToEEPROM(128, apSSID, 32);
|
||||
writeStringToEEPROM(160, apPass, 64);
|
||||
|
||||
EEPROM.write(224, nightlightDelayMinsDefault);
|
||||
EEPROM.write(225, nightlightMode);
|
||||
EEPROM.write(226, notifyDirectDefault);
|
||||
EEPROM.write(227, apChannel);
|
||||
EEPROM.write(228, apHide);
|
||||
EEPROM.write(229, ledCount & 0xFF);
|
||||
EEPROM.write(230, notifyButton);
|
||||
EEPROM.write(231, notifyTwice);
|
||||
EEPROM.write(232, buttonEnabled);
|
||||
//233 reserved for first boot flag
|
||||
|
||||
for (int i = 0; i<4; i++) //ip addresses
|
||||
{
|
||||
EEPROM.write(234+i, staticIP[i]);
|
||||
EEPROM.write(238+i, staticGateway[i]);
|
||||
EEPROM.write(242+i, staticSubnet[i]);
|
||||
}
|
||||
|
||||
EEPROM.write(249, briS);
|
||||
|
||||
EEPROM.write(250, receiveNotificationBrightness);
|
||||
EEPROM.write(251, fadeTransition);
|
||||
EEPROM.write(252, strip.reverseMode);
|
||||
EEPROM.write(253, transitionDelayDefault & 0xFF);
|
||||
EEPROM.write(254, (transitionDelayDefault >> 8) & 0xFF);
|
||||
EEPROM.write(255, briMultiplier);
|
||||
|
||||
//255,250,231,230,226 notifier bytes
|
||||
writeStringToEEPROM(256, otaPass, 32);
|
||||
|
||||
EEPROM.write(288, nightlightTargetBri);
|
||||
EEPROM.write(289, otaLock);
|
||||
EEPROM.write(290, udpPort & 0xFF);
|
||||
EEPROM.write(291, (udpPort >> 8) & 0xFF);
|
||||
writeStringToEEPROM(292, serverDescription, 32);
|
||||
|
||||
EEPROM.write(327, ntpEnabled);
|
||||
EEPROM.write(328, currentTimezone);
|
||||
EEPROM.write(329, useAMPM);
|
||||
EEPROM.write(330, strip.gammaCorrectBri);
|
||||
EEPROM.write(331, strip.gammaCorrectCol);
|
||||
EEPROM.write(332, overlayDefault);
|
||||
|
||||
EEPROM.write(333, alexaEnabled);
|
||||
writeStringToEEPROM(334, alexaInvocationName, 32);
|
||||
EEPROM.write(366, notifyAlexa);
|
||||
|
||||
EEPROM.write(367, (arlsOffset>=0));
|
||||
EEPROM.write(368, abs(arlsOffset));
|
||||
EEPROM.write(369, turnOnAtBoot);
|
||||
|
||||
EEPROM.write(370, noWifiSleep);
|
||||
|
||||
EEPROM.write(372, useRGBW);
|
||||
EEPROM.write(374, strip.paletteFade);
|
||||
EEPROM.write(375, strip.milliampsPerLed); //was apWaitTimeSecs up to 0.8.5
|
||||
EEPROM.write(376, apBehavior);
|
||||
|
||||
EEPROM.write(377, EEPVER); //eeprom was updated to latest
|
||||
|
||||
EEPROM.write(378, udpPort2 & 0xFF);
|
||||
EEPROM.write(379, (udpPort2 >> 8) & 0xFF);
|
||||
|
||||
EEPROM.write(382, strip.paletteBlend);
|
||||
EEPROM.write(383, strip.colorOrder);
|
||||
|
||||
EEPROM.write(385, irEnabled);
|
||||
|
||||
EEPROM.write(387, strip.ablMilliampsMax & 0xFF);
|
||||
EEPROM.write(388, (strip.ablMilliampsMax >> 8) & 0xFF);
|
||||
EEPROM.write(389, bootPreset);
|
||||
EEPROM.write(390, aOtaEnabled);
|
||||
EEPROM.write(391, receiveNotificationColor);
|
||||
EEPROM.write(392, receiveNotificationEffects);
|
||||
EEPROM.write(393, wifiLock);
|
||||
|
||||
EEPROM.write(394, abs(utcOffsetSecs) & 0xFF);
|
||||
EEPROM.write(395, (abs(utcOffsetSecs) >> 8) & 0xFF);
|
||||
EEPROM.write(396, (utcOffsetSecs<0)); //is negative
|
||||
EEPROM.write(397, syncToggleReceive);
|
||||
EEPROM.write(398, (ledCount >> 8) & 0xFF);
|
||||
//EEPROM.write(399, was !enableSecTransition);
|
||||
|
||||
//favorite setting (preset) memory (25 slots/ each 20byte)
|
||||
//400 - 940 reserved
|
||||
writeStringToEEPROM(990, ntpServerName, 32);
|
||||
|
||||
EEPROM.write(2048, huePollingEnabled);
|
||||
//EEPROM.write(2049, hueUpdatingEnabled);
|
||||
for (int i = 2050; i < 2054; ++i)
|
||||
{
|
||||
EEPROM.write(i, hueIP[i-2050]);
|
||||
}
|
||||
writeStringToEEPROM(2054, hueApiKey, 46);
|
||||
EEPROM.write(2100, huePollIntervalMs & 0xFF);
|
||||
EEPROM.write(2101, (huePollIntervalMs >> 8) & 0xFF);
|
||||
EEPROM.write(2102, notifyHue);
|
||||
EEPROM.write(2103, hueApplyOnOff);
|
||||
EEPROM.write(2104, hueApplyBri);
|
||||
EEPROM.write(2105, hueApplyColor);
|
||||
EEPROM.write(2106, huePollLightId);
|
||||
|
||||
EEPROM.write(2150, overlayMin);
|
||||
EEPROM.write(2151, overlayMax);
|
||||
EEPROM.write(2152, analogClock12pixel);
|
||||
EEPROM.write(2153, analogClock5MinuteMarks);
|
||||
EEPROM.write(2154, analogClockSecondsTrail);
|
||||
|
||||
EEPROM.write(2155, countdownMode);
|
||||
EEPROM.write(2156, countdownYear);
|
||||
EEPROM.write(2157, countdownMonth);
|
||||
EEPROM.write(2158, countdownDay);
|
||||
EEPROM.write(2159, countdownHour);
|
||||
EEPROM.write(2160, countdownMin);
|
||||
EEPROM.write(2161, countdownSec);
|
||||
setCountdown();
|
||||
|
||||
writeStringToEEPROM(2165, cronixieDisplay, 6);
|
||||
EEPROM.write(2171, cronixieBacklight);
|
||||
setCronixie();
|
||||
|
||||
EEPROM.write(2175, macroBoot);
|
||||
EEPROM.write(2176, macroAlexaOn);
|
||||
EEPROM.write(2177, macroAlexaOff);
|
||||
EEPROM.write(2178, macroButton);
|
||||
EEPROM.write(2179, macroLongPress);
|
||||
EEPROM.write(2180, macroCountdown);
|
||||
EEPROM.write(2181, macroNl);
|
||||
EEPROM.write(2182, macroDoublePress);
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
EEPROM.write(2185, e131ProxyUniverse & 0xFF);
|
||||
EEPROM.write(2186, (e131ProxyUniverse >> 8) & 0xFF);
|
||||
#endif
|
||||
|
||||
EEPROM.write(2187, e131Port & 0xFF);
|
||||
EEPROM.write(2188, (e131Port >> 8) & 0xFF);
|
||||
|
||||
EEPROM.write(2189, e131SkipOutOfSequence);
|
||||
EEPROM.write(2190, e131Universe & 0xFF);
|
||||
EEPROM.write(2191, (e131Universe >> 8) & 0xFF);
|
||||
EEPROM.write(2192, e131Multicast);
|
||||
EEPROM.write(2193, realtimeTimeoutMs & 0xFF);
|
||||
EEPROM.write(2194, (realtimeTimeoutMs >> 8) & 0xFF);
|
||||
EEPROM.write(2195, arlsForceMaxBri);
|
||||
EEPROM.write(2196, arlsDisableGammaCorrection);
|
||||
EEPROM.write(2197, DMXAddress & 0xFF);
|
||||
EEPROM.write(2198, (DMXAddress >> 8) & 0xFF);
|
||||
EEPROM.write(2199, DMXMode);
|
||||
|
||||
EEPROM.write(2200, !receiveDirect);
|
||||
EEPROM.write(2201, notifyMacro); //was enableRealtime
|
||||
EEPROM.write(2203, strip.rgbwMode);
|
||||
EEPROM.write(2204, skipFirstLed);
|
||||
|
||||
if (saveCurrPresetCycConf)
|
||||
{
|
||||
EEPROM.write(2205, presetCyclingEnabled);
|
||||
EEPROM.write(2206, presetCycleTime & 0xFF);
|
||||
EEPROM.write(2207, (presetCycleTime >> 8) & 0xFF);
|
||||
EEPROM.write(2208, presetCycleMin);
|
||||
EEPROM.write(2209, presetCycleMax);
|
||||
EEPROM.write(2210, presetApplyBri);
|
||||
// was EEPROM.write(2211, presetApplyCol);
|
||||
// was EEPROM.write(2212, presetApplyFx);
|
||||
saveCurrPresetCycConf = false;
|
||||
}
|
||||
|
||||
writeStringToEEPROM(2220, blynkApiKey, 35);
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
EEPROM.write(2260 + i, timerHours[i] );
|
||||
EEPROM.write(2270 + i, timerMinutes[i]);
|
||||
EEPROM.write(2280 + i, timerWeekday[i]);
|
||||
EEPROM.write(2290 + i, timerMacro[i] );
|
||||
}
|
||||
|
||||
EEPROM.write(2299, mqttEnabled);
|
||||
writeStringToEEPROM(2300, mqttServer, 32);
|
||||
writeStringToEEPROM(2333, mqttDeviceTopic, 32);
|
||||
writeStringToEEPROM(2366, mqttGroupTopic, 32);
|
||||
writeStringToEEPROM(2399, mqttUser, 40);
|
||||
writeStringToEEPROM(2440, mqttPass, 40);
|
||||
writeStringToEEPROM(2481, mqttClientID, 40);
|
||||
EEPROM.write(2522, mqttPort & 0xFF);
|
||||
EEPROM.write(2523, (mqttPort >> 8) & 0xFF);
|
||||
|
||||
// DMX (2530 - 2549)
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
EEPROM.write(2530, DMXChannels);
|
||||
EEPROM.write(2531, DMXGap & 0xFF);
|
||||
EEPROM.write(2532, (DMXGap >> 8) & 0xFF);
|
||||
EEPROM.write(2533, DMXStart & 0xFF);
|
||||
EEPROM.write(2534, (DMXStart >> 8) & 0xFF);
|
||||
|
||||
for (int i=0; i<15; i++) {
|
||||
EEPROM.write(2535+i, DMXFixtureMap[i]);
|
||||
} // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye.
|
||||
#endif
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read all configuration from flash
|
||||
*/
|
||||
void loadSettingsFromEEPROM(bool first)
|
||||
void loadSettingsFromEEPROM()
|
||||
{
|
||||
if (EEPROM.read(233) != 233) //first boot/reset to default
|
||||
{
|
||||
DEBUG_PRINT("Settings invalid, restoring defaults...");
|
||||
saveSettingsToEEPROM();
|
||||
DEBUG_PRINTLN("done");
|
||||
DEBUG_PRINTLN(F("EEPROM settings invalid, using defaults..."));
|
||||
return;
|
||||
}
|
||||
int lastEEPROMversion = EEPROM.read(377); //last EEPROM version before update
|
||||
@ -343,7 +95,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
staticSubnet[3] = EEPROM.read(245);
|
||||
|
||||
briS = EEPROM.read(249); bri = briS;
|
||||
if (!EEPROM.read(369) && first)
|
||||
if (!EEPROM.read(369))
|
||||
{
|
||||
bri = 0; briLast = briS;
|
||||
}
|
||||
@ -426,7 +178,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
readStringFromEEPROM(2165, cronixieDisplay, 6);
|
||||
cronixieBacklight = EEPROM.read(2171);
|
||||
|
||||
macroBoot = EEPROM.read(2175);
|
||||
//macroBoot = EEPROM.read(2175);
|
||||
macroAlexaOn = EEPROM.read(2176);
|
||||
macroAlexaOff = EEPROM.read(2177);
|
||||
macroButton = EEPROM.read(2178);
|
||||
@ -457,7 +209,9 @@ void loadSettingsFromEEPROM(bool first)
|
||||
timerMinutes[i] = EEPROM.read(2270 + i);
|
||||
timerWeekday[i] = EEPROM.read(2280 + i);
|
||||
timerMacro[i] = EEPROM.read(2290 + i);
|
||||
if (timerMacro[i] > 0) timerMacro[i] += 16; //add 16 to work with macro --> preset mapping
|
||||
if (timerWeekday[i] == 0) timerWeekday[i] = 255;
|
||||
if (timerMacro[i] == 0) timerWeekday[i] = timerWeekday[i] & 0b11111110;
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,7 +313,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
if (lastEEPROMversion < 21) presetCycleTime /= 100; //was stored in ms, now is in tenths of a second
|
||||
presetCycleMin = EEPROM.read(2208);
|
||||
presetCycleMax = EEPROM.read(2209);
|
||||
presetApplyBri = EEPROM.read(2210);
|
||||
//was presetApplyBri = EEPROM.read(2210);
|
||||
//was presetApplyCol = EEPROM.read(2211);
|
||||
//was presetApplyFx = EEPROM.read(2212);
|
||||
}
|
||||
@ -588,7 +342,7 @@ void loadSettingsFromEEPROM(bool first)
|
||||
for (int i=0;i<15;i++) {
|
||||
DMXFixtureMap[i] = EEPROM.read(2535+i);
|
||||
} //last used: 2549
|
||||
EEPROM.write(2550, DMXStartLED);
|
||||
DMXStartLED = EEPROM.read(2550);
|
||||
#endif
|
||||
|
||||
//Usermod memory
|
||||
@ -597,159 +351,113 @@ void loadSettingsFromEEPROM(bool first)
|
||||
//2944 - 3071 reserved for Usermods (need to increase EEPSIZE to 3072 in const.h)
|
||||
|
||||
overlayCurrent = overlayDefault;
|
||||
|
||||
savedToPresets();
|
||||
}
|
||||
|
||||
|
||||
//PRESET PROTOCOL 20 bytes
|
||||
//0: preset purpose byte 0:invalid 1:valid preset 2:segment preset 2.0
|
||||
//1:a 2:r 3:g 4:b 5:w 6:er 7:eg 8:eb 9:ew 10:fx 11:sx | custom chase 12:numP 13:numS 14:(0:fs 1:both 2:fe) 15:step 16:ix 17: fp 18-19:Zeros
|
||||
//determines which presets already contain save data
|
||||
void savedToPresets()
|
||||
{
|
||||
for (byte index = 1; index < 16; index++)
|
||||
{
|
||||
uint16_t i = 380 + index*20;
|
||||
|
||||
if (EEPROM.read(i) == 1) {
|
||||
savedPresets |= 0x01 << (index-1);
|
||||
} else
|
||||
{
|
||||
savedPresets &= ~(0x01 << (index-1));
|
||||
}
|
||||
}
|
||||
if (EEPROM.read(700) == 2 || EEPROM.read(700) == 3) {
|
||||
savedPresets |= 0x01 << 15;
|
||||
} else
|
||||
{
|
||||
savedPresets &= ~(0x01 << 15);
|
||||
}
|
||||
//provided for increased compatibility with usermods written for v0.10
|
||||
void applyMacro(byte index) {
|
||||
applyPreset(index+16);
|
||||
}
|
||||
|
||||
bool applyPreset(byte index, bool loadBri)
|
||||
{
|
||||
if (index == 255 || index == 0)
|
||||
{
|
||||
loadSettingsFromEEPROM(false);//load boot defaults
|
||||
return true;
|
||||
}
|
||||
if (index > 16 || index < 1) return false;
|
||||
uint16_t i = 380 + index*20;
|
||||
byte ver = EEPROM.read(i);
|
||||
|
||||
if (index < 16) {
|
||||
if (ver != 1) return false;
|
||||
strip.applyToAllSelected = true;
|
||||
if (loadBri) bri = EEPROM.read(i+1);
|
||||
|
||||
for (byte j=0; j<4; j++)
|
||||
{
|
||||
col[j] = EEPROM.read(i+j+2);
|
||||
colSec[j] = EEPROM.read(i+j+6);
|
||||
}
|
||||
strip.setColor(2, EEPROM.read(i+12), EEPROM.read(i+13), EEPROM.read(i+14), EEPROM.read(i+15)); //tertiary color
|
||||
// De-EEPROM routine, upgrade from previous versions to v0.11
|
||||
void deEEP() {
|
||||
if (WLED_FS.exists("/presets.json")) return;
|
||||
|
||||
DEBUG_PRINTLN(F("Preset file not found, attempting to load from EEPROM"));
|
||||
DEBUGFS_PRINTLN(F("Allocating saving buffer for dEEP"));
|
||||
DynamicJsonDocument dDoc(JSON_BUFFER_SIZE *2);
|
||||
JsonObject sObj = dDoc.to<JsonObject>();
|
||||
sObj.createNestedObject("0");
|
||||
|
||||
effectCurrent = EEPROM.read(i+10);
|
||||
effectSpeed = EEPROM.read(i+11);
|
||||
effectIntensity = EEPROM.read(i+16);
|
||||
effectPalette = EEPROM.read(i+17);
|
||||
} else {
|
||||
if (ver != 2 && ver != 3) return false;
|
||||
strip.applyToAllSelected = false;
|
||||
if (loadBri) bri = EEPROM.read(i+1);
|
||||
WS2812FX::Segment* seg = strip.getSegments();
|
||||
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
|
||||
if (ver == 2) { //versions before 2004230 did not have opacity
|
||||
for (byte j = 0; j < strip.getMaxSegments(); j++)
|
||||
{
|
||||
strip.getSegment(j).opacity = 255;
|
||||
strip.getSegment(j).setOption(SEG_OPTION_ON, 1);
|
||||
EEPROM.begin(EEPSIZE);
|
||||
if (EEPROM.read(233) == 233) { //valid EEPROM save
|
||||
for (uint16_t index = 1; index <= 16; index++) { //copy presets to presets.json
|
||||
uint16_t i = 380 + index*20;
|
||||
byte ver = EEPROM.read(i);
|
||||
|
||||
if ((index < 16 && ver != 1) || (index == 16 && (ver < 2 || ver > 3))) continue;
|
||||
|
||||
char nbuf[16];
|
||||
sprintf(nbuf, "%d", index);
|
||||
|
||||
JsonObject pObj = sObj.createNestedObject(nbuf);
|
||||
|
||||
pObj["q"] = nbuf;
|
||||
sprintf_P(nbuf, "Preset %d", index);
|
||||
pObj["n"] = nbuf;
|
||||
|
||||
pObj["bri"] = EEPROM.read(i+1);
|
||||
|
||||
if (index < 16) {
|
||||
JsonObject segObj = pObj.createNestedObject("seg");
|
||||
|
||||
JsonArray colarr = segObj.createNestedArray("col");
|
||||
|
||||
byte numChannels = (useRGBW)? 4:3;
|
||||
|
||||
for (uint8_t k = 0; k < 3; k++) //k=0 primary (i+2) k=1 secondary (i+6) k=2 tertiary color (i+12)
|
||||
{
|
||||
JsonArray colX = colarr.createNestedArray();
|
||||
uint16_t memloc = i + 6*k;
|
||||
if (k == 0) memloc += 2;
|
||||
|
||||
for (byte j = 0; j < numChannels; j++) colX.add(EEPROM.read(memloc + j));
|
||||
}
|
||||
|
||||
segObj[F("fx")] = EEPROM.read(i+10);
|
||||
segObj[F("sx")] = EEPROM.read(i+11);
|
||||
segObj[F("ix")] = EEPROM.read(i+16);
|
||||
segObj[F("pal")] = EEPROM.read(i+17);
|
||||
} else {
|
||||
WS2812FX::Segment* seg = strip.getSegments();
|
||||
memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
|
||||
if (ver == 2) { //versions before 2004230 did not have opacity
|
||||
for (byte j = 0; j < strip.getMaxSegments(); j++)
|
||||
{
|
||||
strip.getSegment(j).opacity = 255;
|
||||
strip.getSegment(j).setOption(SEG_OPTION_ON, 1);
|
||||
}
|
||||
}
|
||||
serializeState(pObj, true, false, true);
|
||||
|
||||
strip.resetSegments();
|
||||
}
|
||||
}
|
||||
setValuesFromMainSeg();
|
||||
}
|
||||
currentPreset = index;
|
||||
isPreset = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void savePreset(byte index, bool persist)
|
||||
{
|
||||
if (index > 16) return;
|
||||
if (index < 1) {saveSettingsToEEPROM();return;}
|
||||
uint16_t i = 380 + index*20;//min400
|
||||
|
||||
if (index < 16) {
|
||||
EEPROM.write(i, 1);
|
||||
EEPROM.write(i+1, bri);
|
||||
for (uint16_t j=0; j<4; j++)
|
||||
{
|
||||
EEPROM.write(i+j+2, col[j]);
|
||||
EEPROM.write(i+j+6, colSec[j]);
|
||||
|
||||
|
||||
for (uint16_t index = 1; index <= 16; index++) { //copy macros to presets.json
|
||||
char m[65];
|
||||
readStringFromEEPROM(1024+64*(index-1), m, 64);
|
||||
if (m[0]) { //macro exists
|
||||
char nbuf[16];
|
||||
sprintf(nbuf, "%d", index + 16);
|
||||
JsonObject pObj = sObj.createNestedObject(nbuf);
|
||||
sprintf_P(nbuf, "Z Macro %d", index);
|
||||
pObj["n"] = nbuf;
|
||||
pObj["win"] = m;
|
||||
}
|
||||
}
|
||||
EEPROM.write(i+10, effectCurrent);
|
||||
EEPROM.write(i+11, effectSpeed);
|
||||
|
||||
uint32_t colTer = strip.getSegment(strip.getMainSegmentId()).colors[2];
|
||||
EEPROM.write(i+12, (colTer >> 16) & 0xFF);
|
||||
EEPROM.write(i+13, (colTer >> 8) & 0xFF);
|
||||
EEPROM.write(i+14, (colTer >> 0) & 0xFF);
|
||||
EEPROM.write(i+15, (colTer >> 24) & 0xFF);
|
||||
|
||||
EEPROM.write(i+16, effectIntensity);
|
||||
EEPROM.write(i+17, effectPalette);
|
||||
} else { //segment 16 can save segments
|
||||
EEPROM.write(i, 3);
|
||||
EEPROM.write(i+1, bri);
|
||||
WS2812FX::Segment* seg = strip.getSegments();
|
||||
memcpy(EEPROM.getDataPtr() +i+2, seg, 240);
|
||||
}
|
||||
|
||||
if (persist) commit();
|
||||
savedToPresets();
|
||||
currentPreset = index;
|
||||
isPreset = true;
|
||||
}
|
||||
|
||||
EEPROM.end();
|
||||
|
||||
void loadMacro(byte index, char* m)
|
||||
{
|
||||
index-=1;
|
||||
if (index > 15) return;
|
||||
readStringFromEEPROM(1024+64*index, m, 64);
|
||||
}
|
||||
|
||||
|
||||
void applyMacro(byte index)
|
||||
{
|
||||
index-=1;
|
||||
if (index > 15) return;
|
||||
String mc="win&";
|
||||
char m[65];
|
||||
loadMacro(index+1, m);
|
||||
mc += m;
|
||||
mc += "&IN"; //internal, no XML response
|
||||
if (!notifyMacro) mc += "&NN";
|
||||
String forbidden = "&M="; //dont apply if called by the macro itself to prevent loop
|
||||
/*
|
||||
* NOTE: loop is still possible if you call a different macro from a macro, which then calls the first macro again.
|
||||
* To prevent that, but also disable calling macros within macros, comment the next line out.
|
||||
*/
|
||||
forbidden = forbidden + index;
|
||||
if (mc.indexOf(forbidden) >= 0) return;
|
||||
handleSet(nullptr, mc);
|
||||
}
|
||||
|
||||
|
||||
void saveMacro(byte index, const String& mc, bool persist) //only commit on single save, not in settings
|
||||
{
|
||||
index-=1;
|
||||
if (index > 15) return;
|
||||
int s = 1024+index*64;
|
||||
for (int i = s; i < s+64; i++)
|
||||
{
|
||||
EEPROM.write(i, mc.charAt(i-s));
|
||||
File f = WLED_FS.open("/presets.json", "w");
|
||||
if (!f) {
|
||||
errorFlag = ERR_FS_GENERAL;
|
||||
return;
|
||||
}
|
||||
if (persist) commit();
|
||||
serializeJson(dDoc, f);
|
||||
f.close();
|
||||
DEBUG_PRINTLN(F("deEEP complete!"));
|
||||
}
|
||||
|
||||
void deEEPSettings() {
|
||||
DEBUG_PRINTLN(F("Restore settings from EEPROM"));
|
||||
EEPROM.begin(EEPSIZE);
|
||||
loadSettingsFromEEPROM();
|
||||
EEPROM.end();
|
||||
|
||||
serializeConfig();
|
||||
}
|
@ -85,7 +85,9 @@ void initServer()
|
||||
if (error || root.isNull()) {
|
||||
request->send(400, "application/json", F("{\"error\":9}")); return;
|
||||
}
|
||||
fileDoc = &jsonBuffer;
|
||||
verboseResponse = deserializeState(root);
|
||||
fileDoc = nullptr;
|
||||
}
|
||||
if (verboseResponse) { //if JSON contains "v"
|
||||
serveJson(request); return;
|
||||
@ -124,13 +126,13 @@ void initServer()
|
||||
if (!otaLock){
|
||||
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_EDITOR
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
server.addHandler(new SPIFFSEditor(SPIFFS));//http_username,http_password));
|
||||
server.addHandler(new SPIFFSEditor(WLED_FS));//http_username,http_password));
|
||||
#else
|
||||
server.addHandler(new SPIFFSEditor());//http_username,http_password));
|
||||
server.addHandler(new SPIFFSEditor("","",WLED_FS));//http_username,http_password));
|
||||
#endif
|
||||
#else
|
||||
server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 501, "Not implemented", F("The SPIFFS editor is disabled in this build."), 254);
|
||||
serveMessage(request, 501, "Not implemented", F("The FS editor is disabled in this build."), 254);
|
||||
});
|
||||
#endif
|
||||
//init ota page
|
||||
|
@ -246,6 +246,8 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
#ifdef ESP8266
|
||||
#if LEDPIN == 3
|
||||
oappend(SET_F("d.Sf.LC.max=500;"));
|
||||
#else
|
||||
oappend(SET_F("d.Sf.LC.max=1500;"));
|
||||
#endif
|
||||
#endif
|
||||
sappend('v',SET_F("LC"),ledCount);
|
||||
@ -386,16 +388,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("CH"),countdownHour);
|
||||
sappend('v',SET_F("CM"),countdownMin);
|
||||
sappend('v',SET_F("CS"),countdownSec);
|
||||
char k[4]; k[0]= 'M';
|
||||
for (int i=1;i<17;i++)
|
||||
{
|
||||
char m[65];
|
||||
loadMacro(i, m);
|
||||
sprintf(k+1,"%i",i);
|
||||
sappends('s',k,m);
|
||||
}
|
||||
|
||||
sappend('v',SET_F("MB"),macroBoot);
|
||||
sappend('v',SET_F("A0"),macroAlexaOn);
|
||||
sappend('v',SET_F("A1"),macroAlexaOff);
|
||||
sappend('v',SET_F("MP"),macroButton);
|
||||
@ -404,6 +397,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('v',SET_F("MN"),macroNl);
|
||||
sappend('v',SET_F("MD"),macroDoublePress);
|
||||
|
||||
char k[4];
|
||||
k[2] = 0; //Time macros
|
||||
for (int i = 0; i<8; i++)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user