Compare commits
2 Commits
main
...
adjustable
Author | SHA1 | Date | |
---|---|---|---|
|
df174f6d26 | ||
|
d9e576c35c |
35
.gitignore
vendored
35
.gitignore
vendored
@ -1,24 +1,21 @@
|
||||
.cache
|
||||
.clang-format
|
||||
.direnv
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.idea
|
||||
.pio
|
||||
.cache
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.vscode
|
||||
|
||||
esp01-update.sh
|
||||
platformio_override.ini
|
||||
replace_fs.py
|
||||
wled-update.sh
|
||||
|
||||
/build_output/
|
||||
/node_modules/
|
||||
|
||||
/wled00/extLibs
|
||||
/wled00/LittleFS
|
||||
/wled00/my_config.h
|
||||
/wled00/Release
|
||||
/wled00/wled00.ino.cpp
|
||||
/wled00/extLibs
|
||||
/platformio_override.ini
|
||||
/wled00/my_config.h
|
||||
/build_output
|
||||
.DS_Store
|
||||
.gitignore
|
||||
.clang-format
|
||||
node_modules
|
||||
.idea
|
||||
.direnv
|
||||
wled-update.sh
|
||||
esp01-update.sh
|
||||
/wled00/LittleFS
|
||||
replace_fs.py
|
||||
wled00/wled00.ino.cpp
|
||||
|
33
CHANGELOG.md
33
CHANGELOG.md
@ -1,32 +1,5 @@
|
||||
## WLED changelog
|
||||
|
||||
#### Build 2310010, build 2310130
|
||||
- Release of WLED version 0.14.0 "Hoshi"
|
||||
- Bugfixes for #3400, #3403, #3405
|
||||
- minor HTML optimizations
|
||||
- audioreactive: bugfix for UDP sound sync (partly initialized packets)
|
||||
|
||||
#### Build 2309240
|
||||
- Release of WLED beta version 0.14.0-b6 "Hoshi"
|
||||
- Effect bugfixes and improvements (Meteor, Meteor Smooth, Scrolling Text)
|
||||
- audioreactive: bugfixes for ES8388 and ES7243 init; minor improvements for analog inputs
|
||||
|
||||
#### Build 2309100
|
||||
- Release of WLED beta version 0.14.0-b5 "Hoshi"
|
||||
- New standard esp32 build with audioreactive
|
||||
- Effect blending bugfixes, and minor optimizations
|
||||
|
||||
#### Build 2309050
|
||||
- Effect blending (#3311) (finally effect transitions!)
|
||||
*WARNING*: May not work well with ESP8266, with plenty of segments or usermods (low RAM condition)!!!
|
||||
- Added receive and send sync groups to JSON API (#3317) (you can change sync groups using preset)
|
||||
- Internal temperature usermod (#3246)
|
||||
- MQTT server and topic length overrides (#3354) (new build flags)
|
||||
- Animated Staircase usermod enhancement (#3348) (on/off toggle/relay control)
|
||||
- Added local time info to Info page (#3351)
|
||||
- New effect: Rolling Balls (a.k.a. linear bounce) (#1039)
|
||||
- Various bug fixes and enhancements.
|
||||
|
||||
#### Build 2308110
|
||||
- Release of WLED beta version 0.14.0-b4 "Hoshi"
|
||||
- Reset effect data immediately upon mode change
|
||||
@ -46,7 +19,7 @@
|
||||
- Preset cycle bugfix (#3262)
|
||||
- Rotary encoder ALT fix for large LED count (#3276)
|
||||
- effect updates (2D Plasmaball), `blur()` speedup
|
||||
- On/Off toggle from nodes view (may show unknown device type on older versions) (#3291)
|
||||
- On/Off toggle from nodes view (may show unknow device type on older versions) (#3291)
|
||||
- various fixes and improvements (ABL, crashes when changing presets with different segments)
|
||||
|
||||
#### Build 2306270
|
||||
@ -59,7 +32,7 @@
|
||||
|
||||
#### Build 2306210
|
||||
- 0.14.0-b3 release
|
||||
- respect global I2C in all usermods (no local initialization of I2C bus)
|
||||
- respect global I2C in all usermods (no local initilaisation of I2C bus)
|
||||
- Multi relay usermod compile-time enabled option (-D MULTI_RELAY_ENABLED=true|false)
|
||||
|
||||
#### Build 2306180
|
||||
@ -85,7 +58,7 @@
|
||||
|
||||
#### Build 2306020
|
||||
- Support for segment sets (PR #3171)
|
||||
- Reduce sound simulation modes to 2 to facilitate segment sets
|
||||
- Reduce sound simulation modes to 2 to facilitiate segment sets
|
||||
- Trigger button immediately on press if all configured presets are the same (PR #3226)
|
||||
- Changes for allowing Alexa to change light color to White when auto-calculating from RGB (PR #3211)
|
||||
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.0-b4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wled",
|
||||
"version": "0.14.0",
|
||||
"version": "0.14.0-b4",
|
||||
"description": "Tools for WLED project",
|
||||
"main": "tools/cdata.js",
|
||||
"directories": {
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
# CI binaries
|
||||
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth # ESP32 variant builds are temporarily excluded from CI due to toolchain issues on the GitHub Actions Linux environment
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, esp32dev_audioreactive, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
|
||||
default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB, esp32s3dev_8MB_PSRAM_opi
|
||||
|
||||
# Release binaries
|
||||
; default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_s2_mini, esp32c3dev, esp32s3dev_8MB
|
||||
@ -87,11 +87,9 @@ platform_packages = platformio/framework-arduinoespressif8266
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# FLAGS: DEBUG
|
||||
# esp8266 : see https://docs.platformio.org/en/latest/platforms/espressif8266.html#debug-level
|
||||
# esp32 : see https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
debug_flags = -D DEBUG=1 -D WLED_DEBUG
|
||||
-DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM ;; for esp8266
|
||||
debug_flags = -D DEBUG=1 -D WLED_DEBUG -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_TLS_MEM
|
||||
#if needed (for memleaks etc) also add; -DDEBUG_ESP_OOM -include "umm_malloc/umm_malloc_cfg.h"
|
||||
#-DDEBUG_ESP_CORE is not working right now
|
||||
|
||||
@ -216,9 +214,6 @@ build_flags =
|
||||
-DVTABLES_IN_FLASH
|
||||
; restrict to minimal mime-types
|
||||
-DMIMETYPE_MINIMAL
|
||||
; other special-purpose framework flags (see https://docs.platformio.org/en/latest/platforms/espressif8266.html)
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48 ;; in case of linker errors like "section `.text1' will not fit in region `iram1_0_seg'"
|
||||
; -D PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED ;; (experimental) adds some extra heap, but may cause slowdown
|
||||
|
||||
lib_deps =
|
||||
#https://github.com/lorol/LITTLEFS.git
|
||||
@ -229,7 +224,9 @@ lib_deps =
|
||||
[esp32]
|
||||
#platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.2.3/platform-espressif32-2.0.2.3.zip
|
||||
platform = espressif32@3.5.0
|
||||
|
||||
platform_packages = framework-arduinoespressif32 @ https://github.com/Aircoookie/arduino-esp32.git#1.0.6.4
|
||||
|
||||
build_flags = -g
|
||||
-DARDUINO_ARCH_ESP32
|
||||
#-DCONFIG_LITTLEFS_FOR_IDF_3_2
|
||||
@ -237,14 +234,14 @@ build_flags = -g
|
||||
#use LITTLEFS library by lorol in ESP32 core 1.x.x instead of built-in in 2.x.x
|
||||
-D LOROL_LITTLEFS
|
||||
; -DARDUINO_USB_CDC_ON_BOOT=0 ;; this flag is mandatory for "classic ESP32" when building with arduino-esp32 >=2.0.3
|
||||
|
||||
default_partitions = tools/WLED_ESP32_4MB_1MB_FS.csv
|
||||
|
||||
lib_deps =
|
||||
https://github.com/lorol/LITTLEFS.git
|
||||
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
|
||||
${env.lib_deps}
|
||||
# additional build flags for audioreactive
|
||||
AR_build_flags = -D USERMOD_AUDIOREACTIVE -D UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
AR_lib_deps = https://github.com/kosme/arduinoFFT#develop @ ^1.9.2
|
||||
|
||||
|
||||
[esp32_idf_V4]
|
||||
;; experimental build environment for ESP32 using ESP-IDF 4.4.x / arduino-esp32 v2.0.5
|
||||
@ -352,7 +349,6 @@ platform_packages = ${common.platform_packages}
|
||||
board_build.ldscript = ${common.ldscript_1m128k}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp8266} -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
|
||||
lib_deps = ${esp8266.lib_deps}
|
||||
|
||||
[env:esp07]
|
||||
@ -403,20 +399,6 @@ lib_deps = ${esp32.lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
|
||||
[env:esp32dev_audioreactive]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
platform_packages = ${esp32.platform_packages}
|
||||
build_unflags = ${common.build_unflags}
|
||||
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_audioreactive #-D WLED_DISABLE_BROWNOUT_DET
|
||||
${esp32.AR_build_flags}
|
||||
lib_deps = ${esp32.lib_deps}
|
||||
${esp32.AR_lib_deps}
|
||||
monitor_filters = esp32_exception_decoder
|
||||
board_build.partitions = ${esp32.default_partitions}
|
||||
; board_build.f_flash = 80000000L
|
||||
; board_build.flash_mode = dio
|
||||
|
||||
[env:esp32dev_qio80]
|
||||
board = esp32dev
|
||||
platform = ${esp32.platform}
|
||||
@ -606,7 +588,6 @@ build_flags = ${common.build_flags} ${esp32s2.build_flags} -D WLED_RELEASE_NAME=
|
||||
-DARDUINO_USB_DFU_ON_BOOT=0
|
||||
-DLOLIN_WIFI_FIX ; seems to work much better with this
|
||||
-D WLED_USE_PSRAM
|
||||
; -D WLED_USE_UNREAL_MATH ;; may cause wrong sunset/sunrise times, but saves 6792 bytes FLASH
|
||||
-D WLED_WATCHDOG_TIMEOUT=0
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-D LEDPIN=16
|
||||
|
@ -3,7 +3,7 @@
|
||||
<a href="https://github.com/Aircoookie/WLED/releases"><img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a>
|
||||
<a href="https://raw.githubusercontent.com/Aircoookie/WLED/master/LICENSE"><img src="https://img.shields.io/github/license/Aircoookie/wled?color=blue&style=flat-square"></a>
|
||||
<a href="https://wled.discourse.group"><img src="https://img.shields.io/discourse/topics?colorB=blue&label=forum&server=https%3A%2F%2Fwled.discourse.group%2F&style=flat-square"></a>
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://discord.gg/KuqP7NE"><img src="https://img.shields.io/discord/473448917040758787.svg?colorB=blue&label=discord&style=flat-square"></a>
|
||||
<a href="https://kno.wled.ge"><img src="https://img.shields.io/badge/quick_start-wiki-blue.svg?style=flat-square"></a>
|
||||
<a href="https://github.com/Aircoookie/WLED-App"><img src="https://img.shields.io/badge/app-wled-blue.svg?style=flat-square"></a>
|
||||
<a href="https://gitpod.io/#https://github.com/Aircoookie/WLED"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?style=flat-square&logo=gitpod"></a>
|
||||
@ -53,7 +53,7 @@ See the [documentation on our official site](https://kno.wled.ge)!
|
||||
[On this page](https://kno.wled.ge/basics/tutorials/) you can find excellent tutorials and tools to help you get your new project up and running!
|
||||
|
||||
## 🖼️ User interface
|
||||
<img src="images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="images/walking-with-iphone-x.jpg" width="50%">
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
||||
## 💾 Compatible hardware
|
||||
|
||||
@ -66,7 +66,7 @@ Credits [here](https://kno.wled.ge/about/contributors/)!
|
||||
|
||||
Join the Discord server to discuss everything about WLED!
|
||||
|
||||
<a href="https://discord.gg/QAh7wJHrRM"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
|
||||
<a href="https://discord.gg/KuqP7NE"><img src="https://discordapp.com/api/guilds/473448917040758787/widget.png?style=banner2" width="25%"></a>
|
||||
|
||||
Check out the WLED [Discourse forum](https://wled.discourse.group)!
|
||||
|
||||
|
@ -25,7 +25,6 @@ class Animated_Staircase : public Usermod {
|
||||
bool useUSSensorBottom = false; // using PIR or UltraSound sensor?
|
||||
unsigned int topMaxDist = 50; // default maximum measured distance in cm, top
|
||||
unsigned int bottomMaxDist = 50; // default maximum measured distance in cm, bottom
|
||||
bool togglePower = false; // toggle power on/off with staircase on/off
|
||||
|
||||
/* runtime variables */
|
||||
bool initDone = false;
|
||||
@ -91,7 +90,6 @@ class Animated_Staircase : public Usermod {
|
||||
static const char _bottomEcho_pin[];
|
||||
static const char _topEchoCm[];
|
||||
static const char _bottomEchoCm[];
|
||||
static const char _togglePower[];
|
||||
|
||||
void publishMqtt(bool bottom, const char* state) {
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
@ -198,7 +196,6 @@ class Animated_Staircase : public Usermod {
|
||||
if (on) {
|
||||
lastSensor = topSensorRead;
|
||||
} else {
|
||||
if (togglePower && onIndex == offIndex && offMode) toggleOnOff(); // toggle power on if off
|
||||
// If the bottom sensor triggered, we need to swipe up, ON
|
||||
swipe = bottomSensorRead;
|
||||
|
||||
@ -252,10 +249,7 @@ class Animated_Staircase : public Usermod {
|
||||
offIndex = MAX(onIndex, offIndex - 1);
|
||||
}
|
||||
}
|
||||
if (oldOn != onIndex || oldOff != offIndex) {
|
||||
updateSegments(); // reduce the number of updates to necessary ones
|
||||
if (togglePower && onIndex == offIndex && !offMode && !on) toggleOnOff(); // toggle power off for all segments off
|
||||
}
|
||||
if (oldOn != onIndex || oldOff != offIndex) updateSegments(); // reduce the number of updates to necessary ones
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,7 +295,6 @@ class Animated_Staircase : public Usermod {
|
||||
strip.setTransition(segment_delay_ms/100);
|
||||
strip.trigger();
|
||||
} else {
|
||||
if (togglePower && !on && offMode) toggleOnOff(); // toggle power on if off
|
||||
// Restore segment options
|
||||
for (int i = 0; i <= strip.getLastActiveSegmentId(); i++) {
|
||||
Segment &seg = strip.getSegment(i);
|
||||
@ -451,7 +444,6 @@ class Animated_Staircase : public Usermod {
|
||||
staircase[FPSTR(_bottomEcho_pin)] = useUSSensorBottom ? bottomEchoPin : -1;
|
||||
staircase[FPSTR(_topEchoCm)] = topMaxDist;
|
||||
staircase[FPSTR(_bottomEchoCm)] = bottomMaxDist;
|
||||
staircase[FPSTR(_togglePower)] = togglePower;
|
||||
DEBUG_PRINTLN(F("Staircase config saved."));
|
||||
}
|
||||
|
||||
@ -496,8 +488,6 @@ class Animated_Staircase : public Usermod {
|
||||
bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist;
|
||||
bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected)
|
||||
|
||||
togglePower = top[FPSTR(_togglePower)] | togglePower; // staircase toggles power on/off
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
if (!initDone) {
|
||||
// first run: reading from cfg.json
|
||||
@ -521,7 +511,7 @@ class Animated_Staircase : public Usermod {
|
||||
if (changed) setup();
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_togglePower)].isNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -561,4 +551,3 @@ const char Animated_Staircase::_bottomPIRorTrigger_pin[] PROGMEM = "bottomPIR
|
||||
const char Animated_Staircase::_bottomEcho_pin[] PROGMEM = "bottomEcho_pin";
|
||||
const char Animated_Staircase::_topEchoCm[] PROGMEM = "top-dist-cm";
|
||||
const char Animated_Staircase::_bottomEchoCm[] PROGMEM = "bottom-dist-cm";
|
||||
const char Animated_Staircase::_togglePower[] PROGMEM = "toggle-on-off";
|
||||
|
@ -1,17 +0,0 @@
|
||||
# Internal Temperature Usermod
|
||||
This usermod adds the temperature readout to the Info tab and also publishes that over the topic `mcutemp` topic.
|
||||
|
||||
## Important
|
||||
A shown temp of 53,33°C might indicate that the internal temp is not supported.
|
||||
|
||||
ESP8266 does not have a internal temp sensor
|
||||
|
||||
ESP32S2 seems to crash on reading the sensor -> disabled
|
||||
|
||||
## Installation
|
||||
Add a build flag `-D USERMOD_INTERNAL_TEMPERATURE` to your `platformio.ini` (or `platformio_override.ini`).
|
||||
|
||||
## Authors
|
||||
Soeren Willrodt [@lost-hope](https://github.com/lost-hope)
|
||||
|
||||
Dimitry Zhemkov [@dima-zhemkov](https://github.com/dima-zhemkov)
|
@ -1,117 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "wled.h"
|
||||
|
||||
class InternalTemperatureUsermod : public Usermod
|
||||
{
|
||||
|
||||
private:
|
||||
unsigned long loopInterval = 10000;
|
||||
unsigned long lastTime = 0;
|
||||
bool isEnabled = false;
|
||||
float temperature = 0;
|
||||
|
||||
static const char _name[];
|
||||
static const char _enabled[];
|
||||
static const char _loopInterval[];
|
||||
|
||||
// any private methods should go here (non-inline methosd should be defined out of class)
|
||||
void publishMqtt(const char *state, bool retain = false); // example for publishing MQTT message
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// if usermod is disabled or called during strip updating just exit
|
||||
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
|
||||
if (!isEnabled || strip.isUpdating() || millis() - lastTime <= loopInterval)
|
||||
return;
|
||||
|
||||
lastTime = millis();
|
||||
|
||||
#ifdef ESP8266 // ESP8266
|
||||
// does not seem possible
|
||||
temperature = -1;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // ESP32S2
|
||||
temperature = -1;
|
||||
#else // ESP32 ESP32S3 and ESP32C3
|
||||
temperature = roundf(temperatureRead() * 10) / 10;
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char array[10];
|
||||
snprintf(array, sizeof(array), "%f", temperature);
|
||||
publishMqtt(array);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void addToJsonInfo(JsonObject &root)
|
||||
{
|
||||
if (!isEnabled)
|
||||
return;
|
||||
|
||||
// if "u" object does not exist yet wee need to create it
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull())
|
||||
user = root.createNestedObject("u");
|
||||
|
||||
JsonArray userTempArr = user.createNestedArray(FPSTR(_name));
|
||||
userTempArr.add(temperature);
|
||||
userTempArr.add(F(" °C"));
|
||||
|
||||
// if "sensor" object does not exist yet wee need to create it
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull())
|
||||
sensor = root.createNestedObject(F("sensor"));
|
||||
|
||||
JsonArray sensorTempArr = sensor.createNestedArray(FPSTR(_name));
|
||||
sensorTempArr.add(temperature);
|
||||
sensorTempArr.add(F("°C"));
|
||||
}
|
||||
|
||||
void addToConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = isEnabled;
|
||||
top[FPSTR(_loopInterval)] = loopInterval;
|
||||
}
|
||||
|
||||
bool readFromConfig(JsonObject &root)
|
||||
{
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
bool configComplete = !top.isNull();
|
||||
configComplete &= getJsonValue(top[FPSTR(_enabled)], isEnabled);
|
||||
configComplete &= getJsonValue(top[FPSTR(_loopInterval)], loopInterval);
|
||||
|
||||
return configComplete;
|
||||
}
|
||||
|
||||
uint16_t getId()
|
||||
{
|
||||
return USERMOD_ID_INTERNAL_TEMPERATURE;
|
||||
}
|
||||
};
|
||||
|
||||
const char InternalTemperatureUsermod::_name[] PROGMEM = "Internal Temperature";
|
||||
const char InternalTemperatureUsermod::_enabled[] PROGMEM = "Enabled";
|
||||
const char InternalTemperatureUsermod::_loopInterval[] PROGMEM = "Loop Interval";
|
||||
|
||||
void InternalTemperatureUsermod::publishMqtt(const char *state, bool retain)
|
||||
{
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
// Check if MQTT Connected, otherwise it will crash the 8266
|
||||
if (WLED_MQTT_CONNECTED)
|
||||
{
|
||||
char subuf[64];
|
||||
strcpy(subuf, mqttDeviceTopic);
|
||||
strcat_P(subuf, PSTR("/mcutemp"));
|
||||
mqtt->publish(subuf, 0, retain, state);
|
||||
}
|
||||
#endif
|
||||
}
|
@ -20,12 +20,6 @@
|
||||
* ....
|
||||
*/
|
||||
|
||||
#if !defined(FFTTASK_PRIORITY)
|
||||
#define FFTTASK_PRIORITY 1 // standard: looptask prio
|
||||
//#define FFTTASK_PRIORITY 2 // above looptask, below asyc_tcp
|
||||
//#define FFTTASK_PRIORITY 4 // above asyc_tcp
|
||||
#endif
|
||||
|
||||
// Comment/Uncomment to toggle usb serial debugging
|
||||
// #define MIC_LOGGER // MIC sampling & sound input debugging (serial plotter)
|
||||
// #define FFT_SAMPLING_LOG // FFT result debugging
|
||||
@ -110,7 +104,7 @@ static float sampleAgc = 0.0f; // Smoothed AGC sample
|
||||
|
||||
// peak detection
|
||||
static bool samplePeak = false; // Boolean flag for peak - used in effects. Responding routine may reset this flag. Auto-reset after strip.getMinShowDelay()
|
||||
static uint8_t maxVol = 31; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
|
||||
static uint8_t maxVol = 10; // Reasonable value for constant volume for 'peak detector', as it won't always trigger (deprecated)
|
||||
static uint8_t binNum = 8; // Used to select the bin for FFT based beat detection (deprecated)
|
||||
static bool udpSamplePeak = false; // Boolean flag for peak. Set at the same tiem as samplePeak, but reset by transmitAudioData
|
||||
static unsigned long timeOfPeak = 0; // time of last sample peak detection.
|
||||
@ -180,17 +174,12 @@ static float windowWeighingFactors[samplesFFT] = {0.0f};
|
||||
// Create FFT object
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
// lib_deps += https://github.com/kosme/arduinoFFT#develop @ 1.9.2
|
||||
// these options actually cause slow-downs on all esp32 processors, don't use them.
|
||||
// #define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc) - not faster on ESP32
|
||||
// #define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt - slower on ESP32
|
||||
// Below options are forcing ArduinoFFT to use sqrtf() instead of sqrt()
|
||||
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 10-50% on ESP32
|
||||
#define sqrt_internal sqrtf // see https://github.com/kosme/arduinoFFT/pull/83
|
||||
#define FFT_SPEED_OVER_PRECISION // enables use of reciprocals (1/x etc), and an a few other speedups
|
||||
#define FFT_SQRT_APPROXIMATION // enables "quake3" style inverse sqrt
|
||||
#define sqrt(x) sqrtf(x) // little hack that reduces FFT time by 50% on ESP32 (as alternative to FFT_SQRT_APPROXIMATION)
|
||||
#else
|
||||
// around 40% slower on -S2
|
||||
// lib_deps += https://github.com/blazoncek/arduinoFFT.git
|
||||
#endif
|
||||
|
||||
#include <arduinoFFT.h>
|
||||
|
||||
#ifdef UM_AUDIOREACTIVE_USE_NEW_FFT
|
||||
@ -422,7 +411,7 @@ static void runMicFilter(uint16_t numSamples, float *sampleBuffer) // p
|
||||
//constexpr float beta1 = 0.8285f; // 18Khz
|
||||
constexpr float beta1 = 0.85f; // 20Khz
|
||||
|
||||
constexpr float beta2 = (1.0f - beta1) / 2.0f;
|
||||
constexpr float beta2 = (1.0f - beta1) / 2.0;
|
||||
static float last_vals[2] = { 0.0f }; // FIR high freq cutoff filter
|
||||
static float lowfilt = 0.0f; // IIR low frequency cutoff filter
|
||||
|
||||
@ -475,17 +464,17 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
|
||||
switch (FFTScalingMode) {
|
||||
case 1:
|
||||
// Logarithmic scaling
|
||||
currentResult *= 0.42f; // 42 is the answer ;-)
|
||||
currentResult -= 8.0f; // this skips the lowest row, giving some room for peaks
|
||||
if (currentResult > 1.0f) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
|
||||
else currentResult = 0.0f; // special handling, because log(1) = 0; log(0) = undefined
|
||||
currentResult *= 0.42; // 42 is the answer ;-)
|
||||
currentResult -= 8.0; // this skips the lowest row, giving some room for peaks
|
||||
if (currentResult > 1.0) currentResult = logf(currentResult); // log to base "e", which is the fastest log() function
|
||||
else currentResult = 0.0; // special handling, because log(1) = 0; log(0) = undefined
|
||||
currentResult *= 0.85f + (float(i)/18.0f); // extra up-scaling for high frequencies
|
||||
currentResult = mapf(currentResult, 0, LOG_256, 0, 255); // map [log(1) ... log(255)] to [0 ... 255]
|
||||
break;
|
||||
case 2:
|
||||
// Linear scaling
|
||||
currentResult *= 0.30f; // needs a bit more damping, get stay below 255
|
||||
currentResult -= 4.0f; // giving a bit more room for peaks
|
||||
currentResult -= 4.0; // giving a bit more room for peaks
|
||||
if (currentResult < 1.0f) currentResult = 0.0f;
|
||||
currentResult *= 0.85f + (float(i)/1.8f); // extra up-scaling for high frequencies
|
||||
break;
|
||||
@ -493,8 +482,8 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
|
||||
// square root scaling
|
||||
currentResult *= 0.38f;
|
||||
currentResult -= 6.0f;
|
||||
if (currentResult > 1.0f) currentResult = sqrtf(currentResult);
|
||||
else currentResult = 0.0f; // special handling, because sqrt(0) = undefined
|
||||
if (currentResult > 1.0) currentResult = sqrtf(currentResult);
|
||||
else currentResult = 0.0; // special handling, because sqrt(0) = undefined
|
||||
currentResult *= 0.85f + (float(i)/4.5f); // extra up-scaling for high frequencies
|
||||
currentResult = mapf(currentResult, 0.0, 16.0, 0.0, 255.0); // map [sqrt(1) ... sqrt(256)] to [0 ... 255]
|
||||
break;
|
||||
@ -522,11 +511,11 @@ static void postProcessFFTResults(bool noiseGateOpen, int numberOfChannels) // p
|
||||
// peak detection is called from FFT task when vReal[] contains valid FFT results
|
||||
static void detectSamplePeak(void) {
|
||||
bool havePeak = false;
|
||||
// softhack007: this code continuously triggers while amplitude in the selected bin is above a certain threshold. So it does not detect peaks - it detects high activity in a frequency bin.
|
||||
|
||||
// Poor man's beat detection by seeing if sample > Average + some value.
|
||||
// This goes through ALL of the 255 bins - but ignores stupid settings
|
||||
// Then we got a peak, else we don't. The peak has to time out on its own in order to support UDP sound sync.
|
||||
if ((sampleAvg > 1) && (maxVol > 0) && (binNum > 4) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) {
|
||||
if ((sampleAvg > 1) && (maxVol > 0) && (binNum > 1) && (vReal[binNum] > maxVol) && ((millis() - timeOfPeak) > 100)) {
|
||||
havePeak = true;
|
||||
}
|
||||
|
||||
@ -769,7 +758,7 @@ class AudioReactive : public Usermod {
|
||||
if (time_now - last_time > 2) {
|
||||
last_time = time_now;
|
||||
|
||||
if((fabsf(sampleReal) < 2.0f) || (sampleMax < 1.0)) {
|
||||
if((fabsf(sampleReal) < 2.0f) || (sampleMax < 1.0f)) {
|
||||
// MIC signal is "squelched" - deliver silence
|
||||
tmpAgc = 0;
|
||||
// we need to "spin down" the intgrated error buffer
|
||||
@ -884,8 +873,8 @@ class AudioReactive : public Usermod {
|
||||
// keep "peak" sample, but decay value if current sample is below peak
|
||||
if ((sampleMax < sampleReal) && (sampleReal > 0.5f)) {
|
||||
sampleMax = sampleMax + 0.5f * (sampleReal - sampleMax); // new peak - with some filtering
|
||||
// another simple way to detect samplePeak - cannot detect beats, but reacts on peak volume
|
||||
if (((binNum < 12) || ((maxVol < 1))) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) {
|
||||
// another simple way to detect samplePeak
|
||||
if ((binNum < 10) && (millis() - timeOfPeak > 80) && (sampleAvg > 1)) {
|
||||
samplePeak = true;
|
||||
timeOfPeak = millis();
|
||||
udpSamplePeak = true;
|
||||
@ -960,8 +949,6 @@ class AudioReactive : public Usermod {
|
||||
//DEBUGSR_PRINTLN("Transmitting UDP Mic Packet");
|
||||
|
||||
audioSyncPacket transmitData;
|
||||
memset(reinterpret_cast<void *>(&transmitData), 0, sizeof(transmitData)); // make sure that the packet - including "invisible" padding bytes added by the compiler - is fully initialized
|
||||
|
||||
strncpy_P(transmitData.header, PSTR(UDP_SYNC_HEADER), 6);
|
||||
// transmit samples that were not modified by limitSampleDynamics()
|
||||
transmitData.sampleRaw = (soundAgc) ? rawSampleAgc: sampleRaw;
|
||||
@ -977,10 +964,9 @@ class AudioReactive : public Usermod {
|
||||
transmitData.FFT_Magnitude = my_magnitude;
|
||||
transmitData.FFT_MajorPeak = FFT_MajorPeak;
|
||||
|
||||
if (fftUdp.beginMulticastPacket() != 0) { // beginMulticastPacket returns 0 in case of error
|
||||
fftUdp.beginMulticastPacket();
|
||||
fftUdp.write(reinterpret_cast<uint8_t *>(&transmitData), sizeof(transmitData));
|
||||
fftUdp.endPacket();
|
||||
}
|
||||
return;
|
||||
} // transmitAudioData()
|
||||
|
||||
@ -1177,7 +1163,6 @@ class AudioReactive : public Usermod {
|
||||
DEBUGSR_PRINTLN(F("AR: Analog Microphone (left channel only)."));
|
||||
audioSource = new I2SAdcSource(SAMPLE_RATE, BLOCK_SIZE);
|
||||
delay(100);
|
||||
useBandPassFilter = true; // PDM bandpass filter seems to help for bad quality analog
|
||||
if (audioSource) audioSource->initialize(audioPin);
|
||||
break;
|
||||
#endif
|
||||
@ -1287,10 +1272,9 @@ class AudioReactive : public Usermod {
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
// complain when audio userloop has been delayed for long time. Currently we need userloop running between 500 and 1500 times per second.
|
||||
// softhack007 disabled temporarily - avoid serial console spam with MANY leds and low FPS
|
||||
//if ((userloopDelay > 65) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
|
||||
//DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
|
||||
//}
|
||||
if ((userloopDelay > 23) && !disableSoundProcessing && (audioSyncEnabled == 0)) {
|
||||
DEBUG_PRINTF("[AR userLoop] hickup detected -> was inactive for last %d millis!\n", userloopDelay);
|
||||
}
|
||||
#endif
|
||||
|
||||
// run filters, and repeat in case of loop delays (hick-up compensation)
|
||||
@ -1327,9 +1311,6 @@ class AudioReactive : public Usermod {
|
||||
if (millis() - lastTime > delayMs) {
|
||||
have_new_sample = receiveAudioData();
|
||||
if (have_new_sample) last_UDPTime = millis();
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
else fftUdp.flush(); // Flush udp input buffers if we haven't read it - avoids hickups in receive mode. Does not work on 8266.
|
||||
#endif
|
||||
lastTime = millis();
|
||||
}
|
||||
if (have_new_sample) syncVolumeSmth = volumeSmth; // remember received sample
|
||||
@ -1348,7 +1329,7 @@ class AudioReactive : public Usermod {
|
||||
// Info Page: keep max sample from last 5 seconds
|
||||
if ((millis() - sampleMaxTimer) > CYCLE_SAMPLEMAX) {
|
||||
sampleMaxTimer = millis();
|
||||
maxSample5sec = (0.15f * maxSample5sec) + 0.85f *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
|
||||
maxSample5sec = (0.15 * maxSample5sec) + 0.85 *((soundAgc) ? sampleAgc : sampleAvg); // reset, and start with some smoothing
|
||||
if (sampleAvg < 1) maxSample5sec = 0; // noise gate
|
||||
} else {
|
||||
if ((sampleAvg >= 1)) maxSample5sec = fmaxf(maxSample5sec, (soundAgc) ? rawSampleAgc : sampleRaw); // follow maximum volume
|
||||
@ -1392,11 +1373,10 @@ class AudioReactive : public Usermod {
|
||||
memset(fftAvg, 0, sizeof(fftAvg));
|
||||
memset(fftResult, 0, sizeof(fftResult));
|
||||
for(int i=(init?0:1); i<NUM_GEQ_CHANNELS; i+=2) fftResult[i] = 16; // make a tiny pattern
|
||||
inputLevel = 128; // reset level slider to default
|
||||
inputLevel = 128; // resset level slider to default
|
||||
autoResetPeak();
|
||||
|
||||
if (init && FFT_Task) {
|
||||
delay(25); // give some time for I2S driver to finish sampling before we suspend it
|
||||
vTaskSuspend(FFT_Task); // update is about to begin, disable task to prevent crash
|
||||
if (udpSyncConnected) { // close UDP sync connection (if open)
|
||||
udpSyncConnected = false;
|
||||
@ -1408,14 +1388,15 @@ class AudioReactive : public Usermod {
|
||||
vTaskResume(FFT_Task);
|
||||
connected(); // resume UDP
|
||||
} else
|
||||
xTaskCreateUniversal( // xTaskCreateUniversal also works on -S2 and -C3 with single core
|
||||
// xTaskCreatePinnedToCore(
|
||||
xTaskCreate( // no need to "pin" this task to core #0
|
||||
FFTcode, // Function to implement the task
|
||||
"FFT", // Name of the task
|
||||
3592, // Stack size in words // 3592 leaves 800-1024 bytes of task stack free
|
||||
5000, // Stack size in words
|
||||
NULL, // Task input parameter
|
||||
FFTTASK_PRIORITY, // Priority of the task
|
||||
1, // Priority of the task
|
||||
&FFT_Task // Task handle
|
||||
, 0 // Core where the task should run
|
||||
// , 0 // Core where the task should run
|
||||
);
|
||||
}
|
||||
micDataReal = 0.0f; // just to be sure
|
||||
@ -1512,7 +1493,7 @@ class AudioReactive : public Usermod {
|
||||
infoArr.add(F("I2S digital"));
|
||||
}
|
||||
// input level or "silence"
|
||||
if (maxSample5sec > 1.0f) {
|
||||
if (maxSample5sec > 1.0) {
|
||||
float my_usage = 100.0f * (maxSample5sec / 255.0f);
|
||||
snprintf_P(myStringBuffer, 15, PSTR(" - peak %3d%%"), int(my_usage));
|
||||
infoArr.add(myStringBuffer);
|
||||
@ -1522,7 +1503,7 @@ class AudioReactive : public Usermod {
|
||||
} else {
|
||||
// error during audio source setup
|
||||
infoArr.add(F("not initialized"));
|
||||
infoArr.add(F(" - check pin settings"));
|
||||
infoArr.add(F(" - check GPIO config"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1826,9 +1807,7 @@ class AudioReactive : public Usermod {
|
||||
const char AudioReactive::_name[] PROGMEM = "AudioReactive";
|
||||
const char AudioReactive::_enabled[] PROGMEM = "enabled";
|
||||
const char AudioReactive::_inputLvl[] PROGMEM = "inputLevel";
|
||||
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
const char AudioReactive::_analogmic[] PROGMEM = "analogmic";
|
||||
#endif
|
||||
const char AudioReactive::_digitalmic[] PROGMEM = "digitalmic";
|
||||
const char AudioReactive::UDP_SYNC_HEADER[] PROGMEM = "00002"; // new sync header version, as format no longer compatible with previous structure
|
||||
const char AudioReactive::UDP_SYNC_HEADER_v1[] PROGMEM = "00001"; // old sync header version - need to add backwards-compatibility feature
|
||||
|
@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include "wled.h"
|
||||
#include <driver/i2s.h>
|
||||
#include <driver/adc.h>
|
||||
@ -22,14 +22,14 @@
|
||||
|
||||
// see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/chip-series-comparison.html#related-documents
|
||||
// and https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/i2s.html#overview-of-all-modes
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(ESP8266) || defined(ESP8265)
|
||||
// there are two things in these MCUs that could lead to problems with audio processing:
|
||||
// * no floating point hardware (FPU) support - FFT uses float calculations. If done in software, a strong slow-down can be expected (between 8x and 20x)
|
||||
// * single core, so FFT task might slow down other things like LED updates
|
||||
#if !defined(SOC_I2S_NUM) || (SOC_I2S_NUM < 1)
|
||||
#error This audio reactive usermod does not support ESP32-C2 or ESP32-C3.
|
||||
#error This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2.
|
||||
#else
|
||||
#warning This audio reactive usermod does not support ESP32-C2 and ESP32-C3.
|
||||
#warning This audio reactive usermod does not support ESP32-C2, ESP32-C3 or ESP32-S2.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
* if you want to receive two channels, one is the actual data from microphone and another channel is suppose to receive 0, it's different data in two channels, you need to choose I2S_CHANNEL_FMT_RIGHT_LEFT in this case.
|
||||
*/
|
||||
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 4))
|
||||
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(4, 4, 3))
|
||||
// espressif bug: only_left has no sound, left and right are swapped
|
||||
// https://github.com/espressif/esp-idf/issues/9635 I2S mic not working since 4.4 (IDFGH-8138)
|
||||
// https://github.com/espressif/esp-idf/issues/8538 I2S channel selection issue? (IDFGH-6918)
|
||||
@ -122,7 +122,7 @@ class AudioSource {
|
||||
This function needs to take care of anything that needs to be done
|
||||
before samples can be obtained from the microphone.
|
||||
*/
|
||||
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
|
||||
virtual void initialize(int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) = 0;
|
||||
|
||||
/* Deinitialize
|
||||
Release all resources and deactivate any functionality that is used
|
||||
@ -191,8 +191,7 @@ class I2SSource : public AudioSource {
|
||||
};
|
||||
}
|
||||
|
||||
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE) {
|
||||
DEBUGSR_PRINTLN("I2SSource:: initialize().");
|
||||
virtual void initialize(int8_t i2swsPin = I2S_PIN_NO_CHANGE, int8_t i2ssdPin = I2S_PIN_NO_CHANGE, int8_t i2sckPin = I2S_PIN_NO_CHANGE, int8_t mclkPin = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
if (i2swsPin != I2S_PIN_NO_CHANGE && i2ssdPin != I2S_PIN_NO_CHANGE) {
|
||||
if (!pinManager.allocatePin(i2swsPin, true, PinOwner::UM_Audioreactive) ||
|
||||
!pinManager.allocatePin(i2ssdPin, false, PinOwner::UM_Audioreactive)) { // #206
|
||||
@ -413,7 +412,6 @@ public:
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("ES7243:: initialize();");
|
||||
if ((i2sckPin < 0) || (mclkPin < 0)) {
|
||||
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
return;
|
||||
@ -529,7 +527,7 @@ class ES8388Source : public I2SSource {
|
||||
};
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
|
||||
DEBUGSR_PRINTLN("ES8388Source:: initialize();");
|
||||
|
||||
if ((i2sckPin < 0) || (mclkPin < 0)) {
|
||||
DEBUGSR_PRINTF("\nAR: invalid I2S pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
|
||||
return;
|
||||
@ -586,8 +584,7 @@ class I2SAdcSource : public I2SSource {
|
||||
/* identify Audiosource type - I2S-ADC*/
|
||||
AudioSourceType getType(void) {return(Type_I2SAdc);}
|
||||
|
||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
DEBUGSR_PRINTLN("I2SAdcSource:: initialize().");
|
||||
void initialize(int8_t audioPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
_myADCchannel = 0x0F;
|
||||
if(!pinManager.allocatePin(audioPin, false, PinOwner::UM_Audioreactive)) {
|
||||
DEBUGSR_PRINTF("failed to allocate GPIO for audio analog input: %d\n", audioPin);
|
||||
@ -758,8 +755,7 @@ class SPH0654 : public I2SSource {
|
||||
I2SSource(sampleRate, blockSize, sampleScale)
|
||||
{}
|
||||
|
||||
void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
DEBUGSR_PRINTLN("SPH0654:: initialize();");
|
||||
void initialize(uint8_t i2swsPin, uint8_t i2ssdPin, uint8_t i2sckPin, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE, int8_t = I2S_PIN_NO_CHANGE) {
|
||||
I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin);
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// these registers are only existing in "classic" ESP32
|
||||
@ -770,4 +766,3 @@ class SPH0654 : public I2SSource {
|
||||
#endif
|
||||
}
|
||||
};
|
||||
#endif
|
@ -1081,7 +1081,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
|
||||
static bool buttonLongPressed = false;
|
||||
static unsigned long buttonPressedTime = 0;
|
||||
static unsigned long buttonWaitTime = 0;
|
||||
bool handled = false;
|
||||
bool handled = true;
|
||||
|
||||
//momentary button logic
|
||||
if (isButtonPressed(b)) { //pressed
|
||||
@ -1090,12 +1090,11 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
|
||||
buttonPressedBefore = true;
|
||||
|
||||
if (now - buttonPressedTime > 600) { //long press
|
||||
buttonLongPressed = true;
|
||||
//TODO: handleButton() handles button 0 without preset in a different way for double click
|
||||
//so we need to override with same behaviour
|
||||
//DEBUG_PRINTLN(F("4LD action."));
|
||||
//if (!buttonLongPressed) longPressAction(0);
|
||||
buttonLongPressed = true;
|
||||
return false;
|
||||
longPressAction(0);
|
||||
//handled = false;
|
||||
}
|
||||
|
||||
} else if (!isButtonPressed(b) && buttonPressedBefore) { //released
|
||||
@ -1127,7 +1126,7 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
|
||||
buttonWaitTime = 0;
|
||||
//TODO: handleButton() handles button 0 without preset in a different way for double click
|
||||
//so we need to override with same behaviour
|
||||
//shortPressAction(0);
|
||||
shortPressAction(0);
|
||||
//handled = false;
|
||||
}
|
||||
return handled;
|
||||
|
@ -398,14 +398,8 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
|
||||
modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
|
||||
re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
|
||||
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount()+strip.customPalettes.size());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()+strip.customPalettes.size());
|
||||
if (strip.customPalettes.size()) {
|
||||
for (int i=0; i<strip.customPalettes.size(); i++) {
|
||||
palettes_alpha_indexes[strip.getPaletteCount()+i] = 255-i;
|
||||
palettes_qstrings[strip.getPaletteCount()+i] = PSTR("~Custom~");
|
||||
}
|
||||
}
|
||||
palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
|
||||
palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount()); // only use internal palettes
|
||||
|
||||
// How many palette names start with '*' and should not be sorted?
|
||||
// (Also skipping the first one, 'Default').
|
||||
@ -502,7 +496,7 @@ void RotaryEncoderUIUsermod::setup()
|
||||
}
|
||||
} else {
|
||||
PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
|
||||
if (pinA<0 || pinB<0 || !pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
|
||||
pinA = pinB = pinC = -1;
|
||||
enabled = false;
|
||||
return;
|
||||
@ -513,7 +507,7 @@ void RotaryEncoderUIUsermod::setup()
|
||||
#endif
|
||||
pinMode(pinA, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinB, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
if (pinC>=0) pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
pinMode(pinC, USERMOD_ROTARY_ENCODER_GPIO);
|
||||
}
|
||||
|
||||
loopTime = millis();
|
||||
@ -688,25 +682,21 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() {
|
||||
void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
|
||||
DEBUG_PRINTLN(F("Finding current mode and palette."));
|
||||
currentEffectAndPaletteInitialized = true;
|
||||
|
||||
effectCurrentIndex = 0;
|
||||
for (int i = 0; i < strip.getModeCount(); i++) {
|
||||
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
|
||||
if (modes_alpha_indexes[i] == effectCurrent) {
|
||||
effectCurrentIndex = i;
|
||||
DEBUG_PRINTLN(F("Found current mode."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("Found current mode."));
|
||||
|
||||
effectPaletteIndex = 0;
|
||||
DEBUG_PRINTLN(effectPalette);
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount()+strip.customPalettes.size(); i++) {
|
||||
for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
|
||||
if (palettes_alpha_indexes[i] == effectPalette) {
|
||||
effectPaletteIndex = i;
|
||||
DEBUG_PRINTLN(F("Found palette."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_PRINTLN(F("Found palette."));
|
||||
}
|
||||
|
||||
bool RotaryEncoderUIUsermod::changeState(const char *stateName, byte markedLine, byte markedCol, byte glyph) {
|
||||
@ -741,9 +731,7 @@ void RotaryEncoderUIUsermod::changeBrightness(bool increase) {
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
//bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
|
||||
if (bri < 40) bri = max(min((increase ? bri+fadeAmount/2 : bri-fadeAmount/2), 255), 0); // slower steps when brightness < 16%
|
||||
else bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
|
||||
bri = max(min((increase ? bri+fadeAmount : bri-fadeAmount), 255), 0);
|
||||
lampUdated();
|
||||
#ifdef USERMOD_FOUR_LINE_DISPLAY
|
||||
display->updateBrightness();
|
||||
@ -890,7 +878,7 @@ void RotaryEncoderUIUsermod::changePalette(bool increase) {
|
||||
}
|
||||
display->updateRedrawTime();
|
||||
#endif
|
||||
effectPaletteIndex = max(min((unsigned)(increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()+strip.customPalettes.size()-1), 0U);
|
||||
effectPaletteIndex = max(min((increase ? effectPaletteIndex+1 : effectPaletteIndex-1), strip.getPaletteCount()-1), 0);
|
||||
effectPalette = palettes_alpha_indexes[effectPaletteIndex];
|
||||
stateChanged = true;
|
||||
if (applyToAll) {
|
||||
|
439
wled00/FX.cpp
439
wled00/FX.cpp
File diff suppressed because it is too large
Load Diff
86
wled00/FX.h
86
wled00/FX.h
@ -176,7 +176,7 @@
|
||||
#define FX_MODE_FIRE_FLICKER 45
|
||||
#define FX_MODE_GRADIENT 46
|
||||
#define FX_MODE_LOADING 47
|
||||
#define FX_MODE_ROLLINGBALLS 48 //was Police before 0.14
|
||||
// #define FX_MODE_POLICE 48 // removed in 0.14!
|
||||
#define FX_MODE_FAIRY 49 //was Police All prior to 0.13.0-b6 (use "Two Dots" with Red/Blue and full intensity)
|
||||
#define FX_MODE_TWO_DOTS 50
|
||||
#define FX_MODE_FAIRYTWINKLE 51 //was Two Areas prior to 0.13.0-b6 (use "Two Dots" with full intensity)
|
||||
@ -381,30 +381,6 @@ typedef struct Segment {
|
||||
byte *data; // effect data pointer
|
||||
static uint16_t maxWidth, maxHeight; // these define matrix width & height (max. segment dimensions)
|
||||
|
||||
typedef struct TemporarySegmentData {
|
||||
uint16_t _optionsT;
|
||||
uint32_t _colorT[NUM_COLORS];
|
||||
uint8_t _speedT;
|
||||
uint8_t _intensityT;
|
||||
uint8_t _custom1T, _custom2T; // custom FX parameters/sliders
|
||||
struct {
|
||||
uint8_t _custom3T : 5; // reduced range slider (0-31)
|
||||
bool _check1T : 1; // checkmark 1
|
||||
bool _check2T : 1; // checkmark 2
|
||||
bool _check3T : 1; // checkmark 3
|
||||
};
|
||||
uint16_t _aux0T;
|
||||
uint16_t _aux1T;
|
||||
uint32_t _stepT;
|
||||
uint32_t _callT;
|
||||
uint8_t *_dataT;
|
||||
uint16_t _dataLenT;
|
||||
TemporarySegmentData()
|
||||
: _dataT(nullptr) // just in case...
|
||||
, _dataLenT(0)
|
||||
{}
|
||||
} tmpsegd_t;
|
||||
|
||||
private:
|
||||
union {
|
||||
uint8_t _capabilities;
|
||||
@ -420,33 +396,43 @@ typedef struct Segment {
|
||||
static uint16_t _usedSegmentData;
|
||||
|
||||
// perhaps this should be per segment, not static
|
||||
static CRGBPalette16 _randomPalette; // actual random palette
|
||||
static CRGBPalette16 _newRandomPalette; // target random palette
|
||||
static unsigned long _lastPaletteChange; // last random palette change time in millis()
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
static bool _modeBlend; // mode/effect blending semaphore
|
||||
#endif
|
||||
static CRGBPalette16 _randomPalette;
|
||||
static CRGBPalette16 _newRandomPalette;
|
||||
static unsigned long _lastPaletteChange;
|
||||
|
||||
// transition data, valid only if transitional==true, holds values during transition (72 bytes)
|
||||
struct Transition {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
tmpsegd_t _segT; // previous segment environment
|
||||
uint8_t _modeT; // previous mode/effect
|
||||
#else
|
||||
uint32_t _colorT[NUM_COLORS];
|
||||
#endif
|
||||
uint8_t _briT; // temporary brightness
|
||||
uint8_t _cctT; // temporary CCT
|
||||
CRGBPalette16 _palT; // temporary palette
|
||||
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 belnds possible)
|
||||
uint8_t _modeP; // previous mode/effect
|
||||
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
|
||||
//uint32_t _step, _call; // previous mode/effect runtime data
|
||||
//byte *_data; // previous mode/effect runtime data
|
||||
unsigned long _start; // must accommodate millis()
|
||||
uint16_t _dur;
|
||||
Transition(uint16_t dur=750)
|
||||
: _palT(CRGBPalette16(CRGB::Black))
|
||||
: _briT(255)
|
||||
, _cctT(127)
|
||||
, _palT(CRGBPalette16(CRGB::Black))
|
||||
, _prevPaletteBlends(0)
|
||||
, _modeP(FX_MODE_STATIC)
|
||||
, _start(millis())
|
||||
, _dur(dur)
|
||||
{}
|
||||
Transition(uint16_t d, uint8_t b, uint8_t c, const uint32_t *o)
|
||||
: _briT(b)
|
||||
, _cctT(c)
|
||||
, _palT(CRGBPalette16(CRGB::Black))
|
||||
, _prevPaletteBlends(0)
|
||||
, _modeP(FX_MODE_STATIC)
|
||||
, _start(millis())
|
||||
, _dur(d)
|
||||
{
|
||||
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = o[i];
|
||||
}
|
||||
} *_t;
|
||||
|
||||
public:
|
||||
@ -485,9 +471,6 @@ typedef struct Segment {
|
||||
_t(nullptr)
|
||||
{
|
||||
//refreshLightCapabilities();
|
||||
#ifdef WLED_DEBUG
|
||||
//Serial.printf("-- Creating segment: %p\n", this);
|
||||
#endif
|
||||
}
|
||||
|
||||
Segment(uint16_t sStartX, uint16_t sStopX, uint16_t sStartY, uint16_t sStopY) : Segment(sStartX, sStopX) {
|
||||
@ -499,14 +482,14 @@ typedef struct Segment {
|
||||
Segment(Segment &&orig) noexcept; // move constructor
|
||||
|
||||
~Segment() {
|
||||
#ifdef WLED_DEBUG
|
||||
//Serial.printf("-- Destroying segment: %p\n", this);
|
||||
//#ifdef WLED_DEBUG
|
||||
//Serial.print(F("Destroying segment:"));
|
||||
//if (name) Serial.printf(" %s (%p)", name, name);
|
||||
//if (data) Serial.printf(" %d (%p)", (int)_dataLen, data);
|
||||
//Serial.println();
|
||||
#endif
|
||||
//#endif
|
||||
if (name) { delete[] name; name = nullptr; }
|
||||
stopTransition();
|
||||
if (_t) { transitional = false; delete _t; _t = nullptr; }
|
||||
deallocateData();
|
||||
}
|
||||
|
||||
@ -532,9 +515,6 @@ typedef struct Segment {
|
||||
|
||||
static uint16_t getUsedSegmentData(void) { return _usedSegmentData; }
|
||||
static void addUsedSegmentData(int len) { _usedSegmentData += len; }
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
static void modeBlend(bool blend) { _modeBlend = blend; }
|
||||
#endif
|
||||
static void handleRandomPalette();
|
||||
|
||||
void setUp(uint16_t i1, uint16_t i2, uint8_t grp=1, uint8_t spc=0, uint16_t ofs=UINT16_MAX, uint16_t i1Y=0, uint16_t i2Y=1, uint8_t segId = 255);
|
||||
@ -562,12 +542,7 @@ typedef struct Segment {
|
||||
|
||||
// transition functions
|
||||
void startTransition(uint16_t dur); // transition has to start before actual segment values change
|
||||
void stopTransition(void);
|
||||
void handleTransition(void);
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
void swapSegenv(tmpsegd_t &tmpSegD);
|
||||
void restoreSegenv(tmpsegd_t &tmpSegD);
|
||||
#endif
|
||||
uint16_t progress(void); //transition progression between 0-65535
|
||||
uint8_t currentBri(uint8_t briNew, bool useCct = false);
|
||||
uint8_t currentMode(uint8_t modeNew);
|
||||
@ -629,9 +604,9 @@ typedef struct Segment {
|
||||
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);
|
||||
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 drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0, int8_t rotate = 0);
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0);
|
||||
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
|
||||
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 drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
|
||||
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
|
||||
void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
|
||||
void blur2d(fract8 blur_amount) { blur(blur_amount); }
|
||||
@ -661,9 +636,8 @@ typedef struct Segment {
|
||||
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) {}
|
||||
void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
|
||||
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) {}
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color) {}
|
||||
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
|
||||
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) {}
|
||||
void wu_pixel(uint32_t x, uint32_t y, CRGB c) {}
|
||||
#endif
|
||||
} segment;
|
||||
|
@ -216,29 +216,23 @@ void /*IRAM_ATTR*/ Segment::setPixelColorXY(int x, int y, uint32_t col)
|
||||
y *= groupLength(); // expand to physical pixels
|
||||
if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
|
||||
|
||||
uint32_t tmpCol = col;
|
||||
for (int j = 0; j < grouping; j++) { // groupping vertically
|
||||
for (int g = 0; g < grouping; g++) { // groupping horizontally
|
||||
uint16_t xX = (x+g), yY = (y+j);
|
||||
if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
// if blending modes, blend with underlying pixel
|
||||
if (_modeBlend) tmpCol = color_blend(strip.getPixelColorXY(start + xX, startY + yY), col, 0xFFFFU - progress(), true);
|
||||
#endif
|
||||
|
||||
strip.setPixelColorXY(start + xX, startY + yY, tmpCol);
|
||||
strip.setPixelColorXY(start + xX, startY + yY, col);
|
||||
|
||||
if (mirror) { //set the corresponding horizontally mirrored pixel
|
||||
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
|
||||
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
|
||||
if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
|
||||
else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
|
||||
}
|
||||
if (mirror_y) { //set the corresponding vertically mirrored pixel
|
||||
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, tmpCol);
|
||||
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, tmpCol);
|
||||
if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
|
||||
else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
|
||||
}
|
||||
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
|
||||
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, tmpCol);
|
||||
strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -571,7 +565,7 @@ void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint3
|
||||
|
||||
// draws a raster font character on canvas
|
||||
// only supports: 4x6=24, 5x8=40, 5x12=60, 6x8=48 and 7x9=63 fonts ATM
|
||||
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2, int8_t rotate) {
|
||||
void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2) {
|
||||
if (!isActive()) return; // not active
|
||||
if (chr < 32 || chr > 126) return; // only ASCII 32-126 supported
|
||||
chr -= 32; // align with font table entries
|
||||
@ -584,6 +578,9 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
|
||||
//if (w<5 || w>6 || h!=8) return;
|
||||
for (int i = 0; i<h; i++) { // character height
|
||||
int16_t y0 = y + i;
|
||||
if (y0 < 0) continue; // drawing off-screen
|
||||
if (y0 >= rows) break; // drawing off-screen
|
||||
uint8_t bits = 0;
|
||||
switch (font) {
|
||||
case 24: bits = pgm_read_byte_near(&console_font_4x6[(chr * h) + i]); break; // 5x8 font
|
||||
@ -595,16 +592,8 @@ void Segment::drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w,
|
||||
}
|
||||
col = ColorFromPalette(grad, (i+1)*255/h, 255, NOBLEND);
|
||||
for (int j = 0; j<w; j++) { // character width
|
||||
int x0, y0;
|
||||
switch (rotate) {
|
||||
case -1: x0 = x + (h-1) - i; y0 = y + (w-1) - j; break; // -90 deg
|
||||
case -2:
|
||||
case 2: x0 = x + j; y0 = y + (h-1) - i; break; // 180 deg
|
||||
case 1: x0 = x + i; y0 = y + j; break; // +90 deg
|
||||
default: x0 = x + (w-1) - j; y0 = y + i; break; // no rotation
|
||||
}
|
||||
if (x0 < 0 || x0 >= cols || y0 < 0 || y0 >= rows) continue; // drawing off-screen
|
||||
if (((bits>>(j+(8-w))) & 0x01)) { // bit set
|
||||
int16_t x0 = x + (w-1) - j;
|
||||
if ((x0 >= 0 || x0 < cols) && ((bits>>(j+(8-w))) & 0x01)) { // bit set & drawing on-screen
|
||||
setPixelColorXY(x0, y0, col);
|
||||
}
|
||||
}
|
||||
|
@ -81,13 +81,9 @@ CRGBPalette16 Segment::_randomPalette = CRGBPalette16(DEFAULT_COLOR);
|
||||
CRGBPalette16 Segment::_newRandomPalette = CRGBPalette16(DEFAULT_COLOR);
|
||||
unsigned long Segment::_lastPaletteChange = 0; // perhaps it should be per segment
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
bool Segment::_modeBlend = false;
|
||||
#endif
|
||||
|
||||
// copy constructor
|
||||
Segment::Segment(const Segment &orig) {
|
||||
//DEBUG_PRINTF("-- Copy segment constructor: %p -> %p\n", &orig, this);
|
||||
//DEBUG_PRINTLN(F("-- Copy segment constructor --"));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
transitional = false; // copied segment cannot be in transition
|
||||
name = nullptr;
|
||||
@ -96,12 +92,12 @@ Segment::Segment(const Segment &orig) {
|
||||
_t = nullptr;
|
||||
if (orig.name) { name = new char[strlen(orig.name)+1]; if (name) strcpy(name, orig.name); }
|
||||
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
|
||||
//if (orig._t) { _t = new Transition(orig._t->_dur); }
|
||||
//if (orig._t) { _t = new Transition(orig._t->_dur, orig._t->_briT, orig._t->_cctT, orig._t->_colorT); }
|
||||
}
|
||||
|
||||
// move constructor
|
||||
Segment::Segment(Segment &&orig) noexcept {
|
||||
//DEBUG_PRINTF("-- Move segment constructor: %p -> %p\n", &orig, this);
|
||||
//DEBUG_PRINTLN(F("-- Move segment constructor --"));
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.transitional = false; // old segment cannot be in transition any more
|
||||
orig.name = nullptr;
|
||||
@ -112,17 +108,12 @@ Segment::Segment(Segment &&orig) noexcept {
|
||||
|
||||
// copy assignment
|
||||
Segment& Segment::operator= (const Segment &orig) {
|
||||
//DEBUG_PRINTF("-- Copying segment: %p -> %p\n", &orig, this);
|
||||
//DEBUG_PRINTLN(F("-- Copying segment --"));
|
||||
if (this != &orig) {
|
||||
// clean destination
|
||||
transitional = false; // copied segment cannot be in transition
|
||||
if (name) delete[] name;
|
||||
if (_t) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT) free(_t->_segT._dataT);
|
||||
#endif
|
||||
delete _t;
|
||||
}
|
||||
if (_t) delete _t;
|
||||
deallocateData();
|
||||
// copy source
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
@ -142,18 +133,12 @@ Segment& Segment::operator= (const Segment &orig) {
|
||||
|
||||
// move assignment
|
||||
Segment& Segment::operator= (Segment &&orig) noexcept {
|
||||
//DEBUG_PRINTF("-- Moving segment: %p -> %p\n", &orig, this);
|
||||
//DEBUG_PRINTLN(F("-- Moving segment --"));
|
||||
if (this != &orig) {
|
||||
transitional = false; // just temporary
|
||||
if (name) { delete[] name; name = nullptr; } // free old name
|
||||
deallocateData(); // free old runtime data
|
||||
if (_t) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT) free(_t->_segT._dataT);
|
||||
#endif
|
||||
delete _t;
|
||||
_t = nullptr;
|
||||
}
|
||||
if (_t) { delete _t; _t = nullptr; }
|
||||
memcpy((void*)this, (void*)&orig, sizeof(Segment));
|
||||
orig.transitional = false; // old segment cannot be in transition
|
||||
orig.name = nullptr;
|
||||
@ -166,39 +151,22 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
|
||||
|
||||
bool Segment::allocateData(size_t len) {
|
||||
if (data && _dataLen == len) return true; //already allocated
|
||||
//DEBUG_PRINTF("-- Allocating data (%d): %p\n", len, this);
|
||||
deallocateData();
|
||||
if (len == 0) return(false); // nothing to do
|
||||
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) {
|
||||
// not enough memory
|
||||
DEBUG_PRINT(F("!!! Effect RAM depleted: "));
|
||||
DEBUG_PRINTF("%d/%d !!!\n", len, Segment::getUsedSegmentData());
|
||||
return false;
|
||||
}
|
||||
if (Segment::getUsedSegmentData() + len > MAX_SEGMENT_DATA) return false; //not enough memory
|
||||
// do not use SPI RAM on ESP32 since it is slow
|
||||
data = (byte*) malloc(len);
|
||||
if (!data) { DEBUG_PRINTLN(F("!!! Allocation failed. !!!")); return false; } //allocation failed
|
||||
if (!data) return false; //allocation failed
|
||||
Segment::addUsedSegmentData(len);
|
||||
//DEBUG_PRINTF("--- Allocated data (%p): %d/%d -> %p\n", this, len, Segment::getUsedSegmentData(), data);
|
||||
_dataLen = len;
|
||||
memset(data, 0, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Segment::deallocateData() {
|
||||
if (!data) { _dataLen = 0; return; }
|
||||
//DEBUG_PRINTF("--- Released data (%p): %d/%d -> %p\n", this, _dataLen, Segment::getUsedSegmentData(), data);
|
||||
if ((Segment::getUsedSegmentData() > 0) && (_dataLen > 0)) { // check that we don't have a dangling / inconsistent data pointer
|
||||
if (!data) return;
|
||||
free(data);
|
||||
} else {
|
||||
DEBUG_PRINT(F("---- Released data "));
|
||||
DEBUG_PRINTF("(%p): ", this);
|
||||
DEBUG_PRINT(F("inconsistent UsedSegmentData "));
|
||||
DEBUG_PRINTF("(%d/%d)", _dataLen, Segment::getUsedSegmentData());
|
||||
DEBUG_PRINTLN(F(", cowardly refusing to free nothing."));
|
||||
}
|
||||
data = nullptr;
|
||||
Segment::addUsedSegmentData(_dataLen <= Segment::getUsedSegmentData() ? -_dataLen : -Segment::getUsedSegmentData());
|
||||
Segment::addUsedSegmentData(-_dataLen);
|
||||
_dataLen = 0;
|
||||
}
|
||||
|
||||
@ -211,7 +179,7 @@ void Segment::deallocateData() {
|
||||
*/
|
||||
void Segment::resetIfRequired() {
|
||||
if (!reset) return;
|
||||
//DEBUG_PRINTF("-- Segment reset: %p\n", this);
|
||||
|
||||
deallocateData();
|
||||
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
|
||||
reset = false;
|
||||
@ -247,7 +215,7 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)),
|
||||
CHSV(random8(), random8(160, 255), random8(128, 255)));
|
||||
_lastPaletteChange = millis();
|
||||
handleRandomPalette(); // do a 1st pass of blend
|
||||
handleRandomPalette(); // do initial blend
|
||||
}
|
||||
targetPalette = _randomPalette;
|
||||
break;}
|
||||
@ -302,8 +270,11 @@ CRGBPalette16 &Segment::loadPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
|
||||
void Segment::startTransition(uint16_t dur) {
|
||||
if (!dur) {
|
||||
if (_t) _t->_dur = dur; // this will stop transition in next handleTransisiton()
|
||||
else transitional = false;
|
||||
transitional = false;
|
||||
if (_t) {
|
||||
delete _t;
|
||||
_t = nullptr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (transitional && _t) return; // already in transition no need to store anything
|
||||
@ -312,165 +283,44 @@ void Segment::startTransition(uint16_t dur) {
|
||||
_t = new Transition(dur); // no previous transition running
|
||||
if (!_t) return; // failed to allocate data
|
||||
|
||||
//DEBUG_PRINTF("-- Started transition: %p\n", this);
|
||||
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
|
||||
_t->_palT = _palT;
|
||||
_t->_briT = on ? opacity : 0;
|
||||
_t->_cctT = cct;
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
swapSegenv(_t->_segT);
|
||||
_t->_modeT = mode;
|
||||
_t->_segT._optionsT |= 0b0000000001000000; // mark old segment transitional
|
||||
_t->_segT._dataLenT = 0;
|
||||
_t->_segT._dataT = nullptr;
|
||||
if (_dataLen > 0 && data) {
|
||||
_t->_segT._dataT = (byte *)malloc(_dataLen);
|
||||
if (_t->_segT._dataT) {
|
||||
//DEBUG_PRINTF("-- Allocated duplicate data (%d): %p\n", _dataLen, _t->_segT._dataT);
|
||||
memcpy(_t->_segT._dataT, data, _dataLen);
|
||||
_t->_segT._dataLenT = _dataLen;
|
||||
}
|
||||
}
|
||||
#else
|
||||
_t->_palT = _palT;
|
||||
_t->_modeP = mode;
|
||||
for (size_t i=0; i<NUM_COLORS; i++) _t->_colorT[i] = colors[i];
|
||||
#endif
|
||||
transitional = true; // setOption(SEG_OPTION_TRANSITIONAL, true);
|
||||
}
|
||||
|
||||
void Segment::stopTransition() {
|
||||
if (!transitional) return;
|
||||
transitional = false; // finish transitioning segment
|
||||
//DEBUG_PRINTF("-- Stopping transition: %p\n", this);
|
||||
if (_t) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_t->_segT._dataT && _t->_segT._dataLenT > 0) {
|
||||
//DEBUG_PRINTF("-- Released duplicate data (%d): %p\n", _t->_segT._dataLenT, _t->_segT._dataT);
|
||||
free(_t->_segT._dataT);
|
||||
_t->_segT._dataT = nullptr;
|
||||
_t->_segT._dataLenT = 0;
|
||||
}
|
||||
#endif
|
||||
delete _t;
|
||||
_t = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::handleTransition() {
|
||||
if (!transitional) return;
|
||||
uint16_t _progress = progress();
|
||||
if (_progress == 0xFFFFU) stopTransition();
|
||||
}
|
||||
|
||||
// transition progression between 0-65535
|
||||
uint16_t Segment::progress() {
|
||||
if (transitional && _t) {
|
||||
if (!transitional || !_t) return 0xFFFFU;
|
||||
unsigned long timeNow = millis();
|
||||
if (_t->_dur > 0 && timeNow - _t->_start < _t->_dur) return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
|
||||
if (timeNow - _t->_start > _t->_dur || _t->_dur == 0) return 0xFFFFU;
|
||||
return (timeNow - _t->_start) * 0xFFFFU / _t->_dur;
|
||||
}
|
||||
return 0xFFFFU;
|
||||
}
|
||||
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
void Segment::swapSegenv(tmpsegd_t &tmpSeg) {
|
||||
//DEBUG_PRINTF("-- Saving temp seg: %p (%p)\n", this, tmpSeg);
|
||||
tmpSeg._optionsT = options;
|
||||
for (size_t i=0; i<NUM_COLORS; i++) tmpSeg._colorT[i] = colors[i];
|
||||
tmpSeg._speedT = speed;
|
||||
tmpSeg._intensityT = intensity;
|
||||
tmpSeg._custom1T = custom1;
|
||||
tmpSeg._custom2T = custom2;
|
||||
tmpSeg._custom3T = custom3;
|
||||
tmpSeg._check1T = check1;
|
||||
tmpSeg._check2T = check2;
|
||||
tmpSeg._check3T = check3;
|
||||
tmpSeg._aux0T = aux0;
|
||||
tmpSeg._aux1T = aux1;
|
||||
tmpSeg._stepT = step;
|
||||
tmpSeg._callT = call;
|
||||
tmpSeg._dataT = data;
|
||||
tmpSeg._dataLenT = _dataLen;
|
||||
if (_t && &tmpSeg != &(_t->_segT)) {
|
||||
// swap SEGENV with transitional data
|
||||
options = _t->_segT._optionsT;
|
||||
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = _t->_segT._colorT[i];
|
||||
speed = _t->_segT._speedT;
|
||||
intensity = _t->_segT._intensityT;
|
||||
custom1 = _t->_segT._custom1T;
|
||||
custom2 = _t->_segT._custom2T;
|
||||
custom3 = _t->_segT._custom3T;
|
||||
check1 = _t->_segT._check1T;
|
||||
check2 = _t->_segT._check2T;
|
||||
check3 = _t->_segT._check3T;
|
||||
aux0 = _t->_segT._aux0T;
|
||||
aux1 = _t->_segT._aux1T;
|
||||
step = _t->_segT._stepT;
|
||||
call = _t->_segT._callT;
|
||||
data = _t->_segT._dataT;
|
||||
_dataLen = _t->_segT._dataLenT;
|
||||
}
|
||||
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
|
||||
}
|
||||
|
||||
void Segment::restoreSegenv(tmpsegd_t &tmpSeg) {
|
||||
//DEBUG_PRINTF("-- Restoring temp seg: %p (%p)\n", this, tmpSeg);
|
||||
if (_t && &(_t->_segT) != &tmpSeg) {
|
||||
// update possibly changed variables to keep old effect running correctly
|
||||
_t->_segT._aux0T = aux0;
|
||||
_t->_segT._aux1T = aux1;
|
||||
_t->_segT._stepT = step;
|
||||
_t->_segT._callT = call;
|
||||
//if (_t->_segT._dataT != data) DEBUG_PRINTF("--- data re-allocated: (%p) %p -> %p\n", this, _t->_segT._dataT, data);
|
||||
_t->_segT._dataT = data; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
|
||||
_t->_segT._dataLenT = _dataLen; // sometimes memory gets re-allocated (!! INVESTIGATE WHY !!)
|
||||
}
|
||||
options = tmpSeg._optionsT;
|
||||
for (size_t i=0; i<NUM_COLORS; i++) colors[i] = tmpSeg._colorT[i];
|
||||
speed = tmpSeg._speedT;
|
||||
intensity = tmpSeg._intensityT;
|
||||
custom1 = tmpSeg._custom1T;
|
||||
custom2 = tmpSeg._custom2T;
|
||||
custom3 = tmpSeg._custom3T;
|
||||
check1 = tmpSeg._check1T;
|
||||
check2 = tmpSeg._check2T;
|
||||
check3 = tmpSeg._check3T;
|
||||
aux0 = tmpSeg._aux0T;
|
||||
aux1 = tmpSeg._aux1T;
|
||||
step = tmpSeg._stepT;
|
||||
call = tmpSeg._callT;
|
||||
data = tmpSeg._dataT;
|
||||
_dataLen = tmpSeg._dataLenT;
|
||||
//DEBUG_PRINTF("-- temp seg data: %p (%d,%p)\n", this, _dataLen, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t Segment::currentBri(uint8_t briNew, bool useCct) {
|
||||
uint32_t prog = progress();
|
||||
if (prog < 0xFFFFU) {
|
||||
if (transitional && _t && prog < 0xFFFFU) {
|
||||
if (useCct) return ((briNew * prog) + _t->_cctT * (0xFFFFU - prog)) >> 16;
|
||||
else return ((briNew * prog) + _t->_briT * (0xFFFFU - prog)) >> 16;
|
||||
}
|
||||
} else {
|
||||
return briNew;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Segment::currentMode(uint8_t newMode) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
uint16_t prog = progress(); // implicit check for transitional & _t in progress()
|
||||
if (prog < 0xFFFFU) return _t->_modeT;
|
||||
#endif
|
||||
return newMode;
|
||||
return (progress()>32767U) ? newMode : _t->_modeP; // change effect in the middle of transition
|
||||
}
|
||||
|
||||
uint32_t Segment::currentColor(uint8_t slot, uint32_t colorNew) {
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
return transitional && _t ? color_blend(_t->_segT._colorT[slot], colorNew, progress(), true) : colorNew;
|
||||
#else
|
||||
return transitional && _t ? color_blend(_t->_colorT[slot], colorNew, progress(), true) : colorNew;
|
||||
#endif
|
||||
}
|
||||
|
||||
CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal) {
|
||||
loadPalette(targetPalette, pal);
|
||||
if (progress() < 0xFFFFU) {
|
||||
if (transitional && _t && progress() < 0xFFFFU) {
|
||||
// blend palettes
|
||||
// there are about 255 blend passes of 48 "blends" to completely blend two palettes (in _dur time)
|
||||
// minimum blend time is 100ms maximum is 65535ms
|
||||
@ -482,6 +332,18 @@ CRGBPalette16 &Segment::currentPalette(CRGBPalette16 &targetPalette, uint8_t pal
|
||||
return targetPalette;
|
||||
}
|
||||
|
||||
void Segment::handleTransition() {
|
||||
if (!transitional) return;
|
||||
uint16_t _progress = progress();
|
||||
if (_progress == 0xFFFFU) transitional = false; // finish transitioning segment
|
||||
if (_t) { // thanks to @nXm AKA https://github.com/NMeirer
|
||||
if (_progress == 0xFFFFU) {
|
||||
delete _t;
|
||||
_t = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// relies on WS2812FX::service() to call it max every 8ms or more (MIN_SHOW_DELAY)
|
||||
void Segment::handleRandomPalette() {
|
||||
// just do a blend; if the palettes are identical it will just compare 48 bytes (same as _randomPalette == _newRandomPalette)
|
||||
@ -564,7 +426,6 @@ void Segment::setCCT(uint16_t k) {
|
||||
void Segment::setOpacity(uint8_t o) {
|
||||
if (opacity == o) return;
|
||||
if (fadeTransition) startTransition(strip.getTransition()); // start transition prior to change
|
||||
DEBUG_PRINT(F("-- Setting opacity: ")); DEBUG_PRINTLN(o);
|
||||
opacity = o;
|
||||
stateChanged = true; // send UDP/WS broadcast
|
||||
}
|
||||
@ -583,6 +444,7 @@ void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
if (fx != mode) {
|
||||
if (fadeTransition) startTransition(strip.getTransition()); // set effect transitions
|
||||
mode = fx;
|
||||
|
||||
// load default values from effect string
|
||||
if (loadDefaults) {
|
||||
int16_t sOpt;
|
||||
@ -763,7 +625,6 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
}
|
||||
i += start; // starting pixel in a group
|
||||
|
||||
uint32_t tmpCol = col;
|
||||
// set all the pixels in the group
|
||||
for (int j = 0; j < grouping; j++) {
|
||||
uint16_t indexSet = i + ((reverse) ? -j : j);
|
||||
@ -772,17 +633,11 @@ void IRAM_ATTR Segment::setPixelColor(int i, uint32_t col)
|
||||
uint16_t indexMir = stop - indexSet + start - 1;
|
||||
indexMir += offset; // offset/phase
|
||||
if (indexMir >= stop) indexMir -= len; // wrap
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexMir), col, 0xFFFFU - progress(), true);
|
||||
#endif
|
||||
strip.setPixelColor(indexMir, tmpCol);
|
||||
strip.setPixelColor(indexMir, col);
|
||||
}
|
||||
indexSet += offset; // offset/phase
|
||||
if (indexSet >= stop) indexSet -= len; // wrap
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (_modeBlend) tmpCol = color_blend(strip.getPixelColor(indexSet), col, 0xFFFFU - progress(), true);
|
||||
#endif
|
||||
strip.setPixelColor(indexSet, tmpCol);
|
||||
strip.setPixelColor(indexSet, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -988,7 +843,7 @@ void Segment::fade_out(uint8_t rate) {
|
||||
const uint16_t rows = virtualHeight(); // will be 1 for 1D
|
||||
|
||||
rate = (255-rate) >> 1;
|
||||
float mappedRate = float(rate) +1.1f;
|
||||
float mappedRate = float(rate) +1.1;
|
||||
|
||||
uint32_t color = colors[1]; // SEGCOLOR(1); // target color
|
||||
int w2 = W(color);
|
||||
@ -1224,10 +1079,8 @@ void WS2812FX::service() {
|
||||
// reset the segment runtime data if needed
|
||||
seg.resetIfRequired();
|
||||
|
||||
if (!seg.isActive()) continue;
|
||||
|
||||
// last condition ensures all solid segments are updated at the same time
|
||||
if (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC))
|
||||
if (seg.isActive() && (nowUp > seg.next_time || _triggered || (doShow && seg.mode == FX_MODE_STATIC)))
|
||||
{
|
||||
doShow = true;
|
||||
uint16_t delay = FRAMETIME;
|
||||
@ -1242,25 +1095,10 @@ void WS2812FX::service() {
|
||||
if (!cctFromRgb || correctWB) busses.setSegmentCCT(seg.currentBri(seg.cct, true), correctWB);
|
||||
for (uint8_t c = 0; c < NUM_COLORS; c++) _colors_t[c] = gamma32(_colors_t[c]);
|
||||
|
||||
// Effect blending
|
||||
// When two effects are being blended, each may have different segment data, this
|
||||
// data needs to be saved first and then restored before running previous/transitional mode.
|
||||
// The blending will largely depend on the effect behaviour since actual output (LEDs) may be
|
||||
// overwritten by later effect. To enable seamless blending for every effect, additional LED buffer
|
||||
// would need to be allocated for each effect and then blended together for each pixel.
|
||||
[[maybe_unused]] uint8_t tmpMode = seg.currentMode(seg.mode); // this will return old mode while in transition
|
||||
delay = (*_mode[seg.mode])(); // run new/current mode
|
||||
#ifndef WLED_DISABLE_MODE_BLEND
|
||||
if (seg.mode != tmpMode) {
|
||||
Segment::tmpsegd_t _tmpSegData;
|
||||
Segment::modeBlend(true); // set semaphore
|
||||
seg.swapSegenv(_tmpSegData); // temporarily store new mode state (and swap it with transitional state)
|
||||
uint16_t d2 = (*_mode[tmpMode])(); // run old mode
|
||||
seg.restoreSegenv(_tmpSegData); // restore mode state (will also update transitional state)
|
||||
delay = MIN(delay,d2); // use shortest delay
|
||||
Segment::modeBlend(false); // unset semaphore
|
||||
}
|
||||
#endif
|
||||
// effect blending (execute previous effect)
|
||||
// actual code may be a bit more involved as effects have runtime data including allocated memory
|
||||
//if (seg.transitional && seg._modeP) (*_mode[seg._modeP])(progress());
|
||||
delay = (*_mode[seg.currentMode(seg.mode)])();
|
||||
if (seg.mode != FX_MODE_HALLOWEEN_EYES) seg.call++;
|
||||
if (seg.transitional && delay > FRAMETIME) delay = FRAMETIME; // force faster updates during transition
|
||||
}
|
||||
@ -1435,7 +1273,9 @@ void WS2812FX::setMode(uint8_t segid, uint8_t m) {
|
||||
if (m >= getModeCount()) m = getModeCount() - 1;
|
||||
|
||||
if (_segments[segid].mode != m) {
|
||||
_segments[segid].setMode(m); // do not load defaults
|
||||
_segments[segid].startTransition(_transitionDur); // set effect transitions
|
||||
_segments[segid].markForReset();
|
||||
_segments[segid].mode = m;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1573,7 +1413,7 @@ void WS2812FX::purgeSegments(bool force) {
|
||||
}
|
||||
if (deleted) {
|
||||
_segments.shrink_to_fit();
|
||||
/*if (_mainSegment >= _segments.size())*/ setMainSegmentId(0);
|
||||
if (_mainSegment >= _segments.size()) setMainSegmentId(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,13 +145,9 @@ void BusDigital::show() {
|
||||
}
|
||||
uint16_t pix = i;
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
else pix += _skip;
|
||||
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co);
|
||||
}
|
||||
#if !defined(STATUSLED) || STATUSLED>=0
|
||||
if (_skip) PolyBus::setPixelColor(_busPtr, _iType, 0, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
#endif
|
||||
for (int i=1; i<_skip; i++) PolyBus::setPixelColor(_busPtr, _iType, i, 0, _colorOrderMap.getPixelColorOrder(_start, _colorOrder)); // paint skipped pixels black
|
||||
}
|
||||
PolyBus::show(_busPtr, _iType, !_buffering); // faster if buffer consistency is not important
|
||||
}
|
||||
@ -211,7 +207,7 @@ void IRAM_ATTR BusDigital::setPixelColor(uint16_t pix, uint32_t c) {
|
||||
if (Bus::hasWhite(_type)) _data[offset] = W(c);
|
||||
} else {
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
else pix += _skip;
|
||||
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
uint16_t pOld = pix;
|
||||
@ -242,7 +238,7 @@ uint32_t BusDigital::getPixelColor(uint16_t pix) {
|
||||
return c;
|
||||
} else {
|
||||
if (_reversed) pix = _len - pix -1;
|
||||
pix += _skip;
|
||||
else pix += _skip;
|
||||
uint8_t co = _colorOrderMap.getPixelColorOrder(pix+_start, _colorOrder);
|
||||
uint32_t c = restoreColorLossy(PolyBus::getPixelColor(_busPtr, _iType, (_type==TYPE_WS2812_1CH_X3) ? IC_INDEX_WS2812_1CH_3X(pix) : pix, co),_bri);
|
||||
if (_type == TYPE_WS2812_1CH_X3) { // map to correct IC, each controls 3 LEDs
|
||||
|
@ -21,7 +21,6 @@ void shortPressAction(uint8_t b)
|
||||
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
unloadPlaylist(); // applying a preset unloads the playlist
|
||||
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
@ -43,7 +42,6 @@ void longPressAction(uint8_t b)
|
||||
case 1: bri += 8; stateUpdated(CALL_MODE_BUTTON); buttonPressedTime[b] = millis(); break; // repeatable action
|
||||
}
|
||||
} else {
|
||||
unloadPlaylist(); // applying a preset unloads the playlist
|
||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
@ -65,7 +63,6 @@ void doublePressAction(uint8_t b)
|
||||
case 1: ++effectPalette %= strip.getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||
}
|
||||
} else {
|
||||
unloadPlaylist(); // applying a preset unloads the playlist
|
||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
|
||||
}
|
||||
|
||||
@ -156,7 +153,6 @@ void handleAnalog(uint8_t b)
|
||||
#ifdef ESP8266
|
||||
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
|
||||
#else
|
||||
if ((btnPin[b] < 0) || (digitalPinToAnalogChannel(btnPin[b]) < 0)) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise
|
||||
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
|
||||
#endif
|
||||
yield(); // keep WiFi task running - analog read may take several millis on ESP8266
|
||||
@ -266,7 +262,7 @@ void handleButton()
|
||||
shortPressAction(b);
|
||||
buttonPressedBefore[b] = true;
|
||||
buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler)
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
|
||||
@ -287,7 +283,7 @@ void handleButton()
|
||||
// released after rising-edge short press action
|
||||
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
|
||||
if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce
|
||||
|
@ -442,14 +442,14 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
JsonObject if_mqtt = interfaces["mqtt"];
|
||||
CJSON(mqttEnabled, if_mqtt["en"]);
|
||||
getStringFromJson(mqttServer, if_mqtt[F("broker")], MQTT_MAX_SERVER_LEN+1);
|
||||
getStringFromJson(mqttServer, if_mqtt[F("broker")], 33);
|
||||
CJSON(mqttPort, if_mqtt["port"]); // 1883
|
||||
getStringFromJson(mqttUser, if_mqtt[F("user")], 41);
|
||||
getStringFromJson(mqttPass, if_mqtt["psk"], 65); //normally not present due to security
|
||||
getStringFromJson(mqttClientID, if_mqtt[F("cid")], 41);
|
||||
|
||||
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], MQTT_MAX_TOPIC_LEN+1); // "wled/test"
|
||||
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], MQTT_MAX_TOPIC_LEN+1); // ""
|
||||
getStringFromJson(mqttDeviceTopic, if_mqtt[F("topics")][F("device")], 33); // "wled/test"
|
||||
getStringFromJson(mqttGroupTopic, if_mqtt[F("topics")][F("group")], 33); // ""
|
||||
CJSON(retainMqttMsg, if_mqtt[F("rtn")]);
|
||||
#endif
|
||||
|
||||
@ -481,6 +481,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
JsonObject if_ntp = interfaces[F("ntp")];
|
||||
CJSON(ntpEnabled, if_ntp["en"]);
|
||||
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
|
||||
CJSON(ntpSyncInterval, if_ntp[F("int")]);
|
||||
CJSON(currentTimezone, if_ntp[F("tz")]);
|
||||
CJSON(utcOffsetSecs, if_ntp[F("offset")]);
|
||||
CJSON(useAMPM, if_ntp[F("ampm")]);
|
||||
@ -939,6 +940,7 @@ void serializeConfig() {
|
||||
JsonObject if_ntp = interfaces.createNestedObject("ntp");
|
||||
if_ntp["en"] = ntpEnabled;
|
||||
if_ntp[F("host")] = ntpServerName;
|
||||
if_ntp[F("int")] = ntpSyncInterval;
|
||||
if_ntp[F("tz")] = currentTimezone;
|
||||
if_ntp[F("offset")] = utcOffsetSecs;
|
||||
if_ntp[F("ampm")] = useAMPM;
|
||||
|
@ -149,7 +149,6 @@
|
||||
#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h
|
||||
#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage
|
||||
#define USERMOD_ID_WIREGUARD 41 //Usermod "wireguard.h"
|
||||
#define USERMOD_ID_INTERNAL_TEMPERATURE 42 //Usermod "usermod_internal_temperature.h"
|
||||
|
||||
//Access point behavior
|
||||
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot
|
||||
|
@ -1034,7 +1034,6 @@ textarea {
|
||||
/*padding: 1px 0 1px 20px;*/
|
||||
display: var(--sgp);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pname {
|
||||
|
@ -683,7 +683,6 @@ ${i.opt&0x100?inforow("Debug","<button class=\"btn btn-xs\" onclick=\"requestJso
|
||||
${inforow("Build",i.vid)}
|
||||
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
|
||||
${inforow("Uptime",getRuntimeStr(i.uptime))}
|
||||
${inforow("Time",i.time)}
|
||||
${inforow("Free heap",heap," kB")}
|
||||
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
|
||||
${inforow("Estimated current",pwru)}
|
||||
@ -835,7 +834,7 @@ function populateSegments(s)
|
||||
}
|
||||
if (segCount < 2) {
|
||||
gId(`segd${lSeg}`).classList.add("hide");
|
||||
if (parseInt(gId("seg0bri").value)==255) gId(`segp0`).classList.add("hide");
|
||||
gId(`segp0`).classList.add("hide");
|
||||
}
|
||||
if (!isM && !noNewSegs && (cfg.comp.seglen?parseInt(gId(`seg${lSeg}s`).value):0)+parseInt(gId(`seg${lSeg}e`).value)<ledCount) gId(`segr${lSeg}`).classList.remove("hide");
|
||||
gId('segutil2').style.display = (segCount > 1) ? "block":"none"; // rsbtn parent
|
||||
@ -1248,7 +1247,7 @@ function updateSelectedPalette(s)
|
||||
if (s > 1 && s < 6) {
|
||||
cd[0].classList.remove('hide'); // * Color 1
|
||||
if (s > 2) cd[1].classList.remove('hide'); // * Color 1 & 2
|
||||
if (s > 3) cd[2].classList.remove('hide'); // all colors
|
||||
if (s == 5) cd[2].classList.remove('hide'); // all colors
|
||||
} else {
|
||||
for (let i of cd) if (i.dataset.hide == '1') i.classList.add('hide');
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>WLED Settings</title>
|
||||
<script>
|
||||
var d=document;
|
||||
|
@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>2D Set-up</title>
|
||||
<script>
|
||||
var d=document;
|
||||
|
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta charset="utf-8">
|
||||
<title>DMX Settings</title>
|
||||
<script>
|
||||
|
@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>LED Settings</title>
|
||||
<script>
|
||||
var d=document,laprev=55,maxB=1,maxV=0,maxM=4000,maxPB=4096,maxL=1333,maxLbquot=0; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||
|
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta charset="utf-8">
|
||||
<title>PIN required</title>
|
||||
<script>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta charset="utf-8">
|
||||
<title>Misc Settings</title>
|
||||
<script>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta charset="utf-8">
|
||||
<title>Sync Settings</title>
|
||||
<script>var d=document;
|
||||
|
@ -1,7 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<meta charset="utf-8">
|
||||
<title>Time Settings</title>
|
||||
<script>
|
||||
@ -170,6 +171,13 @@
|
||||
Get time from NTP server: <input type="checkbox" name="NT"><br>
|
||||
<input type="text" name="NS" maxlength="32"><br>
|
||||
Use 24h format: <input type="checkbox" name="CF"><br>
|
||||
NTP refresh: <select name="NP">
|
||||
<option value="86400">once</option>
|
||||
<option value="43200" selected>twice</option>
|
||||
<option value="21600">4 times</option>
|
||||
<option value="14400">6 times</option>
|
||||
<option value="10800">8 times</option>
|
||||
</select> per day<br>
|
||||
Time zone:
|
||||
<select name="TZ">
|
||||
<option value="0" selected>GMT(UTC)</option>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>UI Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
|
@ -1,8 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>Usermod Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
|
@ -2,7 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>WiFi Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
|
@ -523,7 +523,6 @@ ${urows}
|
||||
${inforow("Build",i.vid)}
|
||||
${inforow("Signal strength",i.wifi.signal +"% ("+ i.wifi.rssi, " dBm)")}
|
||||
${inforow("Uptime",getRuntimeStr(i.uptime))}
|
||||
${inforow("Time",i.time)}
|
||||
${inforow("Free heap",heap," kB")}
|
||||
${i.psram?inforow("Free PSRAM",(i.psram/1024).toFixed(1)," kB"):""}
|
||||
${inforow("Estimated current",pwru)}
|
||||
|
@ -9,7 +9,7 @@
|
||||
// Autogenerated from wled00/data/cpal/cpal.htm, do not edit!!
|
||||
const uint16_t PAGE_cpal_L = 4721;
|
||||
const uint8_t PAGE_cpal[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x13, 0xbd, 0x3b, 0x7f, 0x73, 0xdb, 0xb6,
|
||||
0x92, 0xff, 0xe7, 0x53, 0x20, 0x4c, 0x5f, 0x42, 0xd6, 0x14, 0x45, 0xd2, 0xb6, 0x64, 0x4b, 0xa2,
|
||||
0x3b, 0xa9, 0x93, 0x77, 0xce, 0x8d, 0xdd, 0x64, 0x5e, 0x7c, 0x6e, 0x7b, 0x3e, 0xbf, 0x31, 0x4d,
|
||||
0x42, 0x12, 0x1b, 0x8a, 0xe0, 0x03, 0x21, 0xd9, 0xae, 0xac, 0xef, 0x7e, 0xbb, 0x00, 0x48, 0x91,
|
||||
|
@ -41,47 +41,47 @@ const char PAGE_dmxmap[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
|
||||
// Autogenerated from wled00/data/update.htm, do not edit!!
|
||||
const uint16_t PAGE_update_length = 613;
|
||||
const uint16_t PAGE_update_length = 616;
|
||||
const uint8_t PAGE_update[] PROGMEM = {
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x75, 0x53, 0x5d, 0x6f, 0xd4, 0x30,
|
||||
0x10, 0x7c, 0xcf, 0xaf, 0x70, 0xfd, 0x74, 0x27, 0x51, 0xbb, 0x20, 0x5e, 0x28, 0x49, 0x0a, 0x47,
|
||||
0x2b, 0x54, 0x09, 0xa9, 0x95, 0xda, 0x82, 0x78, 0x42, 0x8e, 0xbd, 0xb9, 0x98, 0x73, 0xec, 0xd4,
|
||||
0xde, 0xdc, 0xe9, 0x84, 0xfa, 0xdf, 0xd9, 0x38, 0x77, 0x05, 0xf1, 0xf1, 0x12, 0xc5, 0xd9, 0xd9,
|
||||
0xf1, 0xee, 0xcc, 0xa4, 0x3c, 0xb9, 0xbc, 0xf9, 0x70, 0xff, 0xf5, 0xf6, 0x8a, 0x75, 0xd8, 0xbb,
|
||||
0xba, 0x3c, 0x3c, 0x41, 0x99, 0xba, 0xec, 0x01, 0x15, 0xd3, 0xc1, 0x23, 0x78, 0xac, 0xf8, 0xce,
|
||||
0x1a, 0xec, 0x2a, 0x03, 0x5b, 0xab, 0xe1, 0x34, 0x1f, 0x38, 0xf3, 0xaa, 0x87, 0x8a, 0x6f, 0x2d,
|
||||
0xec, 0x86, 0x10, 0x91, 0xd7, 0x45, 0x89, 0x16, 0x1d, 0xd4, 0x5f, 0x3e, 0x5d, 0x5d, 0xb2, 0x87,
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0x75, 0x53, 0x4d, 0x6f, 0xd4, 0x30,
|
||||
0x10, 0xbd, 0xe7, 0x57, 0x18, 0x9f, 0x76, 0x25, 0xd6, 0x2e, 0x1f, 0x17, 0x4a, 0x92, 0x42, 0x69,
|
||||
0x85, 0x2a, 0x21, 0xb5, 0x52, 0x5b, 0x10, 0x27, 0xe4, 0xd8, 0x93, 0x8d, 0x59, 0xc7, 0x4e, 0xed,
|
||||
0xc9, 0xae, 0x56, 0xa8, 0xff, 0x9d, 0x89, 0xb3, 0x5b, 0x10, 0x1f, 0x97, 0x28, 0xce, 0xbc, 0x79,
|
||||
0x9e, 0x79, 0xef, 0xa5, 0x7c, 0x76, 0x71, 0xfd, 0xe1, 0xee, 0xeb, 0xcd, 0x25, 0xeb, 0xb0, 0x77,
|
||||
0x75, 0x79, 0x78, 0x82, 0x32, 0x75, 0xd9, 0x03, 0x2a, 0xa6, 0x83, 0x47, 0xf0, 0x58, 0xf1, 0x9d,
|
||||
0x35, 0xd8, 0x55, 0x06, 0xb6, 0x56, 0xc3, 0x2a, 0x1f, 0x38, 0xf3, 0xaa, 0x87, 0x8a, 0x6f, 0x2d,
|
||||
0xec, 0x86, 0x10, 0x91, 0xd7, 0x45, 0x89, 0x16, 0x1d, 0xd4, 0x5f, 0x3e, 0x5d, 0x5e, 0xb0, 0xfb,
|
||||
0xc1, 0x28, 0x84, 0x52, 0xce, 0x9f, 0xca, 0xa4, 0xa3, 0x1d, 0xb0, 0x2e, 0xda, 0xd1, 0x6b, 0xb4,
|
||||
0xc1, 0xb3, 0xd5, 0x62, 0xf9, 0x63, 0x67, 0xbd, 0x09, 0x3b, 0xd1, 0xd9, 0x84, 0x21, 0xee, 0x45,
|
||||
0xa3, 0xf4, 0x66, 0xb1, 0x7c, 0x7a, 0x86, 0x3c, 0x10, 0xc4, 0x04, 0x3d, 0xf6, 0x34, 0x81, 0x58,
|
||||
0x03, 0x5e, 0x39, 0x98, 0x5e, 0x57, 0xfb, 0x6b, 0xb3, 0xe0, 0x63, 0xcb, 0x97, 0x22, 0xe1, 0xde,
|
||||
0x81, 0x30, 0x36, 0x0d, 0x4e, 0xed, 0x2b, 0xee, 0x83, 0x07, 0xfe, 0xe2, 0xbf, 0x2d, 0x7d, 0x5a,
|
||||
0xff, 0xdd, 0xd3, 0xb8, 0xa0, 0x37, 0xfc, 0xa9, 0x28, 0xe5, 0x61, 0xc4, 0xc3, 0xa8, 0x2c, 0x45,
|
||||
0x5d, 0x71, 0x99, 0x00, 0xd1, 0xfa, 0x75, 0x92, 0x49, 0x7c, 0x4f, 0x17, 0x43, 0xf5, 0x86, 0xd7,
|
||||
0xbf, 0x21, 0x27, 0xaa, 0xba, 0x78, 0x67, 0xfb, 0x49, 0x00, 0x36, 0x46, 0xb7, 0xe0, 0x33, 0xbd,
|
||||
0x4e, 0x89, 0x2f, 0xdf, 0x12, 0x32, 0x23, 0x4a, 0x39, 0x4b, 0xda, 0x04, 0xb3, 0x67, 0xc1, 0xbb,
|
||||
0xa0, 0x4c, 0xc5, 0x3f, 0x02, 0x7e, 0x5e, 0x2c, 0x89, 0xae, 0x7b, 0x55, 0x17, 0x59, 0xb2, 0xbb,
|
||||
0xd0, 0xe2, 0x4e, 0x45, 0x78, 0xd6, 0x8e, 0x2a, 0x65, 0x1b, 0x62, 0xcf, 0xc8, 0x8b, 0x2e, 0x50,
|
||||
0xcf, 0xed, 0xcd, 0xdd, 0x3d, 0x67, 0x2a, 0xcb, 0x53, 0x71, 0x21, 0xc7, 0x0c, 0xe4, 0xcc, 0x52,
|
||||
0x8d, 0x04, 0x61, 0x05, 0x90, 0x74, 0xfb, 0x81, 0x5c, 0xe9, 0x47, 0x87, 0x76, 0x50, 0x11, 0xe5,
|
||||
0x44, 0x70, 0x4a, 0x30, 0xc5, 0xe9, 0xea, 0x34, 0x36, 0xbd, 0x25, 0x3b, 0x1f, 0xa6, 0x9b, 0xaf,
|
||||
0x7d, 0x42, 0xe5, 0x1c, 0x18, 0xb6, 0x85, 0x98, 0x88, 0xf2, 0x9c, 0x95, 0x69, 0x50, 0x9e, 0x15,
|
||||
0xda, 0xa9, 0x94, 0x2a, 0x9e, 0xec, 0xc0, 0xeb, 0x33, 0xf1, 0xf2, 0xb5, 0x38, 0xa3, 0x55, 0xa8,
|
||||
0x42, 0x2b, 0xc4, 0xfa, 0x32, 0xec, 0xf2, 0x0a, 0x0c, 0x3b, 0x60, 0x8e, 0xee, 0x4f, 0xc8, 0x1a,
|
||||
0xeb, 0x55, 0xdc, 0x53, 0xbf, 0x62, 0x45, 0x17, 0xa1, 0xad, 0x78, 0x87, 0x38, 0xa4, 0x73, 0x29,
|
||||
0xd7, 0x16, 0xbb, 0xb1, 0x11, 0x3a, 0xf4, 0xf2, 0xbd, 0x8d, 0x3a, 0x84, 0xb0, 0xb1, 0x20, 0xa7,
|
||||
0x7d, 0x65, 0x04, 0x07, 0x2a, 0x41, 0xe2, 0x0c, 0x55, 0x24, 0xb3, 0x2a, 0xfe, 0xad, 0x71, 0xca,
|
||||
0x6f, 0x48, 0x13, 0xdb, 0xaf, 0x59, 0x91, 0x1d, 0x38, 0xf2, 0xd0, 0x17, 0x91, 0x3a, 0x0b, 0xce,
|
||||
0x24, 0x61, 0xc3, 0x81, 0xf6, 0x48, 0xf1, 0x27, 0xb5, 0x48, 0xdb, 0xf5, 0x45, 0xd6, 0xbe, 0x6a,
|
||||
0x69, 0xc2, 0xd3, 0xf4, 0x38, 0x92, 0xae, 0x53, 0x42, 0xa5, 0xca, 0x3b, 0x94, 0xd6, 0x0f, 0x23,
|
||||
0xb2, 0x59, 0xab, 0xd6, 0x3a, 0x38, 0xa6, 0xf9, 0xa8, 0x68, 0x84, 0xc7, 0xd1, 0x46, 0x30, 0x33,
|
||||
0xba, 0x19, 0x11, 0x29, 0x90, 0x33, 0x7c, 0xd6, 0x90, 0xc8, 0x66, 0x9b, 0x4e, 0x4a, 0x39, 0x97,
|
||||
0xff, 0x01, 0x9d, 0x0f, 0x93, 0xf0, 0xda, 0x59, 0xbd, 0xa9, 0xf8, 0x6a, 0xd2, 0x7d, 0x45, 0x39,
|
||||
0xff, 0xd5, 0x94, 0x0d, 0xaa, 0x4b, 0x63, 0xb7, 0x45, 0xf6, 0x71, 0x4a, 0x29, 0xd1, 0xd4, 0x99,
|
||||
0x9d, 0xa2, 0x27, 0x84, 0x20, 0x70, 0x26, 0xbf, 0xcd, 0xcb, 0x32, 0x13, 0x98, 0x0f, 0xc8, 0xb4,
|
||||
0x0b, 0x74, 0x08, 0x91, 0x66, 0x6d, 0x23, 0xa4, 0x2e, 0xfb, 0x31, 0xa8, 0x35, 0xb0, 0xf3, 0x65,
|
||||
0x29, 0x89, 0x6f, 0x5a, 0x77, 0x8a, 0xdc, 0x94, 0xbf, 0xe9, 0xc7, 0xfe, 0x09, 0x45, 0xc9, 0x0d,
|
||||
0xd4, 0xee, 0x03, 0x00, 0x00
|
||||
0xc1, 0xb3, 0xf3, 0xc5, 0xf2, 0xc7, 0xce, 0x7a, 0x13, 0x76, 0xa2, 0xb3, 0x09, 0x43, 0xdc, 0x8b,
|
||||
0x46, 0xe9, 0xcd, 0x62, 0xf9, 0xf8, 0x04, 0xb9, 0x27, 0x88, 0x09, 0x7a, 0xec, 0x69, 0x02, 0xb1,
|
||||
0x06, 0xbc, 0x74, 0x30, 0xbd, 0x9e, 0xef, 0xaf, 0xcc, 0x82, 0x8f, 0x2d, 0x5f, 0x8a, 0x84, 0x7b,
|
||||
0x07, 0xc2, 0xd8, 0x34, 0x38, 0xb5, 0xaf, 0xb8, 0x0f, 0x1e, 0xf8, 0xf3, 0xff, 0xb6, 0xf4, 0x69,
|
||||
0xfd, 0x77, 0x4f, 0xe3, 0x82, 0xde, 0xf0, 0xc7, 0xa2, 0x94, 0x87, 0x11, 0x0f, 0xa3, 0xb2, 0x14,
|
||||
0x75, 0xc5, 0x65, 0x02, 0x44, 0xeb, 0xd7, 0x49, 0x26, 0xf1, 0x3d, 0x9d, 0x0d, 0xd5, 0x1b, 0x5e,
|
||||
0xff, 0x86, 0x9c, 0xa8, 0xea, 0xe2, 0x9d, 0xed, 0x27, 0x01, 0xd8, 0x18, 0xdd, 0x82, 0xcf, 0xf4,
|
||||
0x3a, 0x25, 0xbe, 0x7c, 0x4b, 0xc8, 0x8c, 0x28, 0xe5, 0x2c, 0x69, 0x13, 0xcc, 0x9e, 0x05, 0xef,
|
||||
0x82, 0x32, 0x15, 0xff, 0x08, 0xf8, 0x79, 0xb1, 0x24, 0xba, 0xee, 0x65, 0x5d, 0x64, 0xc9, 0x6e,
|
||||
0x43, 0x8b, 0x3b, 0x15, 0xe1, 0x49, 0x3b, 0xaa, 0x94, 0x6d, 0x88, 0x3d, 0x23, 0x2f, 0xba, 0x40,
|
||||
0x3d, 0x37, 0xd7, 0xb7, 0x77, 0x9c, 0xa9, 0x2c, 0x4f, 0xc5, 0x85, 0x1c, 0x33, 0x90, 0x33, 0x4b,
|
||||
0x35, 0x12, 0x84, 0x15, 0x40, 0xd2, 0xed, 0x07, 0x72, 0xa5, 0x1f, 0x1d, 0xda, 0x41, 0x45, 0x94,
|
||||
0x13, 0xc1, 0x8a, 0x60, 0x8a, 0xd3, 0xd5, 0x69, 0x6c, 0x7a, 0x4b, 0x76, 0xde, 0x4f, 0x37, 0x5f,
|
||||
0xf9, 0x84, 0xca, 0x39, 0x30, 0x6c, 0x0b, 0x31, 0x11, 0xe5, 0x29, 0x2b, 0xd3, 0xa0, 0x3c, 0x2b,
|
||||
0xb4, 0x53, 0x29, 0x55, 0x3c, 0xd9, 0x81, 0xd7, 0x27, 0xe2, 0xc5, 0x6b, 0x71, 0xb2, 0x6a, 0x5e,
|
||||
0xd1, 0x36, 0x54, 0xa4, 0x2d, 0x62, 0x7d, 0x11, 0x76, 0x79, 0x0b, 0x86, 0x1d, 0x30, 0x47, 0x23,
|
||||
0x24, 0x64, 0x8d, 0xf5, 0x2a, 0xee, 0x89, 0x42, 0xb1, 0xa2, 0x8b, 0xd0, 0x56, 0xbc, 0x43, 0x1c,
|
||||
0xd2, 0xa9, 0x94, 0x6b, 0x8b, 0xdd, 0xd8, 0x08, 0x1d, 0x7a, 0xf9, 0xde, 0x46, 0x1d, 0x42, 0xd8,
|
||||
0x58, 0x90, 0xd3, 0xca, 0x32, 0x82, 0x03, 0x95, 0x20, 0x71, 0x86, 0x2a, 0x92, 0x5f, 0x15, 0xff,
|
||||
0xd6, 0x38, 0xe5, 0x37, 0x24, 0x8b, 0xed, 0xd7, 0xac, 0xc8, 0x26, 0x1c, 0x79, 0xe8, 0x8b, 0x48,
|
||||
0x9d, 0x05, 0x67, 0x92, 0xb0, 0xe1, 0x40, 0x7b, 0xa4, 0xf8, 0x93, 0x5a, 0xa4, 0xed, 0xfa, 0x2c,
|
||||
0xcb, 0x5f, 0xb5, 0x34, 0xe1, 0x2a, 0x3d, 0x8c, 0x24, 0xed, 0x14, 0x52, 0xa9, 0xf2, 0x0e, 0xa5,
|
||||
0xf5, 0xc3, 0x88, 0x6c, 0x96, 0xab, 0xb5, 0x0e, 0x8e, 0x81, 0x3e, 0x8a, 0x1a, 0xe1, 0x61, 0xb4,
|
||||
0x11, 0xcc, 0x8c, 0x6e, 0x46, 0x44, 0xca, 0xe4, 0x0c, 0x9f, 0x65, 0x24, 0xb2, 0xd9, 0xa9, 0x67,
|
||||
0xa5, 0x9c, 0xcb, 0xff, 0x80, 0xce, 0x87, 0x49, 0x7b, 0xed, 0xac, 0xde, 0x54, 0xfc, 0x7c, 0x92,
|
||||
0xfe, 0x9c, 0xa2, 0xfe, 0xab, 0x29, 0x7b, 0x54, 0x97, 0xc6, 0x6e, 0x8b, 0x6c, 0xe5, 0x14, 0x54,
|
||||
0xa2, 0xa9, 0x33, 0x3b, 0xa5, 0x4f, 0x08, 0x41, 0xe0, 0x4c, 0x7e, 0x93, 0x97, 0x65, 0x26, 0x30,
|
||||
0x1f, 0x90, 0x69, 0x17, 0xe8, 0x10, 0x22, 0xcd, 0xda, 0x46, 0x48, 0x5d, 0xf6, 0x63, 0x50, 0x6b,
|
||||
0x60, 0xa7, 0xcb, 0x52, 0x12, 0xdf, 0xb4, 0xee, 0x94, 0xba, 0x29, 0x82, 0xd3, 0xbf, 0xfd, 0x13,
|
||||
0x46, 0x22, 0xf9, 0xe1, 0xf1, 0x03, 0x00, 0x00
|
||||
};
|
||||
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2261
wled00/html_simple.h
2261
wled00/html_simple.h
File diff suppressed because it is too large
Load Diff
3936
wled00/html_ui.h
3936
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@ -210,7 +210,7 @@ void sendImprovInfoResponse() {
|
||||
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
|
||||
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
|
||||
char vString[20];
|
||||
sprintf_P(vString, PSTR("0.14.0/%i"), VERSION);
|
||||
sprintf_P(vString, PSTR("0.14.0-b4/%i"), VERSION);
|
||||
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
|
||||
|
||||
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);
|
||||
|
@ -20,7 +20,6 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
byte id = elem["id"] | it;
|
||||
if (id >= strip.getMaxSegments()) return false;
|
||||
|
||||
bool newSeg = false;
|
||||
int stop = elem["stop"] | -1;
|
||||
|
||||
// append segment
|
||||
@ -28,14 +27,10 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
if (stop <= 0) return false; // ignore empty/inactive segments
|
||||
strip.appendSegment(Segment(0, strip.getLengthTotal()));
|
||||
id = strip.getSegmentsNum()-1; // segments are added at the end of list
|
||||
newSeg = true;
|
||||
}
|
||||
|
||||
//DEBUG_PRINTLN("-- JSON deserialize segment.");
|
||||
Segment& seg = strip.getSegment(id);
|
||||
//DEBUG_PRINTF("-- Original segment: %p\n", &seg);
|
||||
Segment prev = seg; //make a backup so we can tell if something changed
|
||||
//DEBUG_PRINTF("-- Duplicate segment: %p\n", &prev);
|
||||
|
||||
uint16_t start = elem["start"] | seg.start;
|
||||
if (stop < 0) {
|
||||
@ -120,12 +115,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
// do not call seg.setUp() here, as it may cause a crash due to concurrent access if the segment is currently drawing effects
|
||||
// WS2812FX handles queueing of the change
|
||||
strip.setSegment(id, start, stop, grp, spc, of, startY, stopY);
|
||||
if (newSeg) seg.refreshLightCapabilities(); // fix for #3403
|
||||
|
||||
if (seg.reset && seg.stop == 0) {
|
||||
if (id == strip.getMainSegmentId()) strip.setMainSegmentId(0); // fix for #3403
|
||||
return true; // segment was deleted & is marked for reset, no need to change anything else
|
||||
}
|
||||
if (seg.reset && seg.stop == 0) return true; // segment was deleted & is marked for reset, no need to change anything else
|
||||
|
||||
byte segbri = seg.opacity;
|
||||
if (getVal(elem["bri"], &segbri)) {
|
||||
@ -748,10 +739,6 @@ void serializeInfo(JsonObject root)
|
||||
#endif
|
||||
root[F("uptime")] = millis()/1000 + rolloverMillis*4294967;
|
||||
|
||||
char time[32];
|
||||
getTimeString(time);
|
||||
root[F("time")] = time;
|
||||
|
||||
usermods.addToJsonInfo(root);
|
||||
|
||||
uint16_t os = 0;
|
||||
@ -988,10 +975,9 @@ void serializeNodes(JsonObject root)
|
||||
// deserializes mode data string into JsonArray
|
||||
void serializeModeData(JsonArray fxdata)
|
||||
{
|
||||
char lineBuffer[256];
|
||||
char lineBuffer[128];
|
||||
for (size_t i = 0; i < strip.getModeCount(); i++) {
|
||||
strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
|
||||
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||
strncpy_P(lineBuffer, strip.getModeData(i), 127);
|
||||
if (lineBuffer[0] != 0) {
|
||||
char* dataPtr = strchr(lineBuffer,'@');
|
||||
if (dataPtr) fxdata.add(dataPtr+1);
|
||||
@ -1002,12 +988,10 @@ void serializeModeData(JsonArray fxdata)
|
||||
|
||||
// deserializes mode names string into JsonArray
|
||||
// also removes effect data extensions (@...) from deserialised names
|
||||
void serializeModeNames(JsonArray arr)
|
||||
{
|
||||
char lineBuffer[256];
|
||||
void serializeModeNames(JsonArray arr) {
|
||||
char lineBuffer[128];
|
||||
for (size_t i = 0; i < strip.getModeCount(); i++) {
|
||||
strncpy_P(lineBuffer, strip.getModeData(i), sizeof(lineBuffer)/sizeof(char)-1);
|
||||
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||
strncpy_P(lineBuffer, strip.getModeData(i), 127);
|
||||
if (lineBuffer[0] != 0) {
|
||||
char* dataPtr = strchr(lineBuffer,'@');
|
||||
if (dataPtr) *dataPtr = 0; // terminate mode data after name
|
||||
|
@ -37,12 +37,12 @@ void applyValuesToSelectedSegs()
|
||||
|
||||
if (effectSpeed != selsegPrev.speed) {seg.speed = effectSpeed; stateChanged = true;}
|
||||
if (effectIntensity != selsegPrev.intensity) {seg.intensity = effectIntensity; stateChanged = true;}
|
||||
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette);}
|
||||
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent);}
|
||||
if (effectPalette != selsegPrev.palette) {seg.setPalette(effectPalette); stateChanged = true;}
|
||||
if (effectCurrent != selsegPrev.mode) {seg.setMode(effectCurrent); stateChanged = true;}
|
||||
uint32_t col0 = RGBW32( col[0], col[1], col[2], col[3]);
|
||||
uint32_t col1 = RGBW32(colSec[0], colSec[1], colSec[2], colSec[3]);
|
||||
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0);}
|
||||
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1);}
|
||||
if (col0 != selsegPrev.colors[0]) {seg.setColor(0, col0); stateChanged = true;}
|
||||
if (col1 != selsegPrev.colors[1]) {seg.setColor(1, col1); stateChanged = true;}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,8 +161,6 @@ void stateUpdated(byte callMode) {
|
||||
|
||||
void updateInterfaces(uint8_t callMode)
|
||||
{
|
||||
if (!interfaceUpdateCallMode || millis() - lastInterfaceUpdate < INTERFACE_UPDATE_COOLDOWN) return;
|
||||
|
||||
sendDataWs();
|
||||
lastInterfaceUpdate = millis();
|
||||
if (callMode == CALL_MODE_WS_SEND) return;
|
||||
@ -181,7 +179,7 @@ void updateInterfaces(uint8_t callMode)
|
||||
void handleTransitions()
|
||||
{
|
||||
//handle still pending interface update
|
||||
updateInterfaces(interfaceUpdateCallMode);
|
||||
if (interfaceUpdateCallMode && millis() - lastInterfaceUpdate > INTERFACE_UPDATE_COOLDOWN) updateInterfaces(interfaceUpdateCallMode);
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
if (doPublishMqtt) publishMqtt();
|
||||
#endif
|
||||
@ -189,7 +187,7 @@ void handleTransitions()
|
||||
if (transitionActive && transitionDelayTemp > 0)
|
||||
{
|
||||
float tper = (millis() - transitionStartTime)/(float)transitionDelayTemp;
|
||||
if (tper >= 1.0f)
|
||||
if (tper >= 1.0)
|
||||
{
|
||||
strip.setTransitionMode(false);
|
||||
transitionActive = false;
|
||||
|
@ -25,7 +25,7 @@ bool parseLx(int lxValue, byte* rgbw)
|
||||
float tmpBri = floor((lxValue - 200000000) / 10000); ;
|
||||
uint16_t ct = (lxValue - 200000000) - (((uint8_t)tmpBri) * 10000);
|
||||
|
||||
tmpBri *= 2.55f;
|
||||
tmpBri *= 2.55;
|
||||
tmpBri = constrain(tmpBri, 0, 255);
|
||||
|
||||
colorKtoRGB(ct, rgbw);
|
||||
|
@ -2,26 +2,10 @@
|
||||
#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
|
||||
|
||||
/*
|
||||
* Acquires time from NTP server
|
||||
*/
|
||||
//#define WLED_DEBUG_NTP
|
||||
#define NTP_SYNC_INTERVAL 42000UL //Get fresh NTP time about twice per day
|
||||
|
||||
Timezone* tz;
|
||||
|
||||
@ -195,7 +179,7 @@ void handleTime() {
|
||||
|
||||
void handleNetworkTime()
|
||||
{
|
||||
if (ntpEnabled && ntpConnected && millis() - ntpLastSyncTime > (1000*NTP_SYNC_INTERVAL) && WLED_CONNECTED)
|
||||
if (ntpEnabled && ntpConnected && millis() - ntpLastSyncTime > (1000UL*ntpSyncInterval) && WLED_CONNECTED)
|
||||
{
|
||||
if (millis() - ntpPacketSentTime > 10000)
|
||||
{
|
||||
@ -427,8 +411,8 @@ int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunse
|
||||
//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 N = N1 - (N2 * N3) + day - 30.0f;
|
||||
float N3 = (1 + floor_t((year - 4 * floor_t(year / 4) + 2) / 3));
|
||||
float N = N1 - (N2 * N3) + day - 30;
|
||||
|
||||
//2. convert the longitude to hour value and calculate an approximate time
|
||||
float lngHour = lon / 15.0f;
|
||||
@ -438,37 +422,37 @@ int getSunriseUTC(int year, int month, int day, float lat, float lon, bool sunse
|
||||
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));
|
||||
if ((cosH > 1.0f) && !sunset) return 0; // the sun never rises on this location (on the specified date)
|
||||
if ((cosH < -1.0f) && sunset) return 0; // the sun never sets on this location (on the specified date)
|
||||
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 && !sunset) return 0; // the sun never rises on this location (on the specified date)
|
||||
if (cosH < -1 && sunset) return 0; // 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;
|
||||
|
@ -112,7 +112,7 @@ int16_t loadPlaylist(JsonObject playlistObj, byte presetId) {
|
||||
if (playlistEndPreset == 255 && currentPreset > 0) playlistEndPreset = currentPreset;
|
||||
if (playlistEndPreset > 250) playlistEndPreset = 0;
|
||||
shuffle = shuffle || playlistObj["r"];
|
||||
if (shuffle) playlistOptions |= PL_OPTION_SHUFFLE;
|
||||
if (shuffle) playlistOptions += PL_OPTION_SHUFFLE;
|
||||
|
||||
currentPlaylist = presetId;
|
||||
DEBUG_PRINTLN(F("Playlist loaded."));
|
||||
@ -156,7 +156,7 @@ void serializePlaylist(JsonObject sObj) {
|
||||
JsonArray ps = playlist.createNestedArray("ps");
|
||||
JsonArray dur = playlist.createNestedArray("dur");
|
||||
JsonArray transition = playlist.createNestedArray(F("transition"));
|
||||
playlist[F("repeat")] = (playlistIndex < 0 && playlistRepeat > 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
|
||||
playlist[F("repeat")] = (playlistIndex < 0) ? playlistRepeat - 1 : playlistRepeat; // remove added repetition count (if not yet running)
|
||||
playlist["end"] = playlistEndPreset;
|
||||
playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE;
|
||||
for (int i=0; i<playlistLen; i++) {
|
||||
|
@ -347,14 +347,14 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
|
||||
#ifdef WLED_ENABLE_MQTT
|
||||
mqttEnabled = request->hasArg(F("MQ"));
|
||||
strlcpy(mqttServer, request->arg(F("MS")).c_str(), MQTT_MAX_SERVER_LEN+1);
|
||||
strlcpy(mqttServer, request->arg(F("MS")).c_str(), 33);
|
||||
t = request->arg(F("MQPORT")).toInt();
|
||||
if (t > 0) mqttPort = t;
|
||||
strlcpy(mqttUser, request->arg(F("MQUSER")).c_str(), 41);
|
||||
if (!isAsterisksOnly(request->arg(F("MQPASS")).c_str(), 41)) strlcpy(mqttPass, request->arg(F("MQPASS")).c_str(), 65);
|
||||
strlcpy(mqttClientID, request->arg(F("MQCID")).c_str(), 41);
|
||||
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), MQTT_MAX_TOPIC_LEN+1);
|
||||
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), MQTT_MAX_TOPIC_LEN+1);
|
||||
strlcpy(mqttDeviceTopic, request->arg(F("MD")).c_str(), 33);
|
||||
strlcpy(mqttGroupTopic, request->arg(F("MG")).c_str(), 33);
|
||||
buttonPublishMqtt = request->hasArg(F("BM"));
|
||||
retainMqttMsg = request->hasArg(F("RT"));
|
||||
#endif
|
||||
@ -390,6 +390,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
ntpEnabled = request->hasArg(F("NT"));
|
||||
strlcpy(ntpServerName, request->arg(F("NS")).c_str(), 33);
|
||||
useAMPM = !request->hasArg(F("CF"));
|
||||
ntpSyncInterval = min(86400U, max(10800U, (unsigned int)request->arg(F("NP")).toInt()));
|
||||
currentTimezone = request->arg(F("TZ")).toInt();
|
||||
utcOffsetSecs = request->arg(F("UO")).toInt();
|
||||
|
||||
|
@ -177,10 +177,6 @@
|
||||
#include "../usermods/boblight/boblight.h"
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_INTERNAL_TEMPERATURE
|
||||
#include "../usermods/Internal_Temperature_v2/usermod_internal_temperature.h"
|
||||
#endif
|
||||
|
||||
#if defined(WLED_USE_SD_MMC) || defined(WLED_USE_SD_SPI)
|
||||
// This include of SD.h and SD_MMC.h must happen here, else they won't be
|
||||
// resolved correctly (when included in mod's header only)
|
||||
@ -369,8 +365,4 @@ void registerUsermods()
|
||||
#ifdef USERMOD_SHT
|
||||
usermods.add(new ShtUsermod());
|
||||
#endif
|
||||
|
||||
#ifdef USERMOD_INTERNAL_TEMPERATURE
|
||||
usermods.add(new InternalTemperatureUsermod());
|
||||
#endif
|
||||
}
|
||||
|
@ -148,14 +148,8 @@ bool oappendi(int i)
|
||||
bool oappend(const char* txt)
|
||||
{
|
||||
uint16_t len = strlen(txt);
|
||||
if (olen + len >= SETTINGS_STACK_BUF_SIZE) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("oappend() buffer overflow. Cannnot append "));
|
||||
DEBUG_PRINT(len); DEBUG_PRINT(F(" bytes \t\""));
|
||||
DEBUG_PRINT(txt); DEBUG_PRINTLN(F("\""));
|
||||
#endif
|
||||
if (olen + len >= SETTINGS_STACK_BUF_SIZE)
|
||||
return false; // buffer full
|
||||
}
|
||||
strcpy(obuf + olen, txt);
|
||||
olen += len;
|
||||
return true;
|
||||
@ -239,8 +233,7 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
|
||||
if (mode < strip.getModeCount()) {
|
||||
char lineBuffer[256];
|
||||
//strcpy_P(lineBuffer, (const char*)pgm_read_dword(&(WS2812FX::_modeData[mode])));
|
||||
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
|
||||
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||
strcpy_P(lineBuffer, strip.getModeData(mode));
|
||||
size_t len = strlen(lineBuffer);
|
||||
size_t j = 0;
|
||||
for (; j < maxLen && j < len; j++) {
|
||||
@ -252,12 +245,6 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
|
||||
} else return 0;
|
||||
}
|
||||
|
||||
if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) {
|
||||
snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode);
|
||||
dest[maxLen-1] = '\0';
|
||||
return strlen(dest);
|
||||
}
|
||||
|
||||
uint8_t qComma = 0;
|
||||
bool insideQuotes = false;
|
||||
uint8_t printedChars = 0;
|
||||
@ -368,9 +355,9 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
|
||||
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
|
||||
{
|
||||
if (mode < strip.getModeCount()) {
|
||||
char lineBuffer[256];
|
||||
strncpy_P(lineBuffer, strip.getModeData(mode), sizeof(lineBuffer)/sizeof(char)-1);
|
||||
lineBuffer[sizeof(lineBuffer)/sizeof(char)-1] = '\0'; // terminate string
|
||||
char lineBuffer[128] = "";
|
||||
strncpy_P(lineBuffer, strip.getModeData(mode), 127);
|
||||
lineBuffer[127] = '\0'; // terminate string
|
||||
if (lineBuffer[0] != 0) {
|
||||
char* startPtr = strrchr(lineBuffer, ';'); // last ";" in FX data
|
||||
if (!startPtr) return -1;
|
||||
@ -520,7 +507,7 @@ um_data_t* simulateSound(uint8_t simulationId)
|
||||
maxVol = 31; // this gets feedback fro UI
|
||||
binNum = 8; // this gets feedback fro UI
|
||||
volumeRaw = volumeSmth;
|
||||
my_magnitude = 10000.0f / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ???
|
||||
my_magnitude = 10000.0 / 8.0f; //no idea if 10000 is a good value for FFT_Magnitude ???
|
||||
if (volumeSmth < 1 ) my_magnitude = 0.001f; // noise gate closed - mute
|
||||
|
||||
return um_data;
|
||||
|
@ -195,15 +195,12 @@ void WLED::loop()
|
||||
|
||||
#if WLED_WATCHDOG_TIMEOUT > 0
|
||||
// we finished our mainloop, reset the watchdog timer
|
||||
static unsigned long lastWDTFeed = 0;
|
||||
if (!strip.isUpdating() || millis() - lastWDTFeed > (WLED_WATCHDOG_TIMEOUT*500)) {
|
||||
if (!strip.isUpdating())
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
esp_task_wdt_reset();
|
||||
#else
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
lastWDTFeed = millis();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (doReboot && (!doInitBusses || !doSerializeConfig)) // if busses have to be inited & saved, wait until next iteration
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2310130
|
||||
#define VERSION 2308110
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@ -429,18 +429,12 @@ WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count numbe
|
||||
// mqtt
|
||||
WLED_GLOBAL unsigned long lastMqttReconnectAttempt _INIT(0); // used for other periodic tasks too
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
#ifndef MQTT_MAX_TOPIC_LEN
|
||||
#define MQTT_MAX_TOPIC_LEN 32
|
||||
#endif
|
||||
#ifndef MQTT_MAX_SERVER_LEN
|
||||
#define MQTT_MAX_SERVER_LEN 32
|
||||
#endif
|
||||
WLED_GLOBAL AsyncMqttClient *mqtt _INIT(NULL);
|
||||
WLED_GLOBAL bool mqttEnabled _INIT(false);
|
||||
WLED_GLOBAL char mqttStatusTopic[40] _INIT(""); // this must be global because of async handlers
|
||||
WLED_GLOBAL char mqttDeviceTopic[MQTT_MAX_TOPIC_LEN+1] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
|
||||
WLED_GLOBAL char mqttGroupTopic[MQTT_MAX_TOPIC_LEN+1] _INIT("wled/all"); // second MQTT topic (for example to group devices)
|
||||
WLED_GLOBAL char mqttServer[MQTT_MAX_SERVER_LEN+1] _INIT(""); // both domains and IPs should work (no SSL)
|
||||
WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
|
||||
WLED_GLOBAL char mqttGroupTopic[33] _INIT("wled/all"); // second MQTT topic (for example to group devices)
|
||||
WLED_GLOBAL char mqttServer[33] _INIT(""); // both domains and IPs should work (no SSL)
|
||||
WLED_GLOBAL char mqttUser[41] _INIT(""); // optional: username for MQTT auth
|
||||
WLED_GLOBAL char mqttPass[65] _INIT(""); // optional: password for MQTT auth
|
||||
WLED_GLOBAL char mqttClientID[41] _INIT(""); // override the client ID
|
||||
@ -645,13 +639,14 @@ WLED_GLOBAL DNSServer dnsServer;
|
||||
// network time
|
||||
WLED_GLOBAL bool ntpConnected _INIT(false);
|
||||
WLED_GLOBAL time_t localTime _INIT(0);
|
||||
WLED_GLOBAL uint32_t ntpSyncInterval _INIT(43200U);
|
||||
WLED_GLOBAL unsigned long ntpLastSyncTime _INIT(999000000L);
|
||||
WLED_GLOBAL unsigned long ntpPacketSentTime _INIT(999000000L);
|
||||
WLED_GLOBAL IPAddress ntpServerIP;
|
||||
WLED_GLOBAL uint16_t ntpLocalPort _INIT(2390);
|
||||
WLED_GLOBAL uint16_t rolloverMillis _INIT(0);
|
||||
WLED_GLOBAL float longitude _INIT(0.0);
|
||||
WLED_GLOBAL float latitude _INIT(0.0);
|
||||
WLED_GLOBAL float longitude _INIT(0.0f);
|
||||
WLED_GLOBAL float latitude _INIT(0.0f);
|
||||
WLED_GLOBAL time_t sunrise _INIT(0);
|
||||
WLED_GLOBAL time_t sunset _INIT(0);
|
||||
WLED_GLOBAL Toki toki _INIT(Toki());
|
||||
|
@ -41,7 +41,7 @@ float sin_t(float x) {
|
||||
|
||||
float tan_t(float x) {
|
||||
float c = cos_t(x);
|
||||
if (c==0.0f) return 0;
|
||||
if (c==0.0) return 0;
|
||||
float res = sin_t(x) / c;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
Serial.printf("tan: %f,%f,%f,(%f)\n",x,res,tan(x),res-tan(x));
|
||||
@ -54,14 +54,14 @@ float tan_t(float x) {
|
||||
float acos_t(float x) {
|
||||
float negate = float(x < 0);
|
||||
float xabs = std::abs(x);
|
||||
float ret = -0.0187293f;
|
||||
float ret = -0.0187293;
|
||||
ret = ret * xabs;
|
||||
ret = ret + 0.0742610f;
|
||||
ret = ret + 0.0742610;
|
||||
ret = ret * xabs;
|
||||
ret = ret - 0.2121144f;
|
||||
ret = ret - 0.2121144;
|
||||
ret = ret * xabs;
|
||||
ret = ret + HALF_PI;
|
||||
ret = ret * sqrt(1.0f-xabs);
|
||||
ret = ret * sqrt(1.0-xabs);
|
||||
ret = ret - 2 * negate * ret;
|
||||
float res = negate * PI + ret;
|
||||
#ifdef WLED_DEBUG_MATH
|
||||
|
@ -478,7 +478,6 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
|
||||
if (subPage == SUBPAGE_SYNC)
|
||||
{
|
||||
char nS[32];
|
||||
sappend('v',SET_F("UP"),udpPort);
|
||||
sappend('v',SET_F("U2"),udpPort2);
|
||||
sappend('v',SET_F("GS"),syncGroups);
|
||||
@ -535,9 +534,6 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappends('s',SET_F("MG"),mqttGroupTopic);
|
||||
sappend('c',SET_F("BM"),buttonPublishMqtt);
|
||||
sappend('c',SET_F("RT"),retainMqttMsg);
|
||||
oappend(SET_F("d.Sf.MD.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
|
||||
oappend(SET_F("d.Sf.MG.maxlength=")); oappend(itoa(MQTT_MAX_TOPIC_LEN,nS,10)); oappend(SET_F(";"));
|
||||
oappend(SET_F("d.Sf.MS.maxlength=")); oappend(itoa(MQTT_MAX_SERVER_LEN,nS,10)); oappend(SET_F(";"));
|
||||
#else
|
||||
oappend(SET_F("toggle('MQTT');")); // hide MQTT settings
|
||||
#endif
|
||||
@ -578,6 +574,15 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappend('c',SET_F("NT"),ntpEnabled);
|
||||
sappends('s',SET_F("NS"),ntpServerName);
|
||||
sappend('c',SET_F("CF"),!useAMPM);
|
||||
int syncIntervalIndex;
|
||||
switch (ntpSyncInterval) {
|
||||
case 86400: syncIntervalIndex = 0; break;
|
||||
case 21600: syncIntervalIndex = 2; break;
|
||||
case 14400: syncIntervalIndex = 3; break;
|
||||
case 10800: syncIntervalIndex = 4; break;
|
||||
default : syncIntervalIndex = 1; break;
|
||||
}
|
||||
sappend('i',SET_F("NP"),syncIntervalIndex);
|
||||
sappend('i',SET_F("TZ"),currentTimezone);
|
||||
sappend('v',SET_F("UO"),utcOffsetSecs);
|
||||
char tm[32];
|
||||
@ -587,7 +592,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
sappends('s',SET_F("LT"),tm);
|
||||
getTimeString(tm);
|
||||
sappends('m',SET_F("(\"times\")[0]"),tm);
|
||||
if ((int)(longitude*10.0f) || (int)(latitude*10.0f)) {
|
||||
if ((int)(longitude*10.) || (int)(latitude*10.)) {
|
||||
sprintf_P(tm, PSTR("Sunrise: %02d:%02d Sunset: %02d:%02d"), hour(sunrise), minute(sunrise), hour(sunset), minute(sunset));
|
||||
sappends('m',SET_F("(\"times\")[1]"),tm);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user