Merge branch '0_15' into blending-styles

This commit is contained in:
Blaž Kristan 2024-05-12 08:12:58 +02:00
commit f441ce9c17
34 changed files with 1414 additions and 804 deletions

View File

@ -1,5 +1,15 @@
## WLED changelog ## WLED changelog
#### Build 240503
- Using brightness in analog clock overlay (#3944 by @paspiz85)
- Add Webpage shortcuts (#3945 by @w00000dy)
- ArtNet Poll reply (#3892 by @askask)
- Improved brightness change via long button presses (#3933 by @gaaat98)
- Relay open drain output (#3920 by @Suxsem)
- NEW JSON API: release info (update page, `info.release`)
- update esp32 platform to arduino-esp32 v2.0.9 (#3902)
- various optimisations and bugfixes (#3952, #3922, #3878, #3926, #3919, #3904 @DedeHai)
#### Build 2404120 #### Build 2404120
- v0.15.0-b3 - v0.15.0-b3
- fix for #3896 & WS2815 current saving - fix for #3896 & WS2815 current saving

View File

@ -1,9 +1,24 @@
# Little convenience script to get an object dump # Little convenience script to get an object dump
# You may add "-S" to the objdump commandline (i.e. replace "-D -C " with "-d -S -C ")
# to get source code intermixed with disassembly (SLOW !)
Import('env') Import('env')
def obj_dump_after_elf(source, target, env): def obj_dump_after_elf(source, target, env):
platform = env.PioPlatform()
board = env.BoardConfig()
mcu = board.get("build.mcu", "esp32")
print("Create firmware.asm") print("Create firmware.asm")
env.Execute("xtensa-lx106-elf-objdump "+ "-D " + str(target[0]) + " > "+ "${PROGNAME}.asm") if mcu == "esp8266":
env.Execute("xtensa-lx106-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32":
env.Execute("xtensa-esp32-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32s2":
env.Execute("xtensa-esp32s2-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32s3":
env.Execute("xtensa-esp32s3-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
if mcu == "esp32c3":
env.Execute("riscv32-esp-elf-objdump "+ "-D -C " + str(target[0]) + " > "+ "$BUILD_DIR/${PROGNAME}.asm")
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf]) env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", [obj_dump_after_elf])

View File

@ -36,6 +36,8 @@ def create_release(source):
def bin_rename_copy(source, target, env): def bin_rename_copy(source, target, env):
_create_dirs() _create_dirs()
variant = env["PIOENV"] variant = env["PIOENV"]
builddir = os.path.join(env["PROJECT_BUILD_DIR"], variant)
source_map = os.path.join(builddir, env["PROGNAME"] + ".map")
# create string with location and file names based on variant # create string with location and file names based on variant
map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant) map_file = "{}map{}{}.map".format(OUTPUT_DIR, os.path.sep, variant)
@ -44,7 +46,11 @@ def bin_rename_copy(source, target, env):
# copy firmware.map to map/<variant>.map # copy firmware.map to map/<variant>.map
if os.path.isfile("firmware.map"): if os.path.isfile("firmware.map"):
shutil.move("firmware.map", map_file) print("Found linker mapfile firmware.map")
shutil.copy("firmware.map", map_file)
if os.path.isfile(source_map):
print(f"Found linker mapfile {source_map}")
shutil.copy(source_map, map_file)
def bin_gzip(source, target): def bin_gzip(source, target):
# only create gzip for esp8266 # only create gzip for esp8266

View File

@ -115,6 +115,7 @@ extra_scripts =
post:pio-scripts/strip-floats.py post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py pre:pio-scripts/user_config_copy.py
pre:pio-scripts/build_ui.py pre:pio-scripts/build_ui.py
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# COMMON SETTINGS: # COMMON SETTINGS:
@ -338,14 +339,14 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_1m128k} board_build.ldscript = ${common.ldscript_1m128k}
build_unflags = ${common.build_unflags} build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01 -D WLED_DISABLE_OTA
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
lib_deps = ${esp8266.lib_deps} lib_deps = ${esp8266.lib_deps}
[env:esp01_1m_full_160] [env:esp01_1m_full_160]
extends = env:esp01_1m_full extends = env:esp01_1m_full
board_build.f_cpu = 160000000L board_build.f_cpu = 160000000L
build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA build_flags = ${common.build_flags} ${esp8266.build_flags} -D WLED_RELEASE_NAME=ESP01_160 -D WLED_DISABLE_OTA
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 7064 bytes FLASH and 975 bytes RAM ; -D WLED_USE_REAL_MATH ;; may fix wrong sunset/sunrise times, at the cost of 7064 bytes FLASH and 975 bytes RAM
[env:esp32dev] [env:esp32dev]
board = esp32dev board = esp32dev
@ -471,8 +472,7 @@ monitor_filters = esp32_exception_decoder
[env:esp32s3_4M_qspi] [env:esp32s3_4M_qspi]
;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi) ;; ESP32-S3, with 4MB FLASH and <= 4MB PSRAM (memory_type: qio_qspi)
board = esp32-s3-devkitc-1 ;; generic dev board; the next line adds PSRAM support board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
board_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
platform = ${esp32s3.platform} platform = ${esp32s3.platform}
platform_packages = ${esp32s3.platform_packages} platform_packages = ${esp32s3.platform_packages}
upload_speed = 921600 upload_speed = 921600

View File

@ -368,9 +368,9 @@ public:
JsonArray temperature_json = user.createNestedArray(F("Temperature")); JsonArray temperature_json = user.createNestedArray(F("Temperature"));
JsonArray pressure_json = user.createNestedArray(F("Pressure")); JsonArray pressure_json = user.createNestedArray(F("Pressure"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals))); temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
temperature_json.add(tempScale); temperature_json.add(tempScale);
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals))); pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
pressure_json.add(F("hPa")); pressure_json.add(F("hPa"));
} }
else if (sensorType==1) //BME280 else if (sensorType==1) //BME280
@ -382,9 +382,9 @@ public:
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point")); JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals)); temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
temperature_json.add(tempScale); temperature_json.add(tempScale);
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals))); humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals));
humidity_json.add(F("%")); humidity_json.add(F("%"));
pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals))); pressure_json.add(roundf(sensorPressure * powf(10, PressureDecimals)) / powf(10, PressureDecimals));
pressure_json.add(F("hPa")); pressure_json.add(F("hPa"));
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals)); heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
heatindex_json.add(tempScale); heatindex_json.add(tempScale);

View File

@ -0,0 +1,160 @@
#ifndef UMBBattery_h
#define UMBBattery_h
#include "battery_defaults.h"
/**
* Battery base class
* all other battery classes should inherit from this
*/
class UMBattery
{
private:
protected:
float minVoltage;
float maxVoltage;
float voltage;
int8_t level = 100;
float calibration; // offset or calibration value to fine tune the calculated voltage
float voltageMultiplier; // ratio for the voltage divider
float linearMapping(float v, float min, float max, float oMin = 0.0f, float oMax = 100.0f)
{
return (v-min) * (oMax-oMin) / (max-min) + oMin;
}
public:
UMBattery()
{
this->setVoltageMultiplier(USERMOD_BATTERY_VOLTAGE_MULTIPLIER);
this->setCalibration(USERMOD_BATTERY_CALIBRATION);
}
virtual void update(batteryConfig cfg)
{
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage);
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage);
if(cfg.level) this->setLevel(cfg.level);
if(cfg.calibration) this->setCalibration(cfg.calibration);
if(cfg.voltageMultiplier) this->setVoltageMultiplier(cfg.voltageMultiplier);
}
/**
* Corresponding battery curves
* calculates the level in % (0-100) with given voltage and possible voltage range
*/
virtual float mapVoltage(float v, float min, float max) = 0;
// {
// example implementation, linear mapping
// return (v-min) * 100 / (max-min);
// };
virtual void calculateAndSetLevel(float voltage) = 0;
/*
*
* Getter and Setter
*
*/
/*
* Get lowest configured battery voltage
*/
virtual float getMinVoltage()
{
return this->minVoltage;
}
/*
* Set lowest battery voltage
* can't be below 0 volt
*/
virtual void setMinVoltage(float voltage)
{
this->minVoltage = max(0.0f, voltage);
}
/*
* Get highest configured battery voltage
*/
virtual float getMaxVoltage()
{
return this->maxVoltage;
}
/*
* Set highest battery voltage
* can't be below minVoltage
*/
virtual void setMaxVoltage(float voltage)
{
this->maxVoltage = max(getMinVoltage()+.5f, voltage);
}
float getVoltage()
{
return this->voltage;
}
/**
* check if voltage is within specified voltage range, allow 10% over/under voltage
*/
void setVoltage(float voltage)
{
// this->voltage = ( (voltage < this->getMinVoltage() * 0.85f) || (voltage > this->getMaxVoltage() * 1.1f) )
// ? -1.0f
// : voltage;
this->voltage = voltage;
}
float getLevel()
{
return this->level;
}
void setLevel(float level)
{
this->level = constrain(level, 0.0f, 110.0f);
}
/*
* Get the configured calibration value
* a offset value to fine-tune the calculated voltage.
*/
virtual float getCalibration()
{
return calibration;
}
/*
* Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage.
*/
virtual void setCalibration(float offset)
{
calibration = offset;
}
/*
* Get the configured calibration value
* a value to set the voltage divider ratio
*/
virtual float getVoltageMultiplier()
{
return voltageMultiplier;
}
/*
* Set the voltage multiplier value
* a value to set the voltage divider ratio.
*/
virtual void setVoltageMultiplier(float multiplier)
{
voltageMultiplier = multiplier;
}
};
#endif

View File

@ -1,3 +1,8 @@
#ifndef UMBDefaults_h
#define UMBDefaults_h
#include "wled.h"
// pin defaults // pin defaults
// for the esp32 it is best to use the ADC1: GPIO32 - GPIO39 // for the esp32 it is best to use the ADC1: GPIO32 - GPIO39
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
@ -9,24 +14,66 @@
#endif #endif
#endif #endif
// The initial delay before the first battery voltage reading after power-on.
// This allows the voltage to stabilize before readings are taken, improving accuracy of initial reading.
#ifndef USERMOD_BATTERY_INITIAL_DELAY
#define USERMOD_BATTERY_INITIAL_DELAY 10000 // (milliseconds)
#endif
// the frequency to check the battery, 30 sec // the frequency to check the battery, 30 sec
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL #ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000 #define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
#endif #endif
// default for 18650 battery
// https://batterybro.com/blogs/18650-wholesale-battery-reviews/18852515-when-to-recycle-18650-batteries-and-how-to-start-a-collection-center-in-your-vape-shop /* Default Battery Type
// Discharge voltage: 2.5 volt + .1 for personal safety * 0 = unkown
#ifndef USERMOD_BATTERY_MIN_VOLTAGE * 1 = Lipo
#ifdef USERMOD_BATTERY_USE_LIPO * 2 = Lion
// LiPo "1S" Batteries should not be dischared below 3V !! */
#define USERMOD_BATTERY_MIN_VOLTAGE 3.2f #ifndef USERMOD_BATTERY_DEFAULT_TYPE
#else #define USERMOD_BATTERY_DEFAULT_TYPE 0
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f #endif
#endif /*
*
* Unkown 'Battery' defaults
*
*/
#ifndef USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE
// Extra save defaults
#define USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE 3.3f
#endif
#ifndef USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE
#define USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE 4.2f
#endif #endif
//the default ratio for the voltage divider /*
*
* Lithium polymer (Li-Po) defaults
*
*/
#ifndef USERMOD_BATTERY_LIPO_MIN_VOLTAGE
// LiPo "1S" Batteries should not be dischared below 3V !!
#define USERMOD_BATTERY_LIPO_MIN_VOLTAGE 3.2f
#endif
#ifndef USERMOD_BATTERY_LIPO_MAX_VOLTAGE
#define USERMOD_BATTERY_LIPO_MAX_VOLTAGE 4.2f
#endif
/*
*
* Lithium-ion (Li-Ion) defaults
*
*/
#ifndef USERMOD_BATTERY_LION_MIN_VOLTAGE
// default for 18650 battery
#define USERMOD_BATTERY_LION_MIN_VOLTAGE 2.6f
#endif
#ifndef USERMOD_BATTERY_LION_MAX_VOLTAGE
#define USERMOD_BATTERY_LION_MAX_VOLTAGE 4.2f
#endif
// the default ratio for the voltage divider
#ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER #ifndef USERMOD_BATTERY_VOLTAGE_MULTIPLIER
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f #define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
@ -35,13 +82,8 @@
#endif #endif
#endif #endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE #ifndef USERMOD_BATTERY_AVERAGING_ALPHA
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f #define USERMOD_BATTERY_AVERAGING_ALPHA 0.1f
#endif
// a common capacity for single 18650 battery cells is between 2500 and 3600 mAh
#ifndef USERMOD_BATTERY_TOTAL_CAPACITY
#define USERMOD_BATTERY_TOTAL_CAPACITY 3100
#endif #endif
// offset or calibration value to fine tune the calculated voltage // offset or calibration value to fine tune the calculated voltage
@ -49,11 +91,6 @@
#define USERMOD_BATTERY_CALIBRATION 0 #define USERMOD_BATTERY_CALIBRATION 0
#endif #endif
// calculate remaining time / the time that is left before the battery runs out of power
// #ifndef USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED
// #define USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED false
// #endif
// auto-off feature // auto-off feature
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED #ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED
#define USERMOD_BATTERY_AUTO_OFF_ENABLED true #define USERMOD_BATTERY_AUTO_OFF_ENABLED true
@ -78,4 +115,26 @@
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION #ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION
#define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5 #define USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION 5
#endif
// battery types
typedef enum
{
unknown=0,
lipo=1,
lion=2
} batteryType;
// used for initial configuration after boot
typedef struct bconfig_t
{
batteryType type;
float minVoltage;
float maxVoltage;
float voltage; // current voltage
int8_t level; // current level
float calibration; // offset or calibration value to fine tune the calculated voltage
float voltageMultiplier;
} batteryConfig;
#endif #endif

View File

