mirror of
https://github.com/wled/WLED.git
synced 2025-04-23 22:37:18 +00:00
Merge branch '0_15' into blending-styles
This commit is contained in:
commit
f441ce9c17
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,5 +1,15 @@
|
||||
## 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
|
||||
- v0.15.0-b3
|
||||
- fix for #3896 & WS2815 current saving
|
||||
|
@ -1,9 +1,24 @@
|
||||
# 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')
|
||||
|
||||
def obj_dump_after_elf(source, target, env):
|
||||
platform = env.PioPlatform()
|
||||
board = env.BoardConfig()
|
||||
mcu = board.get("build.mcu", "esp32")
|
||||
|
||||
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])
|
||||
|
@ -36,6 +36,8 @@ def create_release(source):
|
||||
def bin_rename_copy(source, target, env):
|
||||
_create_dirs()
|
||||
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
|
||||
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
|
||||
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):
|
||||
# only create gzip for esp8266
|
||||
|
@ -115,6 +115,7 @@ extra_scripts =
|
||||
post:pio-scripts/strip-floats.py
|
||||
pre:pio-scripts/user_config_copy.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:
|
||||
@ -338,14 +339,14 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
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}
|
||||
|
||||
[env:esp01_1m_full_160]
|
||||
extends = env:esp01_1m_full
|
||||
board_build.f_cpu = 160000000L
|
||||
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]
|
||||
board = esp32dev
|
||||
@ -471,8 +472,7 @@ monitor_filters = esp32_exception_decoder
|
||||
|
||||
[env:esp32s3_4M_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_build.arduino.memory_type = qio_qspi ;; use with PSRAM: 2MB or 4MB
|
||||
board = lolin_s3_mini ;; -S3 mini, 4MB flash 2MB PSRAM
|
||||
platform = ${esp32s3.platform}
|
||||
platform_packages = ${esp32s3.platform_packages}
|
||||
upload_speed = 921600
|
||||
|
@ -368,9 +368,9 @@ public:
|
||||
|
||||
JsonArray temperature_json = user.createNestedArray(F("Temperature"));
|
||||
JsonArray pressure_json = user.createNestedArray(F("Pressure"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
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"));
|
||||
}
|
||||
else if (sensorType==1) //BME280
|
||||
@ -382,9 +382,9 @@ public:
|
||||
JsonArray dewpoint_json = user.createNestedArray(F("Dew Point"));
|
||||
temperature_json.add(roundf(sensorTemperature * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
temperature_json.add(tempScale);
|
||||
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)));
|
||||
humidity_json.add(roundf(sensorHumidity * powf(10, HumidityDecimals)) / powf(10, HumidityDecimals));
|
||||
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"));
|
||||
heatindex_json.add(roundf(sensorHeatIndex * powf(10, TemperatureDecimals)) / powf(10, TemperatureDecimals));
|
||||
heatindex_json.add(tempScale);
|
||||
|
160
usermods/Battery/UMBattery.h
Normal file
160
usermods/Battery/UMBattery.h
Normal 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
|
@ -1,3 +1,8 @@
|
||||
#ifndef UMBDefaults_h
|
||||
#define UMBDefaults_h
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
// pin defaults
|
||||
// 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
|
||||
@ -9,24 +14,66 @@
|
||||
#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
|
||||
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
|
||||
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 30000
|
||||
#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
|
||||
// Discharge voltage: 2.5 volt + .1 for personal safety
|
||||
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
|
||||
#ifdef USERMOD_BATTERY_USE_LIPO
|
||||
// LiPo "1S" Batteries should not be dischared below 3V !!
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 3.2f
|
||||
#else
|
||||
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
|
||||
#endif
|
||||
|
||||
/* Default Battery Type
|
||||
* 0 = unkown
|
||||
* 1 = Lipo
|
||||
* 2 = Lion
|
||||
*/
|
||||
#ifndef USERMOD_BATTERY_DEFAULT_TYPE
|
||||
#define USERMOD_BATTERY_DEFAULT_TYPE 0
|
||||
#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
|
||||
|
||||
//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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define USERMOD_BATTERY_VOLTAGE_MULTIPLIER 2.0f
|
||||
@ -35,13 +82,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
|
||||
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
|
||||
#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
|
||||
#ifndef USERMOD_BATTERY_AVERAGING_ALPHA
|
||||
#define USERMOD_BATTERY_AVERAGING_ALPHA 0.1f
|
||||
#endif
|
||||
|
||||
// offset or calibration value to fine tune the calculated voltage
|
||||
@ -49,11 +91,6 @@
|
||||
#define USERMOD_BATTERY_CALIBRATION 0
|
||||
#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
|
||||
#ifndef USERMOD_BATTERY_AUTO_OFF_ENABLED
|
||||
#define USERMOD_BATTERY_AUTO_OFF_ENABLED true
|
||||
@ -78,4 +115,26 @@
|
||||
|
||||
#ifndef USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION
|
||||
#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
|
@ -36,13 +36,13 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
|
||||
| Name | Unit | Description |
|
||||
| ----------------------------------------------- | ----------- |-------------------------------------------------------------------------------------- |
|
||||
| `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_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_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_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}_MIN_VOLTAGE` | v | minimum battery voltage. default is 2.6 (18650 battery standard) |
|
||||
| `USERMOD_BATTERY_{TYPE}_MAX_VOLTAGE` | v | maximum battery voltage. default is 4.2 (18650 battery standard) |
|
||||
| `USERMOD_BATTERY_{TYPE}_TOTAL_CAPACITY` | mAh | the capacity of all cells in parallel summed up |
|
||||
| `USERMOD_BATTERY_{TYPE}_CALIBRATION` | | offset / calibration number, fine tune the measured voltage by the microcontroller |
|
||||
| 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 |
|
||||
@ -54,6 +54,13 @@ define `USERMOD_BATTERY` in `wled00/my_config.h`
|
||||
|
||||
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
|
||||
|
||||
- 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
|
||||
|
||||
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
|
||||
|
||||
- basic support for LiPo rechargeable batteries ( `-D USERMOD_BATTERY_USE_LIPO`)
|
||||
|
38
usermods/Battery/types/LionUMBattery.h
Normal file
38
usermods/Battery/types/LionUMBattery.h
Normal 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
|
54
usermods/Battery/types/LipoUMBattery.h
Normal file
54
usermods/Battery/types/LipoUMBattery.h
Normal 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
|
39
usermods/Battery/types/UnkownUMBattery.h
Normal file
39
usermods/Battery/types/UnkownUMBattery.h
Normal 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
|
@ -2,12 +2,15 @@
|
||||
|
||||
#include "wled.h"
|
||||
#include "battery_defaults.h"
|
||||
#include "UMBattery.h"
|
||||
#include "types/UnkownUMBattery.h"
|
||||
#include "types/LionUMBattery.h"
|
||||
#include "types/LiPoUMBattery.h"
|
||||
|
||||
/*
|
||||
* Usermod by Maximilian Mewes
|
||||
* Mail: mewes.maximilian@gmx.de
|
||||
* GitHub: itCarl
|
||||
* Date: 25.12.2022
|
||||
* E-mail: mewes.maximilian@gmx.de
|
||||
* Created at: 25.12.2022
|
||||
* If you have any questions, please feel free to contact me.
|
||||
*/
|
||||
class UsermodBattery : public Usermod
|
||||
@ -15,47 +18,36 @@ class UsermodBattery : public Usermod
|
||||
private:
|
||||
// battery pin can be defined in my_config.h
|
||||
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
|
||||
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
|
||||
unsigned long nextReadTime = 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
|
||||
float alpha = 0.05f;
|
||||
// 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;
|
||||
float alpha = USERMOD_BATTERY_AVERAGING_ALPHA;
|
||||
|
||||
// auto shutdown/shutoff/master off feature
|
||||
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
|
||||
bool lowPowerIndicatorEnabled = USERMOD_BATTERY_LOW_POWER_INDICATOR_ENABLED;
|
||||
int8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
|
||||
int8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
|
||||
int8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
int8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
|
||||
uint8_t lowPowerIndicatorPreset = USERMOD_BATTERY_LOW_POWER_INDICATOR_PRESET;
|
||||
uint8_t lowPowerIndicatorThreshold = USERMOD_BATTERY_LOW_POWER_INDICATOR_THRESHOLD;
|
||||
uint8_t lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
uint8_t lowPowerIndicatorDuration = USERMOD_BATTERY_LOW_POWER_INDICATOR_DURATION;
|
||||
bool lowPowerIndicationDone = false;
|
||||
unsigned long lowPowerActivationTime = 0; // used temporary during active time
|
||||
int8_t lastPreset = 0;
|
||||
uint8_t lastPreset = 0;
|
||||
|
||||
//
|
||||
bool initDone = false;
|
||||
bool initializing = true;
|
||||
|
||||
@ -67,22 +59,17 @@ class UsermodBattery : public Usermod
|
||||
static const char _preset[];
|
||||
static const char _duration[];
|
||||
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 nx = (int)(x * 100 + .5);
|
||||
return (float)(nx / 100);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Turn off all leds
|
||||
*/
|
||||
void turnOff()
|
||||
@ -91,15 +78,15 @@ class UsermodBattery : public Usermod
|
||||
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
|
||||
*/
|
||||
void lowPowerIndicator()
|
||||
{
|
||||
if (!lowPowerIndicatorEnabled) return;
|
||||
if (batteryPin < 0) return; // no measurement
|
||||
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= batteryLevel) lowPowerIndicationDone = false;
|
||||
if (lowPowerIndicatorThreshold <= batteryLevel) return;
|
||||
if (lowPowerIndicationDone && lowPowerIndicatorReactivationThreshold <= bat->getLevel()) lowPowerIndicationDone = false;
|
||||
if (lowPowerIndicatorThreshold <= bat->getLevel()) return;
|
||||
if (lowPowerIndicationDone) return;
|
||||
if (lowPowerActivationTime <= 1) {
|
||||
lowPowerActivationTime = millis();
|
||||
@ -114,26 +101,39 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read the battery voltage in different ways depending on the architecture
|
||||
*/
|
||||
float readVoltage()
|
||||
{
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// use calibrated millivolts analogread on esp32 (150 mV ~ 2450 mV default attentuation) and divide by 1000 to get from milivolts to volts and multiply by voltage multiplier and apply calibration value
|
||||
return (analogReadMilliVolts(batteryPin) / 1000.0f) * voltageMultiplier + calibration;
|
||||
return (analogReadMilliVolts(batteryPin) / 1000.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
|
||||
#else
|
||||
// use analog read on esp8266 ( 0V ~ 1V no attenuation options) and divide by ADC precision 1023 and multiply by voltage multiplier and apply calibration value
|
||||
return (analogRead(batteryPin) / 1023.0f) * voltageMultiplier + calibration;
|
||||
return (analogRead(batteryPin) / 1023.0f) * bat->getVoltageMultiplier() + bat->getCalibration();
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
//Functions called by WLED
|
||||
|
||||
/*
|
||||
/**
|
||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||
* You can use it to initialize variables, sensors or similar.
|
||||
*/
|
||||
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
|
||||
bool success = false;
|
||||
DEBUG_PRINTLN(F("Allocating battery pin..."));
|
||||
@ -141,7 +141,6 @@ class UsermodBattery : public Usermod
|
||||
if (pinManager.allocatePin(batteryPin, false, PinOwner::UM_Battery)) {
|
||||
DEBUG_PRINTLN(F("Battery pin allocation succeeded."));
|
||||
success = true;
|
||||
voltage = readVoltage();
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
@ -152,17 +151,17 @@ class UsermodBattery : public Usermod
|
||||
}
|
||||
#else //ESP8266 boards have only one analog input pin A0
|
||||
pinMode(batteryPin, INPUT);
|
||||
voltage = readVoltage();
|
||||
#endif
|
||||
|
||||
nextReadTime = millis() + readingInterval;
|
||||
// First voltage reading is delayed to allow voltage stabilization after powering up
|
||||
nextReadTime = millis() + initialDelay;
|
||||
lastReadTime = millis();
|
||||
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* connected() is called every time the WiFi is (re)connected
|
||||
* Use it to initialize network interfaces
|
||||
*/
|
||||
@ -182,6 +181,25 @@ class UsermodBattery : public Usermod
|
||||
|
||||
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)
|
||||
if (millis() < nextReadTime) return;
|
||||
|
||||
@ -191,43 +209,17 @@ class UsermodBattery : public Usermod
|
||||
if (batteryPin < 0) return; // nothing to read
|
||||
|
||||
initializing = false;
|
||||
float rawValue = readVoltage();
|
||||
|
||||
rawValue = readVoltage();
|
||||
// filter with exponential smoothing because ADC in esp32 is fluctuating too much for a good single readout
|
||||
voltage = voltage + alpha * (rawValue - voltage);
|
||||
|
||||
// check if voltage is within specified voltage range, allow 10% over/under voltage - removed cause this just makes it hard for people to troubleshoot as the voltage in the web gui will say invalid instead of displaying a voltage
|
||||
//voltage = ((voltage < minBatteryVoltage * 0.85f) || (voltage > maxBatteryVoltage * 1.1f)) ? -1.0f : voltage;
|
||||
float filteredVoltage = bat->getVoltage() + alpha * (rawValue - bat->getVoltage());
|
||||
|
||||
bat->setVoltage(filteredVoltage);
|
||||
// translate battery voltage into percentage
|
||||
/*
|
||||
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;
|
||||
// }
|
||||
bat->calculateAndSetLevel(filteredVoltage);
|
||||
|
||||
// Auto off -- Master power off
|
||||
if (autoOffEnabled && (autoOffThreshold >= batteryLevel))
|
||||
if (autoOffEnabled && (autoOffThreshold >= bat->getLevel()))
|
||||
turnOff();
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@ -236,13 +228,13 @@ class UsermodBattery : public Usermod
|
||||
if (WLED_MQTT_CONNECTED) {
|
||||
char buf[64]; // buffer for snprintf()
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
|
||||
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
|
||||
* Below it is shown how this could be used for e.g. a light sensor
|
||||
@ -262,16 +254,6 @@ class UsermodBattery : public Usermod
|
||||
// info modal display names
|
||||
JsonArray infoPercentage = user.createNestedArray(F("Battery level"));
|
||||
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"));
|
||||
|
||||
infoNextUpdate.add((nextReadTime - millis()) / 1000);
|
||||
@ -283,46 +265,104 @@ class UsermodBattery : public Usermod
|
||||
return;
|
||||
}
|
||||
|
||||
if (batteryLevel < 0) {
|
||||
if (bat->getLevel() < 0) {
|
||||
infoPercentage.add(F("invalid"));
|
||||
} else {
|
||||
infoPercentage.add(batteryLevel);
|
||||
infoPercentage.add(bat->getLevel());
|
||||
}
|
||||
infoPercentage.add(F(" %"));
|
||||
|
||||
if (voltage < 0) {
|
||||
if (bat->getVoltage() < 0) {
|
||||
infoVoltage.add(F("invalid"));
|
||||
} else {
|
||||
infoVoltage.add(dot2round(voltage));
|
||||
infoVoltage.add(dot2round(bat->getVoltage()));
|
||||
}
|
||||
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).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
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).
|
||||
* Values in the state object may be modified by connected clients
|
||||
*/
|
||||
/*
|
||||
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.
|
||||
* 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().
|
||||
@ -359,47 +399,41 @@ class UsermodBattery : public Usermod
|
||||
*/
|
||||
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
|
||||
battery[F("pin")] = batteryPin;
|
||||
#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
|
||||
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;
|
||||
addBatteryToJsonObject(battery, false);
|
||||
|
||||
// read voltage in case calibration or voltage multiplier changed to see immediate effect
|
||||
voltage = readVoltage();
|
||||
bat->setVoltage(readVoltage());
|
||||
|
||||
DEBUG_PRINTLN(F("Battery config saved."));
|
||||
}
|
||||
|
||||
void appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('Battery:min-voltage', 1, 'v');"));
|
||||
oappend(SET_F("addInfo('Battery:max-voltage', 1, 'v');"));
|
||||
oappend(SET_F("addInfo('Battery:capacity', 1, 'mAh');"));
|
||||
oappend(SET_F("addInfo('Battery:interval', 1, 'ms');"));
|
||||
oappend(SET_F("addInfo('Battery:auto-off:threshold', 1, '%');"));
|
||||
oappend(SET_F("addInfo('Battery:indicator:threshold', 1, '%');"));
|
||||
oappend(SET_F("addInfo('Battery:indicator:duration', 1, 's');"));
|
||||
// Total: 462 Bytes
|
||||
oappend(SET_F("td=addDropdown('Battery', 'type');")); // 35 Bytes
|
||||
oappend(SET_F("addOption(td, 'Unkown', '0');")); // 30 Bytes
|
||||
oappend(SET_F("addOption(td, 'LiPo', '1');")); // 28 Bytes
|
||||
oappend(SET_F("addOption(td, 'LiOn', '2');")); // 28 Bytes
|
||||
oappend(SET_F("addInfo('Battery:type',1,'<small style=\"color:orange\">requires reboot</small>');")); // 81 Bytes
|
||||
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
|
||||
// what i wanted is a list of all presets to select one from
|
||||
// this option list would exeed the oappend() buffer
|
||||
// a list of all presets to select one from
|
||||
// oappend(SET_F("bd=addDropdown('Battery:low-power-indicator', 'preset');"));
|
||||
// the loop generates: oappend(SET_F("addOption(bd, 'preset name', preset id);"));
|
||||
// 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().
|
||||
* 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;
|
||||
#endif
|
||||
// calculateTimeLeftEnabled = battery[F("time-left")] | calculateTimeLeftEnabled;
|
||||
setMinBatteryVoltage(battery[F("min-voltage")] | minBatteryVoltage);
|
||||
setMaxBatteryVoltage(battery[F("max-voltage")] | maxBatteryVoltage);
|
||||
setTotalBatteryCapacity(battery[F("capacity")] | totalBatteryCapacity);
|
||||
setCalibration(battery[F("calibration")] | calibration);
|
||||
setVoltageMultiplier(battery[F("voltage-multiplier")] | voltageMultiplier);
|
||||
setMinBatteryVoltage(battery[F("min-voltage")] | bat->getMinVoltage());
|
||||
setMaxBatteryVoltage(battery[F("max-voltage")] | bat->getMaxVoltage());
|
||||
setCalibration(battery[F("calibration")] | bat->getCalibration());
|
||||
setVoltageMultiplier(battery[F("voltage-multiplier")] | bat->getVoltageMultiplier());
|
||||
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); // dropdown trickery (int)lp["preset"]
|
||||
setLowPowerIndicatorThreshold(lp[FPSTR(_threshold)] | lowPowerIndicatorThreshold);
|
||||
lowPowerIndicatorReactivationThreshold = lowPowerIndicatorThreshold+10;
|
||||
setLowPowerIndicatorDuration(lp[FPSTR(_duration)] | lowPowerIndicatorDuration);
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
getUsermodConfigFromJsonObject(battery);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (!initDone)
|
||||
@ -491,8 +513,9 @@ class UsermodBattery : public Usermod
|
||||
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()
|
||||
{
|
||||
@ -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!).
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* get currently active battery type
|
||||
*/
|
||||
batteryType getBatteryType()
|
||||
{
|
||||
return cfg.type;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
unsigned long getReadingInterval()
|
||||
{
|
||||
return readingInterval;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* minimum repetition is 3000ms (3s)
|
||||
*/
|
||||
void setReadingInterval(unsigned long newReadingInterval)
|
||||
@ -552,105 +585,84 @@ class UsermodBattery : public Usermod
|
||||
readingInterval = max((unsigned long)3000, newReadingInterval);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get lowest configured battery voltage
|
||||
*/
|
||||
float getMinBatteryVoltage()
|
||||
{
|
||||
return minBatteryVoltage;
|
||||
return bat->getMinVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set lowest battery voltage
|
||||
* can't be below 0 volt
|
||||
*/
|
||||
void setMinBatteryVoltage(float voltage)
|
||||
{
|
||||
minBatteryVoltage = max(0.0f, voltage);
|
||||
bat->setMinVoltage(voltage);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get highest configured battery voltage
|
||||
*/
|
||||
float getMaxBatteryVoltage()
|
||||
{
|
||||
return maxBatteryVoltage;
|
||||
return bat->getMaxVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set highest battery voltage
|
||||
* can't be below minBatteryVoltage
|
||||
*/
|
||||
void setMaxBatteryVoltage(float voltage)
|
||||
{
|
||||
#ifdef USERMOD_BATTERY_USE_LIPO
|
||||
maxBatteryVoltage = max(getMinBatteryVoltage()+0.7f, voltage);
|
||||
#else
|
||||
maxBatteryVoltage = max(getMinBatteryVoltage()+1.0f, voltage);
|
||||
#endif
|
||||
bat->setMaxVoltage(voltage);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
* formula: (adc pin value / adc precision * max voltage) + calibration
|
||||
*/
|
||||
float getVoltage()
|
||||
{
|
||||
return voltage;
|
||||
return bat->getVoltage();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get the mapped battery level (0 - 100) based on voltage
|
||||
* important: voltage can drop when a load is applied, so its only an estimate
|
||||
*/
|
||||
int8_t getBatteryLevel()
|
||||
{
|
||||
return batteryLevel;
|
||||
return bat->getLevel();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get the configured calibration value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
float getCalibration()
|
||||
{
|
||||
return calibration;
|
||||
return bat->getCalibration();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set the voltage calibration offset value
|
||||
* a offset value to fine-tune the calculated voltage.
|
||||
*/
|
||||
void setCalibration(float offset)
|
||||
{
|
||||
calibration = offset;
|
||||
bat->setCalibration(offset);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set the voltage multiplier value
|
||||
* A multiplier that may need adjusting for different voltage divider setups
|
||||
*/
|
||||
void setVoltageMultiplier(float multiplier)
|
||||
{
|
||||
voltageMultiplier = multiplier;
|
||||
bat->setVoltageMultiplier(multiplier);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -659,10 +671,10 @@ class UsermodBattery : public Usermod
|
||||
*/
|
||||
float getVoltageMultiplier()
|
||||
{
|
||||
return voltageMultiplier;
|
||||
return bat->getVoltageMultiplier();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get auto-off feature enabled status
|
||||
* is auto-off enabled, true/false
|
||||
*/
|
||||
@ -671,7 +683,7 @@ class UsermodBattery : public Usermod
|
||||
return autoOffEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set auto-off feature status
|
||||
*/
|
||||
void setAutoOffEnabled(bool enabled)
|
||||
@ -679,7 +691,7 @@ class UsermodBattery : public Usermod
|
||||
autoOffEnabled = enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get auto-off threshold in percent (0-100)
|
||||
*/
|
||||
int8_t getAutoOffThreshold()
|
||||
@ -687,7 +699,7 @@ class UsermodBattery : public Usermod
|
||||
return autoOffThreshold;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set auto-off threshold in percent (0-100)
|
||||
*/
|
||||
void setAutoOffThreshold(int8_t threshold)
|
||||
@ -697,8 +709,7 @@ class UsermodBattery : public Usermod
|
||||
autoOffThreshold = lowPowerIndicatorEnabled /*&& autoOffEnabled*/ ? min(lowPowerIndicatorThreshold-1, (int)autoOffThreshold) : autoOffThreshold;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator feature enabled status
|
||||
* is the low-power-indicator enabled, true/false
|
||||
*/
|
||||
@ -707,7 +718,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorEnabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator feature status
|
||||
*/
|
||||
void setLowPowerIndicatorEnabled(bool enabled)
|
||||
@ -715,7 +726,7 @@ class UsermodBattery : public Usermod
|
||||
lowPowerIndicatorEnabled = enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator preset to activate when low power is detected
|
||||
*/
|
||||
int8_t getLowPowerIndicatorPreset()
|
||||
@ -723,7 +734,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorPreset;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator preset to activate when low power is detected
|
||||
*/
|
||||
void setLowPowerIndicatorPreset(int8_t presetId)
|
||||
@ -741,7 +752,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorThreshold;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator threshold in percent (0-100)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get low-power-indicator duration in seconds
|
||||
*/
|
||||
int8_t getLowPowerIndicatorDuration()
|
||||
@ -759,7 +770,7 @@ class UsermodBattery : public Usermod
|
||||
return lowPowerIndicatorDuration;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set low-power-indicator duration in seconds
|
||||
*/
|
||||
void setLowPowerIndicatorDuration(int8_t duration)
|
||||
@ -767,9 +778,8 @@ class UsermodBattery : public Usermod
|
||||
lowPowerIndicatorDuration = duration;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get low-power-indicator status when the indication is done thsi returns true
|
||||
/**
|
||||
* Get low-power-indicator status when the indication is done this returns true
|
||||
*/
|
||||
bool getLowPowerIndicatorDone()
|
||||
{
|
||||
|
@ -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.
|
||||
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 windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
|
||||
// Create FFT object
|
||||
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
|
||||
@ -196,7 +195,8 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
|
||||
#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
|
||||
|
||||
@ -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.compute( FFTDirection::Forward ); // Compute FFT
|
||||
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 = 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.
|
||||
|
||||
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) {
|
||||
#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
|
||||
|
@ -2506,7 +2506,7 @@ uint16_t ripple_base()
|
||||
uint16_t cx = rippleorigin >> 8;
|
||||
uint16_t cy = rippleorigin & 0xFF;
|
||||
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
|
||||
#endif
|
||||
{
|
||||
@ -6056,7 +6056,7 @@ uint16_t mode_2Dfloatingblobs(void) {
|
||||
}
|
||||
}
|
||||
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);
|
||||
// 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));
|
||||
@ -6643,7 +6643,6 @@ static const char _data_FX_MODE_GRAVIMETER[] PROGMEM = "Gravimeter@Rate of fall,
|
||||
// * JUGGLES //
|
||||
//////////////////////
|
||||
uint16_t mode_juggles(void) { // Juggles. By Andrew Tuline.
|
||||
if (SEGLEN == 1) return mode_static();
|
||||
um_data_t *um_data;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// 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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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;
|
||||
} // 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;
|
||||
} // 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 //
|
||||
/////////////////////////
|
||||
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;
|
||||
|
||||
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
|
||||
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 = 0; i < mid; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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 //
|
||||
///////////////////////
|
||||
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;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7140,12 +7141,13 @@ uint16_t mode_freqmatrix(void) { // Freqmatrix. By Andreas Plesch
|
||||
|
||||
// shift the pixels one pixel up
|
||||
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
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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.
|
||||
// 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.
|
||||
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;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7246,13 +7248,14 @@ uint16_t mode_freqwave(void) { // Freqwave. By Andreas Pleschun
|
||||
SEGMENT.setPixelColor(SEGLEN/2, color);
|
||||
|
||||
// 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 = 0; i < SEGLEN/2; i++) SEGMENT.setPixelColor(i, SEGMENT.getPixelColor(i+1)); // move to the right
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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 //
|
||||
//////////////////////
|
||||
uint16_t mode_noisemove(void) { // Noisemove. By: Andrew Tuline
|
||||
if (SEGLEN == 1) return mode_static();
|
||||
um_data_t *um_data;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// 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.
|
||||
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.
|
||||
// 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.
|
||||
SEGMENT.setPixelColor(locn, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette(i*64, false, PALETTE_SOLID_WRAP, 0), fftResult[i % 16]*4));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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 //
|
||||
//////////////////////
|
||||
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;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// 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;
|
||||
|
||||
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);
|
||||
SEGMENT.addPixelColor(i, color_blend(SEGCOLOR(1), SEGMENT.color_from_palette((uint8_t)frTemp, false, PALETTE_SOLID_WRAP, 0), volTemp));
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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.
|
||||
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;
|
||||
if (!usermods.getUMData(&um_data, USERMOD_ID_AUDIOREACTIVE)) {
|
||||
// add support for no audio
|
||||
@ -7417,12 +7420,13 @@ uint16_t mode_waterfall(void) { // Waterfall. By: Andrew Tulin
|
||||
} else {
|
||||
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
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
} // 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
|
||||
|
27
wled00/FX.h
27
wled00/FX.h
@ -106,6 +106,10 @@
|
||||
#define PURPLE (uint32_t)0x400080
|
||||
#define ORANGE (uint32_t)0xFF3000
|
||||
#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 DARKSLATEGRAY (uint32_t)0x2F4F4F
|
||||
#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(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(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); }
|
||||
#ifdef WLED_USE_AA_PIXELS
|
||||
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); }
|
||||
@ -651,24 +656,25 @@ typedef struct Segment {
|
||||
void moveX(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 draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
|
||||
void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
|
||||
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c);
|
||||
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
|
||||
void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
|
||||
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 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) { 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);
|
||||
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
|
||||
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 fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
|
||||
void nscale8(uint8_t scale);
|
||||
#else
|
||||
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(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, 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
|
||||
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); }
|
||||
@ -688,9 +694,12 @@ typedef struct Segment {
|
||||
inline void moveX(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 fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
|
||||
inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
|
||||
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
|
||||
inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
|
||||
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, 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) {}
|
||||
|
@ -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])
|
||||
void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
|
||||
if (!isActive() || blur_amount == 0) return; // not active
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
const unsigned dim1 = vertical ? rows : cols;
|
||||
const unsigned dim2 = vertical ? cols : rows;
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
const int dim1 = vertical ? rows : cols;
|
||||
const int dim2 = vertical ? cols : rows;
|
||||
if (i >= dim2) return;
|
||||
const float seep = blur_amount/255.f;
|
||||
const float keep = 3.f - 2.f*seep;
|
||||
// 1D box blur
|
||||
CRGB tmp[dim1];
|
||||
for (unsigned j = 0; j < dim1; j++) {
|
||||
unsigned x = vertical ? i : j;
|
||||
unsigned y = vertical ? j : i;
|
||||
int xp = vertical ? x : x-1; // "signed" to prevent underflow
|
||||
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);
|
||||
uint32_t out[dim1], in[dim1];
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
int x = vertical ? i : j;
|
||||
int y = vertical ? j : i;
|
||||
in[j] = getPixelColorXY(x, y);
|
||||
}
|
||||
for (unsigned j = 0; j < dim1; j++) {
|
||||
unsigned x = vertical ? i : j;
|
||||
unsigned y = vertical ? j : i;
|
||||
setPixelColorXY(x, y, tmp[j]);
|
||||
for (int j = 0; j < dim1; j++) {
|
||||
uint32_t curr = in[j];
|
||||
uint32_t prev = j > 0 ? in[j-1] : BLACK;
|
||||
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) {
|
||||
@ -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
|
||||
// Bresenham’s 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;
|
||||
if (soft) {
|
||||
// Xiaolin Wu’s algorithm
|
||||
int rsq = radius*radius;
|
||||
int x = 0;
|
||||
int y = radius;
|
||||
unsigned oldFade = 0;
|
||||
while (x < y) {
|
||||
float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
|
||||
unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
|
||||
if (oldFade > fade) y--;
|
||||
oldFade = fade;
|
||||
setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
|
||||
setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
|
||||
setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
|
||||
setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
|
||||
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+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 {
|
||||
// Bresenham’s 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
|
||||
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
|
||||
// draw soft bounding circle
|
||||
if (soft) drawCircle(cx, cy, radius, col, soft);
|
||||
// fill it
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
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
|
||||
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
|
||||
const unsigned cols = virtualWidth();
|
||||
const unsigned rows = virtualHeight();
|
||||
const int cols = virtualWidth();
|
||||
const int rows = virtualHeight();
|
||||
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;
|
||||
int err = (dx>dy ? dx : -dy)/2, e2;
|
||||
for (;;) {
|
||||
setPixelColorXY(x0,y0,c);
|
||||
if (x0==x1 && y0==y1) break;
|
||||
e2 = err;
|
||||
if (e2 >-dx) { err -= dy; x0 += sx; }
|
||||
if (e2 < dy) { err += dx; y0 += sy; }
|
||||
|
||||
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
|
||||
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
|
||||
|
||||
// single pixel (line length == 0)
|
||||
if (dx+dy == 0) {
|
||||
setPixelColorXY(x0, y0, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (soft) {
|
||||
// Xiaolin Wu’s 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,11 +74,16 @@
|
||||
#define I_8266_U1_APA106_3 82
|
||||
#define I_8266_DM_APA106_3 83
|
||||
#define I_8266_BB_APA106_3 84
|
||||
//WS2805
|
||||
//WS2805 (RGBCW)
|
||||
#define I_8266_U0_2805_5 89
|
||||
#define I_8266_U1_2805_5 90
|
||||
#define I_8266_DM_2805_5 91
|
||||
#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 ***/
|
||||
//RGB
|
||||
@ -117,10 +122,14 @@
|
||||
#define I_32_RN_APA106_3 85
|
||||
#define I_32_I0_APA106_3 86
|
||||
#define I_32_I1_APA106_3 87
|
||||
//WS2805
|
||||
//WS2805 (RGBCW)
|
||||
#define I_32_RN_2805_5 93
|
||||
#define I_32_I0_2805_5 94
|
||||
#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
|
||||
@ -170,10 +179,10 @@
|
||||
#define B_8266_DM_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266DmaTm1814Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM1_4 NeoPixelBusLg<NeoWrgbTm1814Feature, NeoEsp8266BitBangTm1814Method, NeoGammaNullMethod>
|
||||
//TM1829 (RGB)
|
||||
#define B_8266_U0_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U1_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_DM_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM2_4 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U0_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart0Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_U1_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266Uart1Tm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_DM_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266DmaTm1829Method, NeoGammaNullMethod>
|
||||
#define B_8266_BB_TM2_3 NeoPixelBusLg<NeoBrgFeature, NeoEsp8266BitBangTm1829Method, NeoGammaNullMethod>
|
||||
//UCS8903
|
||||
#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
|
||||
@ -199,6 +208,11 @@
|
||||
#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_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
|
||||
|
||||
/*** 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, NeoEsp32I2s1X8Ws2805Method, NeoGammaNullMethod> // parallel I2S
|
||||
#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
|
||||
|
||||
//APA102
|
||||
@ -363,6 +387,13 @@ class PolyBus {
|
||||
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) {
|
||||
switch (busType) {
|
||||
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_DM_TM1_4: beginTM1814<B_8266_DM_TM1_4*>(busPtr); break;
|
||||
case I_8266_BB_TM1_4: beginTM1814<B_8266_BB_TM1_4*>(busPtr); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_TM2_4*>(busPtr))->Begin(); break;
|
||||
case I_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_3*>(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_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_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;
|
||||
@ -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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Begin(); break;
|
||||
#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()
|
||||
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;
|
||||
@ -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))
|
||||
// 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
|
||||
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
|
||||
void* busPtr = nullptr;
|
||||
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_DM_TM1_4: busPtr = new B_8266_DM_TM1_4(len, pins[0]); break;
|
||||
case I_8266_BB_TM1_4: busPtr = new B_8266_BB_TM1_4(len, pins[0]); break;
|
||||
case I_8266_U0_TM2_3: busPtr = new B_8266_U0_TM2_4(len, pins[0]); break;
|
||||
case I_8266_U1_TM2_3: busPtr = new B_8266_U1_TM2_4(len, pins[0]); break;
|
||||
case I_8266_DM_TM2_3: busPtr = new B_8266_DM_TM2_4(len, pins[0]); break;
|
||||
case I_8266_BB_TM2_3: busPtr = new B_8266_BB_TM2_4(len, pins[0]); break;
|
||||
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_3(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_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_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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: busPtr = new B_32_I1_2805_5(len, pins[0]); break;
|
||||
#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
|
||||
// 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;
|
||||
@ -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_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_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->Show(consistent); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_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_3*>(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_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_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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->Show(consistent); break;
|
||||
#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
|
||||
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;
|
||||
@ -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_DM_TM1_4: return (static_cast<B_8266_DM_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM1_4: return (static_cast<B_8266_BB_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U0_TM2_3: return (static_cast<B_8266_U0_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_U1_TM2_3: return (static_cast<B_8266_U1_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_DM_TM2_3: return (static_cast<B_8266_DM_TM2_4*>(busPtr))->CanShow(); break;
|
||||
case I_8266_BB_TM2_3: return (static_cast<B_8266_BB_TM2_4*>(busPtr))->CanShow(); break;
|
||||
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_3*>(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_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_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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
|
||||
#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
|
||||
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;
|
||||
@ -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_DM_TM1_4: (static_cast<B_8266_DM_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_8266_BB_TM1_4: (static_cast<B_8266_BB_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_8266_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetPixelColor(pix, RgbColor(col)); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(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_BB_TM2_3: (static_cast<B_8266_BB_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_3*>(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_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_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;
|
||||
@ -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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
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
|
||||
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
|
||||
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;
|
||||
@ -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_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_U0_TM2_3: (static_cast<B_8266_U0_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_U1_TM2_3: (static_cast<B_8266_U1_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_DM_TM2_3: (static_cast<B_8266_DM_TM2_4*>(busPtr))->SetLuminance(b); break;
|
||||
case I_8266_BB_TM2_3: (static_cast<B_8266_BB_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_3*>(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_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_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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: (static_cast<B_32_I1_2805_5*>(busPtr))->SetLuminance(b); break;
|
||||
#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
|
||||
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;
|
||||
@ -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_DM_TM1_4: col = (static_cast<B_8266_DM_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM1_4: col = (static_cast<B_8266_BB_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U0_TM2_3: col = (static_cast<B_8266_U0_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_U1_TM2_3: col = (static_cast<B_8266_U1_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_DM_TM2_3: col = (static_cast<B_8266_DM_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_8266_BB_TM2_3: col = (static_cast<B_8266_BB_TM2_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
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_3*>(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_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_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;
|
||||
@ -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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
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
|
||||
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
|
||||
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;
|
||||
@ -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_DM_TM1_4: delete (static_cast<B_8266_DM_TM1_4*>(busPtr)); break;
|
||||
case I_8266_BB_TM1_4: delete (static_cast<B_8266_BB_TM1_4*>(busPtr)); break;
|
||||
case I_8266_U0_TM2_3: delete (static_cast<B_8266_U0_TM2_4*>(busPtr)); break;
|
||||
case I_8266_U1_TM2_3: delete (static_cast<B_8266_U1_TM2_4*>(busPtr)); break;
|
||||
case I_8266_DM_TM2_3: delete (static_cast<B_8266_DM_TM2_4*>(busPtr)); break;
|
||||
case I_8266_BB_TM2_3: delete (static_cast<B_8266_BB_TM2_4*>(busPtr)); break;
|
||||
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_3*>(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_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_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_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_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
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
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
|
||||
case I_32_I1_2805_5: delete (static_cast<B_32_I1_2805_5*>(busPtr)); break;
|
||||
#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
|
||||
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;
|
||||
@ -1481,6 +1600,8 @@ class PolyBus {
|
||||
return I_8266_U0_FW6_5 + offset;
|
||||
case TYPE_WS2805:
|
||||
return I_8266_U0_2805_5 + offset;
|
||||
case TYPE_TM1914:
|
||||
return I_8266_U0_TM1914_3 + offset;
|
||||
}
|
||||
#else //ESP32
|
||||
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;
|
||||
case TYPE_WS2805:
|
||||
return I_32_RN_2805_5 + offset;
|
||||
case TYPE_TM1914:
|
||||
return I_32_RN_TM1914_3 + offset;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -7,11 +7,13 @@
|
||||
#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_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_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 bool buttonBriDirection = false; // true: increase brightness, false: decrease brightness
|
||||
|
||||
void shortPressAction(uint8_t b)
|
||||
{
|
||||
@ -39,7 +41,19 @@ void longPressAction(uint8_t b)
|
||||
if (!macroLongPress[b]) {
|
||||
switch (b) {
|
||||
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 {
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
||||
@ -284,10 +298,12 @@ void handleButton()
|
||||
buttonPressedBefore[b] = true;
|
||||
|
||||
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
||||
if (!buttonLongPressed[b]) longPressAction(b);
|
||||
else if (b) { //repeatable action (~3 times per s) on button > 0
|
||||
if (!buttonLongPressed[b]) {
|
||||
buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
|
||||
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;
|
||||
}
|
||||
|
@ -268,6 +268,7 @@
|
||||
#define TYPE_SK6812_RGBW 30
|
||||
#define TYPE_TM1814 31
|
||||
#define TYPE_WS2805 32 //RGB + WW + CW
|
||||
#define TYPE_TM1914 33 //RGB
|
||||
//"Analog" types (40-47)
|
||||
#define TYPE_ONOFF 40 //binary output (relays etc.; NOT PWM)
|
||||
#define TYPE_ANALOG_1CH 41 //single channel PWM. Uses value of brightest RGBW channel
|
||||
|
@ -272,6 +272,7 @@ function onLoad()
|
||||
|
||||
selectSlot(0);
|
||||
updateTablinks(0);
|
||||
handleLocationHash();
|
||||
cpick.on("input:end", () => {setColor(1);});
|
||||
cpick.on("color:change", () => {updatePSliders()});
|
||||
pmtLS = localStorage.getItem('wledPmt');
|
||||
@ -304,7 +305,6 @@ function updateTablinks(tabI)
|
||||
{
|
||||
var tablinks = gEBCN("tablinks");
|
||||
for (var i of tablinks) i.classList.remove('active');
|
||||
if (pcMode) return;
|
||||
tablinks[tabI].classList.add('active');
|
||||
}
|
||||
|
||||
@ -315,6 +315,21 @@ function openTab(tabI, force = false)
|
||||
_C.classList.toggle('smooth', false);
|
||||
_C.style.setProperty('--i', iSlide);
|
||||
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;
|
||||
@ -3055,8 +3070,7 @@ function togglePcMode(fromB = false)
|
||||
pcMode = (wW >= 1024) && pcModeA;
|
||||
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()
|
||||
openTab(0, true);
|
||||
updateTablinks(0);
|
||||
if (pcMode) openTab(0, true);
|
||||
gId('buttonPcm').className = (pcMode) ? "active":"";
|
||||
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
|
||||
sCol('--bh', gId('bot').clientHeight + "px");
|
||||
@ -3218,6 +3232,7 @@ size();
|
||||
_C.style.setProperty('--n', N);
|
||||
|
||||
window.addEventListener('resize', size, true);
|
||||
window.addEventListener('hashchange', handleLocationHash);
|
||||
|
||||
_C.addEventListener('mousedown', lock, false);
|
||||
_C.addEventListener('touchstart', lock, false);
|
||||
|
@ -386,6 +386,7 @@ ${i+1}:
|
||||
<option value="25">TM1829</option>\
|
||||
<option value="26">UCS8903</option>\
|
||||
<option value="27">APA106/PL9823</option>\
|
||||
<option value="33">TM1914</option>\
|
||||
<option value="28">FW1906 GRBCW</option>\
|
||||
<option value="29">UCS8904 RGBW</option>\
|
||||
<option value="32">WS2805 RGBCW</option>\
|
||||
|
@ -346,7 +346,6 @@ void handleArtnetPollReply(IPAddress ipAddress) {
|
||||
|
||||
switch (DMXMode) {
|
||||
case DMX_MODE_DISABLED:
|
||||
return; // nothing to do
|
||||
break;
|
||||
|
||||
case DMX_MODE_SINGLE_RGB:
|
||||
@ -391,9 +390,17 @@ void handleArtnetPollReply(IPAddress ipAddress) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint16_t i = startUniverse; i <= endUniverse; ++i) {
|
||||
sendArtnetPollReply(&artnetPollReply, ipAddress, i);
|
||||
if (DMXMode != DMX_MODE_DISABLED) {
|
||||
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) {
|
||||
|
@ -143,20 +143,8 @@ void handleImprovWifiScan();
|
||||
void sendImprovIPRPCResult(ImprovRPCType type);
|
||||
|
||||
//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 deInitIR();
|
||||
void handleIR();
|
||||
|
||||
//json.cpp
|
||||
@ -410,7 +398,7 @@ void clearEEPROM();
|
||||
#endif
|
||||
|
||||
//wled_math.cpp
|
||||
#ifndef WLED_USE_REAL_MATH
|
||||
#if defined(ESP8266) && !defined(WLED_USE_REAL_MATH)
|
||||
template <typename T> T atan_t(T x);
|
||||
float cos_t(float phi);
|
||||
float sin_t(float x);
|
||||
@ -421,14 +409,14 @@ void clearEEPROM();
|
||||
float fmod_t(float num, float denom);
|
||||
#else
|
||||
#include <math.h>
|
||||
#define sin_t sin
|
||||
#define cos_t cos
|
||||
#define tan_t tan
|
||||
#define asin_t asin
|
||||
#define acos_t acos
|
||||
#define atan_t atan
|
||||
#define fmod_t fmod
|
||||
#define floor_t floor
|
||||
#define sin_t sinf
|
||||
#define cos_t cosf
|
||||
#define tan_t tanf
|
||||
#define asin_t asinf
|
||||
#define acos_t acosf
|
||||
#define atan_t atanf
|
||||
#define fmod_t fmodf
|
||||
#define floor_t floorf
|
||||
#endif
|
||||
|
||||
//wled_serial.cpp
|
||||
|
@ -381,11 +381,15 @@ void updateFSInfo() {
|
||||
// original idea by @akaricchi (https://github.com/Akaricchi)
|
||||
// returns a pointer to the PSRAM buffer, updates size parameter
|
||||
static const uint8_t *getPresetCache(size_t &size) {
|
||||
static unsigned long presetsCachedTime;
|
||||
static uint8_t *presetsCached;
|
||||
static size_t presetsCachedSize;
|
||||
static unsigned long presetsCachedTime = 0;
|
||||
static uint8_t *presetsCached = nullptr;
|
||||
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) {
|
||||
free(presetsCached);
|
||||
presetsCached = nullptr;
|
||||
@ -396,6 +400,7 @@ static const uint8_t *getPresetCache(size_t &size) {
|
||||
File file = WLED_FS.open(FPSTR(getPresetsFileName()), "r");
|
||||
if (file) {
|
||||
presetsCachedTime = presetsModifiedTime;
|
||||
presetsCachedValidate = cacheInvalidate;
|
||||
presetsCachedSize = 0;
|
||||
presetsCached = (uint8_t*)ps_malloc(file.size() + 1);
|
||||
if (presetsCached) {
|
||||
|
254
wled00/ir.cpp
254
wled00/ir.cpp
@ -1,20 +1,14 @@
|
||||
#include "wled.h"
|
||||
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#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;
|
||||
//change pin in NpbWrapper.h
|
||||
|
||||
decode_results results;
|
||||
|
||||
unsigned long irCheckedTime = 0;
|
||||
uint32_t lastValidCode = 0;
|
||||
byte lastRepeatableAction = ACTION_NONE;
|
||||
@ -35,16 +29,16 @@ uint8_t lastIR6ColourIdx = 0;
|
||||
// print("%d values: %s" % (len(result), result))
|
||||
//
|
||||
// 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
|
||||
};
|
||||
const size_t numBrightnessSteps = sizeof(brightnessSteps) / sizeof(uint8_t);
|
||||
|
||||
// increment `bri` to the next `brightnessSteps` value
|
||||
void incBrightness()
|
||||
static void incBrightness()
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
@ -56,7 +50,7 @@ void incBrightness()
|
||||
}
|
||||
|
||||
// decrement `bri` to the next `brightnessSteps` value
|
||||
void decBrightness()
|
||||
static void decBrightness()
|
||||
{
|
||||
// dumb incremental search is efficient enough for so few items
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
void changeEffect(uint8_t fx)
|
||||
static void changeEffect(uint8_t fx)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
strip.setMode(i, fx);
|
||||
@ -100,10 +94,10 @@ void changeEffect(uint8_t fx)
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changePalette(uint8_t pal)
|
||||
static void changePalette(uint8_t pal)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.setPalette(pal);
|
||||
@ -116,13 +110,13 @@ void changePalette(uint8_t pal)
|
||||
stateChanged = true;
|
||||
}
|
||||
|
||||
void changeEffectSpeed(int8_t amount)
|
||||
static void changeEffectSpeed(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectSpeed + amount;
|
||||
effectSpeed = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.speed = effectSpeed;
|
||||
@ -134,10 +128,7 @@ void changeEffectSpeed(int8_t amount)
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the hue of the primary color
|
||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(sseg.colors[0]);
|
||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
int16_t new_val = (int16_t)prim_hsv.h + amount;
|
||||
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;
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
@ -163,13 +154,13 @@ void changeEffectSpeed(int8_t amount)
|
||||
lastRepeatableValue = amount;
|
||||
}
|
||||
|
||||
void changeEffectIntensity(int8_t amount)
|
||||
static void changeEffectIntensity(int8_t amount)
|
||||
{
|
||||
if (effectCurrent != 0) {
|
||||
int16_t new_val = (int16_t) effectIntensity + amount;
|
||||
effectIntensity = (byte)constrain(new_val,0,255);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.intensity = effectIntensity;
|
||||
@ -181,16 +172,13 @@ void changeEffectIntensity(int8_t amount)
|
||||
}
|
||||
} else { // if Effect == "solid Color", change the saturation of the primary color
|
||||
Segment& sseg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
CRGB fastled_col;
|
||||
fastled_col.red = R(sseg.colors[0]);
|
||||
fastled_col.green = G(sseg.colors[0]);
|
||||
fastled_col.blue = B(sseg.colors[0]);
|
||||
CRGB fastled_col = CRGB(sseg.colors[0]);
|
||||
CHSV prim_hsv = rgb2hsv_approximate(fastled_col);
|
||||
int16_t new_val = (int16_t) prim_hsv.s + amount;
|
||||
prim_hsv.s = (byte)constrain(new_val,0,255); // constrain to 0-255
|
||||
hsv2rgb_rainbow(prim_hsv, fastled_col);
|
||||
if (irApplyToAllSelected) {
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
for (unsigned i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
seg.colors[0] = RGBW32(fastled_col.red, fastled_col.green, fastled_col.blue, W(sseg.colors[0]));
|
||||
@ -208,11 +196,11 @@ void changeEffectIntensity(int8_t amount)
|
||||
lastRepeatableValue = amount;
|
||||
}
|
||||
|
||||
void changeColor(uint32_t c, int16_t cct=-1)
|
||||
static void changeColor(uint32_t c, int16_t cct=-1)
|
||||
{
|
||||
if (irApplyToAllSelected) {
|
||||
// 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);
|
||||
if (!seg.isActive() || !seg.isSelected()) continue;
|
||||
byte capabilities = seg.getLightCapabilities();
|
||||
@ -249,7 +237,7 @@ void changeColor(uint32_t c, int16_t cct=-1)
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
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 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)
|
||||
static void decodeIR24(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_BRIGHTER : incBrightness(); break;
|
||||
@ -356,7 +279,7 @@ void decodeIR24(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR24OLD(uint32_t code)
|
||||
static void decodeIR24OLD(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_OLD_BRIGHTER : incBrightness(); break;
|
||||
@ -388,7 +311,7 @@ void decodeIR24OLD(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR24CT(uint32_t code)
|
||||
static void decodeIR24CT(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR24_CT_BRIGHTER : incBrightness(); break;
|
||||
@ -420,7 +343,7 @@ void decodeIR24CT(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR40(uint32_t code)
|
||||
static void decodeIR40(uint32_t code)
|
||||
{
|
||||
Segment& seg = irApplyToAllSelected ? strip.getFirstSelectedSeg() : strip.getMainSegment();
|
||||
byte r = R(seg.colors[0]);
|
||||
@ -473,7 +396,7 @@ void decodeIR40(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR44(uint32_t code)
|
||||
static void decodeIR44(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR44_BPLUS : incBrightness(); break;
|
||||
@ -525,7 +448,7 @@ void decodeIR44(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR21(uint32_t code)
|
||||
static void decodeIR21(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR21_BRIGHTER: incBrightness(); break;
|
||||
@ -554,7 +477,7 @@ void decodeIR21(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR6(uint32_t code)
|
||||
static void decodeIR6(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR6_POWER: toggleOnOff(); break;
|
||||
@ -587,7 +510,7 @@ void decodeIR6(uint32_t code)
|
||||
lastValidCode = code;
|
||||
}
|
||||
|
||||
void decodeIR9(uint32_t code)
|
||||
static void decodeIR9(uint32_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case IR9_POWER : toggleOnOff(); break;
|
||||
@ -628,7 +551,7 @@ Sample:
|
||||
"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 fileName[16];
|
||||
@ -701,41 +624,102 @@ void decodeIRJson(uint32_t code)
|
||||
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()
|
||||
{
|
||||
if (irEnabled > 0)
|
||||
{
|
||||
if (irEnabled > 0) {
|
||||
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()
|
||||
{
|
||||
if (irEnabled > 0 && millis() - irCheckedTime > 120 && !strip.isUpdating())
|
||||
{
|
||||
irCheckedTime = millis();
|
||||
if (irEnabled > 0)
|
||||
{
|
||||
if (irrecv == NULL)
|
||||
{
|
||||
initIR(); return;
|
||||
unsigned long currentTime = millis();
|
||||
unsigned timeDiff = currentTime - irCheckedTime;
|
||||
if (timeDiff > 120 && irEnabled > 0 && irrecv) {
|
||||
if (strip.isUpdating() && timeDiff < 240) return; // be nice, but not too nice
|
||||
irCheckedTime = currentTime;
|
||||
if (irrecv->decode(&results)) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (irrecv->decode(&results))
|
||||
{
|
||||
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;
|
||||
decodeIR(results.value);
|
||||
irrecv->resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")];
|
||||
if (!wifi.isNull()) {
|
||||
bool apMode = getBoolVal(wifi[F("ap")], apActive);
|
||||
|
391
wled00/mqtt.cpp
391
wled00/mqtt.cpp
@ -1,197 +1,194 @@
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* MQTT communication protocol for home automation
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
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 {
|
||||
uint8_t in = strtoul(payload, NULL, 10);
|
||||
if (in == 0 && bri > 0) briLast = bri;
|
||||
bri = in;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
//(re)subscribe to required topics
|
||||
char subuf[38];
|
||||
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
usermods.onMqttConnect(sessionPresent);
|
||||
|
||||
DEBUG_PRINTLN(F("MQTT ready"));
|
||||
publishMqtt();
|
||||
}
|
||||
|
||||
|
||||
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
static char *payloadStr;
|
||||
|
||||
DEBUG_PRINT(F("MQTT msg: "));
|
||||
DEBUG_PRINTLN(topic);
|
||||
|
||||
// paranoia check to avoid npe if no payload
|
||||
if (payload==nullptr) {
|
||||
DEBUG_PRINTLN(F("no payload -> leave"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0) { // start (1st partial packet or the only packet)
|
||||
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
|
||||
payloadStr = new char[total+1]; // allocate new buffer
|
||||
}
|
||||
if (payloadStr == nullptr) return; // buffer not allocated
|
||||
|
||||
// copy (partial) packet to buffer and 0-terminate it if it is last packet
|
||||
char* buff = payloadStr + index;
|
||||
memcpy(buff, payload, len);
|
||||
if (index + len >= total) { // at end
|
||||
payloadStr[total] = '\0'; // terminate c style string
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("MQTT partial packet received."));
|
||||
return; // process next packet
|
||||
}
|
||||
DEBUG_PRINTLN(payloadStr);
|
||||
|
||||
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
||||
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
topicPrefixLen = strlen(mqttGroupTopic);
|
||||
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Prefix is stripped from the topic at this point
|
||||
|
||||
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||
colorFromDecOrHexString(col, payloadStr);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||
if (!requestJSONBufferLock(15)) {
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
if (payloadStr[0] == '{') { //JSON API
|
||||
deserializeJson(*pDoc, payloadStr);
|
||||
deserializeState(pDoc->as<JsonObject>());
|
||||
} else { //HTTP API
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
apireq += payloadStr;
|
||||
handleSet(nullptr, apireq);
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
} else if (strlen(topic) != 0) {
|
||||
// non standard topic, check with usermods
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
} else {
|
||||
// topmost topic (just wled/MAC)
|
||||
parseMQTTBriPayload(payloadStr);
|
||||
}
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void publishMqtt()
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
DEBUG_PRINTLN(F("Publish MQTT"));
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
char s[10];
|
||||
char subuf[48];
|
||||
|
||||
sprintf_P(s, PSTR("%u"), bri);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/status"));
|
||||
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
|
||||
|
||||
char apires[1024]; // allocating 1024 bytes from stack can be risky
|
||||
XML_response(nullptr, apires);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
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()
|
||||
{
|
||||
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
|
||||
|
||||
if (mqtt == nullptr) {
|
||||
mqtt = new AsyncMqttClient();
|
||||
mqtt->onMessage(onMqttMessage);
|
||||
mqtt->onConnect(onMqttConnect);
|
||||
}
|
||||
if (mqtt->connected()) return true;
|
||||
|
||||
DEBUG_PRINTLN(F("Reconnecting MQTT"));
|
||||
IPAddress mqttIP;
|
||||
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
|
||||
{
|
||||
mqtt->setServer(mqttIP, mqttPort);
|
||||
} else {
|
||||
mqtt->setServer(mqttServer, mqttPort);
|
||||
}
|
||||
mqtt->setClientId(mqttClientID);
|
||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
|
||||
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
|
||||
#endif
|
||||
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
||||
mqtt->connect();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#include "wled.h"
|
||||
|
||||
/*
|
||||
* MQTT communication protocol for home automation
|
||||
*/
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
#define MQTT_KEEP_ALIVE_TIME 60 // contact the MQTT broker every 60 seconds
|
||||
|
||||
void parseMQTTBriPayload(char* payload)
|
||||
{
|
||||
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 {
|
||||
uint8_t in = strtoul(payload, NULL, 10);
|
||||
if (in == 0 && bri > 0) briLast = bri;
|
||||
bri = in;
|
||||
stateUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onMqttConnect(bool sessionPresent)
|
||||
{
|
||||
//(re)subscribe to required topics
|
||||
char subuf[38];
|
||||
|
||||
if (mqttDeviceTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
if (mqttGroupTopic[0] != 0) {
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strcat_P(subuf, PSTR("/col"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
strlcpy(subuf, mqttGroupTopic, 33);
|
||||
strcat_P(subuf, PSTR("/api"));
|
||||
mqtt->subscribe(subuf, 0);
|
||||
}
|
||||
|
||||
usermods.onMqttConnect(sessionPresent);
|
||||
|
||||
DEBUG_PRINTLN(F("MQTT ready"));
|
||||
publishMqtt();
|
||||
}
|
||||
|
||||
|
||||
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||
static char *payloadStr;
|
||||
|
||||
DEBUG_PRINT(F("MQTT msg: "));
|
||||
DEBUG_PRINTLN(topic);
|
||||
|
||||
// paranoia check to avoid npe if no payload
|
||||
if (payload==nullptr) {
|
||||
DEBUG_PRINTLN(F("no payload -> leave"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == 0) { // start (1st partial packet or the only packet)
|
||||
if (payloadStr) delete[] payloadStr; // fail-safe: release buffer
|
||||
payloadStr = new char[total+1]; // allocate new buffer
|
||||
}
|
||||
if (payloadStr == nullptr) return; // buffer not allocated
|
||||
|
||||
// copy (partial) packet to buffer and 0-terminate it if it is last packet
|
||||
char* buff = payloadStr + index;
|
||||
memcpy(buff, payload, len);
|
||||
if (index + len >= total) { // at end
|
||||
payloadStr[total] = '\0'; // terminate c style string
|
||||
} else {
|
||||
DEBUG_PRINTLN(F("MQTT partial packet received."));
|
||||
return; // process next packet
|
||||
}
|
||||
DEBUG_PRINTLN(payloadStr);
|
||||
|
||||
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
||||
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
topicPrefixLen = strlen(mqttGroupTopic);
|
||||
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
||||
topic += topicPrefixLen;
|
||||
} else {
|
||||
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Prefix is stripped from the topic at this point
|
||||
|
||||
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||
colorFromDecOrHexString(col, payloadStr);
|
||||
colorUpdated(CALL_MODE_DIRECT_CHANGE);
|
||||
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||
if (requestJSONBufferLock(15)) {
|
||||
if (payloadStr[0] == '{') { //JSON API
|
||||
deserializeJson(*pDoc, payloadStr);
|
||||
deserializeState(pDoc->as<JsonObject>());
|
||||
} else { //HTTP API
|
||||
String apireq = "win"; apireq += '&'; // reduce flash string usage
|
||||
apireq += payloadStr;
|
||||
handleSet(nullptr, apireq);
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
} else if (strlen(topic) != 0) {
|
||||
// non standard topic, check with usermods
|
||||
usermods.onMqttMessage(topic, payloadStr);
|
||||
} else {
|
||||
// topmost topic (just wled/MAC)
|
||||
parseMQTTBriPayload(payloadStr);
|
||||
}
|
||||
delete[] payloadStr;
|
||||
payloadStr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void publishMqtt()
|
||||
{
|
||||
if (!WLED_MQTT_CONNECTED) return;
|
||||
DEBUG_PRINTLN(F("Publish MQTT"));
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
char s[10];
|
||||
char subuf[48];
|
||||
|
||||
sprintf_P(s, PSTR("%u"), bri);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/g"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/c"));
|
||||
mqtt->publish(subuf, 0, retainMqttMsg, s); // optionally retain message (#2263)
|
||||
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
strcat_P(subuf, PSTR("/status"));
|
||||
mqtt->publish(subuf, 0, true, "online"); // retain message for a LWT
|
||||
|
||||
char apires[1024]; // allocating 1024 bytes from stack can be risky
|
||||
XML_response(nullptr, apires);
|
||||
strlcpy(subuf, mqttDeviceTopic, 33);
|
||||
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()
|
||||
{
|
||||
if (!mqttEnabled || mqttServer[0] == 0 || !WLED_CONNECTED) return false;
|
||||
|
||||
if (mqtt == nullptr) {
|
||||
mqtt = new AsyncMqttClient();
|
||||
mqtt->onMessage(onMqttMessage);
|
||||
mqtt->onConnect(onMqttConnect);
|
||||
}
|
||||
if (mqtt->connected()) return true;
|
||||
|
||||
DEBUG_PRINTLN(F("Reconnecting MQTT"));
|
||||
IPAddress mqttIP;
|
||||
if (mqttIP.fromString(mqttServer)) //see if server is IP or domain
|
||||
{
|
||||
mqtt->setServer(mqttIP, mqttPort);
|
||||
} else {
|
||||
mqtt->setServer(mqttServer, mqttPort);
|
||||
}
|
||||
mqtt->setClientId(mqttClientID);
|
||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||
|
||||
#ifndef USERMOD_SMARTNEST
|
||||
strlcpy(mqttStatusTopic, mqttDeviceTopic, 33);
|
||||
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline"); // LWT message
|
||||
#endif
|
||||
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
||||
mqtt->connect();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -2,20 +2,8 @@
|
||||
#include "wled.h"
|
||||
#include "fcn_declare.h"
|
||||
|
||||
// on esp8266, building with `-D WLED_USE_UNREAL_MATH` saves around 7Kb flash and 1KB RAM
|
||||
// warning: causes errors in sunset calculations, see #3400
|
||||
#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
|
||||
// WARNING: may cause errors in sunset calculations on ESP8266, see #3400
|
||||
// building with `-D WLED_USE_REAL_MATH` will prevent those errors at the expense of flash and RAM
|
||||
|
||||
/*
|
||||
* 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
|
||||
float N1 = 275 * month / 9;
|
||||
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;
|
||||
|
||||
//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;
|
||||
|
||||
//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
|
||||
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
|
||||
float Lquadrant = floorf( L/90) * 90;
|
||||
float RAquadrant = floorf(RA/90) * 90;
|
||||
float Lquadrant = floor_t( L/90) * 90;
|
||||
float RAquadrant = floor_t(RA/90) * 90;
|
||||
RA = RA + (Lquadrant - RAquadrant);
|
||||
|
||||
//5c. right ascension value needs to be converted into hours
|
||||
RA /= 15.0f;
|
||||
|
||||
//6. calculate the Sun's declination
|
||||
float sinDec = 0.39782f * sinf(DEG_TO_RAD*L);
|
||||
float cosDec = cosf(asinf(sinDec));
|
||||
float sinDec = 0.39782f * sin_t(DEG_TO_RAD*L);
|
||||
float cosDec = cos_t(asin_t(sinDec));
|
||||
|
||||
//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 sets on this location (on the specified date)
|
||||
|
||||
//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;
|
||||
|
||||
//8. calculate local mean time of rising/setting
|
||||
float T = H + RA - (0.06571f * t) - 6.622f;
|
||||
|
||||
//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 UT*60;
|
||||
|
@ -25,11 +25,11 @@ void _overlayAnalogClock()
|
||||
{
|
||||
if (secondPixel < analogClock12pixel)
|
||||
{
|
||||
strip.setRange(analogClock12pixel, overlayMax, 0xFF0000);
|
||||
strip.setRange(overlayMin, secondPixel, 0xFF0000);
|
||||
strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri));
|
||||
strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri));
|
||||
} else
|
||||
{
|
||||
strip.setRange(analogClock12pixel, secondPixel, 0xFF0000);
|
||||
strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri));
|
||||
}
|
||||
}
|
||||
if (analogClock5MinuteMarks)
|
||||
@ -38,12 +38,12 @@ void _overlayAnalogClock()
|
||||
{
|
||||
unsigned pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
|
||||
if (pix > overlayMax) pix -= overlaySize;
|
||||
strip.setPixelColor(pix, 0x00FFAA);
|
||||
strip.setPixelColor(pix, color_fade(0x00FFAA, bri));
|
||||
}
|
||||
}
|
||||
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
|
||||
strip.setPixelColor(minutePixel, 0x00FF00);
|
||||
strip.setPixelColor(hourPixel, 0x0000FF);
|
||||
if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri));
|
||||
strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri));
|
||||
strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri));
|
||||
}
|
||||
|
||||
|
||||
|
@ -127,7 +127,7 @@ void handlePlaylist() {
|
||||
static unsigned long presetCycledTime = 0;
|
||||
if (currentPlaylist < 0 || playlistEntries == nullptr) return;
|
||||
|
||||
if (millis() - presetCycledTime > (100*playlistEntryDur)) {
|
||||
if (millis() - presetCycledTime > (100 * playlistEntryDur) || doAdvancePlaylist) {
|
||||
presetCycledTime = millis();
|
||||
if (bri == 0 || nightlightActive) return;
|
||||
|
||||
@ -149,6 +149,7 @@ void handlePlaylist() {
|
||||
strip.setTransition(playlistEntries[playlistIndex].tr * 100);
|
||||
playlistEntryDur = playlistEntries[playlistIndex].dur;
|
||||
applyPresetFromPlaylist(playlistEntries[playlistIndex].preset);
|
||||
doAdvancePlaylist = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) {
|
||||
pinManager.deallocatePin(irPin, PinOwner::IR);
|
||||
deInitIR();
|
||||
pinManager.deallocatePin(irPin, PinOwner::IR);
|
||||
}
|
||||
#endif
|
||||
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) {
|
||||
@ -233,6 +234,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
irPin = -1;
|
||||
}
|
||||
irEnabled = request->arg(F("IT")).toInt();
|
||||
initIR();
|
||||
#endif
|
||||
irApplyToAllSelected = !request->hasArg(F("MSO"));
|
||||
|
||||
@ -898,6 +900,9 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
applyPreset(presetCycCurr);
|
||||
}
|
||||
|
||||
pos = req.indexOf(F("NP")); //advances to next preset in a playlist
|
||||
if (pos > 0) doAdvancePlaylist = true;
|
||||
|
||||
//set brightness
|
||||
updateVal(req.c_str(), "&A=", &bri);
|
||||
|
||||
|
@ -505,6 +505,13 @@ void WLED::setup()
|
||||
initServer();
|
||||
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.
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
const uint32_t seed32 = esp_random();
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// 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
|
||||
//#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 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 bool doAdvancePlaylist _INIT(false);
|
||||
|
||||
//improv
|
||||
WLED_GLOBAL byte improvActive _INIT(0); //0: no improv packet received, 1: improv active, 2: provisioning
|
||||
|
@ -206,9 +206,9 @@ bool sendLiveLedsWs(uint32_t wsClient)
|
||||
uint8_t g = G(c);
|
||||
uint8_t b = B(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++] = scale8(qadd8(w, g), strip.getBrightness()); //G
|
||||
buffer[pos++] = scale8(qadd8(w, b), strip.getBrightness()); //B
|
||||
buffer[pos++] = bri ? qadd8(w, r) : 0; //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
buffer[pos++] = bri ? qadd8(w, g) : 0; //G
|
||||
buffer[pos++] = bri ? qadd8(w, b) : 0; //B
|
||||
}
|
||||
|
||||
wsc->binary(std::move(wsBuf));
|
||||
|
Loading…
x
Reference in New Issue
Block a user