Merge branch 'segment-api' into audioreactive-prototype

This commit is contained in:
Blaz Kristan 2022-07-27 21:35:29 +02:00
commit a6f31a577a
53 changed files with 8161 additions and 7297 deletions

6
package-lock.json generated
View File

@ -2067,9 +2067,9 @@
"integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw=="
}, },
"terser": { "terser": {
"version": "4.8.0", "version": "4.8.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
"source-map": "~0.6.1", "source-map": "~0.6.1",

View File

@ -263,120 +263,60 @@ writeChunks(
name: "PAGE_settings", name: "PAGE_settings",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=0\"></script>"
)
}, },
{ {
file: "settings_wifi.htm", file: "settings_wifi.htm",
name: "PAGE_settings_wifi", name: "PAGE_settings_wifi",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=1\"></script>"
)
}, },
{ {
file: "settings_leds.htm", file: "settings_leds.htm",
name: "PAGE_settings_leds", name: "PAGE_settings_leds",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=2\"></script>"
)
}, },
{ {
file: "settings_dmx.htm", file: "settings_dmx.htm",
name: "PAGE_settings_dmx", name: "PAGE_settings_dmx",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=7\"></script>"
)
}, },
{ {
file: "settings_ui.htm", file: "settings_ui.htm",
name: "PAGE_settings_ui", name: "PAGE_settings_ui",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=3\"></script>"
)
}, },
{ {
file: "settings_sync.htm", file: "settings_sync.htm",
name: "PAGE_settings_sync", name: "PAGE_settings_sync",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=4\"></script>"
)
}, },
{ {
file: "settings_time.htm", file: "settings_time.htm",
name: "PAGE_settings_time", name: "PAGE_settings_time",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=5\"></script>"
)
}, },
{ {
file: "settings_sec.htm", file: "settings_sec.htm",
name: "PAGE_settings_sec", name: "PAGE_settings_sec",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=6\"></script>"
)
}, },
{ {
file: "settings_um.htm", file: "settings_um.htm",
name: "PAGE_settings_um", name: "PAGE_settings_um",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=8\"></script>"
)
}, },
{ {
file: "settings_2D.htm", file: "settings_2D.htm",
name: "PAGE_settings_2D", name: "PAGE_settings_2D",
method: "gzip", method: "gzip",
filter: "html-minify", filter: "html-minify",
mangle: (str) =>
str
.replace(
/function GetV().*\<\/script\>/gms,
"</script><script src=\"/settings/s.js?p=10\"></script>"
)
}, },
{ {
file: "settings_pin.htm", file: "settings_pin.htm",

View File

@ -103,25 +103,24 @@ class Animated_Staircase : public Usermod {
void updateSegments() { void updateSegments() {
mainSegmentId = strip.getMainSegmentId(); mainSegmentId = strip.getMainSegmentId();
WS2812FX::Segment* segments = strip.getSegments(); for (int i = 0; i < strip.getSegmentsNum(); i++) {
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { Segment &seg = strip.getSegment(i);
if (!segments->isActive()) { if (!seg.isActive()) {
maxSegmentId = i - 1; maxSegmentId = i - 1;
break; break;
} }
if (i >= onIndex && i < offIndex) { if (i >= onIndex && i < offIndex) {
segments->setOption(SEG_OPTION_ON, 1, i); seg.setOption(SEG_OPTION_ON, true);
// We may need to copy mode and colors from segment 0 to make sure // We may need to copy mode and colors from segment 0 to make sure
// changes are propagated even when the config is changed during a wipe // changes are propagated even when the config is changed during a wipe
// segments->mode = mainsegment.mode; // segments->mode = mainsegment.mode;
// segments->colors[0] = mainsegment.colors[0]; // segments->colors[0] = mainsegment.colors[0];
} else { } else {
segments->setOption(SEG_OPTION_ON, 0, i); seg.setOption(SEG_OPTION_ON, false);
} }
// Always mark segments as "transitional", we are animating the staircase // Always mark segments as "transitional", we are animating the staircase
segments->setOption(SEG_OPTION_TRANSITIONAL, 1, i); seg.setOption(SEG_OPTION_TRANSITIONAL, true);
} }
colorUpdated(CALL_MODE_DIRECT_CHANGE); colorUpdated(CALL_MODE_DIRECT_CHANGE);
} }
@ -290,13 +289,13 @@ class Animated_Staircase : public Usermod {
} }
} else { } else {
// Restore segment options // Restore segment options
WS2812FX::Segment* segments = strip.getSegments(); for (int i = 0; i < strip.getSegmentsNum(); i++) {
for (int i = 0; i < MAX_NUM_SEGMENTS; i++, segments++) { Segment &seg = strip.getSegment(i);
if (!segments->isActive()) { if (!seg.isActive()) {
maxSegmentId = i - 1; maxSegmentId = i - 1;
break; break;
} }
segments->setOption(SEG_OPTION_ON, 1, i); seg.setOption(SEG_OPTION_ON, true);
} }
colorUpdated(CALL_MODE_DIRECT_CHANGE); colorUpdated(CALL_MODE_DIRECT_CHANGE);
DEBUG_PRINTLN(F("Animated Staircase disabled.")); DEBUG_PRINTLN(F("Animated Staircase disabled."));

View File

@ -1,40 +1,90 @@
Hello! I have written a v2 usermod for the BME280/BMP280 sensor based on the [existing v1 usermod](https://github.com/Aircoookie/WLED/blob/master/usermods/Wemos_D1_mini%2BWemos32_mini_shield/usermod_bme280.cpp). It is not just a refactor, there are many changes which I made to fit my use case, and I hope they will fit the use cases of others as well! Most notably, this usermod is *just* for the BME280 and does not control a display like in the v1 usermod designed for the WeMos shield. # Usermod BME280
This Usermod is designed to read a `BME280` or `BMP280` sensor and output the following:
- Temperature
- Humidity (`BME280` only)
- Pressure
- Heat Index (`BME280` only)
- Dew Point (`BME280` only)
- Requires libraries `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280)) and `Wire`. Please add these under `lib_deps` in your `platform.ini` (or `platform_override.ini`). Configuration is all completed via the Usermod menu. There are no settings to set in code! The following settings can be configured in the Usermod Menu:
- Data is published over MQTT so make sure you've enabled the MQTT sync interface. - Temperature Decimals (number of decimal places to output)
- Humidity Decimals
- Pressure Decimals
- Temperature Interval (how many seconds between reads of temperature and humidity)
- Pressure Interval
- Publish Always (turn off to only publish changes, on to publish whether or not value changed)
- Use Celsius (turn off to use Farenheit)
- Home Assistant Discovery (turn on to sent MQTT Discovery entries for Home Assistant)
- SCL/SDA GPIO Pins
Dependencies
- Libraries
- `BME280@~3.0.0` (by [finitespace](https://github.com/finitespace/BME280))
- `Wire`
- These must be added under `lib_deps` in your `platform.ini` (or `platform_override.ini`).
- Data is published over MQTT - make sure you've enabled the MQTT sync interface.
- This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages! - This usermod also writes to serial (GPIO1 on ESP8266). Please make sure nothing else listening on the serial TX pin of your board will get confused by log messages!
To enable, compile with `USERMOD_BME280` defined (i.e. `platformio_override.ini`) In addition to outputting via MQTT, you can read the values from the Info Screen on the dashboard page of the device's web interface.
Methods also exist to read the read/calculated values from other WLED modules through code.
- `getTemperatureC()`
- `getTemperatureF()`
- `getHumidity()`
- `getPressure()`
- `getDewPointC()`
- `getDewPointF()`
- `getHeatIndexC()`
- `getHeatIndexF()`
# Complilation
To enable, compile with `USERMOD_BME280` defined (e.g. in `platformio_override.ini`)
```ini ```ini
[env:usermod_bme280_d1_mini]
extends = env:d1_mini
build_flags = build_flags =
${common.build_flags_esp8266} ${common.build_flags_esp8266}
-D USERMOD_BME280 -D USERMOD_BME280
``` lib_deps =
or define `USERMOD_BME280` in `my_config.h` ${esp8266.lib_deps}
```c++ BME280@~3.0.0
#define USERMOD_BME280 Wire
``` ```
Changes include:
- Adjustable measure intervals
- Temperature and pressure have separate intervals due to pressure not frequently changing at any constant altitude
- Adjustment of number of decimal places in published sensor values
- Separate adjustment for temperature, humidity and pressure values
- Values are rounded to the specified number of decimal places
- Pressure measured in units of hPa instead of Pa
- Calculation of heat index (apparent temperature) and dew point
- These, along with humidity measurements, are disabled if the sensor is a BMP280
- 16x oversampling of sensor during measurement
- Values are only published if they are different from the previous value
- Values are published on startup (continually until the MQTT broker acknowledges a successful publication)
Adjustments are made through preprocessor definitions at the start of the class definition. # MQTT
MQTT topics are as follows (`<deviceTopic>` is set in MQTT section of Sync Setup menu):
MQTT topics are as follows:
Measurement type | MQTT topic Measurement type | MQTT topic
--- | --- --- | ---
Temperature | `<deviceTopic>/temperature` Temperature | `<deviceTopic>/temperature`
Humidity | `<deviceTopic>/humidity` Humidity | `<deviceTopic>/humidity`
Pressure | `<deviceTopic>/pressure` Pressure | `<deviceTopic>/pressure`
Heat index | `<deviceTopic>/heat_index` Heat index | `<deviceTopic>/heat_index`
Dew point | `<deviceTopic>/dew_point` Dew point | `<deviceTopic>/dew_point`
If you are using Home Assistant, and `Home Assistant Discovery` is turned on, Home Assistant should automatically detect a new device, provided you have the MQTT integration installed. The device is seperate from the main WLED device and will contain sensors for Pressure, Humidity, Temperature, Dew Point and Heat Index.
# Revision History
Jul 2022
- Added Home Assistant Discovery
- Added API interface to output data
- Removed compile-time variables
- Added usermod menu interface
- Added value outputs to info screen
- Updated `readme.md`
- Registered usermod
- Implemented PinManager for usermod
- Implemented reallocation of pins without reboot
Apr 2021
- Added `Publish Always` option
Dec 2020
- Ported to V2 Usermod format
- Customisable `measure intervals`
- Customisable number of `decimal places` in published sensor values
- Pressure measured in units of hPa instead of Pa
- Calculation of heat index (apparent temperature) and dew point
- `16x oversampling` of sensor during measurement
- Values only published if they are different from the previous value

View File

@ -1,3 +1,6 @@
// force the compiler to show a warning to confirm that this file is included
#warning **** Included USERMOD_BME280 version 2.0 ****
#pragma once #pragma once
#include "wled.h" #include "wled.h"
@ -9,43 +12,30 @@
class UsermodBME280 : public Usermod class UsermodBME280 : public Usermod
{ {
private: private:
// User-defined configuration
#define Celsius // Show temperature mesaurement in Celcius. Comment out for Fahrenheit // NOTE: Do not implement any compile-time variables, anything the user needs to configure
#define TemperatureDecimals 1 // Number of decimal places in published temperaure values // should be configurable from the Usermod menu using the methods below
#define HumidityDecimals 2 // Number of decimal places in published humidity values // key settings set via usermod menu
#define PressureDecimals 2 // Number of decimal places in published pressure values unsigned long TemperatureDecimals = 0; // Number of decimal places in published temperaure values
#define TemperatureInterval 5 // Interval to measure temperature (and humidity, dew point if available) in seconds unsigned long HumidityDecimals = 0; // Number of decimal places in published humidity values
#define PressureInterval 300 // Interval to measure pressure in seconds unsigned long PressureDecimals = 0; // Number of decimal places in published pressure values
#define PublishAlways 0 // Publish values even when they have not changed unsigned long TemperatureInterval = 5; // Interval to measure temperature (and humidity, dew point if available) in seconds
unsigned long PressureInterval = 300; // Interval to measure pressure in seconds
bool PublishAlways = false; // Publish values even when they have not changed
bool UseCelsius = true; // Use Celsius for Reporting
bool HomeAssistantDiscovery = false; // Publish Home Assistant Device Information
// Sanity checks // set the default pins based on the architecture, these get overridden by Usermod menu settings
#if !defined(TemperatureDecimals) || TemperatureDecimals < 0 #ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
#define TemperatureDecimals 0 #define HW_PIN_SCL 22
#endif #define HW_PIN_SDA 21
#if !defined(HumidityDecimals) || HumidityDecimals < 0 #else // ESP8266 boards
#define HumidityDecimals 0 #define HW_PIN_SCL 5
#endif #define HW_PIN_SDA 4
#if !defined(PressureDecimals) || PressureDecimals < 0 //uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#define PressureDecimals 0 #endif
#endif int8_t ioPin[2] = {HW_PIN_SCL, HW_PIN_SDA}; // I2C pins: SCL, SDA...defaults to Arch hardware pins but overridden at setup()
#if !defined(TemperatureInterval) || TemperatureInterval < 0 bool initDone = false;
#define TemperatureInterval 1
#endif
#if !defined(PressureInterval) || PressureInterval < 0
#define PressureInterval TemperatureInterval
#endif
#if !defined(PublishAlways)
#define PublishAlways 0
#endif
#ifdef ARDUINO_ARCH_ESP32 // ESP32 boards
uint8_t SCL_PIN = 22;
uint8_t SDA_PIN = 21;
#else // ESP8266 boards
uint8_t SCL_PIN = 5;
uint8_t SDA_PIN = 4;
//uint8_t RST_PIN = 16; // Uncoment for Heltec WiFi-Kit-8
#endif
// BME280 sensor settings // BME280 sensor settings
BME280I2C::Settings settings{ BME280I2C::Settings settings{
@ -75,6 +65,7 @@ private:
float sensorHeatIndex; float sensorHeatIndex;
float sensorDewPoint; float sensorDewPoint;
float sensorPressure; float sensorPressure;
String tempScale;
// Track previous sensor values // Track previous sensor values
float lastTemperature; float lastTemperature;
float lastHumidity; float lastHumidity;
@ -82,43 +73,122 @@ private:
float lastDewPoint; float lastDewPoint;
float lastPressure; float lastPressure;
// MQTT topic strings for publishing Home Assistant discovery topics
bool mqttInitialized = false;
String mqttTemperatureTopic = "";
String mqttHumidityTopic = "";
String mqttPressureTopic = "";
String mqttHeatIndexTopic = "";
String mqttDewPointTopic = "";
// Store packet IDs of MQTT publications // Store packet IDs of MQTT publications
uint16_t mqttTemperaturePub = 0; uint16_t mqttTemperaturePub = 0;
uint16_t mqttPressurePub = 0; uint16_t mqttPressurePub = 0;
// Read the BME280/BMP280 Sensor (which one runs depends on whether Celsius or Farenheit being set in Usermod Menu)
void UpdateBME280Data(int SensorType) void UpdateBME280Data(int SensorType)
{ {
float _temperature, _humidity, _pressure; float _temperature, _humidity, _pressure;
#ifdef Celsius
if (UseCelsius) {
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius); EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Celsius);
#else BME280::PresUnit presUnit(BME280::PresUnit_hPa);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
sensorTemperature = _temperature;
sensorHumidity = _humidity;
sensorPressure = _pressure;
tempScale = "°C";
if (sensorType == 1)
{
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
}
} else {
BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit); BME280::TempUnit tempUnit(BME280::TempUnit_Fahrenheit);
EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit); EnvironmentCalculations::TempUnit envTempUnit(EnvironmentCalculations::TempUnit_Fahrenheit);
#endif BME280::PresUnit presUnit(BME280::PresUnit_hPa);
BME280::PresUnit presUnit(BME280::PresUnit_hPa);
bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit); bme.read(_pressure, _temperature, _humidity, tempUnit, presUnit);
sensorTemperature = _temperature; sensorTemperature = _temperature;
sensorHumidity = _humidity; sensorHumidity = _humidity;
sensorPressure = _pressure; sensorPressure = _pressure;
if (sensorType == 1) tempScale = "°F";
{ if (sensorType == 1)
sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit); {
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit); sensorHeatIndex = EnvironmentCalculations::HeatIndex(_temperature, _humidity, envTempUnit);
sensorDewPoint = EnvironmentCalculations::DewPoint(_temperature, _humidity, envTempUnit);
}
} }
} }
// Procedure to define all MQTT discovery Topics
void _mqttInitialize()
{
mqttTemperatureTopic = String(mqttDeviceTopic) + F("/temperature");
mqttPressureTopic = String(mqttDeviceTopic) + F("/pressure");
mqttHumidityTopic = String(mqttDeviceTopic) + F("/humidity");
mqttHeatIndexTopic = String(mqttDeviceTopic) + F("/heat_index");
mqttDewPointTopic = String(mqttDeviceTopic) + F("/dew_point");
if (HomeAssistantDiscovery) {
_createMqttSensor(F("Temperature"), mqttTemperatureTopic, F("temperature"), tempScale);
_createMqttSensor(F("Pressure"), mqttPressureTopic, F("pressure"), F("hPa"));
_createMqttSensor(F("Humidity"), mqttHumidityTopic, F("humidity"), F("%"));
_createMqttSensor(F("HeatIndex"), mqttHeatIndexTopic, F("temperature"), tempScale);
_createMqttSensor(F("DewPoint"), mqttDewPointTopic, F("temperature"), tempScale);
}
}
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void _createMqttSensor(const String &name, const String &topic, const String &deviceClass, const String &unitOfMeasurement)
{
String t = String(F("homeassistant/sensor/")) + mqttClientID + F("/") + name + F("/config");
StaticJsonDocument<600> doc;
doc[F("name")] = String(serverDescription) + " " + name;
doc[F("state_topic")] = topic;
doc[F("unique_id")] = String(mqttClientID) + name;
if (unitOfMeasurement != "")
doc[F("unit_of_measurement")] = unitOfMeasurement;
if (deviceClass != "")
doc[F("device_class")] = deviceClass;
doc[F("expire_after")] = 1800;
JsonObject device = doc.createNestedObject(F("device")); // attach the sensor to the same device
device[F("name")] = serverDescription;
device[F("identifiers")] = "wled-sensor-" + String(mqttClientID);
device[F("manufacturer")] = F("WLED");
device[F("model")] = F("FOSS");
device[F("sw_version")] = versionString;
String temp;
serializeJson(doc, temp);
DEBUG_PRINTLN(t);
DEBUG_PRINTLN(temp);
mqtt->publish(t.c_str(), 0, true, temp.c_str());
}
public: public:
void setup() void setup()
{ {
Wire.begin(SDA_PIN, SCL_PIN); bool HW_Pins_Used = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); // note whether architecture-based hardware SCL/SDA pins used
PinOwner po = PinOwner::UM_BME280; // defaults to being pinowner for SCL/SDA pins
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; // allocate pins
if (HW_Pins_Used) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
if (!pinManager.allocateMultiplePins(pins, 2, po)) { sensorType=0; return; }
Wire.begin(ioPin[1], ioPin[0]);
if (!bme.begin()) if (!bme.begin())
{ {
sensorType = 0; sensorType = 0;
Serial.println("Could not find BME280I2C sensor!"); DEBUG_PRINTLN(F("Could not find BME280I2C sensor!"));
} }
else else
{ {
@ -126,24 +196,25 @@ public:
{ {
case BME280::ChipModel_BME280: case BME280::ChipModel_BME280:
sensorType = 1; sensorType = 1;
Serial.println("Found BME280 sensor! Success."); DEBUG_PRINTLN(F("Found BME280 sensor! Success."));
break; break;
case BME280::ChipModel_BMP280: case BME280::ChipModel_BMP280:
sensorType = 2; sensorType = 2;
Serial.println("Found BMP280 sensor! No Humidity available."); DEBUG_PRINTLN(F("Found BMP280 sensor! No Humidity available."));
break; break;
default: default:
sensorType = 0; sensorType = 0;
Serial.println("Found UNKNOWN sensor! Error!"); DEBUG_PRINTLN(F("Found UNKNOWN sensor! Error!"));
} }
} }
initDone=true;
} }
void loop() void loop()
{ {
// BME280 sensor MQTT publishing // BME280 sensor MQTT publishing
// Check if sensor present and MQTT Connected, otherwise it will crash the MCU // Check if sensor present and MQTT Connected, otherwise it will crash the MCU
if (sensorType != 0 && mqtt != nullptr) if (sensorType != 0 && WLED_MQTT_CONNECTED)
{ {
// Timer to fetch new temperature, humidity and pressure data at intervals // Timer to fetch new temperature, humidity and pressure data at intervals
timer = millis(); timer = millis();
@ -154,9 +225,15 @@ public:
UpdateBME280Data(sensorType); UpdateBME280Data(sensorType);
float temperature = roundf(sensorTemperature * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); float temperature = roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
float humidity, heatIndex, dewPoint; float humidity, heatIndex, dewPoint;
if (WLED_MQTT_CONNECTED && !mqttInitialized)
{
_mqttInitialize();
mqttInitialized = true;
}
// If temperature has changed since last measure, create string populated with device topic // If temperature has changed since last measure, create string populated with device topic
// from the UI and values read from sensor, then publish to broker // from the UI and values read from sensor, then publish to broker
if (temperature != lastTemperature || PublishAlways) if (temperature != lastTemperature || PublishAlways)
@ -169,25 +246,25 @@ public:
if (sensorType == 1) // Only if sensor is a BME280 if (sensorType == 1) // Only if sensor is a BME280
{ {
humidity = roundf(sensorHumidity * pow(10, HumidityDecimals)) / pow(10, HumidityDecimals); humidity = roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals);
heatIndex = roundf(sensorHeatIndex * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); heatIndex = roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
dewPoint = roundf(sensorDewPoint * pow(10, TemperatureDecimals)) / pow(10, TemperatureDecimals); dewPoint = roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
if (humidity != lastHumidity || PublishAlways) if (humidity != lastHumidity || PublishAlways)
{ {
String topic = String(mqttDeviceTopic) + "/humidity"; String topic = String(mqttDeviceTopic) + F("/humidity");
mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str()); mqtt->publish(topic.c_str(), 0, false, String(humidity, HumidityDecimals).c_str());
} }
if (heatIndex != lastHeatIndex || PublishAlways) if (heatIndex != lastHeatIndex || PublishAlways)
{ {
String topic = String(mqttDeviceTopic) + "/heat_index"; String topic = String(mqttDeviceTopic) + F("/heat_index");
mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str()); mqtt->publish(topic.c_str(), 0, false, String(heatIndex, TemperatureDecimals).c_str());
} }
if (dewPoint != lastDewPoint || PublishAlways) if (dewPoint != lastDewPoint || PublishAlways)
{ {
String topic = String(mqttDeviceTopic) + "/dew_point"; String topic = String(mqttDeviceTopic) + F("/dew_point");
mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str()); mqtt->publish(topic.c_str(), 0, false, String(dewPoint, TemperatureDecimals).c_str());
} }
@ -201,11 +278,11 @@ public:
{ {
lastPressureMeasure = timer; lastPressureMeasure = timer;
float pressure = roundf(sensorPressure * pow(10, PressureDecimals)) / pow(10, PressureDecimals); float pressure = roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals);
if (pressure != lastPressure || PublishAlways) if (pressure != lastPressure || PublishAlways)
{ {
String topic = String(mqttDeviceTopic) + "/pressure"; String topic = String(mqttDeviceTopic) + F("/pressure");
mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str()); mqttPressurePub = mqtt->publish(topic.c_str(), 0, true, String(pressure, PressureDecimals).c_str());
} }
@ -213,4 +290,173 @@ public:
} }
} }
} }
/*
* API calls te enable data exchange between WLED modules
*/
inline float getTemperatureC() {
if (UseCelsius) {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}
inline float getTemperatureF() {
if (UseCelsius) {
return ((float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
inline float getHumidity() {
return (float)roundf(sensorHumidity * powf(10, HumidityDecimals));
}
inline float getPressure() {
return (float)roundf(sensorPressure * powf(10, PressureDecimals));
}
inline float getDewPointC() {
if (UseCelsius) {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}
inline float getDewPointF() {
if (UseCelsius) {
return ((float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
inline float getHeatIndexC() {
if (UseCelsius) {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
} else {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) * 1.8f + 32;
}
}inline float getHeatIndexF() {
if (UseCelsius) {
return ((float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals) -32) * 0.56f;
} else {
return (float)roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals);
}
}
// Publish Sensor Information to Info Page
void addToJsonInfo(JsonObject &root)
{
JsonObject user = root[F("u")];
if (user.isNull()) user = root.createNestedObject(F("u"));
if (sensorType==0) //No Sensor
{
// if we sensor not detected, let the user know
JsonArray temperature_json = user.createNestedArray(F("BME/BMP280 Sensor"));
temperature_json.add(F("Not Found"));
}
else if (sensorType==2) //BMP280
{
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)));
temperature_json.add(tempScale);
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa"));
}
else if (sensorType==1) //BME280
{
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray humidity_json = user.createNestedArray(F("Humidity"));
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
JsonArray heatindex_json = user.createNestedArray(F("Heat Index"));
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
temperature_json.add(tempScale);
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)));
humidity_json.add(F("%"));
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)));
pressure_json.add(F("hPa"));
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
heatindex_json.add(tempScale);
dewpoint_json.add(roundf(sensorDewPoint * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
dewpoint_json.add(tempScale);
}
return;
}
// Save Usermod Config Settings
void addToConfig(JsonObject& root)
{
JsonObject top = root.createNestedObject(F("BME280/BMP280"));
top[F("TemperatureDecimals")] = TemperatureDecimals;
top[F("HumidityDecimals")] = HumidityDecimals;
top[F("PressureDecimals")] = PressureDecimals;
top[F("TemperatureInterval")] = TemperatureInterval;
top[F("PressureInterval")] = PressureInterval;
top[F("PublishAlways")] = PublishAlways;
top[F("UseCelsius")] = UseCelsius;
top[F("HomeAssistantDiscovery")] = HomeAssistantDiscovery;
JsonArray io_pin = top.createNestedArray(F("pin"));
for (byte i=0; i<2; i++) io_pin.add(ioPin[i]);
top[F("help4Pins")] = F("SCL,SDA"); // help for Settings page
DEBUG_PRINTLN(F("BME280 config saved."));
}
// Read Usermod Config Settings
bool readFromConfig(JsonObject& root)
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
int8_t newPin[2]; for (byte i=0; i<2; i++) newPin[i] = ioPin[i]; // prepare to note changed pins
JsonObject top = root[F("BME280/BMP280")];
if (top.isNull()) {
DEBUG_PRINT(F("BME280/BMP280"));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
bool configComplete = !top.isNull();
// A 3-argument getJsonValue() assigns the 3rd argument as a default value if the Json value is missing
configComplete &= getJsonValue(top[F("TemperatureDecimals")], TemperatureDecimals, 1);
configComplete &= getJsonValue(top[F("HumidityDecimals")], HumidityDecimals, 0);
configComplete &= getJsonValue(top[F("PressureDecimals")], PressureDecimals, 0);
configComplete &= getJsonValue(top[F("TemperatureInterval")], TemperatureInterval, 30);
configComplete &= getJsonValue(top[F("PressureInterval")], PressureInterval, 30);
configComplete &= getJsonValue(top[F("PublishAlways")], PublishAlways, false);
configComplete &= getJsonValue(top[F("UseCelsius")], UseCelsius, true);
configComplete &= getJsonValue(top[F("HomeAssistantDiscovery")], HomeAssistantDiscovery, false);
for (byte i=0; i<2; i++) configComplete &= getJsonValue(top[F("pin")][i], newPin[i], ioPin[i]);
DEBUG_PRINT(FPSTR(F("BME280/BMP280")));
if (!initDone) {
// first run: reading from cfg.json
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
DEBUG_PRINTLN(F(" config loaded."));
} else {
DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page
bool pinsChanged = false;
for (byte i=0; i<2; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } // check if any pins changed
if (pinsChanged) { //if pins changed, deallocate old pins and allocate new ones
PinOwner po = PinOwner::UM_BME280;
if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins
pinManager.deallocateMultiplePins((const uint8_t *)ioPin, 2, po); // deallocate pins
for (byte i=0; i<2; i++) ioPin[i] = newPin[i];
setup();
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[F("pin")].isNull();
}
return configComplete;
}
uint16_t getId() {
return USERMOD_ID_BME280;
}
}; };

View File

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

View File

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

View File

@ -132,7 +132,7 @@ static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMP
static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE); static arduinoFFT FFT = arduinoFFT(vReal, vImag, samplesFFT, SAMPLE_RATE);
#endif #endif
static TaskHandle_t FFT_Task; static TaskHandle_t FFT_Task = nullptr;
float fftAddAvg(int from, int to) { float fftAddAvg(int from, int to) {
float result = 0.0f; float result = 0.0f;
@ -1150,24 +1150,26 @@ class AudioReactive : public Usermod {
uiDomString += F("</button>"); uiDomString += F("</button>");
infoArr.add(uiDomString); infoArr.add(uiDomString);
infoArr = user.createNestedArray(F("Input level")); if (enabled) {
uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({"); infoArr = user.createNestedArray(F("Input level"));
uiDomString += FPSTR(_name); uiDomString = F("<div class=\"slider\"><div class=\"sliderwrap il\"><input class=\"noslide\" onchange=\"requestJson({");
uiDomString += F(":{"); uiDomString += FPSTR(_name);
uiDomString += FPSTR(_inputLvl); uiDomString += F(":{");
uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value="); uiDomString += FPSTR(_inputLvl);
uiDomString += inputLevel; uiDomString += F(":parseInt(this.value)}});\" oninput=\"updateTrail(this);\" max=255 min=0 type=\"range\" value=");
uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output> uiDomString += inputLevel;
infoArr.add(uiDomString); uiDomString += F(" /><div class=\"sliderdisplay\"></div></div></div>"); //<output class=\"sliderbubble\"></output>
infoArr.add(uiDomString);
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
infoArr = user.createNestedArray(F("Sampling time")); infoArr = user.createNestedArray(F("Sampling time"));
infoArr.add(sampleTime); infoArr.add(sampleTime);
infoArr.add("ms"); infoArr.add("ms");
infoArr = user.createNestedArray(F("FFT time")); infoArr = user.createNestedArray(F("FFT time"));
infoArr.add(fftTime-sampleTime); infoArr.add(fftTime-sampleTime);
infoArr.add("ms"); infoArr.add("ms");
#endif #endif
}
} }
@ -1175,12 +1177,15 @@ class AudioReactive : public Usermod {
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * 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 * Values in the state object may be modified by connected clients
*/ */
/*
void addToJsonState(JsonObject& root) void addToJsonState(JsonObject& root)
{ {
//root["user0"] = userVar0; if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject usermod = root[FPSTR(_name)];
if (usermod.isNull()) {
usermod = root.createNestedObject(FPSTR(_name));
}
usermod["on"] = enabled;
} }
*/
/* /*
@ -1264,8 +1269,7 @@ class AudioReactive : public Usermod {
JsonObject sync = top.createNestedObject("sync"); JsonObject sync = top.createNestedObject("sync");
sync[F("port")] = audioSyncPort; sync[F("port")] = audioSyncPort;
sync[F("send")] = (bool) (audioSyncEnabled & 0x01); sync[F("mode")] = audioSyncEnabled;
sync[F("receive")] = (bool) (audioSyncEnabled & 0x02);
} }
@ -1305,13 +1309,8 @@ class AudioReactive : public Usermod {
configComplete &= getJsonValue(top["cfg"][F("gain")], sampleGain); configComplete &= getJsonValue(top["cfg"][F("gain")], sampleGain);
configComplete &= getJsonValue(top["cfg"][F("AGC")], soundAgc); configComplete &= getJsonValue(top["cfg"][F("AGC")], soundAgc);
configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort); configComplete &= getJsonValue(top["sync"][F("port")], audioSyncPort);
configComplete &= getJsonValue(top["sync"][F("mode")], audioSyncEnabled);
bool send = audioSyncEnabled & 0x01;
bool receive = audioSyncEnabled & 0x02;
configComplete &= getJsonValue(top["sync"][F("send")], send);
configComplete &= getJsonValue(top["sync"][F("receive")], receive);
audioSyncEnabled = send | (receive << 1);
return configComplete; return configComplete;
} }
@ -1331,6 +1330,10 @@ class AudioReactive : public Usermod {
oappend(SET_F("addOption(dd,'Normal',1);")); oappend(SET_F("addOption(dd,'Normal',1);"));
oappend(SET_F("addOption(dd,'Vivid',2);")); oappend(SET_F("addOption(dd,'Vivid',2);"));
oappend(SET_F("addOption(dd,'Lazy',3);")); oappend(SET_F("addOption(dd,'Lazy',3);"));
oappend(SET_F("dd=addDropdown('AudioReactive','sync:mode');"));
oappend(SET_F("addOption(dd,'Off',0);"));
oappend(SET_F("addOption(dd,'Send',1);"));
oappend(SET_F("addOption(dd,'Receive',2);"));
oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'<i>requires reboot!</i>');")); // 0 is field type, 1 is actual field oappend(SET_F("addInfo('AudioReactive:digitalmic:type',1,'<i>requires reboot!</i>');")); // 0 is field type, 1 is actual field
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'I2S SD');")); oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',0,'I2S SD');"));
oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'I2S WS');")); oappend(SET_F("addInfo('AudioReactive:digitalmic:pin[]',1,'I2S WS');"));

View File

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

View File

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

View File

@ -251,6 +251,7 @@ private:
* Sort either the modes or the palettes using quicksort. * Sort either the modes or the palettes using quicksort.
*/ */
void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) { void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
if (!modeNames) return;
listBeingSorted = modeNames; listBeingSorted = modeNames;
qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp); qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
listBeingSorted = nullptr; listBeingSorted = nullptr;
@ -527,13 +528,13 @@ public:
effectCurrent = modes_alpha_indexes[effectCurrentIndex]; effectCurrent = modes_alpha_indexes[effectCurrentIndex];
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
strip.setMode(i, effectCurrent); strip.setMode(i, effectCurrent);
} }
} else { } else {
//WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); //Segment& seg = strip.getSegment(strip.getMainSegmentId());
strip.setMode(strip.getMainSegmentId(), effectCurrent); strip.setMode(strip.getMainSegmentId(), effectCurrent);
} }
lampUdated(); lampUdated();
@ -555,13 +556,13 @@ public:
effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0); effectSpeed = max(min((increase ? effectSpeed+fadeAmount : effectSpeed-fadeAmount), 255), 0);
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.speed = effectSpeed; seg.speed = effectSpeed;
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.speed = effectSpeed; seg.speed = effectSpeed;
} }
lampUdated(); lampUdated();
@ -583,13 +584,13 @@ public:
effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0); effectIntensity = max(min((increase ? effectIntensity+fadeAmount : effectIntensity-fadeAmount), 255), 0);
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.intensity = effectIntensity; seg.intensity = effectIntensity;
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.intensity = effectIntensity; seg.intensity = effectIntensity;
} }
lampUdated(); lampUdated();
@ -612,22 +613,23 @@ public:
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
uint8_t id = strip.getFirstSelectedSegId(); uint8_t id = strip.getFirstSelectedSegId();
Segment& sid = strip.getSegment(id);
switch (par) { switch (par) {
case 3: val = strip.getSegment(id).custom3 = max(min((increase ? strip.getSegment(id).custom3+fadeAmount : strip.getSegment(id).custom3-fadeAmount), 255), 0); break; case 3: val = sid.custom3 = max(min((increase ? sid.custom3+fadeAmount : sid.custom3-fadeAmount), 255), 0); break;
case 2: val = strip.getSegment(id).custom2 = max(min((increase ? strip.getSegment(id).custom2+fadeAmount : strip.getSegment(id).custom2-fadeAmount), 255), 0); break; case 2: val = sid.custom2 = max(min((increase ? sid.custom2+fadeAmount : sid.custom2-fadeAmount), 255), 0); break;
default: val = strip.getSegment(id).custom1 = max(min((increase ? strip.getSegment(id).custom1+fadeAmount : strip.getSegment(id).custom1-fadeAmount), 255), 0); break; default: val = sid.custom1 = max(min((increase ? sid.custom1+fadeAmount : sid.custom1-fadeAmount), 255), 0); break;
} }
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || i == id) continue; if (!seg.isActive() || i == id) continue;
switch (par) { switch (par) {
case 3: strip.getSegment(i).custom3 = strip.getSegment(id).custom3; break; case 3: seg.custom3 = sid.custom3; break;
case 2: strip.getSegment(i).custom2 = strip.getSegment(id).custom2; break; case 2: seg.custom2 = sid.custom2; break;
default: strip.getSegment(i).custom1 = strip.getSegment(id).custom1; break; default: seg.custom1 = sid.custom1; break;
} }
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getMainSegment();
switch (par) { switch (par) {
case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break; case 3: val = seg.custom3 = max(min((increase ? seg.custom3+fadeAmount : seg.custom3-fadeAmount), 255), 0); break;
case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break; case 2: val = seg.custom2 = max(min((increase ? seg.custom2+fadeAmount : seg.custom2-fadeAmount), 255), 0); break;
@ -656,13 +658,13 @@ public:
effectPalette = palettes_alpha_indexes[effectPaletteIndex]; effectPalette = palettes_alpha_indexes[effectPaletteIndex];
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.palette = effectPalette; seg.palette = effectPalette;
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.palette = effectPalette; seg.palette = effectPalette;
} }
lampUdated(); lampUdated();
@ -685,13 +687,13 @@ public:
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
stateChanged = true; stateChanged = true;
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
lampUdated(); lampUdated();
@ -714,13 +716,13 @@ public:
currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0); currentSat1 = max(min((increase ? currentSat1+fadeAmount : currentSat1-fadeAmount), 255), 0);
colorHStoRGB(currentHue1*256, currentSat1, col); colorHStoRGB(currentHue1*256, currentSat1, col);
if (applyToAll) { if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
} else { } else {
WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); Segment& seg = strip.getSegment(strip.getMainSegmentId());
seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]); seg.colors[0] = RGBW32(col[0], col[1], col[2], col[3]);
} }
lampUdated(); lampUdated();
@ -774,13 +776,13 @@ public:
#endif #endif
currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0); currentCCT = max(min((increase ? currentCCT+fadeAmount : currentCCT-fadeAmount), 255), 0);
// if (applyToAll) { // if (applyToAll) {
for (byte i=0; i<strip.getMaxSegments(); i++) { for (byte i=0; i<strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive()) continue; if (!seg.isActive()) continue;
seg.setCCT(currentCCT, i); seg.setCCT(currentCCT);
} }
// } else { // } else {
// WS2812FX::Segment& seg = strip.getSegment(strip.getMainSegmentId()); // Segment& seg = strip.getSegment(strip.getMainSegmentId());
// seg.setCCT(currentCCT, strip.getMainSegmentId()); // seg.setCCT(currentCCT, strip.getMainSegmentId());
// } // }
lampUdated(); lampUdated();

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@
// but ledmap takes care of that. ledmap is constructed upon initialization // but ledmap takes care of that. ledmap is constructed upon initialization
// so matrix should disable regular ledmap processing // so matrix should disable regular ledmap processing
void WS2812FX::setUpMatrix() { void WS2812FX::setUpMatrix() {
#ifndef WLED_DISABLE_2D
// erase old ledmap, just in case. // erase old ledmap, just in case.
if (customMappingTable != nullptr) delete[] customMappingTable; if (customMappingTable != nullptr) delete[] customMappingTable;
customMappingTable = nullptr; customMappingTable = nullptr;
@ -46,7 +47,7 @@ void WS2812FX::setUpMatrix() {
// safety check // safety check
if (matrixWidth * matrixHeight > MAX_LEDS) { if (matrixWidth * matrixHeight > MAX_LEDS) {
matrixWidth = getLengthTotal(); matrixWidth = _length;
matrixHeight = 1; matrixHeight = 1;
isMatrix = false; isMatrix = false;
return; return;
@ -94,107 +95,113 @@ void WS2812FX::setUpMatrix() {
#endif #endif
} else { } else {
// memory allocation error // memory allocation error
matrixWidth = getLengthTotal(); matrixWidth = _length;
matrixHeight = 1; matrixHeight = 1;
isMatrix = false; isMatrix = false;
return; return;
} }
} else { } else {
// not a matrix set up // not a matrix set up
matrixWidth = getLengthTotal(); matrixWidth = _length;
matrixHeight = 1; matrixHeight = 1;
} }
#endif
} }
// absolute matrix version of setPixelColor()
void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, uint32_t col)
{
#ifndef WLED_DISABLE_2D
if (!isMatrix) return; // not a matrix set-up
uint16_t index = y * matrixWidth + x;
if (index >= _length) return;
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
#endif
}
// returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
#ifndef WLED_DISABLE_2D
uint16_t index = (y * matrixWidth + x);
if (index >= _length) return 0;
if (index < customMappingSize) index = customMappingTable[index];
return busses.getPixelColor(index);
#else
return 0;
#endif
}
///////////////////////////////////////////////////////////
// Segment:: routines
///////////////////////////////////////////////////////////
// XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element) // XY(x,y) - gets pixel index within current segment (often used to reference leds[] array element)
uint16_t IRAM_ATTR WS2812FX::XY(uint16_t x, uint16_t y) { uint16_t IRAM_ATTR Segment::XY(uint16_t x, uint16_t y) {
uint16_t width = SEGMENT.virtualWidth(); // segment width in logical pixels #ifndef WLED_DISABLE_2D
uint16_t height = SEGMENT.virtualHeight(); // segment height in logical pixels uint16_t width = virtualWidth(); // segment width in logical pixels
/* uint16_t height = virtualHeight(); // segment height in logical pixels
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) {
uint16_t t;
// swap X & Y if segment transposed
t = x; x = y; y = t;
// swap width & height if segment transposed
t = width; width = height; height = t;
}
*/
return (x%width) + (y%height) * width; return (x%width) + (y%height) * width;
#else
return 0;
#endif
} }
// get2DPixelIndex(x,y,seg) - returns an index of segment pixel in a matrix layout void IRAM_ATTR Segment::setPixelColorXY(int x, int y, uint32_t col)
// index still needs to undergo ledmap processing to represent actual physical pixel
// matrix is always organized by matrixHeight number of matrixWidth pixels from top to bottom, left to right
// so: pixel at get2DPixelIndex(5,6) in a 2D segment with [start=10, stop=19, startY=20, stopY=29 : 10x10 pixels]
// corresponds to pixel with logical index of 847 (0 based) if a 2D segment belongs to a 32x32 matrix.
// math: (matrixWidth * (startY + y)) + start + x => (32 * (20+6)) + 10 + 5 = 847
uint16_t IRAM_ATTR WS2812FX::get2DPixelIndex(uint16_t x, uint16_t y, uint8_t seg) {
if (seg == 255) seg = _segment_index;
x %= _segments[seg].width(); // just in case constrain x (wrap around)
y %= _segments[seg].height(); // just in case constrain y (wrap around)
return ((_segments[seg].startY + y) * matrixWidth) + _segments[seg].start + x;
}
void IRAM_ATTR WS2812FX::setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w)
{ {
if (!isMatrix) return; // not a matrix set-up #ifndef WLED_DISABLE_2D
if (!strip.isMatrix) return; // not a matrix set-up
if (_bri_t < 255) { uint8_t _bri_t = strip._bri_t;
r = scale8(r, _bri_t); //uint8_t _bri_t = currentBri(getOption(SEG_OPTION_ON) ? opacity : 0);
g = scale8(g, _bri_t); if (_bri_t < 255) {
b = scale8(b, _bri_t); byte r = scale8(R(col), _bri_t);
w = scale8(w, _bri_t); byte g = scale8(G(col), _bri_t);
byte b = scale8(B(col), _bri_t);
byte w = scale8(W(col), _bri_t);
col = RGBW32(r, g, b, w);
} }
uint32_t col = RGBW32(r, g, b, w);
if (SEGMENT.getOption(SEG_OPTION_REVERSED) ) x = SEGMENT.virtualWidth() - x - 1; if (getOption(SEG_OPTION_REVERSED) ) x = virtualWidth() - x - 1;
if (SEGMENT.getOption(SEG_OPTION_REVERSED_Y)) y = SEGMENT.virtualHeight() - y - 1; if (getOption(SEG_OPTION_REVERSED_Y)) y = virtualHeight() - y - 1;
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed if (getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
x *= SEGMENT.groupLength(); // expand to physical pixels x *= groupLength(); // expand to physical pixels
y *= SEGMENT.groupLength(); // expand to physical pixels y *= groupLength(); // expand to physical pixels
if (x >= SEGMENT.width() || y >= SEGMENT.height()) return; // if pixel would fall out of segment just exit if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
for (uint8_t j = 0; j < SEGMENT.grouping; j++) { // groupping vertically for (int j = 0; j < grouping; j++) { // groupping vertically
for (uint8_t g = 0; g < SEGMENT.grouping; g++) { // groupping horizontally for (int g = 0; g < grouping; g++) { // groupping horizontally
uint16_t index, xX = (x+g), yY = (y+j); uint16_t xX = (x+g), yY = (y+j);
if (xX >= SEGMENT.width() || yY >= SEGMENT.height()) continue; // we have reached one dimension's end if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
//if (SEGMENT.getOption(SEG_OPTION_REVERSED) ) xX = SEGMENT.width() - xX - 1; strip.setPixelColorXY(start + xX, startY + yY, col);
//if (SEGMENT.getOption(SEG_OPTION_REVERSED_Y)) yY = SEGMENT.height() - yY - 1;
index = get2DPixelIndex(xX, yY); if (getOption(SEG_OPTION_MIRROR)) { //set the corresponding horizontally mirrored pixel
if (index < customMappingSize) index = customMappingTable[index]; if (getOption(SEG_OPTION_TRANSPOSED)) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
busses.setPixelColor(index, col); else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
if (SEGMENT.getOption(SEG_OPTION_MIRROR)) { //set the corresponding horizontally mirrored pixel
//index = get2DPixelIndex(SEGMENT.width() - xX - 1, yY);
index = SEGMENT.getOption(SEG_OPTION_TRANSPOSED) ? get2DPixelIndex(xX, SEGMENT.height() - yY - 1) : get2DPixelIndex(SEGMENT.width() - xX - 1, yY);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
} }
if (SEGMENT.getOption(SEG_OPTION_MIRROR_Y)) { //set the corresponding vertically mirrored pixel if (getOption(SEG_OPTION_MIRROR_Y)) { //set the corresponding vertically mirrored pixel
//index = get2DPixelIndex(xX, SEGMENT.height() - yY - 1); if (getOption(SEG_OPTION_TRANSPOSED)) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
index = SEGMENT.getOption(SEG_OPTION_TRANSPOSED) ? get2DPixelIndex(SEGMENT.width() - xX - 1, yY) : get2DPixelIndex(xX, SEGMENT.height() - yY - 1); else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
} }
if (SEGMENT.getOption(SEG_OPTION_MIRROR_Y) && SEGMENT.getOption(SEG_OPTION_MIRROR)) { //set the corresponding vertically AND horizontally mirrored pixel if (getOption(SEG_OPTION_MIRROR_Y) && getOption(SEG_OPTION_MIRROR)) { //set the corresponding vertically AND horizontally mirrored pixel
index = get2DPixelIndex(SEGMENT.width() - xX - 1, SEGMENT.height() - yY - 1); strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col);
if (index < customMappingSize) index = customMappingTable[index];
busses.setPixelColor(index, col);
} }
} }
} }
#endif
} }
// anti-aliased version of setPixelColorXY() // anti-aliased version of setPixelColorXY()
void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w, bool aa) void Segment::setPixelColorXY(float x, float y, uint32_t col, bool aa)
{ {
#ifndef WLED_DISABLE_2D
if (!strip.isMatrix) return; // not a matrix set-up
if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized if (x<0.0f || x>1.0f || y<0.0f || y>1.0f) return; // not normalized
const uint16_t cols = SEGMENT.virtualWidth(); const uint16_t cols = virtualWidth();
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t rows = virtualHeight();
float fX = x * (cols-1); float fX = x * (cols-1);
float fY = y * (rows-1); float fY = y * (rows-1);
@ -213,80 +220,59 @@ void /*IRAM_ATTR*/ WS2812FX::setPixelColorXY(float x, float y, byte r, byte g, b
uint32_t cXRYB = getPixelColorXY(xR, yB); uint32_t cXRYB = getPixelColorXY(xR, yB);
if (xL!=xR && yT!=yB) { if (xL!=xR && yT!=yB) {
// blend TL pixel setPixelColorXY(xL, yT, color_blend(col, cXLYT, uint8_t(sqrtf(dL*dT)*255.0f))); // blend TL pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, uint8_t(sqrtf(dL*dT)*255.0f)); setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(sqrtf(dR*dT)*255.0f))); // blend TR pixel
setPixelColorXY(xL, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT)); setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(sqrtf(dL*dB)*255.0f))); // blend BL pixel
// blend TR pixel setPixelColorXY(xR, yB, color_blend(col, cXRYB, uint8_t(sqrtf(dR*dB)*255.0f))); // blend BR pixel
cXRYT = color_blend(RGBW32(r,g,b,w), cXRYT, uint8_t(sqrtf(dR*dT)*255.0f));
setPixelColorXY(xR, yT, R(cXRYT), G(cXRYT), B(cXRYT), W(cXRYT));
// blend BL pixel
cXLYB = color_blend(RGBW32(r,g,b,w), cXLYB, uint8_t(sqrtf(dL*dB)*255.0f));
setPixelColorXY(xL, yB, R(cXLYB), G(cXLYB), B(cXLYB), W(cXLYB));
// blend BR pixel
cXRYB = color_blend(RGBW32(r,g,b,w), cXRYB, uint8_t(sqrtf(dR*dB)*255.0f));
setPixelColorXY(xR, yB, R(cXRYB), G(cXRYB), B(cXRYB), W(cXRYB));
} else if (xR!=xL && yT==yB) { } else if (xR!=xL && yT==yB) {
// blend L pixel setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dL*255.0f))); // blend L pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, uint8_t(dL*255.0f)); setPixelColorXY(xR, yT, color_blend(col, cXRYT, uint8_t(dR*255.0f))); // blend R pixel
setPixelColorXY(xR, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT));
// blend R pixel
cXRYT = color_blend(RGBW32(r,g,b,w), cXRYT, uint8_t(dR*255.0f));
setPixelColorXY(xR, yT, R(cXRYT), G(cXRYT), B(cXRYT), W(cXRYT));
} else if (xR==xL && yT!=yB) { } else if (xR==xL && yT!=yB) {
// blend T pixel setPixelColorXY(xR, yT, color_blend(col, cXLYT, uint8_t(dT*255.0f))); // blend T pixel
cXLYT = color_blend(RGBW32(r,g,b,w), cXLYT, uint8_t(dT*255.0f)); setPixelColorXY(xL, yB, color_blend(col, cXLYB, uint8_t(dB*255.0f))); // blend B pixel
setPixelColorXY(xR, yT, R(cXLYT), G(cXLYT), B(cXLYT), W(cXLYT));
// blend B pixel
cXLYB = color_blend(RGBW32(r,g,b,w), cXLYB, uint8_t(dB*255.0f));
setPixelColorXY(xL, yB, R(cXLYB), G(cXLYB), B(cXLYB), W(cXLYB));
} else { } else {
// exact match (x & y land on a pixel) setPixelColorXY(xL, yT, col); // exact match (x & y land on a pixel)
setPixelColorXY(xL, yT, r, g, b, w);
} }
} else { } else {
setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), r, g, b, w); setPixelColorXY(uint16_t(roundf(fX)), uint16_t(roundf(fY)), col);
} }
#endif
} }
// returns RGBW values of pixel // returns RGBW values of pixel
uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) { uint32_t Segment::getPixelColorXY(uint16_t x, uint16_t y) {
uint16_t index; #ifndef WLED_DISABLE_2D
if (SEGLEN) { if (getOption(SEG_OPTION_REVERSED) ) x = virtualWidth() - x - 1;
if (SEGMENT.getOption(SEG_OPTION_REVERSED) ) x = SEGMENT.virtualWidth() - x - 1; if (getOption(SEG_OPTION_REVERSED_Y)) y = virtualHeight() - y - 1;
if (SEGMENT.getOption(SEG_OPTION_REVERSED_Y)) y = SEGMENT.virtualHeight() - y - 1; if (getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
if (SEGMENT.getOption(SEG_OPTION_TRANSPOSED)) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed x *= groupLength(); // expand to physical pixels
y *= groupLength(); // expand to physical pixels
x *= SEGMENT.groupLength(); // expand to physical pixels if (x >= width() || y >= height()) return 0;
y *= SEGMENT.groupLength(); // expand to physical pixels return strip.getPixelColorXY(start + x, startY + y);
if (x >= SEGMENT.width() || y >= SEGMENT.height()) return 0; #else
return 0;
index = get2DPixelIndex(x, y); #endif
} else {
index = y * matrixWidth + x;
}
if (index < customMappingSize) index = customMappingTable[index];
return busses.getPixelColor(index);
} }
/* // Blends the specified color with the existing pixel color.
* Blends the specified color with the existing pixel color. void Segment::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) {
*/ #ifndef WLED_DISABLE_2D
void WS2812FX::blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend) {
setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend)); setPixelColorXY(x, y, color_blend(getPixelColorXY(x,y), color, blend));
#endif
} }
/* // Adds the specified color with the existing pixel color perserving color balance.
* Adds the specified color with the existing pixel color perserving color balance. void Segment::addPixelColorXY(uint16_t x, uint16_t y, uint32_t color) {
*/ #ifndef WLED_DISABLE_2D
void WS2812FX::addPixelColorXY(uint16_t x, uint16_t y, uint32_t color) {
setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color)); setPixelColorXY(x, y, color_add(getPixelColorXY(x,y), color));
#endif
} }
// blurRow: perform a blur on a row of a rectangular matrix // blurRow: perform a blur on a row of a rectangular matrix
void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) { void Segment::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (row >= rows) return; if (row >= rows) return;
// blur one row // blur one row
@ -294,13 +280,13 @@ void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t x = 0; x < cols; x++) { for (uint16_t x = 0; x < cols; x++) {
CRGB cur = leds ? leds[XY(x,row)] : col_to_crgb(getPixelColorXY(x, row)); CRGB cur = leds ? leds[XY(x,row)] : CRGB(getPixelColorXY(x, row));
CRGB part = cur; CRGB part = cur;
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (x) { if (x) {
CRGB prev = (leds ? leds[XY(x-1,row)] : col_to_crgb(getPixelColorXY(x-1, row))) + part; CRGB prev = (leds ? leds[XY(x-1,row)] : CRGB(getPixelColorXY(x-1, row))) + part;
if (leds) leds[XY(x-1,row)] = prev; if (leds) leds[XY(x-1,row)] = prev;
else setPixelColorXY(x-1, row, prev); else setPixelColorXY(x-1, row, prev);
} }
@ -308,12 +294,14 @@ void WS2812FX::blurRow(uint16_t row, fract8 blur_amount, CRGB* leds) {
else setPixelColorXY(x, row, cur); else setPixelColorXY(x, row, cur);
carryover = part; carryover = part;
} }
#endif
} }
// blurCol: perform a blur on a column of a rectangular matrix // blurCol: perform a blur on a column of a rectangular matrix
void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) { void Segment::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (col >= cols) return; if (col >= cols) return;
// blur one column // blur one column
@ -321,13 +309,13 @@ void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
uint8_t seep = blur_amount >> 1; uint8_t seep = blur_amount >> 1;
CRGB carryover = CRGB::Black; CRGB carryover = CRGB::Black;
for (uint16_t i = 0; i < rows; i++) { for (uint16_t i = 0; i < rows; i++) {
CRGB cur = leds ? leds[XY(col,i)] : col_to_crgb(getPixelColorXY(col, i)); CRGB cur = leds ? leds[XY(col,i)] : CRGB(getPixelColorXY(col, i));
CRGB part = cur; CRGB part = cur;
part.nscale8(seep); part.nscale8(seep);
cur.nscale8(keep); cur.nscale8(keep);
cur += carryover; cur += carryover;
if (i) { if (i) {
CRGB prev = (leds ? leds[XY(col,i-1)] : col_to_crgb(getPixelColorXY(col, i-1))) + part; CRGB prev = (leds ? leds[XY(col,i-1)] : CRGB(getPixelColorXY(col, i-1))) + part;
if (leds) leds[XY(col,i-1)] = prev; if (leds) leds[XY(col,i-1)] = prev;
else setPixelColorXY(col, i-1, prev); else setPixelColorXY(col, i-1, prev);
} }
@ -335,6 +323,7 @@ void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
else setPixelColorXY(col, i, cur); else setPixelColorXY(col, i, cur);
carryover = part; carryover = part;
} }
#endif
} }
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors. // blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
@ -351,17 +340,20 @@ void WS2812FX::blurCol(uint16_t col, fract8 blur_amount, CRGB* leds) {
// eventually all the way to black; this is by design so that // eventually all the way to black; this is by design so that
// it can be used to (slowly) clear the LEDs to black. // it can be used to (slowly) clear the LEDs to black.
void WS2812FX::blur1d(CRGB* leds, fract8 blur_amount) { void Segment::blur1d(CRGB* leds, fract8 blur_amount) {
const uint16_t rows = SEGMENT.virtualHeight(); #ifndef WLED_DISABLE_2D
const uint16_t rows = virtualHeight();
for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount, leds); for (uint16_t y = 0; y < rows; y++) blurRow(y, blur_amount, leds);
#endif
} }
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur]) // 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds) { void Segment::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t dim1 = vertical ? rows : cols; const uint16_t rows = virtualHeight();
const uint16_t dim2 = vertical ? cols : rows; const uint16_t dim1 = vertical ? rows : cols;
const uint16_t dim2 = vertical ? cols : rows;
if (i >= dim2) return; if (i >= dim2) return;
const float seep = blur_amount/255.f; const float seep = blur_amount/255.f;
const float keep = 3.f - 2.f*seep; const float keep = 3.f - 2.f*seep;
@ -374,9 +366,9 @@ void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds)
uint16_t yp = vertical ? y-1 : y; uint16_t yp = vertical ? y-1 : y;
uint16_t xn = vertical ? x : x+1; uint16_t xn = vertical ? x : x+1;
uint16_t yn = vertical ? y+1 : y; uint16_t yn = vertical ? y+1 : y;
CRGB curr = leds ? leds[XY(x,y)] : col_to_crgb(getPixelColorXY(x,y)); CRGB curr = leds ? leds[XY(x,y)] : CRGB(getPixelColorXY(x,y));
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : (leds ? leds[XY(xp,yp)] : col_to_crgb(getPixelColorXY(xp,yp))); CRGB prev = (xp<0 || yp<0) ? CRGB::Black : (leds ? leds[XY(xp,yp)] : CRGB(getPixelColorXY(xp,yp)));
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : (leds ? leds[XY(xn,yn)] : col_to_crgb(getPixelColorXY(xn,yn))); CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : (leds ? leds[XY(xn,yn)] : CRGB(getPixelColorXY(xn,yn)));
uint16_t r, g, b; uint16_t r, g, b;
r = (curr.r*keep + (prev.r + next.r)*seep) / 3; r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
g = (curr.g*keep + (prev.g + next.g)*seep) / 3; g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
@ -389,63 +381,65 @@ void WS2812FX::blur1d(uint16_t i, bool vertical, fract8 blur_amount, CRGB* leds)
if (leds) leds[XY(x,y)] = tmp[j]; if (leds) leds[XY(x,y)] = tmp[j];
else setPixelColorXY(x, y, tmp[j]); else setPixelColorXY(x, y, tmp[j]);
} }
#endif
} }
void WS2812FX::blur2d(CRGB* leds, fract8 blur_amount) { void Segment::blur2d(CRGB* leds, fract8 blur_amount) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount, leds); // blur all rows for (uint16_t i = 0; i < rows; i++) blurRow(i, blur_amount, leds); // blur all rows
for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount, leds); // blur all columns for (uint16_t k = 0; k < cols; k++) blurCol(k, blur_amount, leds); // blur all columns
#endif
} }
void WS2812FX::moveX(CRGB *leds, int8_t delta) { void Segment::moveX(CRGB *leds, int8_t delta) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return; if (!delta) return;
if (delta > 0) { if (delta > 0) {
for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) { for (uint8_t y = 0; y < rows; y++) for (uint8_t x = 0; x < cols-1; x++) {
if (x + delta >= cols) break;
if (leds) leds[XY(x, y)] = leds[XY((x + delta)%cols, y)]; if (leds) leds[XY(x, y)] = leds[XY((x + delta)%cols, y)];
else setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y)); else setPixelColorXY(x, y, getPixelColorXY((x + delta)%cols, y));
} }
} else { } else {
for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) { for (uint8_t y = 0; y < rows; y++) for (int16_t x = cols-1; x >= 0; x--) {
if (x + delta < 0) { if (x + delta < 0) break;
if (leds) leds[XY(x, y)] = leds[XY(cols + delta, y)]; if (leds) leds[XY(x, y)] = leds[XY(x + delta, y)];
else setPixelColorXY(x, y, getPixelColorXY(cols + delta, y)); else setPixelColorXY(x, y, getPixelColorXY(x + delta, y));
} else {
if (leds) leds[XY(x, y)] = leds[XY(x + delta, y)];
else setPixelColorXY(x, y, getPixelColorXY(x + delta, y));
}
} }
} }
#endif
} }
void WS2812FX::moveY(CRGB *leds, int8_t delta) { void Segment::moveY(CRGB *leds, int8_t delta) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (!delta) return; if (!delta) return;
if (delta > 0) { if (delta > 0) {
for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) { for (uint8_t x = 0; x < cols; x++) for (uint8_t y = 0; y < rows-1; y++) {
if (leds) leds[XY(x, y)] = leds[XY(x, (y + delta)%rows)]; if (y + delta >= rows) break;
else setPixelColorXY(x, y, getPixelColorXY(x, (y + delta)%rows)); if (leds) leds[XY(x, y)] = leds[XY(x, (y + delta))];
else setPixelColorXY(x, y, getPixelColorXY(x, (y + delta)));
} }
} else { } else {
for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) { for (uint8_t x = 0; x < cols; x++) for (int16_t y = rows-1; y >= 0; y--) {
if (y + delta < 0) { if (y + delta < 0) break;
if (leds) leds[XY(x, y)] = leds[XY(x, rows + delta)]; if (leds) leds[XY(x, y)] = leds[XY(x, y + delta)];
else setPixelColorXY(x, y, getPixelColorXY(x, rows + delta)); else setPixelColorXY(x, y, getPixelColorXY(x, y + delta));
} else {
if (leds) leds[XY(x, y)] = leds[XY(x, y + delta)];
else setPixelColorXY(x, y, getPixelColorXY(x, y + delta));
}
} }
} }
#endif
} }
// move() - move all pixels in desired direction delta number of pixels // move() - move all pixels in desired direction delta number of pixels
// @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down // @param dir direction: 0=left, 1=left-up, 2=up, 3=right-up, 4=right, 5=right-down, 6=down, 7=left-down
// @param delta number of pixels to move // @param delta number of pixels to move
void WS2812FX::move(uint8_t dir, uint8_t delta, CRGB *leds) { void Segment::move(uint8_t dir, uint8_t delta, CRGB *leds) {
#ifndef WLED_DISABLE_2D
if (delta==0) return; if (delta==0) return;
switch (dir) { switch (dir) {
case 0: moveX(leds, delta); break; case 0: moveX(leds, delta); break;
@ -457,21 +451,25 @@ void WS2812FX::move(uint8_t dir, uint8_t delta, CRGB *leds) {
case 6: moveY(leds,-delta); break; case 6: moveY(leds,-delta); break;
case 7: moveX(leds, delta); moveY(leds,-delta); break; case 7: moveX(leds, delta); moveY(leds,-delta); break;
} }
#endif
} }
void WS2812FX::fill_solid(CRGB* leds, CRGB color) { void Segment::fill_solid(CRGB* leds, CRGB color) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)] = color; if (leds) leds[XY(x,y)] = color;
else setPixelColorXY(x, y, color); else setPixelColorXY(x, y, color);
} }
#endif
} }
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void WS2812FX::fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (int16_t y = -radius; y <= radius; y++) { for (int16_t y = -radius; y <= radius; y++) {
for (int16_t x = -radius; x <= radius; x++) { for (int16_t x = -radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius && if (x * x + y * y <= radius * radius &&
@ -480,31 +478,39 @@ void WS2812FX::fill_circle(CRGB* leds, uint16_t cx, uint16_t cy, uint8_t radius,
leds[XY(cx + x, cy + y)] += col; leds[XY(cx + x, cy + y)] += col;
} }
} }
#endif
} }
void WS2812FX::fadeToBlackBy(CRGB* leds, uint8_t fadeBy) { void Segment::fadeToBlackBy(CRGB* leds, uint8_t fadeBy) {
#ifndef WLED_DISABLE_2D
nscale8(leds, 255 - fadeBy); nscale8(leds, 255 - fadeBy);
#endif
} }
void WS2812FX::nscale8(CRGB* leds, uint8_t scale) { void Segment::nscale8(CRGB* leds, uint8_t scale) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) { for(uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) {
if (leds) leds[XY(x,y)].nscale8(scale); if (leds) leds[XY(x,y)].nscale8(scale);
else setPixelColorXY(x, y, col_to_crgb(getPixelColorXY(x, y)).nscale8(scale)); else setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
} }
#endif
} }
void WS2812FX::setPixels(CRGB* leds) { void Segment::setPixels(CRGB* leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) setPixelColorXY(x, y, leds[XY(x,y)]); for (uint16_t y = 0; y < rows; y++) for (uint16_t x = 0; x < cols; x++) setPixelColorXY(x, y, leds[XY(x,y)]);
#endif
} }
//line function //line function
void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds) { void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, CRGB *leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
@ -517,8 +523,10 @@ void WS2812FX::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB
if (e2 >-dx) { err -= dy; x0 += sx; } if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; } if (e2 < dy) { err += dx; y0 += sy; }
} }
#endif
} }
#ifndef WLED_DISABLE_2D
// font curtesy of https://github.com/idispatch/raster-fonts // font curtesy of https://github.com/idispatch/raster-fonts
static const unsigned char console_font_6x8[] PROGMEM = { static const unsigned char console_font_6x8[] PROGMEM = {
@ -4527,10 +4535,10 @@ static const unsigned char console_font_5x8[] PROGMEM = {
0x00, /* 00000 */ 0x00, /* 00000 */
0x00, /* 00000 */ 0x00, /* 00000 */
0x90, /* 10010 */ 0x90, /* 10010 */
0x90, /* 10010 */
0xF0, /* 11110 */ 0xF0, /* 11110 */
0x90, /* 10010 */ 0x90, /* 10010 */
0x90, /* 10010 */ 0x90, /* 10010 */
0x90, /* 10010 */
0x00, /* 00000 */ 0x00, /* 00000 */
/* /*
@ -6669,19 +6677,21 @@ static const unsigned char console_font_5x8[] PROGMEM = {
0x00, /* 00000 */ 0x00, /* 00000 */
0x00, /* 00000 */ 0x00, /* 00000 */
}; };
#endif
// draws a raster font character on canvas // draws a raster font character on canvas
// only supports 5x8 and 6x8 fonts ATM // only supports 5x8 and 6x8 fonts ATM
void WS2812FX::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color, CRGB *leds) { void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color, CRGB *leds) {
const uint16_t cols = SEGMENT.virtualWidth(); #ifndef WLED_DISABLE_2D
const uint16_t rows = SEGMENT.virtualHeight(); const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
if (w<5 || w>6 || h!=8) return; if (w<5 || w>6 || h!=8) return;
for (uint8_t i = 0; i<h; i++) { // character height for (uint8_t i = 0; i<h; i++) { // character height
int16_t y0 = y + i; int16_t y0 = y + i;
if (y0 < 0) continue; // drawing off-screen if (y0 < 0) continue; // drawing off-screen
if (y0 >= rows) break; // drawing off-screen if (y0 >= rows) break; // drawing off-screen
uint8_t bits; uint8_t bits = 0;
switch (w) { switch (w) {
case 5: bits = pgm_read_byte_near(&console_font_5x8[(chr * 8) + i]); break; case 5: bits = pgm_read_byte_near(&console_font_5x8[(chr * 8) + i]); break;
case 6: bits = pgm_read_byte_near(&console_font_6x8[(chr * 8) + i]); break; case 6: bits = pgm_read_byte_near(&console_font_6x8[(chr * 8) + i]); break;
@ -6694,10 +6704,12 @@ void WS2812FX::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
} }
} }
} }
#endif
} }
#define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8)) #define WU_WEIGHT(a,b) ((uint8_t) (((a)*(b)+(a)+(b))>>8))
void WS2812FX::wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu void Segment::wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel procedure by reddit u/sutaburosu
#ifndef WLED_DISABLE_2D
// extract the fractional parts and derive their inverses // extract the fractional parts and derive their inverses
uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy; uint8_t xx = x & 0xff, yy = y & 0xff, ix = 255 - xx, iy = 255 - yy;
// calculate the intensities for each affected pixel // calculate the intensities for each affected pixel
@ -6710,5 +6722,6 @@ void WS2812FX::wu_pixel(CRGB *leds, uint32_t x, uint32_t y, CRGB c) { //awe
leds[xy].g = qadd8(leds[xy].g, c.g * wu[i] >> 8); leds[xy].g = qadd8(leds[xy].g, c.g * wu[i] >> 8);
leds[xy].b = qadd8(leds[xy].b, c.b * wu[i] >> 8); leds[xy].b = qadd8(leds[xy].b, c.b * wu[i] >> 8);
} }
#endif
} }
#undef WU_WEIGHT #undef WU_WEIGHT

File diff suppressed because it is too large Load Diff

View File

@ -195,11 +195,11 @@ void handleAnalog(uint8_t b)
colorHStoRGB(aRead*256,255,col); colorHStoRGB(aRead*256,255,col);
} else { } else {
// otherwise use "double press" for segment selection // otherwise use "double press" for segment selection
WS2812FX::Segment& seg = strip.getSegment(macroDoublePress[b]); Segment& seg = strip.getSegment(macroDoublePress[b]);
if (aRead == 0) { if (aRead == 0) {
seg.setOption(SEG_OPTION_ON, 0); // off seg.setOption(SEG_OPTION_ON, 0); // off
} else { } else {
seg.setOpacity(aRead, macroDoublePress[b]); seg.setOpacity(aRead);
seg.setOption(SEG_OPTION_ON, 1); seg.setOption(SEG_OPTION_ON, 1);
} }
// this will notify clients of update (websockets,mqtt,etc) // this will notify clients of update (websockets,mqtt,etc)

View File

@ -91,6 +91,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
Bus::setCCTBlend(strip.cctBlending); Bus::setCCTBlend(strip.cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
JsonObject matrix = hw_led[F("matrix")]; JsonObject matrix = hw_led[F("matrix")];
if (!matrix.isNull()) { if (!matrix.isNull()) {
@ -125,6 +126,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
strip.setUpMatrix(); strip.setUpMatrix();
} }
#endif
JsonArray ins = hw_led["ins"]; JsonArray ins = hw_led["ins"];
@ -132,6 +134,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint8_t s = 0; // bus iterator uint8_t s = 0; // bus iterator
if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) busses.removeAll(); // can't safely manipulate busses directly in network callback
uint32_t mem = 0; uint32_t mem = 0;
bool busesChanged = false;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES) break; if (s >= WLED_MAX_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
@ -161,10 +164,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
} else { } else {
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode); busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
doInitBusses = true; busesChanged = true;
} }
s++; s++;
} }
doInitBusses = busesChanged;
// finalization done in beginStrip() // finalization done in beginStrip()
} }
if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus if (hw_led["rev"]) busses.getBus(0)->reversed = true; //set 0.11 global reversed setting for first bus
@ -618,6 +622,7 @@ void serializeConfig() {
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override hw_led[F("rgbwm")] = Bus::getAutoWhiteMode(); // global override
#ifndef WLED_DISABLE_2D
// 2D Matrix Settings // 2D Matrix Settings
if (strip.isMatrix) { if (strip.isMatrix) {
JsonObject matrix = hw_led.createNestedObject(F("matrix")); JsonObject matrix = hw_led.createNestedObject(F("matrix"));
@ -639,6 +644,7 @@ void serializeConfig() {
pnl["s"] = strip.panel[i].serpentine; pnl["s"] = strip.panel[i].serpentine;
} }
} }
#endif
JsonArray hw_led_ins = hw_led.createNestedArray("ins"); JsonArray hw_led_ins = hw_led.createNestedArray("ins");

View File

@ -1,12 +1,57 @@
#include "wled.h" #include "wled.h"
/* /*
* Color conversion methods * Color conversion & utility methods
*/ */
/*
* color blend function
*/
uint32_t IRAM_ATTR color_blend(uint32_t color1, uint32_t color2, uint16_t blend, bool b16) {
if(blend == 0) return color1;
uint16_t blendmax = b16 ? 0xFFFF : 0xFF;
if(blend == blendmax) return color2;
uint8_t shift = b16 ? 16 : 8;
uint32_t w1 = W(color1);
uint32_t r1 = R(color1);
uint32_t g1 = G(color1);
uint32_t b1 = B(color1);
uint32_t w2 = W(color2);
uint32_t r2 = R(color2);
uint32_t g2 = G(color2);
uint32_t b2 = B(color2);
uint32_t w3 = ((w2 * blend) + (w1 * (blendmax - blend))) >> shift;
uint32_t r3 = ((r2 * blend) + (r1 * (blendmax - blend))) >> shift;
uint32_t g3 = ((g2 * blend) + (g1 * (blendmax - blend))) >> shift;
uint32_t b3 = ((b2 * blend) + (b1 * (blendmax - blend))) >> shift;
return RGBW32(r3, g3, b3, w3);
}
/*
* color add function that preserves ratio
* idea: https://github.com/Aircoookie/WLED/pull/2465 by https://github.com/Proto-molecule
*/
uint32_t color_add(uint32_t c1, uint32_t c2)
{
uint32_t r = R(c1) + R(c2);
uint32_t g = G(c1) + G(c2);
uint32_t b = B(c1) + B(c2);
uint32_t w = W(c1) + W(c2);
uint16_t max = r;
if (g > max) max = g;
if (b > max) max = b;
if (w > max) max = w;
if (max < 256) return RGBW32(r, g, b, w);
else return RGBW32(r * 255 / max, g * 255 / max, b * 255 / max, w * 255 / max);
}
void setRandomColor(byte* rgb) void setRandomColor(byte* rgb)
{ {
lastRandomIndex = strip.get_random_wheel_index(lastRandomIndex); lastRandomIndex = strip.getMainSegment().get_random_wheel_index(lastRandomIndex);
colorHStoRGB(lastRandomIndex*256,255,rgb); colorHStoRGB(lastRandomIndex*256,255,rgb);
} }

View File

@ -5,6 +5,8 @@
* Readability defines and their associated numerical values + compile-time constants * Readability defines and their associated numerical values + compile-time constants
*/ */
#define GRADIENT_PALETTE_COUNT 58
//Defaults //Defaults
#define DEFAULT_CLIENT_SSID "Your_Network" #define DEFAULT_CLIENT_SSID "Your_Network"
#define DEFAULT_AP_PASS "wled1234" #define DEFAULT_AP_PASS "wled1234"
@ -76,7 +78,8 @@
#define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h" #define USERMOD_ID_WORDCLOCK 27 //Usermod "usermod_v2_word_clock.h"
#define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h" #define USERMOD_ID_MY9291 28 //Usermod "usermod_MY9291.h"
#define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h" #define USERMOD_ID_SI7021_MQTT_HA 29 //Usermod "usermod_si7021_mqtt_ha.h"
#define USERMOD_ID_AUDIOREACTIVE 30 //Usermod "audioreactive.h" #define USERMOD_ID_BME280 30 //Usermod "usermod_bme280.h
#define USERMOD_ID_AUDIOREACTIVE 31 //Usermod "audioreactive.h"
//Access point behavior //Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
@ -223,6 +226,7 @@
#define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment #define SEG_OPTION_MIRROR 3 //Indicates that the effect will be mirrored within the segment
#define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor #define SEG_OPTION_NONUNITY 4 //Indicates that the effect does not use FRAMETIME or needs getPixelColor
#define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed #define SEG_OPTION_FREEZE 5 //Segment contents will not be refreshed
#define SEG_OPTION_RESET 6 //Segment runtime requires reset
#define SEG_OPTION_TRANSITIONAL 7 #define SEG_OPTION_TRANSITIONAL 7
#define SEG_OPTION_REVERSED_Y 8 #define SEG_OPTION_REVERSED_Y 8
#define SEG_OPTION_MIRROR_Y 9 #define SEG_OPTION_MIRROR_Y 9
@ -358,4 +362,46 @@
#define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates #define INTERFACE_UPDATE_COOLDOWN 2000 //time in ms to wait between websockets, alexa, and MQTT updates
#if defined(ESP8266) && defined(HW_PIN_SCL)
#undef HW_PIN_SCL
#endif
#if defined(ESP8266) && defined(HW_PIN_SDA)
#undef HW_PIN_SDA
#endif
#ifndef HW_PIN_SCL
#ifdef ESP8266
#define HW_PIN_SCL 5
#else
#define HW_PIN_SCL 22
#endif
#endif
#ifndef HW_PIN_SDA
#ifdef ESP8266
#define HW_PIN_SDA 4
#else
#define HW_PIN_SDA 21
#endif
#endif
#if defined(ESP8266) && defined(HW_PIN_CLOCKSPI)
#undef HW_PIN_CLOCKSPI
#endif
#if defined(ESP8266) && defined(HW_PIN_DATASPI)
#undef HW_PIN_DATASPI
#endif
#ifndef HW_PIN_CLOCKSPI
#ifdef ESP8266
#define HW_PIN_CLOCKSPI 14
#else
#define HW_PIN_CLOCKSPI 18
#endif
#endif
#ifndef HW_PIN_DATASPI
#ifdef ESP8266
#define HW_PIN_DATASPI 13
#else
#define HW_PIN_DATASPI 23
#endif
#endif
#endif #endif

View File

@ -193,7 +193,7 @@ button {
position: absolute; position: absolute;
top: 8px; top: 8px;
left: 12px; left: 12px;
pointer-events: none; /*pointer-events: none;*/
width: 24px; width: 24px;
height: 24px; height: 24px;
} }
@ -368,7 +368,7 @@ button {
} }
#sliders { #sliders {
width: 310px; width: 300px;
margin: 0 auto; margin: 0 auto;
position: sticky; position: sticky;
bottom: 0; bottom: 0;
@ -380,18 +380,34 @@ button {
} }
.slider { .slider {
background: var(--c-2); background-color: var(--c-2);
max-width: 310px; max-width: 300px;
min-width: 280px; min-width: 280px;
margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */ margin: 0 auto; /* add 5px; if you want some vertical space but looks ugly */
border-radius: 15px; border-radius: 24px;
position: relative; position: relative;
} }
.filter {
background-color: var(--c-4);
box-shadow: 0px 0px 6px 6px var(--c-1);
border-radius: 26px;
height: 26px;
margin: 0 auto 4px; /* 8px if you want space */
padding: 8px 2px;
position: relative;
/*width: 260px;*/
/*transition: opacity 1s;*/
/*opacity: 1;*/
z-index: 1;
}
/* Tooltip text */ /* Tooltip text */
.slider .tooltiptext { .slider .tooltiptext {
visibility: hidden; visibility: hidden;
background-color: var(--c-5); background-color: var(--c-5);
/*border: 2px solid var(--c-2);*/
box-shadow: 4px 4px 10px 4px var(--c-1);
color: var(--c-f); color: var(--c-f);
text-align: center; text-align: center;
padding: 5px 10px; padding: 5px 10px;
@ -447,6 +463,10 @@ button {
.hide { .hide {
display: none !important; display: none !important;
} }
.fade {
visibility: hidden;
opacity: 0;
}
.first { .first {
margin-top: 10px; margin-top: 10px;
@ -827,19 +847,22 @@ input[type=range]::-moz-range-thumb {
} }
select { select {
padding: 4px; padding: 4px 8px;
margin: 0; margin: 0;
font-size: 19px; font-size: 19px;
background-color: var(--c-3); background-color: var(--c-3);
color: var(--c-d); color: var(--c-d);
cursor: pointer; cursor: pointer;
border: 1px solid var(--c-2); border: 0 solid var(--c-2);
border-radius: 5px; border-radius: 20px;
transition-duration: 0.5s; transition-duration: 0.5s;
-webkit-backface-visibility: hidden; -webkit-backface-visibility: hidden;
-webkit-transform:translate3d(0,0,0); -webkit-transform:translate3d(0,0,0);
-webkit-appearance: none;
-moz-appearance: none;
backface-visibility: hidden; backface-visibility: hidden;
transform:translate3d(0,0,0); transform:translate3d(0,0,0);
text-overflow: ellipsis;
} }
#tt { #tt {
text-align: center; text-align: center;
@ -847,16 +870,26 @@ select {
.cl { .cl {
background-color: #000; background-color: #000;
} }
.sel-p { select.sel-p, select.sel-pl, select.sel-ple {
margin: 5px 0 10px; margin: 5px 0;
width: 5em;
}
.sel-pl {
width: 100%; width: 100%;
background-position: 141px 16px; height: 40px;
} }
.sel-ple { div.sel-p {
width: 100%; position: relative;
}
div.sel-p:after {
content: "";
position: absolute;
right: 10px;
top: 22px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid var(--c-f);
}
select.sel-ple {
text-align: center; text-align: center;
} }
option { option {
@ -1173,6 +1206,25 @@ TD .checkmark, TD .radiomark {
left: 9px; left: 9px;
} }
.filter .fchkl {
display: inline-block;
min-width: 0.7em;
padding: 4px 4px 4px 32px;
text-align: left;
line-height: 24px;
vertical-align: middle;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
}
.lbl-s {
display: inline-block;
/* margin: 10px 4px 0 0; */
font-size: 13px;
width: 48%;
text-align: center;
}
/* list wrapper */ /* list wrapper */
.list { .list {
position: relative; position: relative;
@ -1226,6 +1278,7 @@ TD .checkmark, TD .radiomark {
.lstI.sticky, .lstI.sticky,
.lstI.selected { .lstI.selected {
z-index: 1; z-index: 1;
box-shadow: 0px 0px 10px 4px var(--c-1);
} }
#pcont .selected:not([class*="expanded"]) { #pcont .selected:not([class*="expanded"]) {
@ -1258,6 +1311,9 @@ TD .checkmark, TD .radiomark {
/* list item name (for sorting) */ /* list item name (for sorting) */
.lstIname { .lstIname {
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
} }
/* list item palette preview */ /* list item palette preview */
@ -1287,6 +1343,8 @@ TD .checkmark, TD .radiomark {
border-radius: 21px; border-radius: 21px;
background: var(--c-2); background: var(--c-2);
border: 1px solid var(--c-3); border: 1px solid var(--c-3);
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
} }
.fnd input[type="text"]:focus { .fnd input[type="text"]:focus {

View File

@ -202,7 +202,7 @@
<div class="staytop fnd" id="fxFind"> <div class="staytop fnd" id="fxFind">
<input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist')" /> <input type="text" placeholder="Search" oninput="search(this,'fxlist')" onfocus="search(this,'fxlist')" />
<i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i> <i class="icons clear-icon" onclick="clean(this);">&#xe38f;</i>
<i class="icons search-icon">&#xe0a1;</i> <i class="icons search-icon" onclick="gId('filters').classList.toggle('hide');" style="cursor:pointer;">&#xe0a1;</i>
</div> </div>
<div id="fxlist" class="list"> <div id="fxlist" class="list">
<div class="lstI"> <div class="lstI">
@ -217,6 +217,28 @@
</div> </div>
</div> </div>
<div id="sliders"> <div id="sliders">
<div id="filters" class="filter">
<label id="filterPal" class="check fchkl">&#127912;
<input type="checkbox" data-flt="&#127912;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter1D" class="check fchkl">1D
<input type="checkbox" data-flt="*" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filter2D" class="check fchkl">2D<!-- &#8862; -->
<input type="checkbox" data-flt="2D" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterVol" class="check fchkl">&#9834;
<input type="checkbox" data-flt="&#9834;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
<label id="filterFreq" class="check fchkl">&#9835;
<input type="checkbox" data-flt="&#9835;" onchange="filterFx(this)">
<span class="checkmark"></span>
</label>
</div>
<div id="slider0" class="slider"> <div id="slider0" class="slider">
<i class="icons slider-icon" style="cursor: pointer;" onclick="tglFreeze()">&#xe325;</i> <i class="icons slider-icon" style="cursor: pointer;" onclick="tglFreeze()">&#xe325;</i>
<div class="sliderwrap il"> <div class="sliderwrap il">

View File

@ -5,10 +5,10 @@ var isOn = false, nlA = false, isLv = false, isInfo = false, isNodes = false, sy
var hasWhite = false, hasRGB = false, hasCCT = false; var hasWhite = false, hasRGB = false, hasCCT = false;
var nlDur = 60, nlTar = 0; var nlDur = 60, nlTar = 0;
var nlMode = false; var nlMode = false;
var selectedFx = 0, prevFx = -1; var selectedFx = 0;
var selectedPal = 0; var selectedPal = 0;
var csel = 0; // selected color slot (0-2) var csel = 0; // selected color slot (0-2)
var currentPreset = -1, prevPS = -1; var currentPreset = -1;
var lastUpdate = 0; var lastUpdate = 0;
var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0; var segCount = 0, ledCount = 0, lowestUnused = 0, maxSeg = 0, lSeg = 0;
var pcMode = false, pcModeA = false, lastw = 0, wW; var pcMode = false, pcModeA = false, lastw = 0, wW;
@ -25,7 +25,7 @@ var ws, cpick, ranges;
var cfg = { var cfg = {
theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}}, theme:{base:"dark", bg:{url:""}, alpha:{bg:0.6,tab:0.8}, color:{bg:""}},
comp :{colors:{picker: true, rgb: false, quick: true, hex: false}, comp :{colors:{picker: true, rgb: false, quick: true, hex: false},
labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, css:true, hdays:false} labels:true, pcmbot:false, pid:true, seglen:false, segpwr:false, segexp:false, css:true, hdays:false}
}; };
var hol = [ var hol = [
[0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas [0,11,24,4,"https://aircoookie.github.io/xmas.png"], // christmas
@ -167,12 +167,12 @@ function loadBg(iUrl)
img.src = iUrl; img.src = iUrl;
if (iUrl == "" || iUrl==="https://picsum.photos/1920/1080") { if (iUrl == "" || iUrl==="https://picsum.photos/1920/1080") {
var today = new Date(); var today = new Date();
for (var i=0; i<hol.length; i++) { for (var h of (hol||[])) {
var yr = hol[i][0]==0 ? today.getFullYear() : hol[i][0]; var yr = h[0]==0 ? today.getFullYear() : h[0];
var hs = new Date(yr,hol[i][1],hol[i][2]); var hs = new Date(yr,h[1],h[2]);
var he = new Date(hs); var he = new Date(hs);
he.setDate(he.getDate() + hol[i][3]); he.setDate(he.getDate() + h[3]);
if (today>=hs && today<=he) img.src = hol[i][4]; if (today>=hs && today<=he) img.src = h[4];
} }
} }
img.addEventListener('load', (e) => { img.addEventListener('load', (e) => {
@ -605,7 +605,7 @@ function parseInfo(i) {
mh = i.leds.matrix ? i.leds.matrix.h : 0; mh = i.leds.matrix ? i.leds.matrix.h : 0;
isM = mw>0 && mh>0; isM = mw>0 && mh>0;
if (!isM) hideModes("2D "); if (!isM) hideModes("2D ");
if (!i.u || !i.u.AudioReactive) { /*hideModes("♪ ");*/ hideModes("♫ "); } // hide /*audio*/ frequency reactive effects //if (!i.u || !i.u.AudioReactive) { /*hideModes(" ♪");*/ hideModes(" ♫"); } // hide /*audio*/ frequency reactive effects
} }
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml //https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
@ -621,6 +621,19 @@ function parseInfo(i) {
//} //}
//setInnerHTML(obj, html); //setInnerHTML(obj, html);
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
//var setInnerHTML = function(elm, html) {
// elm.innerHTML = html;
// Array.from(elm.querySelectorAll("script")).forEach( oldScript => {
// const newScript = document.createElement("script");
// Array.from(oldScript.attributes)
// .forEach( attr => newScript.setAttribute(attr.name, attr.value) );
// newScript.appendChild(document.createTextNode(oldScript.innerHTML));
// oldScript.parentNode.replaceChild(newScript, oldScript);
// });
//}
//setInnerHTML(obj, html);
function populateInfo(i) function populateInfo(i)
{ {
var cn=""; var cn="";
@ -671,17 +684,15 @@ function populateSegments(s)
let li = lastinfo; let li = lastinfo;
segCount = 0; lowestUnused = 0; lSeg = 0; segCount = 0; lowestUnused = 0; lSeg = 0;
for (var y = 0; y < (s.seg||[]).length; y++) for (var inst of (s.seg||[])) {
{
segCount++; segCount++;
var inst = s.seg[y];
let i = parseInt(inst.id); let i = parseInt(inst.id);
if (i == lowestUnused) lowestUnused = i+1; if (i == lowestUnused) lowestUnused = i+1;
if (i > lSeg) lSeg = i; if (i > lSeg) lSeg = i;
let sg = gId(`seg${i}`); let sg = gId(`seg${i}`);
let exp = sg ? sg.classList.contains("expanded") : false; let exp = sg ? (sg.classList.contains("expanded") || (i===0 && cfg.comp.segexp)) : false;
let segp = `<div id="segp${i}" class="sbs"> let segp = `<div id="segp${i}" class="sbs">
<i class="icons e-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i> <i class="icons e-icon pwr ${inst.on ? "act":""}" id="seg${i}pwr" onclick="setSegPwr(${i})">&#xe08f;</i>
@ -690,17 +701,33 @@ function populateSegments(s)
<div class="sliderdisplay"></div> <div class="sliderdisplay"></div>
</div> </div>
</div>`; </div>`;
let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark schk"></span></label>`; let rvXck = `<label class="check revchkl">Reverse ${isM?'':'direction'}<input type="checkbox" id="seg${i}rev" onchange="setRev(${i})" ${inst.rev?"checked":""}><span class="checkmark"></span></label>`;
let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark schk"></span></label>`; let miXck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mi" onchange="setMi(${i})" ${inst.mi?"checked":""}><span class="checkmark"></span></label>`;
let rvYck = "", miYck =""; let rvYck = "", miYck ="";
if (isM) { if (isM) {
rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark schk"></span></label>`; rvYck = `<label class="check revchkl">Reverse<input type="checkbox" id="seg${i}rY" onchange="setRevY(${i})" ${inst.rY?"checked":""}><span class="checkmark"></span></label>`;
miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark schk"></span></label>`; miYck = `<label class="check revchkl">Mirror<input type="checkbox" id="seg${i}mY" onchange="setMiY(${i})" ${inst.mY?"checked":""}><span class="checkmark"></span></label>`;
} }
let map2D = `<div id="seg${i}map2D" data-map="map2D" class="lbl-s hide">Expand 1D FX<br>
<div class="sel-p"><select class="sel-p" id="seg${i}mp12" onchange="setMp12(${i})">
<option value="0" ${inst.mp12==0?' selected':''}>None</option>
<option value="1" ${inst.mp12==1?' selected':''}>Bar</option>
<option value="2" ${inst.mp12==2?' selected':''}>Arc</option>
<option value="3" ${inst.mp12==3?' selected':''}>Corner</option>
</select></div>
</div>`;
let sndSim = `<div data-snd="ssim" class="lbl-s hide">Sound sim<br>
<div class="sel-p"><select class="sel-p" id="seg${i}ssim" onchange="setSSim(${i})">
<option value="0" ${inst.ssim==0?' selected':''}>BeatSin</option>
<option value="1" ${inst.ssim==1?' selected':''}>WeWillRockYou</option>
<option value="2" ${inst.ssim==2?' selected':''}>U10_3</option>
<option value="3" ${inst.ssim==3?' selected':''}>U14_3</option>
</select></div>
</div>`;
cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}"> cn += `<div class="seg lstI ${i==s.mainseg ? 'selected' : ''} ${exp ? "expanded":""}" id="seg${i}">
<label class="check schkl"> <label class="check schkl">
<input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}> <input type="checkbox" id="seg${i}sel" onchange="selSeg(${i})" ${inst.sel ? "checked":""}>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i> <i class="icons e-icon frz" id="seg${i}frz" onclick="event.preventDefault();tglFreeze(${i});">&#x${inst.frz ? (li.live && li.liveseg==i?'e410':'e0e8') : 'e325'};</i>
<div class="segname" onclick="selSegEx(${i})"> <div class="segname" onclick="selSegEx(${i})">
@ -741,10 +768,12 @@ function populateSegments(s)
</table> </table>
<div class="h bp" id="seg${i}len"></div> <div class="h bp" id="seg${i}len"></div>
${!isM?rvXck:''} ${!isM?rvXck:''}
${map2D}
${s.AudioReactive && s.AudioReactive.on ? "" : sndSim}
<label class="check revchkl"> <label class="check revchkl">
${isM?'Transpose':'Mirror effect'} ${isM?'Transpose':'Mirror effect'}
<input type="checkbox" id="seg${i}${isM?'tp':'mi'}" onchange="${(isM?'setTp(':'setMi(')+i})" ${isM?(inst.tp?"checked":""):(inst.mi?"checked":"")}> <input type="checkbox" id="seg${i}${isM?'tp':'mi'}" onchange="${(isM?'setTp(':'setMi(')+i})" ${isM?(inst.tp?"checked":""):(inst.mi?"checked":"")}>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<div class="del"> <div class="del">
<button class="btn btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button> <button class="btn btn-xs" id="segr${i}" title="Repeat until end" onclick="rptSeg(${i})"><i class="icons btn-icon">&#xe22d;</i></button>
@ -779,27 +808,34 @@ function populateEffects()
var effects = eJson; var effects = eJson;
var html = ""; var html = "";
effects.shift(); // remove solid effects.shift(); // temporary remove solid
for (let i = 0; i < effects.length; i++) effects[i] = {id: effects[i][0], name:effects[i][1]}; for (let i = 0; i < effects.length; i++) {
effects[i] = {
id: effects[i][0],
name:effects[i][1]
};
}
effects.sort((a,b) => (a.name).localeCompare(b.name)); effects.sort((a,b) => (a.name).localeCompare(b.name));
effects.unshift({ effects.unshift({
"id": 0, "id": 0,
"name": "Solid" "name": "Solid"
}); });
for (let i = 0; i < effects.length; i++) { for (let ef of effects) {
// WLEDSR: add slider and color control to setX (used by requestjson) // WLEDSR: add slider and color control to setX (used by requestjson)
if (effects[i].name.indexOf("Reserved") < 0) { let id = ef.id;
let id = effects[i].id; let nm = ef.name;
html += generateListItemHtml( let fd = "";
'fx', if (ef.name.indexOf("Reserved") < 0) {
id, if (Array.isArray(fxdata) && fxdata.length>id) {
effects[i].name, fd = fxdata[id].substr(1);
'setX', var eP = (fd == '')?[]:fd.split(";");
'', var p = (eP.length<3 || eP[2]==='')?[]:eP[2].split(",");
!(Array.isArray(fxdata) && fxdata.length>id) ? '' : fxdata[id].substr(1) var m = (eP.length<4 || eP[3]==='')?[]:eP[3].split(",");
); if (isM && m.length>0) for (let r of m) { if (r.substring(0,4)=="mp12") nm += " *"; } // 1D effects with defined mapping
if (p.length>0 && p[0].substring(0,1) === "!") nm += " 🎨";
}
html += generateListItemHtml('fx',id,nm,'setX','',fd);
} }
} }
@ -809,7 +845,7 @@ function populateEffects()
function populatePalettes() function populatePalettes()
{ {
var palettes = lJson; var palettes = lJson;
palettes.shift(); // remove default palettes.shift(); // temporary remove default
for (let i = 0; i < palettes.length; i++) { for (let i = 0; i < palettes.length; i++) {
palettes[i] = { palettes[i] = {
"id": palettes[i][0], "id": palettes[i][0],
@ -817,20 +853,19 @@ function populatePalettes()
}; };
} }
palettes.sort((a,b) => (a.name).localeCompare(b.name)); palettes.sort((a,b) => (a.name).localeCompare(b.name));
palettes.unshift({ palettes.unshift({
"id": 0, "id": 0,
"name": "Default" "name": "Default"
}); });
var html = ""; var html = "";
for (let i = 0; i < palettes.length; i++) { for (let pa of palettes) {
html += generateListItemHtml( html += generateListItemHtml(
'palette', 'palette',
palettes[i].id, pa.id,
palettes[i].name, pa.name,
'setPalette', 'setPalette',
`<div class="lstIprev" style="${genPalPrevCss(palettes[i].id)}"></div>` `<div class="lstIprev" style="${genPalPrevCss(pa.id)}"></div>`
); );
} }
@ -895,12 +930,12 @@ function genPalPrevCss(id)
return `background: linear-gradient(to right,${gradient.join()});`; return `background: linear-gradient(to right,${gradient.join()});`;
} }
function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', extraPar = '') function generateListItemHtml(listName, id, name, clickAction, extraHtml = '', effectPar = '')
{ {
return `<div class="lstI${id==0?' sticky':''}" data-id="${id}" data-opt="${extraPar}" onClick="${clickAction}(${id})"> return `<div class="lstI${id==0?' sticky':''}" data-id="${id}" data-opt="${effectPar}" onClick="${clickAction}(${id})">
<label class="radio schkl" onclick="event.preventDefault()"> <label class="radio schkl" onclick="event.preventDefault()">
<input type="radio" value="${id}" name="${listName}"> <input type="radio" value="${id}" name="${listName}">
<span class="radiomark schk"></span> <span class="radiomark"></span>
<div class="lstIcontent"> <div class="lstIcontent">
<span class="lstIname"> <span class="lstIname">
${name} ${name}
@ -933,8 +968,7 @@ function populateNodes(i,n)
var nnodes = 0; var nnodes = 0;
if (n.nodes) { if (n.nodes) {
n.nodes.sort((a,b) => (a.name).localeCompare(b.name)); n.nodes.sort((a,b) => (a.name).localeCompare(b.name));
for (var x=0;x<n.nodes.length;x++) { for (var o of n.nodes) {
var o = n.nodes[x];
if (o.name) { if (o.name) {
var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`; var url = `<button class="btn" title="${o.ip}" onclick="location.assign('http://${o.ip}');">${bname(o)}</button>`;
urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`); urows += inforow(url,`${btype(o.type)}<br><i>${o.vid==0?"N/A":o.vid}</i>`);
@ -979,7 +1013,7 @@ function updateTrail(e)
var max = e.hasAttribute('max') ? e.attributes.max.value : 255; var max = e.hasAttribute('max') ? e.attributes.max.value : 255;
var perc = Math.round(e.value * 100 / max); var perc = Math.round(e.value * 100 / max);
if (perc < 50) perc += 2; if (perc < 50) perc += 2;
var val = `linear-gradient(90deg, var(--bg) ${perc}%, var(--c-4) ${perc}%)`; var val = `linear-gradient(90deg, var(--bg) ${perc}%, var(--c-6) ${perc}%)`;
sd.style.backgroundImage = val; sd.style.backgroundImage = val;
} }
var b = e.parentNode.parentNode.getElementsByTagName('output')[0]; var b = e.parentNode.parentNode.getElementsByTagName('output')[0];
@ -1004,6 +1038,7 @@ function updateLen(s)
start = parseInt(gId(`seg${s}sY`).value); start = parseInt(gId(`seg${s}sY`).value);
stop = parseInt(gId(`seg${s}eY`).value); stop = parseInt(gId(`seg${s}eY`).value);
len *= (stop-(cfg.comp.seglen?0:start)); len *= (stop-(cfg.comp.seglen?0:start));
if (stop-start>1) gId(`seg${s}map2D`).classList.remove("hide"); else gId(`seg${s}map2D`).classList.add("hide");
} }
var out = "(delete)"; var out = "(delete)";
if (len > 1) { if (len > 1) {
@ -1109,15 +1144,21 @@ function updateSelectedFx()
if (selEffectInput) selEffectInput.checked = true; if (selEffectInput) selEffectInput.checked = true;
var selElement = parent.querySelector('.selected'); var selElement = parent.querySelector('.selected');
if (selElement) selElement.classList.remove('selected'); if (selElement) {
selElement.classList.remove('selected');
selElement.style.bottom = null; // remove element style added in slider handling
}
var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`); var selectedEffect = parent.querySelector(`.lstI[data-id="${selectedFx}"]`);
if (selectedEffect) { if (selectedEffect) {
selectedEffect.classList.add('selected'); selectedEffect.classList.add('selected');
var fx = (selectedFx != prevFx) && currentPreset==-1; // effect changed & preset==none setEffectParameters(selectedFx);
var ps = (prevPS != currentPreset) && currentPreset==-1; // preset changed & preset==none
// WLEDSR: extract the Slider and color control string from the HTML element and set it. var selectedName = selectedEffect.querySelector(".lstIname").innerText;
setSliderAndColorControl(selectedFx, (fx || ps)); var segs = gId("segcont").querySelectorAll(`div[data-map="map2D"]`);
for (const seg of segs) if (selectedName.indexOf("2D ")<0) seg.classList.remove("hide"); else seg.classList.add("hide");
var segs = gId("segcont").querySelectorAll(`div[data-snd="ssim"]`);
for (const seg of segs) if (selectedName.indexOf(" ♪")<0 && selectedName.indexOf(" ♫")<0) seg.classList.add("hide"); else seg.classList.remove("hide"); // also "♫ "?
} }
} }
@ -1180,6 +1221,7 @@ function makeWS() {
function readState(s,command=false) function readState(s,command=false)
{ {
if (!s) return false; if (!s) return false;
if (s.success) return true; // no data to process
isOn = s.on; isOn = s.on;
gId('sliderBri').value = s.bri; gId('sliderBri').value = s.bri;
@ -1188,7 +1230,6 @@ function readState(s,command=false)
nlTar = s.nl.tbri; nlTar = s.nl.tbri;
nlFade = s.nl.fade; nlFade = s.nl.fade;
syncSend = s.udpn.send; syncSend = s.udpn.send;
prevPS = currentPreset;
if (s.pl<0) currentPreset = s.ps; if (s.pl<0) currentPreset = s.ps;
else currentPreset = s.pl; else currentPreset = s.pl;
@ -1224,7 +1265,7 @@ function readState(s,command=false)
if (!i) { if (!i) {
showToast('No Segments!', true); showToast('No Segments!', true);
updateUI(); updateUI();
return; return true;
} }
var cd = gId('csl').children; var cd = gId('csl').children;
@ -1266,11 +1307,11 @@ function readState(s,command=false)
showToast('Error ' + s.error + ": " + errstr, true); showToast('Error ' + s.error + ": " + errstr, true);
} }
prevFx = selectedFx;
selectedPal = i.pal; selectedPal = i.pal;
selectedFx = i.fx; selectedFx = i.fx;
redrawPalPrev(); // if any color changed (random palette did at least) redrawPalPrev(); // if any color changed (random palette did at least)
updateUI(); updateUI();
return true;
} }
// WLEDSR: control HTML elements for Slider and Color Control // WLEDSR: control HTML elements for Slider and Color Control
@ -1294,16 +1335,15 @@ function readState(s,command=false)
// Note: Effects can override default pattern behaviour // Note: Effects can override default pattern behaviour
// - FadeToBlack can override the background setting // - FadeToBlack can override the background setting
// - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient) // - Defining SEGCOL(<i>) can override a specific palette using these values (e.g. Color Gradient)
function setSliderAndColorControl(idx, applyDef=false) function setEffectParameters(idx)
{ {
if (!(Array.isArray(fxdata) && fxdata.length>idx)) return; if (!(Array.isArray(fxdata) && fxdata.length>idx)) return;
var controlDefined = (fxdata[idx].substr(0,1) == "@"); var controlDefined = (fxdata[idx].substr(0,1) == "@");
var extra = fxdata[idx].substr(1); var effectPar = fxdata[idx].substr(1);
var extras = (extra == '')?[]:extra.split(";"); var effectPars = (effectPar == '')?[]:effectPar.split(";");
var slOnOff = (extras.length==0 || extras[0]=='')?[]:extras[0].split(","); var slOnOff = (effectPars.length==0 || effectPars[0]=='')?[]:effectPars[0].split(",");
var coOnOff = (extras.length<2 || extras[1]=='')?[]:extras[1].split(","); var coOnOff = (effectPars.length<2 || effectPars[1]=='')?[]:effectPars[1].split(",");
var paOnOff = (extras.length<3 || extras[2]=='')?[]:extras[2].split(","); var paOnOff = (effectPars.length<3 || effectPars[2]=='')?[]:effectPars[2].split(",");
var obj = {"seg":{}};
// set html slider items on/off // set html slider items on/off
var nSliders = Math.min(5,Math.floor(gId("sliders").children.length)); // div for each slider var nSliders = Math.min(5,Math.floor(gId("sliders").children.length)); // div for each slider
@ -1316,10 +1356,6 @@ function setSliderAndColorControl(idx, applyDef=false)
if (slOnOff.length>i && slOnOff[i].indexOf("=")>0) { if (slOnOff.length>i && slOnOff[i].indexOf("=")>0) {
// embeded default values // embeded default values
var dPos = slOnOff[i].indexOf("="); var dPos = slOnOff[i].indexOf("=");
var v = Math.max(0,Math.min(255,parseInt(slOnOff[i].substr(dPos+1))));
if (i==0) { if (applyDef) gId("sliderSpeed").value = v; obj.seg.sx = v; }
else if (i==1) { if (applyDef) gId("sliderIntensity").value = v; obj.seg.ix = v; }
else { if (applyDef) gId("sliderC"+(i-1)).value = v; obj.seg["c"+(i-1)+"x"] = v}
slOnOff[i] = slOnOff[i].substring(0,dPos); slOnOff[i] = slOnOff[i].substring(0,dPos);
} }
if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i]; if (slOnOff.length>i && slOnOff[i]!="!") label.innerHTML = slOnOff[i];
@ -1327,18 +1363,15 @@ function setSliderAndColorControl(idx, applyDef=false)
else if (i==1) label.innerHTML = "Effect intensity"; else if (i==1) label.innerHTML = "Effect intensity";
else label.innerHTML = "Custom" + (i-1); else label.innerHTML = "Custom" + (i-1);
sldCnt++; sldCnt++;
//if (sldCnt++===0) slider.classList.add("top");
slider.classList.remove("hide"); slider.classList.remove("hide");
//slider.setAttribute('title',label.innerHTML);
} else { } else {
slider.classList.add("hide"); slider.classList.add("hide");
//slider.classList.remove("top");
} }
} }
// set the bottom position of selected effect (sticky) as the top of sliders div // set the bottom position of selected effect (sticky) as the top of sliders div
let top = parseInt(getComputedStyle(gId("sliders")).height); let top = parseInt(getComputedStyle(gId("sliders")).height);
/*if (sldCnt===1)*/ top += 28; // size of tooltip //top += 5; // size of tooltip
let sel = d.querySelector('#fxlist .selected'); let sel = d.querySelector('#fxlist .selected');
if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setX()) if (sel) sel.style.bottom = top + "px"; // we will need to remove this when unselected (in setX())
@ -1346,7 +1379,7 @@ function setSliderAndColorControl(idx, applyDef=false)
var cslLabel = ''; var cslLabel = '';
var sep = ''; var sep = '';
var hide = true; var hide = true;
var cslCnt = 0; var cslCnt = 0, oCsel = csel;
for (let i=0; i<gId("csl").children.length; i++) { for (let i=0; i<gId("csl").children.length; i++) {
var btn = gId("csl" + i); var btn = gId("csl" + i);
// if no controlDefined or coOnOff has a value // if no controlDefined or coOnOff has a value
@ -1364,13 +1397,13 @@ function setSliderAndColorControl(idx, applyDef=false)
else if (i==1) btn.innerHTML = "Bg"; else if (i==1) btn.innerHTML = "Bg";
else btn.innerHTML = "Cs"; else btn.innerHTML = "Cs";
hide = false; hide = false;
if (!cslCnt) selectSlot(i); // select 1st displayed slot if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++; cslCnt++;
} else if (!controlDefined /*|| paOnOff.length>0*/) { // if no controls then all buttons should be shown for color 1..3 } else if (!controlDefined /*|| paOnOff.length>0*/) { // if no controls then all buttons should be shown for color 1..3
btn.style.display = "inline"; btn.style.display = "inline";
btn.innerHTML = `${i+1}`; btn.innerHTML = `${i+1}`;
hide = false; hide = false;
if (!cslCnt) selectSlot(i); // select 1st displayed slot if (!cslCnt || oCsel==i) selectSlot(i); // select 1st displayed slot or old one
cslCnt++; cslCnt++;
} else { } else {
btn.style.display = "none"; btn.style.display = "none";
@ -1388,11 +1421,6 @@ function setSliderAndColorControl(idx, applyDef=false)
// embeded default values // embeded default values
var dPos = paOnOff[0].indexOf("="); var dPos = paOnOff[0].indexOf("=");
var v = Math.max(0,Math.min(255,parseInt(paOnOff[0].substr(dPos+1)))); var v = Math.max(0,Math.min(255,parseInt(paOnOff[0].substr(dPos+1))));
var p = d.querySelector(`#pallist input[name="palette"][value="${v}"]`);
if (applyDef && p) {
p.checked = true;
obj.seg.pal = v;
}
paOnOff[0] = paOnOff[0].substring(0,dPos); paOnOff[0] = paOnOff[0].substring(0,dPos);
} }
if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0]; if (paOnOff.length>0 && paOnOff[0] != "!") pall.innerHTML = paOnOff[0];
@ -1401,14 +1429,11 @@ function setSliderAndColorControl(idx, applyDef=false)
// disable palett list // disable palett list
pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette not used'; pall.innerHTML = '<i class="icons sel-icon" onclick="tglHex()">&#xe2b3;</i> Color palette not used';
palw.style.display = "none"; palw.style.display = "none";
// if numeric set as selected palette
if (paOnOff.length>0 && paOnOff[0]!="" && !isNaN(paOnOff[0]) && parseInt(paOnOff[0])!=selectedPal) obj.seg.pal = parseInt(paOnOff[0]);
} }
// not all color selectors shown, hide palettes created from color selectors // not all color selectors shown, hide palettes created from color selectors
for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) { for (let e of (gId('pallist').querySelectorAll('.lstI')||[])) {
if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf("* C")>=0) e.classList.add('hide'); else e.classList.remove('hide'); if (cslCnt < 3 && e.querySelector('.lstIname').innerText.indexOf("* C")>=0) e.classList.add('hide'); else e.classList.remove('hide');
} }
if (!isEmpty(obj.seg) && applyDef) requestJson(obj); // update default values (may need throttling on ESP8266)
} }
var jsonTimeout; var jsonTimeout;
@ -1588,7 +1613,7 @@ function resetUtil()
{ {
// gId('segutil').innerHTML = '<button class="btn btn-s" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>segment</button>'; // gId('segutil').innerHTML = '<button class="btn btn-s" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>segment</button>';
gId('segutil').innerHTML = '<div class="seg btn btn-s" style="border-radius:24px;padding:0;">' gId('segutil').innerHTML = '<div class="seg btn btn-s" style="border-radius:24px;padding:0;">'
+ '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark schk"></span></label>' + '<label class="check schkl"><input type="checkbox" id="selall" onchange="selSegAll(this)"><span class="checkmark"></span></label>'
+ '<div class="segname" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>segment</div></div>'; + '<div class="segname" onclick="makeSeg()"><i class="icons btn-icon">&#xe18a;</i>segment</div></div>';
} }
@ -1601,14 +1626,14 @@ var plJson = {"0":{
"end": 0 "end": 0
}}; }};
function makePlSel(incPl=false) { function makePlSel(el, incPl=false) {
var plSelContent = ""; var plSelContent = "";
delete pJson["0"]; // remove filler preset delete pJson["0"]; // remove filler preset
var arr = Object.entries(pJson); var arr = Object.entries(pJson);
for (var i = 0; i < arr.length; i++) { for (var a of arr) {
var n = arr[i][1].n ? arr[i][1].n : "Preset " + arr[i][0]; var n = a[1].n ? a[1].n : "Preset " + a[0];
if (!incPl && arr[i][1].playlist && arr[i][1].playlist.ps) continue; // remove playlists, sub-playlists not yet supported if (!incPl && a[1].playlist && a[1].playlist.ps) continue; // remove playlists, sub-playlists not yet supported
plSelContent += `<option value="${arr[i][0]}">${n}</option>` plSelContent += `<option value="${a[0]}" ${a[0]==el?"selected":""}>${n}</option>`
} }
return plSelContent; return plSelContent;
} }
@ -1685,20 +1710,20 @@ function makeP(i,pl) {
content = content =
`<div id="ple${i}" style="margin-top:10px;"></div><label class="check revchkl">Shuffle `<div id="ple${i}" style="margin-top:10px;"></div><label class="check revchkl">Shuffle
<input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r||rep<0?"checked":""}> <input type="checkbox" id="pl${i}rtgl" onchange="plR(${i})" ${plJson[i].r||rep<0?"checked":""}>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<label class="check revchkl">Repeat indefinitely <label class="check revchkl">Repeat indefinitely
<input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep>0?"":"checked"}> <input type="checkbox" id="pl${i}rptgl" onchange="plR(${i})" ${rep>0?"":"checked"}>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<div id="pl${i}o1" style="display:${rep>0?"block":"none"}"> <div id="pl${i}o1" style="display:${rep>0?"block":"none"}">
<div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div> <div class="c">Repeat <input class="noslide" type="number" id="pl${i}rp" oninput="plR(${i})" max=127 min=0 value=${rep>0?rep:1}> times</div>
<div class="sel">End preset:<br> <div class="sel">End preset:<br>
<select class="sel sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}> <div class="sel-p"><select class="sel-ple" id="pl${i}selEnd" onchange="plR(${i})" data-val=${plJson[i].end?plJson[i].end:0}>
<option value="0">None</option> <option value="0">None</option>
<option value="255">Restore preset</option> <option value="255">Restore preset</option>
${makePlSel(true)} ${makePlSel(plJson[i].end?plJson[i].end:0, true)}
</select></div> </select></div></div>
</div> </div>
<div class="c"><button class="btn btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'>&#xe139;</i>Test</button></div>`; <div class="c"><button class="btn btn-p" onclick="testPl(${i}, this)"><i class='icons btn-icon'>&#xe139;</i>Test</button></div>`;
} else { } else {
@ -1708,24 +1733,24 @@ ${makePlSel(true)}
Include brightness Include brightness
</span> </span>
<input type="checkbox" id="p${i}ibtgl" checked> <input type="checkbox" id="p${i}ibtgl" checked>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<label class="check revchkl"> <label class="check revchkl">
<span class="lstIname"> <span class="lstIname">
Save segment bounds Save segment bounds
</span> </span>
<input type="checkbox" id="p${i}sbtgl" checked> <input type="checkbox" id="p${i}sbtgl" checked>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
<label class="check revchkl"> <label class="check revchkl">
<span class="lstIname"> <span class="lstIname">
Checked segments only Checked segments only
</span> </span>
<input type="checkbox" id="p${i}sbchk"> <input type="checkbox" id="p${i}sbchk">
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label>`; </label>`;
if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>0) { if (Array.isArray(lastinfo.maps) && lastinfo.maps.length>0) {
content += `<div class="sel">Ledmap:&nbsp;<select class="sel sel-p" id="p${i}lmp"><option value="">None</option>`; content += `<div class="sel">Ledmap:&nbsp;<select class="sel-p" id="p${i}lmp"><option value="">None</option>`;
for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k}</option>`; for (const k of (lastinfo.maps||[])) content += `<option value="${k}"${(i>0 && pJson[i].ledmap==k)?" selected":""}>${k}</option>`;
content += "</select></div>"; content += "</select></div>";
} }
@ -1740,7 +1765,7 @@ ${makePlSel(true)}
${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"} ${pl?"Show playlist editor":(i>0)?"Overwrite with state":"Use current state"}
</span> </span>
<input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}> <input type="checkbox" id="p${i}cstgl" onchange="tglCs(${i})" ${(i==0||pl)?"checked":""}>
<span class="checkmark schk"></span> <span class="checkmark"></span>
</label> </label>
</div> </div>
<div class="po2" id="p${i}o2"> <div class="po2" id="p${i}o2">
@ -1778,9 +1803,9 @@ function makePlEntry(p,i) {
<table> <table>
<tr> <tr>
<td width="80%" colspan=2> <td width="80%" colspan=2>
<select class="sel sel-pl" onchange="plePs(${p},${i},this)" data-val="${plJson[p].ps[i]}" data-index="${i}"> <div class="sel-p"><select class="sel-pl" onchange="plePs(${p},${i},this)" data-val="${plJson[p].ps[i]}" data-index="${i}">
${makePlSel()} ${makePlSel(plJson[p].ps[i])}
</select> </select></div>
</td> </td>
<td class="c"><button class="btn btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon">&#xe18a;</i></button></td> <td class="c"><button class="btn btn-pl-add" onclick="addPl(${p},${i})"><i class="icons btn-icon">&#xe18a;</i></button></td>
</tr> </tr>
@ -1952,6 +1977,20 @@ function setMiY(s)
requestJson(obj); requestJson(obj);
} }
function setMp12(s)
{
var value = gId(`seg${s}mp12`).selectedIndex;
var obj = {"seg": {"id": s, "mp12": value}};
requestJson(obj);
}
function setSSim(s)
{
var value = gId(`seg${s}ssim`).selectedIndex;
var obj = {"seg": {"id": s, "ssim": value}};
requestJson(obj);
}
function setTp(s) function setTp(s)
{ {
var tp = gId(`seg${s}tp`).checked; var tp = gId(`seg${s}tp`).checked;
@ -1990,6 +2029,8 @@ function setX(ind = null)
} else { } else {
d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true; d.querySelector(`#fxlist input[name="fx"][value="${ind}"]`).checked = true;
} }
/*
// this code also in updateSelectedFx
var selElement = d.querySelector('#fxlist .selected'); var selElement = d.querySelector('#fxlist .selected');
if (selElement) { if (selElement) {
selElement.classList.remove('selected'); selElement.classList.remove('selected');
@ -1997,7 +2038,7 @@ function setX(ind = null)
} }
d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected'); d.querySelector(`#fxlist .lstI[data-id="${ind}"]`).classList.add('selected');
*/
var obj = {"seg": {"fx": parseInt(ind)}}; var obj = {"seg": {"fx": parseInt(ind)}};
requestJson(obj); requestJson(obj);
} }
@ -2009,11 +2050,13 @@ function setPalette(paletteId = null)
} else { } else {
d.querySelector(`#pallist input[name="palette"][value="${paletteId}"]`).checked = true; d.querySelector(`#pallist input[name="palette"][value="${paletteId}"]`).checked = true;
} }
/*
var selElement = d.querySelector('#pallist .selected'); var selElement = d.querySelector('#pallist .selected');
if (selElement) { if (selElement) {
selElement.classList.remove('selected') selElement.classList.remove('selected')
} }
d.querySelector(`#pallist .lstI[data-id="${paletteId}"]`).classList.add('selected'); d.querySelector(`#pallist .lstI[data-id="${paletteId}"]`).classList.add('selected');
*/
var obj = {"seg": {"pal": paletteId}}; var obj = {"seg": {"pal": paletteId}};
requestJson(obj); requestJson(obj);
} }
@ -2127,6 +2170,7 @@ function saveP(i,pl)
} }
populatePresets(); populatePresets();
resetPUtil(); resetPUtil();
setTimeout(()=>{pmtLast=0; loadPresets();}, 500); // force reloading of presets
} }
function testPl(i,bt) { function testPl(i,bt) {
@ -2362,6 +2406,11 @@ function rSegs()
bt.style.color = "var(--c-f)"; bt.style.color = "var(--c-f)";
bt.innerHTML = "Reset segments"; bt.innerHTML = "Reset segments";
var obj = {"seg":[{"start":0,"stop":ledCount,"sel":true}]}; var obj = {"seg":[{"start":0,"stop":ledCount,"sel":true}]};
if (isM) {
obj.seg[0].stop = mw;
obj.seg[0].startX = 0;
obj.seg[0].stopY = mh;
}
for (let i=1; i<=lSeg; i++) obj.seg.push({"stop":0}); for (let i=1; i<=lSeg; i++) obj.seg.push({"stop":0});
requestJson(obj); requestJson(obj);
} }
@ -2422,6 +2471,10 @@ function hideModes(txt)
for (let e of (gId('fxlist').querySelectorAll('.lstI')||[])) { for (let e of (gId('fxlist').querySelectorAll('.lstI')||[])) {
if (e.querySelector('.lstIname').innerText.indexOf(txt) >= 0) e.classList.add("hide"); //else e.classList.remove("hide"); if (e.querySelector('.lstIname').innerText.indexOf(txt) >= 0) e.classList.add("hide"); //else e.classList.remove("hide");
} }
if (txt==="2D ") {
gId("filter1D").classList.add("hide");
gId("filter2D").classList.add("hide");
}
} }
function search(f,l=null) function search(f,l=null)
@ -2444,6 +2497,19 @@ function clean(c)
i.value=''; i.value='';
i.focus(); i.focus();
i.dispatchEvent(new Event('input')); i.dispatchEvent(new Event('input'));
if (i.parentElement.id=='fxFind') {
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e)=>{e.checked=false;});
}
}
function filterFx(o)
{
if (!o) return;
let i = gId('fxFind').children[0];
i.value=!o.checked?'':o.dataset.flt;
i.focus();
i.dispatchEvent(new Event('input'));
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e)=>{if(e!==o)e.checked=false;});
} }
// make sure "dur" and "transition" are arrays with at least the length of "ps" // make sure "dur" and "transition" are arrays with at least the length of "ps"

