mirror of
https://github.com/wled/WLED.git
synced 2025-12-01 05:37:41 +00:00
Compare commits
14 Commits
copilot/ad
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28d6e347ec | ||
|
|
1b1f5811de | ||
|
|
ff184c1f41 | ||
|
|
75a7ed132a | ||
|
|
fc25eb2c90 | ||
|
|
dc5732a5f5 | ||
|
|
a9811c2020 | ||
|
|
33411f0300 | ||
|
|
8bc434b614 | ||
|
|
ce6577ee35 | ||
|
|
579021f5fc | ||
|
|
eb87fbf8e4 | ||
|
|
c534328cc5 | ||
|
|
28d8a1c25c |
@@ -38,6 +38,11 @@ const wledBanner = `
|
|||||||
\t\t\x1b[36m build script for web UI
|
\t\t\x1b[36m build script for web UI
|
||||||
\x1b[0m`;
|
\x1b[0m`;
|
||||||
|
|
||||||
|
// Generate build timestamp as UNIX timestamp (seconds since epoch)
|
||||||
|
function generateBuildTime() {
|
||||||
|
return Math.floor(Date.now() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
const singleHeader = `/*
|
const singleHeader = `/*
|
||||||
* Binary array for the Web UI.
|
* Binary array for the Web UI.
|
||||||
* gzip is used for smaller size and improved speeds.
|
* gzip is used for smaller size and improved speeds.
|
||||||
@@ -45,6 +50,9 @@ const singleHeader = `/*
|
|||||||
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
* Please see https://kno.wled.ge/advanced/custom-features/#changing-web-ui
|
||||||
* to find out how to easily modify the web UI source!
|
* to find out how to easily modify the web UI source!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Automatically generated build time for cache busting (UNIX timestamp)
|
||||||
|
#define WEB_BUILD_TIME ${generateBuildTime()}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -313,11 +313,11 @@ class MyExampleUsermod : public Usermod {
|
|||||||
yield();
|
yield();
|
||||||
// ignore certain button types as they may have other consequences
|
// ignore certain button types as they may have other consequences
|
||||||
if (!enabled
|
if (!enabled
|
||||||
|| buttonType[b] == BTN_TYPE_NONE
|
|| buttons[b].type == BTN_TYPE_NONE
|
||||||
|| buttonType[b] == BTN_TYPE_RESERVED
|
|| buttons[b].type == BTN_TYPE_RESERVED
|
||||||
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|
|| buttons[b].type == BTN_TYPE_PIR_SENSOR
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG
|
|| buttons[b].type == BTN_TYPE_ANALOG
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
|| buttons[b].type == BTN_TYPE_ANALOG_INVERTED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1544,7 +1544,7 @@ class AudioReactive : public Usermod {
|
|||||||
// better would be for AudioSource to implement getType()
|
// better would be for AudioSource to implement getType()
|
||||||
if (enabled
|
if (enabled
|
||||||
&& dmType == 0 && audioPin>=0
|
&& dmType == 0 && audioPin>=0
|
||||||
&& (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED)
|
&& (buttons[b].type == BTN_TYPE_ANALOG || buttons[b].type == BTN_TYPE_ANALOG_INVERTED)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -562,11 +562,11 @@ void MultiRelay::loop() {
|
|||||||
bool MultiRelay::handleButton(uint8_t b) {
|
bool MultiRelay::handleButton(uint8_t b) {
|
||||||
yield();
|
yield();
|
||||||
if (!enabled
|
if (!enabled
|
||||||
|| buttonType[b] == BTN_TYPE_NONE
|
|| buttons[b].type == BTN_TYPE_NONE
|
||||||
|| buttonType[b] == BTN_TYPE_RESERVED
|
|| buttons[b].type == BTN_TYPE_RESERVED
|
||||||
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|
|| buttons[b].type == BTN_TYPE_PIR_SENSOR
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG
|
|| buttons[b].type == BTN_TYPE_ANALOG
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
|| buttons[b].type == BTN_TYPE_ANALOG_INVERTED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,20 +581,20 @@ bool MultiRelay::handleButton(uint8_t b) {
|
|||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
//button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
//button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
||||||
if (buttonType[b] == BTN_TYPE_SWITCH) {
|
if (buttons[b].type == BTN_TYPE_SWITCH) {
|
||||||
//handleSwitch(b);
|
//handleSwitch(b);
|
||||||
if (buttonPressedBefore[b] != isButtonPressed(b)) {
|
if (buttons[b].pressedBefore != isButtonPressed(b)) {
|
||||||
buttonPressedTime[b] = now;
|
buttons[b].pressedTime = now;
|
||||||
buttonPressedBefore[b] = !buttonPressedBefore[b];
|
buttons[b].pressedBefore = !buttons[b].pressedBefore;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonLongPressed[b] == buttonPressedBefore[b]) return handled;
|
if (buttons[b].longPressed == buttons[b].pressedBefore) return handled;
|
||||||
|
|
||||||
if (now - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
if (now - buttons[b].pressedTime > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
||||||
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relay[i].button == b) {
|
if (_relay[i].button == b) {
|
||||||
switchRelay(i, buttonPressedBefore[b]);
|
switchRelay(i, buttons[b].pressedBefore);
|
||||||
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
|
buttons[b].longPressed = buttons[b].pressedBefore; //save the last "long term" switch state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -604,40 +604,40 @@ bool MultiRelay::handleButton(uint8_t b) {
|
|||||||
//momentary button logic
|
//momentary button logic
|
||||||
if (isButtonPressed(b)) { //pressed
|
if (isButtonPressed(b)) { //pressed
|
||||||
|
|
||||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
|
if (!buttons[b].pressedBefore) buttons[b].pressedTime = now;
|
||||||
buttonPressedBefore[b] = true;
|
buttons[b].pressedBefore = true;
|
||||||
|
|
||||||
if (now - buttonPressedTime[b] > 600) { //long press
|
if (now - buttons[b].pressedTime > 600) { //long press
|
||||||
//longPressAction(b); //not exposed
|
//longPressAction(b); //not exposed
|
||||||
//handled = false; //use if you want to pass to default behaviour
|
//handled = false; //use if you want to pass to default behaviour
|
||||||
buttonLongPressed[b] = true;
|
buttons[b].longPressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
} else if (!isButtonPressed(b) && buttons[b].pressedBefore) { //released
|
||||||
|
|
||||||
long dur = now - buttonPressedTime[b];
|
long dur = now - buttons[b].pressedTime;
|
||||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
||||||
buttonPressedBefore[b] = false;
|
buttons[b].pressedBefore = false;
|
||||||
return handled;
|
return handled;
|
||||||
} //too short "press", debounce
|
} //too short "press", debounce
|
||||||
bool doublePress = buttonWaitTime[b]; //did we have short press before?
|
bool doublePress = buttons[b].waitTime; //did we have short press before?
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
|
|
||||||
if (!buttonLongPressed[b]) { //short press
|
if (!buttons[b].longPressed) { //short press
|
||||||
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
||||||
if (doublePress) {
|
if (doublePress) {
|
||||||
//doublePressAction(b); //not exposed
|
//doublePressAction(b); //not exposed
|
||||||
//handled = false; //use if you want to pass to default behaviour
|
//handled = false; //use if you want to pass to default behaviour
|
||||||
} else {
|
} else {
|
||||||
buttonWaitTime[b] = now;
|
buttons[b].waitTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buttonPressedBefore[b] = false;
|
buttons[b].pressedBefore = false;
|
||||||
buttonLongPressed[b] = false;
|
buttons[b].longPressed = false;
|
||||||
}
|
}
|
||||||
// if 350ms elapsed since last press/release it is a short press
|
// if 350ms elapsed since last press/release it is a short press
|
||||||
if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) {
|
if (buttons[b].waitTime && now - buttons[b].waitTime > 350 && !buttons[b].pressedBefore) {
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
//shortPressAction(b); //not exposed
|
//shortPressAction(b); //not exposed
|
||||||
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relay[i].button == b) {
|
if (_relay[i].button == b) {
|
||||||
|
|||||||
@@ -461,11 +461,11 @@ class PixelsDiceTrayUsermod : public Usermod {
|
|||||||
#if USING_TFT_DISPLAY
|
#if USING_TFT_DISPLAY
|
||||||
bool handleButton(uint8_t b) override {
|
bool handleButton(uint8_t b) override {
|
||||||
if (!enabled || b > 1 // buttons 0,1 only
|
if (!enabled || b > 1 // buttons 0,1 only
|
||||||
|| buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_NONE ||
|
|| buttons[b].type == BTN_TYPE_SWITCH || buttons[b].type == BTN_TYPE_NONE ||
|
||||||
buttonType[b] == BTN_TYPE_RESERVED ||
|
buttons[b].type == BTN_TYPE_RESERVED ||
|
||||||
buttonType[b] == BTN_TYPE_PIR_SENSOR ||
|
buttons[b].type == BTN_TYPE_PIR_SENSOR ||
|
||||||
buttonType[b] == BTN_TYPE_ANALOG ||
|
buttons[b].type == BTN_TYPE_ANALOG ||
|
||||||
buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
buttons[b].type == BTN_TYPE_ANALOG_INVERTED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,43 +476,43 @@ class PixelsDiceTrayUsermod : public Usermod {
|
|||||||
static unsigned long buttonWaitTime[2] = {0};
|
static unsigned long buttonWaitTime[2] = {0};
|
||||||
|
|
||||||
//momentary button logic
|
//momentary button logic
|
||||||
if (!buttonLongPressed[b] && isButtonPressed(b)) { //pressed
|
if (!buttons[b].longPressed && isButtonPressed(b)) { //pressed
|
||||||
if (!buttonPressedBefore[b]) {
|
if (!buttons[b].pressedBefore) {
|
||||||
buttonPressedTime[b] = now;
|
buttons[b].pressedTime = now;
|
||||||
}
|
}
|
||||||
buttonPressedBefore[b] = true;
|
buttons[b].pressedBefore = true;
|
||||||
|
|
||||||
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
if (now - buttons[b].pressedTime > WLED_LONG_PRESS) { //long press
|
||||||
menu_ctrl.HandleButton(ButtonType::LONG, b);
|
menu_ctrl.HandleButton(ButtonType::LONG, b);
|
||||||
buttonLongPressed[b] = true;
|
buttons[b].longPressed = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (!isButtonPressed(b) && buttonPressedBefore[b]) { //released
|
} else if (!isButtonPressed(b) && buttons[b].pressedBefore) { //released
|
||||||
|
|
||||||
long dur = now - buttonPressedTime[b];
|
long dur = now - buttons[b].pressedTime;
|
||||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
if (dur < WLED_DEBOUNCE_THRESHOLD) {
|
||||||
buttonPressedBefore[b] = false;
|
buttons[b].pressedBefore = false;
|
||||||
return true;
|
return true;
|
||||||
} //too short "press", debounce
|
} //too short "press", debounce
|
||||||
|
|
||||||
bool doublePress = buttonWaitTime[b]; //did we have short press before?
|
bool doublePress = buttons[b].waitTime; //did we have short press before?
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
|
|
||||||
if (!buttonLongPressed[b]) { //short press
|
if (!buttons[b].longPressed) { //short press
|
||||||
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
// if this is second release within 350ms it is a double press (buttonWaitTime!=0)
|
||||||
if (doublePress) {
|
if (doublePress) {
|
||||||
menu_ctrl.HandleButton(ButtonType::DOUBLE, b);
|
menu_ctrl.HandleButton(ButtonType::DOUBLE, b);
|
||||||
} else {
|
} else {
|
||||||
buttonWaitTime[b] = now;
|
buttons[b].waitTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buttonPressedBefore[b] = false;
|
buttons[b].pressedBefore = false;
|
||||||
buttonLongPressed[b] = false;
|
buttons[b].longPressed = false;
|
||||||
}
|
}
|
||||||
// if 350ms elapsed since last press/release it is a short press
|
// if 350ms elapsed since last press/release it is a short press
|
||||||
if (buttonWaitTime[b] && now - buttonWaitTime[b] > WLED_DOUBLE_PRESS &&
|
if (buttons[b].waitTime && now - buttons[b].waitTime > WLED_DOUBLE_PRESS &&
|
||||||
!buttonPressedBefore[b]) {
|
!buttons[b].pressedBefore) {
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
menu_ctrl.HandleButton(ButtonType::SINGLE, b);
|
menu_ctrl.HandleButton(ButtonType::SINGLE, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -749,12 +749,12 @@ bool FourLineDisplayUsermod::handleButton(uint8_t b) {
|
|||||||
yield();
|
yield();
|
||||||
if (!enabled
|
if (!enabled
|
||||||
|| b // button 0 only
|
|| b // button 0 only
|
||||||
|| buttonType[b] == BTN_TYPE_SWITCH
|
|| buttons[b].type == BTN_TYPE_SWITCH
|
||||||
|| buttonType[b] == BTN_TYPE_NONE
|
|| buttons[b].type == BTN_TYPE_NONE
|
||||||
|| buttonType[b] == BTN_TYPE_RESERVED
|
|| buttons[b].type == BTN_TYPE_RESERVED
|
||||||
|| buttonType[b] == BTN_TYPE_PIR_SENSOR
|
|| buttons[b].type == BTN_TYPE_PIR_SENSOR
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG
|
|| buttons[b].type == BTN_TYPE_ANALOG
|
||||||
|| buttonType[b] == BTN_TYPE_ANALOG_INVERTED) {
|
|| buttons[b].type == BTN_TYPE_ANALOG_INVERTED) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ static bool buttonBriDirection = false; // true: increase brightness, false: dec
|
|||||||
|
|
||||||
void shortPressAction(uint8_t b)
|
void shortPressAction(uint8_t b)
|
||||||
{
|
{
|
||||||
if (!macroButton[b]) {
|
if (!buttons[b].macroButton) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case 0: toggleOnOff(); stateUpdated(CALL_MODE_BUTTON); break;
|
case 0: toggleOnOff(); stateUpdated(CALL_MODE_BUTTON); break;
|
||||||
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
|
case 1: ++effectCurrent %= strip.getModeCount(); stateChanged = true; colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
|
applyPreset(buttons[b].macroButton, CALL_MODE_BUTTON_PRESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
@@ -38,7 +38,7 @@ void shortPressAction(uint8_t b)
|
|||||||
|
|
||||||
void longPressAction(uint8_t b)
|
void longPressAction(uint8_t b)
|
||||||
{
|
{
|
||||||
if (!macroLongPress[b]) {
|
if (!buttons[b].macroLongPress) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break;
|
case 0: setRandomColor(colPri); colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
case 1:
|
case 1:
|
||||||
@@ -52,11 +52,11 @@ void longPressAction(uint8_t b)
|
|||||||
else bri -= WLED_LONG_BRI_STEPS;
|
else bri -= WLED_LONG_BRI_STEPS;
|
||||||
}
|
}
|
||||||
stateUpdated(CALL_MODE_BUTTON);
|
stateUpdated(CALL_MODE_BUTTON);
|
||||||
buttonPressedTime[b] = millis();
|
buttons[b].pressedTime = millis();
|
||||||
break; // repeatable action
|
break; // repeatable action
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
applyPreset(buttons[b].macroLongPress, CALL_MODE_BUTTON_PRESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
@@ -71,13 +71,13 @@ void longPressAction(uint8_t b)
|
|||||||
|
|
||||||
void doublePressAction(uint8_t b)
|
void doublePressAction(uint8_t b)
|
||||||
{
|
{
|
||||||
if (!macroDoublePress[b]) {
|
if (!buttons[b].macroDoublePress) {
|
||||||
switch (b) {
|
switch (b) {
|
||||||
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
//case 0: toggleOnOff(); colorUpdated(CALL_MODE_BUTTON); break; //instant short press on button 0 if no macro set
|
||||||
case 1: ++effectPalette %= getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
case 1: ++effectPalette %= getPaletteCount(); colorUpdated(CALL_MODE_BUTTON); break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
applyPreset(macroDoublePress[b], CALL_MODE_BUTTON_PRESET);
|
applyPreset(buttons[b].macroDoublePress, CALL_MODE_BUTTON_PRESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_MQTT
|
#ifndef WLED_DISABLE_MQTT
|
||||||
@@ -92,10 +92,10 @@ void doublePressAction(uint8_t b)
|
|||||||
|
|
||||||
bool isButtonPressed(uint8_t b)
|
bool isButtonPressed(uint8_t b)
|
||||||
{
|
{
|
||||||
if (btnPin[b]<0) return false;
|
if (buttons[b].pin < 0) return false;
|
||||||
unsigned pin = btnPin[b];
|
unsigned pin = buttons[b].pin;
|
||||||
|
|
||||||
switch (buttonType[b]) {
|
switch (buttons[b].type) {
|
||||||
case BTN_TYPE_NONE:
|
case BTN_TYPE_NONE:
|
||||||
case BTN_TYPE_RESERVED:
|
case BTN_TYPE_RESERVED:
|
||||||
break;
|
break;
|
||||||
@@ -113,7 +113,7 @@ bool isButtonPressed(uint8_t b)
|
|||||||
#ifdef SOC_TOUCH_VERSION_2 //ESP32 S2 and S3 provide a function to check touch state (state is updated in interrupt)
|
#ifdef SOC_TOUCH_VERSION_2 //ESP32 S2 and S3 provide a function to check touch state (state is updated in interrupt)
|
||||||
if (touchInterruptGetLastStatus(pin)) return true;
|
if (touchInterruptGetLastStatus(pin)) return true;
|
||||||
#else
|
#else
|
||||||
if (digitalPinToTouchChannel(btnPin[b]) >= 0 && touchRead(pin) <= touchThreshold) return true;
|
if (digitalPinToTouchChannel(pin) >= 0 && touchRead(pin) <= touchThreshold) return true;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@@ -124,25 +124,25 @@ bool isButtonPressed(uint8_t b)
|
|||||||
void handleSwitch(uint8_t b)
|
void handleSwitch(uint8_t b)
|
||||||
{
|
{
|
||||||
// isButtonPressed() handles inverted/noninverted logic
|
// isButtonPressed() handles inverted/noninverted logic
|
||||||
if (buttonPressedBefore[b] != isButtonPressed(b)) {
|
if (buttons[b].pressedBefore != isButtonPressed(b)) {
|
||||||
DEBUG_PRINTF_P(PSTR("Switch: State changed %u\n"), b);
|
DEBUG_PRINTF_P(PSTR("Switch: State changed %u\n"), b);
|
||||||
buttonPressedTime[b] = millis();
|
buttons[b].pressedTime = millis();
|
||||||
buttonPressedBefore[b] = !buttonPressedBefore[b];
|
buttons[b].pressedBefore = !buttons[b].pressedBefore; // toggle pressed state
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
|
if (buttons[b].longPressed == buttons[b].pressedBefore) return;
|
||||||
|
|
||||||
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
if (millis() - buttons[b].pressedTime > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
|
||||||
DEBUG_PRINTF_P(PSTR("Switch: Activating %u\n"), b);
|
DEBUG_PRINTF_P(PSTR("Switch: Activating %u\n"), b);
|
||||||
if (!buttonPressedBefore[b]) { // on -> off
|
if (!buttons[b].pressedBefore) { // on -> off
|
||||||
DEBUG_PRINTF_P(PSTR("Switch: On -> Off (%u)\n"), b);
|
DEBUG_PRINTF_P(PSTR("Switch: On -> Off (%u)\n"), b);
|
||||||
if (macroButton[b]) applyPreset(macroButton[b], CALL_MODE_BUTTON_PRESET);
|
if (buttons[b].macroButton) applyPreset(buttons[b].macroButton, CALL_MODE_BUTTON_PRESET);
|
||||||
else { //turn on
|
else { //turn on
|
||||||
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
if (!bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
||||||
}
|
}
|
||||||
} else { // off -> on
|
} else { // off -> on
|
||||||
DEBUG_PRINTF_P(PSTR("Switch: Off -> On (%u)\n"), b);
|
DEBUG_PRINTF_P(PSTR("Switch: Off -> On (%u)\n"), b);
|
||||||
if (macroLongPress[b]) applyPreset(macroLongPress[b], CALL_MODE_BUTTON_PRESET);
|
if (buttons[b].macroLongPress) applyPreset(buttons[b].macroLongPress, CALL_MODE_BUTTON_PRESET);
|
||||||
else { //turn off
|
else { //turn off
|
||||||
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
if (bri) {toggleOnOff(); stateUpdated(CALL_MODE_BUTTON);}
|
||||||
}
|
}
|
||||||
@@ -152,13 +152,13 @@ void handleSwitch(uint8_t b)
|
|||||||
// publish MQTT message
|
// publish MQTT message
|
||||||
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
if (buttonPublishMqtt && WLED_MQTT_CONNECTED) {
|
||||||
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
char subuf[MQTT_MAX_TOPIC_LEN + 32];
|
||||||
if (buttonType[b] == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b);
|
if (buttons[b].type == BTN_TYPE_PIR_SENSOR) sprintf_P(subuf, PSTR("%s/motion/%d"), mqttDeviceTopic, (int)b);
|
||||||
else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
else sprintf_P(subuf, _mqtt_topic_button, mqttDeviceTopic, (int)b);
|
||||||
mqtt->publish(subuf, 0, false, !buttonPressedBefore[b] ? "off" : "on");
|
mqtt->publish(subuf, 0, false, !buttons[b].pressedBefore ? "off" : "on");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
|
buttons[b].longPressed = buttons[b].pressedBefore; //save the last "long term" switch state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,17 +178,17 @@ void handleAnalog(uint8_t b)
|
|||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
|
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
|
||||||
#else
|
#else
|
||||||
if ((btnPin[b] < 0) /*|| (digitalPinToAnalogChannel(btnPin[b]) < 0)*/) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise
|
if ((buttons[b].pin < 0) /*|| (digitalPinToAnalogChannel(buttons[b].pin) < 0)*/) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise
|
||||||
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
|
rawReading = analogRead(buttons[b].pin); // collect at full 12bit resolution
|
||||||
#endif
|
#endif
|
||||||
yield(); // keep WiFi task running - analog read may take several millis on ESP8266
|
yield(); // keep WiFi task running - analog read may take several millis on ESP8266
|
||||||
|
|
||||||
filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255]
|
filteredReading[b] += POT_SMOOTHING * ((float(rawReading) / 16.0f) - filteredReading[b]); // filter raw input, and scale to [0..255]
|
||||||
unsigned aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit
|
unsigned aRead = max(min(int(filteredReading[b]), 255), 0); // squash into 8bit
|
||||||
if(aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used
|
if (aRead <= POT_SENSITIVITY) aRead = 0; // make sure that 0 and 255 are used
|
||||||
if(aRead >= 255-POT_SENSITIVITY) aRead = 255;
|
if (aRead >= 255-POT_SENSITIVITY) aRead = 255;
|
||||||
|
|
||||||
if (buttonType[b] == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead;
|
if (buttons[b].type == BTN_TYPE_ANALOG_INVERTED) aRead = 255 - aRead;
|
||||||
|
|
||||||
// remove noise & reduce frequency of UI updates
|
// remove noise & reduce frequency of UI updates
|
||||||
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
|
if (abs(int(aRead) - int(oldRead[b])) <= POT_SENSITIVITY) return; // no significant change in reading
|
||||||
@@ -206,10 +206,10 @@ void handleAnalog(uint8_t b)
|
|||||||
oldRead[b] = aRead;
|
oldRead[b] = aRead;
|
||||||
|
|
||||||
// if no macro for "short press" and "long press" is defined use brightness control
|
// if no macro for "short press" and "long press" is defined use brightness control
|
||||||
if (!macroButton[b] && !macroLongPress[b]) {
|
if (!buttons[b].macroButton && !buttons[b].macroLongPress) {
|
||||||
DEBUG_PRINTF_P(PSTR("Analog: Action = %u\n"), macroDoublePress[b]);
|
DEBUG_PRINTF_P(PSTR("Analog: Action = %u\n"), buttons[b].macroDoublePress);
|
||||||
// if "double press" macro defines which option to change
|
// if "double press" macro defines which option to change
|
||||||
if (macroDoublePress[b] >= 250) {
|
if (buttons[b].macroDoublePress >= 250) {
|
||||||
// global brightness
|
// global brightness
|
||||||
if (aRead == 0) {
|
if (aRead == 0) {
|
||||||
briLast = bri;
|
briLast = bri;
|
||||||
@@ -218,27 +218,30 @@ void handleAnalog(uint8_t b)
|
|||||||
if (bri == 0) strip.restartRuntime();
|
if (bri == 0) strip.restartRuntime();
|
||||||
bri = aRead;
|
bri = aRead;
|
||||||
}
|
}
|
||||||
} else if (macroDoublePress[b] == 249) {
|
} else if (buttons[b].macroDoublePress == 249) {
|
||||||
// effect speed
|
// effect speed
|
||||||
effectSpeed = aRead;
|
effectSpeed = aRead;
|
||||||
} else if (macroDoublePress[b] == 248) {
|
} else if (buttons[b].macroDoublePress == 248) {
|
||||||
// effect intensity
|
// effect intensity
|
||||||
effectIntensity = aRead;
|
effectIntensity = aRead;
|
||||||
} else if (macroDoublePress[b] == 247) {
|
} else if (buttons[b].macroDoublePress == 247) {
|
||||||
// selected palette
|
// selected palette
|
||||||
effectPalette = map(aRead, 0, 252, 0, getPaletteCount()-1);
|
effectPalette = map(aRead, 0, 252, 0, getPaletteCount()-1);
|
||||||
effectPalette = constrain(effectPalette, 0, getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
effectPalette = constrain(effectPalette, 0, getPaletteCount()-1); // map is allowed to "overshoot", so we need to contrain the result
|
||||||
} else if (macroDoublePress[b] == 200) {
|
} else if (buttons[b].macroDoublePress == 200) {
|
||||||
// primary color, hue, full saturation
|
// primary color, hue, full saturation
|
||||||
colorHStoRGB(aRead*256,255,colPri);
|
colorHStoRGB(aRead*256, 255, colPri);
|
||||||
} else {
|
} else {
|
||||||
// otherwise use "double press" for segment selection
|
// otherwise use "double press" for segment selection
|
||||||
Segment& seg = strip.getSegment(macroDoublePress[b]);
|
Segment& seg = strip.getSegment(buttons[b].macroDoublePress);
|
||||||
if (aRead == 0) {
|
if (aRead == 0) {
|
||||||
seg.setOption(SEG_OPTION_ON, false); // off (use transition)
|
seg.on = false; // do not use transition
|
||||||
|
//seg.setOption(SEG_OPTION_ON, false); // off (use transition)
|
||||||
} else {
|
} else {
|
||||||
seg.setOpacity(aRead);
|
seg.opacity = aRead; // set brightness (opacity) of segment
|
||||||
seg.setOption(SEG_OPTION_ON, true); // on (use transition)
|
seg.on = true;
|
||||||
|
//seg.setOpacity(aRead);
|
||||||
|
//seg.setOption(SEG_OPTION_ON, true); // on (use transition)
|
||||||
}
|
}
|
||||||
// this will notify clients of update (websockets,mqtt,etc)
|
// this will notify clients of update (websockets,mqtt,etc)
|
||||||
updateInterfaces(CALL_MODE_BUTTON);
|
updateInterfaces(CALL_MODE_BUTTON);
|
||||||
@@ -261,16 +264,16 @@ void handleButton()
|
|||||||
if (strip.isUpdating() && (now - lastRun < ANALOG_BTN_READ_CYCLE+1)) return; // don't interfere with strip update (unless strip is updating continuously, e.g. very long strips)
|
if (strip.isUpdating() && (now - lastRun < ANALOG_BTN_READ_CYCLE+1)) return; // don't interfere with strip update (unless strip is updating continuously, e.g. very long strips)
|
||||||
lastRun = now;
|
lastRun = now;
|
||||||
|
|
||||||
for (unsigned b=0; b<WLED_MAX_BUTTONS; b++) {
|
for (unsigned b = 0; b < buttons.size(); b++) {
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
if ((btnPin[b]<0 && !(buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED)) || buttonType[b] == BTN_TYPE_NONE) continue;
|
if ((buttons[b].pin < 0 && !(buttons[b].type == BTN_TYPE_ANALOG || buttons[b].type == BTN_TYPE_ANALOG_INVERTED)) || buttons[b].type == BTN_TYPE_NONE) continue;
|
||||||
#else
|
#else
|
||||||
if (btnPin[b]<0 || buttonType[b] == BTN_TYPE_NONE) continue;
|
if (buttons[b].pin < 0 || buttons[b].type == BTN_TYPE_NONE) continue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (UsermodManager::handleButton(b)) continue; // did usermod handle buttons
|
if (UsermodManager::handleButton(b)) continue; // did usermod handle buttons
|
||||||
|
|
||||||
if (buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer
|
if (buttons[b].type == BTN_TYPE_ANALOG || buttons[b].type == BTN_TYPE_ANALOG_INVERTED) { // button is not a button but a potentiometer
|
||||||
if (now - lastAnalogRead > ANALOG_BTN_READ_CYCLE) {
|
if (now - lastAnalogRead > ANALOG_BTN_READ_CYCLE) {
|
||||||
handleAnalog(b);
|
handleAnalog(b);
|
||||||
}
|
}
|
||||||
@@ -278,7 +281,7 @@ void handleButton()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
// button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
|
||||||
if (buttonType[b] == BTN_TYPE_SWITCH || buttonType[b] == BTN_TYPE_TOUCH_SWITCH || buttonType[b] == BTN_TYPE_PIR_SENSOR) {
|
if (buttons[b].type == BTN_TYPE_SWITCH || buttons[b].type == BTN_TYPE_TOUCH_SWITCH || buttons[b].type == BTN_TYPE_PIR_SENSOR) {
|
||||||
handleSwitch(b);
|
handleSwitch(b);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -287,40 +290,39 @@ void handleButton()
|
|||||||
if (isButtonPressed(b)) { // pressed
|
if (isButtonPressed(b)) { // pressed
|
||||||
|
|
||||||
// if all macros are the same, fire action immediately on rising edge
|
// if all macros are the same, fire action immediately on rising edge
|
||||||
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
|
if (buttons[b].macroButton && buttons[b].macroButton == buttons[b].macroLongPress && buttons[b].macroButton == buttons[b].macroDoublePress) {
|
||||||
if (!buttonPressedBefore[b])
|
if (!buttons[b].pressedBefore) shortPressAction(b);
|
||||||
shortPressAction(b);
|
buttons[b].pressedBefore = true;
|
||||||
buttonPressedBefore[b] = true;
|
buttons[b].pressedTime = now; // continually update (for debouncing to work in release handler)
|
||||||
buttonPressedTime[b] = now; // continually update (for debouncing to work in release handler)
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!buttonPressedBefore[b]) buttonPressedTime[b] = now;
|
if (!buttons[b].pressedBefore) buttons[b].pressedTime = now;
|
||||||
buttonPressedBefore[b] = true;
|
buttons[b].pressedBefore = true;
|
||||||
|
|
||||||
if (now - buttonPressedTime[b] > WLED_LONG_PRESS) { //long press
|
if (now - buttons[b].pressedTime > WLED_LONG_PRESS) { //long press
|
||||||
if (!buttonLongPressed[b]) {
|
if (!buttons[b].longPressed) {
|
||||||
buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
|
buttonBriDirection = !buttonBriDirection; //toggle brightness direction on long press
|
||||||
longPressAction(b);
|
longPressAction(b);
|
||||||
} else if (b) { //repeatable action (~5 times per s) on button > 0
|
} else if (b) { //repeatable action (~5 times per s) on button > 0
|
||||||
longPressAction(b);
|
longPressAction(b);
|
||||||
buttonPressedTime[b] = now - WLED_LONG_REPEATED_ACTION; //200ms
|
buttons[b].pressedTime = now - WLED_LONG_REPEATED_ACTION; //200ms
|
||||||
}
|
}
|
||||||
buttonLongPressed[b] = true;
|
buttons[b].longPressed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (buttonPressedBefore[b]) { //released
|
} else if (buttons[b].pressedBefore) { //released
|
||||||
long dur = now - buttonPressedTime[b];
|
long dur = now - buttons[b].pressedTime;
|
||||||
|
|
||||||
// released after rising-edge short press action
|
// released after rising-edge short press action
|
||||||
if (macroButton[b] && macroButton[b] == macroLongPress[b] && macroButton[b] == macroDoublePress[b]) {
|
if (buttons[b].macroButton && buttons[b].macroButton == buttons[b].macroLongPress && buttons[b].macroButton == buttons[b].macroDoublePress) {
|
||||||
if (dur > WLED_DEBOUNCE_THRESHOLD) buttonPressedBefore[b] = false; // debounce, blocks button for 50 ms once it has been released
|
if (dur > WLED_DEBOUNCE_THRESHOLD) buttons[b].pressedBefore = false; // debounce, blocks button for 50 ms once it has been released
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} // too short "press", debounce
|
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttons[b].pressedBefore = false; continue;} // too short "press", debounce
|
||||||
bool doublePress = buttonWaitTime[b]; //did we have a short press before?
|
bool doublePress = buttons[b].waitTime; //did we have a short press before?
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
|
|
||||||
if (b == 0 && dur > WLED_LONG_AP) { // long press on button 0 (when released)
|
if (b == 0 && dur > WLED_LONG_AP) { // long press on button 0 (when released)
|
||||||
if (dur > WLED_LONG_FACTORY_RESET) { // factory reset if pressed > 10 seconds
|
if (dur > WLED_LONG_FACTORY_RESET) { // factory reset if pressed > 10 seconds
|
||||||
@@ -332,25 +334,25 @@ void handleButton()
|
|||||||
} else {
|
} else {
|
||||||
WLED::instance().initAP(true);
|
WLED::instance().initAP(true);
|
||||||
}
|
}
|
||||||
} else if (!buttonLongPressed[b]) { //short press
|
} else if (!buttons[b].longPressed) { //short press
|
||||||
//NOTE: this interferes with double click handling in usermods so usermod needs to implement full button handling
|
//NOTE: this interferes with double click handling in usermods so usermod needs to implement full button handling
|
||||||
if (b != 1 && !macroDoublePress[b]) { //don't wait for double press on buttons without a default action if no double press macro set
|
if (b != 1 && !buttons[b].macroDoublePress) { //don't wait for double press on buttons without a default action if no double press macro set
|
||||||
shortPressAction(b);
|
shortPressAction(b);
|
||||||
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)
|
} else { //double press if less than 350 ms between current press and previous short press release (buttonWaitTime!=0)
|
||||||
if (doublePress) {
|
if (doublePress) {
|
||||||
doublePressAction(b);
|
doublePressAction(b);
|
||||||
} else {
|
} else {
|
||||||
buttonWaitTime[b] = now;
|
buttons[b].waitTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buttonPressedBefore[b] = false;
|
buttons[b].pressedBefore = false;
|
||||||
buttonLongPressed[b] = false;
|
buttons[b].longPressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if 350ms elapsed since last short press release it is a short press
|
//if 350ms elapsed since last short press release it is a short press
|
||||||
if (buttonWaitTime[b] && now - buttonWaitTime[b] > WLED_DOUBLE_PRESS && !buttonPressedBefore[b]) {
|
if (buttons[b].waitTime && now - buttons[b].waitTime > WLED_DOUBLE_PRESS && !buttons[b].pressedBefore) {
|
||||||
buttonWaitTime[b] = 0;
|
buttons[b].waitTime = 0;
|
||||||
shortPressAction(b);
|
shortPressAction(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
126
wled00/cfg.cpp
126
wled00/cfg.cpp
@@ -345,97 +345,91 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
|||||||
JsonArray hw_btn_ins = btn_obj["ins"];
|
JsonArray hw_btn_ins = btn_obj["ins"];
|
||||||
if (!hw_btn_ins.isNull()) {
|
if (!hw_btn_ins.isNull()) {
|
||||||
// deallocate existing button pins
|
// deallocate existing button pins
|
||||||
for (unsigned b = 0; b < WLED_MAX_BUTTONS; b++) PinManager::deallocatePin(btnPin[b], PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
|
for (const auto &button : buttons) PinManager::deallocatePin(button.pin, PinOwner::Button); // does nothing if trying to deallocate a pin with PinOwner != Button
|
||||||
|
buttons.clear(); // clear existing buttons
|
||||||
unsigned s = 0;
|
unsigned s = 0;
|
||||||
for (JsonObject btn : hw_btn_ins) {
|
for (JsonObject btn : hw_btn_ins) {
|
||||||
CJSON(buttonType[s], btn["type"]);
|
uint8_t type = btn["type"] | BTN_TYPE_NONE;
|
||||||
int8_t pin = btn["pin"][0] | -1;
|
int8_t pin = btn["pin"][0] | -1;
|
||||||
if (pin > -1 && PinManager::allocatePin(pin, false, PinOwner::Button)) {
|
if (pin > -1 && PinManager::allocatePin(pin, false, PinOwner::Button)) {
|
||||||
btnPin[s] = pin;
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
// ESP32 only: check that analog button pin is a valid ADC gpio
|
// ESP32 only: check that analog button pin is a valid ADC gpio
|
||||||
if ((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) {
|
if ((type == BTN_TYPE_ANALOG) || (type == BTN_TYPE_ANALOG_INVERTED)) {
|
||||||
if (digitalPinToAnalogChannel(btnPin[s]) < 0) {
|
if (digitalPinToAnalogChannel(pin) < 0) {
|
||||||
// not an ADC analog pin
|
// not an ADC analog pin
|
||||||
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), btnPin[s], s);
|
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), pin, s);
|
||||||
btnPin[s] = -1;
|
PinManager::deallocatePin(pin, PinOwner::Button);
|
||||||
PinManager::deallocatePin(pin,PinOwner::Button);
|
pin = -1;
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
analogReadResolution(12); // see #4040
|
analogReadResolution(12); // see #4040
|
||||||
}
|
}
|
||||||
}
|
} else if ((type == BTN_TYPE_TOUCH || type == BTN_TYPE_TOUCH_SWITCH)) {
|
||||||
else if ((buttonType[s] == BTN_TYPE_TOUCH || buttonType[s] == BTN_TYPE_TOUCH_SWITCH))
|
if (digitalPinToTouchChannel(pin) < 0) {
|
||||||
{
|
|
||||||
if (digitalPinToTouchChannel(btnPin[s]) < 0) {
|
|
||||||
// not a touch pin
|
// not a touch pin
|
||||||
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not a touch pin!\n"), btnPin[s], s);
|
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not a touch pin!\n"), pin, s);
|
||||||
btnPin[s] = -1;
|
PinManager::deallocatePin(pin, PinOwner::Button);
|
||||||
PinManager::deallocatePin(pin,PinOwner::Button);
|
pin = -1;
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
//if touch pin, enable the touch interrupt on ESP32 S2 & S3
|
//if touch pin, enable the touch interrupt on ESP32 S2 & S3
|
||||||
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state but need to attach an interrupt to do so
|
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state but need to attach an interrupt to do so
|
||||||
else
|
else touchAttachInterrupt(pin, touchButtonISR, touchThreshold << 4); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
|
||||||
{
|
|
||||||
touchAttachInterrupt(btnPin[s], touchButtonISR, touchThreshold << 4); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
} else
|
||||||
else
|
#endif
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
|
// regular buttons and switches
|
||||||
if (disablePullUp) {
|
if (disablePullUp) {
|
||||||
pinMode(btnPin[s], INPUT);
|
pinMode(pin, INPUT);
|
||||||
} else {
|
} else {
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
pinMode(pin, type==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||||
#else
|
#else
|
||||||
pinMode(btnPin[s], INPUT_PULLUP);
|
pinMode(pin, INPUT_PULLUP);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
JsonArray hw_btn_ins_0_macros = btn["macros"];
|
||||||
btnPin[s] = -1;
|
uint8_t press = hw_btn_ins_0_macros[0] | 0;
|
||||||
|
uint8_t longPress = hw_btn_ins_0_macros[1] | 0;
|
||||||
|
uint8_t doublePress = hw_btn_ins_0_macros[2] | 0;
|
||||||
|
buttons.emplace_back(pin, type, press, longPress, doublePress); // add button to vector
|
||||||
}
|
}
|
||||||
JsonArray hw_btn_ins_0_macros = btn["macros"];
|
|
||||||
CJSON(macroButton[s], hw_btn_ins_0_macros[0]);
|
|
||||||
CJSON(macroLongPress[s],hw_btn_ins_0_macros[1]);
|
|
||||||
CJSON(macroDoublePress[s], hw_btn_ins_0_macros[2]);
|
|
||||||
if (++s >= WLED_MAX_BUTTONS) break; // max buttons reached
|
if (++s >= WLED_MAX_BUTTONS) break; // max buttons reached
|
||||||
}
|
}
|
||||||
// clear remaining buttons
|
|
||||||
for (; s<WLED_MAX_BUTTONS; s++) {
|
|
||||||
btnPin[s] = -1;
|
|
||||||
buttonType[s] = BTN_TYPE_NONE;
|
|
||||||
macroButton[s] = 0;
|
|
||||||
macroLongPress[s] = 0;
|
|
||||||
macroDoublePress[s] = 0;
|
|
||||||
}
|
|
||||||
} else if (fromFS) {
|
} else if (fromFS) {
|
||||||
// new install/missing configuration (button 0 has defaults)
|
// new install/missing configuration (button 0 has defaults)
|
||||||
// relies upon only being called once with fromFS == true, which is currently true.
|
// relies upon only being called once with fromFS == true, which is currently true.
|
||||||
for (size_t s = 0; s < WLED_MAX_BUTTONS; s++) {
|
constexpr uint8_t defTypes[] = {BTNTYPE};
|
||||||
if (buttonType[s] == BTN_TYPE_NONE || btnPin[s] < 0 || !PinManager::allocatePin(btnPin[s], false, PinOwner::Button)) {
|
constexpr int8_t defPins[] = {BTNPIN};
|
||||||
btnPin[s] = -1;
|
constexpr unsigned numTypes = (sizeof(defTypes) / sizeof(defTypes[0]));
|
||||||
buttonType[s] = BTN_TYPE_NONE;
|
constexpr unsigned numPins = (sizeof(defPins) / sizeof(defPins[0]));
|
||||||
|
// check if the number of pins and types are valid; count of pins must be greater than or equal to types
|
||||||
|
static_assert(numTypes <= numPins, "The default button pins defined in BTNPIN do not match the button types defined in BTNTYPE");
|
||||||
|
|
||||||
|
uint8_t type = BTN_TYPE_NONE;
|
||||||
|
buttons.clear(); // clear existing buttons (just in case)
|
||||||
|
for (size_t s = 0; s < WLED_MAX_BUTTONS && s < numPins; s++) {
|
||||||
|
type = defTypes[s < numTypes ? s : numTypes - 1]; // use last known type to set current type if types less than pins
|
||||||
|
if (type == BTN_TYPE_NONE || defPins[s] < 0 || !PinManager::allocatePin(defPins[s], false, PinOwner::Button)) {
|
||||||
|
if (buttons.size() == 0) buttons.emplace_back(-1, BTN_TYPE_NONE); // add disabled button to vector (so we have at least one button defined)
|
||||||
|
continue; // pin not available or invalid, skip configuring this GPIO
|
||||||
}
|
}
|
||||||
if (btnPin[s] >= 0) {
|
if (disablePullUp) {
|
||||||
if (disablePullUp) {
|
pinMode(defPins[s], INPUT);
|
||||||
pinMode(btnPin[s], INPUT);
|
} else {
|
||||||
} else {
|
#ifdef ESP32
|
||||||
#ifdef ESP32
|
pinMode(defPins[s], type==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
#else
|
||||||
#else
|
pinMode(defPins[s], INPUT_PULLUP);
|
||||||
pinMode(btnPin[s], INPUT_PULLUP);
|
#endif
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
macroButton[s] = 0;
|
buttons.emplace_back(defPins[s], type); // add button to vector
|
||||||
macroLongPress[s] = 0;
|
|
||||||
macroDoublePress[s] = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON(buttonPublishMqtt,btn_obj["mqtt"]);
|
CJSON(buttonPublishMqtt, btn_obj["mqtt"]);
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_INFRARED
|
#ifndef WLED_DISABLE_INFRARED
|
||||||
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
|
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
|
||||||
@@ -1016,15 +1010,15 @@ void serializeConfig(JsonObject root) {
|
|||||||
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
|
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
|
||||||
|
|
||||||
// configuration for all buttons
|
// configuration for all buttons
|
||||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
for (const auto &button : buttons) {
|
||||||
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
|
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
|
||||||
hw_btn_ins_0["type"] = buttonType[i];
|
hw_btn_ins_0["type"] = button.type;
|
||||||
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
|
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
|
||||||
hw_btn_ins_0_pin.add(btnPin[i]);
|
hw_btn_ins_0_pin.add(button.pin);
|
||||||
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
|
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
|
||||||
hw_btn_ins_0_macros.add(macroButton[i]);
|
hw_btn_ins_0_macros.add(button.macroButton);
|
||||||
hw_btn_ins_0_macros.add(macroLongPress[i]);
|
hw_btn_ins_0_macros.add(button.macroLongPress);
|
||||||
hw_btn_ins_0_macros.add(macroDoublePress[i]);
|
hw_btn_ins_0_macros.add(button.macroDoublePress);
|
||||||
}
|
}
|
||||||
|
|
||||||
hw_btn[F("tt")] = touchThreshold;
|
hw_btn[F("tt")] = touchThreshold;
|
||||||
|
|||||||
@@ -102,9 +102,9 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
|
|||||||
|
|
||||||
#ifndef WLED_MAX_BUTTONS
|
#ifndef WLED_MAX_BUTTONS
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
#define WLED_MAX_BUTTONS 2
|
#define WLED_MAX_BUTTONS 10
|
||||||
#else
|
#else
|
||||||
#define WLED_MAX_BUTTONS 4
|
#define WLED_MAX_BUTTONS 32
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#if WLED_MAX_BUTTONS < 2
|
#if WLED_MAX_BUTTONS < 2
|
||||||
|
|||||||
@@ -693,6 +693,8 @@ function parseInfo(i) {
|
|||||||
// gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects
|
// gId("filterVol").classList.add("hide"); hideModes(" ♪"); // hide volume reactive effects
|
||||||
// gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects
|
// gId("filterFreq").classList.add("hide"); hideModes(" ♫"); // hide frequency reactive effects
|
||||||
// }
|
// }
|
||||||
|
// Check for version upgrades on page load
|
||||||
|
checkVersionUpgrade(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
|
//https://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
|
||||||
@@ -3319,66 +3321,66 @@ function checkVersionUpgrade(info) {
|
|||||||
fetch(getURL('/edit?func=edit&path=/version-info.json'), {
|
fetch(getURL('/edit?func=edit&path=/version-info.json'), {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.status === 404) {
|
if (res.status === 404) {
|
||||||
// File doesn't exist - first install, show install prompt
|
// File doesn't exist - first install, show install prompt
|
||||||
showVersionUpgradePrompt(info, null, info.ver);
|
showVersionUpgradePrompt(info, null, info.ver);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error('Failed to fetch version-info.json');
|
throw new Error('Failed to fetch version-info.json');
|
||||||
}
|
}
|
||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.then(versionInfo => {
|
.then(versionInfo => {
|
||||||
if (!versionInfo) return; // 404 case already handled
|
if (!versionInfo) return; // 404 case already handled
|
||||||
|
|
||||||
// Check if user opted out
|
// Check if user opted out
|
||||||
if (versionInfo.neverAsk) return;
|
if (versionInfo.neverAsk) return;
|
||||||
|
|
||||||
// Check if version has changed
|
// Check if version has changed
|
||||||
const currentVersion = info.ver;
|
const currentVersion = info.ver;
|
||||||
const storedVersion = versionInfo.version || '';
|
const storedVersion = versionInfo.version || '';
|
||||||
|
|
||||||
if (storedVersion && storedVersion !== currentVersion) {
|
if (storedVersion && storedVersion !== currentVersion) {
|
||||||
// Version has changed, show upgrade prompt
|
// Version has changed, show upgrade prompt
|
||||||
showVersionUpgradePrompt(info, storedVersion, currentVersion);
|
showVersionUpgradePrompt(info, storedVersion, currentVersion);
|
||||||
} else if (!storedVersion) {
|
} else if (!storedVersion) {
|
||||||
// Empty version in file, show install prompt
|
// Empty version in file, show install prompt
|
||||||
showVersionUpgradePrompt(info, null, currentVersion);
|
showVersionUpgradePrompt(info, null, currentVersion);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.log('Failed to load version-info.json', e);
|
console.log('Failed to load version-info.json', e);
|
||||||
// On error, save current version for next time
|
// On error, save current version for next time
|
||||||
if (info && info.ver) {
|
if (info && info.ver) {
|
||||||
updateVersionInfo(info.ver, false);
|
updateVersionInfo(info.ver, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showVersionUpgradePrompt(info, oldVersion, newVersion) {
|
function showVersionUpgradePrompt(info, oldVersion, newVersion) {
|
||||||
// Determine if this is an install or upgrade
|
// Determine if this is an install or upgrade
|
||||||
const isInstall = !oldVersion;
|
const isInstall = !oldVersion;
|
||||||
|
|
||||||
// Create overlay and dialog
|
// Create overlay and dialog
|
||||||
const overlay = d.createElement('div');
|
const overlay = d.createElement('div');
|
||||||
overlay.id = 'versionUpgradeOverlay';
|
overlay.id = 'versionUpgradeOverlay';
|
||||||
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;';
|
overlay.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:10000;display:flex;align-items:center;justify-content:center;';
|
||||||
|
|
||||||
const dialog = d.createElement('div');
|
const dialog = d.createElement('div');
|
||||||
dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);';
|
dialog.style.cssText = 'background:var(--c-1);border-radius:10px;padding:25px;max-width:500px;margin:20px;box-shadow:0 4px 6px rgba(0,0,0,0.3);';
|
||||||
|
|
||||||
// Build contextual message based on install vs upgrade
|
// Build contextual message based on install vs upgrade
|
||||||
const title = isInstall
|
const title = isInstall
|
||||||
? '🎉 Thank you for installing WLED!'
|
? '🎉 Thank you for installing WLED!'
|
||||||
: '🎉 WLED Upgrade Detected!';
|
: '🎉 WLED Upgrade Detected!';
|
||||||
|
|
||||||
const description = isInstall
|
const description = isInstall
|
||||||
? `You are now running WLED <strong style="text-wrap: nowrap">${newVersion}</strong>.`
|
? `You are now running WLED <strong style="text-wrap: nowrap">${newVersion}</strong>.`
|
||||||
: `Your WLED has been upgraded from <strong style="text-wrap: nowrap">${oldVersion}</strong> to <strong style="text-wrap: nowrap">${newVersion}</strong>.`;
|
: `Your WLED has been upgraded from <strong style="text-wrap: nowrap">${oldVersion}</strong> to <strong style="text-wrap: nowrap">${newVersion}</strong>.`;
|
||||||
|
|
||||||
const question = 'Would you like to help the WLED development team by reporting your installation? This helps us understand what hardware and versions are being used.'
|
const question = 'Help make WLED better with a one-time hardware report? It includes only device details like chip type, LED count, etc. — never personal data or your activities.'
|
||||||
|
|
||||||
dialog.innerHTML = `
|
dialog.innerHTML = `
|
||||||
<h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
|
<h2 style="margin-top:0;color:var(--c-f);">${title}</h2>
|
||||||
@@ -3393,21 +3395,21 @@ function showVersionUpgradePrompt(info, oldVersion, newVersion) {
|
|||||||
<button id="versionReportNever" class="btn">Never Ask</button>
|
<button id="versionReportNever" class="btn">Never Ask</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
overlay.appendChild(dialog);
|
overlay.appendChild(dialog);
|
||||||
d.body.appendChild(overlay);
|
d.body.appendChild(overlay);
|
||||||
|
|
||||||
// Add event listeners
|
// Add event listeners
|
||||||
gId('versionReportYes').addEventListener('click', () => {
|
gId('versionReportYes').addEventListener('click', () => {
|
||||||
reportUpgradeEvent(info, oldVersion, newVersion);
|
reportUpgradeEvent(info, oldVersion);
|
||||||
d.body.removeChild(overlay);
|
d.body.removeChild(overlay);
|
||||||
});
|
});
|
||||||
|
|
||||||
gId('versionReportNo').addEventListener('click', () => {
|
gId('versionReportNo').addEventListener('click', () => {
|
||||||
// Don't update version, will ask again on next load
|
// Don't update version, will ask again on next load
|
||||||
d.body.removeChild(overlay);
|
d.body.removeChild(overlay);
|
||||||
});
|
});
|
||||||
|
|
||||||
gId('versionReportNever').addEventListener('click', () => {
|
gId('versionReportNever').addEventListener('click', () => {
|
||||||
updateVersionInfo(newVersion, true);
|
updateVersionInfo(newVersion, true);
|
||||||
d.body.removeChild(overlay);
|
d.body.removeChild(overlay);
|
||||||
@@ -3415,58 +3417,58 @@ function showVersionUpgradePrompt(info, oldVersion, newVersion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function reportUpgradeEvent(info, oldVersion, newVersion) {
|
function reportUpgradeEvent(info, oldVersion) {
|
||||||
showToast('Reporting upgrade...');
|
showToast('Reporting upgrade...');
|
||||||
|
|
||||||
// Fetch fresh data from /json/info endpoint as requested
|
// Fetch fresh data from /json/info endpoint as requested
|
||||||
fetch(getURL('/json/info'), {
|
fetch(getURL('/json/info'), {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(infoData => {
|
.then(infoData => {
|
||||||
// Map to UpgradeEventRequest structure per OpenAPI spec
|
// Map to UpgradeEventRequest structure per OpenAPI spec
|
||||||
// Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256
|
// Required fields: deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256
|
||||||
const upgradeData = {
|
const upgradeData = {
|
||||||
deviceId: infoData.deviceId, // Use anonymous unique device ID
|
deviceId: infoData.deviceId, // Use anonymous unique device ID
|
||||||
version: infoData.ver || '', // Current version string
|
version: infoData.ver || '', // Current version string
|
||||||
previousVersion: oldVersion || '', // Previous version from version-info.json
|
previousVersion: oldVersion || '', // Previous version from version-info.json
|
||||||
releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0")
|
releaseName: infoData.release || '', // Release name (e.g., "WLED 0.15.0")
|
||||||
chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc)
|
chip: infoData.arch || '', // Chip architecture (esp32, esp8266, etc)
|
||||||
ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs
|
ledCount: infoData.leds ? infoData.leds.count : 0, // Number of LEDs
|
||||||
isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup
|
isMatrix: !!(infoData.leds && infoData.leds.matrix), // Whether it's a 2D matrix setup
|
||||||
bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash
|
bootloaderSHA256: infoData.bootloaderSHA256 || '', // Bootloader SHA256 hash
|
||||||
brand: infoData.brand, // Device brand (always present)
|
brand: infoData.brand, // Device brand (always present)
|
||||||
product: infoData.product, // Product name (always present)
|
product: infoData.product, // Product name (always present)
|
||||||
flashSize: infoData.flash // Flash size (always present)
|
flashSize: infoData.flash // Flash size (always present)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add optional fields if available
|
// Add optional fields if available
|
||||||
if (infoData.psram !== undefined) upgradeData.psramSize = infoData.psram;
|
if (infoData.psram !== undefined) upgradeData.psramSize = Math.round(infoData.psram / (1024 * 1024)); // convert bytes to MB
|
||||||
// Note: partitionSizes not currently available in /json/info endpoint
|
// Note: partitionSizes not currently available in /json/info endpoint
|
||||||
|
|
||||||
// Make AJAX call to postUpgradeEvent API
|
// Make AJAX call to postUpgradeEvent API
|
||||||
return fetch('https://usage.wled.me/api/usage/upgrade', {
|
return fetch('https://usage.wled.me/api/usage/upgrade', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify(upgradeData)
|
body: JSON.stringify(upgradeData)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
showToast('Thank you for reporting!');
|
showToast('Thank you for reporting!');
|
||||||
updateVersionInfo(newVersion, false);
|
updateVersionInfo(info.ver, false);
|
||||||
} else {
|
} else {
|
||||||
|
showToast('Report failed. Please try again later.', true);
|
||||||
|
// Do NOT update version info on failure - user will be prompted again
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
console.log('Failed to report upgrade', e);
|
||||||
showToast('Report failed. Please try again later.', true);
|
showToast('Report failed. Please try again later.', true);
|
||||||
// Do NOT update version info on failure - user will be prompted again
|
// Do NOT update version info on error - user will be prompted again
|
||||||
}
|
});
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
console.log('Failed to report upgrade', e);
|
|
||||||
showToast('Report failed. Please try again later.', true);
|
|
||||||
// Do NOT update version info on error - user will be prompted again
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateVersionInfo(version, neverAsk) {
|
function updateVersionInfo(version, neverAsk) {
|
||||||
@@ -3474,23 +3476,23 @@ function updateVersionInfo(version, neverAsk) {
|
|||||||
version: version,
|
version: version,
|
||||||
neverAsk: neverAsk
|
neverAsk: neverAsk
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a Blob with JSON content and use /upload endpoint
|
// Create a Blob with JSON content and use /upload endpoint
|
||||||
const blob = new Blob([JSON.stringify(versionInfo)], { type: 'application/json' });
|
const blob = new Blob([JSON.stringify(versionInfo)], {type: 'application/json'});
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('data', blob, 'version-info.json');
|
formData.append('data', blob, 'version-info.json');
|
||||||
|
|
||||||
fetch(getURL('/upload'), {
|
fetch(getURL('/upload'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(res => res.text())
|
.then(res => res.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
console.log('Version info updated', data);
|
console.log('Version info updated', data);
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.log('Failed to update version-info.json', e);
|
console.log('Failed to update version-info.json', e);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
size();
|
size();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<title>LED Settings</title>
|
<title>LED Settings</title>
|
||||||
<script src="common.js" type="text/javascript"></script>
|
<script src="common.js" type="text/javascript"></script>
|
||||||
<script>
|
<script>
|
||||||
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
var maxB=1,maxD=1,maxA=1,maxV=0,maxM=4000,maxPB=2048,maxL=1664,maxCO=5,maxBT=4; //maximum bytes for LED allocation: 4kB for 8266, 32kB for 32
|
||||||
var customStarts=false,startsDirty=[];
|
var customStarts=false,startsDirty=[];
|
||||||
function off(n) { gN(n).value = -1;}
|
function off(n) { gN(n).value = -1;}
|
||||||
// these functions correspond to C macros found in const.h
|
// these functions correspond to C macros found in const.h
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
}); // If we set async false, file is loaded and executed, then next statement is processed
|
}); // If we set async false, file is loaded and executed, then next statement is processed
|
||||||
if (loc) d.Sf.action = getURL('/settings/leds');
|
if (loc) d.Sf.action = getURL('/settings/leds');
|
||||||
}
|
}
|
||||||
function bLimits(b,v,p,m,l,o=5,d=2,a=6) {
|
function bLimits(b,v,p,m,l,o=5,d=2,a=6,n=4) {
|
||||||
maxB = b; // maxB - max physical (analog + digital) buses: 32 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
|
maxB = b; // maxB - max physical (analog + digital) buses: 32 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266
|
||||||
maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3)
|
maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3)
|
||||||
maxPB = p; // maxPB - max LEDs per bus
|
maxPB = p; // maxPB - max LEDs per bus
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
maxCO = o; // maxCO - max Color Order mappings
|
maxCO = o; // maxCO - max Color Order mappings
|
||||||
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
|
maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
|
||||||
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
|
maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
|
||||||
|
maxBT = n; // maxBT - max buttons
|
||||||
}
|
}
|
||||||
function is8266() { return maxA == 5 && maxD == 3; } // NOTE: see const.h
|
function is8266() { return maxA == 5 && maxD == 3; } // NOTE: see const.h
|
||||||
function is32() { return maxA == 16 && maxD == 16; } // NOTE: see const.h
|
function is32() { return maxA == 16 && maxD == 16; } // NOTE: see const.h
|
||||||
@@ -600,9 +601,9 @@ Swap: <select id="xw${s}" name="XW${s}">
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addBtn(i,p,t) {
|
function addBtn(i,p,t) {
|
||||||
var c = gId("btns").innerHTML;
|
var b = gId("btns");
|
||||||
var s = chrID(i);
|
var s = chrID(i);
|
||||||
c += `Button ${i} GPIO: <input type="number" name="BT${s}" onchange="UI()" class="xs" value="${p}">`;
|
var c = `<div id="btn${i}">#${i} GPIO: <input type="number" name="BT${s}" onchange="UI()" min="-1" max="${d.max_gpio}" class="xs" value="${p}">`;
|
||||||
c += ` <select name="BE${s}">`
|
c += ` <select name="BE${s}">`
|
||||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||||
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
|
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
|
||||||
@@ -614,8 +615,24 @@ Swap: <select id="xw${s}" name="XW${s}">
|
|||||||
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
|
c += `<option value="8" ${t==8?"selected":""}>Analog inverted</option>`;
|
||||||
c += `<option value="9" ${t==9?"selected":""}>Touch (switch)</option>`;
|
c += `<option value="9" ${t==9?"selected":""}>Touch (switch)</option>`;
|
||||||
c += `</select>`;
|
c += `</select>`;
|
||||||
c += `<span style="cursor: pointer;" onclick="off('BT${s}')"> ✕</span><br>`;
|
c += `<span style="cursor: pointer;" onclick="off('BT${s}')"> ✕</span><br></div>`;
|
||||||
gId("btns").innerHTML = c;
|
b.insertAdjacentHTML("beforeend", c);
|
||||||
|
btnBtn();
|
||||||
|
pinDropdowns();
|
||||||
|
UI();
|
||||||
|
}
|
||||||
|
function remBtn() {
|
||||||
|
var b = gId("btns");
|
||||||
|
if (b.children.length <= 1) return;
|
||||||
|
b.lastElementChild.remove();
|
||||||
|
btnBtn();
|
||||||
|
pinDropdowns();
|
||||||
|
UI();
|
||||||
|
}
|
||||||
|
function btnBtn() {
|
||||||
|
var b = gId("btns");
|
||||||
|
gId("btn_rem").style.display = (b.children.length > 1) ? "inline" : "none";
|
||||||
|
gId("btn_add").style.display = (b.children.length < maxBT) ? "inline" : "none";
|
||||||
}
|
}
|
||||||
function tglSi(cs) {
|
function tglSi(cs) {
|
||||||
customStarts = cs;
|
customStarts = cs;
|
||||||
@@ -867,10 +884,16 @@ Swap: <select id="xw${s}" name="XW${s}">
|
|||||||
<div id="com_entries"></div>
|
<div id="com_entries"></div>
|
||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
<button type="button" id="com_add" onclick="addCOM()">+</button>
|
<button type="button" id="com_add" onclick="addCOM()">+</button>
|
||||||
<button type="button" id="com_rem" onclick="remCOM()">-</button><br>
|
<button type="button" id="com_rem" onclick="remCOM()">-</button>
|
||||||
</div>
|
</div>
|
||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
<div id="btns"></div>
|
<div id="btn_wrap">
|
||||||
|
Buttons:
|
||||||
|
<div id="btns"></div>
|
||||||
|
<hr class="sml">
|
||||||
|
<button type="button" id="btn_add" onclick="addBtn(gId('btns').children.length,-1,0)">+</button>
|
||||||
|
<button type="button" id="btn_rem" onclick="remBtn()">-</button>
|
||||||
|
</div>
|
||||||
Disable internal pull-up/down: <input type="checkbox" name="IP"><br>
|
Disable internal pull-up/down: <input type="checkbox" name="IP"><br>
|
||||||
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
||||||
<hr class="sml">
|
<hr class="sml">
|
||||||
|
|||||||
@@ -128,12 +128,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
PinManager::deallocatePin(irPin, PinOwner::IR);
|
PinManager::deallocatePin(irPin, PinOwner::IR);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for (unsigned s=0; s<WLED_MAX_BUTTONS; s++) {
|
for (const auto &button : buttons) {
|
||||||
if (btnPin[s]>=0 && PinManager::isPinAllocated(btnPin[s], PinOwner::Button)) {
|
if (button.pin >= 0 && PinManager::isPinAllocated(button.pin, PinOwner::Button)) {
|
||||||
PinManager::deallocatePin(btnPin[s], PinOwner::Button);
|
PinManager::deallocatePin(button.pin, PinOwner::Button);
|
||||||
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state, detach interrupt
|
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state, detach interrupt
|
||||||
if (digitalPinToTouchChannel(btnPin[s]) >= 0) // if touch capable pin
|
if (digitalPinToTouchChannel(button.pin) >= 0) // if touch capable pin
|
||||||
touchDetachInterrupt(btnPin[s]); // if not assigned previously, this will do nothing
|
touchDetachInterrupt(button.pin); // if not assigned previously, this will do nothing
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,54 +280,56 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
char bt[4] = "BT"; bt[2] = offset+i; bt[3] = 0; // button pin (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||||
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
char be[4] = "BE"; be[2] = offset+i; be[3] = 0; // button type (use A,B,C,... if WLED_MAX_BUTTONS>10)
|
||||||
int hw_btn_pin = request->arg(bt).toInt();
|
int hw_btn_pin = request->arg(bt).toInt();
|
||||||
if (hw_btn_pin >= 0 && PinManager::allocatePin(hw_btn_pin,false,PinOwner::Button)) {
|
if (i >= buttons.size()) buttons.emplace_back(hw_btn_pin, request->arg(be).toInt()); // add button to vector
|
||||||
btnPin[i] = hw_btn_pin;
|
else {
|
||||||
buttonType[i] = request->arg(be).toInt();
|
buttons[i].pin = hw_btn_pin;
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
buttons[i].type = request->arg(be).toInt();
|
||||||
|
}
|
||||||
|
if (buttons[i].pin >= 0 && PinManager::allocatePin(buttons[i].pin, false, PinOwner::Button)) {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// ESP32 only: check that button pin is a valid gpio
|
// ESP32 only: check that button pin is a valid gpio
|
||||||
if ((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED))
|
if ((buttons[i].type == BTN_TYPE_ANALOG) || (buttons[i].type == BTN_TYPE_ANALOG_INVERTED)) {
|
||||||
{
|
if (digitalPinToAnalogChannel(buttons[i].pin) < 0) {
|
||||||
if (digitalPinToAnalogChannel(btnPin[i]) < 0) {
|
|
||||||
// not an ADC analog pin
|
// not an ADC analog pin
|
||||||
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), btnPin[i], i);
|
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), buttons[i].pin, i);
|
||||||
btnPin[i] = -1;
|
PinManager::deallocatePin(buttons[i].pin, PinOwner::Button);
|
||||||
PinManager::deallocatePin(hw_btn_pin,PinOwner::Button);
|
buttons[i].type = BTN_TYPE_NONE;
|
||||||
} else {
|
} else {
|
||||||
analogReadResolution(12); // see #4040
|
analogReadResolution(12); // see #4040
|
||||||
}
|
}
|
||||||
}
|
} else if ((buttons[i].type == BTN_TYPE_TOUCH || buttons[i].type == BTN_TYPE_TOUCH_SWITCH)) {
|
||||||
else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH))
|
if (digitalPinToTouchChannel(buttons[i].pin) < 0) {
|
||||||
{
|
|
||||||
if (digitalPinToTouchChannel(btnPin[i]) < 0)
|
|
||||||
{
|
|
||||||
// not a touch pin
|
// not a touch pin
|
||||||
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), btnPin[i], i);
|
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), buttons[i].pin, i);
|
||||||
btnPin[i] = -1;
|
PinManager::deallocatePin(buttons[i].pin, PinOwner::Button);
|
||||||
PinManager::deallocatePin(hw_btn_pin,PinOwner::Button);
|
buttons[i].type = BTN_TYPE_NONE;
|
||||||
}
|
}
|
||||||
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a fucntion to check touch state but need to attach an interrupt to do so
|
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a fucntion to check touch state but need to attach an interrupt to do so
|
||||||
else
|
else touchAttachInterrupt(buttons[i].pin, touchButtonISR, touchThreshold << 4); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
|
||||||
{
|
#endif
|
||||||
touchAttachInterrupt(btnPin[i], touchButtonISR, touchThreshold << 4); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
|
} else
|
||||||
}
|
#endif
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
|
// regular buttons and switches
|
||||||
if (disablePullUp) {
|
if (disablePullUp) {
|
||||||
pinMode(btnPin[i], INPUT);
|
pinMode(buttons[i].pin, INPUT);
|
||||||
} else {
|
} else {
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
pinMode(btnPin[i], buttonType[i]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
pinMode(buttons[i].pin, buttons[i].type==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||||
#else
|
#else
|
||||||
pinMode(btnPin[i], INPUT_PULLUP);
|
pinMode(buttons[i].pin, INPUT_PULLUP);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
btnPin[i] = -1;
|
buttons[i].pin = -1;
|
||||||
buttonType[i] = BTN_TYPE_NONE;
|
buttons[i].type = BTN_TYPE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we should remove all unused buttons from the vector
|
||||||
|
for (int i = buttons.size()-1; i > 0; i--) {
|
||||||
|
if (buttons[i].pin < 0 && buttons[i].type == BTN_TYPE_NONE) {
|
||||||
|
buttons.erase(buttons.begin() + i); // remove button from vector
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,14 +533,16 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
|||||||
macroAlexaOff = request->arg(F("A1")).toInt();
|
macroAlexaOff = request->arg(F("A1")).toInt();
|
||||||
macroCountdown = request->arg(F("MC")).toInt();
|
macroCountdown = request->arg(F("MC")).toInt();
|
||||||
macroNl = request->arg(F("MN")).toInt();
|
macroNl = request->arg(F("MN")).toInt();
|
||||||
for (unsigned i=0; i<WLED_MAX_BUTTONS; i++) {
|
int i = 0;
|
||||||
char mp[4] = "MP"; mp[2] = (i<10?48:55)+i; mp[3] = 0; // short
|
for (auto &button : buttons) {
|
||||||
char ml[4] = "ML"; ml[2] = (i<10?48:55)+i; ml[3] = 0; // long
|
char mp[4] = "MP"; mp[2] = (i<10?'0':'A'-10)+i; mp[3] = 0; // short
|
||||||
char md[4] = "MD"; md[2] = (i<10?48:55)+i; md[3] = 0; // double
|
char ml[4] = "ML"; ml[2] = (i<10?'0':'A'-10)+i; ml[3] = 0; // long
|
||||||
|
char md[4] = "MD"; md[2] = (i<10?'0':'A'-10)+i; md[3] = 0; // double
|
||||||
//if (!request->hasArg(mp)) break;
|
//if (!request->hasArg(mp)) break;
|
||||||
macroButton[i] = request->arg(mp).toInt(); // these will default to 0 if not present
|
button.macroButton = request->arg(mp).toInt(); // these will default to 0 if not present
|
||||||
macroLongPress[i] = request->arg(ml).toInt();
|
button.macroLongPress = request->arg(ml).toInt();
|
||||||
macroDoublePress[i] = request->arg(md).toInt();
|
button.macroDoublePress = request->arg(md).toInt();
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
char k[3]; k[2] = 0;
|
char k[3]; k[2] = 0;
|
||||||
|
|||||||
@@ -1159,60 +1159,62 @@ String computeSHA1(const String& input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
static String dump_raw_block(esp_efuse_block_t block)
|
#include "esp_adc_cal.h"
|
||||||
{
|
String generateDeviceFingerprint() {
|
||||||
const int WORDS = 8; // ESP32: 8×32-bit words per block i.e. 256bits
|
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
|
||||||
uint32_t buf[WORDS] = {0};
|
esp_chip_info_t chip_info;
|
||||||
|
esp_chip_info(&chip_info);
|
||||||
const esp_efuse_desc_t d = {
|
esp_efuse_mac_get_default((uint8_t*)fp);
|
||||||
.efuse_block = block,
|
fp[1] ^= ESP.getFlashChipSize();
|
||||||
.bit_start = 0,
|
fp[0] ^= chip_info.full_revision | (chip_info.model << 16);
|
||||||
.bit_count = WORDS * 32
|
// mix in ADC calibration data:
|
||||||
};
|
esp_adc_cal_characteristics_t ch;
|
||||||
const esp_efuse_desc_t* field[2] = { &d, NULL };
|
#if SOC_ADC_MAX_BITWIDTH == 13 // S2 has 13 bit ADC
|
||||||
|
#define BIT_WIDTH ADC_WIDTH_BIT_13
|
||||||
esp_err_t err = esp_efuse_read_field_blob(field, buf, WORDS * 32);
|
#else
|
||||||
if (err != ESP_OK) {
|
#define BIT_WIDTH ADC_WIDTH_BIT_12
|
||||||
return "";
|
#endif
|
||||||
|
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, BIT_WIDTH, 1100, &ch);
|
||||||
|
fp[0] ^= ch.coeff_a;
|
||||||
|
fp[1] ^= ch.coeff_b;
|
||||||
|
if (ch.low_curve) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
fp[0] ^= ch.low_curve[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (ch.high_curve) {
|
||||||
String result = "";
|
for (int i = 0; i < 8; i++) {
|
||||||
for (const unsigned int i : buf) {
|
fp[1] ^= ch.high_curve[i];
|
||||||
char line[32];
|
}
|
||||||
sprintf(line, "0x%08X", i);
|
|
||||||
result += line;
|
|
||||||
}
|
}
|
||||||
return result;
|
char fp_string[17]; // 16 hex chars + null terminator
|
||||||
|
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
|
||||||
|
return String(fp_string);
|
||||||
|
}
|
||||||
|
#else // ESP8266
|
||||||
|
String generateDeviceFingerprint() {
|
||||||
|
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
|
||||||
|
WiFi.macAddress((uint8_t*)&fp); // use MAC address as fingerprint base
|
||||||
|
fp[0] ^= ESP.getFlashChipId();
|
||||||
|
fp[1] ^= ESP.getFlashChipSize() | ESP.getFlashChipVendorId() << 16;
|
||||||
|
char fp_string[17]; // 16 hex chars + null terminator
|
||||||
|
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
|
||||||
|
return String(fp_string);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Generate a device ID based on SHA1 hash of MAC address salted with other unique device info
|
||||||
// Generate a device ID based on SHA1 hash of MAC address salted with "WLED"
|
|
||||||
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
|
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
|
||||||
String getDeviceId() {
|
String getDeviceId() {
|
||||||
static String cachedDeviceId = "";
|
static String cachedDeviceId = "";
|
||||||
if (cachedDeviceId.length() > 0) return cachedDeviceId;
|
if (cachedDeviceId.length() > 0) return cachedDeviceId;
|
||||||
|
|
||||||
uint8_t mac[6];
|
|
||||||
WiFi.macAddress(mac);
|
|
||||||
char macStr[18];
|
|
||||||
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
||||||
|
|
||||||
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
|
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
|
||||||
// MAC is salted with other consistent device info to avoid rainbow table attacks.
|
// MAC is salted with other consistent device info to avoid rainbow table attacks.
|
||||||
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
|
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
|
||||||
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
|
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
|
||||||
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
|
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
|
||||||
#ifdef ESP8266
|
|
||||||
String deviceString = String(macStr) + "WLED" + ESP.getFlashChipId();
|
String firstHash = computeSHA1(generateDeviceFingerprint());
|
||||||
#else
|
|
||||||
String deviceString = String(macStr) + "WLED" + ESP.getChipModel() + ESP.getChipRevision();
|
|
||||||
deviceString += dump_raw_block(EFUSE_BLK0);
|
|
||||||
deviceString += dump_raw_block(EFUSE_BLK1);
|
|
||||||
deviceString += dump_raw_block(EFUSE_BLK2);
|
|
||||||
deviceString += dump_raw_block(EFUSE_BLK3);
|
|
||||||
#endif
|
|
||||||
String firstHash = computeSHA1(deviceString);
|
|
||||||
|
|
||||||
// Second hash: SHA1 of the first hash
|
// Second hash: SHA1 of the first hash
|
||||||
String secondHash = computeSHA1(firstHash);
|
String secondHash = computeSHA1(firstHash);
|
||||||
|
|||||||
@@ -286,10 +286,10 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
|
|||||||
|
|
||||||
// Hardware and pin config
|
// Hardware and pin config
|
||||||
#ifndef BTNPIN
|
#ifndef BTNPIN
|
||||||
#define BTNPIN 0,-1
|
#define BTNPIN 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef BTNTYPE
|
#ifndef BTNTYPE
|
||||||
#define BTNTYPE BTN_TYPE_PUSH,BTN_TYPE_NONE
|
#define BTNTYPE BTN_TYPE_PUSH
|
||||||
#endif
|
#endif
|
||||||
#ifndef RLYPIN
|
#ifndef RLYPIN
|
||||||
WLED_GLOBAL int8_t rlyPin _INIT(-1);
|
WLED_GLOBAL int8_t rlyPin _INIT(-1);
|
||||||
@@ -571,9 +571,6 @@ WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0);
|
|||||||
WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over
|
WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over
|
||||||
WLED_GLOBAL byte macroCountdown _INIT(0);
|
WLED_GLOBAL byte macroCountdown _INIT(0);
|
||||||
WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0);
|
WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0);
|
||||||
WLED_GLOBAL byte macroButton[WLED_MAX_BUTTONS] _INIT({0});
|
|
||||||
WLED_GLOBAL byte macroLongPress[WLED_MAX_BUTTONS] _INIT({0});
|
|
||||||
WLED_GLOBAL byte macroDoublePress[WLED_MAX_BUTTONS] _INIT({0});
|
|
||||||
|
|
||||||
// Security CONFIG
|
// Security CONFIG
|
||||||
#ifdef WLED_OTA_PASS
|
#ifdef WLED_OTA_PASS
|
||||||
@@ -639,13 +636,32 @@ WLED_GLOBAL byte briLast _INIT(128); // brightness before
|
|||||||
WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function in ir.cpp
|
WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function in ir.cpp
|
||||||
|
|
||||||
// button
|
// button
|
||||||
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN});
|
struct Button {
|
||||||
WLED_GLOBAL byte buttonType[WLED_MAX_BUTTONS] _INIT({BTNTYPE});
|
unsigned long pressedTime; // time button was pressed
|
||||||
|
unsigned long waitTime; // time to wait for next button press
|
||||||
|
int8_t pin; // pin number
|
||||||
|
struct {
|
||||||
|
uint8_t type : 6; // button type (push, long, double, etc.)
|
||||||
|
bool pressedBefore : 1; // button was pressed before
|
||||||
|
bool longPressed : 1; // button was long pressed
|
||||||
|
};
|
||||||
|
uint8_t macroButton; // macro/preset to call on button press
|
||||||
|
uint8_t macroLongPress; // macro/preset to call on long press
|
||||||
|
uint8_t macroDoublePress; // macro/preset to call on double press
|
||||||
|
|
||||||
|
Button(int8_t p, uint8_t t, uint8_t mB = 0, uint8_t mLP = 0, uint8_t mDP = 0)
|
||||||
|
: pressedTime(0)
|
||||||
|
, waitTime(0)
|
||||||
|
, pin(p)
|
||||||
|
, type(t)
|
||||||
|
, pressedBefore(false)
|
||||||
|
, longPressed(false)
|
||||||
|
, macroButton(mB)
|
||||||
|
, macroLongPress(mLP)
|
||||||
|
, macroDoublePress(mDP) {}
|
||||||
|
};
|
||||||
|
WLED_GLOBAL std::vector<Button> buttons; // vector of button structs
|
||||||
WLED_GLOBAL bool buttonPublishMqtt _INIT(false);
|
WLED_GLOBAL bool buttonPublishMqtt _INIT(false);
|
||||||
WLED_GLOBAL bool buttonPressedBefore[WLED_MAX_BUTTONS] _INIT({false});
|
|
||||||
WLED_GLOBAL bool buttonLongPressed[WLED_MAX_BUTTONS] _INIT({false});
|
|
||||||
WLED_GLOBAL unsigned long buttonPressedTime[WLED_MAX_BUTTONS] _INIT({0});
|
|
||||||
WLED_GLOBAL unsigned long buttonWaitTime[WLED_MAX_BUTTONS] _INIT({0});
|
|
||||||
WLED_GLOBAL bool disablePullUp _INIT(false);
|
WLED_GLOBAL bool disablePullUp _INIT(false);
|
||||||
WLED_GLOBAL byte touchThreshold _INIT(TOUCH_THRESHOLD);
|
WLED_GLOBAL byte touchThreshold _INIT(TOUCH_THRESHOLD);
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ static bool inLocalSubnet(const IPAddress &client) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static void generateEtag(char *etag, uint16_t eTagSuffix) {
|
static void generateEtag(char *etag, uint16_t eTagSuffix) {
|
||||||
sprintf_P(etag, PSTR("%7d-%02x-%04x"), VERSION, cacheInvalidate, eTagSuffix);
|
sprintf_P(etag, PSTR("%u-%02x-%04x"), WEB_BUILD_TIME, cacheInvalidate, eTagSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, uint16_t eTagSuffix = 0) {
|
static void setStaticContentCacheHeaders(AsyncWebServerResponse *response, int code, uint16_t eTagSuffix = 0) {
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
|||||||
settingsScript.printf_P(PSTR("d.ledTypes=%s;"), BusManager::getLEDTypesJSONString().c_str());
|
settingsScript.printf_P(PSTR("d.ledTypes=%s;"), BusManager::getLEDTypesJSONString().c_str());
|
||||||
|
|
||||||
// set limits
|
// set limits
|
||||||
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"),
|
settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d,%d);"),
|
||||||
WLED_MAX_BUSSES,
|
WLED_MAX_BUSSES,
|
||||||
WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
|
WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI
|
||||||
MAX_LEDS_PER_BUS,
|
MAX_LEDS_PER_BUS,
|
||||||
@@ -299,7 +299,8 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
|||||||
MAX_LEDS,
|
MAX_LEDS,
|
||||||
WLED_MAX_COLOR_ORDER_MAPPINGS,
|
WLED_MAX_COLOR_ORDER_MAPPINGS,
|
||||||
WLED_MAX_DIGITAL_CHANNELS,
|
WLED_MAX_DIGITAL_CHANNELS,
|
||||||
WLED_MAX_ANALOG_CHANNELS
|
WLED_MAX_ANALOG_CHANNELS,
|
||||||
|
WLED_MAX_BUTTONS
|
||||||
);
|
);
|
||||||
|
|
||||||
printSetFormCheckbox(settingsScript,PSTR("MS"),strip.autoSegments);
|
printSetFormCheckbox(settingsScript,PSTR("MS"),strip.autoSegments);
|
||||||
@@ -403,8 +404,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
|||||||
printSetFormValue(settingsScript,PSTR("RL"),rlyPin);
|
printSetFormValue(settingsScript,PSTR("RL"),rlyPin);
|
||||||
printSetFormCheckbox(settingsScript,PSTR("RM"),rlyMde);
|
printSetFormCheckbox(settingsScript,PSTR("RM"),rlyMde);
|
||||||
printSetFormCheckbox(settingsScript,PSTR("RO"),rlyOpenDrain);
|
printSetFormCheckbox(settingsScript,PSTR("RO"),rlyOpenDrain);
|
||||||
for (int i = 0; i < WLED_MAX_BUTTONS; i++) {
|
int i = 0;
|
||||||
settingsScript.printf_P(PSTR("addBtn(%d,%d,%d);"), i, btnPin[i], buttonType[i]);
|
for (const auto &button : buttons) {
|
||||||
|
settingsScript.printf_P(PSTR("addBtn(%d,%d,%d);"), i++, button.pin, button.type);
|
||||||
}
|
}
|
||||||
printSetFormCheckbox(settingsScript,PSTR("IP"),disablePullUp);
|
printSetFormCheckbox(settingsScript,PSTR("IP"),disablePullUp);
|
||||||
printSetFormValue(settingsScript,PSTR("TT"),touchThreshold);
|
printSetFormValue(settingsScript,PSTR("TT"),touchThreshold);
|
||||||
@@ -578,8 +580,9 @@ void getSettingsJS(byte subPage, Print& settingsScript)
|
|||||||
printSetFormValue(settingsScript,PSTR("A1"),macroAlexaOff);
|
printSetFormValue(settingsScript,PSTR("A1"),macroAlexaOff);
|
||||||
printSetFormValue(settingsScript,PSTR("MC"),macroCountdown);
|
printSetFormValue(settingsScript,PSTR("MC"),macroCountdown);
|
||||||
printSetFormValue(settingsScript,PSTR("MN"),macroNl);
|
printSetFormValue(settingsScript,PSTR("MN"),macroNl);
|
||||||
for (unsigned i=0; i<WLED_MAX_BUTTONS; i++) {
|
int i = 0;
|
||||||
settingsScript.printf_P(PSTR("addRow(%d,%d,%d,%d);"), i, macroButton[i], macroLongPress[i], macroDoublePress[i]);
|
for (const auto &button : buttons) {
|
||||||
|
settingsScript.printf_P(PSTR("addRow(%d,%d,%d,%d);"), i++, button.macroButton, button.macroLongPress, button.macroDoublePress);
|
||||||
}
|
}
|
||||||
|
|
||||||
char k[4];
|
char k[4];
|
||||||
|
|||||||
Reference in New Issue
Block a user