@ -36,13 +36,13 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
| Name | Unit | Description | | Name | Unit | Description |
| ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- | | ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- |
| `USERMOD_BATTERY` | | define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp | | `USERMOD_BATTERY` | | define this (in `my_config.h`) to have this usermod included wled00\usermods_list.cpp |
| `USERMOD_BATTERY_USE_LIPO` | | define this (in `my_config.h`) if you use LiPo rechargeables (1S) |
| `USERMOD_BATTERY_MEASUREMENT_PIN` | | defaults to A0 on ESP8266 and GPIO35 on ESP32 | | `USERMOD_BATTERY_MEASUREMENT_PIN` | | defaults to A0 on ESP8266 and GPIO35 on ESP32 |
| `USERMOD_BATTERY_INITIAL_DELAY` | ms | delay before initial reading. defaults to 10 seconds to allow voltage stabilization
| `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds | | `USERMOD_BATTERY_MEASUREMENT_INTERVAL` | ms | battery check interval. defaults to 30 seconds |
| `USERMOD_BATTERY_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) | | `USERMOD_BATTERY_{TYPE}_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
| `USERMOD_BATTERY_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) | | `USERMOD_BATTERY_{TYPE}_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
| `USERMOD_BATTERY_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up | | `USERMOD_BATTERY_{TYPE}_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
| `USERMOD_BATTERY_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller | | `USERMOD_BATTERY_{TYPE}_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
| Auto-Off | --- | --- | | Auto-Off | --- | --- |
| `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off | | `USERMOD_BATTERY_AUTO_OFF_ENABLED` | true/false | enables auto-off |
| `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | when this threshold is reached master power turns off | | `USERMOD_BATTERY_AUTO_OFF_THRESHOLD` | % (0-100) | when this threshold is reached master power turns off |
@ -54,6 +54,13 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
All parameters can be configured at runtime via the Usermods settings page. All parameters can be configured at runtime via the Usermods settings page.
**NOTICE:** Each Battery type can be pre-configured individualy (in `my_config.h`)
| Name | Alias | `my_config.h` example |
| --------------- | ------------- | ------------------------------------- |
| Lithium Polymer | lipo (Li-Po) | `USERMOD_BATTERY_lipo_MIN_VOLTAGE` |
| Lithium Ionen | lion (Li-Ion) | `USERMOD_BATTERY_lion_TOTAL_CAPACITY` |
## ⚠️ Important ## ⚠️ Important
- Make sure you know your battery specifications! All batteries are **NOT** the same! - Make sure you know your battery specifications! All batteries are **NOT** the same!
@ -80,6 +87,15 @@ Specification from: [Molicel INR18650-M35A, 3500mAh 10A Lithium-ion battery, 3.
## 📝 Change Log ## 📝 Change Log
2024-04-30
- improved initial reading accuracy by delaying initial measurement to allow voltage to stabilize at power-on
2024-04-30
- integrate factory pattern to make it easier to add other / custom battery types
- update readme
2023-01-04 2023-01-04
- basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`) - basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`)

View File

@ -0,0 +1,38 @@
#ifndef UMBLion_h
#define UMBLion_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* LiOn Battery
*
*/
class LionUMBattery : public UMBattery
{
private:
public:
LionUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_LION_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_LION_MAX_VOLTAGE);
}
float mapVoltage(float v, float min, float max) override
{
return this->linearMapping(v, min, max); // basic mapping
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
virtual void setMaxVoltage(float voltage) override
{
this->maxVoltage = max(getMinVoltage()+1.0f, voltage);
}
};
#endif

View File

@ -0,0 +1,54 @@
#ifndef UMBLipo_h
#define UMBLipo_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* LiPo Battery
*
*/
class LipoUMBattery : public UMBattery
{
private:
public:
LipoUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_LIPO_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_LIPO_MAX_VOLTAGE);
}
/**
* LiPo batteries have a differnt discharge curve, see
* https://blog.ampow.com/lipo-voltage-chart/
*/
float mapVoltage(float v, float min, float max) override
{
float lvl = 0.0f;
lvl = this->linearMapping(v, min, max); // basic mapping
if (lvl < 40.0f)
lvl = this->linearMapping(lvl, 0, 40, 0, 12); // last 45% -> drops very quickly
else {
if (lvl < 90.0f)
lvl = this->linearMapping(lvl, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
else // level > 90%
lvl = this->linearMapping(lvl, 90, 105, 95, 100); // highest 15% -> drop slowly
}
return lvl;
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
virtual void setMaxVoltage(float voltage) override
{
this->maxVoltage = max(getMinVoltage()+0.7f, voltage);
}
};
#endif

View File

@ -0,0 +1,39 @@
#ifndef UMBUnkown_h
#define UMBUnkown_h
#include "../battery_defaults.h"
#include "../UMBattery.h"
/**
* Unkown / Default Battery
*
*/
class UnkownUMBattery : public UMBattery
{
private:
public:
UnkownUMBattery() : UMBattery()
{
this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
}
void update(batteryConfig cfg)
{
if(cfg.minVoltage) this->setMinVoltage(cfg.minVoltage); else this->setMinVoltage(USERMOD_BATTERY_UNKOWN_MIN_VOLTAGE);
if(cfg.maxVoltage) this->setMaxVoltage(cfg.maxVoltage); else this->setMaxVoltage(USERMOD_BATTERY_UNKOWN_MAX_VOLTAGE);
}
float mapVoltage(float v, float min, float max) override
{
return this->linearMapping(v, min, max); // basic mapping
};
void calculateAndSetLevel(float voltage) override
{
this->setLevel(this->mapVoltage(voltage, this->getMinVoltage(), this->getMaxVoltage()));
};
};
#endif

View File

@ -2,12 +2,15 @@
#include "wled.h" #include "wled.h"
#include "battery_defaults.h" #include "battery_defaults.h"
#include "UMBattery.h"
#include "types/UnkownUMBattery.h"
#include "types/LionUMBattery.h"
#include "types/LiPoUMBattery.h"
/* /*
* Usermod by Maximilian Mewes * Usermod by Maximilian Mewes
* Mail: mewes.maximilian@gmx.de * E-mail: mewes.maximilian@gmx.de
* GitHub: itCarl * Created at: 25.12.2022
* Date: 25.12.2022
* If you have any questions, please feel free to contact me. * If you have any questions, please feel free to contact me.
*/ */
class UsermodBattery : public Usermod class UsermodBattery : public Usermod
@ -15,47 +18,36 @@ class UsermodBattery : public Usermod
private: private:
// battery pin can be defined in my_config.h // battery pin can be defined in my_config.h
int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN; int8_t batteryPin = USERMOD_BATTERY_MEASUREMENT_PIN;
UMBattery* bat = new UnkownUMBattery();
batteryConfig cfg;
// Initial delay before first reading to allow voltage stabilization
unsigned long initialDelay = USERMOD_BATTERY_INITIAL_DELAY;
bool initialDelayComplete = false;
bool isFirstVoltageReading = true;
// how often to read the battery voltage // how often to read the battery voltage
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL; unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0; unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0; unsigned long lastReadTime = 0;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
// all battery cells summed up
unsigned int totalBatteryCapacity = USERMOD_BATTERY_TOTAL_CAPACITY;
// raw analog reading
float rawValue = 0.0f;
// calculated voltage
float voltage = maxBatteryVoltage;
// between 0 and 1, to control strength of voltage smoothing filter // between 0 and 1, to control strength of voltage smoothing filter
float alpha = 0.05f; float alpha = USERMOD_BATTERY_AVERAGING_ALPHA;
// multiplier for the voltage divider that is in place between ADC pin and battery, default will be 2 but might be adapted to readout voltages over ~5v ESP32 or ~6.6v ESP8266
float voltageMultiplier = USERMOD_BATTERY_VOLTAGE_MULTIPLIER;
// mapped battery level based on voltage
int8_t batteryLevel = 100;
// offset or calibration value to fine tune the calculated voltage
float calibration = USERMOD_BATTERY_CALIBRATION;
// time left estimation feature
// bool calculateTimeLeftEnabled = USERMOD_BATTERY_CALCULATE_TIME_LEFT_ENABLED;
// float estimatedTimeLeft = 0.0;
// auto shutdown/shutoff/master off feature // auto shutdown/shutoff/master off feature
bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED; bool autoOffEnabled = USERMOD_BATTERY_AUTO_OFF_ENABLED;
int8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD; uint8_t autoOffThreshold = USERMOD_BATTERY_AUTO_OFF_THRESHOLD;
// low power indicator feature // low power indicator feature
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED; bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED;
int8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET; uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
int8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD; uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
int8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10; uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
int8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION; uint8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
bool lowPowerIndicationDone = false; bool lowPowerIndicationDone = false;
unsigned long lowPowerActivationTime = 0; // used temporary during active time unsigned long lowPowerActivationTime = 0; // used temporary during active time
int8_t lastPreset = 0; uint8_t lastPreset = 0;
//
bool initDone = false; bool initDone = false;
bool initializing = true; bool initializing = true;
@ -67,22 +59,17 @@ class UsermodBattery : public Usermod
static const char _preset[]; static const char _preset[];
static const char _duration[]; static const char _duration[];
static const char _init[]; static const char _init[];
// custom map function
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
/**
* Helper for rounding floating point values
*/
float dot2round(float x) float dot2round(float x)
{ {
float nx = (int)(x * 100 + .5); float nx = (int)(x * 100 + .5);
return (float)(nx / 100); return (float)(nx / 100);
} }
/* /**
* Turn off all leds * Turn off all leds
*/ */
void turnOff() void turnOff()
@ -91,15 +78,15 @@ class UsermodBattery : public Usermod
stateUpdated(CALL_MODE_DIRECT_CHANGE); stateUpdated(CALL_MODE_DIRECT_CHANGE);
} }
/* /**
* Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously * Indicate low power by activating a configured preset for a given time and then switching back to the preset that was selected previously
*/ */
void lowPowerIndicator() void lowPowerIndicator()
{ {
if (!lowPowerIndicatorEnabled) return; if (!lowPowerIndicatorEnabled) return;
if (batteryPin < 0) return; // no measurement if (batteryPin < 0) return; // no measurement
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= batteryLevel) lowPowerIndicationDone = false; if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= bat->getLevel()) lowPowerIndicationDone = false;
if (lowPowerIndicatorThreshold <= batteryLevel) return; if (lowPowerIndicatorThreshold <= bat->getLevel()) return;
if (lowPowerIndicationDone) return; if (lowPowerIndicationDone) return;
if (lowPowerActivationTime <= 1) { if (lowPowerActivationTime <= 1) {
lowPowerActivationTime = millis(); lowPowerActivationTime = millis();
@ -114,26 +101,39 @@ class UsermodBattery : public Usermod
} }
} }
/**
* read the battery voltage in different ways depending on the architecture
*/
float readVoltage() float readVoltage()
{ {
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value // use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration; return (analogReadMilliVolts(batteryPin) / 1000.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
#else #else
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value // use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration; return (analogRead(batteryPin) / 1023.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
#endif #endif
} }
public: public:
//Functions called by WLED //Functions called by WLED
/* /**
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void setup()
{ {
// plug in the right battery type
if(cfg.type == (batteryType)lipo) {
bat = new LipoUMBattery();
} else if(cfg.type == (batteryType)lion) {
bat = new LionUMBattery();
}
// update the choosen battery type with configured values
bat->update(cfg);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
bool success = false; bool success = false;
DEBUG_PRINTLN(F("Allocating battery pin...")); DEBUG_PRINTLN(F("Allocating battery pin..."));
@ -141,7 +141,6 @@ class UsermodBattery : public Usermod
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) { if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
DEBUG_PRINTLN(F("Battery pin allocation succeeded.")); DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
success = true; success = true;
voltage = readVoltage();
} }
if (!success) { if (!success) {
@ -152,17 +151,17 @@ class UsermodBattery : public Usermod
} }
#else //ESP8266 boards have only one analog input pin A0 #else //ESP8266 boards have only one analog input pin A0
pinMode(batteryPin, INPUT); pinMode(batteryPin, INPUT);
voltage = readVoltage();
#endif #endif
nextReadTime = millis() + readingInterval; // First voltage reading is delayed to allow voltage stabilization after powering up
nextReadTime = millis() + initialDelay;
lastReadTime = millis(); lastReadTime = millis();
initDone = true; initDone = true;
} }
/* /**
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
@ -182,6 +181,25 @@ class UsermodBattery : public Usermod
lowPowerIndicator(); lowPowerIndicator();
// Handling the initial delay
if (!initialDelayComplete && millis() < nextReadTime)
return; // Continue to return until the initial delay is over
// Once the initial delay is over, set it as complete
if (!initialDelayComplete)
{
initialDelayComplete = true;
// Set the regular interval after initial delay
nextReadTime = millis() + readingInterval;
}
// Make the first voltage reading after the initial delay has elapsed
if (isFirstVoltageReading)
{
bat->setVoltage(readVoltage());
isFirstVoltageReading = false;
}
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms) // check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if (millis() < nextReadTime) return; if (millis() < nextReadTime) return;
@ -191,43 +209,17 @@ class UsermodBattery : public Usermod
if (batteryPin < 0) return; // nothing to read if (batteryPin < 0) return; // nothing to read
initializing = false; initializing = false;
float rawValue = readVoltage();
rawValue = readVoltage();
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout // filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
voltage = voltage + alpha * (rawValue - voltage); float filteredVoltage = bat->getVoltage() + alpha * (rawValue - bat->getVoltage());
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
bat->setVoltage(filteredVoltage);
// translate battery voltage into percentage // translate battery voltage into percentage
/* bat->calculateAndSetLevel(filteredVoltage);
the standard "map" function doesn't work
https://www.arduino.cc/reference/en/language/functions/math/map/ notes and warnings at the bottom
*/
#ifdef USERMOD_BATTERY_USE_LIPO
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100); // basic mapping
// LiPo batteries have a differnt dischargin curve, see
// https://blog.ampow.com/lipo-voltage-chart/
if (batteryLevel < 40.0f)
batteryLevel = mapf(batteryLevel, 0, 40, 0, 12); // last 45% -> drops very quickly
else {
if (batteryLevel < 90.0f)
batteryLevel = mapf(batteryLevel, 40, 90, 12, 95); // 90% ... 40% -> almost linear drop
else // level > 90%
batteryLevel = mapf(batteryLevel, 90, 105, 95, 100); // highest 15% -> drop slowly
}
#else
batteryLevel = mapf(voltage, minBatteryVoltage, maxBatteryVoltage, 0, 100);
#endif
if (voltage > -1.0f) batteryLevel = constrain(batteryLevel, 0.0f, 110.0f);
// if (calculateTimeLeftEnabled) {
// float currentBatteryCapacity = totalBatteryCapacity;
// estimatedTimeLeft = (currentBatteryCapacity/strip.currentMilliamps)*60;
// }
// Auto off -- Master power off // Auto off -- Master power off
if (autoOffEnabled && (autoOffThreshold >= batteryLevel)) if (autoOffEnabled && (autoOffThreshold >= bat->getLevel()))
turnOff(); turnOff();
#ifndef WLED_DISABLE_MQTT #ifndef WLED_DISABLE_MQTT
@ -236,13 +228,13 @@ class UsermodBattery : public Usermod
if (WLED_MQTT_CONNECTED) { if (WLED_MQTT_CONNECTED) {
char buf[64]; // buffer for snprintf() char buf[64]; // buffer for snprintf()
snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic); snprintf_P(buf, 63, PSTR("%s/voltage"), mqttDeviceTopic);
mqtt->publish(buf, 0, false, String(voltage).c_str()); mqtt->publish(buf, 0, false, String(bat->getVoltage()).c_str());
} }
#endif #endif
} }
/* /**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor * Below it is shown how this could be used for e.g. a light sensor
@ -262,16 +254,6 @@ class UsermodBattery : public Usermod
// info modal display names // info modal display names
JsonArray infoPercentage = user.createNestedArray(F("Battery level")); JsonArray infoPercentage = user.createNestedArray(F("Battery level"));
JsonArray infoVoltage = user.createNestedArray(F("Battery voltage")); JsonArray infoVoltage = user.createNestedArray(F("Battery voltage"));
// if (calculateTimeLeftEnabled)
// {
// JsonArray infoEstimatedTimeLeft = user.createNestedArray(F("Estimated time left"));
// if (initializing) {
// infoEstimatedTimeLeft.add(FPSTR(_init));
// } else {
// infoEstimatedTimeLeft.add(estimatedTimeLeft);
// infoEstimatedTimeLeft.add(F(" min"));
// }
// }
JsonArray infoNextUpdate = user.createNestedArray(F("Next update")); JsonArray infoNextUpdate = user.createNestedArray(F("Next update"));
infoNextUpdate.add((nextReadTime - millis()) / 1000); infoNextUpdate.add((nextReadTime - millis()) / 1000);
@ -283,46 +265,104 @@ class UsermodBattery : public Usermod
return; return;
} }
if (batteryLevel < 0) { if (bat->getLevel() < 0) {
infoPercentage.add(F("invalid")); infoPercentage.add(F("invalid"));
} else { } else {
infoPercentage.add(batteryLevel); infoPercentage.add(bat->getLevel());
} }
infoPercentage.add(F(" %")); infoPercentage.add(F(" %"));
if (voltage < 0) { if (bat->getVoltage() < 0) {
infoVoltage.add(F("invalid")); infoVoltage.add(F("invalid"));
} else { } else {
infoVoltage.add(dot2round(voltage)); infoVoltage.add(dot2round(bat->getVoltage()));
} }
infoVoltage.add(F(" V")); infoVoltage.add(F(" V"));
} }
void addBatteryToJsonObject(JsonObject& battery, bool forJsonState)
{
if(forJsonState) { battery[F("type")] = cfg.type; } else {battery[F("type")] = (String)cfg.type; } // has to be a String otherwise it won't get converted to a Dropdown
battery[F("min-voltage")] = bat->getMinVoltage();
battery[F("max-voltage")] = bat->getMaxVoltage();
battery[F("calibration")] = bat->getCalibration();
battery[F("voltage-multiplier")] = bat->getVoltageMultiplier();
battery[FPSTR(_readInterval)] = readingInterval;
/* JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section
ao[FPSTR(_enabled)] = autoOffEnabled;
ao[FPSTR(_threshold)] = autoOffThreshold;
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
}
void getUsermodConfigFromJsonObject(JsonObject& battery)
{
getJsonValue(battery[F("type")], cfg.type);
getJsonValue(battery[F("min-voltage")], cfg.minVoltage);
getJsonValue(battery[F("max-voltage")], cfg.maxVoltage);
getJsonValue(battery[F("calibration")], cfg.calibration);
getJsonValue(battery[F("voltage-multiplier")], cfg.voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
JsonObject ao = battery[F("auto-off")];
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
JsonObject lp = battery[F("indicator")];
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset);
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
if(initDone)
bat->update(cfg);
}
/**
* 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)
{ {
JsonObject battery = root.createNestedObject(FPSTR(_name));
if (battery.isNull())
battery = root.createNestedObject(FPSTR(_name));
addBatteryToJsonObject(battery, true);
DEBUG_PRINTLN(F("Battery state exposed in JSON API."));
} }
*/
/* /**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void readFromJsonState(JsonObject& root) void readFromJsonState(JsonObject& root)
{ {
if (!initDone) return; // prevent crash on boot applyPreset()
JsonObject battery = root[FPSTR(_name)];
if (!battery.isNull()) {
getUsermodConfigFromJsonObject(battery);
DEBUG_PRINTLN(F("Battery state read from JSON API."));
}
} }
*/ */
/* /**
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
* It will be called by WLED when settings are actually saved (for example, LED settings are saved) * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop(). * If you want to force saving the current state, use serializeConfig() in your loop().
@ -359,47 +399,41 @@ class UsermodBattery : public Usermod
*/ */
void addToConfig(JsonObject& root) void addToConfig(JsonObject& root)
{ {
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject battery = root.createNestedObject(FPSTR(_name));
if (battery.isNull()) {
battery = root.createNestedObject(FPSTR(_name));
}
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
battery[F("pin")] = batteryPin; battery[F("pin")] = batteryPin;
#endif #endif
// battery[F("time-left")] = calculateTimeLeftEnabled;
battery[F("min-voltage")] = minBatteryVoltage;
battery[F("max-voltage")] = maxBatteryVoltage;
battery[F("capacity")] = totalBatteryCapacity;
battery[F("calibration")] = calibration;
battery[F("voltage-multiplier")] = voltageMultiplier;
battery[FPSTR(_readInterval)] = readingInterval;
JsonObject ao = battery.createNestedObject(F("auto-off")); // auto off section addBatteryToJsonObject(battery, false);
ao[FPSTR(_enabled)] = autoOffEnabled;
ao[FPSTR(_threshold)] = autoOffThreshold;
JsonObject lp = battery.createNestedObject(F("indicator")); // low power section
lp[FPSTR(_enabled)] = lowPowerIndicatorEnabled;
lp[FPSTR(_preset)] = lowPowerIndicatorPreset; // dropdown trickery (String)lowPowerIndicatorPreset;
lp[FPSTR(_threshold)] = lowPowerIndicatorThreshold;
lp[FPSTR(_duration)] = lowPowerIndicatorDuration;
// read voltage in case calibration or voltage multiplier changed to see immediate effect // read voltage in case calibration or voltage multiplier changed to see immediate effect
voltage = readVoltage(); bat->setVoltage(readVoltage());
DEBUG_PRINTLN(F("Battery config saved.")); DEBUG_PRINTLN(F("Battery config saved."));
} }
void appendConfigData() void appendConfigData()
{ {
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');")); // Total: 462 Bytes
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');")); oappend(SET_F("td=addDropdown('Battery', 'type');")); // 35 Bytes
oappend(SET_F("addInfo('Battery:capacity', 1, 'mAh');")); oappend(SET_F("addOption(td, 'Unkown', '0');")); // 30 Bytes
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');")); oappend(SET_F("addOption(td, 'LiPo', '1');")); // 28 Bytes
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');")); oappend(SET_F("addOption(td, 'LiOn', '2');")); // 28 Bytes
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');")); oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');")); oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');")); // 40 Bytes
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');")); // 40 Bytes
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');")); // 38 Bytes
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');")); // 47 Bytes
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');")); // 48 Bytes
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');")); // 47 Bytes
// cannot quite get this mf to work. its exeeding some buffer limit i think // this option list would exeed the oappend() buffer
// what i wanted is a list of all presets to select one from // a list of all presets to select one from
// oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');")); // oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
// the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);")); // the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
// for(int8_t i=1; i < 42; i++) { // for(int8_t i=1; i < 42; i++) {
@ -412,7 +446,7 @@ class UsermodBattery : public Usermod
} }
/* /**
* readFromConfig() can be used to read back the custom settings you added with addToConfig(). * readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page) * This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
* *
@ -445,25 +479,13 @@ class UsermodBattery : public Usermod
newBatteryPin = battery[F("pin")] | newBatteryPin; newBatteryPin = battery[F("pin")] | newBatteryPin;
#endif #endif
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled; // calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage); setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage());
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage); setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage());
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity); setCalibration(battery[F("calibration")] | bat->getCalibration());
setCalibration(battery[F("calibration")] | calibration); setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier());
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval); setReadingInterval(battery[FPSTR(_readInterval)] | readingInterval);
JsonObject ao = battery[F("auto-off")]; getUsermodConfigFromJsonObject(battery);
setAutoOffEnabled(ao[FPSTR(_enabled)] | autoOffEnabled);
setAutoOffThreshold(ao[FPSTR(_threshold)] | autoOffThreshold);
JsonObject lp = battery[F("indicator")];
setLowPowerIndicatorEnabled(lp[FPSTR(_enabled)] | lowPowerIndicatorEnabled);
setLowPowerIndicatorPreset(lp[FPSTR(_preset)] | lowPowerIndicatorPreset); // dropdown trickery (int)lp["preset"]
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
DEBUG_PRINT(FPSTR(_name));
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
if (!initDone) if (!initDone)
@ -491,8 +513,9 @@ class UsermodBattery : public Usermod
return !battery[FPSTR(_readInterval)].isNull(); return !battery[FPSTR(_readInterval)].isNull();
} }
/* /**
* Generate a preset sample for low power indication * TBD: Generate a preset sample for low power indication
* a button on the config page would be cool, currently not possible
*/ */
void generateExamplePreset() void generateExamplePreset()
{ {
@ -529,7 +552,7 @@ class UsermodBattery : public Usermod
* *
*/ */
/* /**
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed. * This could be used in the future for the system to determine whether your usermod is installed.
*/ */
@ -538,13 +561,23 @@ class UsermodBattery : public Usermod
return USERMOD_ID_BATTERY; return USERMOD_ID_BATTERY;
} }
/**
* get currently active battery type
*/
batteryType getBatteryType()
{
return cfg.type;
}
/**
*
*/
unsigned long getReadingInterval() unsigned long getReadingInterval()
{ {
return readingInterval; return readingInterval;
} }
/* /**
* minimum repetition is 3000ms (3s) * minimum repetition is 3000ms (3s)
*/ */
void setReadingInterval(unsigned long newReadingInterval) void setReadingInterval(unsigned long newReadingInterval)
@ -552,105 +585,84 @@ class UsermodBattery : public Usermod
readingInterval = max((unsigned long)3000, newReadingInterval); readingInterval = max((unsigned long)3000, newReadingInterval);
} }
/**
/*
* Get lowest configured battery voltage * Get lowest configured battery voltage
*/ */
float getMinBatteryVoltage() float getMinBatteryVoltage()
{ {
return minBatteryVoltage; return bat->getMinVoltage();
} }
/* /**
* Set lowest battery voltage * Set lowest battery voltage
* can't be below 0 volt * can't be below 0 volt
*/ */
void setMinBatteryVoltage(float voltage) void setMinBatteryVoltage(float voltage)
{ {
minBatteryVoltage = max(0.0f, voltage); bat->setMinVoltage(voltage);
} }
/* /**
* Get highest configured battery voltage * Get highest configured battery voltage
*/ */
float getMaxBatteryVoltage() float getMaxBatteryVoltage()
{ {
return maxBatteryVoltage; return bat->getMaxVoltage();
} }
/* /**
* Set highest battery voltage * Set highest battery voltage
* can't be below minBatteryVoltage * can't be below minBatteryVoltage
*/ */
void setMaxBatteryVoltage(float voltage) void setMaxBatteryVoltage(float voltage)
{ {
#ifdef USERMOD_BATTERY_USE_LIPO bat->setMaxVoltage(voltage);
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
#else
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
#endif
} }
/* /**
* Get the capacity of all cells in parralel sumed up
* unit: mAh
*/
unsigned int getTotalBatteryCapacity()
{
return totalBatteryCapacity;
}
void setTotalBatteryCapacity(unsigned int capacity)
{
totalBatteryCapacity = capacity;
}
/*
* Get the calculated voltage * Get the calculated voltage
* formula: (adc pin value / adc precision * max voltage) + calibration * formula: (adc pin value / adc precision * max voltage) + calibration
*/ */
float getVoltage() float getVoltage()
{ {
return voltage; return bat->getVoltage();
} }
/* /**
* Get the mapped battery level (0 - 100) based on voltage * Get the mapped battery level (0 - 100) based on voltage
* important: voltage can drop when a load is applied, so its only an estimate * important: voltage can drop when a load is applied, so its only an estimate
*/ */
int8_t getBatteryLevel() int8_t getBatteryLevel()
{ {
return batteryLevel; return bat->getLevel();
} }
/* /**
* Get the configured calibration value * Get the configured calibration value
* a offset value to fine-tune the calculated voltage. * a offset value to fine-tune the calculated voltage.
*/ */
float getCalibration() float getCalibration()
{ {
return calibration; return bat->getCalibration();
} }
/* /**
* Set the voltage calibration offset value * Set the voltage calibration offset value
* a offset value to fine-tune the calculated voltage. * a offset value to fine-tune the calculated voltage.
*/ */
void setCalibration(float offset) void setCalibration(float offset)
{ {
calibration = offset; bat->setCalibration(offset);
} }
/* /**
* Set the voltage multiplier value * Set the voltage multiplier value
* A multiplier that may need adjusting for different voltage divider setups * A multiplier that may need adjusting for different voltage divider setups
*/ */
void setVoltageMultiplier(float multiplier) void setVoltageMultiplier(float multiplier)
{ {
voltageMultiplier = multiplier; bat->setVoltageMultiplier(multiplier);
} }
/* /*
@ -659,10 +671,10 @@ class UsermodBattery : public Usermod
*/ */
float getVoltageMultiplier() float getVoltageMultiplier()
{ {
return voltageMultiplier; return bat->getVoltageMultiplier();
} }
/* /**
* Get auto-off feature enabled status * Get auto-off feature enabled status
* is auto-off enabled, true/false * is auto-off enabled, true/false
*/ */
@ -671,7 +683,7 @@ class UsermodBattery : public Usermod
return autoOffEnabled; return autoOffEnabled;
} }
/* /**
* Set auto-off feature status * Set auto-off feature status
*/ */
void setAutoOffEnabled(bool enabled) void setAutoOffEnabled(bool enabled)
@ -679,7 +691,7 @@ class UsermodBattery : public Usermod
autoOffEnabled = enabled; autoOffEnabled = enabled;
} }
/* /**
* Get auto-off threshold in percent (0-100) * Get auto-off threshold in percent (0-100)
*/ */
int8_t getAutoOffThreshold() int8_t getAutoOffThreshold()
@ -687,7 +699,7 @@ class UsermodBattery : public Usermod
return autoOffThreshold; return autoOffThreshold;
} }
/* /**
* Set auto-off threshold in percent (0-100) * Set auto-off threshold in percent (0-100)
*/ */
void setAutoOffThreshold(int8_t threshold) void setAutoOffThreshold(int8_t threshold)
@ -697,8 +709,7 @@ class UsermodBattery : public Usermod
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold; autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold;
} }
/**
/*
* Get low-power-indicator feature enabled status * Get low-power-indicator feature enabled status
* is the low-power-indicator enabled, true/false * is the low-power-indicator enabled, true/false
*/ */
@ -707,7 +718,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorEnabled; return lowPowerIndicatorEnabled;
} }
/* /**
* Set low-power-indicator feature status * Set low-power-indicator feature status
*/ */
void setLowPowerIndicatorEnabled(bool enabled) void setLowPowerIndicatorEnabled(bool enabled)
@ -715,7 +726,7 @@ class UsermodBattery : public Usermod
lowPowerIndicatorEnabled = enabled; lowPowerIndicatorEnabled = enabled;
} }
/* /**
* Get low-power-indicator preset to activate when low power is detected * Get low-power-indicator preset to activate when low power is detected
*/ */
int8_t getLowPowerIndicatorPreset() int8_t getLowPowerIndicatorPreset()
@ -723,7 +734,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorPreset; return lowPowerIndicatorPreset;
} }
/* /**
* Set low-power-indicator preset to activate when low power is detected * Set low-power-indicator preset to activate when low power is detected
*/ */
void setLowPowerIndicatorPreset(int8_t presetId) void setLowPowerIndicatorPreset(int8_t presetId)
@ -741,7 +752,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorThreshold; return lowPowerIndicatorThreshold;
} }
/* /**
* Set low-power-indicator threshold in percent (0-100) * Set low-power-indicator threshold in percent (0-100)
*/ */
void setLowPowerIndicatorThreshold(int8_t threshold) void setLowPowerIndicatorThreshold(int8_t threshold)
@ -751,7 +762,7 @@ class UsermodBattery : public Usermod
lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold); lowPowerIndicatorThreshold = autoOffEnabled /*&& lowPowerIndicatorEnabled*/ ? max(autoOffThreshold+1, (int)lowPowerIndicatorThreshold) : max(5, (int)lowPowerIndicatorThreshold);
} }
/* /**
* Get low-power-indicator duration in seconds * Get low-power-indicator duration in seconds
*/ */
int8_t getLowPowerIndicatorDuration() int8_t getLowPowerIndicatorDuration()
@ -759,7 +770,7 @@ class UsermodBattery : public Usermod
return lowPowerIndicatorDuration; return lowPowerIndicatorDuration;
} }
/* /**
* Set low-power-indicator duration in seconds * Set low-power-indicator duration in seconds
*/ */
void setLowPowerIndicatorDuration(int8_t duration) void setLowPowerIndicatorDuration(int8_t duration)
@ -767,9 +778,8 @@ class UsermodBattery : public Usermod
lowPowerIndicatorDuration = duration; lowPowerIndicatorDuration = duration;
} }
/**
/* * Get low-power-indicator status when the indication is done this returns true
* Get low-power-indicator status when the indication is done thsi returns true
*/ */
bool getLowPowerIndicatorDone() bool getLowPowerIndicatorDone()
{ {

View File

@ -183,7 +183,6 @@ constexpr uint16_t samplesFFT_2 = 256; // meaningfull part of FFT resul
// These are the input and output vectors. Input vectors receive computed results from FFT. // These are the input and output vectors. Input vectors receive computed results from FFT.
static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins static float vReal[samplesFFT] = {0.0f}; // FFT sample inputs / freq output - these are our raw result bins
static float vImag[samplesFFT] = {0.0f}; // imaginary parts static float vImag[samplesFFT] = {0.0f}; // imaginary parts
static float windowWeighingFactors[samplesFFT] = {0.0f};
// Create FFT object // Create FFT object
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2 // lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
@ -196,7 +195,8 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
#include <arduinoFFT.h> #include <arduinoFFT.h>
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, windowWeighingFactors); /* Create FFT object with weighing factor storage */
static ArduinoFFT<float> FFT = ArduinoFFT<float>( vReal, vImag, samplesFFT, SAMPLE_RATE, true);
// Helper functions // Helper functions
@ -282,6 +282,7 @@ void FFTcode(void * parameter)
//FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection //FFT.windowing(FFTWindow::Blackman_Harris, FFTDirection::Forward); // Weigh data using "Blackman- Harris" window - sharp peaks due to excellent sideband rejection
FFT.compute( FFTDirection::Forward ); // Compute FFT FFT.compute( FFTDirection::Forward ); // Compute FFT
FFT.complexToMagnitude(); // Compute magnitudes FFT.complexToMagnitude(); // Compute magnitudes
vReal[0] = 0; // The remaining DC offset on the signal produces a strong spike on position 0 that should be eliminated to avoid issues.
FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant FFT.majorPeak(&FFT_MajorPeak, &FFT_Magnitude); // let the effects know which freq was most dominant
FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects FFT_MajorPeak = constrain(FFT_MajorPeak, 1.0f, 11025.0f); // restrict value to range expected by effects
@ -1121,6 +1122,11 @@ class AudioReactive : public Usermod {
delay(100); // Give that poor microphone some time to setup. delay(100); // Give that poor microphone some time to setup.
useBandPassFilter = false; useBandPassFilter = false;
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
if ((i2sckPin == I2S_PIN_NO_CHANGE) && (i2ssdPin >= 0) && (i2swsPin >= 0) && ((dmType == 1) || (dmType == 4)) ) dmType = 5; // dummy user support: SCK == -1 --means--> PDM microphone
#endif
switch (dmType) { switch (dmType) {
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
// stub cases for not-yet-supported I2S modes on other ESP32 chips // stub cases for not-yet-supported I2S modes on other ESP32 chips

View File

@ -2506,7 +2506,7 @@ uint16_t ripple_base()
uint16_t cx = rippleorigin >> 8; uint16_t cx = rippleorigin >> 8;
uint16_t cy = rippleorigin & 0xFF; uint16_t cy = rippleorigin & 0xFF;
uint8_t mag = scale8(sin8((propF>>2)), amp); uint8_t mag = scale8(sin8((propF>>2)), amp);
if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag)); if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true);
} else } else
#endif #endif
{ {
@ -6056,7 +6056,7 @@ uint16_t mode_2Dfloatingblobs(void) {
} }
} }
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0); uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
if (blob->r[i] > 1.f) SEGMENT.fill_circle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c); if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c);
else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c); else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
// move x // move x
if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f)); if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f));
@ -6643,7 +6643,6 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,
// * JUGGLES // // * JUGGLES //
////////////////////// //////////////////////
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline. uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -6655,12 +6654,13 @@ uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0); uint16_t my_sampleAgc = fmax(fmin(volumeSmth, 255.0), 0);
for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) { for (size_t i=0; i<SEGMENT.intensity/32+1U; i++) {
// if SEGLEN equals 1, we will always set color to the first and only pixel, but the effect is still good looking
SEGMENT.setPixelColor(beatsin16(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc)); SEGMENT.setPixelColor(beatsin16(SEGMENT.speed/4+i*2,0,SEGLEN-1), color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(strip.now/4+i*2, false, PALETTE_SOLID_WRAP, 0), my_sampleAgc));
} }
return FRAMETIME; return FRAMETIME;
} // mode_juggles() } // mode_juggles()
static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;1v;m12=0,si=0"; // Pixels, Beatsin static const char _data_FX_MODE_JUGGLES[] PROGMEM = "Juggles@!,# of balls;!,!;!;01v;m12=0,si=0"; // Pixels, Beatsin
////////////////////// //////////////////////
@ -6761,7 +6761,7 @@ uint16_t mode_noisefire(void) { // Noisefire. By Andrew Tuline.
return FRAMETIME; return FRAMETIME;
} // mode_noisefire() } // mode_noisefire()
static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;1v;m12=2,si=0"; // Circle, Beatsin static const char _data_FX_MODE_NOISEFIRE[] PROGMEM = "Noisefire@!,!;;;01v;m12=2,si=0"; // Circle, Beatsin
/////////////////////// ///////////////////////
@ -6871,7 +6871,7 @@ uint16_t mode_plasmoid(void) { // Plasmoid. By Andrew Tuline.
return FRAMETIME; return FRAMETIME;
} // mode_plasmoid() } // mode_plasmoid()
static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;1v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin static const char _data_FX_MODE_PLASMOID[] PROGMEM = "Plasmoid@Phase,# of pixels;!,!;!;01v;sx=128,ix=128,m12=0,si=0"; // Pixels, Beatsin
/////////////////////// ///////////////////////
@ -7026,7 +7026,7 @@ static const char _data_FX_MODE_BLURZ[] PROGMEM = "Blurz@Fade rate,Blur;!,Color
// ** DJLight // // ** DJLight //
///////////////////////// /////////////////////////
uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam. uint16_t mode_DJLight(void) { // Written by ??? Adapted by Will Tatam.
if (SEGLEN == 1) return mode_static(); // No need to prevent from executing on single led strips, only mid will be set (mid = 0)
const int mid = SEGLEN / 2; const int mid = SEGLEN / 2;
um_data_t *um_data; um_data_t *um_data;
@ -7047,13 +7047,14 @@ uint16_t mode_DJLight(void) { // Written by ??? Adapted by Wil
CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds CRGB color = CRGB(fftResult[15]/2, fftResult[5]/2, fftResult[0]/2); // 16-> 15 as 16 is out of bounds
SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update SEGMENT.setPixelColor(mid, color.fadeToBlackBy(map(fftResult[4], 0, 255, 255, 4))); // TODO - Update
// if SEGLEN equals 1 these loops won't execute
for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left for (int i = SEGLEN - 1; i > mid; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); // move to the left
for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right for (int i = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
} }
return FRAMETIME; return FRAMETIME;
} // mode_DJLight() } // mode_DJLight()
static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;1f;m12=2,si=0"; // Circle, Beatsin static const char _data_FX_MODE_DJLIGHT[] PROGMEM = "DJ Light@Speed;;;01f;m12=2,si=0"; // Circle, Beatsin
//////////////////// ////////////////////
@ -7097,7 +7098,7 @@ static const char _data_FX_MODE_FREQMAP[] PROGMEM = "Freqmap@Fade rate,Starting
// ** Freqmatrix // // ** Freqmatrix //
/////////////////////// ///////////////////////
uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung. uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static(); // No need to prevent from executing on single led strips, we simply change pixel 0 each time and avoid the shift
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7140,12 +7141,13 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
// shift the pixels one pixel up // shift the pixels one pixel up
SEGMENT.setPixelColor(0, color); SEGMENT.setPixelColor(0, color);
// if SEGLEN equals 1 this loop won't execute
for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left for (int i = SEGLEN - 1; i > 0; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
} }
return FRAMETIME; return FRAMETIME;
} // mode_freqmatrix() } // mode_freqmatrix()
static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;1f;m12=3,si=0"; // Corner, Beatsin static const char _data_FX_MODE_FREQMATRIX[] PROGMEM = "Freqmatrix@Speed,Sound effect,Low bin,High bin,Sensitivity;;;01f;m12=3,si=0"; // Corner, Beatsin
////////////////////// //////////////////////
@ -7202,7 +7204,7 @@ static const char _data_FX_MODE_FREQPIXELS[] PROGMEM = "Freqpixels@Fade rate,Sta
// As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz. // As a compromise between speed and accuracy we are currently sampling with 10240Hz, from which we can then determine with a 512bin FFT our max frequency is 5120Hz.
// Depending on the music stream you have you might find it useful to change the frequency mapping. // Depending on the music stream you have you might find it useful to change the frequency mapping.
uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung. uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschung.
if (SEGLEN == 1) return mode_static(); // As before, this effect can also work on single pixels, we just lose the shifting effect
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7246,13 +7248,14 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
SEGMENT.setPixelColor(SEGLEN/2, color); SEGMENT.setPixelColor(SEGLEN/2, color);
// shift the pixels one pixel outwards // shift the pixels one pixel outwards
// if SEGLEN equals 1 these loops won't execute
for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left for (int i = SEGLEN - 1; i > SEGLEN/2; i--) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i-1)); //move to the left
for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right for (int i = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
} }
return FRAMETIME; return FRAMETIME;
} // mode_freqwave() } // mode_freqwave()
static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;1f;m12=2,si=0"; // Circle, Beatsin static const char _data_FX_MODE_FREQWAVE[] PROGMEM = "Freqwave@Speed,Sound effect,Low bin,High bin,Pre-amp;;;01f;m12=2,si=0"; // Circle, Beatsin
/////////////////////// ///////////////////////
@ -7311,7 +7314,6 @@ static const char _data_FX_MODE_GRAVFREQ[] PROGMEM = "Gravfreq@Rate of fall,Sens
// ** Noisemove // // ** Noisemove //
////////////////////// //////////////////////
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7325,20 +7327,20 @@ uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuli
uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins. uint8_t numBins = map(SEGMENT.intensity,0,255,0,16); // Map slider to fftResult bins.
for (int i=0; i<numBins; i++) { // How many active bins are we using. for (int i=0; i<numBins; i++) { // How many active bins are we using.
uint16_t locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise. uint16_t locn = inoise16(strip.now*SEGMENT.speed+i*50000, strip.now*SEGMENT.speed); // Get a new pixel location from moving noise.
// if SEGLEN equals 1 locn will be always 0, hence we set the first pixel only
locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over. locn = map(locn, 7500, 58000, 0, SEGLEN-1); // Map that to the length of the strand, and ensure we don't go over.
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4)); SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4));
} }
return FRAMETIME; return FRAMETIME;
} // mode_noisemove() } // mode_noisemove()
static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;1f;m12=0,si=0"; // Pixels, Beatsin static const char _data_FX_MODE_NOISEMOVE[] PROGMEM = "Noisemove@Speed of perlin movement,Fade rate;!,!;!;01f;m12=0,si=0"; // Pixels, Beatsin
////////////////////// //////////////////////
// ** Rocktaves // // ** Rocktaves //
////////////////////// //////////////////////
uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline uint16_t mode_rocktaves(void) { // Rocktaves. Same note from each octave is same colour. By: Andrew Tuline
if (SEGLEN == 1) return mode_static();
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7366,12 +7368,13 @@ uint16_t mode_rocktaves(void) { // Rocktaves. Same note from eac
frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255; frTemp = fabsf(frTemp * 2.1f); // Fudge factors to compress octave range starting at 0 and going to 255;
uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1); uint16_t i = map(beatsin8(8+octCount*4, 0, 255, 0, octCount*8), 0, 255, 0, SEGLEN-1);
// i will be always constrained between 0 and 0 if SEGLEN equals 1
i = constrain(i, 0, SEGLEN-1); i = constrain(i, 0, SEGLEN-1);
SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp)); SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
return FRAMETIME; return FRAMETIME;
} // mode_rocktaves() } // mode_rocktaves()
static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1,si=0"; // Bar, Beatsin static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;01f;m12=1,si=0"; // Bar, Beatsin
/////////////////////// ///////////////////////
@ -7379,8 +7382,8 @@ static const char _data_FX_MODE_ROCKTAVES[] PROGMEM = "Rocktaves@;!,!;!;1f;m12=1
/////////////////////// ///////////////////////
// Combines peak detection with FFT_MajorPeak and FFT_Magnitude. // Combines peak detection with FFT_MajorPeak and FFT_Magnitude.
uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tuline
if (SEGLEN == 1) return mode_static(); // effect can work on single pixels, we just lose the shifting effect
um_data_t *um_data; um_data_t *um_data;
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) { if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
// add support for no audio // add support for no audio
@ -7417,12 +7420,13 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
} else { } else {
SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude)); SEGMENT.setPixelColor(SEGLEN-1, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(pixCol+SEGMENT.intensity, false, PALETTE_SOLID_WRAP, 0), (int)my_magnitude));
} }
// loop will not execute if SEGLEN equals 1
for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left for (int i = 0; i < SEGLEN-1; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // shift left
} }
return FRAMETIME; return FRAMETIME;
} // mode_waterfall() } // mode_waterfall()
static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;1f;c2=0,m12=2,si=0"; // Circles, Beatsin static const char _data_FX_MODE_WATERFALL[] PROGMEM = "Waterfall@!,Adjust color,Select bin,Volume (min);!,!;!;01f;c2=0,m12=2,si=0"; // Circles, Beatsin
#ifndef WLED_DISABLE_2D #ifndef WLED_DISABLE_2D

View File

@ -106,6 +106,10 @@
#define PURPLE (uint32_t)0x400080 #define PURPLE (uint32_t)0x400080
#define ORANGE (uint32_t)0xFF3000 #define ORANGE (uint32_t)0xFF3000
#define PINK (uint32_t)0xFF1493 #define PINK (uint32_t)0xFF1493
#define GREY (uint32_t)0x808080
#define GRAY GREY
#define DARKGREY (uint32_t)0x333333
#define DARKGRAY DARKGREY
#define ULTRAWHITE (uint32_t)0xFFFFFFFF #define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F #define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY DARKSLATEGRAY #define DARKSLATEGREY DARKSLATEGRAY
@ -631,6 +635,7 @@ typedef struct Segment {
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); } inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); }
#ifdef WLED_USE_AA_PIXELS #ifdef WLED_USE_AA_PIXELS
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true); void setPixelColorXY(float x, float y, uint32_t c, bool aa = true);
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
@ -651,24 +656,25 @@ typedef struct Segment {
void moveX(int8_t delta, bool wrap = false); void moveX(int8_t delta, bool wrap = false);
void moveY(int8_t delta, bool wrap = false); void moveY(int8_t delta, bool wrap = false);
void move(uint8_t dir, uint8_t delta, bool wrap = false); void move(uint8_t dir, uint8_t delta, bool wrap = false);
void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c); inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c); void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0)); } // automatic inline inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false);
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0); void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0);
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0)); } // automatic inline
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0), rotate); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c); void wu_pixel(uint32_t x, uint32_t y, CRGB c);
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
inline void blur2d(fract8 blur_amount) { blur(blur_amount); } inline void blur2d(fract8 blur_amount) { blur(blur_amount); }
inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); } inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
void nscale8(uint8_t scale);
#else #else
inline uint16_t XY(uint16_t x, uint16_t y) { return x; } inline uint16_t XY(uint16_t x, uint16_t y) { return x; }
inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); } inline void setPixelColorXY(int x, int y, uint32_t c) { setPixelColor(x, c); }
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); } inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); } inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); } inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); }
#ifdef WLED_USE_AA_PIXELS #ifdef WLED_USE_AA_PIXELS
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); } inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); } inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
@ -688,9 +694,12 @@ typedef struct Segment {
inline void moveX(int8_t delta, bool wrap = false) {} inline void moveX(int8_t delta, bool wrap = false) {}
inline void moveY(int8_t delta, bool wrap = false) {} inline void moveY(int8_t delta, bool wrap = false) {}
inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {} inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {} inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {} inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {} inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {}
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {} inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}

View File

@ -373,55 +373,36 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) {
// 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 Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
if (!isActive() || blur_amount == 0) return; // not active if (!isActive() || blur_amount == 0) return; // not active
const unsigned cols = virtualWidth(); const int cols = virtualWidth();
const unsigned rows = virtualHeight(); const int rows = virtualHeight();
const unsigned dim1 = vertical ? rows : cols; const int dim1 = vertical ? rows : cols;
const unsigned dim2 = vertical ? cols : rows; const int 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;
// 1D box blur // 1D box blur
CRGB tmp[dim1]; uint32_t out[dim1], in[dim1];
for (unsigned j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
unsigned x = vertical ? i : j; int x = vertical ? i : j;
unsigned y = vertical ? j : i; int y = vertical ? j : i;
int xp = vertical ? x : x-1; // "signed" to prevent underflow in[j] = getPixelColorXY(x, y);
int yp = vertical ? y-1 : y; // "signed" to prevent underflow
unsigned xn = vertical ? x : x+1;
unsigned yn = vertical ? y+1 : y;
CRGB curr = getPixelColorXY(x,y);
CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp);
CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn);
unsigned r, g, b;
r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
tmp[j] = CRGB(r,g,b);
} }
for (unsigned j = 0; j < dim1; j++) { for (int j = 0; j < dim1; j++) {
unsigned x = vertical ? i : j; uint32_t curr = in[j];
unsigned y = vertical ? j : i; uint32_t prev = j > 0 ? in[j-1] : BLACK;
setPixelColorXY(x, y, tmp[j]); uint32_t next = j < dim1-1 ? in[j+1] : BLACK;
uint8_t r, g, b, w;
r = (R(curr)*keep + (R(prev) + R(next))*seep) / 3;
g = (G(curr)*keep + (G(prev) + G(next))*seep) / 3;
b = (B(curr)*keep + (B(prev) + B(next))*seep) / 3;
w = (W(curr)*keep + (W(prev) + W(next))*seep) / 3;
out[j] = RGBW32(r,g,b,w);
}
for (int j = 0; j < dim1; j++) {
int x = vertical ? i : j;
int y = vertical ? j : i;
setPixelColorXY(x, y, out[j]);
} }
}
// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
//
// 0 = no spread at all
// 64 = moderate spreading
// 172 = maximum smooth, even spreading
//
// 173..255 = wider spreading, but increasing flicker
//
// Total light is NOT entirely conserved, so many repeated
// calls to 'blur' will also result in the light fading,
// eventually all the way to black; this is by design so that
// it can be used to (slowly) clear the LEDs to black.
void Segment::blur1d(fract8 blur_amount) {
const unsigned rows = virtualHeight();
for (unsigned y = 0; y < rows; y++) blurRow(y, blur_amount);
} }
void Segment::moveX(int8_t delta, bool wrap) { void Segment::moveX(int8_t delta, bool wrap) {
@ -478,33 +459,67 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
} }
} }
void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
if (!isActive() || radius == 0) return; // not active if (!isActive() || radius == 0) return; // not active
// Bresenhams Algorithm if (soft) {
int d = 3 - (2*radius); // Xiaolin Wus algorithm
int y = radius, x = 0; int rsq = radius*radius;
while (y >= x) { int x = 0;
setPixelColorXY(cx+x, cy+y, col); int y = radius;
setPixelColorXY(cx-x, cy+y, col); unsigned oldFade = 0;
setPixelColorXY(cx+x, cy-y, col); while (x < y) {
setPixelColorXY(cx-x, cy-y, col); float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
setPixelColorXY(cx+y, cy+x, col); unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
setPixelColorXY(cx-y, cy+x, col); if (oldFade > fade) y--;
setPixelColorXY(cx+y, cy-x, col); oldFade = fade;
setPixelColorXY(cx-y, cy-x, col); setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
x++; setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
if (d > 0) { setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
y--; setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
d += 4 * (x - y) + 10; setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
} else { setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
d += 4 * x + 6; setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true));
setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true));
setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true));
setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true));
setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true));
setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true));
setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true));
setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true));
setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true));
setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true));
x++;
}
} else {
// Bresenhams Algorithm
int d = 3 - (2*radius);
int y = radius, x = 0;
while (y >= x) {
setPixelColorXY(cx+x, cy+y, col);
setPixelColorXY(cx-x, cy+y, col);
setPixelColorXY(cx+x, cy-y, col);
setPixelColorXY(cx-x, cy-y, col);
setPixelColorXY(cx+y, cy+x, col);
setPixelColorXY(cx-y, cy+x, col);
setPixelColorXY(cx+y, cy-x, col);
setPixelColorXY(cx-y, cy-x, col);
x++;
if (d > 0) {
y--;
d += 4 * (x - y) + 10;
} else {
d += 4 * x + 6;
}
} }
} }
} }
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs // by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) { void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
if (!isActive() || radius == 0) return; // not active if (!isActive() || radius == 0) return; // not active
// draw soft bounding circle
if (soft) drawCircle(cx, cy, radius, col, soft);
// fill it
const int cols = virtualWidth(); const int cols = virtualWidth();
const int rows = virtualHeight(); const int rows = virtualHeight();
for (int y = -radius; y <= radius; y++) { for (int y = -radius; y <= radius; y++) {
@ -517,30 +532,58 @@ void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
} }
} }
void Segment::nscale8(uint8_t scale) {
if (!isActive()) return; // not active
const unsigned cols = virtualWidth();
const unsigned rows = virtualHeight();
for (unsigned y = 0; y < rows; y++) for (unsigned x = 0; x < cols; x++) {
setPixelColorXY(x, y, CRGB(getPixelColorXY(x, y)).nscale8(scale));
}
}
//line function //line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) { void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft) {
if (!isActive()) return; // not active if (!isActive()) return; // not active
const unsigned cols = virtualWidth(); const int cols = virtualWidth();
const unsigned rows = virtualHeight(); const int rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return; if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
int err = (dx>dy ? dx : -dy)/2, e2; const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
for (;;) {
setPixelColorXY(x0,y0,c); // single pixel (line length == 0)
if (x0==x1 && y0==y1) break; if (dx+dy == 0) {
e2 = err; setPixelColorXY(x0, y0, c);
if (e2 >-dx) { err -= dy; x0 += sx; } return;
if (e2 < dy) { err += dx; y0 += sy; } }
if (soft) {
// Xiaolin Wus algorithm
const bool steep = dy > dx;
if (steep) {
// we need to go along longest dimension
std::swap(x0,y0);
std::swap(x1,y1);
}
if (x0 > x1) {
// we need to go in increasing fashion
std::swap(x0,x1);
std::swap(y0,y1);
}
float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0);
float intersectY = y0;
for (int x = x0; x <= x1; x++) {
unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep
unsigned seep = 0xFFFF - keep; // how much background to keep
int y = int(intersectY);
if (steep) std::swap(x,y); // temporaryly swap if steep
// pixel coverage is determined by fractional part of y co-ordinate
setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true));
setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true));
intersectY += gradient;
if (steep) std::swap(x,y); // restore if steep
}
} else {
// Bresenham's algorithm
int err = (dx>dy ? dx : -dy)/2; // error direction
for (;;) {
setPixelColorXY(x0, y0, c);
if (x0==x1 && y0==y1) break;
int e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
} }
} }

View File

@ -74,11 +74,16 @@
#define I_8266_U1_APA106_3 82 #define I_8266_U1_APA106_3 82
#define I_8266_DM_APA106_3 83 #define I_8266_DM_APA106_3 83
#define I_8266_BB_APA106_3 84 #define I_8266_BB_APA106_3 84
//WS2805 //WS2805 (RGBCW)
#define I_8266_U0_2805_5 89 #define I_8266_U0_2805_5 89
#define I_8266_U1_2805_5 90 #define I_8266_U1_2805_5 90
#define I_8266_DM_2805_5 91 #define I_8266_DM_2805_5 91
#define I_8266_BB_2805_5 92 #define I_8266_BB_2805_5 92
//TM1914 (RGB)
#define I_8266_U0_TM1914_3 99
#define I_8266_U1_TM1914_3 100
#define I_8266_DM_TM1914_3 101
#define I_8266_BB_TM1914_3 102
/*** ESP32 Neopixel methods ***/ /*** ESP32 Neopixel methods ***/
//RGB //RGB
@ -117,10 +122,14 @@
#define I_32_RN_APA106_3 85 #define I_32_RN_APA106_3 85
#define I_32_I0_APA106_3 86 #define I_32_I0_APA106_3 86
#define I_32_I1_APA106_3 87 #define I_32_I1_APA106_3 87
//WS2805 //WS2805 (RGBCW)
#define I_32_RN_2805_5 93 #define I_32_RN_2805_5 93
#define I_32_I0_2805_5 94 #define I_32_I0_2805_5 94
#define I_32_I1_2805_5 95 #define I_32_I1_2805_5 95
//TM1914 (RGB)
#define I_32_RN_TM1914_3 96
#define I_32_I0_TM1914_3 97
#define I_32_I1_TM1914_3 98
//APA102 //APA102
@ -170,10 +179,10 @@
#define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod> #define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
#define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod> #define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
//TM1829 (RGB) //TM1829 (RGB)
#define B_8266_U0_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod> #define B_8266_U0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
#define B_8266_U1_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod> #define B_8266_U1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
#define B_8266_DM_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod> #define B_8266_DM_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
#define B_8266_BB_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod> #define B_8266_BB_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
//UCS8903 //UCS8903
#define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1 #define B_8266_U0_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart0Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio1
#define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2 #define B_8266_U1_UCS_3 NeoPixelBusLg<NeoRgbUcs8903Feature, NeoEsp8266Uart1Ws2813Method, NeoGammaNullMethod> //3 chan, esp8266, gpio2
@ -199,6 +208,11 @@
#define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2 #define B_8266_U1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266Uart1Ws2805Method, NeoGammaNullMethod> //esp8266, gpio2
#define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3 #define B_8266_DM_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266DmaWs2805Method, NeoGammaNullMethod> //esp8266, gpio3
#define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb #define B_8266_BB_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp8266BitBangWs2805Method, NeoGammaNullMethod> //esp8266, bb
//TM1914 (RGB)
#define B_8266_U0_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart0Tm1914Method, NeoGammaNullMethod>
#define B_8266_U1_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266Uart1Tm1914Method, NeoGammaNullMethod>
#define B_8266_DM_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266DmaTm1914Method, NeoGammaNullMethod>
#define B_8266_BB_TM1914_3 NeoPixelBusLg<NeoRgbTm1914Feature, NeoEsp8266BitBangTm1914Method, NeoGammaNullMethod>
#endif #endif
/*** ESP32 Neopixel methods ***/ /*** ESP32 Neopixel methods ***/
@ -302,6 +316,16 @@
#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1Ws2805Method, NeoGammaNullMethod> #define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1Ws2805Method, NeoGammaNullMethod>
//#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S //#define B_32_I1_2805_5 NeoPixelBusLg<NeoGrbwwFeature, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
#endif #endif
//TM1914 (RGB)
#define B_32_RN_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32RmtNTm1914Method, NeoGammaNullMethod>
#ifndef WLED_NO_I2S0_PIXELBUS
#define B_32_I0_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s0Tm1914Method, NeoGammaNullMethod>
//#define B_32_I0_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s0X8Tm1914Method, NeoGammaNullMethod>
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
#define B_32_I1_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1Tm1914Method, NeoGammaNullMethod>
//#define B_32_I1_TM1914_3 NeoPixelBusLg<NeoGrbTm1914Feature, NeoEsp32I2s1X8Tm1914Method, NeoGammaNullMethod>
#endif
#endif #endif
//APA102 //APA102
@ -363,6 +387,13 @@ class PolyBus {
tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225)); tm1814_strip->SetPixelSettings(NeoTm1814Settings(/*R*/225, /*G*/225, /*B*/225, /*W*/225));
} }
template <class T>
static void beginTM1914(void* busPtr) {
T tm1914_strip = static_cast<T>(busPtr);
tm1914_strip->Begin();
tm1914_strip->SetPixelSettings(NeoTm1914Settings()); //NeoTm1914_Mode_DinFdinAutoSwitch, NeoTm1914_Mode_DinOnly, NeoTm1914_Mode_FdinOnly
}
static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) { static void begin(void* busPtr, uint8_t busType, uint8_t* pins, uint16_t clock_kHz = 0U) {
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;
@ -383,10 +414,10 @@ class PolyBus {
case I_8266_U1_TM1_4: beginTM1814<B_8266_U1_TM1_4*>(busPtr); break; case I_8266_U1_TM1_4: beginTM1814<B_8266_U1_TM1_4*>(busPtr); break;
case I_8266_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break; case I_8266_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break;
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break; case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Begin(); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->Begin(); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->Begin(); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->Begin(); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->Begin(); break;
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break; case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break; case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break; case I_HS_LPO_3: beginDotStar<B_HS_LPO_3*>(busPtr, -1, -1, -1, -1, clock_kHz); break;
@ -412,6 +443,10 @@ class PolyBus {
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Begin(); break; case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Begin(); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Begin(); break; case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Begin(); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Begin(); break; case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Begin(); break;
case I_8266_U0_TM1914_3: beginTM1914<B_8266_U0_TM1914_3*>(busPtr); break;
case I_8266_U1_TM1914_3: beginTM1914<B_8266_U1_TM1914_3*>(busPtr); break;
case I_8266_DM_TM1914_3: beginTM1914<B_8266_DM_TM1914_3*>(busPtr); break;
case I_8266_BB_TM1914_3: beginTM1914<B_8266_BB_TM1914_3*>(busPtr); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
@ -480,6 +515,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Begin(); break; case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Begin(); break;
#endif #endif
case I_32_RN_TM1914_3: beginTM1914<B_32_RN_TM1914_3*>(busPtr); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: beginTM1914<B_32_I0_TM1914_3*>(busPtr); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: beginTM1914<B_32_I1_TM1914_3*>(busPtr); break;
#endif
// ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin() // ESP32 can (and should, to avoid inadvertantly driving the chip select signal) specify the pins used for SPI, but only in begin()
case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break; case I_HS_DOT_3: beginDotStar<B_HS_DOT_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break; case I_HS_LPD_3: beginDotStar<B_HS_LPD_3*>(busPtr, pins[1], -1, pins[0], -1, clock_kHz); break;
@ -499,7 +541,7 @@ class PolyBus {
#if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) #if defined(ARDUINO_ARCH_ESP32) && !(defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
// NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation // NOTE: "channel" is only used on ESP32 (and its variants) for RMT channel allocation
// since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation // since 0.15.0-b3 I2S1 is favoured for classic ESP32 and moved to position 0 (channel 0) so we need to subtract 1 for correct RMT allocation
if (channel > 1) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32 if (channel > 0) channel--; // accommodate I2S1 which is used as 1st bus on classic ESP32
#endif #endif
void* busPtr = nullptr; void* busPtr = nullptr;
switch (busType) { switch (busType) {
@ -521,10 +563,10 @@ class PolyBus {
case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break; case I_8266_U1_TM1_4: busPtr = new B_8266_U1_TM1_4(len, pins[0]); break;
case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break; case I_8266_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break;
case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break; case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break;
case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_4(len, pins[0]); break; case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_3(len, pins[0]); break;
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break; case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_3(len, pins[0]); break;
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break; case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_3(len, pins[0]); break;
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break; case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_3(len, pins[0]); break;
case I_8266_U0_UCS_3: busPtr = new B_8266_U0_UCS_3(len, pins[0]); break; case I_8266_U0_UCS_3: busPtr = new B_8266_U0_UCS_3(len, pins[0]); break;
case I_8266_U1_UCS_3: busPtr = new B_8266_U1_UCS_3(len, pins[0]); break; case I_8266_U1_UCS_3: busPtr = new B_8266_U1_UCS_3(len, pins[0]); break;
case I_8266_DM_UCS_3: busPtr = new B_8266_DM_UCS_3(len, pins[0]); break; case I_8266_DM_UCS_3: busPtr = new B_8266_DM_UCS_3(len, pins[0]); break;
@ -545,6 +587,10 @@ class PolyBus {
case I_8266_U1_2805_5: busPtr = new B_8266_U1_2805_5(len, pins[0]); break; case I_8266_U1_2805_5: busPtr = new B_8266_U1_2805_5(len, pins[0]); break;
case I_8266_DM_2805_5: busPtr = new B_8266_DM_2805_5(len, pins[0]); break; case I_8266_DM_2805_5: busPtr = new B_8266_DM_2805_5(len, pins[0]); break;
case I_8266_BB_2805_5: busPtr = new B_8266_BB_2805_5(len, pins[0]); break; case I_8266_BB_2805_5: busPtr = new B_8266_BB_2805_5(len, pins[0]); break;
case I_8266_U0_TM1914_3: busPtr = new B_8266_U0_TM1914_3(len, pins[0]); break;
case I_8266_U1_TM1914_3: busPtr = new B_8266_U1_TM1914_3(len, pins[0]); break;
case I_8266_DM_TM1914_3: busPtr = new B_8266_DM_TM1914_3(len, pins[0]); break;
case I_8266_BB_TM1914_3: busPtr = new B_8266_BB_TM1914_3(len, pins[0]); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break; case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
@ -613,6 +659,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: busPtr = new B_32_I1_2805_5(len, pins[0]); break; case I_32_I1_2805_5: busPtr = new B_32_I1_2805_5(len, pins[0]); break;
#endif #endif
case I_32_RN_TM1914_3: busPtr = new B_32_RN_TM1914_3(len, pins[0], (NeoBusChannel)channel); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: busPtr = new B_32_I0_TM1914_3(len, pins[0]); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: busPtr = new B_32_I1_TM1914_3(len, pins[0]); break;
#endif
#endif #endif
// for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat) // for 2-wire: pins[1] is clk, pins[0] is dat. begin expects (len, clk, dat)
case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break; case I_HS_DOT_3: busPtr = new B_HS_DOT_3(len, pins[1], pins[0]); break;
@ -650,10 +703,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(consistent); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(consistent); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(consistent); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(consistent); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(consistent); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(consistent); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Show(consistent); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->Show(consistent); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(consistent); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(consistent); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(consistent); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->Show(consistent); break;
@ -674,6 +727,10 @@ class PolyBus {
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Show(consistent); break; case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Show(consistent); break; case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Show(consistent); break; case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->Show(consistent); break;
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->Show(consistent); break;
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->Show(consistent); break;
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->Show(consistent); break;
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->Show(consistent); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(consistent); break;
@ -742,6 +799,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break; case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break;
#endif #endif
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->Show(consistent); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->Show(consistent); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->Show(consistent); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->Show(consistent); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->Show(consistent); break;
@ -776,10 +840,10 @@ class PolyBus {
case I_8266_U1_TM1_4: return (static_cast<B_8266_U1_TM1_4*>(busPtr))->CanShow(); break; case I_8266_U1_TM1_4: return (static_cast<B_8266_U1_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break; case I_8266_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break; case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break;
case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_4*>(busPtr))->CanShow(); break; case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_3*>(busPtr))->CanShow(); break;
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_4*>(busPtr))->CanShow(); break; case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_3*>(busPtr))->CanShow(); break;
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_4*>(busPtr))->CanShow(); break; case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_3*>(busPtr))->CanShow(); break;
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_4*>(busPtr))->CanShow(); break; case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_3*>(busPtr))->CanShow(); break;
case I_8266_U0_UCS_3: return (static_cast<B_8266_U0_UCS_3*>(busPtr))->CanShow(); break; case I_8266_U0_UCS_3: return (static_cast<B_8266_U0_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_U1_UCS_3: return (static_cast<B_8266_U1_UCS_3*>(busPtr))->CanShow(); break; case I_8266_U1_UCS_3: return (static_cast<B_8266_U1_UCS_3*>(busPtr))->CanShow(); break;
case I_8266_DM_UCS_3: return (static_cast<B_8266_DM_UCS_3*>(busPtr))->CanShow(); break; case I_8266_DM_UCS_3: return (static_cast<B_8266_DM_UCS_3*>(busPtr))->CanShow(); break;
@ -799,6 +863,10 @@ class PolyBus {
case I_8266_U1_2805_5: return (static_cast<B_8266_U1_2805_5*>(busPtr))->CanShow(); break; case I_8266_U1_2805_5: return (static_cast<B_8266_U1_2805_5*>(busPtr))->CanShow(); break;
case I_8266_DM_2805_5: return (static_cast<B_8266_DM_2805_5*>(busPtr))->CanShow(); break; case I_8266_DM_2805_5: return (static_cast<B_8266_DM_2805_5*>(busPtr))->CanShow(); break;
case I_8266_BB_2805_5: return (static_cast<B_8266_BB_2805_5*>(busPtr))->CanShow(); break; case I_8266_BB_2805_5: return (static_cast<B_8266_BB_2805_5*>(busPtr))->CanShow(); break;
case I_8266_U0_TM1914_3: return (static_cast<B_8266_U0_TM1914_3*>(busPtr))->CanShow(); break;
case I_8266_U1_TM1914_3: return (static_cast<B_8266_U1_TM1914_3*>(busPtr))->CanShow(); break;
case I_8266_DM_TM1914_3: return (static_cast<B_8266_DM_TM1914_3*>(busPtr))->CanShow(); break;
case I_8266_BB_TM1914_3: return (static_cast<B_8266_BB_TM1914_3*>(busPtr))->CanShow(); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break; case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
@ -867,6 +935,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break; case I_32_I1_2805_5: return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
#endif #endif
case I_32_RN_TM1914_3: return (static_cast<B_32_RN_TM1914_3*>(busPtr))->CanShow(); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: return (static_cast<B_32_I0_TM1914_3*>(busPtr))->CanShow(); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: return (static_cast<B_32_I1_TM1914_3*>(busPtr))->CanShow(); break;
#endif
#endif #endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break; case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;
case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break; case I_SS_DOT_3: return (static_cast<B_SS_DOT_3*>(busPtr))->CanShow(); break;
@ -926,10 +1001,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetPixelColor(pix, Rgb48Color(RgbColor(col))); break;
@ -950,6 +1025,10 @@ class PolyBus {
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -1018,6 +1097,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break; case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetPixelColor(pix, RgbwwColor(col.R, col.G, col.B, cctWW, cctCW)); break;
#endif #endif
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
@ -1052,10 +1138,10 @@ class PolyBus {
case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_TM1_4: (static_cast<B_8266_U1_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->SetLuminance(b); break; case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_U0_UCS_3: (static_cast<B_8266_U0_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_U1_UCS_3: (static_cast<B_8266_U1_UCS_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break; case I_8266_DM_UCS_3: (static_cast<B_8266_DM_UCS_3*>(busPtr))->SetLuminance(b); break;
@ -1076,6 +1162,10 @@ class PolyBus {
case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break; case I_8266_U1_2805_5: (static_cast<B_8266_U1_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break; case I_8266_DM_2805_5: (static_cast<B_8266_DM_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break; case I_8266_BB_2805_5: (static_cast<B_8266_BB_2805_5*>(busPtr))->SetLuminance(b); break;
case I_8266_U0_TM1914_3: (static_cast<B_8266_U0_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_U1_TM1914_3: (static_cast<B_8266_U1_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_DM_TM1914_3: (static_cast<B_8266_DM_TM1914_3*>(busPtr))->SetLuminance(b); break;
case I_8266_BB_TM1914_3: (static_cast<B_8266_BB_TM1914_3*>(busPtr))->SetLuminance(b); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break; case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetLuminance(b); break;
@ -1144,6 +1234,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break; case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break;
#endif #endif
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->SetLuminance(b); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->SetLuminance(b); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: (static_cast<B_32_I1_TM1914_3*>(busPtr))->SetLuminance(b); break;
#endif
#endif #endif
case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_HS_DOT_3: (static_cast<B_HS_DOT_3*>(busPtr))->SetLuminance(b); break;
case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break; case I_SS_DOT_3: (static_cast<B_SS_DOT_3*>(busPtr))->SetLuminance(b); break;
@ -1179,10 +1276,10 @@ class PolyBus {
case I_8266_U1_TM1_4: col = (static_cast<B_8266_U1_TM1_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_U1_TM1_4: col = (static_cast<B_8266_U1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_4*>(busPtr))->GetPixelColor(pix); break; case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U0_UCS_3: { Rgb48Color c = (static_cast<B_8266_U0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break; case I_8266_U0_UCS_3: { Rgb48Color c = (static_cast<B_8266_U0_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_U1_UCS_3: { Rgb48Color c = (static_cast<B_8266_U1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break; case I_8266_U1_UCS_3: { Rgb48Color c = (static_cast<B_8266_U1_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
case I_8266_DM_UCS_3: { Rgb48Color c = (static_cast<B_8266_DM_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break; case I_8266_DM_UCS_3: { Rgb48Color c = (static_cast<B_8266_DM_UCS_3*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R>>8,c.G>>8,c.B>>8,0); } break;
@ -1203,6 +1300,10 @@ class PolyBus {
case I_8266_U1_2805_5: { RgbwwColor c = (static_cast<B_8266_U1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W case I_8266_U1_2805_5: { RgbwwColor c = (static_cast<B_8266_U1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
case I_8266_DM_2805_5: { RgbwwColor c = (static_cast<B_8266_DM_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W case I_8266_DM_2805_5: { RgbwwColor c = (static_cast<B_8266_DM_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
case I_8266_BB_2805_5: { RgbwwColor c = (static_cast<B_8266_BB_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W case I_8266_BB_2805_5: { RgbwwColor c = (static_cast<B_8266_BB_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
case I_8266_U0_TM1914_3: col = (static_cast<B_8266_U0_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_U1_TM1914_3: col = (static_cast<B_8266_U1_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_DM_TM1914_3: col = (static_cast<B_8266_DM_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
case I_8266_BB_TM1914_3: col = (static_cast<B_8266_BB_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break; case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
@ -1271,6 +1372,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: { RgbwwColor c = (static_cast<B_32_I1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W case I_32_I1_2805_5: { RgbwwColor c = (static_cast<B_32_I1_2805_5*>(busPtr))->GetPixelColor(pix); col = RGBW32(c.R,c.G,c.B,max(c.WW,c.CW)); } break; // will not return original W
#endif #endif
case I_32_RN_TM1914_3: col = (static_cast<B_32_RN_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: col = (static_cast<B_32_I0_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: col = (static_cast<B_32_I1_TM1914_3*>(busPtr))->GetPixelColor(pix); break;
#endif
#endif #endif
case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break; case I_HS_DOT_3: col = (static_cast<B_HS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break; case I_SS_DOT_3: col = (static_cast<B_SS_DOT_3*>(busPtr))->GetPixelColor(pix); break;
@ -1324,10 +1432,10 @@ class PolyBus {
case I_8266_U1_TM1_4: delete (static_cast<B_8266_U1_TM1_4*>(busPtr)); break; case I_8266_U1_TM1_4: delete (static_cast<B_8266_U1_TM1_4*>(busPtr)); break;
case I_8266_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break; case I_8266_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break;
case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break; case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break;
case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_4*>(busPtr)); break; case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_3*>(busPtr)); break;
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break; case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_3*>(busPtr)); break;
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break; case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_3*>(busPtr)); break;
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_4*>(busPtr)); break; case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_3*>(busPtr)); break;
case I_8266_U0_UCS_3: delete (static_cast<B_8266_U0_UCS_3*>(busPtr)); break; case I_8266_U0_UCS_3: delete (static_cast<B_8266_U0_UCS_3*>(busPtr)); break;
case I_8266_U1_UCS_3: delete (static_cast<B_8266_U1_UCS_3*>(busPtr)); break; case I_8266_U1_UCS_3: delete (static_cast<B_8266_U1_UCS_3*>(busPtr)); break;
case I_8266_DM_UCS_3: delete (static_cast<B_8266_DM_UCS_3*>(busPtr)); break; case I_8266_DM_UCS_3: delete (static_cast<B_8266_DM_UCS_3*>(busPtr)); break;
@ -1348,6 +1456,10 @@ class PolyBus {
case I_8266_U1_2805_5: delete (static_cast<B_8266_U1_2805_5*>(busPtr)); break; case I_8266_U1_2805_5: delete (static_cast<B_8266_U1_2805_5*>(busPtr)); break;
case I_8266_DM_2805_5: delete (static_cast<B_8266_DM_2805_5*>(busPtr)); break; case I_8266_DM_2805_5: delete (static_cast<B_8266_DM_2805_5*>(busPtr)); break;
case I_8266_BB_2805_5: delete (static_cast<B_8266_BB_2805_5*>(busPtr)); break; case I_8266_BB_2805_5: delete (static_cast<B_8266_BB_2805_5*>(busPtr)); break;
case I_8266_U0_TM1914_3: delete (static_cast<B_8266_U0_TM1914_3*>(busPtr)); break;
case I_8266_U1_TM1914_3: delete (static_cast<B_8266_U1_TM1914_3*>(busPtr)); break;
case I_8266_DM_TM1914_3: delete (static_cast<B_8266_DM_TM1914_3*>(busPtr)); break;
case I_8266_BB_TM1914_3: delete (static_cast<B_8266_BB_TM1914_3*>(busPtr)); break;
#endif #endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break; case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
@ -1416,6 +1528,13 @@ class PolyBus {
#ifndef WLED_NO_I2S1_PIXELBUS #ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_2805_5: delete (static_cast<B_32_I1_2805_5*>(busPtr)); break; case I_32_I1_2805_5: delete (static_cast<B_32_I1_2805_5*>(busPtr)); break;
#endif #endif
case I_32_RN_TM1914_3: delete (static_cast<B_32_RN_TM1914_3*>(busPtr)); break;
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_TM1914_3: delete (static_cast<B_32_I0_TM1914_3*>(busPtr)); break;
#endif
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_TM1914_3: delete (static_cast<B_32_I1_TM1914_3*>(busPtr)); break;
#endif
#endif #endif
case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break; case I_HS_DOT_3: delete (static_cast<B_HS_DOT_3*>(busPtr)); break;
case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break; case I_SS_DOT_3: delete (static_cast<B_SS_DOT_3*>(busPtr)); break;
@ -1481,6 +1600,8 @@ class PolyBus {
return I_8266_U0_FW6_5 + offset; return I_8266_U0_FW6_5 + offset;
case TYPE_WS2805: case TYPE_WS2805:
return I_8266_U0_2805_5 + offset; return I_8266_U0_2805_5 + offset;
case TYPE_TM1914:
return I_8266_U0_TM1914_3 + offset;
} }
#else //ESP32 #else //ESP32
uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1 uint8_t offset = 0; // 0 = RMT (num 1-8), 1 = I2S0 (used by Audioreactive), 2 = I2S1
@ -1526,6 +1647,8 @@ class PolyBus {
return I_32_RN_FW6_5 + offset; return I_32_RN_FW6_5 + offset;
case TYPE_WS2805: case TYPE_WS2805:
return I_32_RN_2805_5 + offset; return I_32_RN_2805_5 + offset;
case TYPE_TM1914:
return I_32_RN_TM1914_3 + offset;
} }
#endif #endif
} }

View File

@ -7,11 +7,13 @@
#define WLED_DEBOUNCE_THRESHOLD 50 // only consider button input of at least 50ms as valid (debouncing) #define WLED_DEBOUNCE_THRESHOLD 50 // only consider button input of at least 50ms as valid (debouncing)
#define WLED_LONG_PRESS 600 // long press if button is released after held for at least 600ms #define WLED_LONG_PRESS 600 // long press if button is released after held for at least 600ms
#define WLED_DOUBLE_PRESS 350 // double press if another press within 350ms after a short press #define WLED_DOUBLE_PRESS 350 // double press if another press within 350ms after a short press
#define WLED_LONG_REPEATED_ACTION 300 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0 #define WLED_LONG_REPEATED_ACTION 400 // how often a repeated action (e.g. dimming) is fired on long press on button IDs >0
#define WLED_LONG_AP 5000 // how long button 0 needs to be held to activate WLED-AP #define WLED_LONG_AP 5000 // how long button 0 needs to be held to activate WLED-AP
#define WLED_LONG_FACTORY_RESET 10000 // how long button 0 needs to be held to trigger a factory reset #define WLED_LONG_FACTORY_RESET 10000 // how long button 0 needs to be held to trigger a factory reset
#define WLED_LONG_BRI_STEPS 16 // how much to increase/decrease the brightness with each long press repetition
static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage static const char _mqtt_topic_button[] PROGMEM = "%s/button/%d"; // optimize flash usage
static bool buttonBriDirection = false; // true: increase brightness, false: decrease brightness
void shortPressAction(uint8_t b) void shortPressAction(uint8_t b)
{ {
@ -39,7 +41,19 @@ void longPressAction(uint8_t b)
if (!macroLongPress[b]) { if (!macroLongPress[b]) {
switch (b) { switch (b) {
case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break; case 0: setRandomColor(col); colorUpdated(CALL_MODE_BUTTON); break;
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action case 1:
if(buttonBriDirection) {
if (bri == 255) break; // avoid unnecessary updates to brightness
if (bri >= 255 - WLED_LONG_BRI_STEPS) bri = 255;
else bri += WLED_LONG_BRI_STEPS;
} else {
if (bri == 1) break; // avoid unnecessary updates to brightness
if (bri <= WLED_LONG_BRI_STEPS) bri = 1;
else bri -= WLED_LONG_BRI_STEPS;
}
stateUpdated(CALL_MODE_BUTTON);
buttonPressedTime[b] = millis();
break; // repeatable action
} }
} else { } else {
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET); applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
@ -284,10 +298,12 @@ void handleButton()
buttonPressedBefore[b] = true; buttonPressedBefore[b] = true;
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
if (!buttonLongPressed[b]) longPressAction(b); if (!buttonLongPressed[b]) {
else if (b) { //repeatable action (~3 times per s) on button > 0 buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
longPressAction(b); longPressAction(b);
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //333ms } else if (b) { //repeatable action (~5 times per s) on button > 0
longPressAction(b);
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //200ms
} }
buttonLongPressed[b] = true; buttonLongPressed[b] = true;
} }

View File

@ -268,6 +268,7 @@
#define TYPE_SK6812_RGBW 30 #define TYPE_SK6812_RGBW 30
#define TYPE_TM1814 31 #define TYPE_TM1814 31
#define TYPE_WS2805 32 //RGB + WW + CW #define TYPE_WS2805 32 //RGB + WW + CW
#define TYPE_TM1914 33 //RGB
//"Analog" types (40-47) //"Analog" types (40-47)
#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM) #define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel #define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel

View File

@ -272,6 +272,7 @@ function onLoad()
selectSlot(0); selectSlot(0);
updateTablinks(0); updateTablinks(0);
handleLocationHash();
cpick.on("input:end", () => {setColor(1);}); cpick.on("input:end", () => {setColor(1);});
cpick.on("color:change", () => {updatePSliders()}); cpick.on("color:change", () => {updatePSliders()});
pmtLS = localStorage.getItem('wledPmt'); pmtLS = localStorage.getItem('wledPmt');
@ -304,7 +305,6 @@ function updateTablinks(tabI)
{ {
var tablinks = gEBCN("tablinks"); var tablinks = gEBCN("tablinks");
for (var i of tablinks) i.classList.remove('active'); for (var i of tablinks) i.classList.remove('active');
if (pcMode) return;
tablinks[tabI].classList.add('active'); tablinks[tabI].classList.add('active');
} }
@ -315,6 +315,21 @@ function openTab(tabI, force = false)
_C.classList.toggle('smooth', false); _C.classList.toggle('smooth', false);
_C.style.setProperty('--i', iSlide); _C.style.setProperty('--i', iSlide);
updateTablinks(tabI); updateTablinks(tabI);
switch (tabI) {
case 0: window.location.hash = "Colors"; break;
case 1: window.location.hash = "Effects"; break;
case 2: window.location.hash = "Segments"; break;
case 3: window.location.hash = "Presets"; break;
}
}
function handleLocationHash() {
switch (window.location.hash) {
case "#Colors": openTab(0); break;
case "#Effects": openTab(1); break;
case "#Segments": openTab(2); break;
case "#Presets": openTab(3); break;
}
} }
var timeout; var timeout;
@ -3055,8 +3070,7 @@ function togglePcMode(fromB = false)
pcMode = (wW >= 1024) && pcModeA; pcMode = (wW >= 1024) && pcModeA;
if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape
if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size() if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size()
openTab(0, true); if (pcMode) openTab(0, true);
updateTablinks(0);
gId('buttonPcm').className = (pcMode) ? "active":""; gId('buttonPcm').className = (pcMode) ? "active":"";
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto"; gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
sCol('--bh', gId('bot').clientHeight + "px"); sCol('--bh', gId('bot').clientHeight + "px");
@ -3218,6 +3232,7 @@ size();
_C.style.setProperty('--n', N); _C.style.setProperty('--n', N);
window.addEventListener('resize', size, true); window.addEventListener('resize', size, true);
window.addEventListener('hashchange', handleLocationHash);
_C.addEventListener('mousedown', lock, false); _C.addEventListener('mousedown', lock, false);
_C.addEventListener('touchstart', lock, false); _C.addEventListener('touchstart', lock, false);

View File

@ -386,6 +386,7 @@ ${i+1}:
<option value="25">TM1829</option>\ <option value="25">TM1829</option>\
<option value="26">UCS8903</option>\ <option value="26">UCS8903</option>\
<option value="27">APA106/PL9823</option>\ <option value="27">APA106/PL9823</option>\
<option value="33">TM1914</option>\
<option value="28">FW1906 GRBCW</option>\ <option value="28">FW1906 GRBCW</option>\
<option value="29">UCS8904 RGBW</option>\ <option value="29">UCS8904 RGBW</option>\
<option value="32">WS2805 RGBCW</option>\ <option value="32">WS2805 RGBCW</option>\

View File

@ -346,7 +346,6 @@ void handleArtnetPollReply(IPAddress ipAddress) {
switch (DMXMode) { switch (DMXMode) {
case DMX_MODE_DISABLED: case DMX_MODE_DISABLED:
return; // nothing to do
break; break;
case DMX_MODE_SINGLE_RGB: case DMX_MODE_SINGLE_RGB:
@ -391,9 +390,17 @@ void handleArtnetPollReply(IPAddress ipAddress) {
break; break;
} }
for (uint16_t i = startUniverse; i <= endUniverse; ++i) { if (DMXMode != DMX_MODE_DISABLED) {
sendArtnetPollReply(&artnetPollReply, ipAddress, i); for (uint16_t i = startUniverse; i <= endUniverse; ++i) {
sendArtnetPollReply(&artnetPollReply, ipAddress, i);
}
} }
#ifdef WLED_ENABLE_DMX
if (e131ProxyUniverse > 0 && (DMXMode == DMX_MODE_DISABLED || (e131ProxyUniverse < startUniverse || e131ProxyUniverse > endUniverse))) {
sendArtnetPollReply(&artnetPollReply, ipAddress, e131ProxyUniverse);
}
#endif
} }
void prepareArtnetPollReply(ArtPollReply *reply) { void prepareArtnetPollReply(ArtPollReply *reply) {

View File

@ -143,20 +143,8 @@ void handleImprovWifiScan();
void sendImprovIPRPCResult(ImprovRPCType type); void sendImprovIPRPCResult(ImprovRPCType type);
//ir.cpp //ir.cpp
void applyRepeatActions();
byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF);
void decodeIR(uint32_t code);
void decodeIR24(uint32_t code);
void decodeIR24OLD(uint32_t code);
void decodeIR24CT(uint32_t code);
void decodeIR40(uint32_t code);
void decodeIR44(uint32_t code);
void decodeIR21(uint32_t code);
void decodeIR6(uint32_t code);
void decodeIR9(uint32_t code);
void decodeIRJson(uint32_t code);
void initIR(); void initIR();
void deInitIR();
void handleIR(); void handleIR();
//json.cpp //json.cpp
@ -410,7 +398,7 @@ void clearEEPROM();
#endif #endif
//wled_math.cpp //wled_math.cpp
#ifndef WLED_USE_REAL_MATH #if defined(ESP8266) && !defined(WLED_USE_REAL_MATH)
template <typename T> T atan_t(T x); template <typename T> T atan_t(T x);
float cos_t(float phi); float cos_t(float phi);
float sin_t(float x); float sin_t(float x);
@ -421,14 +409,14 @@ void clearEEPROM();
float fmod_t(float num, float denom); float fmod_t(float num, float denom);
#else #else
#include <math.h> #include <math.h>
#define sin_t sin #define sin_t sinf
#define cos_t cos #define cos_t cosf
#define tan_t tan #define tan_t tanf
#define asin_t asin #define asin_t asinf
#define acos_t acos #define acos_t acosf
#define atan_t atan #define atan_t atanf
#define fmod_t fmod #define fmod_t fmodf
#define floor_t floor #define floor_t floorf
#endif #endif
//wled_serial.cpp //wled_serial.cpp

View File

@ -381,11 +381,15 @@ void updateFSInfo() {
// original idea by @akaricchi (https://github.com/Akaricchi) // original idea by @akaricchi (https://github.com/Akaricchi)
// returns a pointer to the PSRAM buffer, updates size parameter // returns a pointer to the PSRAM buffer, updates size parameter
static const uint8_t *getPresetCache(size_t &size) { static const uint8_t *getPresetCache(size_t &size) {
static unsigned long presetsCachedTime; static unsigned long presetsCachedTime = 0;
static uint8_t *presetsCached; static uint8_t *presetsCached = nullptr;
static size_t presetsCachedSize; static size_t presetsCachedSize = 0;
static byte presetsCachedValidate = 0;
if (presetsModifiedTime != presetsCachedTime) { //if (presetsModifiedTime != presetsCachedTime) DEBUG_PRINTLN(F("getPresetCache(): presetsModifiedTime changed."));
//if (presetsCachedValidate != cacheInvalidate) DEBUG_PRINTLN(F("getPresetCache(): cacheInvalidate changed."));
if ((presetsModifiedTime != presetsCachedTime) || (presetsCachedValidate != cacheInvalidate)) {
if (presetsCached) { if (presetsCached) {
free(presetsCached); free(presetsCached);
presetsCached = nullptr; presetsCached = nullptr;
@ -396,6 +400,7 @@ static const uint8_t *getPresetCache(size_t &size) {
File file = WLED_FS.open(FPSTR(getPresetsFileName()), "r"); File file = WLED_FS.open(FPSTR(getPresetsFileName()), "r");
if (file) { if (file) {
presetsCachedTime = presetsModifiedTime; presetsCachedTime = presetsModifiedTime;
presetsCachedValidate = cacheInvalidate;
presetsCachedSize = 0; presetsCachedSize = 0;
presetsCached = (uint8_t*)ps_malloc(file.size() + 1); presetsCached = (uint8_t*)ps_malloc(file.size() + 1);
if (presetsCached) { if (presetsCached) {

View File

@ -1,20 +1,14 @@
#include "wled.h" #include "wled.h"
#ifndef WLED_DISABLE_INFRARED
#include "ir_codes.h" #include "ir_codes.h"
/* /*
* Infrared sensor support for generic 24/40/44 key RGB remotes * Infrared sensor support for several generic RGB remotes and custom JSON remote
*/ */
#if defined(WLED_DISABLE_INFRARED)
void handleIR(){}
#else
IRrecv* irrecv; IRrecv* irrecv;
//change pin in NpbWrapper.h
decode_results results; decode_results results;
unsigned long irCheckedTime = 0; unsigned long irCheckedTime = 0;
uint32_t lastValidCode = 0; uint32_t lastValidCode = 0;
byte lastRepeatableAction = ACTION_NONE; byte lastRepeatableAction = ACTION_NONE;
@ -35,16 +29,16 @@ uint8_t lastIR6ColourIdx = 0;
// print("%d values: %s" % (len(result), result)) // print("%d values: %s" % (len(result), result))
// //
// It would be hard to maintain repeatable steps if calculating this on the fly. // It would be hard to maintain repeatable steps if calculating this on the fly.
const byte brightnessSteps[] = { const uint8_t brightnessSteps[] = {
5, 7, 9, 12, 16, 20, 26, 34, 43, 56, 72, 93, 119, 154, 198, 255 5, 7, 9, 12, 16, 20, 26, 34, 43, 56, 72, 93, 119, 154, 198, 255
}; };
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t); const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
// increment `bri` to the next `brightnessSteps` value // increment `bri` to the next `brightnessSteps` value
void incBrightness() static void incBrightness()
{ {
// dumb incremental search is efficient enough for so few items // dumb incremental search is efficient enough for so few items
for (uint8_t index = 0; index < numBrightnessSteps; ++index) for (unsigned index = 0; index < numBrightnessSteps; ++index)
{ {
if (brightnessSteps[index] > bri) if (brightnessSteps[index] > bri)
{ {
@ -56,7 +50,7 @@ void incBrightness()
} }
// decrement `bri` to the next `brightnessSteps` value // decrement `bri` to the next `brightnessSteps` value
void decBrightness() static void decBrightness()
{ {
// dumb incremental search is efficient enough for so few items // dumb incremental search is efficient enough for so few items
for (int index = numBrightnessSteps - 1; index >= 0; --index) for (int index = numBrightnessSteps - 1; index >= 0; --index)
@ -70,12 +64,12 @@ void decBrightness()
} }
} }
void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) static void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID)
{ {
applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID);
} }
byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte higherBoundary) static byte relativeChange(byte property, int8_t amount, byte lowerBoundary = 0, byte higherBoundary = 0xFF)
{ {
int16_t new_val = (int16_t) property + amount; int16_t new_val = (int16_t) property + amount;
if (lowerBoundary >= higherBoundary) return property; if (lowerBoundary >= higherBoundary) return property;
@ -84,10 +78,10 @@ byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte highe
return (byte)constrain(new_val, 0, 255); return (byte)constrain(new_val, 0, 255);
} }
void changeEffect(uint8_t fx) static void changeEffect(uint8_t fx)
{ {
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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);
@ -100,10 +94,10 @@ void changeEffect(uint8_t fx)
stateChanged = true; stateChanged = true;
} }
void changePalette(uint8_t pal) static void changePalette(uint8_t pal)
{ {
if (irApplyToAllSelected) { if (irApplyToAllSelected) {
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
Segment& seg = strip.getSegment(i); Segment& seg = strip.getSegment(i);
if (!seg.isActive() || !seg.isSelected()) continue; if (!seg.isActive() || !seg.isSelected()) continue;
seg.setPalette(pal); seg.setPalette(pal);
@ -116,13 +110,13 @@ void changePalette(uint8_t pal)
stateChanged = true; stateChanged = true;
} }
void changeEffectSpeed(int8_t amount) static void changeEffectSpeed(int8_t amount)
{ {
if (effectCurrent != 0) { if (effectCurrent != 0) {
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.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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;
@ -134,10 +128,7 @@ void changeEffectSpeed(int8_t amount)
} }
} else { // if Effect == "solid Color", change the hue of the primary color } else { // if Effect == "solid Color", change the hue of the primary color
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col; CRGB fastled_col = CRGB(sseg.colors[0]);
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
fastled_col.blue = B(sseg.colors[0]);
CHSV prim_hsv = rgb2hsv_approximate(fastled_col); CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
int16_t new_val = (int16_t)prim_hsv.h + amount; int16_t new_val = (int16_t)prim_hsv.h + amount;
if (new_val > 255) new_val -= 255; // roll-over if bigger than 255 if (new_val > 255) new_val -= 255; // roll-over if bigger than 255
@ -145,7 +136,7 @@ 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.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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]));
@ -163,13 +154,13 @@ void changeEffectSpeed(int8_t amount)
lastRepeatableValue = amount; lastRepeatableValue = amount;
} }
void changeEffectIntensity(int8_t amount) static void changeEffectIntensity(int8_t amount)
{ {
if (effectCurrent != 0) { if (effectCurrent != 0) {
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.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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;
@ -181,16 +172,13 @@ void changeEffectIntensity(int8_t amount)
} }
} else { // if Effect == "solid Color", change the saturation of the primary color } else { // if Effect == "solid Color", change the saturation of the primary color
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment(); Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
CRGB fastled_col; CRGB fastled_col = CRGB(sseg.colors[0]);
fastled_col.red = R(sseg.colors[0]);
fastled_col.green = G(sseg.colors[0]);
fastled_col.blue = B(sseg.colors[0]);
CHSV prim_hsv = rgb2hsv_approximate(fastled_col); CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
int16_t new_val = (int16_t) prim_hsv.s + amount; int16_t new_val = (int16_t) prim_hsv.s + 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.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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]));
@ -208,11 +196,11 @@ void changeEffectIntensity(int8_t amount)
lastRepeatableValue = amount; lastRepeatableValue = amount;
} }
void changeColor(uint32_t c, int16_t cct=-1) static 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.getSegmentsNum(); i++) { for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
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();
@ -249,7 +237,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
stateChanged = true; stateChanged = true;
} }
void changeWhite(int8_t amount, int16_t cct=-1) static void changeWhite(int8_t amount, int16_t cct=-1)
{ {
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]);
@ -259,72 +247,7 @@ void changeWhite(int8_t amount, int16_t cct=-1)
changeColor(RGBW32(r, g, b, w), cct); changeColor(RGBW32(r, g, b, w), cct);
} }
void decodeIR(uint32_t code) static void decodeIR24(uint32_t code)
{
if (code == 0xFFFFFFFF) {
//repeated code, continue brightness up/down
irTimesRepeated++;
applyRepeatActions();
return;
}
lastValidCode = 0; irTimesRepeated = 0;
lastRepeatableAction = ACTION_NONE;
if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code);
stateUpdated(CALL_MODE_BUTTON);
return;
}
if (code > 0xFFFFFF) return; //invalid code
switch (irEnabled) {
case 1:
if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values
else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000
break;
case 2: decodeIR24CT(code); break; // white 24-key remote with CW, WW, CT+ and CT- keys
case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys
case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys
case 5: decodeIR21(code); break; // white 21-key remote
case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness,
// "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE"
// sets bright plain white
case 7: decodeIR9(code); break;
//case 8: return; // ir.json file, handled above switch statement
}
if (nightlightActive && bri == 0) nightlightActive = false;
stateUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
}
void applyRepeatActions()
{
if (irEnabled == 8) {
decodeIRJson(lastValidCode);
return;
} else switch (lastRepeatableAction) {
case ACTION_BRIGHT_UP : incBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_BRIGHT_DOWN : decBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
default: break;
}
if (lastValidCode == IR40_WPLUS) {
changeWhite(10);
stateUpdated(CALL_MODE_BUTTON);
} else if (lastValidCode == IR40_WMINUS) {
changeWhite(-10);
stateUpdated(CALL_MODE_BUTTON);
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
nightlightActive = true;
nightlightStartTime = millis();
stateUpdated(CALL_MODE_BUTTON);
}
}
void decodeIR24(uint32_t code)
{ {
switch (code) { switch (code) {
case IR24_BRIGHTER : incBrightness(); break; case IR24_BRIGHTER : incBrightness(); break;
@ -356,7 +279,7 @@ void decodeIR24(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR24OLD(uint32_t code) static void decodeIR24OLD(uint32_t code)
{ {
switch (code) { switch (code) {
case IR24_OLD_BRIGHTER : incBrightness(); break; case IR24_OLD_BRIGHTER : incBrightness(); break;
@ -388,7 +311,7 @@ void decodeIR24OLD(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR24CT(uint32_t code) static void decodeIR24CT(uint32_t code)
{ {
switch (code) { switch (code) {
case IR24_CT_BRIGHTER : incBrightness(); break; case IR24_CT_BRIGHTER : incBrightness(); break;
@ -420,7 +343,7 @@ void decodeIR24CT(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR40(uint32_t code) static void decodeIR40(uint32_t code)
{ {
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]);
@ -473,7 +396,7 @@ void decodeIR40(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR44(uint32_t code) static void decodeIR44(uint32_t code)
{ {
switch (code) { switch (code) {
case IR44_BPLUS : incBrightness(); break; case IR44_BPLUS : incBrightness(); break;
@ -525,7 +448,7 @@ void decodeIR44(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR21(uint32_t code) static void decodeIR21(uint32_t code)
{ {
switch (code) { switch (code) {
case IR21_BRIGHTER: incBrightness(); break; case IR21_BRIGHTER: incBrightness(); break;
@ -554,7 +477,7 @@ void decodeIR21(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR6(uint32_t code) static void decodeIR6(uint32_t code)
{ {
switch (code) { switch (code) {
case IR6_POWER: toggleOnOff(); break; case IR6_POWER: toggleOnOff(); break;
@ -587,7 +510,7 @@ void decodeIR6(uint32_t code)
lastValidCode = code; lastValidCode = code;
} }
void decodeIR9(uint32_t code) static void decodeIR9(uint32_t code)
{ {
switch (code) { switch (code) {
case IR9_POWER : toggleOnOff(); break; case IR9_POWER : toggleOnOff(); break;
@ -628,7 +551,7 @@ Sample:
"label": "Preset 1, fallback to Saw - Party if not found"}, "label": "Preset 1, fallback to Saw - Party if not found"},
} }
*/ */
void decodeIRJson(uint32_t code) static void decodeIRJson(uint32_t code)
{ {
char objKey[10]; char objKey[10];
char fileName[16]; char fileName[16];
@ -701,41 +624,102 @@ void decodeIRJson(uint32_t code)
releaseJSONBufferLock(); releaseJSONBufferLock();
} }
static void applyRepeatActions()
{
if (irEnabled == 8) {
decodeIRJson(lastValidCode);
return;
} else switch (lastRepeatableAction) {
case ACTION_BRIGHT_UP : incBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_BRIGHT_DOWN : decBrightness(); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_UP : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_SPEED_DOWN : changeEffectSpeed(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_UP : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
case ACTION_INTENSITY_DOWN : changeEffectIntensity(lastRepeatableValue); stateUpdated(CALL_MODE_BUTTON); return;
default: break;
}
if (lastValidCode == IR40_WPLUS) {
changeWhite(10);
stateUpdated(CALL_MODE_BUTTON);
} else if (lastValidCode == IR40_WMINUS) {
changeWhite(-10);
stateUpdated(CALL_MODE_BUTTON);
} else if ((lastValidCode == IR24_ON || lastValidCode == IR40_ON) && irTimesRepeated > 7 ) {
nightlightActive = true;
nightlightStartTime = millis();
stateUpdated(CALL_MODE_BUTTON);
}
}
static void decodeIR(uint32_t code)
{
if (code == 0xFFFFFFFF) {
//repeated code, continue brightness up/down
irTimesRepeated++;
applyRepeatActions();
return;
}
lastValidCode = 0; irTimesRepeated = 0;
lastRepeatableAction = ACTION_NONE;
if (irEnabled == 8) { // any remote configurable with ir.json file
decodeIRJson(code);
stateUpdated(CALL_MODE_BUTTON);
return;
}
if (code > 0xFFFFFF) return; //invalid code
switch (irEnabled) {
case 1:
if (code > 0xF80000) decodeIR24OLD(code); // white 24-key remote (old) - it sends 0xFF0000 values
else decodeIR24(code); // 24-key remote - 0xF70000 to 0xF80000
break;
case 2: decodeIR24CT(code); break; // white 24-key remote with CW, WW, CT+ and CT- keys
case 3: decodeIR40(code); break; // blue 40-key remote with 25%, 50%, 75% and 100% keys
case 4: decodeIR44(code); break; // white 44-key remote with color-up/down keys and DIY1 to 6 keys
case 5: decodeIR21(code); break; // white 21-key remote
case 6: decodeIR6(code); break; // black 6-key learning remote defaults: "CH" controls brightness,
// "VOL +" controls effect, "VOL -" controls colour/palette, "MUTE"
// sets bright plain white
case 7: decodeIR9(code); break;
//case 8: return; // ir.json file, handled above switch statement
}
if (nightlightActive && bri == 0) nightlightActive = false;
stateUpdated(CALL_MODE_BUTTON); //for notifier, IR is considered a button input
}
void initIR() void initIR()
{ {
if (irEnabled > 0) if (irEnabled > 0) {
{
irrecv = new IRrecv(irPin); irrecv = new IRrecv(irPin);
irrecv->enableIRIn(); if (irrecv) irrecv->enableIRIn();
} else irrecv = nullptr;
}
void deInitIR()
{
if (irrecv) {
irrecv->disableIRIn();
delete irrecv;
} }
irrecv = nullptr;
} }
void handleIR() void handleIR()
{ {
if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating()) unsigned long currentTime = millis();
{ unsigned timeDiff = currentTime - irCheckedTime;
irCheckedTime = millis(); if (timeDiff > 120 && irEnabled > 0 && irrecv) {
if (irEnabled > 0) if (strip.isUpdating() && timeDiff < 240) return; // be nice, but not too nice
{ irCheckedTime = currentTime;
if (irrecv == NULL) if (irrecv->decode(&results)) {
{ if (results.value != 0) { // only print results if anything is received ( != 0 )
initIR(); return; if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) // Serial TX pin (GPIO 1 on ESP32 and ESP8266)
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
} }
decodeIR(results.value);
if (irrecv->decode(&results)) irrecv->resume();
{
if (results.value != 0) // only print results if anything is received ( != 0 )
{
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) // Serial TX pin (GPIO 1 on ESP32 and ESP8266)
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
}
decodeIR(results.value);
irrecv->resume();
}
} else if (irrecv != NULL)
{
irrecv->disableIRIn();
delete irrecv; irrecv = NULL;
} }
} }
} }

View File

@ -492,6 +492,8 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
} }
} }
doAdvancePlaylist = root[F("np")] | doAdvancePlaylist; //advances to next preset in playlist when true
JsonObject wifi = root[F("wifi")]; JsonObject wifi = root[F("wifi")];
if (!wifi.isNull()) { if (!wifi.isNull()) {
bool apMode = getBoolVal(wifi[F("ap")], apActive); bool apMode = getBoolVal(wifi[F("ap")], apActive);

View File

@ -1,197 +1,194 @@
#include "wled.h" #include "wled.h"
/* /*
* MQTT communication protocol for home automation * MQTT communication protocol for home automation
*/ */
#ifdef WLED_ENABLE_MQTT #ifdef WLED_ENABLE_MQTT
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds #define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
void parseMQTTBriPayload(char* payload) void parseMQTTBriPayload(char* payload)
{ {
if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);} if (strstr(payload, "ON") || strstr(payload, "on") || strstr(payload, "true")) {bri = briLast; stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);} else if (strstr(payload, "T" ) || strstr(payload, "t" )) {toggleOnOff(); stateUpdated(CALL_MODE_DIRECT_CHANGE);}
else { else {
uint8_t in = strtoul(payload, NULL, 10); uint8_t in = strtoul(payload, NULL, 10);
if (in == 0 && bri > 0) briLast = bri; if (in == 0 && bri > 0) briLast = bri;
bri = in; bri = in;
stateUpdated(CALL_MODE_DIRECT_CHANGE); stateUpdated(CALL_MODE_DIRECT_CHANGE);
} }
} }
void onMqttConnect(bool sessionPresent) void onMqttConnect(bool sessionPresent)
{ {
//(re)subscribe to required topics //(re)subscribe to required topics
char subuf[38]; char subuf[38];
if (mqttDeviceTopic[0] != 0) { if (mqttDeviceTopic[0] != 0) {
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
strcat_P(subuf, PSTR("/col")); strcat_P(subuf, PSTR("/col"));
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
strlcpy(subuf, mqttDeviceTopic, 33); strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/api")); strcat_P(subuf, PSTR("/api"));
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
} }
if (mqttGroupTopic[0] != 0) { if (mqttGroupTopic[0] != 0) {
strlcpy(subuf, mqttGroupTopic, 33); strlcpy(subuf, mqttGroupTopic, 33);
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
strcat_P(subuf, PSTR("/col")); strcat_P(subuf, PSTR("/col"));
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
strlcpy(subuf, mqttGroupTopic, 33); strlcpy(subuf, mqttGroupTopic, 33);
strcat_P(subuf, PSTR("/api")); strcat_P(subuf, PSTR("/api"));
mqtt->subscribe(subuf, 0); mqtt->subscribe(subuf, 0);
} }
usermods.onMqttConnect(sessionPresent); usermods.onMqttConnect(sessionPresent);
DEBUG_PRINTLN(F("MQTT ready")); DEBUG_PRINTLN(F("MQTT ready"));
publishMqtt(); publishMqtt();
} }
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
static char *payloadStr; static char *payloadStr;
DEBUG_PRINT(F("MQTT msg: ")); DEBUG_PRINT(F("MQTT msg: "));
DEBUG_PRINTLN(topic); DEBUG_PRINTLN(topic);
// paranoia check to avoid npe if no payload // paranoia check to avoid npe if no payload
if (payload==nullptr) { if (payload==nullptr) {
DEBUG_PRINTLN(F("no payload -> leave")); DEBUG_PRINTLN(F("no payload -> leave"));
return; return;
} }
if (index == 0) { // start (1st partial packet or the only packet) if (index == 0) { // start (1st partial packet or the only packet)
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
payloadStr = new char[total+1]; // allocate new buffer payloadStr = new char[total+1]; // allocate new buffer
} }
if (payloadStr == nullptr) return; // buffer not allocated if (payloadStr == nullptr) return; // buffer not allocated
// copy (partial) packet to buffer and 0-terminate it if it is last packet // copy (partial) packet to buffer and 0-terminate it if it is last packet
char* buff = payloadStr + index; char* buff = payloadStr + index;
memcpy(buff, payload, len); memcpy(buff, payload, len);
if (index + len >= total) { // at end if (index + len >= total) { // at end
payloadStr[total] = '\0'; // terminate c style string payloadStr[total] = '\0'; // terminate c style string
} else { } else {
DEBUG_PRINTLN(F("MQTT partial packet received.")); DEBUG_PRINTLN(F("MQTT partial packet received."));
return; // process next packet return; // process next packet
} }
DEBUG_PRINTLN(payloadStr); DEBUG_PRINTLN(payloadStr);
size_t topicPrefixLen = strlen(mqttDeviceTopic); size_t topicPrefixLen = strlen(mqttDeviceTopic);
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) { if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
topic += topicPrefixLen; topic += topicPrefixLen;
} else { } else {
topicPrefixLen = strlen(mqttGroupTopic); topicPrefixLen = strlen(mqttGroupTopic);
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) { if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
topic += topicPrefixLen; topic += topicPrefixLen;
} else { } else {
// Non-Wled Topic used here. Probably a usermod subscribed to this topic. // Non-Wled Topic used here. Probably a usermod subscribed to this topic.
usermods.onMqttMessage(topic, payloadStr); usermods.onMqttMessage(topic, payloadStr);
delete[] payloadStr; delete[] payloadStr;
payloadStr = nullptr; payloadStr = nullptr;
return; return;
} }
} }
//Prefix is stripped from the topic at this point //Prefix is stripped from the topic at this point
if (strcmp_P(topic, PSTR("/col")) == 0) { if (strcmp_P(topic, PSTR("/col")) == 0) {
colorFromDecOrHexString(col, payloadStr); colorFromDecOrHexString(col, payloadStr);
colorUpdated(CALL_MODE_DIRECT_CHANGE); colorUpdated(CALL_MODE_DIRECT_CHANGE);
} else if (strcmp_P(topic, PSTR("/api")) == 0) { } else if (strcmp_P(topic, PSTR("/api")) == 0) {
if (!requestJSONBufferLock(15)) { if (requestJSONBufferLock(15)) {
delete[] payloadStr; if (payloadStr[0] == '{') { //JSON API
payloadStr = nullptr; deserializeJson(*pDoc, payloadStr);
return; deserializeState(pDoc->as<JsonObject>());
} } else { //HTTP API
if (payloadStr[0] == '{') { //JSON API String apireq = "win"; apireq += '&'; // reduce flash string usage
deserializeJson(*pDoc, payloadStr); apireq += payloadStr;
deserializeState(pDoc->as<JsonObject>()); handleSet(nullptr, apireq);
} else { //HTTP API }
String apireq = "win"; apireq += '&'; // reduce flash string usage releaseJSONBufferLock();
apireq += payloadStr; }
handleSet(nullptr, apireq); } else if (strlen(topic) != 0) {
} // non standard topic, check with usermods
releaseJSONBufferLock(); usermods.onMqttMessage(topic, payloadStr);
} else if (strlen(topic) != 0) { } else {
// non standard topic, check with usermods // topmost topic (just wled/MAC)
usermods.onMqttMessage(topic, payloadStr); parseMQTTBriPayload(payloadStr);
} else { }
// topmost topic (just wled/MAC) delete[] payloadStr;
parseMQTTBriPayload(payloadStr); payloadStr = nullptr;
} }
delete[] payloadStr;
payloadStr = nullptr;
} void publishMqtt()
{
if (!WLED_MQTT_CONNECTED) return;
void publishMqtt() DEBUG_PRINTLN(F("Publish MQTT"));
{
if (!WLED_MQTT_CONNECTED) return; #ifndef USERMOD_SMARTNEST
DEBUG_PRINTLN(F("Publish MQTT")); char s[10];
char subuf[48];
#ifndef USERMOD_SMARTNEST
char s[10]; sprintf_P(s, PSTR("%u"), bri);
char subuf[48]; strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/g"));
sprintf_P(s, PSTR("%u"), bri); mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/g")); sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263) strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c"));
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2])); mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/c")); strlcpy(subuf, mqttDeviceTopic, 33);
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263) strcat_P(subuf, PSTR("/status"));
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
strlcpy(subuf, mqttDeviceTopic, 33);
strcat_P(subuf, PSTR("/status")); char apires[1024]; // allocating 1024 bytes from stack can be risky
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT XML_response(nullptr, apires);
strlcpy(subuf, mqttDeviceTopic, 33);
char apires[1024]; // allocating 1024 bytes from stack can be risky strcat_P(subuf, PSTR("/v"));
XML_response(nullptr, apires); mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2263)
strlcpy(subuf, mqttDeviceTopic, 33); #endif
strcat_P(subuf, PSTR("/v")); }
mqtt->publish(subuf, 0, retainMqttMsg, apires); // optionally retain message (#2263)
#endif
} //HA autodiscovery was removed in favor of the native integration in HA v0.102.0
bool initMqtt()
//HA autodiscovery was removed in favor of the native integration in HA v0.102.0 {
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
bool initMqtt()
{ if (mqtt == nullptr) {
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false; mqtt = new AsyncMqttClient();
mqtt->onMessage(onMqttMessage);
if (mqtt == nullptr) { mqtt->onConnect(onMqttConnect);
mqtt = new AsyncMqttClient(); }
mqtt->onMessage(onMqttMessage); if (mqtt->connected()) return true;
mqtt->onConnect(onMqttConnect);
} DEBUG_PRINTLN(F("Reconnecting MQTT"));
if (mqtt->connected()) return true; IPAddress mqttIP;
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
DEBUG_PRINTLN(F("Reconnecting MQTT")); {
IPAddress mqttIP; mqtt->setServer(mqttIP, mqttPort);
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain } else {
{ mqtt->setServer(mqttServer, mqttPort);
mqtt->setServer(mqttIP, mqttPort); }
} else { mqtt->setClientId(mqttClientID);
mqtt->setServer(mqttServer, mqttPort); if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
}
mqtt->setClientId(mqttClientID); #ifndef USERMOD_SMARTNEST
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass); strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
strcat_P(mqttStatusTopic, PSTR("/status"));
#ifndef USERMOD_SMARTNEST mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33); #endif
strcat_P(mqttStatusTopic, PSTR("/status")); mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message mqtt->connect();
#endif return true;
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME); }
mqtt->connect(); #endif
return true;
}
#endif

View File

@ -2,20 +2,8 @@
#include "wled.h" #include "wled.h"
#include "fcn_declare.h" #include "fcn_declare.h"
// on esp8266, building with `-D WLED_USE_UNREAL_MATH` saves around 7Kb flash and 1KB RAM // WARNING: may cause errors in sunset calculations on ESP8266, see #3400
// warning: causes errors in sunset calculations, see #3400 // building with `-D WLED_USE_REAL_MATH` will prevent those errors at the expense of flash and RAM
#if defined(WLED_USE_UNREAL_MATH)
#define sinf sin_t
#define asinf asin_t
#define cosf cos_t
#define acosf acos_t
#define tanf tan_t
#define atanf atan_t
#define fmodf fmod_t
#define floorf floor_t
#else
#include <math.h>
#endif
/* /*
* Acquires time from NTP server * Acquires time from NTP server
@ -439,7 +427,7 @@ static int getSunriseUTC(int year, int month, int day, float lat, float lon, boo
//1. first calculate the day of the year //1. first calculate the day of the year
float N1 = 275 * month / 9; float N1 = 275 * month / 9;
float N2 = (month + 9) / 12; float N2 = (month + 9) / 12;
float N3 = (1.0f + floorf((year - 4 * floorf(year / 4) + 2.0f) / 3.0f)); float N3 = (1.0f + floor_t((year - 4 * floor_t(year / 4) + 2.0f) / 3.0f));
float N = N1 - (N2 * N3) + day - 30.0f; float N = N1 - (N2 * N3) + day - 30.0f;
//2. convert the longitude to hour value and calculate an approximate time //2. convert the longitude to hour value and calculate an approximate time
@ -450,37 +438,37 @@ static int getSunriseUTC(int year, int month, int day, float lat, float lon, boo
float M = (0.9856f * t) - 3.289f; float M = (0.9856f * t) - 3.289f;
//4. calculate the Sun's true longitude //4. calculate the Sun's true longitude
float L = fmodf(M + (1.916f * sinf(DEG_TO_RAD*M)) + (0.02f * sinf(2*DEG_TO_RAD*M)) + 282.634f, 360.0f); float L = fmod_t(M + (1.916f * sin_t(DEG_TO_RAD*M)) + (0.02f * sin_t(2*DEG_TO_RAD*M)) + 282.634f, 360.0f);
//5a. calculate the Sun's right ascension //5a. calculate the Sun's right ascension
float RA = fmodf(RAD_TO_DEG*atanf(0.91764f * tanf(DEG_TO_RAD*L)), 360.0f); float RA = fmod_t(RAD_TO_DEG*atan_t(0.91764f * tan_t(DEG_TO_RAD*L)), 360.0f);
//5b. right ascension value needs to be in the same quadrant as L //5b. right ascension value needs to be in the same quadrant as L
float Lquadrant = floorf( L/90) * 90; float Lquadrant = floor_t( L/90) * 90;
float RAquadrant = floorf(RA/90) * 90; float RAquadrant = floor_t(RA/90) * 90;
RA = RA + (Lquadrant - RAquadrant); RA = RA + (Lquadrant - RAquadrant);
//5c. right ascension value needs to be converted into hours //5c. right ascension value needs to be converted into hours
RA /= 15.0f; RA /= 15.0f;
//6. calculate the Sun's declination //6. calculate the Sun's declination
float sinDec = 0.39782f * sinf(DEG_TO_RAD*L); float sinDec = 0.39782f * sin_t(DEG_TO_RAD*L);
float cosDec = cosf(asinf(sinDec)); float cosDec = cos_t(asin_t(sinDec));
//7a. calculate the Sun's local hour angle //7a. calculate the Sun's local hour angle
float cosH = (sinf(DEG_TO_RAD*ZENITH) - (sinDec * sinf(DEG_TO_RAD*lat))) / (cosDec * cosf(DEG_TO_RAD*lat)); float cosH = (sin_t(DEG_TO_RAD*ZENITH) - (sinDec * sin_t(DEG_TO_RAD*lat))) / (cosDec * cos_t(DEG_TO_RAD*lat));
if ((cosH > 1.0f) && !sunset) return INT16_MAX; // the sun never rises on this location (on the specified date) if ((cosH > 1.0f) && !sunset) return INT16_MAX; // the sun never rises on this location (on the specified date)
if ((cosH < -1.0f) && sunset) return INT16_MAX; // the sun never sets on this location (on the specified date) if ((cosH < -1.0f) && sunset) return INT16_MAX; // the sun never sets on this location (on the specified date)
//7b. finish calculating H and convert into hours //7b. finish calculating H and convert into hours
float H = sunset ? RAD_TO_DEG*acosf(cosH) : 360 - RAD_TO_DEG*acosf(cosH); float H = sunset ? RAD_TO_DEG*acos_t(cosH) : 360 - RAD_TO_DEG*acos_t(cosH);
H /= 15.0f; H /= 15.0f;
//8. calculate local mean time of rising/setting //8. calculate local mean time of rising/setting
float T = H + RA - (0.06571f * t) - 6.622f; float T = H + RA - (0.06571f * t) - 6.622f;
//9. adjust back to UTC //9. adjust back to UTC
float UT = fmodf(T - lngHour, 24.0f); float UT = fmod_t(T - lngHour, 24.0f);
// return in minutes from midnight // return in minutes from midnight
return UT*60; return UT*60;

View File

@ -25,11 +25,11 @@ void _overlayAnalogClock()
{ {
if (secondPixel < analogClock12pixel) if (secondPixel < analogClock12pixel)
{ {
strip.setRange(analogClock12pixel, overlayMax, 0xFF0000); strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri));
strip.setRange(overlayMin, secondPixel, 0xFF0000); strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri));
} else } else
{ {
strip.setRange(analogClock12pixel, secondPixel, 0xFF0000); strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri));
} }
} }
if (analogClock5MinuteMarks) if (analogClock5MinuteMarks)
@ -38,12 +38,12 @@ void _overlayAnalogClock()
{ {
unsigned pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i); unsigned pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
if (pix > overlayMax) pix -= overlaySize; if (pix > overlayMax) pix -= overlaySize;
strip.setPixelColor(pix, 0x00FFAA); strip.setPixelColor(pix, color_fade(0x00FFAA, bri));
} }
} }
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000); if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri));
strip.setPixelColor(minutePixel, 0x00FF00); strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri));
strip.setPixelColor(hourPixel, 0x0000FF); strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri));
} }

View File

@ -127,7 +127,7 @@ void handlePlaylist() {
static unsigned long presetCycledTime = 0; static unsigned long presetCycledTime = 0;
if (currentPlaylist < 0 || playlistEntries == nullptr) return; if (currentPlaylist < 0 || playlistEntries == nullptr) return;
if (millis() - presetCycledTime > (100*playlistEntryDur)) { if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) {
presetCycledTime = millis(); presetCycledTime = millis();
if (bri == 0 || nightlightActive) return; if (bri == 0 || nightlightActive) return;
@ -149,6 +149,7 @@ void handlePlaylist() {
strip.setTransition(playlistEntries[playlistIndex].tr * 100); strip.setTransition(playlistEntries[playlistIndex].tr * 100);
playlistEntryDur = playlistEntries[playlistIndex].dur; playlistEntryDur = playlistEntries[playlistIndex].dur;
applyPresetFromPlaylist(playlistEntries[playlistIndex].preset); applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
doAdvancePlaylist = false;
} }
} }

View File

@ -104,7 +104,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} }
#ifndef WLED_DISABLE_INFRARED #ifndef WLED_DISABLE_INFRARED
if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) { if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) {
pinManager.deallocatePin(irPin, PinOwner::IR); deInitIR();
pinManager.deallocatePin(irPin, PinOwner::IR);
} }
#endif #endif
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) { for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) {
@ -233,6 +234,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
irPin = -1; irPin = -1;
} }
irEnabled = request->arg(F("IT")).toInt(); irEnabled = request->arg(F("IT")).toInt();
initIR();
#endif #endif
irApplyToAllSelected = !request->hasArg(F("MSO")); irApplyToAllSelected = !request->hasArg(F("MSO"));
@ -898,6 +900,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
applyPreset(presetCycCurr); applyPreset(presetCycCurr);
} }
pos = req.indexOf(F("NP")); //advances to next preset in a playlist
if (pos > 0) doAdvancePlaylist = true;
//set brightness //set brightness
updateVal(req.c_str(), "&A=", &bri); updateVal(req.c_str(), "&A=", &bri);

View File

@ -505,6 +505,13 @@ void WLED::setup()
initServer(); initServer();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap()); DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#ifndef WLED_DISABLE_INFRARED
// init IR
DEBUG_PRINTLN(F("initIR"));
initIR();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
#endif
// Seed FastLED random functions with an esp random value, which already works properly at this point. // Seed FastLED random functions with an esp random value, which already works properly at this point.
#if defined(ARDUINO_ARCH_ESP32) #if defined(ARDUINO_ARCH_ESP32)
const uint32_t seed32 = esp_random(); const uint32_t seed32 = esp_random();

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2404120 #define VERSION 2405030
//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
@ -644,6 +644,7 @@ WLED_GLOBAL byte timerWeekday[] _INIT_N(({ 255, 255, 255, 255, 255, 255, 255,
WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28})); WLED_GLOBAL byte timerMonth[] _INIT_N(({28,28,28,28,28,28,28,28}));
WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1})); WLED_GLOBAL byte timerDay[] _INIT_N(({1,1,1,1,1,1,1,1}));
WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31})); WLED_GLOBAL byte timerDayEnd[] _INIT_N(({31,31,31,31,31,31,31,31}));
WLED_GLOBAL bool doAdvancePlaylist _INIT(false);
//improv //improv
WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: improv active, 2: provisioning WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: improv active, 2: provisioning

View File

@ -206,9 +206,9 @@ bool sendLiveLedsWs(uint32_t wsClient)
uint8_t g = G(c); uint8_t g = G(c);
uint8_t b = B(c); uint8_t b = B(c);
uint8_t w = W(c); uint8_t w = W(c);
buffer[pos++] = scale8(qadd8(w, r), strip.getBrightness()); //R, add white channel to RGB channels as a simple RGBW -> RGB map buffer[pos++] = bri ? qadd8(w, r) : 0; //R, add white channel to RGB channels as a simple RGBW -> RGB map
buffer[pos++] = scale8(qadd8(w, g), strip.getBrightness()); //G buffer[pos++] = bri ? qadd8(w, g) : 0; //G
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B buffer[pos++] = bri ? qadd8(w, b) : 0; //B
} }
wsc->binary(std::move(wsBuf)); wsc->binary(std::move(wsBuf));