Merge branch 'segment-api' into dev

This commit is contained in:
Blaz Kristan 2022-08-19 15:14:56 +02:00
commit e9f6509cb0
83 changed files with 29204 additions and 7381 deletions

View File

@ -1,10 +1,7 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
]
}

2
package-lock.json generated
View File

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

View File

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

View File

@ -162,7 +162,7 @@ lib_compat_mode = strict
lib_deps =
fastled/FastLED @ 3.5.0
IRremoteESP8266 @ 2.8.2
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.4
https://github.com/Aircoookie/ESPAsyncWebServer.git @ ~2.0.5
#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

View File

@ -263,108 +263,60 @@ writeChunks(
name: "PAGE_settings",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=0\"></script>"
)
},
{
file: "settings_wifi.htm",
name: "PAGE_settings_wifi",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=1\"></script>"
)
},
{
file: "settings_leds.htm",
name: "PAGE_settings_leds",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=2\"></script>"
)
},
{
file: "settings_dmx.htm",
name: "PAGE_settings_dmx",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=7\"></script>"
)
},
{
file: "settings_ui.htm",
name: "PAGE_settings_ui",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=3\"></script>"
)
},
{
file: "settings_sync.htm",
name: "PAGE_settings_sync",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=4\"></script>"
)
},
{
file: "settings_time.htm",
name: "PAGE_settings_time",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=5\"></script>"
)
},
{
file: "settings_sec.htm",
name: "PAGE_settings_sec",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=6\"></script>"
)
},
{
file: "settings_um.htm",
name: "PAGE_settings_um",
method: "gzip",
filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=8\"></script>"
)
},
{
file: "settings_2D.htm",
name: "PAGE_settings_2D",
method: "gzip",
filter: "html-minify",
},
{
file: "settings_pin.htm",
@ -441,6 +393,12 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
method: "gzip",
filter: "html-minify",
},
{
file: "liveviewws2D.htm",
name: "PAGE_liveviewws2D",
method: "gzip",
filter: "html-minify",
},
{
file: "404.htm",
name: "PAGE_404",

View File

@ -103,25 +103,24 @@ class Animated_Staircase : public Usermod {
void updateSegments() {
mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
for (int i = 0; i < strip.getSegmentsNum(); i++) {
Segment &seg = strip.getSegment(i);
if (!seg.isActive()) {
maxSegmentId = i - 1;
break;
}
if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, i);
seg.setOption(SEG_OPTION_ON, true);
// We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0];
} else {
segments->setOption(SEG_OPTION_ON, 0, i);
seg.setOption(SEG_OPTION_ON, false);
}
// Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i);
seg.setOption(SEG_OPTION_TRANSITIONAL, true);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
}
@ -290,13 +289,13 @@ class Animated_Staircase : public Usermod {
}
} else {
// Restore segment options
WS2812FX::Segment* segments = strip.getSegments();
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) {
if (!segments->isActive()) {
for (int i = 0; i < strip.getSegmentsNum(); i++) {
Segment &seg = strip.getSegment(i);
if (!seg.isActive()) {
maxSegmentId = i - 1;
break;
}
segments->setOption(SEG_OPTION_ON, 1, i);
seg.setOption(SEG_OPTION_ON, true);
}
colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled."));
@ -406,6 +405,14 @@ class Animated_Staircase : public Usermod {
}
}
void appendConfigData() {
//oappend(SET_F("dd=addDropdown('staircase','selectfield');"));
//oappend(SET_F("addOption(dd,'1st value',0);"));
//oappend(SET_F("addOption(dd,'2nd value',1);"));
//oappend(SET_F("addInfo('staircase:selectfield',1,'additional info');")); // 0 is field type, 1 is actual field
}
/*
* Writes the configuration to internal flash memory.
*/
@ -458,15 +465,11 @@ class Animated_Staircase : public Usermod {
useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop;
topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin;
// topPIRorTriggerPin = min(33,max(-1,(int)topPIRorTriggerPin)); // bounds check
topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin;
// topEchoPin = min(39,max(-1,(int)topEchoPin)); // bounds check
useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom;
bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin;
// bottomPIRorTriggerPin = min(33,max(-1,(int)bottomPIRorTriggerPin)); // bounds check
bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin;
// bottomEchoPin = min(39,max(-1,(int)bottomEchoPin)); // bounds check
topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist;
topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected)
@ -504,22 +507,22 @@ class Animated_Staircase : public Usermod {
* tab of the web-UI.
*/
void addToJsonInfo(JsonObject& root) {
JsonObject staircase = root["u"];
if (staircase.isNull()) {
staircase = root.createNestedObject("u");
JsonObject user = root["u"];
if (user.isNull()) {
user = root.createNestedObject("u");
}
JsonArray usermodEnabled = staircase.createNestedArray(F("Staircase")); // name
String btn = F("<button class=\"btn infobtn\" onclick=\"requestJson({staircase:{enabled:");
if (enabled) {
btn += F("false}});\">");
btn += F("enabled");
} else {
btn += F("true}});\">");
btn += F("disabled");
}
btn += F("</button>");
usermodEnabled.add(btn); // value
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); // name
String uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
uiDomString += enabled ? F(":false}});\">") : F(":true}});\">");
uiDomString += F("<i class=\"icons ");
uiDomString += enabled ? "on" : "off";
uiDomString += F("\">&#xe08f;</i></button>");
infoArr.add(uiDomString);
}
};

View File

@ -26,15 +26,10 @@ private:
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
// set the default pins based on the architecture, these get overridden by Usermod menu settings
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else // ESP8266 boards
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#ifdef ESP8266
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
int8_t ioPin[2] = {i2c_scl, i2c_sda}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
bool initDone = false;
// BME280 sensor settings
@ -177,7 +172,7 @@ private:
public:
void setup()
{
bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
bool HW_Pins_Used = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda); // note whether architecture-based hardware SCL/SDA pins used
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
@ -444,7 +439,7 @@ public:
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
PinOwner po = PinOwner::UM_BME280;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
setup();

View File

@ -249,7 +249,7 @@ class UsermodCronixie : public Usermod {
if (backlight && _digitOut[i] <11)
{
uint32_t col = strip.gamma32(strip.getSegment(0).colors[1]);
uint32_t col = gamma32(strip.getSegment(0).colors[1]);
for (uint16_t j=o; j< o+10; j++) {
if (j != excl) strip.setPixelColor(j, col);
}

View File

@ -355,7 +355,7 @@ public:
// Color in grayscale bitmaps if Segment 1 exists
// TODO If secondary and tertiary are black, color all in primary,
// else color first three from Seg 1 color slots and last three from Seg 2 color slots
WS2812FX::Segment& seg1 = strip.getSegment(tubeSegment);
Segment& seg1 = strip.getSegment(tubeSegment);
if (seg1.isActive()) {
digitColor = strip.getPixelColor(seg1.start + digit);
dimming = seg1.opacity;

View File

@ -63,7 +63,7 @@ class ElekstubeIPSUsermod : public Usermod {
if (!toki.isTick()) return;
updateLocalTime();
WS2812FX::Segment& seg1 = strip.getSegment(tfts.tubeSegment);
Segment& seg1 = strip.getSegment(tfts.tubeSegment);
if (seg1.isActive()) {
bool update = false;
if (seg1.opacity != lastBri) update = true;

View File

@ -143,15 +143,15 @@ private:
}
} else {
if (m_offPreset) {
applyPreset(m_offPreset, NotifyUpdateMode);
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(m_offPreset, NotifyUpdateMode);
return;
} else if (prevPlaylist) {
applyPreset(prevPlaylist, NotifyUpdateMode);
if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPlaylist, NotifyUpdateMode);
prevPlaylist = 0;
return;
} else if (prevPreset) {
if (prevPreset<255) applyPreset(prevPreset, NotifyUpdateMode);
else applyTemporaryPreset();
if (prevPreset<255) { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyPreset(prevPreset, NotifyUpdateMode); }
else { if (currentPreset==m_onPreset || currentPlaylist==m_onPreset) applyTemporaryPreset(); }
prevPreset = 0;
return;
}
@ -275,20 +275,9 @@ public:
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
String uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
if (enabled) {
uiDomString += F(":false}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe325;</i>");
} else {
uiDomString += F(":true}});\">");
uiDomString += F("PIR <i class=\"icons\">&#xe08f;</i>");
}
uiDomString += F("</button>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
String uiDomString;
if (enabled) {
if (offTimerStart > 0)
{
@ -322,6 +311,20 @@ public:
infoArr.add(F("disabled"));
}
uiDomString = F(" <button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_enabled);
if (enabled) {
uiDomString += F(":false}});\">");
uiDomString += F("<i class=\"icons on\">&#xe325;</i>");
} else {
uiDomString += F(":true}});\">");
uiDomString += F("<i class=\"icons off\">&#xe08f;</i>");
}
uiDomString += F("</button>");
infoArr.add(uiDomString);
JsonObject sensor = root[F("sensor")];
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;

View File

@ -51,6 +51,7 @@ class PWMFanUsermod : public Usermod {
float targetTemperature = 25.0;
uint8_t minPWMValuePct = 50;
uint8_t numberOfInterrupsInOneSingleRotation = 2; // Number of interrupts ESP32 sees on tacho signal on a single fan rotation. All the fans I've seen trigger two interrups.
uint8_t pwmValuePct = 0;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
@ -83,6 +84,8 @@ class PWMFanUsermod : public Usermod {
}
void updateTacho(void) {
// store milliseconds when tacho was measured the last time
msLastTachoMeasurement = millis();
if (tachoPin < 0) return;
// start of tacho measurement
@ -93,8 +96,6 @@ class PWMFanUsermod : public Usermod {
last_rpm /= tachoUpdateSec;
// reset counter
counter_rpm = 0;
// store milliseconds when tacho was measured the last time
msLastTachoMeasurement = millis();
// attach interrupt again
attachInterrupt(digitalPinToInterrupt(tachoPin), rpm_fan, FALLING);
}
@ -217,12 +218,30 @@ class PWMFanUsermod : public Usermod {
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root) {
if (tachoPin < 0) return;
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray data = user.createNestedArray(FPSTR(_name));
data.add(last_rpm);
data.add(F("rpm"));
if (enabled) {
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
// if (!tempUM) {
JsonArray infoArr = user.createNestedArray(F("Fan speed [%]"));
String uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({'");
uiDomString += FPSTR(_name);
uiDomString += F("':{'");
uiDomString += FPSTR(_speed);
uiDomString += F("':parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=100 min=0 type=\"range\" value=");
uiDomString += pwmValuePct;
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
infoArr.add(uiDomString);
// }
if (tachoPin >= 0) {
JsonArray data = user.createNestedArray(FPSTR(_name));
data.add(last_rpm);
data.add(F("rpm"));
} else {
JsonArray data = user.createNestedArray(FPSTR(_name));
if (lockFan) data.add(F("locked"));
else data.add(F("auto"));
}
}
}
/*

View File

@ -3,14 +3,6 @@
#include "src/dependencies/time/DS1307RTC.h"
#include "wled.h"
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
//Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod {
@ -20,8 +12,9 @@ class RTCUsermod : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
RTC.begin();
time_t rtcTime = RTC.get();
if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC);
@ -44,13 +37,13 @@ class RTCUsermod : public Usermod {
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* 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("RTC");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
// void addToConfig(JsonObject& root)
// {
// JsonObject top = root.createNestedObject("RTC");
// JsonArray pins = top.createNestedArray("pin");
// pins.add(i2c_scl);
// pins.add(i2c_sda);
// }
uint16_t getId()
{

View File

@ -21,14 +21,6 @@
#include <Wire.h>
#include <VL53L0X.h>
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
#ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif
@ -59,7 +51,7 @@ class UsermodVL53L0XGestures : public Usermod {
public:
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
Wire.begin();
@ -127,13 +119,13 @@ class UsermodVL53L0XGestures : public Usermod {
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* 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("VL53L0x");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
// void addToConfig(JsonObject& root)
// {
// JsonObject top = root.createNestedObject("VL53L0x");
// JsonArray pins = top.createNestedArray("pin");
// pins.add(i2c_scl);
// pins.add(i2c_sda);
// }
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,508 @@
#pragma once
#include <Wire.h>
#include "wled.h"
#include <driver/i2s.h>
#include <driver/adc.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include <driver/adc_deprecated.h>
#include <driver/adc_types_deprecated.h>
#endif
//#include <driver/i2s_std.h>
//#include <driver/i2s_pdm.h>
//#include <driver/gpio.h>
/* ToDo: remove. ES7243 is controlled via compiler defines
Until this configuration is moved to the webinterface
*/
// if you have problems to get your microphone work on the left channel, uncomment the following line
//#define I2S_USE_RIGHT_CHANNEL // (experimental) define this to use right channel (digital mics only)
#ifdef I2S_USE_RIGHT_CHANNEL
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_RIGHT
#define I2S_MIC_CHANNEL_TEXT "right channel only."
#else
#define I2S_MIC_CHANNEL I2S_CHANNEL_FMT_ONLY_LEFT
#define I2S_MIC_CHANNEL_TEXT "left channel only."
#endif
// Uncomment the line below to utilize ADC1 _exclusively_ for I2S sound input.
// benefit: analog mic inputs will be sampled contiously -> better response times and less "glitches"
// WARNING: this option WILL lock-up your device in case that any other analogRead() operation is performed;
// for example if you want to read "analog buttons"
//#define I2S_GRAB_ADC1_COMPLETELY // (experimental) continously sample analog ADC microphone. WARNING will cause analogRead() lock-up
// data type requested from the I2S driver - currently we always use 32bit
//#define I2S_USE_16BIT_SAMPLES // (experimental) define this to request 16bit - more efficient but possibly less compatible
#ifdef I2S_USE_16BIT_SAMPLES
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_16BIT
#define I2S_datatype int16_t
#define I2S_unsigned_datatype uint16_t
#undef I2S_SAMPLE_DOWNSCALE_TO_16BIT
#else
#define I2S_SAMPLE_RESOLUTION I2S_BITS_PER_SAMPLE_32BIT
#define I2S_datatype int32_t
#define I2S_unsigned_datatype uint32_t
#define I2S_SAMPLE_DOWNSCALE_TO_16BIT
#endif
/* Interface class
AudioSource serves as base class for all microphone types
This enables accessing all microphones with one single interface
which simplifies the caller code
*/
class AudioSource {
public:
/* All public methods are virtual, so they can be overridden
Everything but the destructor is also removed, to make sure each mic
Implementation provides its version of this function
*/
virtual ~AudioSource() {};
/* Initialize
This function needs to take care of anything that needs to be done
before samples can be obtained from the microphone.
*/
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
/* Deinitialize
Release all resources and deactivate any functionality that is used
by this microphone
*/
virtual void deinitialize() = 0;
/* getSamples
Read num_samples from the microphone, and store them in the provided
buffer
*/
virtual void getSamples(float *buffer, uint16_t num_samples) = 0;
/* check if the audio source driver was initialized successfully */
virtual bool isInitialized(void) {return(_initialized);}
/* identify Audiosource type - I2S-ADC or I2S-digital */
typedef enum{Type_unknown=0, Type_I2SAdc=1, Type_I2SDigital=2} AudioSourceType;
virtual AudioSourceType getType(void) {return(Type_I2SDigital);} // default is "I2S digital source" - ADC type overrides this method
protected:
/* Post-process audio sample - currently on needed for I2SAdcSource*/
virtual I2S_datatype postProcessSample(I2S_datatype sample_in) {return(sample_in);} // default method can be overriden by instances (ADC) that need sample postprocessing
// Private constructor, to make sure it is not callable except from derived classes
AudioSource(int sampleRate, int blockSize) :
_sampleRate(sampleRate),
_blockSize(blockSize),
_initialized(false)
{};
int _sampleRate; // Microphone sampling rate
int _blockSize; // I2S block size
bool _initialized; // Gets set to true if initialization is successful
};
/* Basic I2S microphone source
All functions are marked virtual, so derived classes can replace them
*/
class I2SSource : public AudioSource {
public:
I2SSource(int sampleRate, int blockSize) :
AudioSource(sampleRate, blockSize) {
_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = _sampleRate,
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
.channel_format = I2S_MIC_CHANNEL,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
#else
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
#endif
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = _blockSize
};
}
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
return;
}
}
// i2ssckPin needs special treatment, since it might be unused on PDM mics
if (i2sckPin != I2S_PIN_NO_CHANGE) {
if (!pinManager.allocatePin(i2sckPin, true, PinOwner::UM_Audioreactive)) return;
} else {
// This is an I2S PDM microphone, these microphones only use a clock and
// data line, to make it simpler to debug, use the WS pin as CLK and SD
// pin as DATA
_config.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); // Change mode to pdm if clock pin not provided
}
// Reserve the master clock pin if provided
_mclkPin = mclkPin;
if (mclkPin != I2S_PIN_NO_CHANGE) {
if(!pinManager.allocatePin(mclkPin, true, PinOwner::UM_Audioreactive)) return;
_routeMclk(mclkPin);
}
_pinConfig = {
.bck_io_num = i2sckPin,
.ws_io_num = i2swsPin,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = i2ssdPin
};
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
return;
}
err = i2s_set_pin(I2S_NUM_0, &_pinConfig);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to set i2s pin config: %d\n", err);
return;
}
_initialized = true;
}
virtual void deinitialize() {
_initialized = false;
esp_err_t err = i2s_driver_uninstall(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
return;
}
if (_pinConfig.ws_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.ws_io_num, PinOwner::UM_Audioreactive);
if (_pinConfig.data_in_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.data_in_num, PinOwner::UM_Audioreactive);
if (_pinConfig.bck_io_num != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_pinConfig.bck_io_num, PinOwner::UM_Audioreactive);
// Release the master clock pin
if (_mclkPin != I2S_PIN_NO_CHANGE) pinManager.deallocatePin(_mclkPin, PinOwner::UM_Audioreactive);
}
virtual void getSamples(float *buffer, uint16_t num_samples) {
if (_initialized) {
esp_err_t err;
size_t bytes_read = 0; /* Counter variable to check if we actually got enough data */
I2S_datatype newSamples[num_samples]; /* Intermediary sample storage */
err = i2s_read(I2S_NUM_0, (void *)newSamples, sizeof(newSamples), &bytes_read, portMAX_DELAY);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to get samples: %d\n", err);
return;
}
// For correct operation, we need to read exactly sizeof(samples) bytes from i2s
if (bytes_read != sizeof(newSamples)) {
DEBUGSR_PRINTF("Failed to get enough samples: wanted: %d read: %d\n", sizeof(newSamples), bytes_read);
return;
}
// Store samples in sample buffer and update DC offset
for (int i = 0; i < num_samples; i++) {
newSamples[i] = postProcessSample(newSamples[i]); // perform postprocessing (needed for ADC samples)
float currSample = 0.0f;
#ifdef I2S_SAMPLE_DOWNSCALE_TO_16BIT
currSample = (float) newSamples[i] / 65536.0f; // 32bit input -> 16bit; keeping lower 16bits as decimal places
#else
currSample = (float) newSamples[i]; // 16bit input -> use as-is
#endif
buffer[i] = currSample;
}
}
}
protected:
void _routeMclk(int8_t mclkPin) {
/* Enable the mclk routing depending on the selected mclk pin
Only I2S_NUM_0 is supported
*/
if (mclkPin == GPIO_NUM_0) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
WRITE_PERI_REG(PIN_CTRL,0xFFF0);
} else if (mclkPin == GPIO_NUM_1) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_CLK_OUT3);
WRITE_PERI_REG(PIN_CTRL, 0xF0F0);
} else {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_CLK_OUT2);
WRITE_PERI_REG(PIN_CTRL, 0xFF00);
}
}
i2s_config_t _config;
i2s_pin_config_t _pinConfig;
int8_t _mclkPin;
};
/* ES7243 Microphone
This is an I2S microphone that requires ininitialization over
I2C before I2S data can be received
*/
class ES7243 : public I2SSource {
private:
// I2C initialization functions for ES7243
void _es7243I2cBegin() {
Wire.begin(pin_ES7243_SDA, pin_ES7243_SCL, 100000U);
}
void _es7243I2cWrite(uint8_t reg, uint8_t val) {
#ifndef ES7243_ADDR
Wire.beginTransmission(0x13);
#else
Wire.beginTransmission(ES7243_ADDR);
#endif
Wire.write((uint8_t)reg);
Wire.write((uint8_t)val);
Wire.endTransmission();
}
void _es7243InitAdc() {
_es7243I2cBegin();
_es7243I2cWrite(0x00, 0x01);
_es7243I2cWrite(0x06, 0x00);
_es7243I2cWrite(0x05, 0x1B);
_es7243I2cWrite(0x01, 0x00); // 0x00 for 24 bit to match INMP441 - not sure if this needs adjustment to get 16bit samples from I2S
_es7243I2cWrite(0x08, 0x43);
_es7243I2cWrite(0x05, 0x13);
}
public:
ES7243(int sampleRate, int blockSize) :
I2SSource(sampleRate, blockSize) {
_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
};
void initialize(int8_t sdaPin, int8_t sclPin, int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
// Reserve SDA and SCL pins of the I2C interface
if (!pinManager.allocatePin(sdaPin, true, PinOwner::HW_I2C) ||
!pinManager.allocatePin(sclPin, true, PinOwner::HW_I2C)) {
return;
}
pin_ES7243_SDA = sdaPin;
pin_ES7243_SCL = sclPin;
// First route mclk, then configure ADC over I2C, then configure I2S
_es7243InitAdc();
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
}
void deinitialize() {
// Release SDA and SCL pins of the I2C interface
pinManager.deallocatePin(pin_ES7243_SDA, PinOwner::HW_I2C);
pinManager.deallocatePin(pin_ES7243_SCL, PinOwner::HW_I2C);
I2SSource::deinitialize();
}
private:
int8_t pin_ES7243_SDA;
int8_t pin_ES7243_SCL;
};
/* ADC over I2S Microphone
This microphone is an ADC pin sampled via the I2S interval
This allows to use the I2S API to obtain ADC samples with high sample rates
without the need of manual timing of the samples
*/
class I2SAdcSource : public I2SSource {
public:
I2SAdcSource(int sampleRate, int blockSize) :
I2SSource(sampleRate, blockSize) {
_config = {
.mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_ADC_BUILT_IN),
.sample_rate = _sampleRate,
.bits_per_sample = I2S_SAMPLE_RESOLUTION,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
#else
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
#endif
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = _blockSize,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = 0
};
}
/* identify Audiosource type - I2S-ADC*/
AudioSourceType getType(void) {return(Type_I2SAdc);}
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
_myADCchannel = 0x0F;
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
return;
}
_audioPin = audioPin;
// Determine Analog channel. Only Channels on ADC1 are supported
int8_t channel = digitalPinToAnalogChannel(_audioPin);
if (channel > 9) {
DEBUGSR_PRINTF("Incompatible GPIO used for audio in: %d\n", _audioPin);
return;
} else {
adc_gpio_init(ADC_UNIT_1, adc_channel_t(channel));
_myADCchannel = channel;
}
// Install Driver
esp_err_t err = i2s_driver_install(I2S_NUM_0, &_config, 0, nullptr);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to install i2s driver: %d\n", err);
return;
}
// Enable I2S mode of ADC
err = i2s_set_adc_mode(ADC_UNIT_1, adc1_channel_t(channel));
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to set i2s adc mode: %d\n", err);
return;
}
// adc1_config_channel_atten(adc1_channel_t(channel), ADC_ATTEN_DB_11)); //see https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/I2S/HiFreq_ADC/HiFreq_ADC.ino
#if defined(I2S_GRAB_ADC1_COMPLETELY)
// according to docs from espressif, the ADC needs to be started explicitly
// fingers crossed
err = i2s_adc_enable(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
//return;
}
#else
err = i2s_adc_disable(I2S_NUM_0);
//err = i2s_stop(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to initially disable i2s adc: %d\n", err);
}
#endif
_initialized = true;
}
I2S_datatype postProcessSample(I2S_datatype sample_in) {
static I2S_datatype lastADCsample = 0; // last good sample
static unsigned int broken_samples_counter = 0; // number of consecutive broken (and fixed) ADC samples
I2S_datatype sample_out = 0;
// bring sample down down to 16bit unsigned
I2S_unsigned_datatype rawData = * reinterpret_cast<I2S_unsigned_datatype *> (&sample_in); // C++ acrobatics to get sample as "unsigned"
#ifndef I2S_USE_16BIT_SAMPLES
rawData = (rawData >> 16) & 0xFFFF; // scale input down from 32bit -> 16bit
I2S_datatype lastGoodSample = lastADCsample / 16384 ; // prepare "last good sample" accordingly (26bit-> 12bit with correct sign handling)
#else
rawData = rawData & 0xFFFF; // input is already in 16bit, just mask off possible junk
I2S_datatype lastGoodSample = lastADCsample * 4; // prepare "last good sample" accordingly (10bit-> 12bit)
#endif
// decode ADC sample data fields
uint16_t the_channel = (rawData >> 12) & 0x000F; // upper 4 bit = ADC channel
uint16_t the_sample = rawData & 0x0FFF; // lower 12bit -> ADC sample (unsigned)
I2S_datatype finalSample = (int(the_sample) - 2048); // convert unsigned sample to signed (centered at 0);
if ((the_channel != _myADCchannel) && (_myADCchannel != 0x0F)) { // 0x0F means "don't know what my channel is"
// fix bad sample
finalSample = lastGoodSample; // replace with last good ADC sample
broken_samples_counter ++;
if (broken_samples_counter > 256) _myADCchannel = 0x0F; // too many bad samples in a row -> disable sample corrections
//Serial.print("\n!ADC rogue sample 0x"); Serial.print(rawData, HEX); Serial.print("\tchannel:");Serial.println(the_channel);
} else broken_samples_counter = 0; // good sample - reset counter
// back to original resolution
#ifndef I2S_USE_16BIT_SAMPLES
finalSample = finalSample << 16; // scale up from 16bit -> 32bit;
#endif
finalSample = finalSample / 4; // mimic old analog driver behaviour (12bit -> 10bit)
sample_out = (3 * finalSample + lastADCsample) / 4; // apply low-pass filter (2-tap FIR)
//sample_out = (finalSample + lastADCsample) / 2; // apply stronger low-pass filter (2-tap FIR)
lastADCsample = sample_out; // update ADC last sample
return(sample_out);
}
void getSamples(float *buffer, uint16_t num_samples) {
/* Enable ADC. This has to be enabled and disabled directly before and
* after sampling, otherwise Wifi dies
*/
if (_initialized) {
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
// old code - works for me without enable/disable, at least on ESP32.
//esp_err_t err = i2s_start(I2S_NUM_0);
esp_err_t err = i2s_adc_enable(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to enable i2s adc: %d\n", err);
return;
}
#endif
I2SSource::getSamples(buffer, num_samples);
#if !defined(I2S_GRAB_ADC1_COMPLETELY)
// old code - works for me without enable/disable, at least on ESP32.
err = i2s_adc_disable(I2S_NUM_0); //i2s_adc_disable() may cause crash with IDF 4.4 (https://github.com/espressif/arduino-esp32/issues/6832)
//err = i2s_stop(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
return;
}
#endif
}
}
void deinitialize() {
pinManager.deallocatePin(_audioPin, PinOwner::UM_Audioreactive);
_initialized = false;
_myADCchannel = 0x0F;
esp_err_t err;
#if defined(I2S_GRAB_ADC1_COMPLETELY)
// according to docs from espressif, the ADC needs to be stopped explicitly
// fingers crossed
err = i2s_adc_disable(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to disable i2s adc: %d\n", err);
}
#endif
i2s_stop(I2S_NUM_0);
err = i2s_driver_uninstall(I2S_NUM_0);
if (err != ESP_OK) {
DEBUGSR_PRINTF("Failed to uninstall i2s driver: %d\n", err);
return;
}
}
private:
int8_t _audioPin;
int8_t _myADCchannel = 0x0F; // current ADC channel for analog input. 0x0F means "undefined"
};
/* SPH0645 Microphone
This is an I2S microphone with some timing quirks that need
special consideration.
*/
class SPH0654 : public I2SSource {
public:
SPH0654(int sampleRate, int blockSize) :
I2SSource(sampleRate, blockSize)
{}
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
REG_SET_BIT(I2S_TIMING_REG(I2S_NUM_0), BIT(9));
REG_SET_BIT(I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT);
}
};

View File

