From 81e70925c47b0fe1252c51b2a2797fa9bce4f413 Mon Sep 17 00:00:00 2001 From: Erwin Repolust Date: Wed, 8 Mar 2023 03:24:16 +0100 Subject: [PATCH 01/22] Changed to running average to improve accuracy --- usermods/Battery/usermod_v2_Battery.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 2dc85424..05532d9b 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -126,6 +126,8 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; + //initialize voltage again with analog read as that will give us faster precision + voltage = (analogReadMilliVolts(batteryPin) / 1000.0f + calibration / 2.0f) * 2.0f; } if (!success) { @@ -178,9 +180,9 @@ class UsermodBattery : public Usermod #ifdef ARDUINO_ARCH_ESP32 // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV) - rawValue = analogReadMilliVolts(batteryPin); - // calculate the voltage - voltage = (rawValue / 1000.0f) + calibration; + rawValue = analogReadMilliVolts(batteryPin) / 1000.0f + calibration / 2.0f; + // calculate the voltage with a 1/20th weighted running average + voltage = ((voltage / 2.0f) * 19.0f + rawValue) / 20.0f; // usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2 voltage *= 2.0f; #else @@ -191,8 +193,8 @@ class UsermodBattery : public Usermod voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration; #endif // check if voltage is within specified voltage range, allow 10% over/under voltage - voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; - + //voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; + // translate battery voltage into percentage /* the standard "map" function doesn't work From e00e778bce2b4bb905d3efa7d74642ba5b6b7633 Mon Sep 17 00:00:00 2001 From: Erwin Repolust Date: Wed, 8 Mar 2023 03:54:48 +0100 Subject: [PATCH 02/22] Less operations and better readable --- usermods/Battery/usermod_v2_Battery.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 05532d9b..0e9fc563 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -126,8 +126,8 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; - //initialize voltage again with analog read as that will give us faster precision - voltage = (analogReadMilliVolts(batteryPin) / 1000.0f + calibration / 2.0f) * 2.0f; + //initialize voltage with analog read + voltage = analogReadMilliVolts(batteryPin) / 500.0f + calibration; } if (!success) { @@ -179,12 +179,10 @@ class UsermodBattery : public Usermod initializing = false; #ifdef ARDUINO_ARCH_ESP32 - // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV) - rawValue = analogReadMilliVolts(batteryPin) / 1000.0f + calibration / 2.0f; - // calculate the voltage with a 1/20th weighted running average - voltage = ((voltage / 2.0f) * 19.0f + rawValue) / 20.0f; - // usually a voltage divider (50%) is used on ESP32, so we need to multiply by 2 - voltage *= 2.0f; + // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV) and divide by 500 to fix to the right decimal place and multiply by 2 to account for the voltage divider + rawValue = analogReadMilliVolts(batteryPin) / 500.0f + calibration; + // calculate the voltage with a weighted running average because ADC in ESP32 is fluctuating too much for a good single readout + voltage = (voltage * 19.0f + rawValue) / 20.0f; #else // read battery raw input rawValue = analogRead(batteryPin); @@ -192,9 +190,9 @@ class UsermodBattery : public Usermod // calculate the voltage voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration; #endif - // check if voltage is within specified voltage range, allow 10% over/under voltage + // check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage //voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; - + // translate battery voltage into percentage /* the standard "map" function doesn't work From 8b61b9ebfea84b2a4f1780747365fbb483b34e5c Mon Sep 17 00:00:00 2001 From: Erwin Repolust Date: Fri, 10 Mar 2023 01:28:04 +0100 Subject: [PATCH 03/22] Added code for esp8266 --- usermods/Battery/usermod_v2_Battery.h | 44 +++++++++++---------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 0e9fc563..60e90019 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -29,6 +29,10 @@ class UsermodBattery : public Usermod float rawValue = 0.0f; // calculated voltage float voltage = maxBatteryVoltage; + // between 0 and 1, to control strength of voltage smoothing filter + float alpha = 0.05f; + // multiplyer for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266 + float voltageMultiplyer = 2.0f; // mapped battery level based on voltage int8_t batteryLevel = 100; // offset or calibration value to fine tune the calculated voltage @@ -126,8 +130,8 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; - //initialize voltage with analog read - voltage = analogReadMilliVolts(batteryPin) / 500.0f + calibration; + //use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider and apply calibration value + voltage = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplyer + calibration; } if (!success) { @@ -137,8 +141,9 @@ class UsermodBattery : public Usermod pinMode(batteryPin, INPUT); } #else //ESP8266 boards have only one analog input pin A0 - pinMode(batteryPin, INPUT); + //use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by 3v ADC max voltage and apply calibration value + voltage = (analogRead(batteryPin) / 1023.0f) * 3.3f * voltageMultiplyer + calibration; #endif nextReadTime = millis() + readingInterval; @@ -179,16 +184,15 @@ class UsermodBattery : public Usermod initializing = false; #ifdef ARDUINO_ARCH_ESP32 - // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV) and divide by 500 to fix to the right decimal place and multiply by 2 to account for the voltage divider - rawValue = analogReadMilliVolts(batteryPin) / 500.0f + calibration; - // calculate the voltage with a weighted running average because ADC in ESP32 is fluctuating too much for a good single readout - voltage = (voltage * 19.0f + rawValue) / 20.0f; + // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider and apply calibration value + rawValue = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplyer + calibration; + // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout + voltage = voltage + alpha * (rawValue - voltage); #else - // read battery raw input - rawValue = analogRead(batteryPin); - - // calculate the voltage - voltage = ((rawValue / getAdcPrecision()) * maxBatteryVoltage) + calibration; + // use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by 3v ADC max voltage and apply calibration value + rawValue = (analogRead(batteryPin) / 1023.0f) * 3.0f * voltageMultiplyer + calibration; + // filter with exponential smoothing + voltage = voltage + alpha * (rawValue - voltage); #endif // check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage //voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; @@ -595,21 +599,7 @@ class UsermodBattery : public Usermod totalBatteryCapacity = capacity; } - /* - * Get the choosen adc precision - * esp8266 = 10bit resolution = 1024.0f - * esp32 = 12bit resolution = 4095.0f - */ - float getAdcPrecision() - { - #ifdef ARDUINO_ARCH_ESP32 - // esp32 - return 4096.0f; - #else - // esp8266 - return 1024.0f; - #endif - } + /* * Get the calculated voltage From ec08432f9256e250aa9dbadfd9804948419be4ef Mon Sep 17 00:00:00 2001 From: Erwin Repolust Date: Tue, 14 Mar 2023 01:44:41 +0100 Subject: [PATCH 04/22] added voltage multiplier to gui and set defaults --- usermods/Battery/battery_defaults.h | 9 +++++++ usermods/Battery/usermod_v2_Battery.h | 35 +++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/usermods/Battery/battery_defaults.h b/usermods/Battery/battery_defaults.h index c682cb45..958bfe52 100644 --- a/usermods/Battery/battery_defaults.h +++ b/usermods/Battery/battery_defaults.h @@ -26,6 +26,15 @@ #endif #endif +//the default ratio for the voltage divider +#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER + #ifdef ARDUINO_ARCH_ESP32 + #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f + #else //ESP8266 boards + #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 4.2f + #endif +#endif + #ifndef USERMOD_BATTERY_MAX_VOLTAGE #define USERMOD_BATTERY_MAX_VOLTAGE 4.2f #endif diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 60e90019..5fbfd6a7 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -31,8 +31,8 @@ class UsermodBattery : public Usermod float voltage = maxBatteryVoltage; // between 0 and 1, to control strength of voltage smoothing filter float alpha = 0.05f; - // multiplyer for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266 - float voltageMultiplyer = 2.0f; + // multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266 + float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER; // mapped battery level based on voltage int8_t batteryLevel = 100; // offset or calibration value to fine tune the calculated voltage @@ -130,8 +130,8 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; - //use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider and apply calibration value - voltage = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplyer + calibration; + //use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider ratio and apply calibration value + voltage = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; } if (!success) { @@ -142,8 +142,8 @@ class UsermodBattery : public Usermod } #else //ESP8266 boards have only one analog input pin A0 pinMode(batteryPin, INPUT); - //use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by 3v ADC max voltage and apply calibration value - voltage = (analogRead(batteryPin) / 1023.0f) * 3.3f * voltageMultiplyer + calibration; + //use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by voltage divider ratio and apply calibration value + voltage = (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; #endif nextReadTime = millis() + readingInterval; @@ -185,12 +185,12 @@ class UsermodBattery : public Usermod #ifdef ARDUINO_ARCH_ESP32 // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider and apply calibration value - rawValue = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplyer + calibration; + rawValue = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout voltage = voltage + alpha * (rawValue - voltage); #else // use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by 3v ADC max voltage and apply calibration value - rawValue = (analogRead(batteryPin) / 1023.0f) * 3.0f * voltageMultiplyer + calibration; + rawValue = (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; // filter with exponential smoothing voltage = voltage + alpha * (rawValue - voltage); #endif @@ -367,6 +367,7 @@ class UsermodBattery : public Usermod battery[F("max-voltage")] = maxBatteryVoltage; battery[F("capacity")] = totalBatteryCapacity; battery[F("calibration")] = calibration; + battery[F("voltage-multiplier")] = voltageMultiplier; battery[FPSTR(_readInterval)] = readingInterval; JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section @@ -443,6 +444,7 @@ class UsermodBattery : public Usermod setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage); setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity); setCalibration(battery[F("calibration")] | calibration); + setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); JsonObject ao = battery[F("auto-off")]; @@ -637,6 +639,23 @@ class UsermodBattery : public Usermod calibration = offset; } + /* + * Set the voltage multiplier value + * A multiplier that may need adjusting for different voltage divider setups + */ + void setVoltageMultiplier(float multiplier) + { + voltageMultiplier = multiplier; + } + + /* + * Get the voltage multiplier value + * A multiplier that may need adjusting for different voltage divider setups + */ + float getVoltageMultiplier() + { + return voltageMultiplier; + } /* * Get auto-off feature enabled status From 2c3fa0fd8f3b2f4d4e74d3b15d01eea6dd6bee49 Mon Sep 17 00:00:00 2001 From: Erwin Repolust Date: Thu, 16 Mar 2023 01:33:57 +0100 Subject: [PATCH 05/22] added function for voltage reads --- usermods/Battery/usermod_v2_Battery.h | 31 ++++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index 5fbfd6a7..d3e52e00 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -114,6 +114,17 @@ class UsermodBattery : public Usermod } } + float readVoltage() + { + #ifdef ARDUINO_ARCH_ESP32 + // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value + return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; + #else + // use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value + return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; + #endif + } + public: //Functions called by WLED @@ -130,8 +141,7 @@ class UsermodBattery : public Usermod if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); success = true; - //use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider ratio and apply calibration value - voltage = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; + voltage = readVoltage(); } if (!success) { @@ -142,8 +152,7 @@ class UsermodBattery : public Usermod } #else //ESP8266 boards have only one analog input pin A0 pinMode(batteryPin, INPUT); - //use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by voltage divider ratio and apply calibration value - voltage = (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; + voltage = readVoltage(); #endif nextReadTime = millis() + readingInterval; @@ -183,17 +192,10 @@ class UsermodBattery : public Usermod initializing = false; -#ifdef ARDUINO_ARCH_ESP32 - // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default) and divide by 1000 to get from milivolts to volts and multiply by voltage divider and apply calibration value - rawValue = (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; + rawValue = readVoltage(); // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout voltage = voltage + alpha * (rawValue - voltage); -#else - // use analog read on esp8266 ( 150 mV ~ 3000mV no attenuation options) and divide by ADC precision 1023 and multiply by 3v ADC max voltage and apply calibration value - rawValue = (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; - // filter with exponential smoothing - voltage = voltage + alpha * (rawValue - voltage); -#endif + // check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage //voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage; @@ -380,6 +382,9 @@ class UsermodBattery : public Usermod lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold; lp[FPSTR(_duration)] = lowPowerIndicatorDuration; + // read voltage in case calibration or voltage multiplier changed to see immediate effect + voltage = readVoltage() + DEBUG_PRINTLN(F("Battery config saved.")); } From 2119d08543407811ea370dd2f0e39b683002abf6 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 28 Apr 2023 22:00:35 +0200 Subject: [PATCH 06/22] Octopus 2D effect - by Stepko --- wled00/FX.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ wled00/FX.h | 1 + 2 files changed, 53 insertions(+) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c3f82ae3..34a16da0 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7495,6 +7495,57 @@ uint16_t mode_2Dsoap() { } static const char _data_FX_MODE_2DSOAP[] PROGMEM = "Soap@!,Smoothness;;!;2"; + +//Idea from https://www.youtube.com/watch?v=HsA-6KIbgto&ab_channel=GreatScott%21 +//Octopus (https://editor.soulmatelights.com/gallery/671-octopus) +//Stepko and Sutaburosu +// adapted for WLED by @blazoncek +uint16_t mode_2Doctopus() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + const uint8_t C_X = cols / 2; + const uint8_t C_Y = rows / 2; + const uint8_t mapp = 255 / cols; + + typedef struct { + uint8_t angle; + uint8_t radius; + } map_t; + + const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped + if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + + map_t *rMap = reinterpret_cast(SEGENV.data); + + if (SEGENV.call == 0) { + SEGENV.aux0 = 0; // t + for (int x = -C_X; x < C_X + (cols % 2); x++) { + for (int y = -C_Y; y < C_Y + (rows % 2); y++) { + rMap[XY(x + C_X, y + C_Y)].angle = 128 * (atan2f(y, x) / PI); + rMap[XY(x + C_X, y + C_Y)].radius = hypotf(x, y) * mapp; //thanks Sutaburosu + } + } + } + + SEGENV.aux0 += SEGMENT.speed / 32 + 1; // 1-8 range + for (uint8_t x = 0; x < cols; x++) { + for (uint8_t y = 0; y < rows; y++) { + byte angle = rMap[XY(x,y)].angle; + byte radius = rMap[XY(x,y)].radius; + //CRGB c = CHSV(SEGENV.aux0 / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.aux0) + radius - SEGENV.aux0 * 2 + angle * (SEGMENT.custom3/3+1))); + uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.aux0/2) + radius - SEGENV.aux0 + angle * (SEGMENT.custom3/4+1)); + intensity = map(intensity*intensity, 0, 65535, 0, SEGMENT.intensity); // add a bit of non-linearity to brightness + CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.aux0 / 2 - radius, intensity); + SEGMENT.setPixelColorXY(x, y, c); + } + } + return FRAMETIME; +} +static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,!,,,Legs;;!;2;ix=255"; + + #endif // WLED_DISABLE_2D @@ -7729,6 +7780,7 @@ void WS2812FX::setupEffectData() { addEffect(FX_MODE_2DDNASPIRAL, &mode_2DDNASpiral, _data_FX_MODE_2DDNASPIRAL); addEffect(FX_MODE_2DBLACKHOLE, &mode_2DBlackHole, _data_FX_MODE_2DBLACKHOLE); addEffect(FX_MODE_2DSOAP, &mode_2Dsoap, _data_FX_MODE_2DSOAP); + addEffect(FX_MODE_2DOCTOPUS, &mode_2Doctopus, _data_FX_MODE_2DOCTOPUS); addEffect(FX_MODE_2DAKEMI, &mode_2DAkemi, _data_FX_MODE_2DAKEMI); // audio #endif // WLED_DISABLE_2D diff --git a/wled00/FX.h b/wled00/FX.h index b4ba2f21..897f397d 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -256,6 +256,7 @@ #define FX_MODE_2DDRIFTROSE 123 //gap fill #define FX_MODE_2DDISTORTIONWAVES 124 //gap fill #define FX_MODE_2DSOAP 125 //gap fill +#define FX_MODE_2DOCTOPUS 126 //gap fill // WLED-SR effects (SR compatible IDs !!!) #define FX_MODE_PIXELS 128 From f0dade5856002a65db7b08fc8e334d0dcbedc608 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 29 Apr 2023 15:51:25 +0200 Subject: [PATCH 07/22] Uneven matrix fix. --- wled00/FX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 34a16da0..f3bbb957 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7507,7 +7507,7 @@ uint16_t mode_2Doctopus() { const uint16_t rows = SEGMENT.virtualHeight(); const uint8_t C_X = cols / 2; const uint8_t C_Y = rows / 2; - const uint8_t mapp = 255 / cols; + const uint8_t mapp = 255 / MAX(cols,rows); typedef struct { uint8_t angle; From 61eb7b0a6a0314ad4c3ec588e2742aa0a5f4cd2a Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 29 Apr 2023 17:04:16 +0200 Subject: [PATCH 08/22] Waving Cell FX --- wled00/FX.cpp | 19 +++++++++++++++++++ wled00/FX.h | 1 + 2 files changed, 20 insertions(+) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index f3bbb957..0f04810e 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7546,6 +7546,24 @@ uint16_t mode_2Doctopus() { static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,!,,,Legs;;!;2;ix=255"; +//Waving Cell +//@Stepko (https://editor.soulmatelights.com/gallery/1704-wavingcells) +// adapted for WLED by @blazoncek +uint16_t mode_2Dwavingcell() { + if (!strip.isMatrix) return mode_static(); // not a 2D set-up + + const uint16_t cols = SEGMENT.virtualWidth(); + const uint16_t rows = SEGMENT.virtualHeight(); + + uint32_t t = millis()/(256-SEGMENT.speed); + for (int x = 0; x < cols; x++) for (int y = 0; y Date: Sun, 30 Apr 2023 13:25:08 +0200 Subject: [PATCH 09/22] Tweaks. --- wled00/FX.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 0f04810e..b2731166 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7520,7 +7520,7 @@ uint16_t mode_2Doctopus() { map_t *rMap = reinterpret_cast(SEGENV.data); if (SEGENV.call == 0) { - SEGENV.aux0 = 0; // t + SEGENV.step = 0; // t for (int x = -C_X; x < C_X + (cols % 2); x++) { for (int y = -C_Y; y < C_Y + (rows % 2); y++) { rMap[XY(x + C_X, y + C_Y)].angle = 128 * (atan2f(y, x) / PI); @@ -7529,15 +7529,15 @@ uint16_t mode_2Doctopus() { } } - SEGENV.aux0 += SEGMENT.speed / 32 + 1; // 1-8 range - for (uint8_t x = 0; x < cols; x++) { - for (uint8_t y = 0; y < rows; y++) { + SEGENV.step += SEGMENT.speed / 32 + 1; // 1-4 range + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { byte angle = rMap[XY(x,y)].angle; byte radius = rMap[XY(x,y)].radius; - //CRGB c = CHSV(SEGENV.aux0 / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.aux0) + radius - SEGENV.aux0 * 2 + angle * (SEGMENT.custom3/3+1))); - uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.aux0/2) + radius - SEGENV.aux0 + angle * (SEGMENT.custom3/4+1)); - intensity = map(intensity*intensity, 0, 65535, 0, SEGMENT.intensity); // add a bit of non-linearity to brightness - CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.aux0 / 2 - radius, intensity); + //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); + uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); + intensity = map(intensity*intensity, 0, 65535, 0, SEGMENT.intensity); // add a bit of non-linearity for cleaner display + CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); SEGMENT.setPixelColorXY(x, y, c); } } @@ -7555,13 +7555,16 @@ uint16_t mode_2Dwavingcell() { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - uint32_t t = millis()/(256-SEGMENT.speed); + uint32_t t = millis()/(257-SEGMENT.speed); + uint8_t aX = SEGMENT.custom1/16 + 9; + uint8_t aY = SEGMENT.custom2/16 + 1; + uint8_t aZ = SEGMENT.custom3 + 1; for (int x = 0; x < cols; x++) for (int y = 0; y Date: Mon, 1 May 2023 20:43:03 +0200 Subject: [PATCH 10/22] Fix for mirroring --- wled00/FX.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index bc34f95e..8e484f52 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7406,12 +7406,11 @@ uint16_t mode_2Dsoap() { *noise32_x = random16(); *noise32_y = random16(); *noise32_z = random16(); - for (int i = 0; i < cols; i++) { - int32_t ioffset = scale32_x * (i - cols / 2); - for (int j = 0; j < rows; j++) { - int32_t joffset = scale32_y * (j - rows / 2); - uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; - noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], SEGMENT.intensity) + scale8(data, 255 - SEGMENT.intensity); + for (int i = 0; i < SEGMENT.width(); i++) { + int32_t ioffset = scale32_x * (i - SEGMENT.width() / 2); + for (int j = 0; j < SEGMENT.height(); j++) { + int32_t joffset = scale32_y * (j - SEGMENT.height() / 2); + noise3d[XY(i,j)] = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); } } @@ -7499,8 +7498,6 @@ uint16_t mode_2Doctopus() { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint8_t C_X = cols / 2; - const uint8_t C_Y = rows / 2; const uint8_t mapp = 255 / MAX(cols,rows); typedef struct { @@ -7513,8 +7510,13 @@ uint16_t mode_2Doctopus() { map_t *rMap = reinterpret_cast(SEGENV.data); - if (SEGENV.call == 0) { + // re-init if SEGMENT dimensions changed + if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) { SEGENV.step = 0; // t + SEGMENT.aux0 = cols; + SEGMENT.aux1 = rows; + const uint8_t C_X = cols / 2; + const uint8_t C_Y = rows / 2; for (int x = -C_X; x < C_X + (cols % 2); x++) { for (int y = -C_Y; y < C_Y + (rows % 2); y++) { rMap[XY(x + C_X, y + C_Y)].angle = 128 * (atan2f(y, x) / PI); From 3da086438b744836f59377fb9180a6dad54e1e43 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Tue, 2 May 2023 11:16:24 +0200 Subject: [PATCH 11/22] Add rotating to Octopus Soap optimization --- wled00/FX.cpp | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 8e484f52..226f6357 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7380,6 +7380,7 @@ uint16_t mode_2Ddistortionwaves() { } static const char _data_FX_MODE_2DDISTORTIONWAVES[] PROGMEM = "Distortion Waves@!,Scale;;;2"; + //Soap //@Stepko //Idea from https://www.youtube.com/watch?v=DiHBgITrZck&ab_channel=StefanPetrick @@ -7397,8 +7398,10 @@ uint16_t mode_2Dsoap() { uint32_t *noise32_x = reinterpret_cast(SEGENV.data + dataSize); uint32_t *noise32_y = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)); uint32_t *noise32_z = reinterpret_cast(SEGENV.data + dataSize + sizeof(uint32_t)*2); - uint32_t scale32_x = 160000U/cols; - uint32_t scale32_y = 160000U/rows; + const uint32_t scale32_x = 160000U/cols; + const uint32_t scale32_y = 160000U/rows; + const uint32_t mov = MIN(cols,rows)*(SEGMENT.speed+2)/2; + const uint8_t smoothness = MIN(250,SEGMENT.intensity); // limit as >250 produces very little changes // init if (SEGENV.call == 0) { @@ -7406,27 +7409,28 @@ uint16_t mode_2Dsoap() { *noise32_x = random16(); *noise32_y = random16(); *noise32_z = random16(); - for (int i = 0; i < SEGMENT.width(); i++) { - int32_t ioffset = scale32_x * (i - SEGMENT.width() / 2); - for (int j = 0; j < SEGMENT.height(); j++) { - int32_t joffset = scale32_y * (j - SEGMENT.height() / 2); - noise3d[XY(i,j)] = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; - SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); - } - } + } else { + *noise32_x += mov; + *noise32_y += mov; + *noise32_z += mov; } - uint32_t mov = MAX(cols,rows)*(SEGMENT.speed+1)/2; - *noise32_x += mov; - *noise32_y += mov; - *noise32_z += mov; - for (int i = 0; i < cols; i++) { int32_t ioffset = scale32_x * (i - cols / 2); for (int j = 0; j < rows; j++) { int32_t joffset = scale32_y * (j - rows / 2); uint8_t data = inoise16(*noise32_x + ioffset, *noise32_y + joffset, *noise32_z) >> 8; - noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], SEGMENT.intensity) + scale8(data, 256 - SEGMENT.intensity); + noise3d[XY(i,j)] = scale8(noise3d[XY(i,j)], smoothness) + scale8(data, 255 - smoothness); + } + } + // init also if dimensions changed + if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) { + SEGMENT.aux0 = cols; + SEGMENT.aux1 = rows; + for (int i = 0; i < cols; i++) { + for (int j = 0; j < rows; j++) { + SEGMENT.setPixelColorXY(i, j, ColorFromPalette(SEGPALETTE,~noise3d[XY(i,j)]*3)); + } } } @@ -7534,12 +7538,15 @@ uint16_t mode_2Doctopus() { uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); intensity = map(intensity*intensity, 0, 65535, 0, SEGMENT.intensity); // add a bit of non-linearity for cleaner display CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); - SEGMENT.setPixelColorXY(x, y, c); + int rX = SEGMENT.custom1 ? (strip.now / (320 - SEGMENT.custom1) + x) % cols : x; + int rY = SEGMENT.custom2 ? (strip.now / (320 - SEGMENT.custom2) + y) % rows : y; + SEGMENT.setPixelColorXY(rX, rY, c); } } + return FRAMETIME; } -static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,!,,,Legs;;!;2;ix=255"; +static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,!,Rotate X,Rotate Y,Legs;;!;2;ix=255,c1=0,c2=0"; //Waving Cell From b47c12cbee296724fb4f54dd1ae9a0c3c7c19d3e Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 8 May 2023 08:48:52 +0200 Subject: [PATCH 12/22] Serg74 ethernet board. --- wled00/const.h | 5 +- wled00/data/settings_wifi.htm | 1 + wled00/html_settings.h | 293 +++++++++++++++++----------------- wled00/network.cpp | 39 +++-- 4 files changed, 176 insertions(+), 162 deletions(-) diff --git a/wled00/const.h b/wled00/const.h index e9f39af3..f87db71d 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -265,7 +265,7 @@ #define BTN_TYPE_ANALOG_INVERTED 8 //Ethernet board types -#define WLED_NUM_ETH_TYPES 10 +#define WLED_NUM_ETH_TYPES 11 #define WLED_ETH_NONE 0 #define WLED_ETH_WT32_ETH01 1 @@ -274,7 +274,10 @@ #define WLED_ETH_QUINLED 4 #define WLED_ETH_TWILIGHTLORD 5 #define WLED_ETH_ESP32DEUX 6 +#define WLED_ETH_ESP32ETHKITVE 7 +#define WLED_ETH_QUINLED_OCTA 8 #define WLED_ETH_ABCWLEDV43ETH 9 +#define WLED_ETH_SERG74 10 //Hue error codes #define HUE_ERROR_INACTIVE 0 diff --git a/wled00/data/settings_wifi.htm b/wled00/data/settings_wifi.htm index 3c9dcc19..72ba6097 100644 --- a/wled00/data/settings_wifi.htm +++ b/wled00/data/settings_wifi.htm @@ -192,6 +192,7 @@ + diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 6a18961c..82637210 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -8,7 +8,7 @@ // Autogenerated from wled00/data/style.css, do not edit!! const uint16_t PAGE_settingsCss_length = 847; const uint8_t PAGE_settingsCss[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xad, 0x55, 0xc1, 0x8e, 0x9b, 0x30, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x55, 0xc1, 0x8e, 0x9b, 0x30, 0x10, 0xfd, 0x15, 0xaa, 0x68, 0xa5, 0xad, 0x14, 0x10, 0x10, 0xc8, 0xa6, 0x46, 0x95, 0xaa, 0xde, 0x7b, 0xab, 0xaa, 0x4a, 0xd5, 0x1e, 0x0c, 0x1e, 0x82, 0x15, 0x63, 0x23, 0xdb, 0x74, 0x49, 0x11, 0xff, 0x5e, 0xdb, 0xc0, 0x42, 0xb2, 0x68, 0x7b, 0xa9, 0xa2, 0x44, 0xc4, 0x63, 0xc6, 0x6f, 0xde, @@ -67,7 +67,7 @@ const uint8_t PAGE_settingsCss[] PROGMEM = { // Autogenerated from wled00/data/settings.htm, do not edit!! const uint16_t PAGE_settings_length = 992; const uint8_t PAGE_settings[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xad, 0x56, 0x6d, 0x6f, 0xdb, 0x36, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x56, 0x6d, 0x6f, 0xdb, 0x36, 0x10, 0xfe, 0xee, 0x5f, 0xc1, 0xb0, 0x58, 0x23, 0xa1, 0xb2, 0xec, 0x38, 0xc3, 0xb0, 0xc9, 0x96, 0x8b, 0x35, 0x2f, 0x9b, 0x87, 0x04, 0x0d, 0x90, 0xa4, 0xdd, 0x80, 0x7d, 0xa1, 0xc9, 0x93, 0xcc, 0x46, 0x22, 0x05, 0xf2, 0xe4, 0xc4, 0x73, 0xf3, 0xdf, 0x77, 0x94, 0x9d, 0xb7, 0x26, 0x18, 0x86, @@ -133,149 +133,150 @@ const uint8_t PAGE_settings[] PROGMEM = { // Autogenerated from wled00/data/settings_wifi.htm, do not edit!! -const uint16_t PAGE_settings_wifi_length = 2138; +const uint16_t PAGE_settings_wifi_length = 2145; const uint8_t PAGE_settings_wifi[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xad, 0x58, 0x7b, 0x6f, 0xdb, 0x38, - 0x12, 0xff, 0xdf, 0x9f, 0x82, 0xe1, 0x1d, 0x02, 0x09, 0x51, 0xe4, 0x38, 0xde, 0xf6, 0x7a, 0x89, - 0xe5, 0x5e, 0x1e, 0xde, 0x26, 0x77, 0x69, 0xea, 0x83, 0x83, 0x06, 0x87, 0x5e, 0xb1, 0xab, 0x48, - 0x63, 0x9b, 0x8d, 0x44, 0xea, 0x44, 0xca, 0x4e, 0x90, 0xfa, 0xbb, 0xdf, 0x0c, 0x29, 0x3f, 0x13, - 0x77, 0x17, 0x45, 0x50, 0xb4, 0x15, 0xc9, 0xe1, 0x3c, 0x7f, 0xf3, 0xa0, 0x3b, 0x3b, 0xe7, 0x9f, - 0xce, 0x6e, 0xfe, 0xd3, 0xef, 0xb1, 0xb1, 0xc9, 0xb3, 0x6e, 0x87, 0xfe, 0x65, 0x59, 0x2c, 0x47, - 0x11, 0x07, 0xc9, 0x71, 0x0d, 0x71, 0xda, 0xed, 0xe4, 0x60, 0x62, 0x96, 0x8c, 0xe3, 0x52, 0x83, - 0x89, 0x78, 0x65, 0x86, 0xfb, 0xef, 0x78, 0xbd, 0xdb, 0x90, 0x71, 0x0e, 0x11, 0x9f, 0x08, 0x98, - 0x16, 0xaa, 0x34, 0x9c, 0x25, 0x4a, 0x1a, 0x90, 0x48, 0x36, 0x15, 0xa9, 0x19, 0x47, 0x6f, 0x0e, - 0x0e, 0x16, 0xa4, 0x1b, 0x47, 0x29, 0x4c, 0x44, 0x02, 0xfb, 0x76, 0x11, 0x08, 0x29, 0x8c, 0x88, - 0xb3, 0x7d, 0x9d, 0xc4, 0x19, 0x44, 0xad, 0x20, 0x8f, 0x1f, 0x44, 0x5e, 0xe5, 0x8b, 0x75, 0xa5, - 0xa1, 0xb4, 0x8b, 0xf8, 0x0e, 0xd7, 0x52, 0xf1, 0x67, 0x92, 0xbb, 0x1d, 0x23, 0x4c, 0x06, 0xdd, - 0x5b, 0xf1, 0xab, 0x60, 0x03, 0x30, 0x46, 0xc8, 0x91, 0xee, 0x34, 0xdd, 0x66, 0x47, 0x27, 0xa5, - 0x28, 0x4c, 0xb7, 0x31, 0x89, 0x4b, 0x96, 0xa9, 0x44, 0x14, 0x41, 0x1a, 0xa5, 0x2a, 0xa9, 0x72, - 0x54, 0x28, 0xc0, 0x8d, 0x68, 0xa7, 0x15, 0x20, 0x7b, 0x79, 0xa5, 0x54, 0xa1, 0xa3, 0x83, 0xa0, - 0x28, 0x61, 0x80, 0xcb, 0xc1, 0xe0, 0xf2, 0x3c, 0xe2, 0xfc, 0x78, 0x58, 0xc9, 0xc4, 0x08, 0x25, - 0xd9, 0xe8, 0x32, 0xf5, 0xc0, 0x7f, 0x2a, 0xc1, 0x54, 0xa5, 0x64, 0x69, 0x38, 0x02, 0xd3, 0xcb, - 0x80, 0xb8, 0x9c, 0x3e, 0xda, 0xa3, 0xd9, 0x82, 0x34, 0xe9, 0xad, 0x51, 0x26, 0x25, 0xc4, 0x06, - 0x6a, 0xe2, 0x35, 0xc2, 0x0b, 0xcf, 0x7f, 0x9a, 0x0a, 0x99, 0xaa, 0x69, 0xa8, 0x0a, 0x90, 0x1e, - 0x1f, 0x1b, 0x53, 0xe8, 0xa3, 0x66, 0xf3, 0x5e, 0xaa, 0x70, 0x9a, 0x01, 0x49, 0x69, 0x0e, 0xf1, - 0x76, 0x55, 0x82, 0x6e, 0xea, 0xda, 0xb4, 0xe6, 0x5f, 0xa6, 0x62, 0x28, 0xf6, 0xe7, 0x4b, 0xbe, - 0xc2, 0xf0, 0x74, 0x93, 0xe1, 0xe2, 0x12, 0x0f, 0xf8, 0x6f, 0x1a, 0xb2, 0xe1, 0x2a, 0xf5, 0x35, - 0x52, 0x63, 0x68, 0xb4, 0x61, 0x10, 0x79, 0xe8, 0x8a, 0xf7, 0x56, 0x3e, 0x8a, 0xe7, 0x7b, 0xd6, - 0x53, 0x47, 0x9c, 0xfb, 0x7b, 0xbc, 0xf9, 0x4d, 0x2b, 0xd9, 0x94, 0x60, 0x78, 0x60, 0x22, 0xf2, - 0x02, 0x27, 0x77, 0x71, 0xff, 0xd8, 0x84, 0xa9, 0xd0, 0x14, 0x95, 0x34, 0xda, 0x39, 0x08, 0x4c, - 0x28, 0xa4, 0x84, 0xf2, 0xe2, 0xe6, 0xe3, 0x55, 0xc4, 0xc9, 0x83, 0x12, 0xc5, 0x86, 0x61, 0xc8, - 0x83, 0x21, 0x98, 0x64, 0x8c, 0x76, 0x87, 0x66, 0x8c, 0x2a, 0x41, 0xd4, 0x85, 0x90, 0x58, 0x7a, - 0xfe, 0x72, 0xe7, 0xd3, 0xdd, 0x37, 0x48, 0x4c, 0x18, 0x6b, 0x2d, 0x46, 0xd2, 0x7b, 0x9a, 0x05, - 0x4f, 0x28, 0x6f, 0xaa, 0xca, 0x7b, 0x7d, 0xf4, 0xe5, 0xeb, 0x2c, 0xc0, 0xbb, 0xf3, 0x75, 0xa8, - 0x31, 0xe4, 0x9e, 0x07, 0x81, 0xf1, 0xa3, 0xae, 0x09, 0x4b, 0xbc, 0xb1, 0x0f, 0xf6, 0x3f, 0x3f, - 0x2c, 0x21, 0xad, 0x12, 0x98, 0x1f, 0x7a, 0x80, 0xb4, 0x39, 0x38, 0x81, 0x78, 0x9e, 0x46, 0x51, - 0x64, 0xec, 0x87, 0xff, 0xfd, 0x3b, 0x84, 0x45, 0xa5, 0xc7, 0x9e, 0xf1, 0x91, 0x77, 0xf0, 0xe5, - 0xeb, 0x8a, 0x2a, 0x4f, 0x62, 0xe8, 0x1d, 0x20, 0x29, 0x84, 0x19, 0xc8, 0x91, 0x19, 0xef, 0xee, - 0x2e, 0xd0, 0xd1, 0x69, 0x1d, 0xf8, 0x75, 0x54, 0x17, 0x7b, 0x7b, 0x7b, 0xc1, 0x44, 0x89, 0x94, - 0xa1, 0x9f, 0x6f, 0x44, 0x0e, 0xaa, 0x32, 0xde, 0x75, 0xd0, 0x82, 0xb6, 0x7f, 0xbc, 0x02, 0xaa, - 0xe3, 0x0c, 0x0c, 0x93, 0xce, 0x79, 0x67, 0x03, 0x74, 0x1d, 0xca, 0x90, 0xfe, 0x13, 0xed, 0x9a, - 0x08, 0xd1, 0xc2, 0x31, 0x32, 0x68, 0xbf, 0xf5, 0x29, 0x32, 0x3a, 0x31, 0xa6, 0x14, 0x77, 0x95, - 0x01, 0x8f, 0x8b, 0x14, 0x03, 0x47, 0x57, 0x82, 0xcd, 0x13, 0x4a, 0x82, 0x6d, 0x67, 0x4a, 0x62, - 0xce, 0xca, 0x11, 0x9d, 0xdf, 0x78, 0x3e, 0x12, 0xac, 0x82, 0x5a, 0x86, 0x93, 0x38, 0xab, 0xe0, - 0x78, 0xa8, 0x4a, 0x8f, 0x34, 0x00, 0xd4, 0x0f, 0x3a, 0x26, 0x4c, 0xc6, 0x22, 0x4b, 0x4b, 0x90, - 0xb5, 0xdd, 0xc7, 0xb0, 0xb7, 0xe7, 0xa3, 0x83, 0x21, 0x57, 0x13, 0x38, 0xa3, 0x33, 0x6f, 0x49, - 0xf3, 0x05, 0xbe, 0xfa, 0x0b, 0x06, 0x0a, 0x19, 0xa8, 0xce, 0xdc, 0x5f, 0xc7, 0x0a, 0xef, 0xd5, - 0xb0, 0x12, 0xd6, 0x38, 0x55, 0x10, 0xda, 0xc8, 0xea, 0x0d, 0x35, 0xad, 0x1e, 0x3c, 0x80, 0x2f, - 0xea, 0xab, 0x8b, 0x4b, 0x20, 0x56, 0x40, 0xf4, 0xfb, 0x5f, 0x9f, 0x16, 0x27, 0x33, 0xe6, 0xd5, - 0x2b, 0x8a, 0xf4, 0x8c, 0xa5, 0xa7, 0xb9, 0xff, 0xfb, 0xf2, 0x22, 0x86, 0xab, 0xb6, 0x6a, 0x77, - 0x77, 0x53, 0x88, 0x73, 0x2d, 0x90, 0x1f, 0x17, 0x9f, 0xe4, 0xb1, 0xb8, 0xc0, 0x04, 0x49, 0x9d, - 0x61, 0xc2, 0x9f, 0x39, 0x85, 0xd5, 0xba, 0xc2, 0xea, 0x65, 0x85, 0xf9, 0xce, 0x19, 0x66, 0x5d, - 0xa0, 0x56, 0x11, 0xff, 0x09, 0xf1, 0x53, 0xb2, 0x1a, 0xa5, 0x16, 0xf6, 0xeb, 0x22, 0x94, 0x1f, - 0x48, 0x74, 0x66, 0x91, 0xc5, 0x09, 0xdc, 0x0a, 0x43, 0xd0, 0x9b, 0xad, 0x26, 0x50, 0xeb, 0x79, - 0x02, 0xf1, 0xd9, 0x4a, 0xae, 0x62, 0x1c, 0x9f, 0x5c, 0xb4, 0xd6, 0x50, 0xb4, 0x03, 0xdf, 0xbf, - 0x5b, 0x75, 0x76, 0x10, 0xb0, 0x56, 0xbd, 0x1a, 0xa1, 0xc7, 0x4b, 0x70, 0x09, 0x59, 0x54, 0x0e, - 0x5b, 0xe6, 0xb1, 0xc0, 0xc2, 0x69, 0xe0, 0x81, 0x72, 0x39, 0x44, 0xbf, 0x11, 0x1f, 0xfc, 0x72, - 0x05, 0xd5, 0x7d, 0xaf, 0x5b, 0x8c, 0xd5, 0xd8, 0x85, 0x95, 0x07, 0xed, 0x43, 0x72, 0x9b, 0x95, - 0x11, 0xad, 0xe0, 0x29, 0x80, 0x4d, 0xb3, 0x16, 0x3a, 0x67, 0x2a, 0x4e, 0xff, 0x39, 0xa0, 0x74, - 0xc4, 0x02, 0xe1, 0xd4, 0x97, 0x0e, 0xee, 0xb6, 0x26, 0xa3, 0x4a, 0x72, 0x33, 0x58, 0x65, 0xc2, - 0x29, 0x23, 0x37, 0xf7, 0x49, 0x71, 0xf4, 0x3b, 0x69, 0xde, 0xfc, 0x16, 0x4f, 0xe2, 0x39, 0x83, - 0x67, 0x84, 0xb1, 0x7e, 0x94, 0xc8, 0x02, 0xf3, 0x3a, 0x0d, 0xef, 0x54, 0xfa, 0xb8, 0x16, 0x03, - 0x49, 0xf4, 0x71, 0x9a, 0xf6, 0x26, 0x58, 0x88, 0xaf, 0x84, 0xc6, 0x9e, 0x04, 0xa5, 0xc7, 0x49, - 0x4d, 0x1e, 0x78, 0x58, 0x32, 0x9e, 0x3e, 0x80, 0xf9, 0xec, 0xf9, 0xb3, 0x97, 0xe9, 0xa0, 0x2c, - 0x55, 0x89, 0xea, 0x21, 0x1d, 0xa1, 0x45, 0x65, 0x08, 0x79, 0x35, 0xf2, 0x78, 0x8f, 0xf6, 0x59, - 0x6d, 0x2f, 0x16, 0x3d, 0x36, 0x14, 0x19, 0x58, 0x33, 0xb0, 0x83, 0x61, 0xb1, 0xe2, 0x57, 0xf5, - 0xbe, 0x1a, 0x52, 0x93, 0x1c, 0x8a, 0x51, 0x55, 0xc6, 0xd6, 0x41, 0xce, 0x0c, 0x36, 0x8c, 0x05, - 0xd5, 0xfa, 0xff, 0xca, 0x4b, 0x99, 0xa8, 0xbc, 0x40, 0x3f, 0x01, 0x2b, 0xe2, 0x11, 0xb0, 0x34, - 0x36, 0xf1, 0x0e, 0x56, 0xec, 0x15, 0x9f, 0x0e, 0x10, 0x07, 0x9c, 0x04, 0x1c, 0xf1, 0x28, 0xaa, - 0x4b, 0x3d, 0x16, 0x6b, 0xcb, 0x2f, 0x2c, 0x4a, 0x65, 0x54, 0xa2, 0xb2, 0xdd, 0x5d, 0xcf, 0x76, - 0xb6, 0x83, 0xc0, 0xb3, 0x85, 0x3c, 0x22, 0x8a, 0x6c, 0x60, 0x54, 0x89, 0x5c, 0xa9, 0x73, 0x5d, - 0x1a, 0xc8, 0xc9, 0xf0, 0xe4, 0xb2, 0xe0, 0x3e, 0x96, 0xc3, 0x9a, 0x0c, 0xef, 0xe7, 0x05, 0x2a, - 0xfc, 0x2b, 0xf2, 0x67, 0x1f, 0x55, 0x0a, 0x21, 0xeb, 0x67, 0x10, 0x6b, 0x60, 0xe8, 0x08, 0x84, - 0xf7, 0xed, 0x55, 0xef, 0x9c, 0x5d, 0xf6, 0x51, 0xa5, 0x60, 0x8d, 0xa3, 0x5e, 0xe7, 0x18, 0x58, - 0x6e, 0xbe, 0x4f, 0x54, 0x16, 0x01, 0x3f, 0x68, 0x2e, 0x8b, 0xa6, 0xa6, 0xb1, 0x29, 0xbc, 0x2f, - 0xa2, 0x16, 0x0f, 0x76, 0x5a, 0xfe, 0xac, 0xd1, 0x69, 0xd6, 0x8d, 0xbb, 0xa3, 0xcd, 0x23, 0xf6, - 0xf1, 0x7f, 0x88, 0x9c, 0x9a, 0x3d, 0xab, 0xca, 0x0c, 0x61, 0x42, 0x5b, 0x61, 0xa2, 0x31, 0x0b, - 0x8f, 0x91, 0xd0, 0x12, 0x74, 0x9a, 0x6e, 0x64, 0xa1, 0xa8, 0x63, 0x30, 0x48, 0x32, 0xa6, 0x11, - 0x56, 0xbf, 0x6e, 0x07, 0x0b, 0x55, 0xde, 0x60, 0x04, 0x77, 0xfa, 0xfa, 0x4d, 0x73, 0xe6, 0x00, - 0x3f, 0x18, 0x72, 0x86, 0x03, 0xca, 0x58, 0xe1, 0x49, 0xa1, 0x34, 0x4d, 0x12, 0xa9, 0x98, 0xb0, - 0x24, 0xc3, 0x3e, 0x84, 0x69, 0xa2, 0xd0, 0x1d, 0xd3, 0xf5, 0xbd, 0x31, 0x64, 0xc5, 0x29, 0xef, - 0x36, 0x3a, 0x88, 0x36, 0x83, 0xd1, 0x70, 0x09, 0xe5, 0x16, 0x1c, 0xa5, 0x26, 0x99, 0x48, 0xee, - 0x23, 0x7e, 0x41, 0x62, 0xdf, 0x77, 0x9a, 0xee, 0x00, 0x55, 0x43, 0x16, 0xdd, 0x97, 0xef, 0x34, - 0x16, 0x97, 0x4e, 0xe9, 0xd2, 0x69, 0x9c, 0xdc, 0x2f, 0xef, 0xad, 0xdd, 0xd0, 0xd5, 0x5d, 0x2e, - 0x50, 0xc7, 0x41, 0x3c, 0x01, 0xb6, 0xcb, 0xce, 0x14, 0x56, 0x8b, 0xc4, 0x2c, 0x89, 0xc7, 0x25, - 0xea, 0xe5, 0x24, 0x8d, 0x0f, 0xdd, 0x30, 0x84, 0xce, 0xad, 0x0a, 0x74, 0xcc, 0x21, 0x6e, 0xb5, - 0xbb, 0xf5, 0x0d, 0x66, 0x14, 0x83, 0x07, 0x44, 0x35, 0x41, 0xb2, 0x2e, 0x58, 0x48, 0xd3, 0x5e, - 0x88, 0x6b, 0xac, 0x6b, 0x48, 0x7e, 0xb3, 0x1d, 0x7f, 0x69, 0xdf, 0x35, 0xa9, 0x4a, 0x05, 0x60, - 0x45, 0x55, 0x94, 0x7e, 0xed, 0x98, 0x59, 0xef, 0x32, 0xcf, 0x16, 0x07, 0x06, 0x08, 0xa8, 0x47, - 0x12, 0x29, 0x95, 0x21, 0xf4, 0x93, 0x06, 0xfe, 0x11, 0x91, 0x77, 0x6c, 0x5d, 0x62, 0x2b, 0x35, - 0x89, 0xd5, 0x15, 0x69, 0x3e, 0xe1, 0xd1, 0xe7, 0xa2, 0x00, 0x45, 0xbc, 0x7d, 0xc8, 0xad, 0x9c, - 0xb9, 0x98, 0x02, 0x43, 0x82, 0x1f, 0xe9, 0x73, 0x6e, 0xf3, 0x93, 0x25, 0xa7, 0xfe, 0x1a, 0xa7, - 0xb7, 0x6d, 0xc7, 0x69, 0x60, 0x30, 0x6f, 0x12, 0x44, 0x34, 0xc3, 0x46, 0x46, 0x6e, 0x8d, 0x0d, - 0x3b, 0x08, 0xed, 0x1f, 0x86, 0x50, 0x61, 0xe7, 0x17, 0x67, 0xfd, 0x35, 0x65, 0x6b, 0x76, 0x97, - 0x07, 0xbc, 0x96, 0x24, 0xab, 0xfc, 0x0e, 0x4a, 0x3e, 0xc7, 0x07, 0x42, 0x2b, 0x17, 0x32, 0xe2, - 0x07, 0x56, 0x5c, 0xc4, 0x0f, 0xdf, 0xbc, 0xe1, 0xac, 0x84, 0xff, 0x55, 0x02, 0x67, 0x93, 0x2e, - 0x0b, 0xd9, 0x06, 0x9f, 0xd6, 0x2b, 0xf1, 0x39, 0x7c, 0x25, 0x3e, 0xed, 0x9f, 0xe2, 0xb3, 0xe2, - 0xca, 0x11, 0x4e, 0xba, 0xd3, 0xf8, 0xf1, 0xa8, 0xb1, 0xe2, 0x34, 0xc7, 0xfb, 0xc3, 0x4f, 0xfb, - 0xac, 0xb1, 0xce, 0xe7, 0x95, 0x7c, 0xf6, 0xe1, 0x95, 0x7c, 0xf6, 0xe1, 0xe7, 0x7d, 0xd6, 0xa8, - 0x9d, 0x86, 0x99, 0x8d, 0x79, 0x88, 0x54, 0xfa, 0xfe, 0xe8, 0x99, 0xdf, 0x06, 0x7f, 0xc2, 0x6f, - 0x8d, 0x1f, 0x2a, 0x5a, 0xf3, 0x69, 0xbd, 0x12, 0x9f, 0xc3, 0x57, 0xe2, 0xd3, 0xfe, 0x39, 0x3e, - 0xe4, 0xa0, 0xfc, 0xfc, 0x7a, 0xc0, 0xb0, 0x41, 0xe3, 0xab, 0x48, 0xcf, 0x53, 0xd7, 0x15, 0x1a, - 0xca, 0x5a, 0xa9, 0x18, 0x11, 0xb8, 0xc4, 0xad, 0x9b, 0x4e, 0x2d, 0xbb, 0xb1, 0x56, 0x6e, 0xea, - 0xda, 0xf0, 0xf1, 0x59, 0x95, 0x61, 0xb6, 0xa5, 0x66, 0x74, 0xff, 0x2c, 0x13, 0xd8, 0xfb, 0xb0, - 0x44, 0x1c, 0xb1, 0x8e, 0x2e, 0x62, 0xb9, 0xd0, 0x52, 0x14, 0xd8, 0x07, 0xae, 0x97, 0x45, 0x0d, - 0x52, 0xec, 0x44, 0x48, 0x60, 0x15, 0xac, 0xcb, 0xad, 0x6d, 0xf5, 0xc0, 0x4e, 0x92, 0x84, 0x14, - 0xed, 0x2b, 0x21, 0x8d, 0xad, 0xb4, 0x8d, 0x93, 0x3e, 0xa3, 0xf2, 0xf8, 0xa2, 0xee, 0x27, 0xfd, - 0xad, 0xf5, 0xd1, 0x29, 0x7c, 0xf2, 0xbc, 0x2c, 0xda, 0x74, 0xbb, 0x10, 0x29, 0xca, 0xea, 0x5b, - 0xaa, 0x23, 0xb6, 0x76, 0x3d, 0x19, 0x43, 0x72, 0x7f, 0xa7, 0x1e, 0x16, 0x2c, 0x2e, 0x5c, 0x01, - 0x24, 0x45, 0xe6, 0xc5, 0xf2, 0xb9, 0x32, 0xf4, 0x84, 0xf4, 0x7f, 0x54, 0x5d, 0x6b, 0x66, 0x7d, - 0x17, 0xa9, 0x95, 0xea, 0x8a, 0x4c, 0x0d, 0x0e, 0x0c, 0x18, 0x45, 0x2f, 0x7c, 0x7a, 0x17, 0xbc, - 0x6d, 0xcf, 0xfc, 0xef, 0xd8, 0x33, 0x98, 0x7d, 0x93, 0x47, 0xbc, 0x67, 0x45, 0xa0, 0x04, 0x0c, - 0x74, 0xc8, 0xde, 0xd9, 0x5f, 0x18, 0x62, 0x74, 0x61, 0xa9, 0xe7, 0x6a, 0xad, 0x78, 0x8c, 0xd9, - 0x56, 0x46, 0x0f, 0x1a, 0x09, 0xd9, 0xd1, 0x3a, 0x88, 0x4e, 0xce, 0xb6, 0x80, 0xe8, 0x61, 0x8e, - 0xa2, 0xd6, 0x1c, 0x45, 0xad, 0xf6, 0x06, 0x88, 0xd0, 0x74, 0x32, 0x50, 0x53, 0x60, 0xed, 0xc3, - 0x60, 0xce, 0x13, 0x1b, 0x7c, 0xc7, 0x3d, 0x01, 0x98, 0x9b, 0x76, 0x11, 0x8a, 0x14, 0xea, 0x79, - 0xa4, 0xe9, 0x20, 0x1e, 0xd2, 0x3c, 0x74, 0xa7, 0x14, 0x06, 0xd4, 0xd1, 0x6e, 0xde, 0x69, 0xf1, - 0xee, 0xb9, 0xd0, 0x2b, 0xe0, 0xd8, 0x20, 0x6b, 0xd4, 0x74, 0x18, 0xbd, 0x93, 0x0c, 0x4b, 0xa6, - 0xde, 0xc6, 0x08, 0x7b, 0xd5, 0x35, 0x4c, 0x50, 0x9a, 0x47, 0x2d, 0xb4, 0x04, 0x1c, 0x11, 0x73, - 0x1c, 0x69, 0x21, 0xf5, 0x17, 0x37, 0x68, 0x5a, 0xb2, 0x26, 0xcc, 0x0d, 0x7b, 0x19, 0xae, 0x84, - 0x56, 0x74, 0xb3, 0x98, 0xc0, 0x06, 0x54, 0x7b, 0x0f, 0x05, 0x94, 0x82, 0x7e, 0x9e, 0x40, 0xd0, - 0x5b, 0x80, 0x9e, 0xbb, 0x87, 0x89, 0xf3, 0xbd, 0xce, 0x00, 0x8a, 0x3f, 0x40, 0xd4, 0xed, 0xc0, - 0x85, 0xae, 0x23, 0xba, 0x8d, 0x33, 0x94, 0x4b, 0x93, 0x12, 0x9b, 0xe2, 0x8b, 0x60, 0xe1, 0xb5, - 0x89, 0xc0, 0x98, 0x0b, 0xad, 0x2b, 0xd0, 0xa1, 0x0d, 0xf2, 0xb9, 0x1b, 0x0a, 0x40, 0x5a, 0x49, - 0x62, 0xe8, 0x84, 0x09, 0xcd, 0xa8, 0xbb, 0xd3, 0x70, 0x92, 0xa8, 0x12, 0xcd, 0x35, 0xd9, 0x63, - 0xc0, 0x84, 0xa4, 0x5f, 0x51, 0x34, 0x68, 0x56, 0xa8, 0x29, 0xfa, 0x82, 0x06, 0xf0, 0x2a, 0xb7, - 0xd6, 0x87, 0x9d, 0xa6, 0xb0, 0x43, 0x9a, 0x1b, 0xf1, 0x70, 0x9e, 0x4b, 0xb9, 0xb3, 0x8a, 0x9e, - 0x64, 0x54, 0x58, 0x6f, 0x50, 0x63, 0x37, 0xe1, 0xac, 0x05, 0xba, 0x77, 0x73, 0xf1, 0x52, 0xa4, - 0xaf, 0x95, 0x84, 0xc6, 0xb6, 0x60, 0xfc, 0x1d, 0xa3, 0x75, 0x7a, 0xb6, 0xe3, 0x26, 0xe1, 0xcf, - 0xbf, 0xb4, 0x69, 0x0c, 0x33, 0xa5, 0xca, 0x70, 0xd0, 0xc7, 0x99, 0x8c, 0xa6, 0x77, 0xac, 0xe8, - 0x68, 0xce, 0xe6, 0xfd, 0x06, 0x5b, 0x86, 0xbb, 0x37, 0xe8, 0xb7, 0x0f, 0xf7, 0xfb, 0x9f, 0x7a, - 0xdb, 0x84, 0xbc, 0xad, 0x69, 0xce, 0xa1, 0x7a, 0xd8, 0x8a, 0x9b, 0xbf, 0xf1, 0xee, 0xbf, 0x2e, - 0x6f, 0xf6, 0x3f, 0x6f, 0xe5, 0xf2, 0x8e, 0x77, 0xff, 0x5d, 0x09, 0x89, 0x8a, 0xee, 0x9f, 0x8b, - 0xd1, 0xfe, 0xa7, 0xc4, 0xc4, 0x5b, 0x99, 0xfd, 0xb2, 0xa4, 0xb5, 0x92, 0xb7, 0xf1, 0x7c, 0xc3, - 0xbb, 0x37, 0x53, 0x91, 0x89, 0xd1, 0xd8, 0x5c, 0x61, 0x05, 0xd8, 0x20, 0x6e, 0x3c, 0x47, 0xee, - 0xed, 0x0f, 0xd9, 0x61, 0x8e, 0xdc, 0xde, 0xa0, 0x2f, 0x30, 0x12, 0x07, 0xad, 0x25, 0xd1, 0x1c, - 0xcc, 0x6e, 0x88, 0xa0, 0xbf, 0xf5, 0x58, 0x5b, 0x76, 0xff, 0x60, 0xf0, 0xde, 0x3e, 0x43, 0x37, - 0xfe, 0xdc, 0x10, 0xdd, 0xa4, 0x17, 0x02, 0xfe, 0x47, 0xaf, 0x08, 0x7a, 0x52, 0xd0, 0x6f, 0xa3, - 0xff, 0x07, 0x10, 0x30, 0x1d, 0xec, 0x2b, 0x15, 0x00, 0x00 + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xb5, 0x58, 0x6b, 0x73, 0xdb, 0xba, + 0x11, 0xfd, 0xae, 0x5f, 0x01, 0xa3, 0x1d, 0x0f, 0x39, 0xa6, 0x29, 0xc9, 0x4a, 0x72, 0x53, 0x5b, + 0xd4, 0xad, 0x1f, 0xba, 0xb1, 0x5b, 0xc7, 0x51, 0x47, 0x9e, 0x78, 0x3a, 0x69, 0xe6, 0x5e, 0x9a, + 0x5c, 0x49, 0x88, 0x49, 0x80, 0x25, 0x40, 0xc9, 0x1e, 0x47, 0xff, 0xbd, 0xbb, 0x00, 0xf5, 0xb4, + 0x9d, 0x76, 0x32, 0xee, 0x64, 0x92, 0x90, 0xc4, 0x62, 0x77, 0x71, 0xf6, 0xec, 0x03, 0xea, 0xee, + 0x9c, 0x7d, 0x3a, 0xbd, 0xfe, 0xe7, 0xa0, 0xcf, 0x26, 0x26, 0xcf, 0x7a, 0x5d, 0xfa, 0x97, 0x65, + 0xb1, 0x1c, 0x47, 0x1c, 0x24, 0xc7, 0x77, 0x88, 0xd3, 0x5e, 0x37, 0x07, 0x13, 0xb3, 0x64, 0x12, + 0x97, 0x1a, 0x4c, 0xc4, 0x2b, 0x33, 0xda, 0x7f, 0xcf, 0xeb, 0xaf, 0x0d, 0x19, 0xe7, 0x10, 0xf1, + 0xa9, 0x80, 0x59, 0xa1, 0x4a, 0xc3, 0x59, 0xa2, 0xa4, 0x01, 0x89, 0x62, 0x33, 0x91, 0x9a, 0x49, + 0xf4, 0xb6, 0xd5, 0x5a, 0x8a, 0x6e, 0x2d, 0xa5, 0x30, 0x15, 0x09, 0xec, 0xdb, 0x97, 0x40, 0x48, + 0x61, 0x44, 0x9c, 0xed, 0xeb, 0x24, 0xce, 0x20, 0x6a, 0x07, 0x79, 0x7c, 0x2f, 0xf2, 0x2a, 0x5f, + 0xbe, 0x57, 0x1a, 0x4a, 0xfb, 0x12, 0xdf, 0xe2, 0xbb, 0x54, 0xfc, 0x89, 0xe5, 0x5e, 0xd7, 0x08, + 0x93, 0x41, 0xef, 0x46, 0xfc, 0x26, 0xd8, 0x10, 0x8c, 0x11, 0x72, 0xac, 0xbb, 0x4d, 0xf7, 0xb1, + 0xab, 0x93, 0x52, 0x14, 0xa6, 0xd7, 0x98, 0xc6, 0x25, 0xcb, 0x54, 0x22, 0x8a, 0x20, 0x8d, 0x52, + 0x95, 0x54, 0x39, 0x3a, 0x14, 0xe0, 0x87, 0x68, 0xa7, 0x1d, 0xa0, 0x7a, 0x79, 0xa9, 0x54, 0xa1, + 0xa3, 0x56, 0x50, 0x94, 0x30, 0xc4, 0xd7, 0xe1, 0xf0, 0xe2, 0x2c, 0xe2, 0xfc, 0x68, 0x54, 0xc9, + 0xc4, 0x08, 0x25, 0xd9, 0xf8, 0x22, 0xf5, 0xc0, 0x7f, 0x2c, 0xc1, 0x54, 0xa5, 0x64, 0x69, 0x38, + 0x06, 0xd3, 0xcf, 0x80, 0xb4, 0x9c, 0x3c, 0xd8, 0xa5, 0xf9, 0x52, 0x34, 0xe9, 0x6f, 0x48, 0x26, + 0x25, 0xc4, 0x06, 0x6a, 0xe1, 0x0d, 0xc1, 0x73, 0xcf, 0x7f, 0x9c, 0x09, 0x99, 0xaa, 0x59, 0xa8, + 0x0a, 0x90, 0x1e, 0x9f, 0x18, 0x53, 0xe8, 0xc3, 0x66, 0xf3, 0x4e, 0xaa, 0x70, 0x96, 0x01, 0x59, + 0x69, 0x8e, 0x70, 0x77, 0x55, 0x82, 0x6e, 0xea, 0xfa, 0x68, 0xcd, 0x3f, 0xcd, 0xc4, 0x48, 0xec, + 0x2f, 0x5e, 0xf9, 0x9a, 0xc2, 0x93, 0x6d, 0x85, 0xcb, 0x4d, 0x3c, 0xe0, 0xbf, 0x6b, 0xc8, 0x46, + 0xeb, 0xd2, 0x57, 0x28, 0x8d, 0xa1, 0xd1, 0x86, 0x41, 0xe4, 0x21, 0x14, 0xbf, 0x5a, 0xfb, 0x68, + 0x9e, 0xef, 0x59, 0xa4, 0x0e, 0x39, 0xf7, 0xf7, 0x78, 0xf3, 0x9b, 0x56, 0xb2, 0x29, 0xc1, 0xf0, + 0xc0, 0x44, 0x84, 0x02, 0x27, 0xb8, 0xb8, 0x7f, 0x64, 0xc2, 0x54, 0x68, 0x8a, 0x4a, 0x1a, 0xed, + 0xb4, 0x02, 0x13, 0x0a, 0x29, 0xa1, 0x3c, 0xbf, 0xfe, 0x78, 0x19, 0x71, 0x42, 0x50, 0xa2, 0xd9, + 0x30, 0x0c, 0x79, 0x30, 0x02, 0x93, 0x4c, 0xf0, 0xdc, 0xa1, 0x99, 0xa0, 0x4b, 0x10, 0xf5, 0x20, + 0x24, 0x95, 0x9e, 0xbf, 0xfa, 0xf2, 0xe9, 0xf6, 0x1b, 0x24, 0x26, 0x8c, 0xb5, 0x16, 0x63, 0xe9, + 0x3d, 0xce, 0x83, 0x47, 0xb4, 0x37, 0x53, 0xe5, 0x9d, 0x3e, 0xfc, 0xf2, 0x75, 0x1e, 0xe0, 0xde, + 0xc5, 0x7b, 0xa8, 0x31, 0xe4, 0x9e, 0x07, 0x81, 0xf1, 0xa3, 0x9e, 0x09, 0x4b, 0xdc, 0xb1, 0x0f, + 0xf6, 0x3f, 0x3f, 0x2c, 0x21, 0xad, 0x12, 0x58, 0x2c, 0x7a, 0x80, 0xb2, 0x39, 0x38, 0x83, 0xb8, + 0x9e, 0x46, 0x51, 0x64, 0xec, 0x83, 0xff, 0xfd, 0x3b, 0x84, 0x45, 0xa5, 0x27, 0x9e, 0xf1, 0x51, + 0x77, 0xf0, 0xe5, 0xeb, 0x9a, 0x2b, 0x8f, 0x62, 0xe4, 0xb5, 0x50, 0x14, 0xc2, 0x0c, 0xe4, 0xd8, + 0x4c, 0x76, 0x77, 0x97, 0xec, 0xe8, 0xb6, 0x5b, 0x7e, 0x1d, 0xd5, 0xe5, 0xb7, 0xbd, 0xbd, 0x60, + 0xaa, 0x44, 0xca, 0x10, 0xe7, 0x6b, 0x91, 0x83, 0xaa, 0x8c, 0x77, 0x15, 0xb4, 0xa1, 0xe3, 0x1f, + 0xad, 0x91, 0xea, 0x28, 0x03, 0xc3, 0xa4, 0x03, 0xef, 0x74, 0x88, 0xd0, 0xa1, 0x0d, 0xe9, 0x3f, + 0xd2, 0x57, 0x13, 0x21, 0x5b, 0x38, 0x46, 0x06, 0xcf, 0x6f, 0x31, 0x45, 0x45, 0xc7, 0xc6, 0x94, + 0xe2, 0xb6, 0x32, 0xe0, 0x71, 0x91, 0x62, 0xe0, 0x68, 0x4b, 0xb0, 0xbd, 0x42, 0x49, 0xf0, 0xd2, + 0x9a, 0x92, 0x98, 0xb3, 0x72, 0x4c, 0xeb, 0xd7, 0x9e, 0x8f, 0x02, 0xeb, 0xa4, 0x96, 0xe1, 0x34, + 0xce, 0x2a, 0x38, 0x1a, 0xa9, 0xd2, 0x23, 0x0f, 0x00, 0xfd, 0x83, 0xae, 0x09, 0x93, 0x89, 0xc8, + 0xd2, 0x12, 0x64, 0x7d, 0xee, 0x23, 0xd8, 0xdb, 0xf3, 0x11, 0x60, 0xc8, 0xd5, 0x14, 0x4e, 0x69, + 0xcd, 0x5b, 0xc9, 0x7c, 0x81, 0xaf, 0xfe, 0x52, 0x81, 0x42, 0x05, 0xaa, 0xbb, 0xc0, 0xeb, 0x48, + 0xe1, 0xbe, 0x9a, 0x56, 0xc2, 0x1e, 0x4e, 0x15, 0xc4, 0x36, 0x3a, 0xf5, 0x96, 0x9b, 0xd6, 0x0f, + 0x1e, 0xc0, 0x17, 0xf5, 0xd5, 0xc5, 0x25, 0x10, 0x6b, 0x24, 0xfa, 0xe3, 0xcf, 0x8f, 0xcb, 0x95, + 0x39, 0xf3, 0xea, 0x37, 0x8a, 0xf4, 0x9c, 0xa5, 0x27, 0xb9, 0xff, 0xc7, 0x6a, 0x23, 0x86, 0xab, + 0x3e, 0xd5, 0xee, 0xee, 0xb6, 0x11, 0x07, 0x2d, 0x10, 0x8e, 0xcb, 0x47, 0x42, 0x2c, 0x2e, 0x30, + 0x41, 0x52, 0x77, 0x30, 0xe1, 0xcf, 0x9d, 0xc3, 0x6a, 0xd3, 0x61, 0xf5, 0xbc, 0xc3, 0x7c, 0xe7, + 0x14, 0xb3, 0x2e, 0x50, 0xeb, 0x8c, 0xff, 0x84, 0xfc, 0x29, 0x59, 0xcd, 0x52, 0x4b, 0xfb, 0x4d, + 0x13, 0xca, 0x0f, 0x24, 0x82, 0x59, 0x64, 0x71, 0x02, 0x37, 0xc2, 0x10, 0xf5, 0xe6, 0xeb, 0x09, + 0xd4, 0x7e, 0x9a, 0x40, 0x7c, 0xbe, 0x96, 0xab, 0x18, 0xc7, 0x47, 0x17, 0xad, 0x0d, 0x16, 0xed, + 0xc0, 0xf7, 0xef, 0xd6, 0x9d, 0x1d, 0x24, 0xac, 0x75, 0xaf, 0x66, 0xe8, 0xd1, 0x8a, 0x5c, 0x42, + 0x16, 0x95, 0xe3, 0x96, 0x79, 0x28, 0xb0, 0x70, 0x1a, 0xb8, 0xa7, 0x5c, 0x0e, 0x11, 0x37, 0xd2, + 0x83, 0x4f, 0xae, 0xa0, 0xba, 0xe7, 0xcd, 0x13, 0x63, 0x35, 0x76, 0x61, 0xe5, 0x41, 0xe7, 0x80, + 0x60, 0xb3, 0x36, 0xa2, 0x35, 0x3e, 0x05, 0xb0, 0x7d, 0xac, 0xa5, 0xcf, 0x99, 0x8a, 0xd3, 0xbf, + 0x0d, 0x29, 0x1d, 0xb1, 0x40, 0x38, 0xf7, 0xa5, 0xa3, 0xbb, 0xad, 0xc9, 0xe8, 0x92, 0xdc, 0x0e, + 0x56, 0x99, 0x70, 0xca, 0xc8, 0xed, 0xef, 0xe4, 0x38, 0xe2, 0x4e, 0x9e, 0x37, 0xbf, 0xc5, 0xd3, + 0x78, 0xa1, 0xe0, 0x89, 0x60, 0xac, 0x1f, 0x24, 0xaa, 0xc0, 0xbc, 0x4e, 0xc3, 0x5b, 0x95, 0x3e, + 0x6c, 0xc4, 0x40, 0x92, 0x7c, 0x9c, 0xa6, 0xfd, 0x29, 0x16, 0xe2, 0x4b, 0xa1, 0xb1, 0x27, 0x41, + 0xe9, 0x71, 0x72, 0x93, 0x07, 0x1e, 0x96, 0x8c, 0xc7, 0x0f, 0x60, 0x3e, 0x7b, 0xfe, 0xfc, 0x79, + 0x39, 0x28, 0x4b, 0x55, 0xa2, 0x7b, 0x28, 0x47, 0x6c, 0x51, 0x19, 0x52, 0x5e, 0x8d, 0x3d, 0xde, + 0xa7, 0xef, 0xac, 0x3e, 0x2f, 0x16, 0x3d, 0x36, 0x12, 0x19, 0xd8, 0x63, 0x60, 0x07, 0xc3, 0x62, + 0xc5, 0x2f, 0xeb, 0xef, 0x6a, 0x44, 0x4d, 0x72, 0x24, 0xc6, 0x55, 0x19, 0x5b, 0x80, 0xdc, 0x31, + 0xd8, 0x28, 0x16, 0x54, 0xeb, 0xff, 0x25, 0x2f, 0x64, 0xa2, 0xf2, 0x02, 0x71, 0x02, 0x56, 0xc4, + 0x63, 0x60, 0x69, 0x6c, 0xe2, 0x1d, 0xac, 0xd8, 0x6b, 0x98, 0x0e, 0x91, 0x07, 0x9c, 0x0c, 0x1c, + 0xf2, 0x28, 0xaa, 0x4b, 0x3d, 0x16, 0x6b, 0xab, 0x2f, 0x2c, 0x4a, 0x65, 0x54, 0xa2, 0xb2, 0xdd, + 0x5d, 0xcf, 0x76, 0xb6, 0x56, 0xe0, 0xd9, 0x42, 0x1e, 0x91, 0x44, 0x36, 0x34, 0xaa, 0x44, 0xad, + 0xd4, 0xb9, 0x2e, 0x0c, 0xe4, 0x74, 0xf0, 0xe4, 0xa2, 0xe0, 0x3e, 0x96, 0xc3, 0x5a, 0x0c, 0xf7, + 0xe7, 0x05, 0x3a, 0xfc, 0x1b, 0xea, 0x67, 0x1f, 0x55, 0x0a, 0x21, 0x1b, 0x64, 0x10, 0x6b, 0x60, + 0x08, 0x04, 0xd2, 0xfb, 0xe6, 0xb2, 0x7f, 0xc6, 0x2e, 0x06, 0xe8, 0x52, 0xb0, 0xa1, 0x51, 0x6f, + 0x6a, 0x0c, 0xac, 0x36, 0xdf, 0x27, 0x29, 0xcb, 0x80, 0x1f, 0x34, 0x97, 0x65, 0x53, 0xd3, 0xd8, + 0x14, 0x7e, 0x2d, 0xa2, 0x36, 0x0f, 0x76, 0xda, 0xfe, 0xbc, 0xd1, 0x6d, 0xd6, 0x8d, 0xbb, 0xab, + 0xcd, 0x03, 0xf6, 0xf1, 0xbf, 0x8a, 0x9c, 0x9a, 0x3d, 0xab, 0xca, 0x0c, 0x69, 0x42, 0x9f, 0xc2, + 0x44, 0x63, 0x16, 0x1e, 0xa1, 0xa0, 0x15, 0xe8, 0x36, 0xdd, 0xc8, 0x42, 0x51, 0xc7, 0x60, 0x90, + 0x65, 0x4c, 0x23, 0xac, 0x7e, 0xbd, 0x2e, 0x16, 0xaa, 0xbc, 0xc1, 0x88, 0xee, 0xf4, 0xf4, 0xbb, + 0xe6, 0xcc, 0x11, 0x7e, 0x38, 0xe2, 0x0c, 0x07, 0x94, 0x89, 0xc2, 0x95, 0x42, 0x69, 0x9a, 0x24, + 0x52, 0x31, 0x65, 0x49, 0x86, 0x7d, 0x08, 0xd3, 0x44, 0x21, 0x1c, 0xb3, 0xcd, 0x6f, 0x13, 0xc8, + 0x8a, 0x13, 0xde, 0x6b, 0x74, 0x91, 0x6d, 0x06, 0xa3, 0xe1, 0x12, 0xca, 0xbd, 0x70, 0xb4, 0x9a, + 0x64, 0x22, 0xb9, 0x8b, 0xf8, 0x39, 0x99, 0xfd, 0xb5, 0xdb, 0x74, 0x0b, 0xe8, 0x1a, 0xaa, 0xe8, + 0x3d, 0xbf, 0xa7, 0xb1, 0xdc, 0x74, 0x42, 0x9b, 0x4e, 0xe2, 0xe4, 0x6e, 0xb5, 0x6f, 0x63, 0x87, + 0xae, 0x6e, 0x73, 0x81, 0x3e, 0x0e, 0xe3, 0x29, 0xb0, 0x5d, 0x76, 0xaa, 0xb0, 0x5a, 0x24, 0x66, + 0x25, 0x3c, 0x29, 0xd1, 0x2f, 0x67, 0x69, 0x72, 0xe0, 0x86, 0x21, 0x04, 0xb7, 0x2a, 0x10, 0x98, + 0x03, 0xfc, 0xd4, 0xe9, 0xd5, 0x3b, 0x98, 0x51, 0x0c, 0xee, 0x91, 0xd5, 0x44, 0xc9, 0xba, 0x60, + 0xa1, 0x4c, 0x67, 0x69, 0xae, 0xb1, 0xe9, 0x21, 0xe1, 0x66, 0x3b, 0xfe, 0xea, 0x7c, 0x57, 0xe4, + 0x2a, 0x15, 0x80, 0x35, 0x57, 0xd1, 0xfa, 0x95, 0x53, 0x66, 0xd1, 0x65, 0x9e, 0x2d, 0x0e, 0x0c, + 0x90, 0x50, 0x0f, 0x64, 0x52, 0x2a, 0x43, 0xec, 0x27, 0x0f, 0xfc, 0x43, 0x12, 0xef, 0xda, 0xba, + 0xc4, 0xd6, 0x6a, 0x12, 0xab, 0x2b, 0xd2, 0x62, 0xc2, 0xa3, 0xc7, 0x65, 0x01, 0x8a, 0x78, 0xe7, + 0x80, 0x5b, 0x3b, 0x0b, 0x33, 0x05, 0x86, 0x04, 0x1f, 0xd2, 0xa7, 0xda, 0x16, 0x2b, 0x2b, 0x4d, + 0x83, 0x0d, 0x4d, 0xef, 0x3a, 0x4e, 0xd3, 0xd0, 0x60, 0xde, 0x24, 0xc8, 0x68, 0x86, 0x8d, 0x8c, + 0x60, 0x8d, 0x0d, 0x6b, 0x85, 0xf6, 0x0f, 0x43, 0xaa, 0xb0, 0xb3, 0xf3, 0xd3, 0xc1, 0x86, 0xb3, + 0xb5, 0xba, 0x8b, 0x16, 0xaf, 0x2d, 0xc9, 0x2a, 0xbf, 0x85, 0x92, 0x2f, 0xf8, 0x81, 0xd4, 0xca, + 0x85, 0x8c, 0x78, 0xcb, 0x9a, 0x8b, 0xf8, 0xc1, 0xdb, 0xb7, 0x9c, 0x95, 0xf0, 0xef, 0x4a, 0xe0, + 0x6c, 0xd2, 0x63, 0x21, 0xdb, 0xd2, 0xd3, 0x7e, 0x25, 0x3d, 0x07, 0xaf, 0xa4, 0xa7, 0xf3, 0x53, + 0x7a, 0xd6, 0xa0, 0x1c, 0xe3, 0xa4, 0x3b, 0x8b, 0x1f, 0x0e, 0x1b, 0x6b, 0xa0, 0x39, 0xdd, 0x1f, + 0x7e, 0x1a, 0xb3, 0xc6, 0xa6, 0x9e, 0x57, 0xc2, 0xec, 0xc3, 0x2b, 0x61, 0xf6, 0xe1, 0xe7, 0x31, + 0x6b, 0xd4, 0xa0, 0x61, 0x66, 0x63, 0x1e, 0xa2, 0x94, 0xbe, 0x3b, 0x7c, 0x82, 0xdb, 0xf0, 0x7f, + 0xc0, 0xad, 0xf1, 0x43, 0x47, 0x6b, 0x3d, 0xed, 0x57, 0xd2, 0x73, 0xf0, 0x4a, 0x7a, 0x3a, 0x3f, + 0xa7, 0x87, 0x00, 0xca, 0xcf, 0xae, 0x86, 0x0c, 0x1b, 0x34, 0xde, 0x8a, 0xf4, 0x22, 0x75, 0x5d, + 0xa1, 0xa1, 0xac, 0x95, 0x8a, 0x91, 0x80, 0x4b, 0xdc, 0xba, 0xe9, 0xd4, 0xb6, 0x1b, 0x1b, 0xe5, + 0xa6, 0xae, 0x0d, 0x1f, 0x9f, 0x54, 0x19, 0x66, 0x5b, 0x6a, 0x46, 0xfb, 0x4f, 0x33, 0x81, 0xbd, + 0x0f, 0x4b, 0xc4, 0x21, 0xeb, 0xea, 0x22, 0x96, 0x4b, 0x2f, 0x45, 0x81, 0x7d, 0xe0, 0x6a, 0x55, + 0xd4, 0x20, 0xc5, 0x4e, 0x84, 0x02, 0xd6, 0xc1, 0xba, 0xdc, 0xda, 0x56, 0x0f, 0xec, 0x38, 0x49, + 0xc8, 0xd1, 0x81, 0x12, 0xd2, 0xd8, 0x4a, 0xdb, 0x38, 0x1e, 0x30, 0x2a, 0x8f, 0xcf, 0xfa, 0x7e, + 0x3c, 0x78, 0xb1, 0x3e, 0x3a, 0x87, 0x8f, 0x9f, 0x96, 0x45, 0x9b, 0x6e, 0xe7, 0x22, 0x45, 0x5b, + 0x03, 0x2b, 0x75, 0xc8, 0x36, 0xb6, 0x27, 0x13, 0x48, 0xee, 0x6e, 0xd5, 0xfd, 0x52, 0xc5, 0xb9, + 0x2b, 0x80, 0xe4, 0xc8, 0xa2, 0x58, 0x3e, 0x75, 0x86, 0xae, 0x90, 0xfe, 0x8f, 0xaa, 0x6b, 0xad, + 0x6c, 0xe0, 0x22, 0xb5, 0x56, 0x5d, 0x51, 0xa9, 0xc1, 0x81, 0x01, 0xa3, 0xe8, 0x85, 0x8f, 0xef, + 0x83, 0x77, 0x9d, 0xb9, 0xff, 0x1d, 0x7b, 0x06, 0xb3, 0x77, 0xf2, 0x88, 0xf7, 0xad, 0x09, 0xb4, + 0x80, 0x81, 0x0e, 0xd9, 0x7b, 0xfb, 0x0b, 0x43, 0x8c, 0x10, 0x96, 0x7a, 0xe1, 0xd6, 0x1a, 0x62, + 0xcc, 0xb6, 0x32, 0xba, 0xd0, 0x48, 0xc8, 0x0e, 0x37, 0x49, 0x74, 0x7c, 0xfa, 0x02, 0x89, 0xee, + 0x17, 0x2c, 0x6a, 0x2f, 0x58, 0xd4, 0xee, 0x6c, 0x91, 0x08, 0x8f, 0x4e, 0x07, 0xd4, 0x14, 0x58, + 0x7b, 0x31, 0x58, 0xe8, 0xc4, 0x06, 0xdf, 0x75, 0x57, 0x00, 0xe6, 0xa6, 0x5d, 0xa4, 0x22, 0x85, + 0x7a, 0x11, 0x69, 0x5a, 0x88, 0x47, 0x34, 0x0f, 0xdd, 0x2a, 0x85, 0x01, 0x75, 0xb2, 0xdb, 0x7b, + 0xda, 0xbc, 0x77, 0x26, 0xf4, 0x1a, 0x39, 0xb6, 0xc4, 0x1a, 0xb5, 0x1c, 0x46, 0xef, 0x38, 0xc3, + 0x92, 0xa9, 0x5f, 0x52, 0x84, 0xbd, 0xea, 0x0a, 0xa6, 0x68, 0xcd, 0xa3, 0x16, 0x5a, 0x02, 0x8e, + 0x88, 0x39, 0x8e, 0xb4, 0x90, 0xfa, 0xcb, 0x1d, 0x34, 0x2d, 0xd9, 0x23, 0x2c, 0x0e, 0xf6, 0x3c, + 0x5d, 0x89, 0xad, 0x08, 0xb3, 0x98, 0xc2, 0x16, 0x55, 0xfb, 0xf7, 0x05, 0x94, 0x82, 0x7e, 0x9e, + 0x40, 0xd2, 0x5b, 0x82, 0x9e, 0xb9, 0x8b, 0x89, 0xc3, 0x5e, 0x67, 0x00, 0xc5, 0x7f, 0x61, 0xd4, + 0xcd, 0xd0, 0x85, 0xae, 0x2b, 0x7a, 0x8d, 0x53, 0xb4, 0x4b, 0x93, 0x12, 0x9b, 0xe1, 0x8d, 0x60, + 0x89, 0xda, 0x54, 0x60, 0xcc, 0x85, 0xd6, 0x15, 0xe8, 0xd0, 0x06, 0xf9, 0xcc, 0x0d, 0x05, 0x20, + 0xad, 0x25, 0x31, 0x72, 0xc6, 0x84, 0x66, 0xd4, 0xdd, 0x69, 0x38, 0x49, 0x54, 0x89, 0xc7, 0x35, + 0xd9, 0x43, 0xc0, 0x84, 0xa4, 0x5f, 0x51, 0x34, 0x68, 0x56, 0xa8, 0x19, 0x62, 0x41, 0x03, 0x78, + 0x95, 0xdb, 0xd3, 0x87, 0xdd, 0xa6, 0xb0, 0x43, 0x9a, 0x1b, 0xf1, 0x70, 0x9e, 0x4b, 0xb9, 0x3b, + 0x15, 0x5d, 0xc9, 0xa8, 0xb0, 0x5e, 0xa3, 0xc7, 0x6e, 0xc2, 0xd9, 0x08, 0x74, 0xff, 0xfa, 0xfc, + 0xb9, 0x48, 0x5f, 0x29, 0x09, 0x8d, 0x97, 0x82, 0xf1, 0x17, 0x8c, 0xd6, 0xc9, 0xe9, 0x8e, 0x9b, + 0x84, 0x3f, 0xbf, 0xe9, 0xd0, 0x18, 0x66, 0x4a, 0x95, 0xe1, 0xa0, 0x8f, 0x33, 0x19, 0x4d, 0xef, + 0x58, 0xd1, 0xf1, 0x38, 0xdb, 0xfb, 0x1b, 0x6c, 0x15, 0xee, 0xfe, 0x70, 0xd0, 0x39, 0xd8, 0x1f, + 0x7c, 0xea, 0xbf, 0x64, 0xe4, 0x5d, 0x2d, 0x73, 0x06, 0xd5, 0xfd, 0x8b, 0xbc, 0xf9, 0x85, 0xf7, + 0xfe, 0x7e, 0x71, 0xbd, 0xff, 0xf9, 0x45, 0x2d, 0xef, 0x79, 0xef, 0x1f, 0x95, 0x90, 0xe8, 0xe8, + 0xfe, 0x99, 0x18, 0xef, 0x7f, 0x4a, 0x4c, 0xfc, 0xa2, 0xb2, 0x37, 0x2b, 0x59, 0x6b, 0xf9, 0x45, + 0x52, 0x23, 0x3e, 0x43, 0x28, 0xc7, 0xbf, 0xbc, 0xd9, 0x47, 0xf4, 0x9e, 0x91, 0x5b, 0x28, 0x7c, + 0xcb, 0x7b, 0xd7, 0x33, 0x91, 0x89, 0xf1, 0xc4, 0x5c, 0x62, 0xa9, 0xf8, 0xb1, 0x56, 0x64, 0xf8, + 0xcd, 0xf3, 0x02, 0x8d, 0x55, 0x32, 0xdd, 0x5c, 0x23, 0x68, 0x68, 0xb4, 0xd5, 0x5e, 0x49, 0xad, + 0xb3, 0xde, 0xfe, 0xad, 0xc7, 0xdf, 0xf2, 0xff, 0x3e, 0x6c, 0x37, 0xe9, 0x26, 0x41, 0x79, 0x47, + 0xd7, 0x0d, 0xba, 0x7b, 0xd0, 0x8f, 0xa8, 0xff, 0x01, 0x31, 0x18, 0xad, 0x59, 0x54, 0x15, 0x00, + 0x00 }; // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const uint16_t PAGE_settings_leds_length = 7593; const uint8_t PAGE_settings_leds[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xdd, 0x3c, 0xdb, 0x76, 0xe2, 0x48, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xdd, 0x3c, 0xdb, 0x76, 0xe2, 0x48, 0x92, 0xef, 0x7c, 0x45, 0x5a, 0xdd, 0xed, 0x92, 0x1a, 0x19, 0x24, 0x2e, 0x6e, 0x0c, 0x08, 0xaf, 0xa1, 0x2e, 0xed, 0x19, 0xbb, 0xed, 0x03, 0xae, 0xaa, 0x99, 0x53, 0x5d, 0xa7, 0x4b, 0x48, 0x09, 0xa8, 0x2c, 0x24, 0x46, 0x12, 0xbe, 0xac, 0xed, 0xfd, 0xa6, 0xfd, 0x86, 0xfd, 0xb2, 0x8d, 0xc8, @@ -756,7 +757,7 @@ const uint8_t PAGE_settings_leds[] PROGMEM = { // Autogenerated from wled00/data/settings_dmx.htm, do not edit!! const uint16_t PAGE_settings_dmx_length = 1612; const uint8_t PAGE_settings_dmx[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x95, 0x57, 0xdb, 0x72, 0xdb, 0x36, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x95, 0x57, 0xdb, 0x72, 0xdb, 0x36, 0x10, 0x7d, 0xd7, 0x57, 0x20, 0x78, 0x88, 0xc9, 0x31, 0x43, 0x4a, 0x4e, 0x95, 0x36, 0x32, 0x49, 0x37, 0x56, 0x5c, 0xdb, 0x1d, 0xdb, 0xf5, 0x44, 0x49, 0xd3, 0x4e, 0xd3, 0xe9, 0x40, 0xe4, 0x4a, 0x44, 0x4c, 0x02, 0x2c, 0x00, 0x4a, 0x76, 0x2e, 0xff, 0xde, 0x05, 0x48, 0x5d, 0xec, 0xd8, 0x69, @@ -863,7 +864,7 @@ const uint8_t PAGE_settings_dmx[] PROGMEM = { // Autogenerated from wled00/data/settings_ui.htm, do not edit!! const uint16_t PAGE_settings_ui_length = 3181; const uint8_t PAGE_settings_ui[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xad, 0x59, 0x6d, 0x77, 0xda, 0x38, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x59, 0x6d, 0x77, 0xda, 0x38, 0x16, 0xfe, 0xce, 0xaf, 0x50, 0xd5, 0x39, 0x19, 0x38, 0x71, 0x81, 0xb4, 0xb3, 0x67, 0x5b, 0xc0, 0x64, 0x9b, 0x34, 0xd3, 0x66, 0x4e, 0x3a, 0xed, 0x96, 0x74, 0x3b, 0x73, 0xba, 0x3d, 0x19, 0x63, 0x0b, 0x50, 0x63, 0x5b, 0x1e, 0x4b, 0x0e, 0xc9, 0x52, 0xfe, 0xfb, 0x3e, 0x57, 0xb2, 0xc1, 0x90, @@ -1068,7 +1069,7 @@ const uint8_t PAGE_settings_ui[] PROGMEM = { // Autogenerated from wled00/data/settings_sync.htm, do not edit!! const uint16_t PAGE_settings_sync_length = 3318; const uint8_t PAGE_settings_sync[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xad, 0x1a, 0x6b, 0x53, 0xdb, 0xb8, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xad, 0x1a, 0x6b, 0x53, 0xdb, 0xb8, 0xf6, 0xbb, 0x7f, 0x85, 0xf0, 0xce, 0x74, 0x93, 0x25, 0xe4, 0x01, 0x84, 0x52, 0x88, 0xdd, 0x0b, 0x84, 0x42, 0xee, 0x2d, 0x34, 0x4d, 0xe8, 0xb6, 0x3b, 0x73, 0x67, 0x76, 0x14, 0x5b, 0x49, 0x04, 0xb6, 0xe5, 0xb5, 0x64, 0x1e, 0xd3, 0xed, 0x7f, 0xbf, 0xe7, 0x48, 0xb6, 0x93, 0x98, 0xbc, 0x7a, @@ -1282,7 +1283,7 @@ const uint8_t PAGE_settings_sync[] PROGMEM = { // Autogenerated from wled00/data/settings_time.htm, do not edit!! const uint16_t PAGE_settings_time_length = 3313; const uint8_t PAGE_settings_time[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xd5, 0x1a, 0x6b, 0x57, 0xdb, 0x38, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xd5, 0x1a, 0x6b, 0x57, 0xdb, 0x38, 0xf6, 0x7b, 0x7e, 0x85, 0x50, 0x7b, 0x98, 0x78, 0x70, 0x9e, 0x90, 0x16, 0x92, 0xd8, 0xdd, 0x10, 0xd2, 0x42, 0x4b, 0x02, 0x67, 0x92, 0x0e, 0xbb, 0xd3, 0xf6, 0x4c, 0x15, 0x5b, 0x49, 0x0c, 0x8e, 0xe4, 0xb5, 0x65, 0x02, 0x4b, 0xf9, 0xef, 0x7b, 0x25, 0x39, 0xce, 0xd3, 0xd0, 0x76, 0x66, 0x3f, @@ -1496,7 +1497,7 @@ const uint8_t PAGE_settings_time[] PROGMEM = { // Autogenerated from wled00/data/settings_sec.htm, do not edit!! const uint16_t PAGE_settings_sec_length = 2407; const uint8_t PAGE_settings_sec[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xa5, 0x58, 0x6d, 0x53, 0xdb, 0x48, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xa5, 0x58, 0x6d, 0x53, 0xdb, 0x48, 0x12, 0xfe, 0xee, 0x5f, 0x31, 0x9e, 0x54, 0xb1, 0xd6, 0x45, 0x58, 0x40, 0x72, 0x5b, 0x09, 0x58, 0xce, 0x41, 0x20, 0x1b, 0xae, 0x20, 0x50, 0xd8, 0x6c, 0xee, 0x2a, 0x97, 0x4a, 0x8d, 0xa5, 0xb1, 0x35, 0xb1, 0xac, 0xd1, 0xce, 0x8c, 0x70, 0x7c, 0xd9, 0xfd, 0xef, 0xf7, 0xf4, 0x48, 0x32, 0x86, @@ -1653,7 +1654,7 @@ const uint8_t PAGE_settings_sec[] PROGMEM = { // Autogenerated from wled00/data/settings_um.htm, do not edit!! const uint16_t PAGE_settings_um_length = 2786; const uint8_t PAGE_settings_um[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xcd, 0x59, 0x6d, 0x73, 0xdb, 0x36, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xcd, 0x59, 0x6d, 0x73, 0xdb, 0x36, 0x12, 0xfe, 0xee, 0x5f, 0x41, 0x23, 0x1e, 0x9b, 0x1c, 0xd1, 0x94, 0x9c, 0xb4, 0x33, 0xa9, 0x24, 0xd2, 0x97, 0x38, 0x69, 0xa3, 0xcb, 0x8b, 0x3d, 0xe3, 0xb4, 0x9d, 0x1b, 0xc7, 0x17, 0x53, 0x22, 0x24, 0x21, 0xa6, 0x00, 0x16, 0x04, 0xfd, 0x72, 0xb2, 0xfe, 0xfb, 0x3d, 0x0b, 0xbe, 0x88, 0x72, @@ -1834,7 +1835,7 @@ const uint8_t PAGE_settings_um[] PROGMEM = { // Autogenerated from wled00/data/settings_2D.htm, do not edit!! const uint16_t PAGE_settings_2D_length = 3172; const uint8_t PAGE_settings_2D[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xc5, 0x5a, 0x5b, 0x77, 0xdb, 0x36, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xc5, 0x5a, 0x5b, 0x77, 0xdb, 0x36, 0x12, 0x7e, 0xd7, 0xaf, 0x80, 0xd1, 0xae, 0x4b, 0x5a, 0xd4, 0xcd, 0x4d, 0x7a, 0x5a, 0x49, 0x94, 0x36, 0x8e, 0xd3, 0xd8, 0x7b, 0xec, 0x44, 0xc7, 0x72, 0xed, 0xe4, 0xb4, 0x3d, 0x0d, 0x4d, 0x42, 0x12, 0x12, 0x0a, 0x60, 0x09, 0x48, 0xb6, 0xeb, 0xf8, 0xbf, 0xef, 0x0c, 0xc0, 0x9b, 0x68, 0xd9, @@ -2039,7 +2040,7 @@ const uint8_t PAGE_settings_2D[] PROGMEM = { // Autogenerated from wled00/data/settings_pin.htm, do not edit!! const uint16_t PAGE_settings_pin_length = 471; const uint8_t PAGE_settings_pin[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x5d, 0x52, 0x4d, 0x6f, 0x13, 0x31, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0x5d, 0x52, 0x4d, 0x6f, 0x13, 0x31, 0x10, 0xbd, 0xef, 0xaf, 0x30, 0x73, 0x69, 0x82, 0x92, 0x6c, 0xa8, 0xa8, 0x04, 0xaa, 0xbd, 0x42, 0x81, 0x1e, 0xb8, 0x94, 0x48, 0xe5, 0x52, 0x55, 0x55, 0xe5, 0xd8, 0xb3, 0x89, 0x55, 0x7f, 0x2c, 0xb6, 0x37, 0x21, 0x54, 0xfc, 0x77, 0xc6, 0xbb, 0xa1, 0xa0, 0x5c, 0xd6, 0x7e, 0x33, 0xe3, 0x37, diff --git a/wled00/network.cpp b/wled00/network.cpp index ddd44c36..9407c523 100644 --- a/wled00/network.cpp +++ b/wled00/network.cpp @@ -27,22 +27,22 @@ const ethernet_settings ethernetBoards[] = { // These pins do not appear to work from my testing: // IO35, IO36, IO39 { - 1, // eth_address, - 16, // eth_power, - 23, // eth_mdc, - 18, // eth_mdio, - ETH_PHY_LAN8720, // eth_type, - ETH_CLOCK_GPIO0_IN // eth_clk_mode + 1, // eth_address, + 16, // eth_power, + 23, // eth_mdc, + 18, // eth_mdio, + ETH_PHY_LAN8720, // eth_type, + ETH_CLOCK_GPIO0_IN // eth_clk_mode }, // ESP32-POE { - 0, // eth_address, - 12, // eth_power, - 23, // eth_mdc, - 18, // eth_mdio, - ETH_PHY_LAN8720, // eth_type, - ETH_CLOCK_GPIO17_OUT // eth_clk_mode + 0, // eth_address, + 12, // eth_power, + 23, // eth_mdc, + 18, // eth_mdio, + ETH_PHY_LAN8720, // eth_type, + ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, // WESP32 @@ -88,11 +88,11 @@ const ethernet_settings ethernetBoards[] = { // ESP32-ETHERNET-KIT-VE { 0, // eth_address, - 5, // eth_power, + 5, // eth_power, 23, // eth_mdc, 18, // eth_mdio, - ETH_PHY_IP101, // eth_type, - ETH_CLOCK_GPIO0_IN // eth_clk_mode + ETH_PHY_IP101, // eth_type, + ETH_CLOCK_GPIO0_IN // eth_clk_mode }, // QuinLed-Dig-Octa Brainboard-32-8L and LilyGO-T-ETH-POE @@ -115,6 +115,15 @@ const ethernet_settings ethernetBoards[] = { ETH_CLOCK_GPIO17_OUT // eth_clk_mode }, + // Serg74-ESP32 Ethernet Shield + { + 1, // eth_address, + 5, // eth_power, + 23, // eth_mdc, + 18, // eth_mdio, + ETH_PHY_LAN8720, // eth_type, + ETH_CLOCK_GPIO17_OUT // eth_clk_mode + } }; #endif From 52c4093fb0e12a054f7582cdbe83c548566f6b87 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Mon, 8 May 2023 20:59:57 +0200 Subject: [PATCH 13/22] minor bugfix for usermod_v2_Battery.h missing semicolon - caused compile errorsin debug mode. --- usermods/Battery/usermod_v2_Battery.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usermods/Battery/usermod_v2_Battery.h b/usermods/Battery/usermod_v2_Battery.h index d3e52e00..be3d8748 100644 --- a/usermods/Battery/usermod_v2_Battery.h +++ b/usermods/Battery/usermod_v2_Battery.h @@ -383,7 +383,7 @@ class UsermodBattery : public Usermod lp[FPSTR(_duration)] = lowPowerIndicatorDuration; // read voltage in case calibration or voltage multiplier changed to see immediate effect - voltage = readVoltage() + voltage = readVoltage(); DEBUG_PRINTLN(F("Battery config saved.")); } From bc90309dd69533ff9f4b879fb82ecfd1b1db35d7 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 May 2023 17:34:30 +0200 Subject: [PATCH 14/22] fix error message in latest platformio fix for "Error: Invalid environment name 'codm-controller-0.6-rev2'. The name can contain alphanumeric, underscore, and hyphen characters (a-z, 0-9, -, _)" --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 74cf55ca..5bd8a699 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_ ; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB # Build everything -; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips +; default_envs = esp32dev, esp8285_4CH_MagicHome, codm-controller-0_6-rev2, codm-controller-0_6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips # Single binaries (uncomment your board) ; default_envs = elekstube_ips @@ -727,10 +727,10 @@ lib_deps = ${esp8266.lib_deps} # ------------------------------------------------------------------------------ # codm pixel controller board configurations -# codm-controller-0.6 can also be used for the TYWE3S controller +# codm-controller-0_6 can also be used for the TYWE3S controller # ------------------------------------------------------------------------------ -[env:codm-controller-0.6] +[env:codm-controller-0_6] board = esp_wroom_02 platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} @@ -739,7 +739,7 @@ build_unflags = ${common.build_unflags} build_flags = ${common.build_flags_esp8266} lib_deps = ${esp8266.lib_deps} -[env:codm-controller-0.6-rev2] +[env:codm-controller-0_6-rev2] board = esp_wroom_02 platform = ${common.platform_wled_default} platform_packages = ${common.platform_packages} From b0118d2d57956bae3666f31fac29dbe25d16d75d Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 May 2023 17:44:26 +0200 Subject: [PATCH 15/22] use size_t as file index type (might prevent corruption) * use size_t instead of uint16_t -> prevents random behaviour (corruption) in case that JSON files get larger than 64Kbytes. * use a constant for max large file space (was UINT16_MAX) * reduced the scope of some functions and variables to "static" - avoids name clashes and may support better optimization by the compiler --- wled00/file.cpp | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/wled00/file.cpp b/wled00/file.cpp index 1186560e..4edbdd6f 100644 --- a/wled00/file.cpp +++ b/wled00/file.cpp @@ -27,9 +27,10 @@ // There are no consecutive spaces longer than this in the file, so if more space is required, findSpace() can return false immediately // Actual space may be lower -uint16_t knownLargestSpace = UINT16_MAX; +constexpr size_t MAX_SPACE = UINT16_MAX * 2U; // smallest supported config has 128Kb flash size +static volatile size_t knownLargestSpace = MAX_SPACE; -File f; +static File f; // don't export to other cpp files //wrapper to find out how long closing takes void closeFile() { @@ -44,7 +45,7 @@ void closeFile() { //find() that reads and buffers data from file stream in 256-byte blocks. //Significantly faster, f.find(key) can take SECONDS for multi-kB files -bool bufferedFind(const char *target, bool fromStart = true) { +static bool bufferedFind(const char *target, bool fromStart = true) { #ifdef WLED_DEBUG_FS DEBUGFS_PRINT("Find "); DEBUGFS_PRINTLN(target); @@ -59,8 +60,8 @@ bool bufferedFind(const char *target, bool fromStart = true) { if (fromStart) f.seek(0); while (f.position() < f.size() -1) { - uint16_t bufsize = f.read(buf, FS_BUFSIZE); - uint16_t count = 0; + size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead if uint16_t + size_t count = 0; while (count < bufsize) { if(buf[count] != target[index]) index = 0; // reset index if any char does not match @@ -80,7 +81,7 @@ bool bufferedFind(const char *target, bool fromStart = true) { } //find empty spots in file stream in 256-byte blocks. -bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) { +static bool bufferedFindSpace(size_t targetLen, bool fromStart = true) { #ifdef WLED_DEBUG_FS DEBUGFS_PRINTF("Find %d spaces\n", targetLen); @@ -95,20 +96,20 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) { if (!f || !f.size()) return false; - uint16_t index = 0; + size_t index = 0; // better to use size_t instead if uint16_t byte buf[FS_BUFSIZE]; if (fromStart) f.seek(0); while (f.position() < f.size() -1) { - uint16_t bufsize = f.read(buf, FS_BUFSIZE); - uint16_t count = 0; + size_t bufsize = f.read(buf, FS_BUFSIZE); + size_t count = 0; while (count < bufsize) { if(buf[count] == ' ') { if(++index >= targetLen) { // return true if space long enough if (fromStart) { f.seek((f.position() - bufsize) + count +1 - targetLen); - knownLargestSpace = UINT16_MAX; //there may be larger spaces after, so we don't know + knownLargestSpace = MAX_SPACE; //there may be larger spaces after, so we don't know } DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s); return true; @@ -116,7 +117,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) { } else { if (!fromStart) return false; if (index) { - if (knownLargestSpace < index || knownLargestSpace == UINT16_MAX) knownLargestSpace = index; + if (knownLargestSpace < index || (knownLargestSpace == MAX_SPACE)) knownLargestSpace = index; index = 0; // reset index if not space } } @@ -129,7 +130,7 @@ bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) { } //find the closing bracket corresponding to the opening bracket at the file pos when calling this function -bool bufferedFindObjectEnd() { +static bool bufferedFindObjectEnd() { #ifdef WLED_DEBUG_FS DEBUGFS_PRINTLN(F("Find obj end")); uint32_t s = millis(); @@ -142,8 +143,8 @@ bool bufferedFindObjectEnd() { byte buf[FS_BUFSIZE]; while (f.position() < f.size() -1) { - uint16_t bufsize = f.read(buf, FS_BUFSIZE); - uint16_t count = 0; + size_t bufsize = f.read(buf, FS_BUFSIZE); // better to use size_t instead of uint16_t + size_t count = 0; while (count < bufsize) { if (buf[count] == '{') objDepth++; @@ -161,13 +162,13 @@ bool bufferedFindObjectEnd() { } //fills n bytes from current file pos with ' ' characters -void writeSpace(uint16_t l) +static void writeSpace(size_t l) { byte buf[FS_BUFSIZE]; memset(buf, ' ', FS_BUFSIZE); while (l > 0) { - uint16_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l; + size_t block = (l>FS_BUFSIZE) ? FS_BUFSIZE : l; f.write(buf, block); l -= block; } @@ -270,7 +271,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content) s = millis(); #endif - uint32_t pos = 0; + size_t pos = 0; f = WLED_FS.open(file, "r+"); if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+"); if (!f) { @@ -287,7 +288,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content) pos = f.position(); //measure out end of old object bufferedFindObjectEnd(); - uint32_t pos2 = f.position(); + size_t pos2 = f.position(); uint32_t oldLen = pos2 - pos; DEBUGFS_PRINTF("Old obj len %d\n", oldLen); @@ -298,7 +299,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content) //3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new //4. The new content is larger than old + trailing spaces, delete old and append - uint32_t contentLen = 0; + size_t contentLen = 0; if (!content->isNull()) contentLen = measureJson(*content); if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces @@ -375,7 +376,7 @@ void updateFSInfo() { //Un-comment any file types you need -String getContentType(AsyncWebServerRequest* request, String filename){ +static String getContentType(AsyncWebServerRequest* request, String filename){ if(request->hasArg("download")) return "application/octet-stream"; else if(filename.endsWith(".htm")) return "text/html"; else if(filename.endsWith(".html")) return "text/html"; @@ -394,7 +395,7 @@ String getContentType(AsyncWebServerRequest* request, String filename){ } bool handleFileRead(AsyncWebServerRequest* request, String path){ - DEBUG_PRINTLN("FileRead: " + path); + DEBUG_PRINTLN("WS FileRead: " + path); if(path.endsWith("/")) path += "index.htm"; if(path.indexOf("sec") > -1) return false; String contentType = getContentType(request, path); From 9e446210fb8dafe736c0083447b084af7455ae28 Mon Sep 17 00:00:00 2001 From: Frank <91616163+softhack007@users.noreply.github.com> Date: Tue, 9 May 2023 17:57:17 +0200 Subject: [PATCH 16/22] refresh build number --- wled00/wled.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/wled.h b/wled00/wled.h index 1368bc4c..eefdf531 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2304280 +#define VERSION 2305090 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG From 16b66afa7a27941e29a1077504519808e2d9227c Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 10 May 2023 21:06:48 +0200 Subject: [PATCH 17/22] Octopus offset --- wled00/FX.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index c3873765..e3b6a202 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7518,21 +7518,25 @@ uint16_t mode_2Doctopus() { } map_t; const size_t dataSize = SEGMENT.width() * SEGMENT.height() * sizeof(map_t); // prevent reallocation if mirrored or grouped - if (!SEGENV.allocateData(dataSize)) return mode_static(); //allocation failed + if (!SEGENV.allocateData(dataSize + 2)) return mode_static(); //allocation failed map_t *rMap = reinterpret_cast(SEGENV.data); + uint8_t *offsX = reinterpret_cast(SEGENV.data + dataSize); + uint8_t *offsY = reinterpret_cast(SEGENV.data + dataSize + 1); - // re-init if SEGMENT dimensions changed - if (SEGENV.call == 0 || SEGMENT.aux0 != cols || SEGMENT.aux1 != rows) { + // re-init if SEGMENT dimensions or offset changed + if (SEGENV.call == 0 || SEGENV.aux0 != cols || SEGENV.aux1 != rows || SEGMENT.custom1 != *offsX || SEGMENT.custom2 != *offsY) { SEGENV.step = 0; // t - SEGMENT.aux0 = cols; - SEGMENT.aux1 = rows; - const uint8_t C_X = cols / 2; - const uint8_t C_Y = rows / 2; - for (int x = -C_X; x < C_X + (cols % 2); x++) { - for (int y = -C_Y; y < C_Y + (rows % 2); y++) { - rMap[XY(x + C_X, y + C_Y)].angle = 128 * (atan2f(y, x) / PI); - rMap[XY(x + C_X, y + C_Y)].radius = hypotf(x, y) * mapp; //thanks Sutaburosu + SEGENV.aux0 = cols; + SEGENV.aux1 = rows; + *offsX = SEGMENT.custom1; + *offsY = SEGMENT.custom2; + const uint8_t C_X = cols / 2 + (SEGMENT.custom1 - 128)*cols/255; + const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255; + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + rMap[XY(x, y)].angle = 128 * (atan2f(y - C_Y, x - C_X) / PI); + rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu } } } @@ -7544,17 +7548,14 @@ uint16_t mode_2Doctopus() { byte radius = rMap[XY(x,y)].radius; //CRGB c = CHSV(SEGENV.step / 2 - radius, 255, sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step) + radius - SEGENV.step * 2 + angle * (SEGMENT.custom3/3+1))); uint16_t intensity = sin8(sin8((angle * 4 - radius) / 4 + SEGENV.step/2) + radius - SEGENV.step + angle * (SEGMENT.custom3/4+1)); - intensity = map(intensity*intensity, 0, 65535, 0, SEGMENT.intensity); // add a bit of non-linearity for cleaner display + intensity = map(intensity*intensity, 0, 65535, 0, 255); // add a bit of non-linearity for cleaner display CRGB c = ColorFromPalette(SEGPALETTE, SEGENV.step / 2 - radius, intensity); - int rX = SEGMENT.custom1 ? (strip.now / (320 - SEGMENT.custom1) + x) % cols : x; - int rY = SEGMENT.custom2 ? (strip.now / (320 - SEGMENT.custom2) + y) % rows : y; - SEGMENT.setPixelColorXY(rX, rY, c); + SEGMENT.setPixelColorXY(x, y, c); } } - return FRAMETIME; } -static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,!,Rotate X,Rotate Y,Legs;;!;2;ix=255,c1=0,c2=0"; +static const char _data_FX_MODE_2DOCTOPUS[] PROGMEM = "Octopus@!,,Offset X,Offset Y,Legs;;!;2;"; //Waving Cell From bf6a18a414e156d8968ddbd78396749987a02142 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 10 May 2023 21:09:28 +0200 Subject: [PATCH 18/22] Bugfix - SHT enable/disable crash --- usermods/sht/usermod_sht.h | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/usermods/sht/usermod_sht.h b/usermods/sht/usermod_sht.h index 1123a10a..56f3dd41 100644 --- a/usermods/sht/usermod_sht.h +++ b/usermods/sht/usermod_sht.h @@ -22,7 +22,7 @@ class ShtUsermod : public Usermod bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics // SHT vars - SHT *shtTempHumidSensor; // Instance of SHT lib + SHT *shtTempHumidSensor = nullptr; // Instance of SHT lib byte shtType = 0; // SHT sensor type to be used. Default: SHT30 byte unitOfTemp = 0; // Temperature unit to be used. Default: Celsius (0 = Celsius, 1 = Fahrenheit) bool shtInitDone = false; // Remembers if SHT sensor has been initialised @@ -37,7 +37,7 @@ class ShtUsermod : public Usermod void initShtTempHumiditySensor(); void cleanupShtTempHumiditySensor(); void cleanup(); - bool isShtReady(); + inline bool isShtReady() { return shtInitDone; } // Checks if the SHT sensor has been initialised. void publishTemperatureAndHumidityViaMqtt(); void publishHomeAssistantAutodiscovery(); @@ -113,8 +113,11 @@ void ShtUsermod::initShtTempHumiditySensor() */ void ShtUsermod::cleanupShtTempHumiditySensor() { - if (isShtReady()) shtTempHumidSensor->reset(); - delete shtTempHumidSensor; + if (isShtReady()) { + shtTempHumidSensor->reset(); + delete shtTempHumidSensor; + shtTempHumidSensor = nullptr; + } shtInitDone = false; } @@ -139,16 +142,6 @@ void ShtUsermod::cleanup() enabled = false; } -/** - * Checks if the SHT sensor has been initialised. - * - * @return bool - */ -bool ShtUsermod::isShtReady() -{ - return shtInitDone; -} - /** * Publish temperature and humidity to WLED device topic. * @@ -463,7 +456,19 @@ void ShtUsermod::addToJsonInfo(JsonObject& root) jsonHumidity.add(F(" RH")); jsonTemp.add(getTemperature()); - jsonTemp.add(unitOfTemp ? "°F" : "°C"); + jsonTemp.add(getUnitString()); + + // sensor object + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + + jsonTemp = sensor.createNestedArray(F("temp")); + jsonTemp.add(getTemperature()); + jsonTemp.add(getUnitString()); + + jsonHumidity = sensor.createNestedArray(F("humidity")); + jsonHumidity.add(getHumidity()); + jsonHumidity.add(F(" RH")); } /** From cdfc0f6b71436899edfbb03c48b0c8cd90c33158 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 11 May 2023 17:33:09 +0200 Subject: [PATCH 19/22] Temperature usermod rewrite --- usermods/Temperature/platformio_override.ini | 9 +- usermods/Temperature/readme.md | 13 +- usermods/Temperature/usermod_temperature.h | 651 ++++++++++--------- usermods/Temperature/usermods_list.cpp | 31 - 4 files changed, 353 insertions(+), 351 deletions(-) delete mode 100644 usermods/Temperature/usermods_list.cpp diff --git a/usermods/Temperature/platformio_override.ini b/usermods/Temperature/platformio_override.ini index d9e3fbac..0e354da9 100644 --- a/usermods/Temperature/platformio_override.ini +++ b/usermods/Temperature/platformio_override.ini @@ -1,13 +1,12 @@ ; Options ; ------- ; USERMOD_DALLASTEMPERATURE - define this to have this user mod included wled00\usermods_list.cpp -; USERMOD_DALLASTEMPERATURE_CELSIUS - define this to report temperatures in degrees celsius, otherwise fahrenheit will be reported ; USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL - the number of milliseconds between measurements, defaults to 60 seconds -; USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - the number of milliseconds after boot to take first measurement, defaults to 20 seconds ; [env:d1_mini_usermod_dallas_temperature_C] extends = env:d1_mini -build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE -D USERMOD_DALLASTEMPERATURE_CELSIUS +build_flags = ${common.build_flags_esp8266} -D USERMOD_DALLASTEMPERATURE lib_deps = ${env.lib_deps} - milesburton/DallasTemperature@^3.9.0 - OneWire@~2.3.5 + paulstoffregen/OneWire@~2.3.7 +# you may want to use following with ESP32 +; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32 \ No newline at end of file diff --git a/usermods/Temperature/readme.md b/usermods/Temperature/readme.md index c917461a..2657c6c8 100644 --- a/usermods/Temperature/readme.md +++ b/usermods/Temperature/readme.md @@ -7,6 +7,8 @@ May be expanded with support for different sensor types in the future. If temperature sensor is not detected during boot, this usermod will be disabled. +Maintained by @blazoncek + ## Installation Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`. @@ -14,7 +16,7 @@ Copy the example `platformio_override.ini` to the root directory. This file sho ### Define Your Options * `USERMOD_DALLASTEMPERATURE` - enables this user mod wled00/usermods_list.cpp -* `USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT` - number of milliseconds after boot to take first measurement, defaults to 20000 ms +* `USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL` - number of milliseconds between measurements, defaults to 60000 ms (60s) All parameters can be configured at runtime via the Usermods settings page, including pin, temperature in degrees Celsius or Farenheit and measurement interval. @@ -27,7 +29,6 @@ All parameters can be configured at runtime via the Usermods settings page, incl If you are using `platformio_override.ini`, you should be able to refresh the task list and see your custom task, for example `env:d1_mini_usermod_dallas_temperature_C`. - If you are not using `platformio_override.ini`, you might have to uncomment `OneWire@~2.3.5 under` `[common]` section in `platformio.ini`: ```ini @@ -43,8 +44,9 @@ default_envs = d1_mini lib_deps = ... #For Dallas sensor uncomment following line - OneWire@~2.3.5 -... + OneWire@~2.3.7 + # ... or you may want to use following with ESP32 +; https://github.com/blazoncek/OneWire.git # fixes Sensor error on ESP32... ``` ## Change Log @@ -56,3 +58,6 @@ lib_deps = * Report the number of seconds until the first read in the info screen instead of sensor error 2021-04 * Adaptation for runtime configuration. +2023-05 +* Rewrite to conform to newer recommendations. +* Recommended @blazoncek fork of OneWire for ESP32 to avoid Sensor error \ No newline at end of file diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index b55076c7..a15baf87 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -57,343 +57,372 @@ class UsermodTemperature : public Usermod { static const char _parasitePin[]; //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 - float readDallas() { - byte data[9]; - int16_t result; // raw data from sensor - float retVal = -127.0f; - if (oneWire->reset()) { // if reset() fails there are no OneWire devices - oneWire->skip(); // skip ROM - oneWire->write(0xBE); // read (temperature) from EEPROM - oneWire->read_bytes(data, 9); // first 2 bytes contain temperature - #ifdef WLED_DEBUG - if (OneWire::crc8(data,8) != data[8]) { - DEBUG_PRINTLN(F("CRC error reading temperature.")); - for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]); - DEBUG_PRINT(F(" => ")); - DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8)); - } - #endif - switch(sensorFound) { - case 0x10: // DS18S20 has 9-bit precision - result = (data[1] << 8) | data[0]; - retVal = float(result) * 0.5f; - break; - case 0x22: // DS18B20 - case 0x28: // DS1822 - case 0x3B: // DS1825 - case 0x42: // DS28EA00 - result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning - if (data[1] & 0x80) result |= 0xF000; // fix negative value - retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f); - break; - } - } - for (byte i=1; i<9; i++) data[0] &= data[i]; - return data[0]==0xFF ? -127.0f : retVal; - } - - void requestTemperatures() { - DEBUG_PRINTLN(F("Requesting temperature.")); - oneWire->reset(); - oneWire->skip(); // skip ROM - oneWire->write(0x44,parasite); // request new temperature reading - if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET) - lastTemperaturesRequest = millis(); - waitingForConversion = true; - } - - void readTemperature() { - if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) - temperature = readDallas(); - lastMeasurement = millis(); - waitingForConversion = false; - //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266 - DEBUG_PRINT(F("Read temperature ")); - DEBUG_PRINTLN(temperature); - } - - bool findSensor() { - DEBUG_PRINTLN(F("Searching for sensor...")); - uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0}; - // find out if we have DS18xxx sensor attached - oneWire->reset_search(); - delay(10); - while (oneWire->search(deviceAddress)) { - DEBUG_PRINTLN(F("Found something...")); - if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) { - switch (deviceAddress[0]) { - case 0x10: // DS18S20 - case 0x22: // DS18B20 - case 0x28: // DS1822 - case 0x3B: // DS1825 - case 0x42: // DS28EA00 - DEBUG_PRINTLN(F("Sensor found.")); - sensorFound = deviceAddress[0]; - DEBUG_PRINTF("0x%02X\n", sensorFound); - return true; - } - } - } - DEBUG_PRINTLN(F("Sensor NOT found.")); - return false; - } - + float readDallas(); + void requestTemperatures(); + void readTemperature(); + bool findSensor(); #ifndef WLED_DISABLE_MQTT - void publishHomeAssistantAutodiscovery() { - if (!WLED_MQTT_CONNECTED) return; - - char json_str[1024], buf[128]; - size_t payload_size; - StaticJsonDocument<1024> json; - - sprintf_P(buf, PSTR("%s Temperature"), serverDescription); - json[F("name")] = buf; - strcpy(buf, mqttDeviceTopic); - strcat_P(buf, PSTR("/temperature")); - json[F("state_topic")] = buf; - json[F("device_class")] = F("temperature"); - json[F("unique_id")] = escapedMac.c_str(); - json[F("unit_of_measurement")] = F("°C"); - payload_size = serializeJson(json, json_str); - - sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str()); - mqtt->publish(buf, 0, true, json_str, payload_size); - HApublished = true; - } + void publishHomeAssistantAutodiscovery(); #endif public: - void setup() { - int retries = 10; - sensorFound = 0; - temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C - if (enabled) { - // config says we are enabled - DEBUG_PRINTLN(F("Allocating temperature pin...")); - // pin retrieved from cfg.json (readFromConfig()) prior to running setup() - if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) { - oneWire = new OneWire(temperaturePin); - if (oneWire->reset()) { - while (!findSensor() && retries--) { - delay(25); // try to find sensor - } - } - if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) { - pinMode(parasitePin, OUTPUT); - digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) - } else { - parasitePin = -1; - } - } else { - if (temperaturePin >= 0) { - DEBUG_PRINTLN(F("Temperature pin allocation failed.")); - } - temperaturePin = -1; // allocation failed - } - } - lastMeasurement = millis() - readingInterval + 10000; - initDone = true; - } - - void loop() { - if (!enabled || !sensorFound || strip.isUpdating()) return; - - static uint8_t errorCount = 0; - unsigned long now = millis(); - - // check to see if we are due for taking a measurement - // lastMeasurement will not be updated until the conversion - // is complete the the reading is finished - if (now - lastMeasurement < readingInterval) return; - - // we are due for a measurement, if we are not already waiting - // for a conversion to complete, then make a new request for temps - if (!waitingForConversion) { - requestTemperatures(); - return; - } - - // we were waiting for a conversion to complete, have we waited log enough? - if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) { - readTemperature(); - if (getTemperatureC() < -100.0f) { - if (++errorCount > 10) sensorFound = 0; - lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms - return; - } - errorCount = 0; - -#ifndef WLED_DISABLE_MQTT - if (WLED_MQTT_CONNECTED) { - char subuf[64]; - strcpy(subuf, mqttDeviceTopic); - if (temperature > -100.0f) { - // dont publish super low temperature as the graph will get messed up - // the DallasTemperature library returns -127C or -196.6F when problem - // reading the sensor - strcat_P(subuf, PSTR("/temperature")); - mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); - strcat_P(subuf, PSTR("_f")); - mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); - } else { - // publish something else to indicate status? - } - } -#endif - } - } - - /** - * connected() is called every time the WiFi is (re)connected - * Use it to initialize network interfaces - */ - //void connected() {} - -#ifndef WLED_DISABLE_MQTT - /** - * subscribe to MQTT topic if needed - */ - void onMqttConnect(bool sessionPresent) { - //(re)subscribe to required topics - //char subuf[64]; - if (mqttDeviceTopic[0] != 0) { - publishHomeAssistantAutodiscovery(); - } - } -#endif - /* * API calls te enable data exchange between WLED modules */ - inline float getTemperatureC() { - return (float)temperature; + inline float getTemperatureC() { return temperature; } + inline float getTemperatureF() { return temperature * 1.8f + 32.0f; } + float getTemperature(); + const char *getTemperatureUnit(); + uint16_t getId() { return USERMOD_ID_TEMPERATURE; } + + void setup(); + void loop(); + //void connected(); +#ifndef WLED_DISABLE_MQTT + void onMqttConnect(bool sessionPresent); +#endif + //void onUpdateBegin(bool init); + + //bool handleButton(uint8_t b); + //void handleOverlayDraw(); + + void addToJsonInfo(JsonObject& root); + //void addToJsonState(JsonObject &root); + //void readFromJsonState(JsonObject &root); + void addToConfig(JsonObject &root); + bool readFromConfig(JsonObject &root); + + void appendConfigData(); +}; + +//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 +float UsermodTemperature::readDallas() { + byte data[9]; + int16_t result; // raw data from sensor + float retVal = -127.0f; + if (oneWire->reset()) { // if reset() fails there are no OneWire devices + oneWire->skip(); // skip ROM + oneWire->write(0xBE); // read (temperature) from EEPROM + oneWire->read_bytes(data, 9); // first 2 bytes contain temperature + #ifdef WLED_DEBUG + if (OneWire::crc8(data,8) != data[8]) { + DEBUG_PRINTLN(F("CRC error reading temperature.")); + for (byte i=0; i < 9; i++) DEBUG_PRINTF("0x%02X ", data[i]); + DEBUG_PRINT(F(" => ")); + DEBUG_PRINTF("0x%02X\n", OneWire::crc8(data,8)); } - inline float getTemperatureF() { - return (float)temperature * 1.8f + 32; + #endif + switch(sensorFound) { + case 0x10: // DS18S20 has 9-bit precision + result = (data[1] << 8) | data[0]; + retVal = float(result) * 0.5f; + break; + case 0x22: // DS18B20 + case 0x28: // DS1822 + case 0x3B: // DS1825 + case 0x42: // DS28EA00 + result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning + if (data[1] & 0x80) result |= 0xF000; // fix negative value + retVal = float(result) + ((data[0] & 0x08) ? 0.5f : 0.0f); + break; } + } + for (byte i=1; i<9; i++) data[0] &= data[i]; + return data[0]==0xFF ? -127.0f : retVal; +} - /* - * 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. - * Below it is shown how this could be used for e.g. a light sensor - */ - void addToJsonInfo(JsonObject& root) { - // dont add temperature to info if we are disabled - if (!enabled) return; +void UsermodTemperature::requestTemperatures() { + DEBUG_PRINTLN(F("Requesting temperature.")); + oneWire->reset(); + oneWire->skip(); // skip ROM + oneWire->write(0x44,parasite); // request new temperature reading + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, HIGH); // has to happen within 10us (open MOSFET) + lastTemperaturesRequest = millis(); + waitingForConversion = true; +} - JsonObject user = root["u"]; - if (user.isNull()) user = root.createNestedObject("u"); +void UsermodTemperature::readTemperature() { + if (parasite && parasitePin >=0 ) digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) + temperature = readDallas(); + lastMeasurement = millis(); + waitingForConversion = false; + //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266 + DEBUG_PRINT(F("Read temperature ")); + DEBUG_PRINTLN(temperature); +} - JsonArray temp = user.createNestedArray(FPSTR(_name)); - - if (temperature <= -100.0f) { - temp.add(0); - temp.add(F(" Sensor Error!")); - return; +bool UsermodTemperature::findSensor() { + DEBUG_PRINTLN(F("Searching for sensor...")); + uint8_t deviceAddress[8] = {0,0,0,0,0,0,0,0}; + // find out if we have DS18xxx sensor attached + oneWire->reset_search(); + delay(10); + while (oneWire->search(deviceAddress)) { + DEBUG_PRINTLN(F("Found something...")); + if (oneWire->crc8(deviceAddress, 7) == deviceAddress[7]) { + switch (deviceAddress[0]) { + case 0x10: // DS18S20 + case 0x22: // DS18B20 + case 0x28: // DS1822 + case 0x3B: // DS1825 + case 0x42: // DS28EA00 + DEBUG_PRINTLN(F("Sensor found.")); + sensorFound = deviceAddress[0]; + DEBUG_PRINTF("0x%02X\n", sensorFound); + return true; } - - temp.add(degC ? getTemperatureC() : getTemperatureF()); - temp.add(degC ? F("°C") : F("°F")); - - JsonObject sensor = root[F("sensor")]; - if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); - temp = sensor.createNestedArray(F("temp")); - temp.add(degC ? temperature : (float)temperature * 1.8f + 32); - temp.add(degC ? F("°C") : F("°F")); } + } + DEBUG_PRINTLN(F("Sensor NOT found.")); + return false; +} - /** - * 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) - //{ - //} +#ifndef WLED_DISABLE_MQTT +void UsermodTemperature::publishHomeAssistantAutodiscovery() { + if (!WLED_MQTT_CONNECTED) return; - /** - * 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 - * Read "_" from json state and and change settings (i.e. GPIO pin) used. - */ - //void readFromJsonState(JsonObject &root) { - // if (!initDone) return; // prevent crash on boot applyPreset() - //} + char json_str[1024], buf[128]; + size_t payload_size; + StaticJsonDocument<1024> json; - /** - * addToConfig() (called from set.cpp) stores persistent properties to cfg.json - */ - void addToConfig(JsonObject &root) { - // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} - JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - top[FPSTR(_enabled)] = enabled; - top["pin"] = temperaturePin; // usermodparam - top["degC"] = degC; // usermodparam - top[FPSTR(_readInterval)] = readingInterval / 1000; - top[FPSTR(_parasite)] = parasite; - top[FPSTR(_parasitePin)] = parasitePin; - DEBUG_PRINTLN(F("Temperature config saved.")); - } + sprintf_P(buf, PSTR("%s Temperature"), serverDescription); + json[F("name")] = buf; + strcpy(buf, mqttDeviceTopic); + strcat_P(buf, PSTR("/temperature")); + json[F("state_topic")] = buf; + json[F("device_class")] = F("temperature"); + json[F("unique_id")] = escapedMac.c_str(); + json[F("unit_of_measurement")] = F("°C"); + payload_size = serializeJson(json, json_str); - /** - * readFromConfig() is called before setup() to populate properties from values stored in cfg.json - * - * The function should return true if configuration was successfully loaded or false if there was no configuration. - */ - bool readFromConfig(JsonObject &root) { - // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} - int8_t newTemperaturePin = temperaturePin; - DEBUG_PRINT(FPSTR(_name)); + sprintf_P(buf, PSTR("homeassistant/sensor/%s/config"), escapedMac.c_str()); + mqtt->publish(buf, 0, true, json_str, payload_size); + HApublished = true; +} +#endif - JsonObject top = root[FPSTR(_name)]; - if (top.isNull()) { - DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); - return false; - } - - enabled = top[FPSTR(_enabled)] | enabled; - newTemperaturePin = top["pin"] | newTemperaturePin; - degC = top["degC"] | degC; - readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; - readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms - parasite = top[FPSTR(_parasite)] | parasite; - parasitePin = top[FPSTR(_parasitePin)] | parasitePin; - - if (!initDone) { - // first run: reading from cfg.json - temperaturePin = newTemperaturePin; - DEBUG_PRINTLN(F(" config loaded.")); - } else { - DEBUG_PRINTLN(F(" config (re)loaded.")); - // changing paramters from settings page - if (newTemperaturePin != temperaturePin) { - DEBUG_PRINTLN(F("Re-init temperature.")); - // deallocate pin and release memory - delete oneWire; - pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); - temperaturePin = newTemperaturePin; - pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature); - // initialise - setup(); +void UsermodTemperature::setup() { + int retries = 10; + sensorFound = 0; + temperature = -127.0f; // default to -127, DS18B20 only goes down to -50C + if (enabled) { + // config says we are enabled + DEBUG_PRINTLN(F("Allocating temperature pin...")); + // pin retrieved from cfg.json (readFromConfig()) prior to running setup() + if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) { + oneWire = new OneWire(temperaturePin); + if (oneWire->reset()) { + while (!findSensor() && retries--) { + delay(25); // try to find sensor } } - // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[FPSTR(_parasitePin)].isNull(); + if (parasite && pinManager.allocatePin(parasitePin, true, PinOwner::UM_Temperature)) { + pinMode(parasitePin, OUTPUT); + digitalWrite(parasitePin, LOW); // deactivate power (close MOSFET) + } else { + parasitePin = -1; + } + } else { + if (temperaturePin >= 0) { + DEBUG_PRINTLN(F("Temperature pin allocation failed.")); + } + temperaturePin = -1; // allocation failed } + } + lastMeasurement = millis() - readingInterval + 10000; + initDone = true; +} - void appendConfigData() - { - oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str()); - oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field - oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str()); - oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field - } +void UsermodTemperature::loop() { + if (!enabled || !sensorFound || strip.isUpdating()) return; - uint16_t getId() - { - return USERMOD_ID_TEMPERATURE; + static uint8_t errorCount = 0; + unsigned long now = millis(); + + // check to see if we are due for taking a measurement + // lastMeasurement will not be updated until the conversion + // is complete the the reading is finished + if (now - lastMeasurement < readingInterval) return; + + // we are due for a measurement, if we are not already waiting + // for a conversion to complete, then make a new request for temps + if (!waitingForConversion) { + requestTemperatures(); + return; + } + + // we were waiting for a conversion to complete, have we waited log enough? + if (now - lastTemperaturesRequest >= 750 /* 93.75ms per the datasheet but can be up to 750ms */) { + readTemperature(); + if (getTemperatureC() < -100.0f) { + if (++errorCount > 10) sensorFound = 0; + lastMeasurement = now - readingInterval + 300; // force new measurement in 300ms + return; } -}; + errorCount = 0; + +#ifndef WLED_DISABLE_MQTT + if (WLED_MQTT_CONNECTED) { + char subuf[64]; + strcpy(subuf, mqttDeviceTopic); + if (temperature > -100.0f) { + // dont publish super low temperature as the graph will get messed up + // the DallasTemperature library returns -127C or -196.6F when problem + // reading the sensor + strcat_P(subuf, PSTR("/temperature")); + mqtt->publish(subuf, 0, false, String(getTemperatureC()).c_str()); + strcat_P(subuf, PSTR("_f")); + mqtt->publish(subuf, 0, false, String(getTemperatureF()).c_str()); + } else { + // publish something else to indicate status? + } + } +#endif + } +} + +/** + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ +//void UsermodTemperature::connected() {} + +#ifndef WLED_DISABLE_MQTT +/** + * subscribe to MQTT topic if needed + */ +void UsermodTemperature::onMqttConnect(bool sessionPresent) { + //(re)subscribe to required topics + //char subuf[64]; + if (mqttDeviceTopic[0] != 0) { + publishHomeAssistantAutodiscovery(); + } +} +#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. + * Below it is shown how this could be used for e.g. a light sensor + */ +void UsermodTemperature::addToJsonInfo(JsonObject& root) { + // dont add temperature to info if we are disabled + if (!enabled) return; + + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray temp = user.createNestedArray(FPSTR(_name)); + + if (temperature <= -100.0f) { + temp.add(0); + temp.add(F(" Sensor Error!")); + return; + } + + temp.add(getTemperature()); + temp.add(getTemperatureUnit()); + + JsonObject sensor = root[F("sensor")]; + if (sensor.isNull()) sensor = root.createNestedObject(F("sensor")); + temp = sensor.createNestedArray(F("temperature")); + temp.add(getTemperature()); + temp.add(getTemperatureUnit()); +} + +/** + * 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 UsermodTemperature::addToJsonState(JsonObject &root) +//{ +//} + +/** + * 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 + * Read "_" from json state and and change settings (i.e. GPIO pin) used. + */ +//void UsermodTemperature::readFromJsonState(JsonObject &root) { +// if (!initDone) return; // prevent crash on boot applyPreset() +//} + +/** + * addToConfig() (called from set.cpp) stores persistent properties to cfg.json + */ +void UsermodTemperature::addToConfig(JsonObject &root) { + // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} + JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname + top[FPSTR(_enabled)] = enabled; + top["pin"] = temperaturePin; // usermodparam + top["degC"] = degC; // usermodparam + top[FPSTR(_readInterval)] = readingInterval / 1000; + top[FPSTR(_parasite)] = parasite; + top[FPSTR(_parasitePin)] = parasitePin; + DEBUG_PRINTLN(F("Temperature config saved.")); +} + +/** + * readFromConfig() is called before setup() to populate properties from values stored in cfg.json + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. + */ +bool UsermodTemperature::readFromConfig(JsonObject &root) { + // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} + int8_t newTemperaturePin = temperaturePin; + DEBUG_PRINT(FPSTR(_name)); + + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; + } + + enabled = top[FPSTR(_enabled)] | enabled; + newTemperaturePin = top["pin"] | newTemperaturePin; + degC = top["degC"] | degC; + readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; + readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms + parasite = top[FPSTR(_parasite)] | parasite; + parasitePin = top[FPSTR(_parasitePin)] | parasitePin; + + if (!initDone) { + // first run: reading from cfg.json + temperaturePin = newTemperaturePin; + DEBUG_PRINTLN(F(" config loaded.")); + } else { + DEBUG_PRINTLN(F(" config (re)loaded.")); + // changing paramters from settings page + if (newTemperaturePin != temperaturePin) { + DEBUG_PRINTLN(F("Re-init temperature.")); + // deallocate pin and release memory + delete oneWire; + pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); + temperaturePin = newTemperaturePin; + pinManager.deallocatePin(parasitePin, PinOwner::UM_Temperature); + // initialise + setup(); + } + } + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return !top[FPSTR(_parasitePin)].isNull(); +} + +void UsermodTemperature::appendConfigData() { + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasite)).c_str()); + oappend(SET_F("',1,'(if no Vcc connected)');")); // 0 is field type, 1 is actual field + oappend(SET_F("addInfo('")); oappend(String(FPSTR(_name)).c_str()); oappend(SET_F(":")); oappend(String(FPSTR(_parasitePin)).c_str()); + oappend(SET_F("',1,'(for external MOSFET)');")); // 0 is field type, 1 is actual field +} + +float UsermodTemperature::getTemperature() { + return degC ? getTemperatureC() : getTemperatureF(); +} + +const char *UsermodTemperature::getTemperatureUnit() { + return degC ? "°C" : "°F"; +} // strings to reduce flash memory usage (used more than twice) const char UsermodTemperature::_name[] PROGMEM = "Temperature"; diff --git a/usermods/Temperature/usermods_list.cpp b/usermods/Temperature/usermods_list.cpp deleted file mode 100644 index 50dd7816..00000000 --- a/usermods/Temperature/usermods_list.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "wled.h" -/* - * Register your v2 usermods here! - */ - -/* - * Add/uncomment your usermod filename here (and once more below) - * || || || - * \/ \/ \/ - */ -//#include "usermod_v2_example.h" -#ifdef USERMOD_DALLASTEMPERATURE -#include "../usermods/Temperature/usermod_temperature.h" -#endif - -//#include "usermod_v2_empty.h" - -void registerUsermods() -{ - /* - * Add your usermod class name here - * || || || - * \/ \/ \/ - */ - //usermods.add(new MyExampleUsermod()); -#ifdef USERMOD_DALLASTEMPERATURE - usermods.add(new UsermodTemperature()); -#endif - - //usermods.add(new UsermodRenameMe()); -} \ No newline at end of file From cae43e97cd3f6db93059cabb47a1e412afadf162 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 13 May 2023 15:17:49 +0200 Subject: [PATCH 20/22] Corner fix --- wled00/FX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/FX.cpp b/wled00/FX.cpp index e3b6a202..5d0d30ba 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -7510,7 +7510,7 @@ uint16_t mode_2Doctopus() { const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t rows = SEGMENT.virtualHeight(); - const uint8_t mapp = 255 / MAX(cols,rows); + const uint8_t mapp = 180 / MAX(cols,rows); typedef struct { uint8_t angle; @@ -7535,7 +7535,7 @@ uint16_t mode_2Doctopus() { const uint8_t C_Y = rows / 2 + (SEGMENT.custom2 - 128)*rows/255; for (int x = 0; x < cols; x++) { for (int y = 0; y < rows; y++) { - rMap[XY(x, y)].angle = 128 * (atan2f(y - C_Y, x - C_X) / PI); + rMap[XY(x, y)].angle = 40.7436f * atan2f(y - C_Y, x - C_X); // avoid 128*atan2()/PI rMap[XY(x, y)].radius = hypotf(x - C_X, y - C_Y) * mapp; //thanks Sutaburosu } } From 217004c70ced669f6c35cacf2e4fc55c29a570bd Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 14 May 2023 18:18:09 +0200 Subject: [PATCH 21/22] Bugfix. - disbled transitions/crossfade prevented segment off --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 666a24ac..08f68818 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -623,7 +623,7 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col) uint16_t len = length(); uint8_t _bri_t = currentBri(on ? opacity : 0); - if (!_bri_t && !transitional) return; + if (!_bri_t && !transitional && fadeTransition) return; // if _bri_t == 0 && segment is not transitionig && transitions are enabled then save a few CPU cycles if (_bri_t < 255) { byte r = scale8(R(col), _bri_t); byte g = scale8(G(col), _bri_t); From 1c8f349a625d4c8285961b0df3096db0ab421484 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 14 May 2023 20:30:57 +0200 Subject: [PATCH 22/22] Bugfix. - prevent LED flash on realtime end --- wled00/udp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 5b7d15fd..237eb512 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -189,7 +189,7 @@ void realtimeLock(uint32_t timeoutMs, byte md) void exitRealtime() { if (!realtimeMode) return; if (realtimeOverride == REALTIME_OVERRIDE_ONCE) realtimeOverride = REALTIME_OVERRIDE_NONE; - strip.setBrightness(scaledBri(bri)); + strip.setBrightness(scaledBri(bri), true); realtimeTimeout = 0; // cancel realtime mode immediately realtimeMode = REALTIME_MODE_INACTIVE; // inform UI immediately realtimeIP[0] = 0;