View File

@ -6,9 +6,38 @@
<title>WLED Settings</title> <title>WLED Settings</title>
<script> <script>
var d=document; var d=document;
var loc = false, locip;
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
function S(){GetV();} // https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function GetV(){} function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
function S(){
if (window.location.protocol == "file:") {
loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=0';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
}
</script> </script>
<style> <style>
body { body {

View File

@ -41,8 +41,6 @@
} }
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10'; var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=10';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
//if (loc) loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
//else { GetV(); UI(); }
} }
var maxPanels=64; var maxPanels=64;
@ -104,9 +102,6 @@ Serpentine: <input type="checkbox" name="P${i}S"></div>`;
gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none"; gId("pnl_add").style.display = (i<maxPanels) ? "inline":"none";
gId("pnl_rem").style.display = (i>1) ? "inline":"none"; gId("pnl_rem").style.display = (i>1) ? "inline":"none";
} }
//values injected by server while sending HTML
//fun-ction GetV() {}
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>

View File

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

View File

@ -8,10 +8,29 @@
<script> <script>
var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32 var d=document,laprev=55,maxB=1,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
var customStarts=false,startsDirty=[],maxCOOverrides=5; var customStarts=false,startsDirty=[],maxCOOverrides=5;
var loc = false, locip;
function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");} function H(){window.open("https://kno.wled.ge/features/settings/#led-settings");}
function B(){window.open("/settings","_self");} function B(){window.open("/settings","_self");}
function gId(n){return d.getElementById(n);} function gId(n){return d.getElementById(n);}
function off(n){d.getElementsByName(n)[0].value = -1;} function off(n){d.getElementsByName(n)[0].value = -1;}
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();checkSi();setABL();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout; var timeout;
function showToast(text, error = false) function showToast(text, error = false)
{ {
@ -506,12 +525,17 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
} }
} }
} }
function S(){GetV();checkSi();setABL();} function S(){
function GetV() if (window.location.protocol == "file:") {
{ loc = true;
//values injected by server while sending HTML locip = localStorage.getItem('locIp');
//d.um_p=[6,7,8,9,10,11,1];bLimits(3,4096,4000,1664);d.Sf.MS.checked=1;addLEDs(1);d.Sf.L00.value=2;d.Sf.LC0.value=30;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=15;d.Sf.CV0.checked=1;d.Sf.SL0.checked=0;addLEDs(1);d.Sf.L01.value=10;d.Sf.L11.value=10;d.Sf.L21.value=1;d.Sf.L31.value=10;d.Sf.LC1.value=60;d.Sf.LT1.value=80;d.Sf.CO1.value=1;d.Sf.LS1.value=0;d.Sf.CV1.checked=0;d.Sf.SL1.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=56;d.Sf.AW.value=3;d.Sf.BO.checked=1;d.Sf.BP.value=80;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=0;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=1;addBtn(0,0,0);addBtn(1,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=0; if (!locip) {
//d.um_p=[6,7,8,9,10,11,14,15,13,1,21,19,22,25,26,27,5,23,18,17];bLimits(10,2048,64000,8192);d.Sf.MS.checked=1;d.Sf.CCT.checked=0;addLEDs(1);d.Sf.L00.value=192;d.Sf.L10.value=168;d.Sf.L20.value=0;d.Sf.L30.value=61;d.Sf.LC0.value=421;d.Sf.LT0.value=80;d.Sf.CO0.value=1;d.Sf.LS0.value=0;d.Sf.CV0.checked=0;d.Sf.SL0.checked=0;d.Sf.RF0.checked=0;d.Sf.MA.value=850;d.Sf.LA.value=0;d.Sf.CA.value=127;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=0;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=1;d.Sf.BF.value=100;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=-1;d.Sf.RM.checked=1;addBtn(0,-1,0);addBtn(1,-1,0);addBtn(2,-1,0);addBtn(3,-1,0);d.Sf.TT.value=32;d.Sf.IR.value=-1;d.Sf.IT.value=8; locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=2';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
} }
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>

View File

@ -7,11 +7,30 @@
<title>Misc Settings</title> <title>Misc Settings</title>
<script> <script>
var d = document; var d = document;
var loc = false, locip;
function H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); } function H() { window.open("https://kno.wled.ge/features/settings/#security-settings"); }
function B() { window.open("/settings","_self"); } function B() { window.open("/settings","_self"); }
function U() { window.open("/update","_self"); } function U() { window.open("/update","_self"); }
function gId(s) { return d.getElementById(s); } function gId(s) { return d.getElementById(s); }
function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); } function isObj(o) { return (o && typeof o === 'object' && !Array.isArray(o)); }
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
function loadJS(FILE_URL, async = true) {
let scE = d.createElement("script");
scE.setAttribute("src", FILE_URL);
scE.setAttribute("type", "text/javascript");
scE.setAttribute("async", async);
d.body.appendChild(scE);
// success event
scE.addEventListener("load", () => {
//console.log("File loaded");
GetV();
});
// error event
scE.addEventListener("error", (ev) => {
console.log("Error on loading file", ev);
alert("Loading of configuration script failed.\nIncomplete page data!");
});
}
var timeout; var timeout;
function showToast(text, error = false) function showToast(text, error = false)
{ {
@ -40,16 +59,24 @@
event.preventDefault(); event.preventDefault();
return false; return false;
} }
function GetV() function S() {
{ if (window.location.protocol == "file:") {
//values injected by server while sending HTML loc = true;
locip = localStorage.getItem('locIp');
if (!locip) {
locip = prompt("File Mode. Please enter WLED IP!");
localStorage.setItem('locIp', locip);
}
}
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=6';
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
} }
</script> </script>
<style> <style>
@import url("style.css"); @import url("style.css");
</style> </style>
</head> </head>
<body onload="GetV()"> <body onload="S()">
<form id="form_s" name="Sf" method="post"> <form id="form_s" name="Sf" method="post">
<div class="toprow"> <div class="toprow">
<div class="helpB"><button type="button" onclick="H()">?</button></div> <div class="helpB"><button type="button" onclick="H()">?</button></div>

View File

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

View File

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

View File

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

View File

@ -81,6 +81,7 @@
} }
function addField(k,f,o,a=false) { //key, field, (sub)object, isArray function addField(k,f,o,a=false) { //key, field, (sub)object, isArray
if (isO(o)) { if (isO(o)) {
urows += '<hr style="width:260px">';
for (const [s,v] of Object.entries(o)) { for (const [s,v] of Object.entries(o)) {
// possibility to nest objects (only 1 level) // possibility to nest objects (only 1 level)
if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v); if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
@ -191,7 +192,6 @@
e.preventDefault(); e.preventDefault();
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914 if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
} }
//fun-ction GetV() {} // replaced during 'npm run build'
</script> </script>
<style>@import url("style.css");</style> <style>@import url("style.css");</style>
</head> </head>

View File

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

View File

@ -58,6 +58,9 @@ bool getJsonValue(const JsonVariant& element, DestType& destination, const Defau
//colors.cpp //colors.cpp
uint32_t color_blend(uint32_t,uint32_t,uint16_t,bool b16=false);
uint32_t color_add(uint32_t,uint32_t);
inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); } inline uint32_t colorFromRgbw(byte* rgbw) { return uint32_t((byte(rgbw[3]) << 24) | (byte(rgbw[0]) << 16) | (byte(rgbw[1]) << 8) | (byte(rgbw[2]))); }
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorKtoRGB(uint16_t kelvin, byte* rgb);
@ -129,7 +132,7 @@ void handleIR();
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0); void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0); bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root); void serializeInfo(JsonObject root);
void serializeModeNames(JsonArray arr, const char *qstring); void serializeModeNames(JsonArray arr, const char *qstring);
@ -296,7 +299,7 @@ class UsermodManager {
void onUpdateBegin(bool); void onUpdateBegin(bool);
bool add(Usermod* um); bool add(Usermod* um);
Usermod* lookup(uint16_t mod_id); Usermod* lookup(uint16_t mod_id);
byte getModCount(); byte getModCount() {return numMods;};
}; };
//usermods_list.cpp //usermods_list.cpp
@ -321,8 +324,10 @@ bool isAsterisksOnly(const char* str, byte maxLen);
bool requestJSONBufferLock(uint8_t module=255); bool requestJSONBufferLock(uint8_t module=255);
void releaseJSONBufferLock(); void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen); uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
uint16_t crc16(const unsigned char* data_p, size_t length); uint16_t crc16(const unsigned char* data_p, size_t length);
um_data_t* simulateSound(uint8_t simulationId);
//wled_eeprom.cpp //wled_eeprom.cpp
void applyMacro(byte index); void applyMacro(byte index);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -89,8 +89,8 @@ byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte highe
void changeEffect(uint8_t fx) void changeEffect(uint8_t fx)
{ {
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
strip.setMode(i, fx); strip.setMode(i, fx);
} }
@ -105,8 +105,8 @@ void changeEffect(uint8_t fx)
void changePalette(uint8_t pal) void changePalette(uint8_t pal)
{ {
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.palette = pal; seg.palette = pal;
} }
@ -124,8 +124,8 @@ void changeEffectSpeed(int8_t amount)
int16_t new_val = (int16_t) effectSpeed + amount; int16_t new_val = (int16_t) effectSpeed + amount;
effectSpeed = (byte)constrain(new_val,0,255); effectSpeed = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.speed = effectSpeed; seg.speed = effectSpeed;
} }
@ -135,7 +135,7 @@ void changeEffectSpeed(int8_t amount)
setValuesFromMainSeg(); setValuesFromMainSeg();
} }
} else { // if Effect == "solid Color", change the hue of the primary color } else { // if Effect == "solid Color", change the hue of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col; CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]); fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]); fastled_col.green = G(sseg.colors[0]);
@ -147,8 +147,8 @@ void changeEffectSpeed(int8_t amount)
prim_hsv.h = (byte)new_val; prim_hsv.h = (byte)new_val;
hsv2rgb_rainbow(prim_hsv, fastled_col); hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
} }
@ -171,8 +171,8 @@ void changeEffectIntensity(int8_t amount)
int16_t new_val = (int16_t) effectIntensity + amount; int16_t new_val = (int16_t) effectIntensity + amount;
effectIntensity = (byte)constrain(new_val,0,255); effectIntensity = (byte)constrain(new_val,0,255);
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.intensity = effectIntensity; seg.intensity = effectIntensity;
} }
@ -182,7 +182,7 @@ void changeEffectIntensity(int8_t amount)
setValuesFromMainSeg(); setValuesFromMainSeg();
} }
} else { // if Effect == "solid Color", change the saturation of the primary color } else { // if Effect == "solid Color", change the saturation of the primary color
WS2812FX::Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col; CRGB fastled_col;
fastled_col.red = R(sseg.colors[0]); fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]); fastled_col.green = G(sseg.colors[0]);
@ -192,8 +192,8 @@ void changeEffectIntensity(int8_t amount)
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255 prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
hsv2rgb_rainbow(prim_hsv, fastled_col); hsv2rgb_rainbow(prim_hsv, fastled_col);
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0])); seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
} }
@ -214,8 +214,8 @@ void changeColor(uint32_t c, int16_t cct=-1)
{ {
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
// main segment may not be selected! // main segment may not be selected!
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
byte capabilities = seg.getLightCapabilities(); byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0; uint32_t mask = 0;
@ -226,14 +226,14 @@ void changeColor(uint32_t c, int16_t cct=-1)
if (isRGB) mask |= 0x00FFFFFF; // RGB if (isRGB) mask |= 0x00FFFFFF; // RGB
if (hasW) mask |= 0xFF000000; // white if (hasW) mask |= 0xFF000000; // white
if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified
seg.setColor(0, c | 0xFFFFFF, i); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF) seg.setColor(0, c | 0xFFFFFF); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black } else if (c & mask) seg.setColor(0, c & mask); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct, i); if (isCCT && cct >= 0) seg.setCCT(cct);
} }
setValuesFromFirstSelectedSeg(); setValuesFromFirstSelectedSeg();
} else { } else {
byte i = strip.getMainSegmentId(); byte i = strip.getMainSegmentId();
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
byte capabilities = seg.getLightCapabilities(); byte capabilities = seg.getLightCapabilities();
uint32_t mask = 0; uint32_t mask = 0;
bool isRGB = GET_BIT(capabilities, 0); // is segment RGB capable bool isRGB = GET_BIT(capabilities, 0); // is segment RGB capable
@ -243,9 +243,9 @@ void changeColor(uint32_t c, int16_t cct=-1)
if (isRGB) mask |= 0x00FFFFFF; // RGB if (isRGB) mask |= 0x00FFFFFF; // RGB
if (hasW) mask |= 0xFF000000; // white if (hasW) mask |= 0xFF000000; // white
if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified if (hasW && !wSlider && (c & 0xFF000000)) { // segment has white channel & white channel is auto calculated & white specified
seg.setColor(0, c | 0xFFFFFF, i); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF) seg.setColor(0, c | 0xFFFFFF); // for accurate/brighter mode we fake white (since button may not set white color to 0xFFFFFF)
} else if (c & mask) seg.setColor(0, c & mask, i); // only apply if not black } else if (c & mask) seg.setColor(0, c & mask); // only apply if not black
if (isCCT && cct >= 0) seg.setCCT(cct, i); if (isCCT && cct >= 0) seg.setCCT(cct);
setValuesFromMainSeg(); setValuesFromMainSeg();
} }
stateChanged = true; stateChanged = true;
@ -253,7 +253,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
void changeWhite(int8_t amount, int16_t cct=-1) void changeWhite(int8_t amount, int16_t cct=-1)
{ {
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]); byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]); byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]); byte b = B(seg.colors[0]);
@ -424,7 +424,7 @@ void decodeIR24CT(uint32_t code)
void decodeIR40(uint32_t code) void decodeIR40(uint32_t code)
{ {
WS2812FX::Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
byte r = R(seg.colors[0]); byte r = R(seg.colors[0]);
byte g = G(seg.colors[0]); byte g = G(seg.colors[0]);
byte b = B(seg.colors[0]); byte b = B(seg.colors[0]);
@ -712,7 +712,7 @@ void initIR()
void handleIR() void handleIR()
{ {
if (irEnabled > 0 && millis() - irCheckedTime > 120) if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating())
{ {
irCheckedTime = millis(); irCheckedTime = millis();
if (irEnabled > 0) if (irEnabled > 0)

View File

@ -11,11 +11,19 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte id = elem["id"] | it; byte id = elem["id"] | it;
if (id >= strip.getMaxSegments()) return; if (id >= strip.getMaxSegments()) return;
WS2812FX::Segment& seg = strip.getSegment(id); int stop = elem["stop"] | -1;
WS2812FX::Segment prev = seg; //make a backup so we can tell if something changed
// if using vectors use this code to append segment
if (id >= strip.getSegmentsNum()) {
if (stop <= 0) return; // ignore empty/inactive segments
strip.appendSegment(Segment(0, strip.getLengthTotal()));
id = strip.getSegmentsNum()-1; // segments are added at the end of list
}
Segment& seg = strip.getSegment(id);
Segment prev = seg; //make a backup so we can tell if something changed
uint16_t start = elem["start"] | seg.start; uint16_t start = elem["start"] | seg.start;
int stop = elem["stop"] | -1;
if (stop < 0) { if (stop < 0) {
uint16_t len = elem["len"]; uint16_t len = elem["len"];
stop = (len > 0) ? start + len : seg.stop; stop = (len > 0) ? start + len : seg.stop;
@ -31,7 +39,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
elem.remove("rpt"); // remove for recursive call elem.remove("rpt"); // remove for recursive call
elem.remove("n"); // remove for recursive call elem.remove("n"); // remove for recursive call
uint16_t len = stop - start; uint16_t len = stop - start;
for (byte i=id+1; i<strip.getMaxSegments(); i++) { for (size_t i=id+1; i<strip.getMaxSegments(); i++) {
start = start + len; start = start + len;
if (start >= strip.getLengthTotal()) break; if (start >= strip.getLengthTotal()) break;
//TODO: add support for 2D //TODO: add support for 2D
@ -70,9 +78,14 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
uint16_t grp = elem["grp"] | seg.grouping; uint16_t grp = elem["grp"] | seg.grouping;
uint16_t spc = elem[F("spc")] | seg.spacing; uint16_t spc = elem[F("spc")] | seg.spacing;
uint16_t of = seg.offset; uint16_t of = seg.offset;
uint8_t soundSim = elem[F("ssim")] | seg.soundSim;
uint8_t map1D2D = elem[F("mp12")] | seg.map1D2D;
if (spc>0 && spc!=seg.spacing) strip.fill(BLACK, id); // clear spacing gaps if ((spc>0 && spc!=seg.spacing) || seg.map1D2D!=map1D2D) seg.fill(BLACK); // clear spacing gaps
seg.map1D2D = map1D2D & 0x03;
seg.soundSim = soundSim & 0x07;
uint16_t len = 1; uint16_t len = 1;
if (stop > start) len = stop - start; if (stop > start) len = stop - start;
@ -88,23 +101,23 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte segbri = seg.opacity; byte segbri = seg.opacity;
if (getVal(elem["bri"], &segbri)) { if (getVal(elem["bri"], &segbri)) {
if (segbri > 0) seg.setOpacity(segbri, id); if (segbri > 0) seg.setOpacity(segbri);
seg.setOption(SEG_OPTION_ON, segbri, id); seg.setOption(SEG_OPTION_ON, segbri);
} }
bool on = elem["on"] | seg.getOption(SEG_OPTION_ON); bool on = elem["on"] | seg.getOption(SEG_OPTION_ON);
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on; if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id); seg.setOption(SEG_OPTION_ON, on);
bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE); bool frz = elem["frz"] | seg.getOption(SEG_OPTION_FREEZE);
if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE); if (elem["frz"].is<const char*>() && elem["frz"].as<const char*>()[0] == 't') frz = !seg.getOption(SEG_OPTION_FREEZE);
seg.setOption(SEG_OPTION_FREEZE, frz, id); seg.setOption(SEG_OPTION_FREEZE, frz);
seg.setCCT(elem["cct"] | seg.cct, id); seg.setCCT(elem["cct"] | seg.cct);
JsonArray colarr = elem["col"]; JsonArray colarr = elem["col"];
if (!colarr.isNull()) if (!colarr.isNull())
{ {
for (uint8_t i = 0; i < 3; i++) for (size_t i = 0; i < 3; i++)
{ {
int rgbw[] = {0,0,0,0}; int rgbw[] = {0,0,0,0};
bool colValid = false; bool colValid = false;
@ -115,13 +128,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400 if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1; int kelvin = colarr[i] | -1;
if (kelvin < 0) continue; if (kelvin < 0) continue;
if (kelvin == 0) seg.setColor(i, 0, id); if (kelvin == 0) seg.setColor(i, 0);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw); if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
colValid = true; colValid = true;
} else { //HEX string, e.g. "FFAA00" } else { //HEX string, e.g. "FFAA00"
colValid = colorFromHexString(brgbw, hexCol); colValid = colorFromHexString(brgbw, hexCol);
} }
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c]; for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} else { //Array of ints (RGB or RGBW color), e.g. [255,160,0] } else { //Array of ints (RGB or RGBW color), e.g. [255,160,0]
byte sz = colX.size(); byte sz = colX.size();
if (sz == 0) continue; //do nothing on empty array if (sz == 0) continue; //do nothing on empty array
@ -132,7 +145,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (!colValid) continue; if (!colValid) continue;
seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]), id); seg.setColor(i, RGBW32(rgbw[0],rgbw[1],rgbw[2],rgbw[3]));
if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh if (seg.mode == FX_MODE_STATIC) strip.trigger(); //instant refresh
} }
} }
@ -149,31 +162,74 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
} }
#endif #endif
seg.setOption(SEG_OPTION_SELECTED, elem[F("sel")] | seg.getOption(SEG_OPTION_SELECTED)); seg.setOption(SEG_OPTION_SELECTED, elem["sel"] | seg.getOption(SEG_OPTION_SELECTED));
seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED)); seg.setOption(SEG_OPTION_REVERSED, elem["rev"] | seg.getOption(SEG_OPTION_REVERSED));
seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR )); seg.setOption(SEG_OPTION_MIRROR , elem[F("mi")] | seg.getOption(SEG_OPTION_MIRROR ));
// 2D options #ifndef WLED_DISABLE_2D
seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y)); seg.setOption(SEG_OPTION_REVERSED_Y, elem[F("rY")] | seg.getOption(SEG_OPTION_REVERSED_Y));
seg.setOption(SEG_OPTION_MIRROR_Y , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y )); seg.setOption(SEG_OPTION_MIRROR_Y , elem[F("mY")] | seg.getOption(SEG_OPTION_MIRROR_Y ));
seg.setOption(SEG_OPTION_TRANSPOSED, elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSED)); seg.setOption(SEG_OPTION_TRANSPOSED, elem[F("tp")] | seg.getOption(SEG_OPTION_TRANSPOSED));
#endif
byte fx = seg.mode; byte fx = seg.mode;
if (getVal(elem["fx"], &fx, 1, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 1-255 exact value) if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
if (!presetId && currentPlaylist>=0) unloadPlaylist(); if (!presetId && currentPlaylist>=0) unloadPlaylist();
strip.setMode(id, fx); if (fx != seg.mode) {
//seg.startTransition(strip.getTransition()); // set effect transitions
seg.markForReset();
seg.mode = fx;
// load default values from effect string if effect is selected without
// any other effect parameter (i.e. effect clicked in UI)
if ( elem[F("sx")].isNull()
&& elem[F("ix")].isNull()
&& elem["pal"].isNull()
&& elem[F("c1")].isNull()
&& elem[F("c2")].isNull()
&& elem[F("c3")].isNull() )
{
// compatibility mode begin
char buf[5]; // dummy buffer
for (int i=0; i<5; i++) {
uint8_t *var;
switch (i) {
case 0: var = &seg.speed; break;
case 1: var = &seg.intensity; break;
case 2: var = &seg.custom1; break;
case 3: var = &seg.custom2; break;
case 4: var = &seg.custom3; break;
}
extractModeSlider(fx, i, buf, 4, var);
}
extractModeSlider(fx, 255, buf, 4, &seg.palette);
//end compatibility mode
int16_t sOpt;
sOpt = extractModeDefaults(fx, SET_F("sx")); if (sOpt >= 0) seg.speed = sOpt;
sOpt = extractModeDefaults(fx, SET_F("ix")); if (sOpt >= 0) seg.intensity = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c1")); if (sOpt >= 0) seg.custom1 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c2")); if (sOpt >= 0) seg.custom2 = sOpt;
sOpt = extractModeDefaults(fx, SET_F("c3")); if (sOpt >= 0) seg.custom3 = sOpt;
sOpt = extractModeDefaults(fx, "pal"); if (sOpt >= 0 && sOpt < strip.getPaletteCount()) seg.palette = sOpt;
sOpt = extractModeDefaults(fx, SET_F("mp12")); if (sOpt >= 0) seg.map1D2D = sOpt & 0x03;
sOpt = extractModeDefaults(fx, SET_F("ssim")); if (sOpt >= 0) seg.soundSim = sOpt & 0x07;
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) seg.reverse = (bool)sOpt; // setOption(SEG_OPTION_REVERSED, (bool)sOpt); // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, SET_F("mi")); if (sOpt >= 0) seg.mirror = (bool)sOpt; // setOption(SEG_OPTION_MIRROR, (bool)sOpt); // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, SET_F("rY")); if (sOpt >= 0) seg.reverse_y = (bool)sOpt; // setOption(SEG_OPTION_REVERSED_Y, (bool)sOpt); // NOTE: setting this option is a risky business
sOpt = extractModeDefaults(fx, SET_F("mY")); if (sOpt >= 0) seg.mirror_y = (bool)sOpt; // setOption(SEG_OPTION_MIRROR_Y, (bool)sOpt); // NOTE: setting this option is a risky business
}
}
} }
//getVal also supports inc/decrementing and random //getVal also supports inc/decrementing and random
getVal(elem[F("sx")], &seg.speed); getVal(elem[F("sx")], &seg.speed);
getVal(elem[F("ix")], &seg.intensity); getVal(elem[F("ix")], &seg.intensity);
getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount()); getVal(elem["pal"], &seg.palette, 1, strip.getPaletteCount());
getVal(elem[F("c1")], &seg.custom1); getVal(elem[F("c1")], &seg.custom1);
getVal(elem[F("c2")], &seg.custom2); getVal(elem[F("c2")], &seg.custom2);
getVal(elem[F("c3")], &seg.custom3); getVal(elem[F("c3")], &seg.custom3);
JsonArray iarr = elem[F("i")]; //set individual LEDs JsonArray iarr = elem[F("i")]; //set individual LEDs
if (!iarr.isNull()) { if (!iarr.isNull()) {
uint8_t oldSegId = strip.setPixelSegment(id); //uint8_t oldSegId = strip.setPixelSegment(id);
// set brightness immediately and disable transition // set brightness immediately and disable transition
transitionDelayTemp = 0; transitionDelayTemp = 0;
@ -183,13 +239,13 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
// freeze and init to black // freeze and init to black
if (!seg.getOption(SEG_OPTION_FREEZE)) { if (!seg.getOption(SEG_OPTION_FREEZE)) {
seg.setOption(SEG_OPTION_FREEZE, true); seg.setOption(SEG_OPTION_FREEZE, true);
strip.fill(0); seg.fill(BLACK);
} }
uint16_t start = 0, stop = 0; uint16_t start = 0, stop = 0;
byte set = 0; //0 nothing set, 1 start set, 2 range set byte set = 0; //0 nothing set, 1 start set, 2 range set
for (uint16_t i = 0; i < iarr.size(); i++) { for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is<JsonInteger>()) { if(iarr[i].is<JsonInteger>()) {
if (!set) { if (!set) {
start = iarr[i]; start = iarr[i];
@ -208,29 +264,28 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
byte brgbw[] = {0,0,0,0}; byte brgbw[] = {0,0,0,0};
const char* hexCol = iarr[i]; const char* hexCol = iarr[i];
if (colorFromHexString(brgbw, hexCol)) { if (colorFromHexString(brgbw, hexCol)) {
for (uint8_t c = 0; c < 4; c++) rgbw[c] = brgbw[c]; for (size_t c = 0; c < 4; c++) rgbw[c] = brgbw[c];
} }
} }
if (set < 2) stop = start + 1; if (set < 2) stop = start + 1;
for (uint16_t i = start; i < stop; i++) { for (int i = start; i < stop; i++) {
if (strip.gammaCorrectCol) { if (strip.gammaCorrectCol) {
strip.setPixelColor(i, strip.gamma8(rgbw[0]), strip.gamma8(rgbw[1]), strip.gamma8(rgbw[2]), strip.gamma8(rgbw[3])); seg.setPixelColor(i, strip.gamma8(rgbw[0]), strip.gamma8(rgbw[1]), strip.gamma8(rgbw[2]), strip.gamma8(rgbw[3]));
} else { } else {
strip.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]); seg.setPixelColor(i, rgbw[0], rgbw[1], rgbw[2], rgbw[3]);
} }
} }
if (!set) start++; if (!set) start++;
set = 0; set = 0;
} }
} }
strip.setPixelSegment(oldSegId); //strip.setPixelSegment(oldSegId);
strip.trigger(); strip.trigger();
} }
// send UDP if not in preset and something changed that is not just selection // send UDP if not in preset and something changed that is not just selection
// send UDP if something changed that is not just selection or segment power/opacity // send UDP if something changed that is not just selection or segment power/opacity
if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true; if ((seg.differs(prev) & 0x7E) && seg.getOption(SEG_OPTION_ON)==prev.getOption(SEG_OPTION_ON)) stateChanged = true;
return;
} }
// deserializes WLED state (fileDoc points to doc object if called from web server) // deserializes WLED state (fileDoc points to doc object if called from web server)
@ -248,11 +303,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff(); if (root["on"].is<const char*>() && root["on"].as<const char*>()[0] == 't') toggleOnOff();
if (bri && !onBefore) { // unfreeze all segments when turning on if (bri && !onBefore) { // unfreeze all segments when turning on
for (uint8_t s=0; s < strip.getMaxSegments(); s++) { for (size_t s=0; s < strip.getSegmentsNum(); s++) {
strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false, s); strip.getSegment(s).setOption(SEG_OPTION_FREEZE, false);
} }
if (realtimeMode && !realtimeOverride && useMainSegmentOnly) { // keep live segment frozen if live if (realtimeMode && !realtimeOverride && useMainSegmentOnly) { // keep live segment frozen if live
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true, strip.getMainSegmentId()); strip.getMainSegment().setOption(SEG_OPTION_FREEZE, true);
} }
} }
@ -304,7 +359,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
realtimeOverride = root[F("lor")] | realtimeOverride; realtimeOverride = root[F("lor")] | realtimeOverride;
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS; if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) { if (realtimeMode && useMainSegmentOnly) {
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId()); strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
} }
if (root.containsKey("live")) { if (root.containsKey("live")) {
@ -326,13 +381,11 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
if (id < 0) { if (id < 0) {
//apply all selected segments //apply all selected segments
//bool didSet = false; //bool didSet = false;
for (byte s = 0; s < strip.getMaxSegments(); s++) { for (size_t s = 0; s < strip.getSegmentsNum(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s); Segment &sg = strip.getSegment(s);
if (sg.isActive()) { if (sg.isSelected()) {
if (sg.isSelected()) { deserializeSegment(segVar, s, presetId);
deserializeSegment(segVar, s, presetId); //didSet = true;
//didSet = true;
}
} }
} }
//TODO: not sure if it is good idea to change first active but unselected segment //TODO: not sure if it is good idea to change first active but unselected segment
@ -342,8 +395,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
} }
} else { } else {
JsonArray segs = segVar.as<JsonArray>(); JsonArray segs = segVar.as<JsonArray>();
for (JsonObject elem : segs) for (JsonObject elem : segs) {
{
deserializeSegment(elem, it, presetId); deserializeSegment(elem, it, presetId);
it++; it++;
} }
@ -396,7 +448,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
return stateResponse; return stateResponse;
} }
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds) void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, bool segmentBounds)
{ {
root["id"] = id; root["id"] = id;
if (segmentBounds) { if (segmentBounds) {
@ -423,7 +475,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
// this will reduce RAM footprint from ~300 bytes to 84 bytes per segment // this will reduce RAM footprint from ~300 bytes to 84 bytes per segment
char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255) char colstr[70]; colstr[0] = '['; colstr[1] = '\0'; //max len 68 (5 chan, all 255)
const char *format = strip.hasWhiteChannel() ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]"); const char *format = strip.hasWhiteChannel() ? PSTR("[%u,%u,%u,%u]") : PSTR("[%u,%u,%u]");
for (uint8_t i = 0; i < 3; i++) for (size_t i = 0; i < 3; i++)
{ {
byte segcol[4]; byte* c = segcol; byte segcol[4]; byte* c = segcol;
segcol[0] = R(seg.colors[i]); segcol[0] = R(seg.colors[i]);
@ -452,6 +504,8 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y); root[F("mY")] = seg.getOption(SEG_OPTION_MIRROR_Y);
root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSED); root[F("tp")] = seg.getOption(SEG_OPTION_TRANSPOSED);
} }
root[F("ssim")] = seg.soundSim;
root[F("mp12")] = seg.map1D2D;
} }
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds) void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
@ -493,9 +547,17 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
bool selectedSegmentsOnly = root[F("sc")] | false; bool selectedSegmentsOnly = root[F("sc")] | false;
JsonArray seg = root.createNestedArray("seg"); JsonArray seg = root.createNestedArray("seg");
for (byte s = 0; s < strip.getMaxSegments(); s++) { for (size_t s = 0; s < strip.getMaxSegments(); s++) {
WS2812FX::Segment &sg = strip.getSegment(s); if (s >= strip.getSegmentsNum()) {
if (selectedSegmentsOnly && !sg.isSelected()) continue; if (forPreset && segmentBounds) { //disable segments not part of preset
JsonObject seg0 = seg.createNestedObject();
seg0["stop"] = 0;
continue;
} else
break;
}
Segment &sg = strip.getSegment(s);
if (!forPreset && selectedSegmentsOnly && !sg.isSelected()) continue;
if (sg.isActive()) { if (sg.isActive()) {
JsonObject seg0 = seg.createNestedObject(); JsonObject seg0 = seg.createNestedObject();
serializeSegment(seg0, sg, s, forPreset, segmentBounds); serializeSegment(seg0, sg, s, forPreset, segmentBounds);
@ -518,18 +580,22 @@ void serializeInfo(JsonObject root)
leds["fps"] = strip.getFps(); leds["fps"] = strip.getFps();
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0; leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
leds[F("maxseg")] = strip.getMaxSegments(); leds[F("maxseg")] = strip.getMaxSegments();
//leds[F("actseg")] = strip.getActiveSegmentsNum();
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config //leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
#ifndef WLED_DISABLE_2D
if (strip.isMatrix) { if (strip.isMatrix) {
JsonObject matrix = leds.createNestedObject("matrix"); JsonObject matrix = leds.createNestedObject("matrix");
matrix["w"] = strip.matrixWidth; matrix["w"] = strip.matrixWidth;
matrix["h"] = strip.matrixHeight; matrix["h"] = strip.matrixHeight;
} }
#endif
uint8_t totalLC = 0; uint8_t totalLC = 0;
JsonArray lcarr = leds.createNestedArray(F("seglc")); JsonArray lcarr = leds.createNestedArray(F("seglc"));
uint8_t nSegs = strip.getLastActiveSegmentId(); size_t nSegs = strip.getSegmentsNum();
for (byte s = 0; s <= nSegs; s++) { for (size_t s = 0; s < nSegs; s++) {
if (!strip.getSegment(s).isActive()) continue;
uint8_t lc = strip.getSegment(s).getLightCapabilities(); uint8_t lc = strip.getSegment(s).getLightCapabilities();
totalLC |= lc; totalLC |= lc;
lcarr.add(lc); lcarr.add(lc);
@ -577,7 +643,7 @@ void serializeInfo(JsonObject root)
root[F("palcount")] = strip.getPaletteCount(); root[F("palcount")] = strip.getPaletteCount();
JsonArray ledmaps = root.createNestedArray(F("maps")); JsonArray ledmaps = root.createNestedArray(F("maps"));
for (uint8_t i=0; i<10; i++) { for (size_t i=0; i<10; i++) {
char fileName[16]; char fileName[16];
strcpy_P(fileName, PSTR("/ledmap")); strcpy_P(fileName, PSTR("/ledmap"));
if (i) sprintf(fileName +7, "%d", i); if (i) sprintf(fileName +7, "%d", i);
@ -956,7 +1022,7 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
obuf = buffer; obuf = buffer;
olen = 9; olen = 9;
for (uint16_t i= 0; i < used; i += n) for (size_t i= 0; i < used; i += n)
{ {
uint32_t c = strip.getPixelColor(i); uint32_t c = strip.getPixelColor(i);
uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map uint8_t r = qadd8(W(c), R(c)); //add white channel to RGB channels as a simple RGBW -> RGB map

View File

@ -8,7 +8,7 @@ void setValuesFromMainSeg() { setValuesFromSegment(strip.getMainSegment
void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); } void setValuesFromFirstSelectedSeg() { setValuesFromSegment(strip.getFirstSelectedSegId()); }
void setValuesFromSegment(uint8_t s) void setValuesFromSegment(uint8_t s)
{ {
WS2812FX::Segment& seg = strip.getSegment(s); Segment& seg = strip.getSegment(s);
col[0] = R(seg.colors[0]); col[0] = R(seg.colors[0]);
col[1] = G(seg.colors[0]); col[1] = G(seg.colors[0]);
col[2] = B(seg.colors[0]); col[2] = B(seg.colors[0]);
@ -30,9 +30,9 @@ void applyValuesToSelectedSegs()
{ {
// copy of first selected segment to tell if value was updated // copy of first selected segment to tell if value was updated
uint8_t firstSel = strip.getFirstSelectedSegId(); uint8_t firstSel = strip.getFirstSelectedSegId();
WS2812FX::Segment selsegPrev = strip.getSegment(firstSel); Segment selsegPrev = strip.getSegment(firstSel);
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue; if (i != firstSel && (!seg.isActive() || !seg.isSelected())) continue;
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;} if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
@ -41,8 +41,8 @@ void applyValuesToSelectedSegs()
if (effectCurrent != selsegPrev.mode) {strip.setMode(i, effectCurrent); stateChanged = true;} if (effectCurrent != selsegPrev.mode) {strip.setMode(i, effectCurrent); stateChanged = true;}
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]); uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]); uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0, i); stateChanged = true;} if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;}
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1, i); stateChanged = true;} if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;}
} }
} }

View File

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

View File

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

View File

@ -56,7 +56,8 @@ enum struct PinOwner : uint8_t {
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager // #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h"
UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h" UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h"
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE // 0x1E // Usermod: "audio_reactive.h" UM_BME280 = USERMOD_ID_BME280, // 0x18 // Usermod "usermod_bme280.h -- Uses "standard" HW_I2C pins
UM_Audioreactive = USERMOD_ID_AUDIOREACTIVE // 0x1E // Usermod "audio_reactive.h"
}; };
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");

View File

@ -84,7 +84,7 @@ void handlePresets(bool force)
presetToApply = 0; //clear request for preset presetToApply = 0; //clear request for preset
callModeToApply = 0; callModeToApply = 0;
DEBUG_PRINTLN(F("Applying preset: ")); DEBUG_PRINT(F("Applying preset: "));
DEBUG_PRINTLN(tmpPreset); DEBUG_PRINTLN(tmpPreset);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32

View File

@ -86,6 +86,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
Bus::setAutoWhiteMode(request->arg(F("AW")).toInt()); Bus::setAutoWhiteMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt());
bool busesChanged = false;
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) { for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin
char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length char lc[4] = "LC"; lc[2] = 48+s; lc[3] = 0; //strip length
@ -98,7 +99,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode char aw[4] = "AW"; aw[2] = 48+s; aw[3] = 0; //auto white mode
char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap char wo[4] = "WO"; wo[2] = 48+s; wo[3] = 0; //channel swap
if (!request->hasArg(lp)) { if (!request->hasArg(lp)) {
DEBUG_PRINTLN(F("No data.")); break; DEBUG_PRINT(F("No data for "));
DEBUG_PRINTLN(s);
break;
} }
for (uint8_t i = 0; i < 5; i++) { for (uint8_t i = 0; i < 5; i++) {
lp[1] = 48+i; lp[1] = 48+i;
@ -118,10 +121,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
awmode = request->arg(aw).toInt(); awmode = request->arg(aw).toInt();
channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0; channelSwap = (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) ? request->arg(wo).toInt() : 0;
// actual finalization is done in WLED::loop() (removing old busses and adding new) // actual finalization is done in WLED::loop() (removing old busses and adding new)
// this may happen even before this loop is finished so we do "doInitBusses" after the loop
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode); busConfigs[s] = new BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode);
doInitBusses = true; busesChanged = true;
} }
//doInitBusses = busesChanged; // we will do that below to ensure all input data is processed
ColorOrderMap com = {}; ColorOrderMap com = {};
for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) { for (uint8_t s = 0; s < WLED_MAX_COLOR_ORDER_MAPPINGS; s++) {
@ -197,6 +202,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (t >= 0 && t < 4) strip.paletteBlend = t; if (t >= 0 && t < 4) strip.paletteBlend = t;
t = request->arg(F("BF")).toInt(); t = request->arg(F("BF")).toInt();
if (t > 0) briMultiplier = t; if (t > 0) briMultiplier = t;
doInitBusses = busesChanged;
} }
//UI //UI
@ -547,6 +554,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
#ifndef WLED_DISABLE_2D
//2D panels //2D panels
if (subPage == 10) if (subPage == 10)
{ {
@ -570,6 +578,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
strip.setUpMatrix(); // will check limits strip.setUpMatrix(); // will check limits
} }
#endif
lastEditTime = millis(); lastEditTime = millis();
if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init) if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
@ -599,17 +608,17 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SS=")); pos = req.indexOf(F("SS="));
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t < strip.getMaxSegments()) { if (t < strip.getSegmentsNum()) {
selectedSeg = t; selectedSeg = t;
singleSegment = true; singleSegment = true;
} }
} }
WS2812FX::Segment& selseg = strip.getSegment(selectedSeg); Segment& selseg = strip.getSegment(selectedSeg);
pos = req.indexOf(F("SV=")); //segment selected pos = req.indexOf(F("SV=")); //segment selected
if (pos > 0) { if (pos > 0) {
byte t = getNumVal(&req, pos); byte t = getNumVal(&req, pos);
if (t == 2) for (uint8_t i = 0; i < strip.getMaxSegments(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments if (t == 2) for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) strip.getSegment(i).setOption(SEG_OPTION_SELECTED, 0); // unselect other segments
selseg.setOption(SEG_OPTION_SELECTED, t); selseg.setOption(SEG_OPTION_SELECTED, t);
} }
@ -657,9 +666,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
pos = req.indexOf(F("SB=")); //Segment brightness/opacity pos = req.indexOf(F("SB=")); //Segment brightness/opacity
if (pos > 0) { if (pos > 0) {
byte segbri = getNumVal(&req, pos); byte segbri = getNumVal(&req, pos);
selseg.setOption(SEG_OPTION_ON, segbri, selectedSeg); selseg.setOption(SEG_OPTION_ON, segbri);
if (segbri) { if (segbri) {
selseg.setOpacity(segbri, selectedSeg); selseg.setOpacity(segbri);
} }
} }
@ -762,7 +771,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (pos > 0) { if (pos > 0) {
colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str()); colorFromDecOrHexString(tmpCol, (char*)req.substring(pos + 3).c_str());
uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]); uint32_t col2 = RGBW32(tmpCol[0], tmpCol[1], tmpCol[2], tmpCol[3]);
selseg.setColor(2, col2, selectedSeg); // defined above (SS= or main) selseg.setColor(2, col2); // defined above (SS= or main)
stateChanged = true; stateChanged = true;
if (!singleSegment) strip.setColor(2, col2); // will set color to all active & selected segments if (!singleSegment) strip.setColor(2, col2); // will set color to all active & selected segments
} }
@ -791,14 +800,14 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
if (col0Changed) { if (col0Changed) {
stateChanged = true; stateChanged = true;
uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]); uint32_t colIn0 = RGBW32(colIn[0], colIn[1], colIn[2], colIn[3]);
selseg.setColor(0, colIn0, selectedSeg); selseg.setColor(0, colIn0);
if (!singleSegment) strip.setColor(0, colIn0); // will set color to all active & selected segments if (!singleSegment) strip.setColor(0, colIn0); // will set color to all active & selected segments
} }
if (col1Changed) { if (col1Changed) {
stateChanged = true; stateChanged = true;
uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]); uint32_t colIn1 = RGBW32(colInSec[0], colInSec[1], colInSec[2], colInSec[3]);
selseg.setColor(1, colIn1, selectedSeg); selseg.setColor(1, colIn1);
if (!singleSegment) strip.setColor(1, colIn1); // will set color to all active & selected segments if (!singleSegment) strip.setColor(1, colIn1); // will set color to all active & selected segments
} }
@ -815,8 +824,8 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged); stateChanged |= (fxModeChanged || speedChanged || intensityChanged || paletteChanged);
// apply to main and all selected segments to prevent #1618. // apply to main and all selected segments to prevent #1618.
for (uint8_t i = 0; i < strip.getMaxSegments(); i++) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
WS2812FX::Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
if (fxModeChanged) strip.setMode(i, effectIn); if (fxModeChanged) strip.setMode(i, effectIn);
if (speedChanged) seg.speed = speedIn; if (speedChanged) seg.speed = speedIn;
@ -918,7 +927,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
realtimeOverride = getNumVal(&req, pos); realtimeOverride = getNumVal(&req, pos);
if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS; if (realtimeOverride > 2) realtimeOverride = REALTIME_OVERRIDE_ALWAYS;
if (realtimeMode && useMainSegmentOnly) { if (realtimeMode && useMainSegmentOnly) {
strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride, strip.getMainSegmentId()); strip.getMainSegment().setOption(SEG_OPTION_FREEZE, !realtimeOverride);
} }
} }

View File

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

View File

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

View File

@ -238,14 +238,13 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
char lineBuffer[256]; char lineBuffer[256];
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode]))); //strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
strcpy_P(lineBuffer, strip.getModeData(mode)); strcpy_P(lineBuffer, strip.getModeData(mode));
if (strlen(lineBuffer) > 0) { size_t len = strlen(lineBuffer);
size_t j = 0; size_t j = 0;
for (; j < maxLen; j++) { for (; j < maxLen && j < len; j++) {
if (lineBuffer[j] == '\0' || lineBuffer[j] == '@') break; if (lineBuffer[j] == '\0' || lineBuffer[j] == '@') break;
dest[j] = lineBuffer[j]; dest[j] = lineBuffer[j];
}
dest[j] = 0; // terminate string
} }
dest[j] = 0; // terminate string
return strlen(dest); return strlen(dest);
} else return 0; } else return 0;
} }
@ -282,7 +281,7 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
// extracts effect slider data (1st group after @) // extracts effect slider data (1st group after @)
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen) uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var)
{ {
dest[0] = '\0'; // start by clearing buffer dest[0] = '\0'; // start by clearing buffer
@ -292,30 +291,50 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
int16_t start = lineBuffer.indexOf('@'); int16_t start = lineBuffer.indexOf('@');
int16_t stop = lineBuffer.indexOf(';', start); int16_t stop = lineBuffer.indexOf(';', start);
if (start>0 && stop>0) { if (start>0 && stop>0) {
String names = lineBuffer.substring(start+1, stop); String names = lineBuffer.substring(start, stop); // include @
int16_t nameBegin = 0, nameEnd; int16_t nameBegin = 1, nameEnd, nameDefault;
for (size_t i=0; i<=slider; i++) { if (slider < 10) {
const char *tmpstr; for (size_t i=0; i<=slider; i++) {
dest[0] = '\0'; //clear dest buffer const char *tmpstr;
if (i > 0 && nameBegin == 0) break; // there are no more names dest[0] = '\0'; //clear dest buffer
nameEnd = names.indexOf(',', nameBegin); if (nameBegin == 0) break; // there are no more names
if (names.charAt(nameBegin) == '!') { nameEnd = names.indexOf(',', nameBegin);
switch (i) { if (i == slider) {
case 0: tmpstr = PSTR("FX Speed"); break; nameDefault = names.indexOf('=', nameBegin); // find default value
case 1: tmpstr = PSTR("FX Intensity"); break; if (nameDefault > 0 && var && ((nameEnd>0 && nameDefault<nameEnd) || nameEnd<0)) {
case 2: tmpstr = PSTR("FX Custom 1"); break; *var = (uint8_t)atoi(names.substring(nameDefault+1).c_str());
case 3: tmpstr = PSTR("FX Custom 2"); break; }
case 4: tmpstr = PSTR("FX Custom 3"); break; if (names.charAt(nameBegin) == '!') {
default: tmpstr = PSTR("FX Custom"); break; switch (slider) {
case 0: tmpstr = PSTR("FX Speed"); break;
case 1: tmpstr = PSTR("FX Intensity"); break;
case 2: tmpstr = PSTR("FX Custom 1"); break;
case 3: tmpstr = PSTR("FX Custom 2"); break;
case 4: tmpstr = PSTR("FX Custom 3"); break;
default: tmpstr = PSTR("FX Custom"); break;
}
} else {
if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
else tmpstr = names.substring(nameBegin, nameEnd).c_str();
}
strlcpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous)
}
nameBegin = nameEnd+1; // next name (if "," is not found it will be 0)
} // next slider
} else if (slider == 255) {
// palette
strlcpy(dest, "pal", maxLen);
names = lineBuffer.substring(stop+1); // stop has index of color slot names
nameBegin = names.indexOf(';'); // look for palette
if (nameBegin >= 0) {
nameEnd = names.indexOf(';', nameBegin+1);
if (!isdigit(names[nameBegin+1])) nameBegin = names.indexOf('=', nameBegin+1); // look for default value
if (nameEnd >= 0 && nameBegin > nameEnd) nameBegin = -1;
if (nameBegin >= 0 && var) {
*var = (uint8_t)atoi(names.substring(nameBegin+1).c_str());
} }
} else {
if (nameEnd<0) tmpstr = names.substring(nameBegin).c_str(); // did not find ",", last name?
else tmpstr = names.substring(nameBegin, nameEnd).c_str();
} }
strncpy(dest, tmpstr, maxLen); // copy the name into buffer (replacing previous) }
nameBegin = nameEnd+1; // next name (if "," is not found it will be 0)
} // next slider
// we have slider name (including default value) in the dest buffer // we have slider name (including default value) in the dest buffer
for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value for (size_t i=0; i<strlen(dest); i++) if (dest[i]=='=') { dest[i]='\0'; break; } // truncate default value
@ -325,6 +344,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
case 0: strncpy_P(dest, PSTR("FX Speed"), maxLen); break; case 0: strncpy_P(dest, PSTR("FX Speed"), maxLen); break;
case 1: strncpy_P(dest, PSTR("FX Intensity"), maxLen); break; case 1: strncpy_P(dest, PSTR("FX Intensity"), maxLen); break;
} }
dest[maxLen] = '\0'; // strncpy does not necessarily null terminate string
} }
} }
return strlen(dest); return strlen(dest);
@ -333,6 +353,23 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
} }
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
{
if (mode < strip.getModeCount()) {
String lineBuffer = strip.getModeData(mode);
if (lineBuffer.length() > 0) {
int16_t start = lineBuffer.lastIndexOf(';');
if (start<0) return -1;
int16_t stop = lineBuffer.indexOf(segVar, start+1);
if (stop<0) return -1;
return atoi(lineBuffer.substring(stop+strlen(segVar)+1).c_str());
}
}
return -1;
}
uint16_t crc16(const unsigned char* data_p, size_t length) { uint16_t crc16(const unsigned char* data_p, size_t length) {
uint8_t x; uint8_t x;
uint16_t crc = 0xFFFF; uint16_t crc = 0xFFFF;
@ -343,4 +380,155 @@ uint16_t crc16(const unsigned char* data_p, size_t length) {
crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x); crc = (crc << 8) ^ ((uint16_t)(x << 12)) ^ ((uint16_t)(x <<5)) ^ ((uint16_t)x);
} }
return crc; return crc;
} }
///////////////////////////////////////////////////////////////////////////////
// Begin simulateSound (to enable audio enhanced effects to display something)
///////////////////////////////////////////////////////////////////////////////
// Currently 4 types defined, to be fine tuned and new types added
typedef enum UM_SoundSimulations {
UMS_BeatSin = 0,
UMS_WeWillRockYou,
UMS_10_3,
UMS_14_3
} um_soundSimulations_t;
// this is still work in progress
um_data_t* simulateSound(uint8_t simulationId)
{
static float sampleAvg;
static uint8_t soundAgc;
static float sampleAgc;
static int16_t sampleRaw;
static int16_t rawSampleAgc;
static uint8_t samplePeak;
static float FFT_MajorPeak;
static float FFT_Magnitude;
static uint8_t maxVol;
static uint8_t binNum;
static float multAgc;
float sampleGain;
uint8_t soundSquelch;
uint8_t inputLevel;
//arrays
uint8_t *fftResult;
uint8_t *myVals;
float *fftBin;
static um_data_t* um_data = nullptr;
if (!um_data) {
//claim storage for arrays
fftResult = (uint8_t *)malloc(sizeof(uint8_t) * 16);
myVals = (uint8_t *)malloc(sizeof(uint8_t) * 32);
fftBin = (float *)malloc(sizeof(float) * 256); // not used (for debugging purposes)
// initialize um_data pointer structure
// NOTE!!!
// This may change as AudioReactive usermod may change
um_data = new um_data_t;
um_data->u_size = 18;
um_data->u_type = new um_types_t[um_data->u_size];
um_data->u_data = new void*[um_data->u_size];
um_data->u_data[ 0] = &sampleAvg;
um_data->u_data[ 1] = &soundAgc;
um_data->u_data[ 2] = &sampleAgc;
um_data->u_data[ 3] = &sampleRaw;
um_data->u_data[ 4] = &rawSampleAgc;
um_data->u_data[ 5] = &samplePeak;
um_data->u_data[ 6] = &FFT_MajorPeak;
um_data->u_data[ 7] = &FFT_Magnitude;
um_data->u_data[ 8] = fftResult;
um_data->u_data[ 9] = &maxVol;
um_data->u_data[10] = &binNum;
um_data->u_data[11] = &multAgc;
um_data->u_data[14] = myVals; //*used (only once, Pixels)
um_data->u_data[13] = &sampleGain;
um_data->u_data[15] = &soundSquelch;
um_data->u_data[16] = fftBin; //only used in binmap
um_data->u_data[17] = &inputLevel;
} else {
// get arrays from um_data
fftResult = (uint8_t*)um_data->u_data[8];
myVals = (uint8_t*)um_data->u_data[14];
fftBin = (float*)um_data->u_data[16];
}
uint32_t ms = millis();
switch (simulationId) {
default:
case UMS_BeatSin:
for (int i = 0; i<16; i++)
fftResult[i] = beatsin8(120 / (i+1), 0, 255);
// fftResult[i] = (beatsin8(120, 0, 255) + (256/16 * i)) % 256;
sampleAvg = fftResult[8];
break;
case UMS_WeWillRockYou:
if (ms%2000 < 200) {
sampleAvg = random8(255);
for (int i = 0; i<5; i++)
fftResult[i] = random8(255);
}
else if (ms%2000 < 400) {
sampleAvg = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
else if (ms%2000 < 600) {
sampleAvg = random8(255);
for (int i = 5; i<11; i++)
fftResult[i] = random8(255);
}
else if (ms%2000 < 800) {
sampleAvg = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
else if (ms%2000 < 1000) {
sampleAvg = random8(255);
for (int i = 11; i<16; i++)
fftResult[i] = random8(255);
}
else {
sampleAvg = 0;
for (int i = 0; i<16; i++)
fftResult[i] = 0;
}
break;
case UMS_10_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(90 / (i+1), 0, 200)*15 + (ms>>10), ms>>3);
sampleAvg = fftResult[8];
break;
case UMS_14_3:
for (int i = 0; i<16; i++)
fftResult[i] = inoise8(beatsin8(120 / (i+1), 10, 30)*10 + (ms>>14), ms>>3);
sampleAvg = fftResult[8];
break;
}
//derive other vars from sampleAvg
//sampleAvg = mapf(sampleAvg, 0, 255, 0, 255); // help me out here
soundAgc = 0; //only avg in simulations
sampleAgc = sampleAvg;
sampleRaw = sampleAvg;
sampleRaw = map(sampleRaw, 50, 190, 0, 224);
rawSampleAgc = sampleAvg;
samplePeak = random8() > 250;
FFT_MajorPeak = sampleAvg;
FFT_Magnitude = sampleAvg;
multAgc = sampleAvg;
myVals[millis()%32] = sampleAvg; // filling values semi randomly (why?)
sampleGain = 40;
soundSquelch = 10;
maxVol = 10; // this gets feedback fro UI
binNum = 8; // this gets feedback fro UI
inputLevel = 128; // this gets feedback fro UI
return um_data;
}

View File

@ -118,6 +118,7 @@ void WLED::loop()
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis; if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif #endif
} }
yield(); yield();
#ifdef ESP8266 #ifdef ESP8266
MDNS.update(); MDNS.update();
@ -692,6 +693,7 @@ void WLED::handleConnection()
DEBUG_PRINT(F("Heap too low! ")); DEBUG_PRINT(F("Heap too low! "));
DEBUG_PRINTLN(heap); DEBUG_PRINTLN(heap);
forceReconnect = true; forceReconnect = true;
strip.purgeSegments(); // remove inactive segments from memory
} }
lastHeap = heap; lastHeap = heap;
heapTime = now; heapTime = now;

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2206281 #define VERSION 2207271
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG
@ -152,19 +152,19 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
#define PSRAMDynamicJsonDocument DynamicJsonDocument #define PSRAMDynamicJsonDocument DynamicJsonDocument
#endif #endif
#include "const.h"
#include "fcn_declare.h" #include "fcn_declare.h"
#include "html_ui.h" #include "html_ui.h"
#ifdef WLED_ENABLE_SIMPLE_UI #ifdef WLED_ENABLE_SIMPLE_UI
#include "html_simple.h" #include "html_simple.h"
#endif #endif
#include "html_settings.h" #include "html_settings.h"
#include "html_other.h" #include "html_other.h"
#include "FX.h"
#include "ir_codes.h" #include "ir_codes.h"
#include "const.h"
#include "NodeStruct.h" #include "NodeStruct.h"
#include "pin_manager.h" #include "pin_manager.h"
#include "bus_manager.h" #include "bus_manager.h"
#include "FX.h"
#ifndef CLIENT_SSID #ifndef CLIENT_SSID
#define CLIENT_SSID DEFAULT_CLIENT_SSID #define CLIENT_SSID DEFAULT_CLIENT_SSID

View File

@ -418,7 +418,7 @@ void deEEP() {
segObj[F("ix")] = EEPROM.read(i+16); segObj[F("ix")] = EEPROM.read(i+16);
segObj["pal"] = EEPROM.read(i+17); segObj["pal"] = EEPROM.read(i+17);
} else { } else {
WS2812FX::Segment* seg = strip.getSegments(); Segment* seg = strip.getSegments();
memcpy(seg, EEPROM.getDataPtr() +i+2, 240); memcpy(seg, EEPROM.getDataPtr() +i+2, 240);
if (ver == 2) { //versions before 2004230 did not have opacity if (ver == 2) { //versions before 2004230 did not have opacity
for (byte j = 0; j < strip.getMaxSegments(); j++) for (byte j = 0; j < strip.getMaxSegments(); j++)

View File

@ -297,8 +297,8 @@ void initServer()
DEBUG_PRINTLN(F("Update Success")); DEBUG_PRINTLN(F("Update Success"));
} else { } else {
DEBUG_PRINTLN(F("Update Failed")); DEBUG_PRINTLN(F("Update Failed"));
WLED::instance().enableWatchdog();
usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init) usermods.onUpdateBegin(false); // notify usermods that update has failed (some may require task init)
WLED::instance().enableWatchdog();
} }
} }
}); });

View File

@ -58,6 +58,10 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if (verboseResponse) { if (verboseResponse) {
sendDataWs(client); sendDataWs(client);
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500); lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
} else {
// we have to send something back otherwise WS connection closes
client->text(F("{\"success\":true}"));
lastInterfaceUpdate = millis() - (INTERFACE_UPDATE_COOLDOWN -500);
} }
} }
} else { } else {
@ -96,8 +100,6 @@ void sendDataWs(AsyncWebSocketClient * client)
if (!ws.count()) return; if (!ws.count()) return;
AsyncWebSocketMessageBuffer * buffer; AsyncWebSocketMessageBuffer * buffer;
while (strip.isUpdating()) yield();
if (!requestJSONBufferLock(12)) return; if (!requestJSONBufferLock(12)) return;
JsonObject state = doc.createNestedObject("state"); JsonObject state = doc.createNestedObject("state");

View File

@ -308,9 +308,15 @@ void getSettingsJS(byte subPage, char* dest)
// set limits // set limits
oappend(SET_F("bLimits(")); oappend(SET_F("bLimits("));
#ifdef ESP32
// requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
if (usermods.lookup(USERMOD_ID_AUDIOREACTIVE))
oappend(itoa(WLED_MAX_BUSSES-2,nS,10)); // prevent use of I2S buses if audio installed
else
#endif
oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(","); oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(",");
oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(","); oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(",");
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(","); oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
oappend(itoa(MAX_LEDS,nS,10)); oappend(itoa(MAX_LEDS,nS,10));
oappend(SET_F(");")); oappend(SET_F(");"));
@ -636,6 +642,7 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == 10) // 2D matrices if (subPage == 10) // 2D matrices
{ {
sappend('v',SET_F("SOMP"),strip.isMatrix); sappend('v',SET_F("SOMP"),strip.isMatrix);
#ifndef WLED_DISABLE_2D
oappend(SET_F("resetPanels();")); oappend(SET_F("resetPanels();"));
if (strip.isMatrix) { if (strip.isMatrix) {
sappend('v',SET_F("PH"),strip.panelH); sappend('v',SET_F("PH"),strip.panelH);
@ -660,5 +667,8 @@ void getSettingsJS(byte subPage, char* dest)
pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine); pO[l] = 'S'; sappend('c',pO,strip.panel[i].serpentine);
} }
} }
#else
oappend(SET_F("gId(\"somp\").remove(1);")); // remove 2D option from dropdown
#endif
} }
} }