@ -0,0 +1,36 @@
# Audioreactive usermod
This usermod allows controlling LEDs using audio input. Audio input can be either microphone or analog-in (AUX) using appropriate adapter.
Supported microphones range from analog (MAX4466, MAX9814, ...) to digital (INMP441, ICS-43434, ...).
The usermod does audio processing and provides data structure that specially written effect can use.
The usermod **does not** provide effects or draws anything to LED strip/matrix.
## Installation
Add `-D USERMOD_AUDIOREACTIVE` to your PlatformIO environment as well as `arduinoFFT` to your `lib_deps`.
If you are not using PlatformIO (which you should) try adding `#define USERMOD_AUDIOREACTIVE` to *my_config.h* and make sure you have _arduinoFFT_ library downloaded and installed.
Customised _arduinoFFT_ library for use with this usermod can be found at https://github.com/blazoncek/arduinoFFT.git
## Configuration
All parameters are runtime configurable though some may require hard boot after change (I2S microphone or selected GPIOs).
If you want to define default GPIOs during compile time use the following (default values in parentheses):
- `DMTYPE=x` : defines digital microphone type: 0=analog, 1=generic I2S, 2=ES7243 I2S, 3=SPH0645 I2S, 4=generic I2S with master clock, 5=PDM I2S
- `AUDIOPIN=x` : GPIO for analog microphone/AUX-in (36)
- `I2S_SDPIN=x` : GPIO for SD pin on digital mcrophone (32)
- `I2S_WSPIN=x` : GPIO for WS pin on digital mcrophone (15)
- `I2S_CKPIN=x` : GPIO for SCK pin on digital mcrophone (14)
- `ES7243_SDAPIN` : GPIO for I2C SDA pin on ES7243 microphone (-1)
- `ES7243_SCLPIN` : GPIO for I2C SCL pin on ES7243 microphone (-1)
- `MCLK_PIN=x` : GPIO for master clock pin on digital mcrophone (-1)
**NOTE** Due to the fact that usermod uses I2S peripherial for analog audio sampling, use of analog *buttons* (i.e. potentiometers) is disabled while running this usermod with analog microphone.
## Release notes
2022-06 Ported from [soundreactive](https://github.com/atuline/WLED) by @blazoncek (AKA Blaz Kristan)

View File

@ -42,14 +42,6 @@
#include "Wire.h"
#endif
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#endif
// ================================================================
// === INTERRUPT DETECTION ROUTINE ===
// ================================================================
@ -93,7 +85,7 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point.
*/
void setup() {
PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } };
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
// join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
@ -258,20 +250,20 @@ class MPU6050Driver : public Usermod {
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void addToJsonState(JsonObject& root)
{
//void addToJsonState(JsonObject& root)
//{
//root["user0"] = userVar0;
}
//}
/*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
void readFromJsonState(JsonObject& root)
{
//void readFromJsonState(JsonObject& root)
//{
//if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!"));
}
//}
/*
@ -279,13 +271,13 @@ class MPU6050Driver : public Usermod {
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* 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("MPU6050_IMU");
JsonArray pins = top.createNestedArray("pin");
pins.add(HW_PIN_SCL);
pins.add(HW_PIN_SDA);
}
// void addToConfig(JsonObject& root)
// {
// JsonObject top = root.createNestedObject("MPU6050_IMU");
// JsonArray pins = top.createNestedArray("pin");
// pins.add(HW_PIN_SCL);
// pins.add(HW_PIN_SDA);
// }
/*
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).

View File

@ -468,13 +468,17 @@ class MultiRelay : public Usermod {
if (user.isNull())
user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
JsonArray infoArr = user.createNestedArray(FPSTR(_name)); //name
infoArr.add(String(getActiveRelayCount()));
infoArr.add(F(" relays"));
String uiDomString;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0 || !_relay[i].external) continue;
uiDomString = F("<button class=\"btn\" onclick=\"requestJson({");
uiDomString = F("Relay "); uiDomString += i;
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);
uiDomString += F(":{");
uiDomString += FPSTR(_relay_str);
@ -483,12 +487,10 @@ class MultiRelay : public Usermod {
uiDomString += F(",on:");
uiDomString += _relay[i].state ? "false" : "true";
uiDomString += F("}});\">");
uiDomString += F("Relay ");
uiDomString += i;
uiDomString += F(" <i class=\"icons\">&#xe08f;</i></button>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
infoArr.add(_relay[i].state ? "on" : "off");
uiDomString += F("<i class=\"icons");
uiDomString += _relay[i].state ? F(" on") : F(" off");
uiDomString += F("\">&#xe08f;</i></button>");
infoArr.add(uiDomString);
}
}
}

View File

@ -96,7 +96,7 @@ class StairwayWipeUsermod : public Usermod {
resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
WS2812FX::Segment& seg = strip.getSegment(0);
Segment& seg = strip.getSegment(0);
bool doReverse = (userVar0 == 2);
seg.setOption(1, doReverse);

View File

@ -89,7 +89,7 @@ void startWipe()
resetTimebase(); //make sure wipe starts from beginning
//set wipe direction
WS2812FX::Segment& seg = strip.getSegment(0);
Segment& seg = strip.getSegment(0);
bool doReverse = (userVar0 == 2);
seg.setOption(1, doReverse);

View File

@ -24,54 +24,31 @@
//
//The SCL and SDA pins are defined here.
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL i2c_scl
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA i2c_sda
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI spi_sclk
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI spi_mosi
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS spi_cs
#endif
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#define HW_PIN_CLOCKSPI 18
#define HW_PIN_DATASPI 23
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 21
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 18
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 23
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 19
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 5
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 26
#endif
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#define HW_PIN_CLOCKSPI 14
#define HW_PIN_DATASPI 13
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 4
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 14
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 13
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 12
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 15
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 16
@ -192,13 +169,14 @@ class FourLineDisplayUsermod : public Usermod {
bool isHW;
PinOwner po = PinOwner::UM_FourLineDisplay;
if (type == SSD1306_SPI || type == SSD1306_SPI64) {
isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI);
isHW = (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi);
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
} else {
isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
isHW = (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda);
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
}
@ -718,8 +696,14 @@ class FourLineDisplayUsermod : public Usermod {
if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8;
PinOwner po = PinOwner::UM_FourLineDisplay;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
if (isSPI) {
if (ioPin[0]==spi_sclk && ioPin[1]==spi_mosi) po = PinOwner::HW_SPI; // allow multiple allocations of HW SPI bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 5, po);
} else {
if (ioPin[0]==i2c_scl && ioPin[1]==i2c_sda) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po);
}
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE;

View File

@ -25,54 +25,32 @@
//
//The SCL and SDA pins are defined here.
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL i2c_scl
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA i2c_sda
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI spi_sclk
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI spi_mosi
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS spi_cs
#endif
#ifdef ARDUINO_ARCH_ESP32
#define HW_PIN_SCL 22
#define HW_PIN_SDA 21
#define HW_PIN_CLOCKSPI 18
#define HW_PIN_DATASPI 23
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 21
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 18
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 23
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 19
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 5
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 26
#endif
#else
#define HW_PIN_SCL 5
#define HW_PIN_SDA 4
#define HW_PIN_CLOCKSPI 14
#define HW_PIN_DATASPI 13
#ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5
#endif
#ifndef FLD_PIN_SDA
#define FLD_PIN_SDA 4
#endif
#ifndef FLD_PIN_CLOCKSPI
#define FLD_PIN_CLOCKSPI 14
#endif
#ifndef FLD_PIN_DATASPI
#define FLD_PIN_DATASPI 13
#endif
#ifndef FLD_PIN_DC
#define FLD_PIN_DC 12
#endif
#ifndef FLD_PIN_CS
#define FLD_PIN_CS 15
#endif
#ifndef FLD_PIN_RESET
#define FLD_PIN_RESET 16
@ -92,13 +70,20 @@
#define SCREEN_TIMEOUT_MS 60*1000 // 1 min
// Minimum time between redrawing screen in ms
#define USER_LOOP_REFRESH_RATE_MS 1000
#define REFRESH_RATE_MS 1000
// Extra char (+1) for null
#define LINE_BUFFER_SIZE 16+1
#define MAX_JSON_CHARS 19+1
#define MAX_MODE_LINE_SPACE 13+1
#ifdef ARDUINO_ARCH_ESP32
static TaskHandle_t Display_Task = nullptr;
void DisplayTaskCode(void * parameter);
#endif
typedef enum {
NONE = 0,
SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C
@ -112,10 +97,15 @@ typedef enum {
class FourLineDisplayUsermod : public Usermod {
public:
FourLineDisplayUsermod() { if (!instance) instance = this; }
static FourLineDisplayUsermod* getInstance(void) { return instance; }
private:
static FourLineDisplayUsermod *instance;
bool initDone = false;
volatile bool drawing = false;
// HW interface & configuration
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
@ -132,8 +122,8 @@ class FourLineDisplayUsermod : public Usermod {
bool flip = false; // flip display 180°
uint8_t contrast = 10; // screen contrast
uint8_t lineHeight = 1; // 1 row or 2 rows
uint16_t refreshRate = USER_LOOP_REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
uint16_t refreshRate = REFRESH_RATE_MS; // in ms
uint32_t screenTimeout = SCREEN_TIMEOUT_MS; // in ms
bool sleepMode = true; // allow screen sleep?
bool clockMode = false; // display clock
bool showSeconds = true; // display clock with seconds
@ -195,6 +185,123 @@ class FourLineDisplayUsermod : public Usermod {
u8x8_cad_EndTransfer(u8x8_struct);
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string);
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string);
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
}
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph);
}
uint8_t getCols() {
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
if (type == NONE || !enabled) return;
u8x8->clear();
}
void setPowerSave(uint8_t save) {
if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
}
void draw2x2GlyphIcons() {
if (lineHeight == 2) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon
drawGlyph(14, 2*lineHeight, 4, u8x8_4LineDisplay_WLED_icons_2x2, true); //palette icon
drawGlyph(14, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon
} else {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x1); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x1); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x1); //intensity icon
drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
}
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime() {
if (type == NONE || !enabled || !displayTurnedOff) return;
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
char lineBuffer[LINE_BUFFER_SIZE];
static byte lastSecond;
byte secondCurrent = second(localTime);
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
if (knownMinute != minuteCurrent) { //only redraw clock if it has changed
//updateLocalTime();
byte AmPmHour = hourCurrent;
boolean isitAM = true;
if (useAMPM) {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; }
}
if (knownHour != hourCurrent) {
// only update date when hour changes
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
}
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
drawStatusIcons(); //icons power, wifi, timer, etc
knownMinute = minuteCurrent;
knownHour = hourCurrent;
}
if (showSeconds && secondCurrent != lastSecond) {
lastSecond = secondCurrent;
draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":");
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
}
drawing = false;
}
public:
// gets called once at boot. Do all initialization that doesn't depend on
@ -205,13 +312,32 @@ class FourLineDisplayUsermod : public Usermod {
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
PinOwner po = PinOwner::UM_FourLineDisplay;
if (isSPI) {
isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI);
PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }};
if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
} else {
isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk;
uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi;
if (ioPin[0] < 0 || ioPin[1] < 0) {
ioPin[0] = hw_sclk;
ioPin[1] = hw_mosi;
}
isHW = (ioPin[0]==hw_sclk && ioPin[1]==hw_mosi);
PinManagerPinType cspins[3] = { { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true } };
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
if (isHW) po = PinOwner::HW_SPI; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) {
pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay);
type = NONE;
return;
}
} else {
uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl;
uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda;
if (ioPin[0] < 0 || ioPin[1] < 0) {
ioPin[0] = hw_scl;
ioPin[1] = hw_sda;
}
isHW = (ioPin[0]==hw_scl && ioPin[1]==hw_sda);
if (isHW) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
PinManagerPinType pins[2] = { {ioPin[0], true }, { ioPin[1], true } };
if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; }
}
@ -304,6 +430,7 @@ class FourLineDisplayUsermod : public Usermod {
setPowerSave(0);
//drawString(0, 0, "Loading...");
overlayLogo(3500);
onUpdateBegin(false); // create Display task
initDone = true;
}
@ -319,63 +446,13 @@ class FourLineDisplayUsermod : public Usermod {
* Da loop.
*/
void loop() {
#ifndef ARDUINO_ARCH_ESP32
if (!enabled || strip.isUpdating()) return;
unsigned long now = millis();
if (now < nextUpdate) return;
nextUpdate = now + ((displayTurnedOff && clockMode && showSeconds) ? 1000 : refreshRate);
redraw(false);
}
/**
* Wrappers for screen drawing
*/
void setFlipMode(uint8_t mode) {
if (type == NONE || !enabled) return;
u8x8->setFlipMode(mode);
}
void setContrast(uint8_t contrast) {
if (type == NONE || !enabled) return;
u8x8->setContrast(contrast);
}
void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string);
}
void draw2x2String(uint8_t col, uint8_t row, const char *string) {
if (type == NONE || !enabled) return;
u8x8->setFont(u8x8_font_chroma48medium8_r);
u8x8->draw2x2String(col, row, string);
}
void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph);
}
void draw2x2Glyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font) {
if (type == NONE || !enabled) return;
u8x8->setFont(font);
u8x8->draw2x2Glyph(col, row, glyph);
}
uint8_t getCols() {
if (type==NONE || !enabled) return 0;
return u8x8->getCols();
}
void clear() {
if (type == NONE || !enabled) return;
u8x8->clear();
}
void setPowerSave(uint8_t save) {
if (type == NONE || !enabled) return;
u8x8->setPowerSave(save);
}
void center(String &line, uint8_t width) {
int len = line.length();
if (len<width) for (byte i=(width-len)/2; i>0; i--) line = ' ' + line;
for (byte i=line.length(); i<width; i++) line += ' ';
#endif
}
//function to update lastredraw
@ -404,6 +481,8 @@ class FourLineDisplayUsermod : public Usermod {
}
}
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
if (apActive && WLED_WIFI_CONFIGURED && now<15000) {
knownSsid = apSSID;
networkOverlay(PSTR("NETWORK INFO"),30000);
@ -529,22 +608,6 @@ class FourLineDisplayUsermod : public Usermod {
}
}
void draw2x2GlyphIcons() {
if (lineHeight == 2) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon
drawGlyph(14, 2*lineHeight, 4, u8x8_4LineDisplay_WLED_icons_2x2, true); //palette icon
drawGlyph(14, 3*lineHeight, 5, u8x8_4LineDisplay_WLED_icons_2x2, true); //effect icon
} else {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x1); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x1); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x1); //intensity icon
drawGlyph(15, 2, 4, u8x8_4LineDisplay_WLED_icons_1x1); //palette icon
drawGlyph(15, 3, 5, u8x8_4LineDisplay_WLED_icons_1x1); //effect icon
}
}
void drawStatusIcons() {
uint8_t col = 15;
uint8_t row = 0;
@ -570,8 +633,8 @@ class FourLineDisplayUsermod : public Usermod {
if (markColNum != 255 && markLineNum !=255) drawGlyph(markColNum, markLineNum*lineHeight, 21, u8x8_4LineDisplay_WLED_icons_1x1);
}
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
//Display the current effect or palette (desiredEntry)
// on the appropriate line (row).
void showCurrentEffectOrPalette(int inputEffPal, const char *qstring, uint8_t row) {
char lineBuffer[MAX_JSON_CHARS];
if (overlayUntil == 0) {
@ -581,6 +644,10 @@ class FourLineDisplayUsermod : public Usermod {
// remove "* " from dynamic palettes
for (byte i=2; i<=printedChars; i++) lineBuffer[i-2] = lineBuffer[i]; //include '\0'
printedChars -= 2;
} else if ((lineBuffer[0]==' ' && lineBuffer[1]>127)) {
// remove note symbol from effect names
for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0'
printedChars -= 5;
}
if (lineHeight == 2) { // use this code for 8 line display
char smallBuffer1[MAX_MODE_LINE_SPACE];
@ -635,10 +702,14 @@ class FourLineDisplayUsermod : public Usermod {
bool wakeDisplay() {
if (type == NONE || !enabled) return false;
if (displayTurnedOff) {
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
clear();
// Turn the display back on
sleepOrClock(false);
//lastRedraw = millis();
drawing = false;
return true;
}
return false;
@ -650,6 +721,9 @@ class FourLineDisplayUsermod : public Usermod {
* Used in Rotary Encoder usermod.
*/
void overlay(const char* line1, long showHowLong, byte glyphType) {
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
// Turn the display back on
if (!wakeDisplay()) clear();
// Print the overlay
@ -663,6 +737,7 @@ class FourLineDisplayUsermod : public Usermod {
drawString(0, (glyphType<255?3:0)*lineHeight, buf.c_str());
}
overlayUntil = millis() + showHowLong;
drawing = false;
}
/**
@ -670,6 +745,9 @@ class FourLineDisplayUsermod : public Usermod {
* Clears the screen and prints.
*/
void overlayLogo(long showHowLong) {
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
// Turn the display back on
if (!wakeDisplay()) clear();
// Print the overlay
@ -719,6 +797,7 @@ class FourLineDisplayUsermod : public Usermod {
}
}
overlayUntil = millis() + showHowLong;
drawing = false;
}
/**
@ -727,6 +806,9 @@ class FourLineDisplayUsermod : public Usermod {
* Used in Auto Save usermod
*/
void overlay(const char* line1, const char* line2, long showHowLong) {
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
// Turn the display back on
if (!wakeDisplay()) clear();
// Print the overlay
@ -741,9 +823,14 @@ class FourLineDisplayUsermod : public Usermod {
drawString(0, 2*lineHeight, buf.c_str());
}
overlayUntil = millis() + showHowLong;
drawing = false;
}
void networkOverlay(const char* line1, long showHowLong) {
unsigned long now = millis();
while (drawing && millis()-now < 250) delay(1); // wait if someone else is drawing
drawing = true;
String line;
// Turn the display back on
if (!wakeDisplay()) clear();
@ -774,6 +861,7 @@ class FourLineDisplayUsermod : public Usermod {
center(line, getCols());
drawString(0, lineHeight*3, line.c_str());
overlayUntil = millis() + showHowLong;
drawing = false;
}
@ -794,52 +882,6 @@ class FourLineDisplayUsermod : public Usermod {
}
}
/**
* Display the current date and time in large characters
* on the middle rows. Based 24 or 12 hour depending on
* the useAMPM configuration.
*/
void showTime() {
if (type == NONE || !enabled || !displayTurnedOff) return;
char lineBuffer[LINE_BUFFER_SIZE];
static byte lastSecond;
byte secondCurrent = second(localTime);
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
if (knownMinute != minuteCurrent) { //only redraw clock if it has changed
//updateLocalTime();
byte AmPmHour = hourCurrent;
boolean isitAM = true;
if (useAMPM) {
if (AmPmHour > 11) { AmPmHour -= 12; isitAM = false; }
if (AmPmHour == 0) { AmPmHour = 12; }
}
if (knownHour != hourCurrent) {
// only update date when hour changes
sprintf_P(lineBuffer, PSTR("%s %2d "), monthShortStr(month(localTime)), day(localTime));
draw2x2String(2, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays, draw month and day
}
sprintf_P(lineBuffer,PSTR("%2d:%02d"), (useAMPM ? AmPmHour : hourCurrent), minuteCurrent);
draw2x2String(2, lineHeight*2, lineBuffer); //draw hour, min. blink ":" depending on odd/even seconds
if (useAMPM) drawString(12, lineHeight*2, (isitAM ? "AM" : "PM"), true); //draw am/pm if using 12 time
drawStatusIcons(); //icons power, wifi, timer, etc
knownMinute = minuteCurrent;
knownHour = hourCurrent;
} else {
if (secondCurrent == lastSecond) return;
}
if (showSeconds) {
lastSecond = secondCurrent;
draw2x2String(6, lineHeight*2, secondCurrent%2 ? " " : ":");
sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent);
drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line
}
}
/**
* handleButton() can be used to override default button behaviour. Returning true
* will prevent button working in a default way.
@ -913,7 +955,44 @@ class FourLineDisplayUsermod : public Usermod {
}
return handled;
}
#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif
void onUpdateBegin(bool init) {
#ifdef ARDUINO_ARCH_ESP32
if (init && Display_Task) {
vTaskSuspend(Display_Task); // update is about to begin, disable task to prevent crash
} else {
// update has failed or create task requested
if (Display_Task)
vTaskResume(Display_Task);
else
xTaskCreatePinnedToCore(
[](void * par) { // Function to implement the task
// see https://www.freertos.org/vtaskdelayuntil.html
const TickType_t xFrequency = REFRESH_RATE_MS * portTICK_PERIOD_MS / 2;
TickType_t xLastWakeTime = xTaskGetTickCount();
for(;;) {
delay(1); // DO NOT DELETE THIS LINE! It is needed to give the IDLE(0) task enough time and to keep the watchdog happy.
// taskYIELD(), yield(), vTaskDelay() and esp_task_wdt_feed() didn't seem to work.
vTaskDelayUntil(&xLastWakeTime, xFrequency); // release CPU, by doing nothing for REFRESH_RATE_MS millis
FourLineDisplayUsermod::getInstance()->redraw(false);
}
},
"4LD", // Name of the task
3072, // Stack size in words
NULL, // Task input parameter
1, // Priority of the task (not idle)
&Display_Task, // Task handle
ARDUINO_RUNNING_CORE
);
}
#endif
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
@ -951,8 +1030,8 @@ class FourLineDisplayUsermod : public Usermod {
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'I2C/SPI CLK');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'I2C/SPI DTA');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'I2C/SPI CLK (-1 use global)');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'I2C/SPI DTA (-1 use global)');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',2,'SPI CS');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',3,'SPI DC');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',4,'SPI RST');"));
@ -981,7 +1060,9 @@ class FourLineDisplayUsermod : public Usermod {
top[FPSTR(_flip)] = (bool) flip;
top[FPSTR(_contrast)] = contrast;
top[FPSTR(_contrastFix)] = (bool) contrastFix;
#ifndef ARDUINO_ARCH_ESP32
top[FPSTR(_refreshRate)] = refreshRate;
#endif
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
@ -1001,7 +1082,7 @@ class FourLineDisplayUsermod : public Usermod {
bool readFromConfig(JsonObject& root) {
bool needsRedraw = false;
DisplayType newType = type;
int8_t newPin[5]; for (byte i=0; i<5; i++) newPin[i] = ioPin[i];
int8_t oldPin[5]; for (byte i=0; i<5; i++) oldPin[i] = ioPin[i];
JsonObject top = root[FPSTR(_name)];
if (top.isNull()) {
@ -1012,11 +1093,13 @@ class FourLineDisplayUsermod : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled;
newType = top["type"] | newType;
for (byte i=0; i<5; i++) newPin[i] = top["pin"][i] | ioPin[i];
for (byte i=0; i<5; i++) ioPin[i] = top["pin"][i] | ioPin[i];
flip = top[FPSTR(_flip)] | flip;
contrast = top[FPSTR(_contrast)] | contrast;
#ifndef ARDUINO_ARCH_ESP32
refreshRate = top[FPSTR(_refreshRate)] | refreshRate;
refreshRate = min(5000, max(250, (int)refreshRate));
#endif
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
@ -1030,24 +1113,31 @@ class FourLineDisplayUsermod : public Usermod {
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
type = newType;
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
for (byte i=0; i<5; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8;
PinOwner po = PinOwner::UM_FourLineDisplay;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po);
for (byte i=0; i<5; i++) ioPin[i] = newPin[i];
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE;
return true;
} else type = newType;
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
if (isSPI) {
pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po);
uint8_t hw_sclk = spi_sclk<0 ? HW_PIN_CLOCKSPI : spi_sclk;
uint8_t hw_mosi = spi_mosi<0 ? HW_PIN_DATASPI : spi_mosi;
bool isHW = (oldPin[0]==hw_sclk && oldPin[1]==hw_mosi);
if (isHW) po = PinOwner::HW_SPI;
} else {
uint8_t hw_scl = i2c_scl<0 ? HW_PIN_SCL : i2c_scl;
uint8_t hw_sda = i2c_sda<0 ? HW_PIN_SDA : i2c_sda;
bool isHW = (oldPin[0]==hw_scl && oldPin[1]==hw_sda);
if (isHW) po = PinOwner::HW_I2C;
}
pinManager.deallocateMultiplePins((const uint8_t *)oldPin, 2, po);
type = newType;
setup();
needsRedraw |= true;
} else {
@ -1085,3 +1175,5 @@ const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_showSeconds[] PROGMEM = "showSeconds";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
const char FourLineDisplayUsermod::_contrastFix[] PROGMEM = "contrastFix";
FourLineDisplayUsermod *FourLineDisplayUsermod::instance = nullptr;

View File

@ -47,7 +47,7 @@
// The last UI state, remove color and saturation option if diplay not active(too many options)
#ifdef USERMOD_FOUR_LINE_DISPLAY
#define LAST_UI_STATE 8
#define LAST_UI_STATE 11
#else
#define LAST_UI_STATE 4
#endif
@ -56,7 +56,7 @@
#define MODE_SORT_SKIP_COUNT 1
// Which list is being sorted
static char **listBeingSorted;
static const char **listBeingSorted;
/**
* Modes and palettes are stored as strings that
@ -65,8 +65,8 @@ static char **listBeingSorted;
* JSON_mode_names or JSON_palette_names.
*/
static int re_qstringCmp(const void *ap, const void *bp) {
char *a = listBeingSorted[*((byte *)ap)];
char *b = listBeingSorted[*((byte *)bp)];
const char *a = listBeingSorted[*((byte *)ap)];
const char *b = listBeingSorted[*((byte *)bp)];
int i = 0;
do {
char aVal = pgm_read_byte_near(a + i);
@ -139,13 +139,13 @@ private:
#endif
// Pointers the start of the mode names within JSON_mode_names
char **modes_qstrings = nullptr;
const char **modes_qstrings = nullptr;
// Array of mode indexes in alphabetical order.
byte *modes_alpha_indexes = nullptr;
// Pointers the start of the palette names within JSON_palette_names
char **palettes_qstrings = nullptr;
const char **palettes_qstrings = nullptr;
// Array of palette indexes in alphabetical order.
byte *palettes_alpha_indexes = nullptr;
@ -185,7 +185,8 @@ private:
* modes_alpha_indexes and palettes_alpha_indexes.
*/
void sortModesAndPalettes() {
modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
//modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
modes_qstrings = strip.getModeDataSrc();
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
@ -211,8 +212,8 @@ private:
* Return an array of mode or palette names from the JSON string.
* They don't end in '\0', they end in '"'.
*/
char **re_findModeStrings(const char json[], int numModes) {
char **modeStrings = (char **)malloc(sizeof(char *) * numModes);
const char **re_findModeStrings(const char json[], int numModes) {
const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes);
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
@ -249,7 +250,8 @@ private:
/**
* Sort either the modes or the palettes using quicksort.
*/
void re_sortModes(char **modeNames, byte *indexes, int count, int numSkip) {
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
if (!modeNames) return;
listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr;
@ -285,7 +287,7 @@ public:
if (!initDone) sortModesAndPalettes();
#ifdef USERMOD_FOUR_LINE_DISPLAY
#ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod uses FourLineDisplayUsermod for the best experience.
// But it's optional. But you want it.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
@ -365,19 +367,43 @@ public:
if (buttonWaitTime && currentTime-buttonWaitTime>350 && !buttonPressedBefore) { //same speed as in button.cpp
buttonWaitTime = 0;
char newState = select_state + 1;
bool changedState = true;
if (newState > LAST_UI_STATE || (newState == 8 && presetHigh==0 && presetLow == 0)) newState = 0;
bool changedState = false;
char lineBuffer[64];
do {
// finde new state
switch (newState) {
case 0: strcpy_P(lineBuffer, PSTR("Brightness")); changedState = true; break;
case 1: if (!extractModeSlider(effectCurrent, 0, lineBuffer, 63)) newState++; else changedState = true; break; // speed
case 2: if (!extractModeSlider(effectCurrent, 1, lineBuffer, 63)) newState++; else changedState = true; break; // intensity
case 3: strcpy_P(lineBuffer, PSTR("Color Palette")); changedState = true; break;
case 4: strcpy_P(lineBuffer, PSTR("Effect")); changedState = true; break;
case 5: strcpy_P(lineBuffer, PSTR("Main Color")); changedState = true; break;
case 6: strcpy_P(lineBuffer, PSTR("Saturation")); changedState = true; break;
case 7:
if (!(strip.getSegment(applyToAll ? strip.getFirstSelectedSegId() : strip.getMainSegmentId()).getLightCapabilities() & 0x04)) newState++;
else { strcpy_P(lineBuffer, PSTR("CCT")); changedState = true; }
break;
case 8: if (presetHigh==0 || presetLow == 0) newState++; else { strcpy_P(lineBuffer, PSTR("Preset")); changedState = true; } break;
case 9:
case 10:
case 11: if (!extractModeSlider(effectCurrent, newState-7, lineBuffer, 63)) newState++; else changedState = true; break; // custom
}
if (newState > LAST_UI_STATE) newState = 0;
} while (!changedState);
if (display != nullptr) {
switch (newState) {
case 0: changedState = changeState(PSTR("Brightness"), 1, 0, 1); break; //1 = sun
case 1: changedState = changeState(PSTR("Speed"), 1, 4, 2); break; //2 = skip forward
case 2: changedState = changeState(PSTR("Intensity"), 1, 8, 3); break; //3 = fire
case 3: changedState = changeState(PSTR("Color Palette"), 2, 0, 4); break; //4 = custom palette
case 4: changedState = changeState(PSTR("Effect"), 3, 0, 5); break; //5 = puzzle piece
case 5: changedState = changeState(PSTR("Main Color"), 255, 255, 7); break; //7 = brush
case 6: changedState = changeState(PSTR("Saturation"), 255, 255, 8); break; //8 = contrast
case 7: changedState = changeState(PSTR("CCT"), 255, 255, 10); break; //10 = star
case 8: changedState = changeState(PSTR("Preset"), 255, 255, 11); break; //11 = heart
case 0: changedState = changeState(lineBuffer, 1, 0, 1); break; //1 = sun
case 1: changedState = changeState(lineBuffer, 1, 4, 2); break; //2 = skip forward
case 2: changedState = changeState(lineBuffer, 1, 8, 3); break; //3 = fire
case 3: changedState = changeState(lineBuffer, 2, 0, 4); break; //4 = custom palette
case 4: changedState = changeState(lineBuffer, 3, 0, 5); break; //5 = puzzle piece
case 5: changedState = changeState(lineBuffer, 255, 255, 7); break; //7 = brush
case 6: changedState = changeState(lineBuffer, 255, 255, 8); break; //8 = contrast
case 7: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star
case 8: changedState = changeState(lineBuffer, 255, 255, 11); break; //11 = heart
case 9: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star
case 10: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star
case 11: changedState = changeState(lineBuffer, 255, 255, 10); break; //10 = star
}
}
if (changedState) select_state = newState;
@ -390,29 +416,35 @@ public:
if (Enc_B == LOW) //changes to LOW so that then encoder registers a change at the very end of a pulse
{ // B is high so clockwise
switch(select_state) {
case 0: changeBrightness(true); break;
case 1: changeEffectSpeed(true); break;
case 2: changeEffectIntensity(true); break;
case 3: changePalette(true); break;
case 4: changeEffect(true); break;
case 5: changeHue(true); break;
case 6: changeSat(true); break;
case 7: changeCCT(true); break;
case 8: changePreset(true); break;
case 0: changeBrightness(true); break;
case 1: changeEffectSpeed(true); break;
case 2: changeEffectIntensity(true); break;
case 3: changePalette(true); break;
case 4: changeEffect(true); break;
case 5: changeHue(true); break;
case 6: changeSat(true); break;
case 7: changeCCT(true); break;
case 8: changePreset(true); break;
case 9: changeCustom(1,true); break;
case 10: changeCustom(2,true); break;
case 11: changeCustom(3,true); break;
}
}
else if (Enc_B == HIGH)
{ // B is low so counter-clockwise
switch(select_state) {
case 0: changeBrightness(false); break;
case 1: changeEffectSpeed(false); break;
case 2: changeEffectIntensity(false); break;
case 3: changePalette(false); break;
case 4: changeEffect(false); break;
case 5: changeHue(false); break;
case 6: changeSat(false); break;
case 7: changeCCT(false); break;
case 8: changePreset(false); break;
case 0: changeBrightness(false); break;
case 1: changeEffectSpeed(false); break;
case 2: changeEffectIntensity(false); break;
case 3: changePalette(false); break;
case 4: changeEffect(false); break;
case 5: changeHue(false); break;
case 6: changeSat(false); break;
case 7: changeCCT(false); break;
case 8: changePreset(false); break;
case 9: changeCustom(1,false); break;
case 10: changeCustom(2,false); break;
case 11: changeCustom(3,false); break;
}
}
}
@ -496,13 +528,13 @@ public:
effectCurrent = modes_alpha_indexes[effectCurrentIndex];
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
strip.setMode(i, effectCurrent);
}
} else {
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
//Segment& seg = strip.getSegment(strip.getMainSegmentId());
strip.setMode(strip.getMainSegmentId(), effectCurrent);
}
lampUdated();
@ -524,13 +556,13 @@ public:
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.speed = effectSpeed;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.speed = effectSpeed;
}
lampUdated();
@ -552,13 +584,13 @@ public:
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.intensity = effectIntensity;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.intensity = effectIntensity;
}
lampUdated();
@ -568,6 +600,51 @@ public:
}
void changeCustom(uint8_t par, bool increase) {
uint8_t val = 0;
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
display->redraw(true);
// Throw away wake up input
return;
}
display->updateRedrawTime();
#endif
stateChanged = true;
if (applyToAll) {
uint8_t id = strip.getFirstSelectedSegId();
Segment& sid = strip.getSegment(id);
switch (par) {
case 3: val = sid.custom3 = max(min((increase ? sid.custom3+fadeAmount : sid.custom3-fadeAmount), 255), 0); break;
case 2: val = sid.custom2 = max(min((increase ? sid.custom2+fadeAmount : sid.custom2-fadeAmount), 255), 0); break;
default: val = sid.custom1 = max(min((increase ? sid.custom1+fadeAmount : sid.custom1-fadeAmount), 255), 0); break;
}
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || i == id) continue;
switch (par) {
case 3: seg.custom3 = sid.custom3; break;
case 2: seg.custom2 = sid.custom2; break;
default: seg.custom1 = sid.custom1; break;
}
}
} else {
Segment& seg = strip.getMainSegment();
switch (par) {
case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break;
case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break;
default: val = seg.custom1 = max(min((increase ? seg.custom1+fadeAmount : seg.custom1-fadeAmount), 255), 0); break;
}
}
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64];
sprintf(lineBuffer, "%d", val);
display->overlay(lineBuffer, 500, 10); // use star
#endif
}
void changePalette(bool increase) {
#ifdef USERMOD_FOUR_LINE_DISPLAY
if (display && display->wakeDisplay()) {
@ -581,13 +658,13 @@ public:
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.palette = effectPalette;
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.palette = effectPalette;
}
lampUdated();
@ -610,16 +687,21 @@ public:
colorHStoRGB(currentHue1*256, currentSat1, col);
stateChanged = true;
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64];
sprintf(lineBuffer, "%d", currentHue1);
display->overlay(lineBuffer, 500, 7); // use brush
#endif
}
void changeSat(bool increase){
@ -634,16 +716,21 @@ public:
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
} else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
}
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64];
sprintf(lineBuffer, "%d", currentSat1);
display->overlay(lineBuffer, 500, 8); // use contrast
#endif
}
void changePreset(bool increase) {
@ -656,6 +743,12 @@ public:
display->updateRedrawTime();
#endif
if (presetHigh && presetLow && presetHigh > presetLow) {
StaticJsonDocument<64> root;
char str[64];
sprintf_P(str, PSTR("%d~%d~%s"), presetLow, presetHigh, increase?"":"-");
root[F("ps")] = str;
deserializeState(root.as<JsonObject>(), CALL_MODE_BUTTON_PRESET);
/*
String apireq = F("win&PL=~");
if (!increase) apireq += '-';
apireq += F("&P1=");
@ -663,7 +756,12 @@ public:
apireq += F("&P2=");
apireq += presetHigh;
handleSet(nullptr, apireq, false);
*/
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
sprintf(str, "%d", currentPreset);
display->overlay(str, 500, 11); // use heart
#endif
}
}
@ -678,16 +776,21 @@ public:
#endif
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (byte i=0; i<strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue;
seg.setCCT(currentCCT, i);
seg.setCCT(currentCCT);
}
// } else {
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId());
// Segment& seg = strip.getSegment(strip.getMainSegmentId());
// seg.setCCT(currentCCT, strip.getMainSegmentId());
// }
lampUdated();
#ifdef USERMOD_FOUR_LINE_DISPLAY
char lineBuffer[64];
sprintf(lineBuffer, "%d", currentCCT);
display->overlay(lineBuffer, 500, 10); // use star
#endif
}
/*

View File

@ -27,14 +27,14 @@ saveMacro(1, "&FX=0&R=255&G=255&B=255", false);
//strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//select first two segments (background color + FX settable)
WS2812FX::Segment &seg = strip.getSegment(0);
Segment &seg = strip.getSegment(0);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((0 & 0xFF) << 8) | ((0 & 0xFF)));
strip.getSegment(0).setOption(0, false);
strip.getSegment(0).setOption(2, false);
//other segments are text
for (int i = 1; i < 10; i++)
{
WS2812FX::Segment &seg = strip.getSegment(i);
Segment &seg = strip.getSegment(i);
seg.colors[0] = ((0 << 24) | ((0 & 0xFF) << 16) | ((190 & 0xFF) << 8) | ((180 & 0xFF)));
strip.getSegment(i).setOption(0, true);
strip.setBrightness(128);
@ -50,7 +50,7 @@ void selectWordSegments(bool state)
{
for (int i = 1; i < 10; i++)
{
//WS2812FX::Segment &seg = strip.getSegment(i);
//Segment &seg = strip.getSegment(i);
strip.getSegment(i).setOption(0, state);
// strip.getSegment(1).setOption(SEG_OPTION_SELECTED, true);
//seg.mode = 12;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

513
wled00/FX_2Dfcn.cpp Normal file
View File

@ -0,0 +1,513 @@
/*
FX_2Dfcn.cpp contains all 2D utility functions
LICENSE
The MIT License (MIT)
Copyright (c) 2022 Blaz Kristan (https://blaz.at/home)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Parts of the code adapted from WLED Sound Reactive
*/
#include "wled.h"
#include "FX.h"
#include "palettes.h"
// setUpMatrix() - constructs ledmap array from matrix of panels with WxH pixels
// this converts physical (possibly irregular) LED arrangement into well defined
// array of logical pixels: fist entry corresponds to left-topmost logical pixel
// followed by horizontal pixels, when matrixWidth logical pixels are added they
// are followed by next row (down) of matrixWidth pixels (and so forth)
// note: matrix may be comprised of multiple panels each with different orientation
// but ledmap takes care of that. ledmap is constructed upon initialization
// so matrix should disable regular ledmap processing
void WS2812FX::setUpMatrix() {
#ifndef WLED_DISABLE_2D
// erase old ledmap, just in case.
if (customMappingTable != nullptr) delete[] customMappingTable;
customMappingTable = nullptr;
customMappingSize = 0;
if (isMatrix) {
matrixWidth = hPanels * panelW;
matrixHeight = vPanels * panelH;
// safety check
if (matrixWidth * matrixHeight > MAX_LEDS) {
matrixWidth = _length;
matrixHeight = 1;
isMatrix = false;
return;
}
customMappingSize = matrixWidth * matrixHeight;
customMappingTable = new uint16_t[customMappingSize];
if (customMappingTable != nullptr) {
uint16_t startL; // index in custom mapping array (logical strip)
uint16_t startP; // position of 1st pixel of panel on (virtual) strip
uint16_t x, y, offset;
uint8_t h = matrix.vertical ? vPanels : hPanels;
uint8_t v = matrix.vertical ? hPanels : vPanels;
for (uint8_t j=0, p=0; j<v; j++) {
for (uint8_t i=0; i<h; i++, p++) {
y = (matrix.vertical ? matrix.rightStart : matrix.bottomStart) ? v - j - 1 : j;
x = (matrix.vertical ? matrix.bottomStart : matrix.rightStart) ? h - i - 1 : i;
x = matrix.serpentine && j%2 ? h - x - 1 : x;
startL = (matrix.vertical ? y : x) * panelW + (matrix.vertical ? x : y) * matrixWidth * panelH; // logical index (top-left corner)
startP = p * panelW * panelH; // physical index (top-left corner)
uint8_t H = panel[h*j + i].vertical ? panelW : panelH;
uint8_t W = panel[h*j + i].vertical ? panelH : panelW;
for (uint16_t l=0, q=0; l<H; l++) {
for (uint16_t k=0; k<W; k++, q++) {
y = (panel[h*j + i].vertical ? panel[h*j + i].rightStart : panel[h*j + i].bottomStart) ? H - l - 1 : l;
x = (panel[h*j + i].vertical ? panel[h*j + i].bottomStart : panel[h*j + i].rightStart) ? W - k - 1 : k;
x = (panel[h*j + i].serpentine && l%2) ? (W - x - 1) : x;
offset = (panel[h*j + i].vertical ? y : x) + (panel[h*j + i].vertical ? x : y) * matrixWidth;
customMappingTable[startL + offset] = startP + q;
}
}
}
}
#ifdef WLED_DEBUG
DEBUG_PRINT(F("Matrix ledmap:"));
for (uint16_t i=0; i<customMappingSize; i++) {
if (!(i%matrixWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF("%4d,", customMappingTable[i]);
}
DEBUG_PRINTLN();
#endif
} else {
// memory allocation error
matrixWidth = _length;
matrixHeight = 1;
isMatrix = false;
return;
}
} else {
// not a matrix set up
matrixWidth = _length;
matrixHeight = 1;
}
#endif
}
// absolute matrix version of setPixelColor()
void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{
#ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * matrixWidth + x;
#else
uint16_t index = x;
#endif
if (index >= _length) return;
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
}
// returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D
uint16_t index = (y * matrixWidth + x);
#else
uint16_t index = x;
#endif
if (index >= _length) return 0;
if (index < customMappingSize) index = customMappingTable[index];
return busses.getPixelColor(index);
}
///////////////////////////////////////////////////////////
// Segment:: routines
///////////////////////////////////////////////////////////
#ifndef WLED_DISABLE_2D
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = virtualWidth(); // segment width in logical pixels
uint16_t height = virtualHeight(); // segment height in logical pixels
return (x%width) + (y%height) * width;
}
void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
{
if (!strip.isMatrix) return; // not a matrix set-up
if (leds) leds[XY(x,y)] = col;
uint8_t _bri_t = currentBri(on ? opacity : 0);
if (_bri_t < 255) {
byte r = scale8(R(col), _bri_t);
byte g = scale8(G(col), _bri_t);
byte b = scale8(B(col), _bri_t);
byte w = scale8(W(col), _bri_t);
col = RGBW32(r, g, b, w);
}
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
for (int j = 0; j < grouping; j++) { // groupping vertically
for (int g = 0; g < grouping; g++) { // groupping horizontally
uint16_t xX = (x+g), yY = (y+j);
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
strip.setPixelColorXY(start + xX, startY + yY, col);
if (mirror) { //set the corresponding horizontally mirrored pixel
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col);
}
}
}
}
// anti-aliased version of setPixelColorXY()
void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
{
if (!strip.isMatrix) return; // not a matrix set-up
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
float fX = x * (cols-1);
float fY = y * (rows-1);
if (aa) {
uint16_t xL = roundf(fX-0.49f);
uint16_t xR = roundf(fX+0.49f);
uint16_t yT = roundf(fY-0.49f);
uint16_t yB = roundf(fY+0.49f);
float dL = fX - xL;
float dR = xR - fX;
float dT = fY - yT;
float dB = yB - fY;
uint32_t cXLYT = getPixelColorXY(xL, yT);
uint32_t cXRYT = getPixelColorXY(xR, yT);
uint32_t cXLYB = getPixelColorXY(xL, yB);
uint32_t cXRYB = getPixelColorXY(xR, yB);
if (xL!=xR && yT!=yB) {
setPixelColorXY(xL, yT, color_blend(col, cXLYT, uint8_t(sqrtf(dL*dT)*255.0f))); // blend TL pixel
setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(sqrtf(dR*dT)*255.0f))); // blend TR pixel
setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(sqrtf(dL*dB)*255.0f))); // blend BL pixel
setPixelColorXY(xR, yB, color_blend(col, cXRYB, uint8_t(sqrtf(dR*dB)*255.0f))); // blend BR pixel
} else if (xR!=xL && yT==yB) {
setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dL*255.0f))); // blend L pixel
setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(dR*255.0f))); // blend R pixel
} else if (xR==xL && yT!=yB) {
setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dT*255.0f))); // blend T pixel
setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(dB*255.0f))); // blend B pixel
} else {
setPixelColorXY(xL, yT, col); // exact match (x & y land on a pixel)
}
} else {
setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col);
}
}
// returns RGBW values of pixel
uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
int i = XY(x,y);
if (leds) return RGBW32(leds[i].r, leds[i].g, leds[i].b, 0);
if (reverse ) x = virtualWidth() - x - 1;
if (reverse_y) y = virtualHeight() - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
if (x >= width() || y >= height()) return 0;
return strip.getPixelColorXY(start + x, startY + y);
}
// Blends the specified color with the existing pixel color.
void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) {
setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend));
}
// Adds the specified color with the existing pixel color perserving color balance.
void Segment::addPixelColorXY(int x, int y, uint32_t color) {
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color));
}
void Segment::fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade) {
CRGB pix = CRGB(getPixelColorXY(x,y)).nscale8_video(fade);
setPixelColor(x, y, pix);
}
// blurRow: perform a blur on a row of a rectangular matrix
void Segment::blurRow(uint16_t row, fract8 blur_amount) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (row >= rows) return;
// blur one row
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) {
CRGB cur = getPixelColorXY(x, row);
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (x) {
CRGB prev = CRGB(getPixelColorXY(x-1, row)) + part;
setPixelColorXY(x-1, row, prev);
}
setPixelColorXY(x, row, cur);
carryover = part;
}
}
// blurCol: perform a blur on a column of a rectangular matrix
void Segment::blurCol(uint16_t col, fract8 blur_amount) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (col >= cols) return;
// blur one column
uint8_t keep = 255 - blur_amount;
uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) {
CRGB cur = getPixelColorXY(col, i);
CRGB part = cur;
part.nscale8(seep);
cur.nscale8(keep);
cur += carryover;
if (i) {
CRGB prev = CRGB(getPixelColorXY(col, i-1)) + part;
setPixelColorXY(col, i-1, prev);
}
setPixelColorXY(col, i, cur);
carryover = part;
}
}
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const uint16_t dim1 = vertical ? rows : cols;
const uint16_t dim2 = vertical ? cols : rows;
if (i >= dim2) return;
const float seep = blur_amount/255.f;
const float keep = 3.f - 2.f*seep;
// 1D box blur
CRGB tmp[dim1];
for (uint16_t j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
uint16_t xp = vertical ? x : x-1;
uint16_t yp = vertical ? y-1 : y;
uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y);
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp);
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn);
uint16_t r, g, b;
r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b);
}
for (uint16_t j = 0; j < dim1; j++) {
uint16_t x = vertical ? i : j;
uint16_t y = vertical ? j : i;
setPixelColorXY(x, y, tmp[j]);
}
}
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
//
// 0 = no spread at all
// 64 = moderate spreading
// 172 = maximum smooth, even spreading
//
// 173..255 = wider spreading, but increasing flicker
//
// Total light is NOT entirely conserved, so many repeated
// calls to 'blur' will also result in the light fading,
// eventually all the way to black; this is by design so that
// it can be used to (slowly) clear the LEDs to black.
void Segment::blur1d(fract8 blur_amount) {
const uint16_t rows = virtualHeight();
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
}
void Segment::moveX(int8_t delta) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return;
if (delta > 0) {
for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) {
if (x + delta >= cols) break;
setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y));
}
} else {
for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) {
if (x + delta < 0) break;
setPixelColorXY(x, y, getPixelColorXY(x + delta, y));
}
}
}
void Segment::moveY(int8_t delta) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return;
if (delta > 0) {
for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) {
if (y + delta >= rows) break;
setPixelColorXY(x, y, getPixelColorXY(x, (y + delta)));
}
} else {
for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) {
if (y + delta < 0) break;
setPixelColorXY(x, y, getPixelColorXY(x, y + delta));
}
}
}
// move() - move all pixels in desired direction delta number of pixels
// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down
// @param delta number of pixels to move
void Segment::move(uint8_t dir, uint8_t delta) {
if (delta==0) return;
switch (dir) {
case 0: moveX( delta); break;
case 1: moveX( delta); moveY( delta); break;
case 2: moveY( delta); break;
case 3: moveX(-delta); moveY( delta); break;
case 4: moveX(-delta); break;
case 5: moveX(-delta); moveY(-delta); break;
case 6: moveY(-delta); break;
case 7: moveX( delta); moveY(-delta); break;
}
}
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) {
for (int16_t x = -radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius &&
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
int16_t(cx)+x<cols && int16_t(cy)+y<rows)
addPixelColorXY(cx + x, cy + y, col);
}
}
}
void Segment::nscale8(uint8_t scale) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)].nscale8(scale);
else setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
}
}
//line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int16_t err = (dx>dy ? dx : -dy)/2, e2;
for (;;) {
setPixelColorXY(x0,y0,c);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
#include "console_font_5x8.h"
#include "console_font_5x12.h"
#include "console_font_6x8.h"
#include "console_font_7x9.h"
// draws a raster font character on canvas
// only supports 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const int font = w*h;
//if (w<5 || w>6 || h!=8) return;
for (int i = 0; i<h; i++) { // character height
int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen
uint8_t bits = 0;
switch (font) {
case 40: bits = pgm_read_byte_near(&console_font_5x8[(chr * h) + i]); break; // 5x8 font
case 48: bits = pgm_read_byte_near(&console_font_6x8[(chr * h) + i]); break; // 6x8 font
case 63: bits = pgm_read_byte_near(&console_font_7x9[(chr * h) + i]); break; // 7x9 font
case 60: bits = pgm_read_byte_near(&console_font_5x12[(chr * h) + i]); break; // 5x12 font
default: return;
}
for (int j = 0; j<w; j++) { // character width
int16_t x0 = x + (w-1) - j;
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
setPixelColorXY(x0, y0, color);
}
}
}
}
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
// extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel
uint8_t wu[4] = {WU_WEIGHT(ix, iy), WU_WEIGHT(xx, iy),
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)};
// multiply the intensities by the colour, and saturating-add them to the pixels
for (int i = 0; i < 4; i++) {
CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1));
led.r = qadd8(led.r, c.r * wu[i] >> 8);
led.g = qadd8(led.g, c.g * wu[i] >> 8);
led.b = qadd8(led.b, c.b * wu[i] >> 8);
setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led);
}
}
#undef WU_WEIGHT
#endif // WLED_DISABLE_2D

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@ class BusPwm : public Bus {
return numPins;
}
inline void cleanup() {
void cleanup() {
deallocatePins();
}
@ -494,6 +494,71 @@ class BusPwm : public Bus {
};
class BusOnOff : public Bus {
public:
BusOnOff(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_valid = false;
if (bc.type != TYPE_ONOFF) return;
uint8_t currentPin = bc.pins[0];
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
deallocatePins(); return;
}
_pin = currentPin; //store only after allocatePin() succeeds
pinMode(_pin, OUTPUT);
reversed = bc.reversed;
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
c = autoWhiteCalc(c);
uint8_t r = R(c);
uint8_t g = G(c);
uint8_t b = B(c);
uint8_t w = W(c);
_data = bool((r+g+b+w) && _bri) ? 0xFF : 0;
}
uint32_t getPixelColor(uint16_t pix) {
if (!_valid) return 0;
return RGBW32(_data, _data, _data, _data);
}
void show() {
if (!_valid) return;
digitalWrite(_pin, reversed ? !(bool)_data : (bool)_data);
}
inline void setBrightness(uint8_t b) {
_bri = b;
}
uint8_t getPins(uint8_t* pinArray) {
if (!_valid) return 0;
pinArray[0] = _pin;
return 1;
}
void cleanup() {
deallocatePins();
}
~BusOnOff() {
cleanup();
}
private:
uint8_t _pin = 255;
uint8_t _data = 0;
void deallocatePins() {
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
}
};
class BusNetwork : public Bus {
public:
BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {

View File

@ -33,6 +33,11 @@
#define I_8266_U1_TM1_4 14
#define I_8266_DM_TM1_4 15
#define I_8266_BB_TM1_4 16
//TM1829 (RGB)
#define I_8266_U0_TM2_3 39
#define I_8266_U1_TM2_3 40
#define I_8266_DM_TM2_3 41
#define I_8266_BB_TM2_3 42
/*** ESP32 Neopixel methods ***/
//RGB
@ -52,6 +57,11 @@
#define I_32_I0_TM1_4 27
#define I_32_I1_TM1_4 28
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB)
#define I_32_RN_TM2_3 43
#define I_32_I0_TM2_3 44
#define I_32_I1_TM2_3 45
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//APA102
#define I_HS_DOT_3 29 //hardware SPI
@ -96,6 +106,11 @@
#define B_8266_U1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266Uart1Tm1814Method>
#define B_8266_DM_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method>
#define B_8266_BB_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method>
//TM1829 (RGB)
#define B_8266_U0_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method>
#define B_8266_U1_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method>
#define B_8266_DM_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266DmaTm1829Method>
#define B_8266_BB_TM2_4 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp8266BitBangTm1829Method>
#endif
/*** ESP32 Neopixel methods ***/
@ -133,6 +148,15 @@
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
//TM1829 (RGB)
#define B_32_RN_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32RmtNTm1829Method>
#ifndef CONFIG_IDF_TARGET_ESP32C3
#define B_32_I0_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s0Tm1829Method>
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
#define B_32_I1_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s1Tm1829Method>
#endif
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
#endif
@ -201,6 +225,10 @@ class PolyBus {
case I_8266_U1_TM1_4: beginTM1814<B_8266_U1_TM1_4*>(busPtr); break;
case I_8266_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break;
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Begin(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break;
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(); break;
case I_HS_LPD_3: (static_cast<B_HS_LPD_3*>(busPtr))->Begin(); break;
case I_HS_LPO_3: (static_cast<B_HS_LPO_3*>(busPtr))->Begin(); break;
@ -230,11 +258,14 @@ class PolyBus {
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
#endif
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Begin(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Begin(); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Begin(); break;
#endif
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Begin(pins[1], -1, pins[0], -1); break;
@ -271,6 +302,10 @@ class PolyBus {
case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break;
case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break;
case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break;
case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_4(len, pins[0]); break;
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break;
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break;
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@ -295,11 +330,14 @@ class PolyBus {
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
#endif
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
case I_32_RN_TM2_3: busPtr = new B_32_RN_TM2_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
case I_32_I0_TM2_3: busPtr = new B_32_I0_TM2_3(len, pins[0]); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
case I_32_I1_TM2_3: busPtr = new B_32_I1_TM2_3(len, pins[0]); break;
#endif
#endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
@ -337,6 +375,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
@ -361,11 +403,14 @@ class PolyBus {
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break;
#endif
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(); break;
@ -400,6 +445,10 @@ class PolyBus {
case I_8266_U1_TM1_4: return (static_cast<B_8266_U1_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_4*>(busPtr))->CanShow(); break;
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_4*>(busPtr))->CanShow(); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
@ -424,11 +473,14 @@ class PolyBus {
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
#endif
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
case I_32_RN_TM2_3: return (static_cast<B_32_RN_TM2_3*>(busPtr))->CanShow(); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I0_TM2_3: return (static_cast<B_32_I0_TM2_3*>(busPtr))->CanShow(); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I1_TM2_3: return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
#endif
#endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
@ -487,6 +539,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
@ -511,11 +567,14 @@ class PolyBus {
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
#endif
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
@ -550,6 +609,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetBrightness(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetBrightness(b); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
@ -574,11 +637,14 @@ class PolyBus {
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
#endif
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetBrightness(b); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetBrightness(b); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetBrightness(b); break;
#endif
#endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetBrightness(b); break;
@ -614,6 +680,10 @@ class PolyBus {
case I_8266_U1_TM1_4: col = (static_cast<B_8266_U1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_4*>(busPtr))->GetPixelColor(pix); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
@ -638,11 +708,14 @@ class PolyBus {
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
#endif
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_RN_TM2_3: col = (static_cast<B_32_RN_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I0_TM2_3: col = (static_cast<B_32_I0_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_32_I1_TM2_3: col = (static_cast<B_32_I1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
#endif
#endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
@ -697,6 +770,10 @@ class PolyBus {
case I_8266_U1_TM1_4: delete (static_cast<B_8266_U1_TM1_4*>(busPtr)); break;
case I_8266_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break;
case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break;
case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_4*>(busPtr)); break;
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break;
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break;
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_4*>(busPtr)); break;
#endif
#ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
@ -721,11 +798,14 @@ class PolyBus {
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
#endif
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
case I_32_RN_TM2_3: delete (static_cast<B_32_RN_TM2_3*>(busPtr)); break;
#ifndef CONFIG_IDF_TARGET_ESP32C3
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
case I_32_I0_TM2_3: delete (static_cast<B_32_I0_TM2_3*>(busPtr)); break;
#endif
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
case I_32_I1_TM2_3: delete (static_cast<B_32_I1_TM2_3*>(busPtr)); break;
#endif
#endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
@ -779,6 +859,8 @@ class PolyBus {
return I_8266_U0_400_3 + offset;
case TYPE_TM1814:
return I_8266_U0_TM1_4 + offset;
case TYPE_TM1829:
return I_8266_U0_TM2_3 + offset;
}
#else //ESP32
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
@ -799,6 +881,8 @@ class PolyBus {
return I_32_RN_400_3 + offset;
case TYPE_TM1814:
return I_32_RN_TM1_4 + offset;
case TYPE_TM1829:
return I_32_RN_TM2_3 + offset;
}
#endif
}

View File

@ -195,11 +195,11 @@ void handleAnalog(uint8_t b)
colorHStoRGB(aRead*256,255,col);
} else {
// otherwise use "double press" for segment selection
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]);
Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) {
seg.setOption(SEG_OPTION_ON, 0); // off
} else {
seg.setOpacity(aRead, macroDoublePress[b]);
seg.setOpacity(aRead);
seg.setOption(SEG_OPTION_ON, 1);
}
// this will notify clients of update (websockets,mqtt,etc)
@ -216,10 +216,13 @@ void handleAnalog(uint8_t b)
void handleButton()
{
static unsigned long lastRead = 0UL;
static unsigned long lastRun = 0UL;
bool analog = false;
unsigned long now = millis();
if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle)
//if (strip.isUpdating()) return; // don't interfere with strip updates. Our button will still be there in 1ms (next cycle)
if (strip.isUpdating() && (millis() - lastRun < 400)) return; // be niced, but avoid button starvation
lastRun = millis();
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
#ifdef ESP8266
@ -265,7 +268,9 @@ void handleButton()
if (b == 0 && dur > WLED_LONG_AP) { // long press on button 0 (when released)
if (dur > WLED_LONG_FACTORY_RESET) { // factory reset if pressed > 10 seconds
WLED_FS.format();
#ifdef WLED_ADD_EEPROM_SUPPORT
clearEEPROM();
#endif
doReboot = true;
} else {
WLED::instance().initAP(true);

View File

@ -90,6 +90,44 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(strip.useLedsArray, hw_led[F("ld")]);
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings
JsonObject matrix = hw_led[F("matrix")];
if (!matrix.isNull()) {
strip.isMatrix = true;
CJSON(strip.panelH, matrix[F("ph")]);
CJSON(strip.panelW, matrix[F("pw")]);
CJSON(strip.hPanels, matrix[F("mph")]);
CJSON(strip.vPanels, matrix[F("mpv")]);
CJSON(strip.matrix.bottomStart, matrix[F("pb")]);
CJSON(strip.matrix.rightStart, matrix[F("pr")]);
CJSON(strip.matrix.vertical, matrix[F("pv")]);
CJSON(strip.matrix.serpentine, matrix[F("ps")]);
JsonArray panels = matrix[F("panels")];
uint8_t s = 0;
if (!panels.isNull()) {
for (JsonObject pnl : panels) {
CJSON(strip.panel[s].bottomStart, pnl["b"]);
CJSON(strip.panel[s].rightStart, pnl["r"]);
CJSON(strip.panel[s].vertical, pnl["v"]);
CJSON(strip.panel[s].serpentine, pnl["s"]);
if (++s >= WLED_MAX_PANELS) break; // max panels reached
}
}
// clear remaining panels
for (; s<WLED_MAX_PANELS; s++) {
strip.panel[s].bottomStart = 0;
strip.panel[s].rightStart = 0;
strip.panel[s].vertical = 0;
strip.panel[s].serpentine = 0;
}
strip.setUpMatrix();
}
#endif
JsonArray ins = hw_led["ins"];
@ -229,6 +267,30 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (serialBaud < 96 || serialBaud > 15000) serialBaud = 1152;
updateBaudRate(serialBaud *100);
JsonArray hw_if_i2c = hw[F("if")][F("i2c-pin")];
CJSON(i2c_sda, hw_if_i2c[0]);
CJSON(i2c_scl, hw_if_i2c[1]);
PinManagerPinType i2c[2] = { { i2c_sda, true }, { i2c_scl, true } };
if (i2c_scl >= 0 && i2c_sda >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
#ifdef ESP32
Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called prior)
#endif
Wire.begin();
} else {
i2c_sda = -1;
i2c_scl = -1;
}
JsonArray hw_if_spi = hw[F("if")][F("spi-pin")];
CJSON(spi_mosi, hw_if_spi[0]);
CJSON(spi_sclk, hw_if_spi[1]);
PinManagerPinType spi[3] = { { spi_mosi, true }, { spi_sclk, true } };
if (spi_mosi >= 0 && spi_sclk >= 0 && pinManager.allocateMultiplePins(spi, 2, PinOwner::HW_SPI)) {
// do not initialise bus here
} else {
spi_mosi = -1;
spi_sclk = -1;
}
//int hw_status_pin = hw[F("status")]["pin"]; // -1
JsonObject light = doc[F("light")];
@ -470,7 +532,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
void deserializeConfigFromFS() {
bool success = deserializeConfigSec();
if (!success) { //if file does not exist, try reading from EEPROM
#ifdef WLED_ADD_EEPROM_SUPPORT
deEEPSettings();
#endif
return;
}
@ -480,7 +544,9 @@ void deserializeConfigFromFS() {
success = readObjectFromFile("/cfg.json", nullptr, &doc);
if (!success) { //if file does not exist, try reading from EEPROM
#ifdef WLED_ADD_EEPROM_SUPPORT
deEEPSettings();
#endif
releaseJSONBufferLock();
return;
}
@ -584,6 +650,31 @@ void serializeConfig() {
hw_led[F("cb")] = strip.cctBlending;
hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override
hw_led[F("ld")] = strip.useLedsArray;
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings
if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix"));
matrix[F("ph")] = strip.panelH;
matrix[F("pw")] = strip.panelW;
matrix[F("mph")] = strip.hPanels;
matrix[F("mpv")] = strip.vPanels;
matrix[F("pb")] = strip.matrix.bottomStart;
matrix[F("pr")] = strip.matrix.rightStart;
matrix[F("pv")] = strip.matrix.vertical;
matrix[F("ps")] = strip.matrix.serpentine;
JsonArray panels = matrix.createNestedArray(F("panels"));
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
JsonObject pnl = panels.createNestedObject();
pnl["b"] = strip.panel[i].bottomStart;
pnl["r"] = strip.panel[i].rightStart;
pnl["v"] = strip.panel[i].vertical;
pnl["s"] = strip.panel[i].serpentine;
}
}
#endif
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
@ -648,6 +739,14 @@ void serializeConfig() {
hw[F("baud")] = serialBaud;
JsonObject hw_if = hw.createNestedObject(F("if"));
JsonArray hw_if_i2c = hw_if.createNestedArray("i2c-pin");
hw_if_i2c.add(i2c_sda);
hw_if_i2c.add(i2c_scl);
JsonArray hw_if_spi = hw_if.createNestedArray("spi-pin");
hw_if_spi.add(spi_mosi);
hw_if_spi.add(spi_sclk);
//JsonObject hw_status = hw.createNestedObject("status");
//hw_status["pin"] = -1;

View File

@ -1,12 +1,57 @@
#include "wled.h"
/*
* Color conversion methods
* Color conversion & utility methods
*/
/*
* color blend function
*/
uint32_t color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
uint8_t shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
}
/*
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/
uint32_t color_add(uint32_t c1, uint32_t c2)
{
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = w;
if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
}
void setRandomColor(byte* rgb)
{
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex);
lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb);
}
@ -274,3 +319,53 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb) {
return (k > 10091) ? 10091 : k;
}
}
//gamma 2.8 lookup table used for color correction
static 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,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
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 gamma8_cal(uint8_t b, float gamma)
{
return (int)(powf((float)b / 255.0f, gamma) * 255.0f + 0.5f);
}
void calcGammaTable(float gamma)
{
for (uint16_t i = 0; i < 256; i++) {
gammaT[i] = gamma8_cal(i, gamma);
}
}
uint8_t gamma8(uint8_t b)
{
return gammaT[b];
}
uint32_t gamma32(uint32_t color)
{
//if (!strip.gammaCorrectCol) return color;
uint8_t w = W(color);
uint8_t r = R(color);
uint8_t g = G(color);
uint8_t b = B(color);
w = gammaT[w];
r = gammaT[r];
g = gammaT[g];
b = gammaT[b];
return RGBW32(r, g, b, w);
}

4099
wled00/console_font_5x12.h Normal file

File diff suppressed because it is too large Load Diff

3075
wled00/console_font_5x8.h Normal file

File diff suppressed because it is too large Load Diff

3075
wled00/console_font_6x8.h Normal file

File diff suppressed because it is too large Load Diff

3331
wled00/console_font_7x9.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@
* Readability defines and their associated numerical values + compile-time constants
*/
#define GRADIENT_PALETTE_COUNT 58
//Defaults
#define DEFAULT_CLIENT_SSID "Your_Network"
#define DEFAULT_AP_PASS "wled1234"
@ -77,6 +79,7 @@
#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h"
#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h"
#define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h
#define USERMOD_ID_AUDIOREACTIVE 31 //Usermod "audioreactive.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -151,6 +154,7 @@
#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_TM1829 25
#define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31
//"Analog" types (PWM) (32-47)
@ -224,7 +228,11 @@
#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment
#define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 6 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 7
#define SEG_OPTION_REVERSED_Y 8
#define SEG_OPTION_MIRROR_Y 9
#define SEG_OPTION_TRANSPOSED 10
//Segment differs return byte
#define SEG_DIFFERS_BRI 0x01
@ -241,6 +249,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_NOBUF 3 // JSON buffer was not released in time, request cannot be handled at this time
#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
@ -356,4 +365,37 @@
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
#if defined(ESP8266) && defined(HW_PIN_SCL)
#undef HW_PIN_SCL
#endif
#if defined(ESP8266) && defined(HW_PIN_SDA)
#undef HW_PIN_SDA
#endif
#ifndef HW_PIN_SCL
#define HW_PIN_SCL SCL
#endif
#ifndef HW_PIN_SDA
#define HW_PIN_SDA SDA
#endif
#if defined(ESP8266) && defined(HW_PIN_CLOCKSPI)
#undef HW_PIN_CLOCKSPI
#endif
#if defined(ESP8266) && defined(HW_PIN_DATASPI)
#undef HW_PIN_DATASPI
#endif
#if defined(ESP8266) && defined(HW_PIN_CSSPI)
#undef HW_PIN_CSSPI
#endif
// defaults for VSPI
#ifndef HW_PIN_CLOCKSPI
#define HW_PIN_CLOCKSPI SCK
#endif
#ifndef HW_PIN_DATASPI
#define HW_PIN_DATASPI MOSI
#endif
#ifndef HW_PIN_CSSPI
#define HW_PIN_CSSPI SS
#endif
#endif

View File

@ -123,11 +123,19 @@ button {
.icons {
font-family: 'WIcons';
font-style: normal;
font-size: 24px;
line-height: 1;
font-size: 24px !important;
line-height: 1 !important;
display: inline-block;
}
.icons.on {
color: var(--c-g);
}
.icons.off {
color: var(--c-6);
}
.top .icons, .bot .icons {
margin: -2px 0 4px 0;
}
@ -185,7 +193,7 @@ button {
position: absolute;
top: 8px;
left: 12px;
pointer-events: none;
/*pointer-events: none;*/
width: 24px;
height: 24px;
}
@ -225,6 +233,17 @@ button {
border: 0px;
}
#liveview2D {
height: 90%;
display: none;
width: 90%;
border: 0px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
.tab {
background-color: transparent;
color: var(--c-d);
@ -359,27 +378,85 @@ button {
bottom: 5px;
}
#fx {
height: calc(100% - 121px);
overflow-y: scroll;
padding-left: 6px; /* compensate scroll bar */
}
#sliders {
width: 310px;
width: 300px;
margin: 0 auto;
position: sticky;
bottom: 0;
}
#sliders .labels {
padding-top: 3px;
font-size: small;
}
#slider0, #slider1, #slider2, #slider3, #slider4 {
background: var(--c-2);
max-width: 310px;
.slider {
background-color: var(--c-2);
max-width: 300px;
min-width: 280px;
margin: 0 auto 5px;
border-radius: 15px;
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
border-radius: 24px;
position: relative;
}
.filter, .option {
background-color: var(--c-4);
border-radius: 26px;
height: 26px;
margin: 0 auto; /* add 4-8px if you want space at the bottom */
padding: 4px 2px;
position: relative;
z-index: 1;
opacity: 1;
transition: opacity 0.5s linear, height 0.5s, transform 0.5s;
transform: scaleY(1);
}
.option {
z-index: unset;
}
/* Tooltip text */
.slider .tooltiptext, .option .tooltiptext {
visibility: hidden;
background-color: var(--c-5);
/*border: 2px solid var(--c-2);*/
box-shadow: 4px 4px 10px 4px var(--c-1);
color: var(--c-f);
text-align: center;
padding: 5px 10px;
border-radius: 6px;
/* Position the tooltip text */
width: 160px;
position: absolute;
z-index: 1;
bottom: 100%;
left: 50%;
margin-left: -92px;
/* Fade in tooltip */
opacity: 0;
transition: opacity 0.75s;
}
.option .tooltiptext {
bottom: 120%;
}
/* Tooltip arrow */
.slider .tooltiptext::after, .option .tooltiptext::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: var(--c-5) transparent transparent transparent;
}
/* Show the tooltip text when you mouse over the tooltip container */
.slider:hover .tooltiptext, .option .check:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
#pql, .edit-icon {
@ -389,6 +466,13 @@ button {
.hide {
display: none !important;
}
.fade {
visibility: hidden; /* hide it */
opacity: 0; /* make it transparent */
transform: scaleY(0); /* shrink content */
height: 0px; /* force other elements to move */
padding: 0; /* remove empty space */
}
.first {
margin-top: 10px;
@ -473,13 +557,14 @@ button {
z-index: -2;
}
#imgw {
display: inline-block;
margin: 8px;
#info .slider {
max-width: 200px;
min-width: 145px;
float: right;
margin: 0;
}
#kv, #kn {
display: inline-block;
#info .sliderwrap {
width: 200px;
}
#info table, #nodes table {
@ -502,6 +587,15 @@ button {
margin: 0 auto;
}
#imgw {
/*display: inline-block;*/
margin: 8px auto;
}
/*
#kv, #kn {
display: inline-block;
}
*/
#lv {
max-width: 600px;
display: inline-block;
@ -708,6 +802,9 @@ input[type=range]::-moz-range-thumb {
#putil .btn-xs {
margin: 0;
}
#info .btn-xs {
border: 1px solid var(--c-4);
}
#putil .btn-s {
width: 135px;
@ -756,19 +853,22 @@ input[type=range]::-moz-range-thumb {
}
select {
padding: 4px;
padding: 4px 8px;
margin: 0;
font-size: 19px;
background-color: var(--c-3);
color: var(--c-d);
cursor: pointer;
border: 1px solid var(--c-2);
border-radius: 5px;
border: 0 solid var(--c-2);
border-radius: 20px;
transition-duration: 0.5s;
-webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0);
-webkit-appearance: none;
-moz-appearance: none;
backface-visibility: hidden;
transform:translate3d(0,0,0);
text-overflow: ellipsis;
}
#tt {
text-align: center;
@ -776,16 +876,26 @@ select {
.cl {
background-color: #000;
}
.sel-p {
margin: 5px 0 10px;
width: 5em;
}
.sel-pl {
select.sel-p, select.sel-pl, select.sel-ple {
margin: 5px 0;
width: 100%;
background-position: 141px 16px;
height: 40px;
}
.sel-ple {
width: 100%;
div.sel-p {
position: relative;
}
div.sel-p:after {
content: "";
position: absolute;
right: 10px;
top: 22px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid var(--c-f);
}
select.sel-ple {
text-align: center;
}
option {
@ -869,10 +979,18 @@ textarea {
white-space: nowrap;
cursor: pointer;
text-align: center;
overflow: hidden;
text-overflow: clip;
overflow: clip;
text-overflow: ellipsis;
line-height: 24px;
padding: 8px 0;
padding: 8px 24px;
max-width: 170px;
margin: 0 auto;
position: relative;
}
.segname .flr, .pname .flr {
transform: rotate(0deg);
right: -6px;
}
/* segment power wrapper */
@ -907,19 +1025,24 @@ textarea {
}
.xxs {
border: 2px solid var(--c-e) !important;
width: 44px;
height: 44px;
margin: 5px;
/*box-shadow: 0 0 0 2px #fff;*/
}
.xxs-w {
border-width: 5px !important;
margin: 2px;
width: 50px;
height: 50px;
/*box-shadow: 0 0 0 5px #fff;*/
}
#csl .xxs {
border: 2px solid var(--c-d) !important;
/*box-shadow: 0 0 0 2px var(--c-d);*/
}
#csl .xxs-w {
border-width: 5px !important;
/*box-shadow: 0 0 0 5px var(--c-d);*/
}
.qcs, #namelabel {
@ -948,11 +1071,12 @@ textarea {
}
.frz {
left: 36px;
left: 32px;
position: absolute;
top: -3px;
cursor: pointer;
padding: 8px;
z-index: 1;
}
.expanded .frz {
@ -972,6 +1096,11 @@ textarea {
margin-top: 8px;
}
TD .revchkl {
padding: 0 0 0 32px;
margin-top: 0;
}
.check input, .radio input {
position: absolute;
opacity: 0;
@ -1041,6 +1170,10 @@ textarea {
background: var(--c-f);
}
TD .checkmark, TD .radiomark {
top: -6px;
}
.h {
font-size: 13px;
text-align: center;
@ -1068,16 +1201,32 @@ textarea {
top: auto !important; /* prevent sticky */
bottom: auto !important;
}
/*
.seg:last-child {
margin: 0;
}
*/
/* checkmark labels */
.seg .schkl {
position: absolute;
top: 7px;
left: 9px;
}
/* checkmark labels */
.filter .fchkl, .option .ochkl {
display: inline-block;
min-width: 0.7em;
padding: 1px 4px 4px 32px;
text-align: left;
line-height: 24px;
vertical-align: middle;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
.lbl-s {
display: inline-block;
/* margin: 10px 4px 0 0; */
margin-top: 6px;
font-size: 13px;
width: 48%;
text-align: center;
}
/* list wrapper */
.list {
@ -1132,6 +1281,7 @@ textarea {
.lstI.sticky,
.lstI.selected {
z-index: 1;
box-shadow: 0px 0px 10px 4px var(--c-1);
}
#pcont .selected:not([class*="expanded"]) {
@ -1164,6 +1314,9 @@ textarea {
/* list item name (for sorting) */
.lstIname {
white-space: nowrap;
text-overflow: ellipsis;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
/* list item palette preview */
@ -1193,6 +1346,8 @@ textarea {
border-radius: 21px;
background: var(--c-2);
border: 1px solid var(--c-3);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
.fnd input[type="text"]:focus {
@ -1293,7 +1448,7 @@ textarea {
width: 145px;
}
#info div, #nodes div {
width: 320px;
max-width: 320px;
}
}

View File

@ -46,9 +46,9 @@
}
}, 100);
});
setTimeout(()=>{h.appendChild(l)},50);
setTimeout(()=>{h.appendChild(l)},100);
});
setTimeout(()=>{h.appendChild(l)},50);
setTimeout(()=>{h.appendChild(l)},100);
</script>
<link rel="stylesheet" href="index.css">
</head>
@ -200,9 +200,9 @@
<div id="fx">
<p class="labels hd" id="modeLabel">Effect mode</p>
<div class="staytop fnd" id="fxFind">
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist')" />
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist');gId('filters').classList.add('fade');" onblur="gId('filters').classList.remove('fade')"/>
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i>
<i class="icons search-icon" onclick="gId('filters').classList.toggle('hide');" style="cursor:pointer;">&#xe0a1;</i>
</div>
<div id="fxlist" class="list">
<div class="lstI">
@ -217,50 +217,86 @@
</div>
</div>
<div id="sliders">
<p class="labels hd" id="sliderLabel0">Effect speed</p>
<div id="slider0">
<div id="filters" class="filter">
<label id="filterPal" class="check fchkl">&#x1F3A8;
<input type="checkbox" data-flt="&#x1F3A8;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter1D" class="check fchkl">&#8942;
<input type="checkbox" data-flt="&#8942;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter2D" class="check fchkl">&#9638;
<input type="checkbox" data-flt="&#9638;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterVol" class="check fchkl">&#9834;
<input type="checkbox" data-flt="&#9834;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterFreq" class="check fchkl">&#9835;
<input type="checkbox" data-flt="&#9835;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
</div>
<div id="slider0" class="slider">
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il">
<input id="sliderSpeed" class="noslide" onchange="setSpeed()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<span id="sliderLabel0" class="tooltiptext">Effect speed</span>
</div>
<p class="labels hd" id="sliderLabel1">Effect intensity</p>
<div id="slider1">
<div id="slider1" class="slider">
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglLabels()">&#xe409;</i>
<div class="sliderwrap il">
<input id="sliderIntensity" class="noslide" onchange="setIntensity()" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<span class="tooltiptext" id="sliderLabel1">Effect intensity</span>
</div>
<p class="labels hd" id="sliderLabel2">Custom 1</p>
<div id="slider2" class="hide">
<div id="slider2" class="slider hide">
<i class="icons slider-icon">&#xe410;</i>
<div class="sliderwrap il">
<input id="sliderC1" class="noslide" onchange="setCustom(1)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<input id="sliderC1" class="noslide" onchange="setCustom(1)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<span class="tooltiptext" id="sliderLabel2">Custom 1</span>
</div>
<p class="labels hd" id="sliderLabel3">Custom 2</p>
<div id="slider3" class="hide">
<div id="slider3" class="slider hide">
<i class="icons slider-icon">&#xe0a2;</i>
<div class="sliderwrap il">
<input id="sliderC2" class="noslide" onchange="setCustom(2)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<input id="sliderC2" class="noslide" onchange="setCustom(2)" oninput="updateTrail(this)" max="255" min="0" type="range" value="0" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<span class="tooltiptext" id="sliderLabel3">Custom 2</span>
</div>
<p class="labels hd" id="sliderLabel4">Custom 3</p>
<div id="slider4" class="hide">
<div id="slider4" class="slider hide">
<i class="icons slider-icon">&#xe0e8;</i>
<div class="sliderwrap il">
<input id="sliderC3" class="noslide" onchange="setCustom(3)" oninput="updateTrail(this)" max="255" min="0" type="range" value="6" />
<input id="sliderC3" class="noslide" onchange="setCustom(3)" oninput="updateTrail(this)" max="31" min="0" type="range" value="0" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<span class="tooltiptext" id="sliderLabel4">Custom 3</span>
</div>
<div id="fxopt" class="option fade">
<label id="opt0" class="check ochkl hide"><i class="icons">&#xe2b3;</i><span class="tooltiptext" id="optLabel0">Check 1</span>
<input type="checkbox" onchange="setOption(1, this.checked)">
<span class="checkmark"></span>
</label>
<label id="opt1" class="check ochkl hide"><i class="icons">&#xe34b;</i><span class="tooltiptext" id="optLabel1">Check 2</span>
<input type="checkbox" onchange="setOption(2, this.checked)">
<span class="checkmark"></span>
</label>
<label id="opt2" class="check ochkl hide"><i class="icons">&#xe04c;</i><span class="tooltiptext" id="optLabel2">Check 3</span>
<input type="checkbox" onchange="setOption(3, this.checked)">
<span class="checkmark"></span>
</label>
</div>
</div>
</div>
@ -308,7 +344,7 @@
<button class="btn btn-xs close" onclick="toggleInfo()"><i class="icons rot45">&#xe18a;</i></button>
<div id="imgw">
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br>
</div>
<div id="kv">Loading...</div><br>
<div>
<button class="btn infobtn" onclick="requestJson()">Refresh</button>
@ -329,6 +365,10 @@
</div>
</div>
<div id="mliveview2D" class="modal">
<div id="kliveview2D" style="width:100%; height:100%">Loading...</div><br>
</div>
<div id="rover" class="modal">
<i class="icons huge">&#xe410;</i><br>
<div id="lv">?</div><br><br>

File diff suppressed because it is too large Load Diff

View File

@ -19,49 +19,43 @@
</style>
</head>
<body>
<div id="canv" />
<div id="canv"></div>
<script>
function updatePreview(leds) {
var str = "linear-gradient(90deg,";
var len = leds.length;
for (i = 2; i < len; i+=3) {
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
if (i < len -3) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
}
function getLiveJson(e) {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
updatePreview(leds);
}
}
catch (err) {
console.error("Peek WS error:",err);
}
}
var ws;
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
console.info("Peek uses top WS");
//console.info("Peek uses top WS");
ws.send("{'lv':true}");
} else {
console.info("Peek WS opening");
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+"://"+document.location.host+"/ws");
ws.onopen = function () {
console.info("Peek WS open");
//console.info("Peek WS open");
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message',getLiveJson);
ws.addEventListener('message', (e) => {
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76) return; //'L'
let str = "linear-gradient(90deg,";
let len = leds.length;
let start = leds[1]==2 ? 4 : 2; // 1 = 1D, 2 = 1D/2D (leds[2]=w, leds[3]=h)
for (i = start; i < len; i+=3) {
str += `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
if (i < len -3) str += ","
}
str += ")";
document.getElementById("canv").style.background = str;
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta charset="utf-8">
<meta name="theme-color" content="#222222">
<title>WLED Live Preview</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<canvas id="canv"></canvas>
<script>
var c = document.getElementById('canv');
var leds = "";
var throttled = false;
function setCanvas() {
c.width = window.innerWidth * 0.98; //remove scroll bars
c.height = window.innerHeight * 0.98; //remove scroll bars
}
setCanvas();
// Check for canvas support
var ctx = c.getContext('2d');
if (ctx) { // Access the rendering context
// use parent WS or open new
var ws;
try {
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send("{'lv':true}");
} else {
ws = new WebSocket((window.location.protocol == "https:"?"wss":"ws")+"://"+document.location.host+"/ws");
ws.onopen = ()=>{
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";
ws.addEventListener('message',(e)=>{
try {
if (toString.call(e.data) === '[object ArrayBuffer]') {
let leds = new Uint8Array(event.data);
if (leds[0] != 76 || leds[1] != 2 || !ctx) return; //'L', set in ws.cpp
let mW = leds[2]; // matrix width
let mH = leds[3]; // matrix height
let pPL = Math.min(c.width / mW, c.height / mH); // pixels per LED (width of circle)
let lOf = Math.floor((c.width - pPL*mW)/2); //left offeset (to center matrix)
var i = 4;
for (y=0.5;y<mH;y++) for (x=0.5; x<mW; x++) {
ctx.fillStyle = `rgb(${leds[i]},${leds[i+1]},${leds[i+2]})`;
ctx.beginPath();
ctx.arc(x*pPL+lOf, y*pPL, pPL*0.4, 0, 2 * Math.PI);
ctx.fill();
i+=3;
}
}
} catch (err) {
console.error("Peek WS error:",err);
}
});
}
// window.resize event listener
window.addEventListener('resize', (e)=>{
if (!throttled) { // only run if we're not throttled
setCanvas(); // actual callback action
throttled = true; // we're throttled!
setTimeout(()=>{ // set a timeout to un-throttle
throttled = false;
}, 250);
}
});
</script>
</body>
</html>

View File

@ -6,9 +6,38 @@
<title>WLED Settings</title>
<script>
var d=document;
var loc = false, locip;
function gId(n){return d.getElementById(n);}
function S(){GetV();}
function GetV(){}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S(){
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=0';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>
body {
@ -18,31 +47,33 @@
margin: 0;
}
html {
--h: 10.2vh;
--h: 9vh;
}
button {
background: #333;
color: #fff;
font-family: Verdana, Helvetica, sans-serif;
display: inline-block;
display: block;
border: 1px solid #333;
border-radius: var(--h);
font-size: 6vmin;
height: var(--h);
width: calc(100% - 40px);
margin-top: 2vh;
margin: 2vh auto 0;
cursor: pointer;
}
</style>
</head>
<body onload="S()">
<form action="/"><button type=submit id="b">Back</button></form>
<form action="/settings/wifi"><button type="submit">WiFi Setup</button></form>
<form action="/settings/leds"><button type="submit">LED Preferences</button></form>
<form action="/settings/ui"><button type="submit">User Interface</button></form>
<form action="/settings/dmx"><button id="dmxbtn" style="display: none;" type="submit">DMX Output</button></form>
<form action="/settings/sync"><button type="submit">Sync Interfaces</button></form>
<form action="/settings/time"><button type="submit">Time & Macros</button></form>
<form action="/settings/um"><button type="submit">Usermods</button></form>
<form action="/settings/sec"><button type="submit">Security & Updates</button></form>
<button type=submit id="b" onclick="window.location='/'">Back</button>
<button type="submit" onclick="window.location='./settings/wifi'">WiFi Setup</button>
<button type="submit" onclick="window.location='./settings/leds'">LED Preferences</button>
<button type="submit" onclick="window.location='./settings/2D'">2D Configuration</button>
<button type="submit" onclick="window.location='./settings/ui'">User Interface</button>
<button id="dmxbtn" style="display: none;" type="submit" onclick="window.location='./settings/dmx'">DMX Output</button>
<button type="submit" onclick="window.location='./settings/sync'">Sync Interfaces</button>
<button type="submit" onclick="window.location='./settings/time'">Time & Macros</button>
<button type="submit" onclick="window.location='./settings/um'">Usermods</button>
<button type="submit" onclick="window.location='./settings/sec'">Security & Updates</button>
</body>
</html>

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

@ -0,0 +1,150 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=500">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>2D Set-up</title>
<script>
var d=document;
var loc = false, locip;
function H(){window.open("https://kno.wled.ge/features/2D");}
function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
UI();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() {
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
var maxPanels=64;
function UI(change=false)
{
if (gId("somp").value === "0") {
gId("mpdiv").style.display = "none";
resetPanels();
return;
}
gId("mpdiv").style.display = "block";
maxPanels = parseInt(d.Sf.MPH.value) * parseInt(d.Sf.MPV.value);
let i = gId("panels").children.length;
if (i<maxPanels) for (let j=i; j<maxPanels; j++) addPanel(j);
if (i>maxPanels) for (let j=i; j>maxPanels; j--) remPanel();
//btnPanel(gId("panels").children.length);
}
function addPanels() {
let h = parseInt(d.Sf.MPH.value);
let v = parseInt(d.Sf.MPV.value);
for (let i=0; i<h*v; i++) addPanel(i);
}
function addPanel(i=0) {
let p = gId("panels");
if (p.children.length >= maxPanels) return;
let b = `<div id="pnl${i}">${i===0?"":'<hr style="width:260px">'}Panel ${i}<br>1<sup>st</sup> LED: <select name="P${i}B">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="P${i}R">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="P${i}V">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="P${i}S"></div>`;
p.insertAdjacentHTML("beforeend", b);
}
function remPanel() {
let p = gId("panels").children;
var i = p.length;
if (i <= 1) return;
p[i-1].remove();
}
function resetPanels() {
d.Sf.MPH.value = 1;
d.Sf.MPV.value = 1;
for (let e of gId("panels").children) e.remove();
}
function btnPanel(i) {
gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
gId("pnl_rem").style.display = (i>1) ? "inline":"none";
}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>2D setup</h2>
Strip or panel:
<select id="somp" name="SOMP" onchange="resetPanels();addPanels();UI();" >
<option value="0">1D Strip</option>
<option value="1">2D Matrix</option>
</select><br>
<div id="mpdiv" style="display:none;">
<h3>Panel set-up</h3>
Panel dimensions (WxH): <input name="PW" type="number" min="1" max="128" value="8"> x <input name="PH" type="number" min="1" max="128" value="8"><br>
Horizontal panels: <input name="MPH" type="number" min="1" max="8" value="1" oninput="UI()">
Vertical panels: <input name="MPV" type="number" min="1" max="8" value="1" oninput="UI()"><br>
1<sup>st</sup> panel: <select name="PB">
<option value="0">Top</option>
<option value="1">Bottom</option>
</select><select name="PR">
<option value="0">Left</option>
<option value="1">Right</option>
</select><br>
Orientation: <select name="PV">
<option value="0">Horizontal</option>
<option value="1">Vertical</option>
</select><br>
Serpentine: <input type="checkbox" name="PS">
<hr style="width:260px">
<i>A matrix is made of 1 or more physical LED panels of the same dimensions.<br>
Panels should be arranged from top-left to bottom-right order, starting with lower panel number on the left (or top if transposed).<br>
Each panel can have different LED orientation and/or starting point and/or layout.</i><br>
<hr style="width:260px">
<h3>LED panel layout</h3>
<div id="panels">
</div>
</div>
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
</body>
</html>

View File

@ -6,35 +6,65 @@
<meta charset="utf-8">
<title>DMX Settings</title>
<script>
function GCH(num) {
d=document;
d.getElementById('dmxchannels').innerHTML += "";
for (i=0;i<num;i++) {
d.getElementById('dmxchannels').innerHTML += "<span id=CH" + (i+1) + "s >Channel " + (i+1) + ": <select name=CH" + (i+1) + " id=\"CH" + (i+1) + "\"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n";
}
}
function mMap(){
d=document;
numCh=document.Sf.CN.value;
numGap=document.Sf.CG.value;
if (parseInt(numCh)>parseInt(numGap)) {
d.getElementById("gapwarning").style.display="block";
} else {
d.getElementById("gapwarning").style.display="none";
}
for (i=0;i<15;i++) {
if (i>=numCh) {
d.getElementById("CH"+(i+1) + "s").style.opacity = "0.5";
d.getElementById("CH"+(i+1)).disabled = true;
} else {
d.getElementById("CH"+(i+1) + "s").style.opacity = "1";
d.getElementById("CH"+(i+1)).disabled = false;
var d=document;
var loc = false, locip;
function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}
function B(){window.history.back();}
function GCH(num) {
d.getElementById('dmxchannels').innerHTML += "";
for (i=0;i<num;i++) {
d.getElementById('dmxchannels').innerHTML += "<span id=CH" + (i+1) + "s >Channel " + (i+1) + ": <select name=CH" + (i+1) + " id=\"CH" + (i+1) + "\"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n";
}
}
}
function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}function B(){window.history.back();}
function GetV(){}
function mMap(){
numCh=document.Sf.CN.value;
numGap=document.Sf.CG.value;
if (parseInt(numCh)>parseInt(numGap)) {
d.getElementById("gapwarning").style.display="block";
} else {
d.getElementById("gapwarning").style.display="none";
}
for (i=0;i<15;i++) {
if (i>=numCh) {
d.getElementById("CH"+(i+1) + "s").style.opacity = "0.5";
d.getElementById("CH"+(i+1)).disabled = true;
} else {
d.getElementById("CH"+(i+1) + "s").style.opacity = "1";
d.getElementById("CH"+(i+1)).disabled = false;
}
}
}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GCH(15);GetV();mMap();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S(){
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=7';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>@import url("style.css");</style>
</head>

View File

@ -8,10 +8,29 @@
<script>
var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip;
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);}
function off(n){d.getElementsByName(n)[0].value = -1;}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();checkSi();setABL();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout;
function showToast(text, error = false)
{
@ -131,7 +150,7 @@
if (s[i].name.substring(0,2)=="LT") {
var n = s[i].name.substring(2);
var t = parseInt(s[i].value,10);
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t >41) ? "GPIOs:" : "GPIO:";
gId("p0d"+n).innerHTML = (t>=80 && t<96) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t > 41) ? "GPIOs:" : "GPIO:";
gId("p1d"+n).innerHTML = (t> 49 && t<64) ? "Clk GPIO:" : "";
var LK = d.getElementsByName("L1"+n)[0]; // clock pin
@ -157,18 +176,18 @@
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state
if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED
}
gId("rf"+n).onclick = (t == 31) ? (function(){return false}) : (function(){}); // prevent change for TM1814
gId("rf"+n).onclick = (t == 31) ? (()=>{return false}) : (()=>{}); // prevent change for TM1814
isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t == 30 || t == 31) ? "inline":"none"; // show swap channels dropdown
if (!(t == 30 || t == 31)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (t>=80 && t<96) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = ((t>=80 && t<96) || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = (t>=16 && t<32 || t>=50 && t<64) ? "inline":"none"; // hide refresh
gId("dig"+n+"a").style.display = (isRGBW) ? "inline":"none"; // auto calculate white
gId("rev"+n).innerHTML = (t > 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
gId("psd"+n).innerHTML = (t > 40 && t < 48) ? "Index:":"Start:"; // change analog start description
gId("dig"+n+"c").style.display = (t >= 40 && t < 48) ? "none":"inline"; // hide count for analog
gId("dig"+n+"r").style.display = (t >= 80 && t < 96) ? "none":"inline"; // hide reversed for virtual
gId("dig"+n+"s").style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog
gId("dig"+n+"f").style.display = ((t >= 16 && t < 32) || (t >= 50 && t < 64)) ? "inline":"none"; // hide refresh
gId("dig"+n+"a").style.display = (isRGBW && t != 40) ? "inline":"none"; // auto calculate white
gId("rev"+n).innerHTML = (t >= 40 && t < 48) ? "Inverted output":"Reversed (rotated 180°)"; // change reverse text for analog
gId("psd"+n).innerHTML = (t >= 40 && t < 48) ? "Index:":"Start:"; // change analog start description
}
}
// display white channel calculation method
@ -297,11 +316,13 @@ ${i+1}:
<option value="30">SK6812 RGBW</option>
<option value="31">TM1814</option>
<option value="24">400kHz</option>
<option value="25">TM1829</option>
<option value="50">WS2801</option>
<option value="51">APA102</option>
<option value="52">LPD8806</option>
<option value="54">LPD6803</option>
<option value="53">P9813</option>
<option value="40">On/Off</option>
<option value="41">PWM White</option>
<option value="42">PWM CCT</option>
<option value="43">PWM RGB</option>
@ -506,12 +527,17 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
}
}
}
function S(){GetV();checkSi();setABL();}
function GetV()
{
//values injected by server while sending HTML
//d.um_p=[6,7,8,9,10,11,1];bLimits(3,4096,4000,1664);d.Sf.MS.checked=1;addLEDs(1);d.Sf.L00.value=2;d.Sf.LC0.value=30;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=15;d.Sf.CV0.checked=1;d.Sf.SL0.checked=0;addLEDs(1);d.Sf.L01.value=10;d.Sf.L11.value=10;d.Sf.L21.value=1;d.Sf.L31.value=10;d.Sf.LC1.value=60;d.Sf.LT1.value=80;d.Sf.CO1.value=1;d.Sf.LS1.value=0;d.Sf.CV1.checked=0;d.Sf.SL1.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=56;d.Sf.AW.value=3;d.Sf.BO.checked=1;d.Sf.BP.value=80;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=0;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=1;addBtn(0,0,0);addBtn(1,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=0;
//d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8;
function S(){
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=2';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>@import url("style.css");</style>
@ -564,8 +590,9 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
Use less than <span id="wreason">800 LEDs per output</span> for the best experience!<br>
</div>
<hr style="width:260px">
Make a segment for each output: <input type="checkbox" name="MS"> <br>
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
Make a segment for each output: <input type="checkbox" name="MS"><br>
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"><br>
Use global LED buffer: <input type="checkbox" name="LD"><br>
<hr style="width:260px">
<div id="color_order_mapping">
Color Order Override:

View File

@ -7,11 +7,32 @@
<title>Misc Settings</title>
<script>
var d = document;
var loc = false, locip;
function H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); }
function B() { window.open("/settings","_self"); }
function U() { window.open("/update","_self"); }
function gId(s) { return d.getElementById(s); }
function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
setBckFilename(gId("bckcfg"));
setBckFilename(gId("bckpresets"));
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout;
function showToast(text, error = false)
{
@ -40,16 +61,27 @@
event.preventDefault();
return false;
}
function GetV()
{
//values injected by server while sending HTML
function setBckFilename(x) {
x.setAttribute("download","wled_" + x.getAttribute("download") + (sd=="WLED"?"":("_" +sd)));
}
function S() {
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=6';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>
@import url("style.css");
</style>
</head>
<body onload="GetV()">
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>
@ -74,9 +106,9 @@
Enable ArduinoOTA: <input type="checkbox" name="AO">
<hr>
<h3>Backup & Restore</h3>
<a class="btn lnk" href="/presets.json?download" target="download-frame">Backup presets</a><br>
<a class="btn lnk" id="bckcfg" href="/presets.json" download="presets">Backup presets</a><br>
<div>Restore presets<br><input type="file" name="data" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data,'/presets.json');">Upload</button><br></div><br>
<a class="btn lnk" href="/cfg.json?download" target="download-frame">Backup configuration</a><br>
<a class="btn lnk" id="bckpresets" href="/cfg.json" download="cfg">Backup configuration</a><br>
<div>Restore configuration<br><input type="file" name="data2" accept=".json"> <button type="button" onclick="uploadFile(d.Sf.data2,'/cfg.json');">Upload</button><br></div>
<div style="color: #fa0;">&#9888; Restoring presets/configuration will OVERWRITE your current presets/configuration.<br>
Incorrect configuration may require a factory reset or re-flashing of your ESP.</div>
@ -92,6 +124,5 @@
<div id="toast"></div>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
<iframe name=download-frame style='display:none;'></iframe>
</body>
</html>

View File

@ -5,42 +5,71 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<meta charset="utf-8">
<title>Sync Settings</title>
<script>var d=document;
function gId(s)
{
return d.getElementById(s);
}
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
function B(){window.open("/settings","_self");}
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
function FC()
{
for(j=0;j<8;j++)
<script>var d=document;
var loc = false, locip;
function gId(s)
{
gId("G"+(j+1)).checked=gId("GS").value>>j&1;
gId("R"+(j+1)).checked=gId("GR").value>>j&1;
return d.getElementById(s);
}
}
function GC()
{
var a=0, b=0;
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
function B(){window.open("/settings","_self");}
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();SetVal();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function FC()
{
for(j=0;j<8;j++)
{
gId("G"+(j+1)).checked=gId("GS").value>>j&1;
gId("R"+(j+1)).checked=gId("GR").value>>j&1;
}
}
function GC()
{
var a=0, b=0;
var m=1;
for(j=0;j<8;j++)
{
a+=gId("G"+(j+1)).checked*m;
b+=gId("R"+(j+1)).checked*m;
m*=2;
var m=1;
for(j=0;j<8;j++)
{
a+=gId("G"+(j+1)).checked*m;
b+=gId("R"+(j+1)).checked*m;
m*=2;
}
gId("GS").value=a;
gId("GR").value=b;
}
gId("GS").value=a;
gId("GR").value=b;
}
function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();}
function S(){GetV();SetVal();}
function GetV(){var d=document;}
</script>
function SP(){var p = d.Sf.DI.value; gId("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;}
function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();FC();}
function S(){
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=4';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="S()">

View File

@ -7,26 +7,42 @@
<title>Time Settings</title>
<script>
var d=document;
var loc = false, locip;
var el=false;
var ms=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
function H()
{
window.open("https://kno.wled.ge/features/settings/#time-settings");
function H() { window.open("https://kno.wled.ge/features/settings/#time-settings"); }
function B() { window.open("/settings","_self"); }
function gId(s) { return d.getElementById(s); }
function gN(s) { return d.getElementsByName(s)[0]; }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
BTa();GetV();updLoc();Cs();FC();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function B()
{
window.open("/settings","_self");
}
function S()
{
BTa();GetV();updLoc();Cs();FC();
}
function gId(s)
{
return d.getElementById(s);
}
function gN(s) {
return d.getElementsByName(s)[0];
function S() {
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=5';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
function expand(o,i)
{
@ -34,10 +50,7 @@
t.style.display = t.style.display!=="none" ? "none" : "";
o.innerHTML = t.style.display==="none" ? "&#128197;" : "&#x2715;";
}
function Cs()
{
gId("cac").style.display=(gN("OL").checked)?"block":"none";
}
function Cs() { gId("cac").style.display=(gN("OL").checked)?"block":"none"; }
function BTa()
{
var ih="<thead><tr><th>En.</th><th>Hour</th><th>Minute</th><th>Preset</th><th></th></tr></thead>";
@ -132,10 +145,6 @@
if (parseFloat(d.Sf.LT.value)<0) { d.Sf.LTR.value = "S"; d.Sf.LT.value = -1*parseFloat(d.Sf.LT.value); } else d.Sf.LTR.value = "N";
if (parseFloat(d.Sf.LN.value)<0) { d.Sf.LNR.value = "W"; d.Sf.LN.value = -1*parseFloat(d.Sf.LN.value); } else d.Sf.LNR.value = "E";
}
function GetV()
{
//values injected by server while sending HTML
}
</script>
<style>@import url("style.css");</style>
</head>

View File

@ -7,6 +7,7 @@
<title>UI Settings</title>
<script>
var d = document;
var loc = false, locip;
var initial_ds, initial_st, initial_su;
var sett = null;
var l = {
@ -23,6 +24,7 @@
"pid": "Show preset IDs",
"seglen": "Set segment length instead of stop LED",
"segpwr": "Hide segment power &amp; brightness",
"segexp" : "Always expand first segment",
"css": "Enable custom CSS",
"hdays": "Enable custom Holidays list"
},
@ -40,10 +42,7 @@
}
}
};
function gId(s)
{
return d.getElementById(s);
}
function gId(s) { return d.getElementById(s); }
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
@ -163,22 +162,43 @@
if (d.Sf.DS.value != initial_ds || d.Sf.ST.checked != initial_st || d.Sf.SU.checked != initial_su) d.Sf.submit();
}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
initial_ds = d.Sf.DS.value;
initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S()
{
GetV();
initial_ds = d.Sf.DS.value;
initial_st = d.Sf.ST.checked;
initial_su = d.Sf.SU.checked;
GetLS();
}
function H()
{
window.open("https://kno.wled.ge/features/settings/#user-interface-settings");
}
function B()
{
window.open("/settings","_self");
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=3';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
function H() { window.open("https://kno.wled.ge/features/settings/#user-interface-settings"); }
function B() { window.open("/settings","_self"); }
function UI()
{
gId('idonthateyou').style.display = (gId('dm').checked) ? 'inline':'none';
@ -213,7 +233,6 @@
fO.value = '';
return false;
}
function GetV(){var d=document;}
</script>
<style>@import url("style.css");</style>
</head>
@ -241,6 +260,7 @@
<span class="l"></span>: <input type="checkbox" id="comp_pid" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_seglen" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_segpwr" class="agi cb"><br>
<span class="l"></span>: <input type="checkbox" id="comp_segexp" class="agi cb"><br>
I hate dark mode: <input type="checkbox" id="dm" onchange="UI()"><br>
<span id="idonthateyou" style="display:none"><i>Why would you? </i>&#x1F97A;<br></span>
<span class="l"></span>: <input type="number" min=0.0 max=1.0 step=0.01 id="theme_alpha_tab" class="agi"><br>

View File

@ -17,6 +17,24 @@
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
function B() { window.open("/settings","_self"); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() {
if (window.location.protocol == "file:") {
loc = true;
@ -37,6 +55,21 @@
if (o.type=="number" && n.substr(0,3)=="pin") {
for (var i=0; i<pins.length; i++) {
if (k==pinO[i]) continue;
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="lime"; break; }
if (o.value==pins[i] || o.value<-1 || o.value>39) { o.style.color="red"; break; } else o.style.color=o.value>33?"orange":"#fff";
}
} else {
switch (o.name) {
case "SDA": break;
case "SCL": break;
case "MOSI": break;
case "SCLK": break;
case "CS": break;
default: return;
}
for (var i=0; i<pins.length; i++) {
if (k==pinO[i]) continue;
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="lime"; break; }
if (o.value==pins[i] || o.value<-1 || o.value>39) { o.style.color="red"; break; } else o.style.color=o.value>33?"orange":"#fff";
}
}
@ -63,6 +96,7 @@
}
function addField(k,f,o,a=false) { //key, field, (sub)object, isArray
if (isO(o)) {
urows += '<hr style="width:260px">';
for (const [s,v] of Object.entries(o)) {
// possibility to nest objects (only 1 level)
if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
@ -137,7 +171,9 @@
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
function addInfo(name,el,txt) {
let obj = d.getElementsByName(name);
if (obj[el]) obj[el].insertAdjacentHTML('afterend', '&nbsp;'+txt);
if (!obj.length) return;
if (typeof el === "string" && obj[0]) obj[0].placeholder = el;
else if (obj[el]) obj[el].insertAdjacentHTML('afterend', '&nbsp;'+txt);
}
// load settings and insert values into DOM
function ldS() {
@ -161,7 +197,8 @@
}
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
gId("um").innerHTML = urows;
GetV();
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
})
.catch((error)=>{
gId('lserr').style.display = "inline";
@ -172,7 +209,6 @@
e.preventDefault();
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
}
function GetV() {} // replaced during 'npm run build'
</script>
<style>@import url("style.css");</style>
</head>
@ -186,6 +222,15 @@
<span id="lserr" style="color:red; display:none">&#9888; Could not load configuration.</span><hr>
</div>
<h2>Usermod Setup</h2>
Global I<sup>2</sup>C GPIOs (HW)<br>
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
SDA:<input type="number" min="-1" max="33" name="SDA" onchange="check(this,'if')" class="s" placeholder="SDA">
SCL:<input type="number" min="-1" max="33" name="SCL" onchange="check(this,'if')" class="s" placeholder="SCL">
<hr style="width:260px">
Global SPI GPIOs (HW)<br>
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
MOSI:<input type="number" min="-1" max="33" name="MOSI" onchange="check(this,'if')" class="s" placeholder="MOSI">
SCLK:<input type="number" min="-1" max="33" name="SCLK" onchange="check(this,'if')" class="s" placeholder="SCLK">
<div id="um">Loading settings...</div>
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>

View File

@ -6,16 +6,44 @@
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>WiFi Settings</title>
<script>
var d=document;
var loc = false, locip;
function H(){window.open("https://kno.wled.ge/features/settings/#wifi-settings");}
function B(){window.open("/settings","_self");}
function GetV()
{
//values injected by server while sending HTML
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S() {
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=1';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script>
<style>@import url("style.css");</style>
</head>
<body onload="GetV()">
<body onload="S()">
<form id="form_s" name="Sf" method="post">
<div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div>

View File

@ -644,7 +644,7 @@ function populateEffects()
for (let i = 0; i < effects.length; i++) {
// WLEDSR: add slider and color control to setEffect (used by requestjson)
if (effects[i].name.indexOf("Reserved") < 0) {
if (effects[i].name.indexOf("RSVD") < 0) {
var posAt = effects[i].name.indexOf("@");
var extra = '';
if (posAt > 0)

View File

@ -154,7 +154,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
DMXOldDimmer = e131_data[dataOffset+0];
bri = e131_data[dataOffset+0];
}
if (e131_data[dataOffset+1] < MODE_COUNT)
if (e131_data[dataOffset+1] < strip.getModeCount())
effectCurrent = e131_data[dataOffset+ 1];
effectSpeed = e131_data[dataOffset+ 2]; // flickers
effectIntensity = e131_data[dataOffset+ 3];

View File

@ -58,21 +58,23 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb);
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO
void colorRGBtoXY(byte* rgb, float* xy); // only defined if huesync disabled TODO
void colorFromDecOrHexString(byte* rgb, char* in);
bool colorFromHexString(byte* rgb, const char* in);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
uint8_t gamma8_cal(uint8_t b, float gamma);
void calcGammaTable(float gamma);
uint8_t gamma8(uint8_t b);
uint32_t gamma32(uint32_t);
//dmx.cpp
void initDMX();
@ -129,7 +131,7 @@ void handleIR();
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeSegment(JsonObject& root, 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 serializeModeNames(JsonArray arr, const char *qstring);
@ -296,7 +298,7 @@ class UsermodManager {
void onUpdateBegin(bool);
bool add(Usermod* um);
Usermod* lookup(uint16_t mod_id);
byte getModCount();
byte getModCount() {return numMods;};
};
//usermods_list.cpp
@ -311,7 +313,7 @@ void userLoop();
int getNumVal(const String* req, uint16_t pos);
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255);
bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte maxv=255);
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);
bool oappend(const char* txt); // append new c string to temp buffer efficiently
bool oappendi(int i); // append new number to temp buffer efficiently
void sappend(char stype, const char* key, int val);
@ -321,12 +323,19 @@ bool isAsterisksOnly(const char* str, byte maxLen);
bool requestJSONBufferLock(uint8_t module=255);
void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId);
void enumerateLedmaps();
#ifdef WLED_ADD_EEPROM_SUPPORT
//wled_eeprom.cpp
void applyMacro(byte index);
void deEEP();
void deEEPSettings();
void clearEEPROM();
#endif
//wled_math.cpp
#ifndef WLED_USE_REAL_MATH

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,7 @@
#include "wled.h"
#include "ir_codes.h"
/*
* Infrared sensor support for generic 24/40/44 key RGB remotes
*/
@ -89,8 +91,8 @@ byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte highe
void changeEffect(uint8_t fx)
{
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
strip.setMode(i, fx);
}
@ -105,8 +107,8 @@ void changeEffect(uint8_t fx)
void changePalette(uint8_t pal)
{
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.palette = pal;
}
@ -124,8 +126,8 @@ void changeEffectSpeed(int8_t amount)
int16_t new_val = (int16_t) effectSpeed + amount;
effectSpeed = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.speed = effectSpeed;
}
@ -135,7 +137,7 @@ void changeEffectSpeed(int8_t amount)
setValuesFromMainSeg();
}
} else { // if Effect == "solid Color", change the hue of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
@ -147,8 +149,8 @@ void changeEffectSpeed(int8_t amount)
prim_hsv.h = (byte)new_val;
hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
}
@ -171,8 +173,8 @@ void changeEffectIntensity(int8_t amount)
int16_t new_val = (int16_t) effectIntensity + amount;
effectIntensity = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.intensity = effectIntensity;
}
@ -182,7 +184,7 @@ void changeEffectIntensity(int8_t amount)
setValuesFromMainSeg();
}
} else { // if Effect == "solid Color", change the saturation of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
@ -192,8 +194,8 @@ void changeEffectIntensity(int8_t amount)
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
}
@ -214,8 +216,8 @@ void changeColor(uint32_t c, int16_t cct=-1)
{
if (irApplyToAllSelected) {
// main segment may not be selected!
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0;
@ -226,14 +228,14 @@ void changeColor(uint32_t c, int16_t cct=-1)
if (isRGB) mask |= 0x00FFFFFF; // RGB
if (hasW) mask |= 0xFF000000; // white
if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified
seg.setColor(0, c | 0xFFFFFF, i); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct, i);
seg.setColor(0, c | 0xFFFFFF); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct);
}
setValuesFromFirstSelectedSeg();
} else {
byte i = strip.getMainSegmentId();
WS2812FX::Segment& seg = strip.getSegment(i);
Segment& seg = strip.getSegment(i);
byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0;
bool isRGB = GET_BIT(capabilities, 0); // is segment RGB capable
@ -243,9 +245,9 @@ void changeColor(uint32_t c, int16_t cct=-1)
if (isRGB) mask |= 0x00FFFFFF; // RGB
if (hasW) mask |= 0xFF000000; // white
if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified
seg.setColor(0, c | 0xFFFFFF, i); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct, i);
seg.setColor(0, c | 0xFFFFFF); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct);
setValuesFromMainSeg();
}
stateChanged = true;
@ -253,7 +255,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
void changeWhite(int8_t amount, int16_t cct=-1)
{
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]);
@ -424,7 +426,7 @@ void decodeIR24CT(uint32_t code)
void decodeIR40(uint32_t code)
{
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]);
@ -502,8 +504,8 @@ void decodeIR44(uint32_t code)
case IR44_WARMWHITE : changeColor(COLOR_WARMWHITE, 63); changeEffect(FX_MODE_STATIC); break;
case IR44_COLDWHITE : changeColor(COLOR_COLDWHITE, 191); changeEffect(FX_MODE_STATIC); break;
case IR44_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break;
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, MODE_COUNT -1)); break;
case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, strip.getModeCount() -1)); break;
case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); break;
case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, strip.getPaletteCount() -1)); break;
case IR44_BLUEPLUS : changeEffectIntensity( 16); break;
@ -562,7 +564,7 @@ void decodeIR6(uint32_t code)
case IR6_POWER: toggleOnOff(); break;
case IR6_CHANNEL_UP: incBrightness(); break;
case IR6_CHANNEL_DOWN: decBrightness(); break;
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1));
switch(lastIR6ColourIdx) {
case 0: changeColor(COLOR_RED); break;
@ -600,7 +602,7 @@ void decodeIR9(uint32_t code)
case IR9_DOWN : decBrightness(); break;
case IR9_LEFT : changeEffectSpeed(-16); break;
case IR9_RIGHT : changeEffectSpeed(16); break;
case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, MODE_COUNT -1)); break;
case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break;
default: return;
}
lastValidCode = code;
@ -670,7 +672,7 @@ void decodeIRJson(uint32_t code)
decBrightness();
} else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback
uint8_t p1 = fdo["PL"] | 1;
uint8_t p2 = fdo["FX"] | random8(MODE_COUNT -1);
uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1);
uint8_t p3 = fdo["FP"] | 0;
presetFallback(p1, p2, p3);
}
@ -712,7 +714,7 @@ void initIR()
void handleIR()
{
if (irEnabled > 0 && millis() - irCheckedTime > 120)
if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating())
{
irCheckedTime = millis();
if (irEnabled > 0)

View File

@ -11,15 +11,26 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte id = elem["id"] | it;
if (id >= strip.getMaxSegments()) return;
WS2812FX::Segment& seg = strip.getSegment(id);
WS2812FX::Segment prev = seg; //make a backup so we can tell if something changed
int stop = elem["stop"] | -1;
// if using vectors use this code to append segment
if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal()));
id = strip.getSegmentsNum()-1; // segments are added at the end of list
}
Segment& seg = strip.getSegment(id);
Segment prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
if (stop < 0) {
uint16_t len = elem["len"];
stop = (len > 0) ? start + len : seg.stop;
}
// 2D segments
uint16_t startY = elem["startY"] | seg.startY;
uint16_t stopY = elem["stopY"] | seg.stopY;
//repeat, multiplies segment until all LEDs are used, or max segments reached
bool repeat = elem["rpt"] | false;
@ -28,9 +39,10 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
elem.remove("rpt"); // remove for recursive call
elem.remove("n"); // remove for recursive call
uint16_t len = stop - start;
for (byte i=id+1; i<strip.getMaxSegments(); i++) {
for (size_t i=id+1; i<strip.getMaxSegments(); i++) {
start = start + len;
if (start >= strip.getLengthTotal()) break;
//TODO: add support for 2D
elem["start"] = start;
elem["stop"] = start + len;
elem["rev"] = !elem["rev"]; // alternate reverse on even/odd segments
@ -66,7 +78,14 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset;
uint16_t of = seg.offset;
uint8_t soundSim = elem[F("ssim")] | seg.soundSim;
uint8_t map1D2D = elem[F("mp12")] | seg.map1D2D;
if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = map1D2D & 0x03;
seg.soundSim = soundSim & 0x07;
uint16_t len = 1;
if (stop > start) len = stop - start;
@ -78,27 +97,27 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
of = offsetAbs;
}
if (stop > start && of > len -1) of = len -1;
strip.setSegment(id, start, stop, grp, spc, of);
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
byte segbri = seg.opacity;
if (getVal(elem["bri"], &segbri)) {
if (segbri > 0) seg.setOpacity(segbri, id);
seg.setOption(SEG_OPTION_ON, segbri, id);
if (segbri > 0) seg.setOpacity(segbri);
seg.setOption(SEG_OPTION_ON, segbri);
}
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id);
seg.setOption(SEG_OPTION_ON, on);
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
seg.setOption(SEG_OPTION_FREEZE, frz, id);
seg.setOption(SEG_OPTION_FREEZE, frz);
seg.setCCT(elem["cct"] | seg.cct, id);
seg.setCCT(elem["cct"] | seg.cct);
JsonArray colarr = elem["col"];
if (!colarr.isNull())
{
for (uint8_t i = 0; i < 3; i++)
for (size_t i = 0; i < 3; i++)
{
int rgbw[] = {0,0,0,0};
bool colValid = false;
@ -109,13 +128,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1;
if (kelvin < 0) continue;
if (kelvin == 0) seg.setColor(i, 0, id);
if (kelvin == 0) seg.setColor(i, 0);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true;
} else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol);
}
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array
@ -126,7 +145,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (!colValid) continue;
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]), id);
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]));
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
}
}
@ -143,28 +162,76 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
}
#endif
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
#ifndef WLED_DISABLE_2D
seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y));
seg.setOption(SEG_OPTION_MIRROR_Y , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y ));
seg.setOption(SEG_OPTION_TRANSPOSED, elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSED));
#endif
byte fx = seg.mode;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value)
if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
if (!presetId && currentPlaylist>=0) unloadPlaylist();
strip.setMode(id, fx);
if (fx != seg.mode) {
seg.startTransition(strip.getTransition()); // set effect transitions
//seg.markForReset();
seg.mode = fx;
// load default values from effect string if effect is selected without
// any other effect parameter (i.e. effect clicked in UI)
if ( elem[F("sx")].isNull()
&& elem[F("ix")].isNull()
&& elem["pal"].isNull()
&& elem[F("c1")].isNull()
&& elem[F("c2")].isNull()
&& elem[F("c3")].isNull() )
{
int16_t sOpt;
sOpt = extractModeDefaults(fx, SET_F("sx")); if (sOpt >= 0) seg.speed = sOpt;
sOpt = extractModeDefaults(fx, SET_F("ix")); if (sOpt >= 0) seg.intensity = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c1")); if (sOpt >= 0) seg.custom1 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c2")); if (sOpt >= 0) seg.custom2 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c3")); if (sOpt >= 0) seg.custom3 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("mp12")); if (sOpt >= 0) seg.map1D2D = sOpt & 0x03;
sOpt = extractModeDefaults(fx, SET_F("ssim")); if (sOpt >= 0) seg.soundSim = sOpt & 0x07;
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) seg.reverse = (bool)sOpt;
sOpt = extractModeDefaults(fx, SET_F("mi")); if (sOpt >= 0) seg.mirror = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, SET_F("rY")); if (sOpt >= 0) seg.reverse_y = (bool)sOpt;
sOpt = extractModeDefaults(fx, SET_F("mY")); if (sOpt >= 0) seg.mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, "pal");
if (sOpt >= 0 && sOpt < strip.getPaletteCount() + strip.customPalettes.size()) {
if (sOpt != seg.palette) {
if (strip.paletteFade && !seg.transitional) seg.startTransition(strip.getTransition());
seg.palette = sOpt;
}
}
}
}
}
//getVal also supports inc/decrementing and random
getVal(elem[F("sx")], &seg.speed, 0, 255);
getVal(elem[F("ix")], &seg.intensity, 0, 255);
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
getVal(elem[F("c1x")], &seg.c1x, 0, 255);
getVal(elem[F("c2x")], &seg.c2x, 0, 255);
getVal(elem[F("c3x")], &seg.c3x, 0, 255);
getVal(elem[F("sx")], &seg.speed);
getVal(elem[F("ix")], &seg.intensity);
uint8_t pal = seg.palette;
if (getVal(elem["pal"], &pal, 1, strip.getPaletteCount())) {
if (pal != seg.palette) {
if (strip.paletteFade && !seg.transitional) seg.startTransition(strip.getTransition());
seg.palette = pal;
}
}
getVal(elem[F("c1")], &seg.custom1);
getVal(elem[F("c2")], &seg.custom2);
uint8_t cust3 = seg.custom3;
getVal(elem[F("c3")], &cust3); // we can't pass reference to bifield
seg.custom3 = cust3;
seg.check1 = elem[F("o1")] | seg.check1;
seg.check2 = elem[F("o2")] | seg.check2;
seg.check3 = elem[F("o3")] | seg.check3;
JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) {
uint8_t oldSegId = strip.setPixelSegment(id);
// set brightness immediately and disable transition
transitionDelayTemp = 0;
jsonTransitionOnce = true;
@ -173,13 +240,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
// freeze and init to black
if (!seg.getOption(SEG_OPTION_FREEZE)) {
seg.setOption(SEG_OPTION_FREEZE, true);
strip.fill(0);
seg.fill(BLACK);
}
uint16_t start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set
for (uint16_t i = 0; i < iarr.size(); i++) {
for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) {
if (!set) {
start = iarr[i];
@ -189,7 +256,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
set = 2;
}
} else { //color
int rgbw[] = {0,0,0,0};
uint8_t rgbw[] = {0,0,0,0};
JsonArray icol = iarr[i];
if (!icol.isNull()) { //array, e.g. [255,0,0]
byte sz = icol.size();
@ -198,29 +265,27 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte brgbw[] = {0,0,0,0};
const char* hexCol = iarr[i];
if (colorFromHexString(brgbw, hexCol)) {
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
}
}
if (set < 2) stop = start + 1;
for (uint16_t i = start; i < stop; i++) {
for (int i = start; i < stop; i++) {
if (strip.gammaCorrectCol) {
strip.setPixelColor(i, strip.gamma8(rgbw[0]), strip.gamma8(rgbw[1]), strip.gamma8(rgbw[2]), strip.gamma8(rgbw[3]));
seg.setPixelColor(i, gamma8(rgbw[0]), gamma8(rgbw[1]), gamma8(rgbw[2]), gamma8(rgbw[3]));
} else {
strip.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
seg.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
}
}
if (!set) start++;
set = 0;
}
}
strip.setPixelSegment(oldSegId);
strip.trigger();
}
// send UDP if not in preset and something changed that is not just selection
// send UDP if something changed that is not just selection or segment power/opacity
if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true;
return;
}
// deserializes WLED state (fileDoc points to doc object if called from web server)
@ -230,19 +295,30 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
bool stateResponse = root[F("v")] | false;
bool onBefore = bri;
getVal(root["bri"], &bri);
uint8_t tmpBri = bri;
getVal(root["bri"], &tmpBri);
bool on = root["on"] | (bri > 0);
if (!on != !bri) toggleOnOff();
if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff();
if (root["on"].isNull()) {
if ((onBefore && tmpBri==0) || (!onBefore && tmpBri>0)) toggleOnOff();
bri = tmpBri;
} else {
bool on = root["on"] | onBefore;
if (on != onBefore || (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't')) {
toggleOnOff();
// a hack is needed after toggleOnOf()
if (!root["bri"].isNull()) {
if (bri==0) briLast = tmpBri;
else bri = tmpBri;
}
}
}
if (bri && !onBefore) { // unfreeze all segments when turning on
for (uint8_t s=0; s < strip.getMaxSegments(); s++) {
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false, s);
for (size_t s=0; s < strip.getSegmentsNum(); s++) {
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false);
}
if (realtimeMode && !realtimeOverride && useMainSegmentOnly) { // keep live segment frozen if live
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId());
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true);
}
}
@ -257,6 +333,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
}
}
// temporary transition (applies only once)
tr = root[F("tt")] | -1;
if (tr >= 0)
{
@ -294,7 +371,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
realtimeOverride = root[F("lor")] | realtimeOverride;
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) {
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId());
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
}
if (root.containsKey("live")) {
@ -316,13 +393,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (id < 0) {
//apply all selected segments
//bool didSet = false;
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
if (sg.isActive()) {
if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
//didSet = true;
}
for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
Segment &sg = strip.getSegment(s);
if (sg.isSelected()) {
deserializeSegment(segVar, s, presetId);
//didSet = true;
}
}
//TODO: not sure if it is good idea to change first active but unselected segment
@ -332,8 +407,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
}
} else {
JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs)
{
for (JsonObject elem : segs) {
deserializeSegment(elem, it, presetId);
it++;
}
@ -344,28 +418,34 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
loadLedmap = root[F("ledmap")] | loadLedmap;
byte ps = root[F("psave")];
if (ps > 0) {
savePreset(ps, nullptr, root);
} else {
ps = root[F("pdel")]; //deletion
if (ps > 0) {
deletePreset(ps);
}
if (ps > 0 && ps < 251) savePreset(ps, nullptr, root);
ps = root[F("pdel")]; //deletion
if (ps > 0 && ps < 251) deletePreset(ps);
// HTTP API commands (must be handled before "ps")
const char* httpwin = root["win"];
if (httpwin) {
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += httpwin;
handleSet(nullptr, apireq, false); // may set stateChanged
}
// applying preset (2 cases: a) API call includes all preset values, b) API only specifies preset ID)
if (!root["ps"].isNull()) {
ps = presetCycCurr;
if (getVal(root["ps"], &ps, presetCycMin, presetCycMax)) { //load preset (clears state request!)
if (ps >= presetCycMin && ps <= presetCycMax) presetCycCurr = ps;
applyPreset(ps, callMode, !presetId); // may clear root object and replace it by preset content if presetId==0
if (stateChanged) {
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
currentPreset = root["ps"] | currentPreset;
// if preset contains HTTP API call do not change presetCycCurr
if (root["win"].isNull()) presetCycCurr = currentPreset;
stateChanged = false; // cancel state change update (preset was set directly by applying values stored in UI JSON array)
} else if (root["win"].isNull() && getVal(root["ps"], &ps, 0, 0) && ps > 0 && ps < 251 && ps != currentPreset) {
// b) preset ID only (use embedded cycling limits if they exist in getVal())
presetCycCurr = ps;
applyPreset(ps, callMode); // async load
return stateResponse;
}
//HTTP API commands
const char* httpwin = root["win"];
if (httpwin) {
String apireq = "win"; apireq += '&'; // reduce flash string usage
apireq += httpwin;
handleSet(nullptr, apireq, false);
}
}
JsonObject playlist = root[F("playlist")];
@ -380,12 +460,16 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
return stateResponse;
}
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, bool segmentBounds)
{
root["id"] = id;
if (segmentBounds) {
root["start"] = seg.start;
root["stop"] = seg.stop;
if (strip.isMatrix) {
root[F("startY")] = seg.startY;
root[F("stopY")] = seg.stopY;
}
}
if (!forPreset) root["len"] = seg.stop - seg.start;
root["grp"] = seg.grouping;
@ -403,7 +487,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
const char *format = strip.hasWhiteChannel() ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
for (uint8_t i = 0; i < 3; i++)
for (size_t i = 0; i < 3; i++)
{
byte segcol[4]; byte* c = segcol;
segcol[0] = R(seg.colors[i]);
@ -421,12 +505,22 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root[F("sx")] = seg.speed;
root[F("ix")] = seg.intensity;
root["pal"] = seg.palette;
root[F("c1x")] = seg.c1x;
root[F("c2x")] = seg.c2x;
root[F("c3x")] = seg.c3x;
root[F("c1")] = seg.custom1;
root[F("c2")] = seg.custom2;
root[F("c3")] = seg.custom3;
root[F("sel")] = seg.isSelected();
root["rev"] = seg.getOption(SEG_OPTION_REVERSED);
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
if (strip.isMatrix) {
root[F("rY")] = seg.getOption(SEG_OPTION_REVERSED_Y);
root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y);
root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSED);
}
root[F("o1")] = seg.check1;
root[F("o2")] = seg.check2;
root[F("o3")] = seg.check3;
root[F("ssim")] = seg.soundSim;
root[F("mp12")] = seg.map1D2D;
}
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
@ -435,7 +529,6 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
root["on"] = (bri > 0);
root["bri"] = briLast;
root[F("transition")] = transitionDelay/100; //in 100ms
root[F("tdd")] = transitionDelayDefault/100; //in 100ms
}
if (!forPreset) {
@ -468,9 +561,17 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
bool selectedSegmentsOnly = root[F("sc")] | false;
JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s);
if (selectedSegmentsOnly && !sg.isSelected()) continue;
for (size_t s = 0; s < strip.getMaxSegments(); s++) {
if (s >= strip.getSegmentsNum()) {
if (forPreset && segmentBounds) { //disable segments not part of preset
JsonObject seg0 = seg.createNestedObject();
seg0["stop"] = 0;
continue;
} else
break;
}
Segment &sg = strip.getSegment(s);
if (!forPreset && selectedSegmentsOnly && !sg.isSelected()) continue;
if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
@ -493,12 +594,23 @@ void serializeInfo(JsonObject root)
leds["fps"] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("actseg")] = strip.getActiveSegmentsNum();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
leds[F("cpal")] = strip.customPalettes.size(); //number of custom palettes
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
JsonObject matrix = leds.createNestedObject("matrix");
matrix["w"] = strip.matrixWidth;
matrix["h"] = strip.matrixHeight;
}
#endif
uint8_t totalLC = 0;
JsonArray lcarr = leds.createNestedArray(F("seglc"));
uint8_t nSegs = strip.getLastActiveSegmentId();
for (byte s = 0; s <= nSegs; s++) {
size_t nSegs = strip.getSegmentsNum();
for (size_t s = 0; s < nSegs; s++) {
if (!strip.getSegment(s).isActive()) continue;
uint8_t lc = strip.getSegment(s).getLightCapabilities();
totalLC |= lc;
lcarr.add(lc);
@ -510,6 +622,19 @@ void serializeInfo(JsonObject root)
leds[F("wv")] = totalLC & 0x02; // deprecated, true if white slider should be displayed for any segment
leds["cct"] = totalLC & 0x04; // deprecated, use info.leds.lc
#ifdef WLED_DISABLE_AUDIO
root[F("noaudio")] = true;
#endif
#ifdef WLED_DEBUG
JsonArray i2c = root.createNestedArray(F("i2c"));
i2c.add(i2c_sda);
i2c.add(i2c_scl);
JsonArray spi = root.createNestedArray(F("spi"));
spi.add(spi_mosi);
spi.add(spi_sclk);
#endif
root[F("str")] = syncToggleReceive;
root[F("name")] = serverDescription;
@ -546,13 +671,8 @@ void serializeInfo(JsonObject root)
root[F("palcount")] = strip.getPaletteCount();
JsonArray ledmaps = root.createNestedArray(F("maps"));
for (uint8_t i=0; i<10; i++) {
char fileName[16];
strcpy_P(fileName, PSTR("/ledmap"));
if (i) sprintf(fileName +7, "%d", i);
strcat_P(fileName, PSTR(".json"));
bool isFile = WLED_FS.exists(fileName);
if (isFile || i==0) ledmaps.add(i);
for (size_t i=0; i<10; i++) {
if ((ledMaps>>i) & 0x0001) ledmaps.add(i);
}
JsonObject wifi_info = root.createNestedObject("wifi");
@ -681,6 +801,7 @@ void setPaletteColors(JsonArray json, byte* tcp)
void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
{
byte tcp[72];
#ifdef ESP8266
int itemPerPage = 5;
#else
@ -693,19 +814,20 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
}
int palettesCount = strip.getPaletteCount();
int customPalettes = strip.customPalettes.size();
int maxPage = (palettesCount -1) / itemPerPage;
int maxPage = (palettesCount + customPalettes -1) / itemPerPage;
if (page > maxPage) page = maxPage;
int start = itemPerPage * page;
int end = start + itemPerPage;
if (end >= palettesCount) end = palettesCount;
if (end > palettesCount + customPalettes) end = palettesCount + customPalettes;
root[F("m")] = maxPage;
root[F("m")] = maxPage; // inform caller how many pages there are
JsonObject palettes = root.createNestedObject("p");
for (int i = start; i < end; i++) {
JsonArray curPalette = palettes.createNestedArray(String(i));
JsonArray curPalette = palettes.createNestedArray(String(i>=palettesCount ? 255 - i + palettesCount : i));
switch (i) {
case 0: //default palette
setPaletteColors(curPalette, PartyColors_p);
@ -771,9 +893,12 @@ void serializePalettes(JsonObject root, AsyncWebServerRequest* request)
break;
default:
{
byte tcp[72];
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - 13])), 72);
setPaletteColors(curPalette, tcp);
if (i>=palettesCount) {
setPaletteColors(curPalette, strip.customPalettes[i - palettesCount]);
} else {
memcpy_P(tcp, (byte*)pgm_read_dword(&(gGradientPalettes[i - 13])), 72);
setPaletteColors(curPalette, tcp);
}
}
break;
}
@ -800,72 +925,25 @@ void serializeNodes(JsonObject root)
void serializeModeData(JsonArray fxdata)
{
//JsonArray fxdata = root.createNestedArray("fxdata");
String lineBuffer;
bool insideQuotes = false;
char singleJsonSymbol;
size_t len = strlen_P(JSON_mode_names);
// Find the mode name in JSON
for (size_t i = 0; i < len; i++) {
singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
if (insideQuotes) lineBuffer += singleJsonSymbol;
break;
case ']':
if (insideQuotes) {lineBuffer += singleJsonSymbol; break;}
case ',':
if (insideQuotes) {lineBuffer += singleJsonSymbol; break;}
if (lineBuffer.length() > 0) {
uint8_t endPos = lineBuffer.indexOf('@');
if (endPos>0) fxdata.add(lineBuffer.substring(endPos));
else fxdata.add("");
lineBuffer.clear();
}
break;
default:
if (!insideQuotes) break;
lineBuffer += singleJsonSymbol;
for (size_t i = 0; i < strip.getModeCount(); i++) {
String lineBuffer = FPSTR(strip.getModeData(i));
if (lineBuffer.length() > 0) {
uint8_t endPos = lineBuffer.indexOf('@');
if (endPos>0) fxdata.add(lineBuffer.substring(endPos));
else fxdata.add("");
}
}
}
// deserializes mode names string into JsonArray
// also removes WLED-SR extensions (@...) from deserialised names
void serializeModeNames(JsonArray arr, const char *qstring) {
String lineBuffer;
bool insideQuotes = false;
char singleJsonSymbol;
size_t len = strlen_P(qstring);
// Find the mode name in JSON
for (size_t i = 0; i < len; i++) {
singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) {
case '"':
insideQuotes = !insideQuotes;
break;
case '[':
break;
case ']':
case ',':
if (insideQuotes) break;
if (lineBuffer.length() > 0) {
uint8_t endPos = lineBuffer.indexOf('@');
if (endPos>0) arr.add(lineBuffer.substring(0,endPos));
else arr.add(lineBuffer);
lineBuffer.clear();
}
break;
default:
if (!insideQuotes) break;
lineBuffer += singleJsonSymbol;
void serializeModeNames(JsonArray arr) {
for (size_t i = 0; i < strip.getModeCount(); i++) {
String lineBuffer = FPSTR(strip.getModeData(i));
if (lineBuffer.length() > 0) {
uint8_t endPos = lineBuffer.indexOf('@');
if (endPos>0) arr.add(lineBuffer.substring(0, endPos));
else arr.add(lineBuffer);
}
}
}
@ -887,16 +965,17 @@ void serveJson(AsyncWebServerRequest* request)
}
#endif
else if (url.indexOf(F("eff")) > 0) {
// this is going to serve raw effect names which will include WLED-SR extensions in names
// this serves just effect names without FX data extensions in names
if (requestJSONBufferLock(19)) {
AsyncJsonResponse* response = new AsyncJsonResponse(&doc, true); // array document
JsonArray lDoc = response->getRoot();
serializeModeNames(lDoc, JSON_mode_names); // remove WLED-SR extensions from effect names
serializeModeNames(lDoc); // remove WLED-SR extensions from effect names
response->setLength();
request->send(response);
releaseJSONBufferLock();
} else {
request->send_P(200, "application/json", JSON_mode_names);
//request->send_P(200, "application/json", JSON_mode_names);
request->send(503, "application/json", F("{\"error\":3}"));
}
return;
}
@ -908,11 +987,14 @@ void serveJson(AsyncWebServerRequest* request)
return;
}
else if (url.length() > 6) { //not just /json
request->send( 501, "application/json", F("{\"error\":\"Not implemented\"}"));
request->send(501, "application/json", F("{\"error\":\"Not implemented\"}"));
return;
}
if (!requestJSONBufferLock(17)) return;
if (!requestJSONBufferLock(17)) {
request->send(503, "application/json", F("{\"error\":3}"));
return;
}
AsyncJsonResponse *response = new AsyncJsonResponse(&doc, subJson==6);
JsonVariant lDoc = response->getRoot();
@ -938,7 +1020,7 @@ void serveJson(AsyncWebServerRequest* request)
{
//lDoc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names);
JsonArray effects = lDoc.createNestedArray(F("effects"));
serializeModeNames(effects, JSON_mode_names); // remove WLED-SR extensions from effect names
serializeModeNames(effects); // remove WLED-SR extensions from effect names
lDoc[F("palettes")] = serialized((const __FlashStringHelper*)JSON_palette_names);
}
}
@ -970,7 +1052,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
obuf = buffer;
olen = 9;
for (uint16_t i= 0; i < used; i += n)
for (size_t i= 0; i < used; i += n)
{
uint32_t c = strip.getPixelColor(i);
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map

View File

@ -8,7 +8,7 @@ void setValuesFromMainSeg() { setValuesFromSegment(strip.getMainSegment
void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); }
void setValuesFromSegment(uint8_t s)
{
WS2812FX::Segment& seg = strip.getSegment(s);
Segment& seg = strip.getSegment(s);
col[0] = R(seg.colors[0]);
col[1] = G(seg.colors[0]);
col[2] = B(seg.colors[0]);
@ -30,9 +30,9 @@ void applyValuesToSelectedSegs()
{
// copy of first selected segment to tell if value was updated
uint8_t firstSel = strip.getFirstSelectedSegId();
WS2812FX::Segment selsegPrev = strip.getSegment(firstSel);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
Segment selsegPrev = strip.getSegment(firstSel);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
@ -41,8 +41,8 @@ void applyValuesToSelectedSegs()
if (effectCurrent != selsegPrev.mode) {strip.setMode(i, effectCurrent); stateChanged = true;}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0, i); stateChanged = true;}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1, i); stateChanged = true;}
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;}
}
}
@ -130,7 +130,7 @@ void stateUpdated(byte callMode) {
if (fadeTransition) {
//set correct delay if not using notification delay
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay;
if (callMode != CALL_MODE_NOTIFICATION && !jsonTransitionOnce) transitionDelayTemp = transitionDelay; // load actual transition duration
jsonTransitionOnce = false;
strip.setTransition(transitionDelayTemp);
if (transitionDelayTemp == 0) {
@ -143,7 +143,7 @@ void stateUpdated(byte callMode) {
briOld = briT;
tperLast = 0;
}
strip.setTransitionMode(true);
strip.setTransitionMode(true); // force all segments to transition mode
transitionActive = true;
transitionStartTime = millis();
} else {

View File

@ -69,7 +69,7 @@ void parseLxJson(int lxValue, byte segId, bool secondary)
} else {
DEBUG_PRINT(F("LX: segment "));
DEBUG_PRINTLN(segId);
strip.getSegment(segId).setColor(secondary, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]), segId);
strip.getSegment(segId).setColor(secondary, RGBW32(rgbw[0], rgbw[1], rgbw[2], rgbw[3]));
}
}
}

View File

@ -90,7 +90,6 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
colorFromDecOrHexString(col, (char*)payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
DEBUG_PRINTLN(F("MQTT JSON buffer requested."));
if (!requestJSONBufferLock(15)) { delete[] payloadStr; return; }
if (payload[0] == '{') { //JSON API
deserializeJson(doc, payloadStr);

View File

@ -13,8 +13,6 @@
#ifndef PalettesWLED_h
#define PalettesWLED_h
#define GRADIENT_PALETTE_COUNT 58
const byte ib_jul01_gp[] PROGMEM = {
0, 194, 1, 1,
94, 1, 29, 18,

View File

@ -75,12 +75,25 @@ bool PinManagerClass::deallocateMultiplePins(const uint8_t *pinArray, byte array
return true;
}
}
if (tag==PinOwner::HW_SPI) {
if (spiAllocCount && --spiAllocCount>0) {
// no deallocation done until last owner releases pins
return true;
}
}
for (int i = 0; i < arrayElementCount; i++) {
deallocatePin(pinArray[i], tag);
}
return true;
}
bool PinManagerClass::deallocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{
uint8_t pins[arrayElementCount];
for (int i=0; i<arrayElementCount; i++) pins[i] = mptArray[i].pin;
return deallocateMultiplePins(pins, arrayElementCount, tag);
}
bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{
bool shouldFail = false;
@ -100,8 +113,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
#endif
shouldFail = true;
}
if (tag==PinOwner::HW_I2C && isPinAllocated(gpio, tag)) {
// allow multiple "allocations" of HW I2C bus pins
if ((tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) && isPinAllocated(gpio, tag)) {
// allow multiple "allocations" of HW I2C & SPI bus pins
continue;
} else if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
@ -119,6 +132,7 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
}
if (tag==PinOwner::HW_I2C) i2cAllocCount++;
if (tag==PinOwner::HW_SPI) spiAllocCount++;
// all pins are available .. track each one
for (int i = 0; i < arrayElementCount; i++) {
@ -145,8 +159,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
{
// HW I2C pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C) return false;
// HW I2C & SPI pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) return false;
if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin "));

View File

@ -25,7 +25,7 @@ enum struct PinOwner : uint8_t {
// High bit is set for all built-in pin owners
Ethernet = 0x81,
BusDigital = 0x82,
BusDigital2 = 0x83,
BusOnOff = 0x83,
BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
Button = 0x85, // 'Butn' == button from configuration
IR = 0x86, // 'IR' == IR receiver pin from configuration
@ -34,6 +34,7 @@ enum struct PinOwner : uint8_t {
DebugOut = 0x89, // 'Dbg' == debug output always IO1
DMX = 0x8A, // 'DMX' == hard-coded to IO2
HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32)
HW_SPI = 0x8C, // 'SPI' == hardware (V)SPI pins (13,14&15 on ESP8266, 5,18&23 on ESP32)
// Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
@ -54,7 +55,8 @@ enum struct PinOwner : uint8_t {
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
UM_BME280 = USERMOD_ID_BME280 // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
UM_BME280 = USERMOD_ID_BME280, // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE // 0x1E // Usermod "audio_reactive.h"
};
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
@ -68,13 +70,18 @@ class PinManagerClass {
uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
PinOwner ownerTag[40] = { PinOwner::None };
#endif
uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
struct {
uint8_t i2cAllocCount : 4; // allow multiple allocation of I2C bus pins but keep track of allocations
uint8_t spiAllocCount : 4; // allow multiple allocation of SPI bus pins but keep track of allocations
};
public:
PinManagerClass() : i2cAllocCount(0), spiAllocCount(0) {}
// De-allocates a single pin
bool deallocatePin(byte gpio, PinOwner tag);
// De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified)
bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag);
bool deallocateMultiplePins(const managed_pin_type *pinArray, byte arrayElementCount, PinOwner tag);
// Allocates a single pin, with an owner tag.
// De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag);

View File

@ -13,6 +13,8 @@ static volatile byte callModeToApply = 0;
bool applyPreset(byte index, byte callMode, bool fromJson)
{
DEBUG_PRINT(F("Request to apply preset: "));
DEBUG_PRINTLN(index);
presetToApply = index;
callModeToApply = callMode;
// the following is needed in case of HTTP JSON API call to return correct state to the caller
@ -24,11 +26,13 @@ bool applyPreset(byte index, byte callMode, bool fromJson)
void handlePresets(bool force)
{
bool changePreset = false;
uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset()
uint8_t tmpMode = callModeToApply;
if (presetToApply == 0 || (fileDoc && !force)) return; // JSON buffer already allocated and not force apply or no preset waiting
if (tmpPreset == 0 || (fileDoc && !force)) return; // JSON buffer already allocated and not force apply or no preset waiting
JsonObject fdo;
const char *filename = presetToApply < 255 ? "/presets.json" : "/tmp.json";
const char *filename = tmpPreset < 255 ? "/presets.json" : "/tmp.json";
//crude way to determine if this was called by a network request
uint8_t core = 1;
@ -37,38 +41,60 @@ void handlePresets(bool force)
#endif
//only allow use of fileDoc from the core responsible for network requests (AKA HTTP JSON API)
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
if (fileDoc && core && force && presetToApply < 255) {
if (fileDoc && core && force && tmpPreset < 255) {
DEBUG_PRINT(F("Force applying preset: "));
DEBUG_PRINTLN(presetToApply);
presetToApply = 0; //clear request for preset
callModeToApply = 0;
// this will overwrite doc with preset content but applyPreset() is the last in such case and content of doc is no longer needed
errorFlag = readObjectFromFileUsingId(filename, presetToApply, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
JsonObject fdo = fileDoc->as<JsonObject>();
if (!fdo["seg"].isNull()) unloadPlaylist(); // if preset contains "seg" we must unload playlist
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull() || !fdo["win"].isNull()) changePreset = true;
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
deserializeState(fdo, callModeToApply, presetToApply);
if (!errorFlag && changePreset) currentPreset = presetToApply;
colorUpdated(callModeToApply);
//HTTP API commands
const char* httpwin = fdo["win"];
if (httpwin) {
String apireq = "win"; // reduce flash string usage
apireq += F("&IN&"); // internal call
apireq += httpwin;
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
setValuesFromFirstSelectedSeg(); // fills legacy values
changePreset = true;
} else {
if (!fdo["seg"].isNull()) unloadPlaylist(); // if preset contains "seg" we must unload playlist
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
presetToApply = 0; //clear request for preset
callModeToApply = 0;
deserializeState(fdo, tmpMode, tmpPreset); // may call applyPreset() which will overwrite presetToApply
}
if (!errorFlag && changePreset) presetCycCurr = currentPreset = tmpPreset;
colorUpdated(tmpMode);
return;
}
if (force) return; // something went wrong with force option (most likely WS request), quit and wait for async load
// allocate buffer
DEBUG_PRINTLN(F("Apply preset JSON buffer requested."));
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
presetToApply = 0; //clear request for preset
callModeToApply = 0;
DEBUG_PRINT(F("Applying preset: "));
DEBUG_PRINTLN(tmpPreset);
#ifdef ARDUINO_ARCH_ESP32
if (presetToApply==255 && tmpRAMbuffer!=nullptr) {
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
deserializeJson(*fileDoc,tmpRAMbuffer);
errorFlag = ERR_NONE;
} else
#endif
{
errorFlag = readObjectFromFileUsingId(filename, presetToApply, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
}
fdo = fileDoc->as<JsonObject>();
@ -78,30 +104,27 @@ void handlePresets(bool force)
String apireq = "win"; // reduce flash string usage
apireq += F("&IN&"); // internal call
apireq += httpwin;
handleSet(nullptr, apireq, false);
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
setValuesFromFirstSelectedSeg(); // fills legacy values
changePreset = true;
} else {
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["nl"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull() || !fdo["win"].isNull()) changePreset = true;
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["nl"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
deserializeState(fdo, CALL_MODE_NO_NOTIFY, presetToApply);
deserializeState(fdo, CALL_MODE_NO_NOTIFY, tmpPreset); // may change presetToApply by calling applyPreset()
}
if (!errorFlag && presetToApply < 255 && changePreset) currentPreset = presetToApply;
if (!errorFlag && tmpPreset < 255 && changePreset) presetCycCurr = currentPreset = tmpPreset;
#if defined(ARDUINO_ARCH_ESP32)
//Aircoookie recommended not to delete buffer
if (presetToApply==255 && tmpRAMbuffer!=nullptr) {
if (tmpPreset==255 && tmpRAMbuffer!=nullptr) {
free(tmpRAMbuffer);
tmpRAMbuffer = nullptr;
}
#endif
releaseJSONBufferLock(); // will also clear fileDoc
colorUpdated(callModeToApply);
updateInterfaces(callModeToApply);
presetToApply = 0; //clear request for preset
callModeToApply = 0;
colorUpdated(tmpMode);
updateInterfaces(tmpMode);
}
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
@ -117,7 +140,6 @@ void savePreset(byte index, const char* pname, JsonObject saveobj)
if (!fileDoc) {
// called from handleSet() HTTP API
DEBUG_PRINTLN(F("Save preset JSON buffer requested."));
if (!requestJSONBufferLock(10)) return;
sObj = fileDoc->to<JsonObject>();
bufferAllocated = true;

View File

@ -15,8 +15,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
return;
}
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods
if (subPage <1 || subPage >8 || !correctPIN) return;
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX 8: usermods 9: N/A 10: 2D
if (subPage <1 || subPage >10 || !correctPIN) return;
//WIFI SETTINGS
if (subPage == 1)
@ -85,6 +85,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
Bus::setCCTBlend(strip.cctBlending);
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt());
strip.useLedsArray = request->hasArg(F("LD"));
bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
@ -99,7 +100,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
if (!request->hasArg(lp)) {
DEBUG_PRINTLN(F("No data.")); break;
DEBUG_PRINT(F("No data for "));
DEBUG_PRINTLN(s);
break;
}
for (uint8_t i = 0; i < 5; i++) {
lp[1] = 48+i;
@ -119,6 +122,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
awmode = request->arg(aw).toInt();
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0;
// actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode);
busesChanged = true;
@ -185,8 +189,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
fadeTransition = request->hasArg(F("TF"));
t = request->arg(F("TD")).toInt();
if (t >= 0) transitionDelay = t;
transitionDelayDefault = t;
if (t >= 0) transitionDelayDefault = t;
strip.paletteFade = request->hasArg(F("PF"));
nightlightTargetBri = request->arg(F("TB")).toInt();
@ -396,7 +399,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (request->hasArg(F("RS"))) //complete factory reset
{
WLED_FS.format();
#ifdef WLED_ADD_EEPROM_SUPPORT
clearEEPROM();
#endif
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
doReboot = true;
}
@ -472,6 +477,51 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{
if (!requestJSONBufferLock(5)) return;
// global I2C & SPI pins
int8_t hw_sda_pin = !request->arg(F("SDA")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SDA")).toInt()));
int8_t hw_scl_pin = !request->arg(F("SCL")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SCL")).toInt()));
#ifdef ESP8266
// cannot change pins on ESP8266
if (hw_sda_pin >= 0 && hw_sda_pin != HW_PIN_SDA) hw_sda_pin = HW_PIN_SDA;
if (hw_scl_pin >= 0 && hw_scl_pin != HW_PIN_SCL) hw_scl_pin = HW_PIN_SCL;
#endif
PinManagerPinType i2c[2] = { { hw_sda_pin, true }, { hw_scl_pin, true } };
if (hw_sda_pin >= 0 && hw_scl_pin >= 0 && pinManager.allocateMultiplePins(i2c, 2, PinOwner::HW_I2C)) {
i2c_sda = hw_sda_pin;
i2c_scl = hw_scl_pin;
#ifdef ESP32
Wire.setPins(i2c_sda, i2c_scl); // this will fail if Wire is initilised (Wire.begin() called)
#endif
Wire.begin();
} else {
// there is no Wire.end()
DEBUG_PRINTLN(F("Could not allocate I2C pins."));
uint8_t i2c[2] = { i2c_scl, i2c_sda };
pinManager.deallocateMultiplePins(i2c, 2, PinOwner::HW_I2C); // just in case deallocation of old pins
i2c_sda = -1;
i2c_scl = -1;
}
int8_t hw_mosi_pin = !request->arg(F("MOSI")).length() ? -1 : max(-1,min(33,(int)request->arg(F("MOSI")).toInt()));
int8_t hw_sclk_pin = !request->arg(F("SCLK")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SCLK")).toInt()));
#ifdef ESP8266
// cannot change pins on ESP8266
if (hw_mosi_pin >= 0 && hw_mosi_pin != HW_PIN_DATASPI) hw_mosi_pin = HW_PIN_DATASPI;
if (hw_sclk_pin >= 0 && hw_sclk_pin != HW_PIN_CLOCKSPI) hw_sclk_pin = HW_PIN_CLOCKSPI;
#endif
PinManagerPinType spi[2] = { { hw_mosi_pin, true }, { hw_sclk_pin, true } };
if (hw_mosi_pin >= 0 && hw_sclk_pin >= 0 && pinManager.allocateMultiplePins(spi, 2, PinOwner::HW_SPI)) {
spi_mosi = hw_mosi_pin;
spi_sclk = hw_sclk_pin;
// no bus initialisation
} else {
//SPI.end();
DEBUG_PRINTLN(F("Could not allocate SPI pins."));
uint8_t spi[2] = { spi_mosi, spi_sclk };
pinManager.deallocateMultiplePins(spi, 2, PinOwner::HW_SPI); // just in case deallocation of old pins
spi_mosi = -1;
spi_sclk = -1;
}
JsonObject um = doc.createNestedObject("um");
size_t args = request->args();
@ -482,7 +532,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// POST request parameters are combined as <usermodname>_<usermodparameter>
int umNameEnd = name.indexOf(":");
if (umNameEnd<1) break; // parameter does not contain ":" or on 1st place -> wrong
if (umNameEnd<1) continue; // parameter does not contain ":" or on 1st place -> wrong
JsonObject mod = um[name.substring(0,umNameEnd)]; // get a usermod JSON object
if (mod.isNull()) {
@ -551,6 +601,32 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
releaseJSONBufferLock();
}
#ifndef WLED_DISABLE_2D
//2D panels
if (subPage == 10)
{
strip.isMatrix = request->arg(F("SOMP")).toInt();
strip.panelH = MAX(1,MIN(128,request->arg(F("PH")).toInt()));
strip.panelW = MAX(1,MIN(128,request->arg(F("PW")).toInt()));
strip.hPanels = MAX(1,MIN(8,request->arg(F("MPH")).toInt()));
strip.vPanels = MAX(1,MIN(8,request->arg(F("MPV")).toInt()));
strip.matrix.bottomStart = request->arg(F("PB")).toInt();
strip.matrix.rightStart = request->arg(F("PR")).toInt();
strip.matrix.vertical = request->arg(F("PV")).toInt();
strip.matrix.serpentine = request->hasArg(F("PS"));
for (uint8_t i=0; i<WLED_MAX_PANELS; i++) {
char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
uint8_t l = strlen(pO); pO[l+1] = 0;
pO[l] = 'B'; if (!request->hasArg(pO)) break;
pO[l] = 'B'; strip.panel[i].bottomStart = request->arg(pO).toInt();
pO[l] = 'R'; strip.panel[i].rightStart = request->arg(pO).toInt();
pO[l] = 'V'; strip.panel[i].vertical = request->arg(pO).toInt();
pO[l] = 'S'; strip.panel[i].serpentine = request->hasArg(pO);
}
strip.setUpMatrix(); // will check limits
}
#endif
lastEditTime = millis();
if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
if (subPage == 4) alexaInit();
@ -579,17 +655,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SS="));
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) {
if (t < strip.getSegmentsNum()) {
selectedSeg = t;
singleSegment = true;
}
}
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg);
Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) {
byte t = getNumVal(&req, pos);
if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
if (t == 2) for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
selseg.setOption(SEG_OPTION_SELECTED, t);
}
@ -605,6 +681,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
uint16_t startI = selseg.start;
uint16_t stopI = selseg.stop;
uint16_t startY = selseg.startY;
uint16_t stopY = selseg.stopY;
uint8_t grpI = selseg.grouping;
uint16_t spcI = selseg.spacing;
pos = req.indexOf(F("&S=")); //segment start
@ -624,7 +702,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
spcI = getNumVal(&req, pos);
}
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI);
strip.setSegment(selectedSeg, startI, stopI, grpI, spcI, UINT16_MAX, startY, stopY);
pos = req.indexOf(F("RV=")); //Segment reverse
if (pos > 0) selseg.setOption(SEG_OPTION_REVERSED, req.charAt(pos+3) != '0');
@ -635,9 +713,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SB=")); //Segment brightness/opacity
if (pos > 0) {
byte segbri = getNumVal(&req, pos);
selseg.setOption(SEG_OPTION_ON, segbri, selectedSeg);
selseg.setOption(SEG_OPTION_ON, segbri);
if (segbri) {
selseg.setOpacity(segbri, selectedSeg);
selseg.setOpacity(segbri);
}
}
@ -660,25 +738,25 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) presetCycMax = getNumVal(&req, pos);
//apply preset
if (updateVal(&req, "PL=", &presetCycCurr, presetCycMin, presetCycMax)) {
if (updateVal(req.c_str(), "PL=", &presetCycCurr, presetCycMin, presetCycMax)) {
unloadPlaylist();
applyPreset(presetCycCurr);
}
//set brightness
updateVal(&req, "&A=", &bri);
updateVal(req.c_str(), "&A=", &bri);
bool col0Changed = false, col1Changed = false;
//set colors
col0Changed |= updateVal(&req, "&R=", &colIn[0]);
col0Changed |= updateVal(&req, "&G=", &colIn[1]);
col0Changed |= updateVal(&req, "&B=", &colIn[2]);
col0Changed |= updateVal(&req, "&W=", &colIn[3]);
col0Changed |= updateVal(req.c_str(), "&R=", &colIn[0]);
col0Changed |= updateVal(req.c_str(), "&G=", &colIn[1]);
col0Changed |= updateVal(req.c_str(), "&B=", &colIn[2]);
col0Changed |= updateVal(req.c_str(), "&W=", &colIn[3]);
col1Changed |= updateVal(&req, "R2=", &colInSec[0]);
col1Changed |= updateVal(&req, "G2=", &colInSec[1]);
col1Changed |= updateVal(&req, "B2=", &colInSec[2]);
col1Changed |= updateVal(&req, "W2=", &colInSec[3]);
col1Changed |= updateVal(req.c_str(), "R2=", &colInSec[0]);
col1Changed |= updateVal(req.c_str(), "G2=", &colInSec[1]);
col1Changed |= updateVal(req.c_str(), "B2=", &colInSec[2]);
col1Changed |= updateVal(req.c_str(), "W2=", &colInSec[3]);
#ifdef WLED_ENABLE_LOXONE
//lox parser
@ -740,7 +818,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) {
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
selseg.setColor(2, col2, selectedSeg); // defined above (SS= or main)
selseg.setColor(2, col2); // defined above (SS= or main)
stateChanged = true;
if (!singleSegment) strip.setColor(2, col2); // will set color to all active & selected segments
}
@ -769,37 +847,44 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (col0Changed) {
stateChanged = true;
uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
selseg.setColor(0, colIn0, selectedSeg);
selseg.setColor(0, colIn0);
if (!singleSegment) strip.setColor(0, colIn0); // will set color to all active & selected segments
}
if (col1Changed) {
stateChanged = true;
uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
selseg.setColor(1, colIn1, selectedSeg);
selseg.setColor(1, colIn1);
if (!singleSegment) strip.setColor(1, colIn1); // will set color to all active & selected segments
}
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
// set effect parameters
if (updateVal(&req, "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (updateVal(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
fxModeChanged = true;
}
speedChanged = updateVal(&req, "SX=", &speedIn);
intensityChanged = updateVal(&req, "IX=", &intensityIn);
paletteChanged = updateVal(&req, "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
speedChanged = updateVal(req.c_str(), "SX=", &speedIn);
intensityChanged = updateVal(req.c_str(), "IX=", &intensityIn);
paletteChanged = updateVal(req.c_str(), "FP=", &paletteIn, 0, strip.getPaletteCount()-1);
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged);
// apply to main and all selected segments to prevent #1618.
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
if (fxModeChanged) strip.setMode(i, effectIn);
if (fxModeChanged) {
seg.startTransition(strip.getTransition());
seg.mode = effectIn;
// TODO: we should load defaults here as well
}
if (speedChanged) seg.speed = speedIn;
if (intensityChanged) seg.intensity = intensityIn;
if (paletteChanged) seg.palette = paletteIn;
if (paletteChanged) {
if (strip.paletteBlend) seg.startTransition(strip.getTransition());
seg.palette = paletteIn;
}
}
//set advanced overlay
@ -896,7 +981,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
realtimeOverride = getNumVal(&req, pos);
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) {
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId());
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
}
}

View File

@ -33,11 +33,6 @@
#define DS1307_CTRL_ID 0x68
DS1307RTC::DS1307RTC()
{
Wire.begin();
}
// PUBLIC FUNCTIONS
time_t DS1307RTC::get() // Aquire data from buffer and convert to time_t
{

View File

@ -13,7 +13,8 @@ class DS1307RTC
{
// user-accessible "public" interface
public:
DS1307RTC();
DS1307RTC() {}
static void begin() { Wire.begin(); }
static time_t get();
static bool set(time_t t);
static bool read(tmElements_t &tm);

View File

@ -28,7 +28,7 @@ void notify(byte callMode, bool followUp)
default: return;
}
byte udpOut[WLEDPACKETSIZE];
WS2812FX::Segment& mainseg = strip.getMainSegment();
Segment& mainseg = strip.getMainSegment();
udpOut[0] = 0; //0: wled notifier protocol 1: WARLS protocol
udpOut[1] = callMode;
udpOut[2] = bri;
@ -89,12 +89,14 @@ void notify(byte callMode, bool followUp)
udpOut[37] = strip.hasCCTBus() ? 0 : 255; //check this is 0 for the next value to be significant
udpOut[38] = mainseg.cct;
udpOut[39] = strip.getMaxSegments();
udpOut[39] = strip.getActiveSegmentsNum();
udpOut[40] = UDP_SEG_SIZE; //size of each loop iteration (one segment)
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment &selseg = strip.getSegment(i);
uint16_t ofs = 41 + i*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = i;
size_t s = 0, nsegs = strip.getSegmentsNum();
for (size_t i = 0; i < nsegs; i++) {
Segment &selseg = strip.getSegment(i);
if (!selseg.isActive()) continue;
uint16_t ofs = 41 + s*UDP_SEG_SIZE; //start of segment offset byte
udpOut[0 +ofs] = s;
udpOut[1 +ofs] = selseg.start >> 8;
udpOut[2 +ofs] = selseg.start & 0xFF;
udpOut[3 +ofs] = selseg.stop >> 8;
@ -122,6 +124,7 @@ void notify(byte callMode, bool followUp)
udpOut[25+ofs] = B(selseg.colors[2]);
udpOut[26+ofs] = W(selseg.colors[2]);
udpOut[27+ofs] = selseg.cct;
++s;
}
//uint16_t offs = SEG_OFFSET;
@ -143,20 +146,20 @@ void realtimeLock(uint32_t timeoutMs, byte md)
if (!realtimeMode && !realtimeOverride) {
uint16_t stop, start;
if (useMainSegmentOnly) {
WS2812FX::Segment& mainseg = strip.getMainSegment();
Segment& mainseg = strip.getMainSegment();
start = mainseg.start;
stop = mainseg.stop;
mainseg.setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId());
mainseg.setOption(SEG_OPTION_FREEZE, true);
} else {
start = 0;
stop = strip.getLengthTotal();
}
// clear strip/segment
for (uint16_t i = start; i < stop; i++) strip.setPixelColor(i,0,0,0,0);
for (size_t i = start; i < stop; i++) strip.setPixelColor(i,BLACK);
// if WLED was off and using main segment only, freeze non-main segments so they stay off
if (useMainSegmentOnly && bri == 0) {
for (uint8_t s=0; s < strip.getMaxSegments(); s++) {
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, true, s);
for (size_t s=0; s < strip.getSegmentsNum(); s++) {
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, true);
}
}
}
@ -183,7 +186,7 @@ void exitRealtime() {
realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately
realtimeIP[0] = 0;
if (useMainSegmentOnly) { // unfreeze live segment again
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, false, strip.getMainSegmentId());
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, false);
}
updateInterfaces(CALL_MODE_WS_SEND);
}
@ -221,7 +224,7 @@ void handleNotifications()
if (!udpConnected) return;
bool isSupp = false;
uint16_t packetSize = notifierUdp.parsePacket();
size_t packetSize = notifierUdp.parsePacket();
if (!packetSize && udp2Connected) {
packetSize = notifier2Udp.parsePacket();
isSupp = true;
@ -241,7 +244,7 @@ void handleNotifications()
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
uint16_t id = 0;
uint16_t totalLen = strip.getLengthTotal();
for (uint16_t i = 0; i < packetSize -2; i += 3)
for (size_t i = 0; i < packetSize -2; i += 3)
{
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
id++; if (id >= totalLen) break;
@ -275,7 +278,7 @@ void handleNotifications()
}
if (it != Nodes.end()) {
for (byte x = 0; x < 4; x++) {
for (size_t x = 0; x < 4; x++) {
it->second.ip[x] = udpIn[x + 2];
}
it->second.age = 0; // reset 'age counter'
@ -287,7 +290,7 @@ void handleNotifications()
it->second.nodeType = udpIn[38];
uint32_t build = 0;
if (len >= 44)
for (byte i=0; i<sizeof(uint32_t); i++)
for (size_t i=0; i<sizeof(uint32_t); i++)
build |= udpIn[40+i]<<(8*i);
it->second.build = build;
}
@ -339,11 +342,11 @@ void handleNotifications()
if (applyEffects && currentPlaylist >= 0) unloadPlaylist();
if (version > 10 && (receiveSegmentOptions || receiveSegmentBounds)) {
uint8_t numSrcSegs = udpIn[39];
for (uint8_t i = 0; i < numSrcSegs; i++) {
for (size_t i = 0; i < numSrcSegs; i++) {
uint16_t ofs = 41 + i*udpIn[40]; //start of segment offset byte
uint8_t id = udpIn[0 +ofs];
if (id > strip.getMaxSegments()) break;
WS2812FX::Segment& selseg = strip.getSegment(id);
if (id > strip.getSegmentsNum()) break;
Segment& selseg = strip.getSegment(id);
uint16_t start = (udpIn[1+ofs] << 8 | udpIn[2+ofs]);
uint16_t stop = (udpIn[3+ofs] << 8 | udpIn[4+ofs]);
uint16_t offset = (udpIn[7+ofs] << 8 | udpIn[8+ofs]);
@ -351,8 +354,8 @@ void handleNotifications()
strip.setSegment(id, start, stop, selseg.grouping, selseg.spacing, offset);
continue;
}
for (uint8_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01, id); //only take into account mirrored, selected, on, reversed
selseg.setOpacity(udpIn[10+ofs], id);
for (size_t j = 0; j<4; j++) selseg.setOption(j, (udpIn[9 +ofs] >> j) & 0x01); //only take into account mirrored, selected, on, reversed
selseg.setOpacity(udpIn[10+ofs]);
if (applyEffects) {
strip.setMode(id, udpIn[11+ofs]);
selseg.speed = udpIn[12+ofs];
@ -360,10 +363,10 @@ void handleNotifications()
selseg.palette = udpIn[14+ofs];
}
if (receiveNotificationColor || !someSel) {
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]), id);
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]), id);
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]), id);
selseg.setCCT(udpIn[27+ofs], id);
selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
selseg.setCCT(udpIn[27+ofs]);
}
//setSegment() also properly resets segments
if (receiveSegmentBounds) {
@ -377,8 +380,8 @@ void handleNotifications()
// simple effect sync, applies to all selected segments
if (applyEffects && (version < 11 || !receiveSegmentOptions)) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i);
for (size_t i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue;
if (udpIn[8] < strip.getModeCount()) strip.setMode(i, udpIn[8]);
seg.speed = udpIn[9];
@ -458,7 +461,7 @@ void handleNotifications()
uint16_t id = (tpmPayloadFrameSize/3)*(packetNum-1); //start LED
uint16_t totalLen = strip.getLengthTotal();
for (uint16_t i = 6; i < tpmPayloadFrameSize + 4; i += 3)
for (size_t i = 6; i < tpmPayloadFrameSize + 4U; i += 3)
{
if (id < totalLen)
{
@ -494,14 +497,14 @@ void handleNotifications()
uint16_t totalLen = strip.getLengthTotal();
if (udpIn[0] == 1) //warls
{
for (uint16_t i = 2; i < packetSize -3; i += 4)
for (size_t i = 2; i < packetSize -3; i += 4)
{
setRealtimePixel(udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3], 0);
}
} else if (udpIn[0] == 2) //drgb
{
uint16_t id = 0;
for (uint16_t i = 2; i < packetSize -2; i += 3)
for (size_t i = 2; i < packetSize -2; i += 3)
{
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
@ -510,7 +513,7 @@ void handleNotifications()
} else if (udpIn[0] == 3) //drgbw
{
uint16_t id = 0;
for (uint16_t i = 2; i < packetSize -3; i += 4)
for (size_t i = 2; i < packetSize -3; i += 4)
{
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
@ -519,7 +522,7 @@ void handleNotifications()
} else if (udpIn[0] == 4) //dnrgb
{
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
for (uint16_t i = 4; i < packetSize -2; i += 3)
for (size_t i = 4; i < packetSize -2; i += 3)
{
if (id >= totalLen) break;
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
@ -528,7 +531,7 @@ void handleNotifications()
} else if (udpIn[0] == 5) //dnrgbw
{
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
for (uint16_t i = 4; i < packetSize -2; i += 4)
for (size_t i = 4; i < packetSize -2; i += 4)
{
if (id >= totalLen) break;
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
@ -560,11 +563,16 @@ void handleNotifications()
void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
{
uint16_t pix = i + arlsOffset;
if (pix < strip.getLengthTotal())
{
if (!arlsDisableGammaCorrection && strip.gammaCorrectCol)
{
strip.setPixelColor(pix, strip.gamma8(r), strip.gamma8(g), strip.gamma8(b), strip.gamma8(w));
if (pix < strip.getLengthTotal()) {
if (!arlsDisableGammaCorrection && strip.gammaCorrectCol) {
r = gamma8(r);
g = gamma8(g);
b = gamma8(b);
w = gamma8(w);
}
if (useMainSegmentOnly) {
Segment &seg = strip.getMainSegment();
if (pix<seg.length()) seg.setPixelColor(pix, r, g, b, w);
} else {
strip.setPixelColor(pix, r, g, b, w);
}
@ -618,7 +626,7 @@ void sendSysInfoUDP()
data[0] = 255;
data[1] = 1;
for (byte x = 0; x < 4; x++) {
for (size_t x = 0; x < 4; x++) {
data[x + 2] = ip[x];
}
memcpy((byte *)data + 6, serverDescription, 32);
@ -632,7 +640,7 @@ void sendSysInfoUDP()
data[39] = ip[3]; // unit ID == last IP number
uint32_t build = VERSION;
for (byte i=0; i<sizeof(uint32_t); i++)
for (size_t i=0; i<sizeof(uint32_t); i++)
data[40+i] = (build>>(8*i)) & 0xFF;
IPAddress broadcastIP(255, 255, 255, 255);
@ -684,15 +692,15 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
case 0: // DDP
{
// calculate the number of UDP packets we need to send
uint16_t channelCount = length * 3; // 1 channel for every R,G,B value
uint16_t packetCount = ((channelCount-1) / DDP_CHANNELS_PER_PACKET) +1;
size_t channelCount = length * 3; // 1 channel for every R,G,B value
size_t packetCount = ((channelCount-1) / DDP_CHANNELS_PER_PACKET) +1;
// there are 3 channels per RGB pixel
uint32_t channel = 0; // TODO: allow specifying the start channel
// the current position in the buffer
uint16_t bufferOffset = 0;
size_t bufferOffset = 0;
for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
for (size_t currentPacket = 0; currentPacket < packetCount; currentPacket++) {
if (sequenceNumber > 15) sequenceNumber = 0;
if (!ddpUdp.beginPacket(client, DDP_DEFAULT_PORT)) { // port defined in ESPAsyncE131.h
@ -701,10 +709,10 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
}
// the amount of data is AFTER the header in the current packet
uint16_t packetSize = DDP_CHANNELS_PER_PACKET;
size_t packetSize = DDP_CHANNELS_PER_PACKET;
uint8_t flags = DDP_FLAGS1_VER1;
if (currentPacket == (packetCount - 1)) {
if (currentPacket == (packetCount - 1U)) {
// last packet, set the push flag
// TODO: determine if we want to send an empty push packet to each destination after sending the pixel data
flags = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH;
@ -729,7 +737,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8
// write the colors, the write write(const uint8_t *buffer, size_t size)
// function is just a loop internally too
for (uint16_t i = 0; i < packetSize; i += 3) {
for (size_t i = 0; i < packetSize; i += 3) {
ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // R
ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // G
ddpUdp.write(scale8(buffer[bufferOffset++], bri)); // B

View File

@ -59,5 +59,3 @@ bool UsermodManager::add(Usermod* um)
ums[numMods++] = um;
return true;
}
byte UsermodManager::getModCount() {return numMods;}

View File

@ -132,6 +132,13 @@
#include "../usermods/Si7021_MQTT_HA/usermod_si7021_mqtt_ha.h"
#endif
#ifdef USERMOD_AUDIOREACTIVE
#ifdef WLED_DISABLE_AUDIO
#error Incompatible options: WLED_DISABLE_AUDIO and USERMOD_AUDIOREACTIVE
#endif
#include "../usermods/audioreactive/audio_reactive.h"
#endif
void registerUsermods()
{
/*
@ -251,4 +258,11 @@ void registerUsermods()
#ifdef USERMOD_SI7021_MQTT_HA
usermods.add(new Si7021_MQTT_HA());
#endif
#ifdef USERMOD_AUDIOREACTIVE
#ifdef WLED_DISABLE_AUDIO
#error Incompatible options: WLED_DISABLE_AUDIO and USERMOD_AUDIOREACTIVE
#endif
usermods.add(new AudioReactive());
#endif
}

View File

@ -14,16 +14,14 @@ int getNumVal(const String* req, uint16_t pos)
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
{
if (str == nullptr || str[0] == '\0') return;
if (str[0] == 'r') {*val = random8(minv,maxv); return;}
if (str[0] == 'r') {*val = random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
bool wrap = false;
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
if (str[0] == '~') {
int out = atoi(str +1);
if (out == 0)
{
if (out == 0) {
if (str[1] == '0') return;
if (str[1] == '-')
{
if (str[1] == '-') {
*val = (int)(*val -1) < (int)minv ? maxv : min((int)maxv,(*val -1)); //-1, wrap around
} else {
*val = (int)(*val +1) > (int)maxv ? minv : max((int)minv,(*val +1)); //+1, wrap around
@ -38,19 +36,20 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
}
*val = out;
}
} else
{
return;
} else if (minv == maxv && minv == 0) { // limits "unset" i.e. both 0
byte p1 = atoi(str);
const char* str2 = strchr(str,'~'); //min/max range (for preset cycle, e.g. "1~5~")
const char* str2 = strchr(str,'~'); // min/max range (for preset cycle, e.g. "1~5~")
if (str2) {
byte p2 = atoi(str2+1);
presetCycMin = p1; presetCycMax = p2;
while (isdigit((str2+1)[0])) str2++;
parseNumber(str2+1, val, p1, p2);
} else {
*val = p1;
byte p2 = atoi(++str2); // skip ~
if (p2 > 0) {
while (isdigit(*(++str2))); // skip digits
parseNumber(str2, val, p1, p2);
return;
}
}
}
*val = atoi(str);
}
@ -70,12 +69,12 @@ bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
}
bool updateVal(const String* req, const char* key, byte* val, byte minv, byte maxv)
bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv)
{
int pos = req->indexOf(key);
if (pos < 1) return false;
if (req->length() < (unsigned int)(pos + 4)) return false;
parseNumber(req->c_str() + pos +3, val, minv, maxv);
const char *v = strstr(req, key);
if (v) v += strlen(key);
else return false;
parseNumber(v, val, minv, maxv);
return true;
}
@ -211,6 +210,9 @@ bool requestJSONBufferLock(uint8_t module)
}
jsonBufferLock = module ? module : 255;
DEBUG_PRINT(F("JSON buffer locked. ("));
DEBUG_PRINT(jsonBufferLock);
DEBUG_PRINTLN(")");
fileDoc = &doc; // used for applying presets (presets.cpp)
doc.clear();
return true;
@ -231,6 +233,22 @@ void releaseJSONBufferLock()
// caller must provide large enough buffer for name (incluing SR extensions)!
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
{
if (src == JSON_mode_names || src == nullptr) {
if (mode < strip.getModeCount()) {
char lineBuffer[256];
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
strcpy_P(lineBuffer, strip.getModeData(mode));
size_t len = strlen(lineBuffer);
size_t j = 0;
for (; j < maxLen && j < len; j++) {
if (lineBuffer[j] == '\0' || lineBuffer[j] == '@') break;
dest[j] = lineBuffer[j];
}
dest[j] = 0; // terminate string
return strlen(dest);
} else return 0;
}
uint8_t qComma = 0;
bool insideQuotes = false;
uint8_t printedChars = 0;
@ -260,3 +278,239 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
dest[printedChars] = '\0';
return strlen(dest);
}
// extracts effect slider data (1st group after @)
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var)
{
dest[0] = '\0'; // start by clearing buffer
if (mode < strip.getModeCount()) {
String lineBuffer = FPSTR(strip.getModeData(mode));
if (lineBuffer.length() > 0) {
int16_t start = lineBuffer.indexOf('@');
int16_t stop = lineBuffer.indexOf(';', start);
if (start>0 && stop>0) {
String names = lineBuffer.substring(start, stop); // include @
int16_t nameBegin = 1, nameEnd, nameDefault;
if (slider < 10) {
for (size_t i=0; i<=slider; i++) {
const char *tmpstr;
dest[0] = '\0'; //clear dest buffer
if (nameBegin == 0) break; // there are no more names
nameEnd = names.indexOf(',', nameBegin);
if (i == slider) {
nameDefault = names.indexOf('=', nameBegin); // find default value
if (nameDefault > 0 && var && ((nameEnd>0 && nameDefault<nameEnd) || nameEnd<0)) {
*var = (uint8_t)atoi(names.substring(nameDefault+1).c_str());
}
if (names.charAt(nameBegin) == '!') {
switch (slider) {
case 0: tmpstr = PSTR("FX Speed"); break;
case 1: tmpstr = PSTR("FX Intensity"); break;
case 2: tmpstr = PSTR("FX Custom 1"); break;
case 3: tmpstr = PSTR("FX Custom 2"); break;
case 4: tmpstr = PSTR("FX Custom 3"); break;
default: tmpstr = PSTR("FX Custom"); break;
}
strncpy_P(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
dest[maxLen-1] = '\0';
} else {
if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
else tmpstr = names.substring(nameBegin, nameEnd).c_str();
strlcpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
}
}
nameBegin = nameEnd+1; // next name (if "," is not found it will be 0)
} // next slider
} else if (slider == 255) {
// palette
strlcpy(dest, "pal", maxLen);
names = lineBuffer.substring(stop+1); // stop has index of color slot names
nameBegin = names.indexOf(';'); // look for palette
if (nameBegin >= 0) {
nameEnd = names.indexOf(';', nameBegin+1);
if (!isdigit(names[nameBegin+1])) nameBegin = names.indexOf('=', nameBegin+1); // look for default value
if (nameEnd >= 0 && nameBegin > nameEnd) nameBegin = -1;
if (nameBegin >= 0 && var) {
*var = (uint8_t)atoi(names.substring(nameBegin+1).c_str());
}
}
}
// we have slider name (including default value) in the dest buffer
for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value
} else {
// defaults to just speed and intensity since there is no slider data
switch (slider) {
case 0: strncpy_P(dest, PSTR("FX Speed"), maxLen); break;
case 1: strncpy_P(dest, PSTR("FX Intensity"), maxLen); break;
}
dest[maxLen] = '\0'; // strncpy does not necessarily null terminate string
}
}
return strlen(dest);
}
return 0;
}
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{
if (mode < strip.getModeCount()) {
String lineBuffer = FPSTR(strip.getModeData(mode));
if (lineBuffer.length() > 0) {
int16_t start = lineBuffer.lastIndexOf(';');
if (start<0) return -1;
int16_t stop = lineBuffer.indexOf(segVar, start+1);
if (stop<0) return -1;
return atoi(lineBuffer.substring(stop+strlen(segVar)+1).c_str());
}
}
return -1;
}
uint16_t crc16(const unsigned char* data_p, size_t length) {
uint8_t x;
uint16_t crc = 0xFFFF;
if (!length) return 0x1D0F;
while (length--) {
x = crc >> 8 ^ *data_p++;
x ^= x>>4;
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
}
return crc;
}
#ifndef WLED_DISABLE_AUDIO
///////////////////////////////////////////////////////////////////////////////
// Begin simulateSound (to enable audio enhanced effects to display something)
///////////////////////////////////////////////////////////////////////////////
// Currently 4 types defined, to be fine tuned and new types added
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0,
UMS_WeWillRockYou,
UMS_10_3,
UMS_14_3
} um_soundSimulations_t;
// this is still work in progress
um_data_t* simulateSound(uint8_t simulationId)
{
static uint8_t samplePeak;
static float FFT_MajorPeak;
static uint8_t maxVol;
static uint8_t binNum;
static float volumeSmth;
static uint16_t volumeRaw;
static float my_magnitude;
//arrays
uint8_t *fftResult;
static um_data_t* um_data = nullptr;
if (!um_data) {
//claim storage for arrays
fftResult = (uint8_t *)malloc(sizeof(uint8_t) * 16);
// initialize um_data pointer structure
// NOTE!!!
// This may change as AudioReactive usermod may change
um_data = new um_data_t;
um_data->u_size = 8;
um_data->u_type = new um_types_t[um_data->u_size];
um_data->u_data = new void*[um_data->u_size];
um_data->u_data[0] = &volumeSmth;
um_data->u_data[1] = &volumeRaw;
um_data->u_data[2] = fftResult;
um_data->u_data[3] = &samplePeak;
um_data->u_data[4] = &FFT_MajorPeak;
um_data->u_data[5] = &my_magnitude;
um_data->u_data[6] = &maxVol;
um_data->u_data[7] = &binNum;
} else {
// get arrays from um_data
fftResult = (uint8_t*)um_data->u_data[2];
}
uint32_t ms = millis();
switch (simulationId) {
default:
case UMS_BeatSin:
for (int i = 0; i<16; i++)
fftResult[i] = beatsin8(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8(120, 0, 255) + (256/16 * i)) % 256;
volumeSmth = fftResult[8];
break;
case UMS_WeWillRockYou:
if (ms%2000 < 200) {
volumeSmth = random8(255);
for (int i = 0; i<5; i++)
fftResult[i] = random8(255);
}
else if (ms%2000 < 400) {
volumeSmth = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
else if (ms%2000 < 600) {
volumeSmth = random8(255);
for (int i = 5; i<11; i++)
fftResult[i] = random8(255);
}
else if (ms%2000 < 800) {
volumeSmth = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
else if (ms%2000 < 1000) {
volumeSmth = random8(255);
for (int i = 11; i<16; i++)
fftResult[i] = random8(255);
}
else {
volumeSmth = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
break;
case UMS_10_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
volumeSmth = fftResult[8];
break;
case UMS_14_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
volumeSmth = fftResult[8];
break;
}
samplePeak = random8() > 250;
FFT_MajorPeak = volumeSmth;
maxVol = 10; // this gets feedback fro UI
binNum = 8; // this gets feedback fro UI
volumeRaw = volumeSmth;
my_magnitude = 10000.0 / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ???
if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
return um_data;
}
#endif
void enumerateLedmaps() {
ledMaps = 1;
for (size_t i=1; i<10; i++) {
char fileName[16];
sprintf_P(fileName, PSTR("/ledmap%d.json"), i);
bool isFile = WLED_FS.exists(fileName);
if (isFile) ledMaps |= 1 << i;
}
}

View File

@ -118,6 +118,7 @@ void WLED::loop()
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
}
yield();
#ifdef ESP8266
MDNS.update();
@ -204,6 +205,7 @@ void WLED::loop()
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
strip.printSize();
loops = 0;
maxUsermodMillis = 0;
maxStripMillis = 0;
@ -313,7 +315,10 @@ void WLED::setup()
if (!fsinit) {
DEBUGFS_PRINTLN(F("FS failed!"));
errorFlag = ERR_FS_BEGIN;
} else deEEP();
}
#ifdef WLED_ADD_EEPROM_SUPPORT
else deEEP();
#endif
updateFSInfo();
DEBUG_PRINTLN(F("Reading config"));
@ -410,6 +415,7 @@ void WLED::beginStrip()
{
// Initialize NeoPixel Strip and button
strip.finalizeInit(); // busses created during deserializeConfig()
strip.loadCustomPalettes();
strip.deserializeMap();
strip.makeAutoSegments();
strip.setBrightness(0);
@ -692,6 +698,7 @@ void WLED::handleConnection()
DEBUG_PRINT(F("Heap too low! "));
DEBUG_PRINTLN(heap);
forceReconnect = true;
strip.purgeSegments(true); // remove all but one segments from memory
}
lastHeap = heap;
heapTime = now;

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2208140
#define VERSION 2208191
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -86,6 +86,8 @@
#endif
#include "esp_task_wdt.h"
#endif
#include <Wire.h>
#include <SPI.h>
#include "src/dependencies/network/Network.h"
@ -94,7 +96,9 @@
#endif
#include <ESPAsyncWebServer.h>
#include <EEPROM.h>
#ifdef WLED_ADD_EEPROM_SUPPORT
#include <EEPROM.h>
#endif
#include <WiFiUdp.h>
#include <DNSServer.h>
#ifndef WLED_DISABLE_OTA
@ -152,19 +156,12 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define PSRAMDynamicJsonDocument DynamicJsonDocument
#endif
#include "fcn_declare.h"
#include "html_ui.h"
#ifdef WLED_ENABLE_SIMPLE_UI
#include "html_simple.h"
#endif
#include "html_settings.h"
#include "html_other.h"
#include "FX.h"
#include "ir_codes.h"
#include "const.h"
#include "fcn_declare.h"
#include "NodeStruct.h"
#include "pin_manager.h"
#include "bus_manager.h"
#include "FX.h"
#ifndef CLIENT_SSID
#define CLIENT_SSID DEFAULT_CLIENT_SSID
@ -458,12 +455,12 @@ WLED_GLOBAL bool wasConnected _INIT(false);
WLED_GLOBAL byte lastRandomIndex _INIT(0); // used to save last random color so the new one is not the same
// transitions
WLED_GLOBAL bool transitionActive _INIT(false);
WLED_GLOBAL uint16_t transitionDelayDefault _INIT(transitionDelay);
WLED_GLOBAL uint16_t transitionDelayTemp _INIT(transitionDelay);
WLED_GLOBAL bool transitionActive _INIT(false);
WLED_GLOBAL uint16_t transitionDelayDefault _INIT(transitionDelay); // default transition time (storec in cfg.json)
WLED_GLOBAL uint16_t transitionDelayTemp _INIT(transitionDelay); // actual transition duration (overrides transitionDelay in certain cases)
WLED_GLOBAL unsigned long transitionStartTime;
WLED_GLOBAL float tperLast _INIT(0); // crossfade transition progress, 0.0f - 1.0f
WLED_GLOBAL bool jsonTransitionOnce _INIT(false);
WLED_GLOBAL float tperLast _INIT(0.0f); // crossfade transition progress, 0.0f - 1.0f
WLED_GLOBAL bool jsonTransitionOnce _INIT(false); // flag to override transitionDelay (playlist, JSON API: "live" & "seg":{"i"} & "tt")
// nightlight
WLED_GLOBAL bool nightlightActive _INIT(false);
@ -642,10 +639,16 @@ WLED_GLOBAL WS2812FX strip _INIT(WS2812FX());
WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after
WLED_GLOBAL bool doInitBusses _INIT(false);
WLED_GLOBAL int8_t loadLedmap _INIT(-1);
WLED_GLOBAL uint16_t ledMaps _INIT(0); // bitfield representation of available ledmaps
// Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
WLED_GLOBAL int8_t i2c_sda _INIT(-1); // global I2C SDA pin [HW_PIN_SDA] (used for usermods)
WLED_GLOBAL int8_t i2c_scl _INIT(-1); // global I2C SCL pin [HW_PIN_SCL] (used for usermods)
WLED_GLOBAL int8_t spi_mosi _INIT(-1); // global SPI DATA/MOSI pin [HW_PIN_DATASPI] (used for usermods)
WLED_GLOBAL int8_t spi_sclk _INIT(-1); // global SPI CLOCK/SCLK pin [HW_PIN_CLOCKSPI] (used for usermods)
// global ArduinoJson buffer
WLED_GLOBAL StaticJsonDocument<JSON_BUFFER_SIZE> doc;
WLED_GLOBAL volatile uint8_t jsonBufferLock _INIT(0);

View File

@ -1,3 +1,4 @@
#ifdef WLED_ADD_EEPROM_SUPPORT
#include <EEPROM.h>
#include "wled.h"
@ -418,7 +419,7 @@ void deEEP() {
segObj[F("ix")] = EEPROM.read(i+16);
segObj["pal"] = EEPROM.read(i+17);
} else {
WS2812FX::Segment* seg = strip.getSegments();
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++)
@ -472,4 +473,5 @@ void deEEPSettings() {
usermods.readFromConfig(empty);
serializeConfig();
}
}
#endif

View File

@ -1,5 +1,12 @@
#include "wled.h"
#include "html_ui.h"
#ifdef WLED_ENABLE_SIMPLE_UI
#include "html_simple.h"
#endif
#include "html_settings.h"
#include "html_other.h"
/*
* Integrated HTTP web server page declarations
*/
@ -103,6 +110,16 @@ void initServer()
request->send(response);
//request->send_P(200, "text/html", PAGE_liveviewws);
});
#ifndef WLED_DISABLE_2D
server.on("/liveview2D", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return;
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", PAGE_liveviewws2D, PAGE_liveviewws2D_length);
response->addHeader(FPSTR(s_content_enc),"gzip");
setStaticContentCacheHeaders(response);
request->send(response);
//request->send_P(200, "text/html", PAGE_liveviewws);
});
#endif
#else
server.on("/liveview", HTTP_GET, [](AsyncWebServerRequest *request){
if (handleIfNoneMatchCacheHeader(request)) return;
@ -482,7 +499,7 @@ void serveSettingsJS(AsyncWebServerRequest* request)
char buf[SETTINGS_STACK_BUF_SIZE+37];
buf[0] = 0;
byte subPage = request->arg(F("p")).toInt();
if (subPage > 9) {
if (subPage > 10) {
strcpy_P(buf, PSTR("alert('Settings for this request are not implemented.');"));
request->send(501, "application/javascript", buf);
return;
@ -516,6 +533,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
else if (url.indexOf("sec") > 0) subPage = 6;
else if (url.indexOf("dmx") > 0) subPage = 7;
else if (url.indexOf("um") > 0) subPage = 8;
else if (url.indexOf("2D") > 0) subPage = 10;
else if (url.indexOf("lock") > 0) subPage = 251;
}
else if (url.indexOf("/update") >= 0) subPage = 9; // update page, for PIN check
@ -548,6 +566,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
case 6: strcpy_P(s, PSTR("Security")); if (doReboot) strcpy_P(s2, PSTR("Rebooting, please wait ~10 seconds...")); break;
case 7: strcpy_P(s, PSTR("DMX")); break;
case 8: strcpy_P(s, PSTR("Usermods")); break;
case 10: strcpy_P(s, PSTR("2D")); break;
case 252: strcpy_P(s, correctPIN ? PSTR("PIN accepted") : PSTR("PIN rejected")); break;
}
@ -578,6 +597,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post)
case 7: response = request->beginResponse_P(200, "text/html", PAGE_settings_dmx, PAGE_settings_dmx_length); break;
case 8: response = request->beginResponse_P(200, "text/html", PAGE_settings_um, PAGE_settings_um_length); break;
case 9: response = request->beginResponse_P(200, "text/html", PAGE_update, PAGE_update_length); break;
case 10: response = request->beginResponse_P(200, "text/html", PAGE_settings_2D, PAGE_settings_2D_length); break;
case 251: {
correctPIN = !strlen(settingsPIN); // lock if a pin is set
createEditHandler(correctPIN);

View File

@ -15,8 +15,8 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
{
if(type == WS_EVT_CONNECT){
//client connected
sendDataWs(client);
DEBUG_PRINTLN(F("WS client connected."));
sendDataWs(client);
} else if(type == WS_EVT_DISCONNECT){
//client disconnected
if (client->id() == wsLiveClientId) wsLiveClientId = 0;
@ -36,7 +36,6 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
}
bool verboseResponse = false;
DEBUG_PRINTLN(F("WS JSON receive buffer requested."));
if (!requestJSONBufferLock(11)) return;
DeserializationError error = deserializeJson(doc, data, len);
@ -59,6 +58,10 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if (verboseResponse) {
sendDataWs(client);
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
} else {
// we have to send something back otherwise WS connection closes
client->text(F("{\"success\":true}"));
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
}
}
} else {
@ -97,7 +100,6 @@ void sendDataWs(AsyncWebSocketClient * client)
if (!ws.count()) return;
AsyncWebSocketMessageBuffer * buffer;
DEBUG_PRINTLN(F("WS JSON send buffer requested."));
if (!requestJSONBufferLock(12)) return;
JsonObject state = doc.createNestedObject("state");
@ -105,21 +107,22 @@ void sendDataWs(AsyncWebSocketClient * client)
JsonObject info = doc.createNestedObject("info");
serializeInfo(info);
DEBUG_PRINTF("JSON buffer size: %u for WS request.\n", doc.memoryUsage());
size_t len = measureJson(doc);
DEBUG_PRINTF("JSON buffer size: %u for WS request (%u).\n", doc.memoryUsage(), len);
size_t heap1 = ESP.getFreeHeap();
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
size_t heap2 = ESP.getFreeHeap();
if (!buffer || heap1-heap2<len) {
releaseJSONBufferLock();
DEBUG_PRINTLN(F("WS buffer allocation failed."));
ws.closeAll(1013); //code 1013 = temporary overload, try again later
ws.cleanupClients(0); //disconnect all clients to release memory
return; //out of memory
}
serializeJson(doc, (char *)buffer->get(), len +1);
releaseJSONBufferLock();
buffer->lock();
serializeJson(doc, (char *)buffer->get(), len);
DEBUG_PRINT(F("Sending WS data "));
if (client) {
@ -129,9 +132,11 @@ void sendDataWs(AsyncWebSocketClient * client)
ws.textAll(buffer);
DEBUG_PRINTLN(F("to multiple clients."));
}
}
buffer->unlock();
ws._cleanBuffers();
#define MAX_LIVE_LEDS_WS 256
releaseJSONBufferLock();
}
bool sendLiveLedsWs(uint32_t wsClient)
{
@ -139,16 +144,24 @@ bool sendLiveLedsWs(uint32_t wsClient)
if (!wsc || wsc->queueLength() > 0) return false; //only send if queue free
uint16_t used = strip.getLengthTotal();
const uint16_t MAX_LIVE_LEDS_WS = strip.isMatrix ? 1024 : 256;
uint16_t n = ((used -1)/MAX_LIVE_LEDS_WS) +1; //only serve every n'th LED if count over MAX_LIVE_LEDS_WS
uint16_t bufSize = 2 + (used/n)*3;
uint16_t pos = (strip.isMatrix ? 4 : 2);
uint16_t bufSize = pos + (used/n)*3;
AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
if (!wsBuf) return false; //out of memory
uint8_t* buffer = wsBuf->get();
buffer[0] = 'L';
buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) {
buffer[1] = 2; //version
buffer[2] = strip.matrixWidth;
buffer[3] = strip.matrixHeight;
}
#endif
uint16_t pos = 2;
for (uint16_t i= 0; pos < bufSize -2; i += n)
for (uint16_t i = 0; pos < bufSize -2; i += n)
{
uint32_t c = strip.getPixelColor(i);
buffer[pos++] = qadd8(W(c), R(c)); //R, add white channel to RGB channels as a simple RGBW -> RGB map

View File

@ -177,7 +177,7 @@ void getSettingsJS(byte subPage, char* dest)
obuf = dest;
olen = 0;
if (subPage <0 || subPage >9) return;
if (subPage <0 || subPage >10) return;
if (subPage == 0)
{
@ -261,6 +261,15 @@ void getSettingsJS(byte subPage, char* dest)
// add reserved and usermod pins as d.um_p array
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
if (i2c_sda > -1 && i2c_scl > -1) {
oappend(","); oappend(itoa(i2c_sda,nS,10));
oappend(","); oappend(itoa(i2c_scl,nS,10));
}
if (spi_mosi > -1 && spi_sclk > -1) {
oappend(","); oappend(itoa(spi_mosi,nS,10));
oappend(","); oappend(itoa(spi_sclk,nS,10));
}
if (requestJSONBufferLock(6)) {
// if we can't allocate JSON buffer ignore usermod pins
JsonObject mods = doc.createNestedObject(F("um"));
@ -308,9 +317,14 @@ void getSettingsJS(byte subPage, char* dest)
// set limits
oappend(SET_F("bLimits("));
#if defined(ESP32) && defined(USERMOD_AUDIOREACTIVE)
// requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
oappend(itoa(WLED_MAX_BUSSES-2,nS,10)); oappend(","); // prevent use of I2S buses if audio installed
#else
oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(",");
#endif
oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(",");
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
oappend(itoa(MAX_LEDS,nS,10));
oappend(SET_F(");"));
@ -320,6 +334,7 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("CB"),strip.cctBlending);
sappend('v',SET_F("FR"),strip.getTargetFps());
sappend('v',SET_F("AW"),Bus::getAutoWhiteMode());
sappend('v',SET_F("LD"),strip.useLedsArray);
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s);
@ -580,6 +595,9 @@ void getSettingsJS(byte subPage, char* dest)
oappend(SET_F(" (build "));
oappendi(VERSION);
oappend(SET_F(")\";"));
oappend(SET_F("sd=\""));
oappend(serverDescription);
oappend(SET_F("\";"));
}
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
@ -615,6 +633,14 @@ void getSettingsJS(byte subPage, char* dest)
oappend(SET_F("numM="));
oappendi(usermods.getModCount());
oappend(";");
sappend('v',SET_F("SDA"),i2c_sda);
sappend('v',SET_F("SCL"),i2c_scl);
sappend('v',SET_F("MOSI"),spi_mosi);
sappend('v',SET_F("SCLK"),spi_sclk);
oappend(SET_F("addInfo('SDA','")); oappendi(HW_PIN_SDA); oappend(SET_F("');"));
oappend(SET_F("addInfo('SCL','")); oappendi(HW_PIN_SCL); oappend(SET_F("');"));
oappend(SET_F("addInfo('MOSI','")); oappendi(HW_PIN_DATASPI); oappend(SET_F("');"));
oappend(SET_F("addInfo('SCLK','")); oappendi(HW_PIN_CLOCKSPI); oappend(SET_F("');"));
usermods.appendConfigData();
}
@ -632,4 +658,37 @@ void getSettingsJS(byte subPage, char* dest)
oappendi(VERSION);
oappend(SET_F(")\";"));
}
if (subPage == 10) // 2D matrices
{
sappend('v',SET_F("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D
oappend(SET_F("resetPanels();"));
if (strip.isMatrix) {
sappend('v',SET_F("PH"),strip.panelH);
sappend('v',SET_F("PW"),strip.panelW);
sappend('v',SET_F("MPH"),strip.hPanels);
sappend('v',SET_F("MPV"),strip.vPanels);
sappend('v',SET_F("PB"),strip.matrix.bottomStart);
sappend('v',SET_F("PR"),strip.matrix.rightStart);
sappend('v',SET_F("PV"),strip.matrix.vertical);
sappend('c',SET_F("PS"),strip.matrix.serpentine);
// panels
for (uint8_t i=0; i<strip.hPanels*strip.vPanels; i++) {
char n[5];
oappend(SET_F("addPanel("));
oappend(itoa(i,n,10));
oappend(SET_F(");"));
char pO[8]; sprintf_P(pO, PSTR("P%d"), i);
uint8_t l = strlen(pO); pO[l+1] = 0;
pO[l] = 'B'; sappend('v',pO,strip.panel[i].bottomStart);
pO[l] = 'R'; sappend('v',pO,strip.panel[i].rightStart);
pO[l] = 'V'; sappend('v',pO,strip.panel[i].vertical);
pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine);
}
}
#else
oappend(SET_F("gId(\"somp\").remove(1);")); // remove 2D option from dropdown
#endif
}
}