Multi button implementation.

Inverted button support.
Added configurable touch button and touch threshold.
This commit is contained in:
Blaz Kristan 2021-05-20 15:49:26 +02:00
commit 28e714db1e
13 changed files with 1131 additions and 957 deletions

View File

@ -6,105 +6,124 @@
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
void shortPressAction()
void shortPressAction(uint8_t b)
{
if (!macroButton)
if (!macroButton[b])
{
toggleOnOff();
colorUpdated(NOTIFIER_CALL_MODE_BUTTON);
} else {
applyPreset(macroButton);
applyPreset(macroButton[b]);
}
}
bool isButtonPressed()
bool isButtonPressed(uint8_t i)
{
if (btnPin>=0 && digitalRead(btnPin) == LOW) return true;
#ifdef TOUCHPIN
if (touchRead(TOUCHPIN) <= TOUCH_THRESHOLD) return true;
#endif
if (btnPin[i]<0) return false;
switch (buttonType[i]) {
case BTN_TYPE_NONE:
case BTN_TYPE_RESERVED:
break;
case BTN_TYPE_PUSH:
case BTN_TYPE_SWITCH:
if (digitalRead(btnPin[i]) == LOW) return true;
break;
case BTN_TYPE_PUSH_ACT_HIGH:
case BTN_TYPE_SWITCH_ACT_HIGH:
if (digitalRead(btnPin[i]) == HIGH) return true;
break;
case BTN_TYPE_TOUCH:
#ifdef ARDUINO_ARCH_ESP32
if (touchRead(btnPin[i]) <= touchThreshold) return true;
DEBUG_PRINT(F("Touch value: "));
DEBUG_PRINTLN(touchRead(btnPin[i]));
#endif
break;
}
return false;
}
void handleSwitch()
void handleSwitch(uint8_t b)
{
if (buttonPressedBefore != isButtonPressed()) {
buttonPressedTime = millis();
buttonPressedBefore = !buttonPressedBefore;
if (buttonPressedBefore[b] != isButtonPressed(b)) {
buttonPressedTime[b] = millis();
buttonPressedBefore[b] = !buttonPressedBefore[b];
}
if (buttonLongPressed == buttonPressedBefore) return;
if (buttonLongPressed[b] == buttonPressedBefore[b]) return;
if (millis() - buttonPressedTime > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (buttonPressedBefore) { //LOW, falling edge, switch closed
if (macroButton) applyPreset(macroButton);
if (millis() - buttonPressedTime[b] > WLED_DEBOUNCE_THRESHOLD) { //fire edge event only after 50ms without change (debounce)
if (buttonPressedBefore[b]) { //LOW, falling edge, switch closed
if (macroButton[b]) applyPreset(macroButton[b]);
else { //turn on
if (!bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
}
} else { //HIGH, rising edge, switch opened
if (macroLongPress) applyPreset(macroLongPress);
if (macroLongPress[b]) applyPreset(macroLongPress[b]);
else { //turn off
if (bri) {toggleOnOff(); colorUpdated(NOTIFIER_CALL_MODE_BUTTON);}
}
}
buttonLongPressed = buttonPressedBefore; //save the last "long term" switch state
buttonLongPressed[b] = buttonPressedBefore[b]; //save the last "long term" switch state
}
}
void handleButton()
{
if (btnPin<0 || buttonType < BTN_TYPE_PUSH) return;
for (uint8_t b=0; b<WLED_MAX_BUTTONS; b++) {
if (btnPin[b]<0 || !(buttonType[b] > BTN_TYPE_NONE)) continue;
if (buttonType == BTN_TYPE_SWITCH) { //button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NO gpio0)
handleSwitch(); return;
}
if (buttonType[b] == BTN_TYPE_SWITCH) { //button is not momentary, but switch. This is only suitable on pins whose on-boot state does not matter (NOT gpio0)
handleSwitch(b); continue;
}
//momentary button logic
if (isButtonPressed()) //pressed
{
if (!buttonPressedBefore) buttonPressedTime = millis();
buttonPressedBefore = true;
if (millis() - buttonPressedTime > 600) //long press
//momentary button logic
if (isButtonPressed(b)) //pressed
{
if (!buttonLongPressed)
{
if (macroLongPress) {applyPreset(macroLongPress);}
else _setRandomColor(false,true);
if (!buttonPressedBefore[b]) buttonPressedTime[b] = millis();
buttonPressedBefore[b] = true;
buttonLongPressed = true;
if (millis() - buttonPressedTime[b] > 600) //long press
{
if (!buttonLongPressed[b])
{
if (macroLongPress[b]) {applyPreset(macroLongPress[b]);}
else _setRandomColor(false,true);
buttonLongPressed[b] = true;
}
}
}
}
else if (!isButtonPressed() && buttonPressedBefore) //released
{
long dur = millis() - buttonPressedTime;
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore = false; return;} //too short "press", debounce
bool doublePress = buttonWaitTime;
buttonWaitTime = 0;
if (dur > 6000) //long press
else if (!isButtonPressed(b) && buttonPressedBefore[b]) //released
{
WLED::instance().initAP(true);
}
else if (!buttonLongPressed) { //short press
if (macroDoublePress)
{
if (doublePress) applyPreset(macroDoublePress);
else buttonWaitTime = millis();
} else shortPressAction();
}
buttonPressedBefore = false;
buttonLongPressed = false;
}
long dur = millis() - buttonPressedTime[b];
if (dur < WLED_DEBOUNCE_THRESHOLD) {buttonPressedBefore[b] = false; continue;} //too short "press", debounce
bool doublePress = buttonWaitTime[b];
buttonWaitTime[b] = 0;
if (buttonWaitTime && millis() - buttonWaitTime > 450 && !buttonPressedBefore)
{
buttonWaitTime = 0;
shortPressAction();
if (dur > 6000 && b==0) //long press on button 0
{
WLED::instance().initAP(true);
}
else if (!buttonLongPressed[b]) { //short press
if (macroDoublePress[b])
{
if (doublePress) applyPreset(macroDoublePress[b]);
else buttonWaitTime[b] = millis();
} else shortPressAction(b);
}
buttonPressedBefore[b] = false;
buttonLongPressed[b] = false;
}
if (buttonWaitTime[b] && millis() - buttonWaitTime[b] > 450 && !buttonPressedBefore[b])
{
buttonWaitTime[b] = 0;
shortPressAction(b);
}
}
}

View File

@ -131,10 +131,54 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (lC > ledCount) ledCount = lC; // fix incorrect total length (honour analog setup)
DEBUG_PRINTLN(F(" Done LEDs."));
// read multiple button configuration
JsonArray hw_btn_ins = hw[F("btn")][F("ins")];
if (!hw_btn_ins.isNull()) {
uint8_t s = 0;
for (JsonObject btn : hw_btn_ins) {
CJSON(buttonType[s], btn["type"]);
int8_t pin = btn[F("pin")][0] | -1;
if (pin > -1) {
if (pinManager.allocatePin(pin,false)) {
btnPin[s] = pin;
pinMode(btnPin[s], INPUT_PULLUP);
} else {
btnPin[s] = -1;
}
}
JsonArray hw_btn_ins_0_macros = btn[F("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
}
// 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 {
// new install/missing configuration (button 0 has defaults)
if (fromFS)
for (uint8_t s=1; s<WLED_MAX_BUTTONS; s++) {
btnPin[s] = -1;
buttonType[s] = BTN_TYPE_NONE;
macroButton[s] = 0;
macroLongPress[s] = 0;
macroDoublePress[s] = 0;
}
}
CJSON(touchThreshold,hw[F("btn")][F("tt")]);
DEBUG_PRINTLN(F(" Done buttons."));
/*
JsonObject hw_btn_ins_0 = hw[F("btn")][F("ins")][0];
CJSON(buttonType, hw_btn_ins_0["type"]);
int hw_btn_pin = hw_btn_ins_0[F("pin")][0] | -2; //-2 = not present in doc, keep current. -1 = disable
if (hw_btn_pin > -2) {
if (hw_btn_pin > -1) {
if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP);
@ -147,7 +191,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(macroButton, hw_btn_ins_0_macros[0]);
CJSON(macroLongPress,hw_btn_ins_0_macros[1]);
CJSON(macroDoublePress, hw_btn_ins_0_macros[2]);
*/
#ifndef WLED_DISABLE_INFRARED
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) {
@ -509,21 +553,34 @@ void serializeConfig() {
ins[F("rgbw")] = bus->isRgbw();
}
// button(s)
JsonObject hw_btn = hw.createNestedObject("btn");
hw_btn["max"] = WLED_MAX_BUTTONS; // just information about max number of buttons (not actually used)
JsonArray hw_btn_ins = hw_btn.createNestedArray("ins");
// button BTNPIN
// there is always at least one button
JsonObject hw_btn_ins_0 = hw_btn_ins.createNestedObject();
hw_btn_ins_0["type"] = buttonType;
hw_btn_ins_0["type"] = buttonType[0];
JsonArray hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
hw_btn_ins_0_pin.add(btnPin);
hw_btn_ins_0_pin.add(btnPin[0]);
JsonArray hw_btn_ins_0_macros = hw_btn_ins_0.createNestedArray("macros");
hw_btn_ins_0_macros.add(macroButton);
hw_btn_ins_0_macros.add(macroLongPress);
hw_btn_ins_0_macros.add(macroDoublePress);
hw_btn_ins_0_macros.add(macroButton[0]);
hw_btn_ins_0_macros.add(macroLongPress[0]);
hw_btn_ins_0_macros.add(macroDoublePress[0]);
// additional buttons
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) {
//if (btnPin[i]<0) continue;
hw_btn_ins_0 = hw_btn_ins.createNestedObject();
hw_btn_ins_0["type"] = buttonType[i];
hw_btn_ins_0_pin = hw_btn_ins_0.createNestedArray("pin");
hw_btn_ins_0_pin.add(btnPin[i]);
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(macroLongPress[i]);
hw_btn_ins_0_macros.add(macroDoublePress[i]);
}
hw_btn[F("tt")] = touchThreshold;
JsonObject hw_ir = hw.createNestedObject("ir");
hw_ir["pin"] = irPin;

View File

@ -31,6 +31,14 @@
#endif
#endif
#ifndef WLED_MAX_BUTTONS
#ifdef ESP8266
#define WLED_MAX_BUTTONS 2
#else
#define WLED_MAX_BUTTONS 4
#endif
#endif
//Usermod IDs
#define USERMOD_ID_RESERVED 0 //Unused. Might indicate no usermod present
#define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID
@ -151,6 +159,7 @@
#define BTN_TYPE_PUSH_ACT_HIGH 3 //not implemented
#define BTN_TYPE_SWITCH 4
#define BTN_TYPE_SWITCH_ACT_HIGH 5 //not implemented
#define BTN_TYPE_TOUCH 6
//Ethernet board types
#define WLED_NUM_ETH_TYPES 5

View File

@ -14,6 +14,7 @@
{
window.open("/settings","_self");
}
function gId(n){return d.getElementById(n);}
function off(n){
d.getElementsByName(n)[0].value = -1;
}
@ -48,32 +49,32 @@
function S(){GetV();setABL();}
function enABL()
{
var en = d.getElementById('able').checked;
var en = gId('able').checked;
d.Sf.LA.value = (en) ? laprev:0;
d.getElementById('abl').style.display = (en) ? 'inline':'none';
d.getElementById('psu2').style.display = (en) ? 'inline':'none';
gId('abl').style.display = (en) ? 'inline':'none';
gId('psu2').style.display = (en) ? 'inline':'none';
if (d.Sf.LA.value > 0) setABL();
}
function enLA()
{
var val = d.Sf.LAsel.value;
d.Sf.LA.value = val;
d.getElementById('LAdis').style.display = (val == 50) ? 'inline':'none';
gId('LAdis').style.display = (val == 50) ? 'inline':'none';
UI();
}
function setABL()
{
d.getElementById('able').checked = true;
gId('able').checked = true;
d.Sf.LAsel.value = 50;
switch (parseInt(d.Sf.LA.value)) {
case 0: d.getElementById('able').checked = false; enABL(); break;
case 0: gId('able').checked = false; enABL(); break;
case 30: d.Sf.LAsel.value = 30; break;
case 35: d.Sf.LAsel.value = 35; break;
case 55: d.Sf.LAsel.value = 55; break;
case 255: d.Sf.LAsel.value = 255; break;
default: d.getElementById('LAdis').style.display = 'inline';
default: gId('LAdis').style.display = 'inline';
}
d.getElementById('m1').innerHTML = maxM;
gId('m1').innerHTML = maxM;
d.getElementsByName("Sf")[0].addEventListener("submit", trySubmit);
UI();
}
@ -100,7 +101,7 @@
{
var isRGBW = false, memu = 0;
d.getElementById('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
gId('ampwarning').style.display = (d.Sf.MA.value > 7200) ? 'inline':'none';
if (d.Sf.LA.value == 255) laprev = 12;
else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value;
@ -110,8 +111,8 @@
if (s[i].name.substring(0,2)=="LT") {
n=s[i].name.substring(2);
var type = parseInt(s[i].value,10);
d.getElementById("p0d"+n).innerHTML = (type > 49) ? "Data:" : (type >41) ? "Pins:" : "Pin:";
d.getElementById("p1d"+n).innerHTML = (type > 49) ? "Clk:" : "";
gId("p0d"+n).innerHTML = (type > 49) ? "Data:" : (type >41) ? "Pins:" : "Pin:";
gId("p1d"+n).innerHTML = (type > 49) ? "Clk:" : "";
var LK = d.getElementsByName("L1"+n)[0];
memu += getMem(type, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value);
@ -129,17 +130,17 @@
LK.value="";
}
}
d.getElementById("ls"+n).readOnly = !(type > 31 && type < 48); // not analog
d.getElementById("LC").readOnly = !(type > 31 && type < 48); // not analog
gId("ls"+n).readOnly = !(type > 31 && type < 48); // not analog
gId("LC").readOnly = !(type > 31 && type < 48); // not analog
if (change) {
// d.getElementById("ew"+n).checked = (type == 30 || type == 31 || type == 44 || type == 45); // RGBW checkbox, TYPE_xxxx values from const.h
d.getElementById("ls"+n).value = n+1;
// gId("ew"+n).checked = (type == 30 || type == 31 || type == 44 || type == 45); // RGBW checkbox, TYPE_xxxx values from const.h
gId("ls"+n).value = n+1;
}
// d.getElementById("ew"+n).onclick = (type > 31 && type < 48) ? (function(){return false}) : (function(){}); // prevent change for analog
// isRGBW |= d.getElementById("ew"+n).checked;
// gId("ew"+n).onclick = (type > 31 && type < 48) ? (function(){return false}) : (function(){}); // prevent change for analog
// isRGBW |= gId("ew"+n).checked;
isRGBW |= (type == 30 || type == 31 || type == 44 || type == 45); // RGBW checkbox, TYPE_xxxx values from const.h
d.getElementById("dig"+n).style.display = (type > 31 && type < 48) ? "none":"inline";
d.getElementById("psd"+n).innerHTML = (type > 31 && type < 48) ? "Index:":"Start:";
gId("dig"+n).style.display = (type > 31 && type < 48) ? "none":"inline";
gId("psd"+n).innerHTML = (type > 31 && type < 48) ? "Index:":"Start:";
}
}
@ -156,7 +157,7 @@
if (nm=="LC" && LCs[i].name !== "LC") {
var n=LCs[i].name.substring(2);
var c=parseInt(LCs[i].value,10);
if(d.getElementById("ls"+n).readOnly) d.getElementById("ls"+n).value=sLC;
if(gId("ls"+n).readOnly) gId("ls"+n).value=sLC;
if(c){sLC+=c;if(c>maxLC)maxLC=c;}
continue;
}
@ -178,18 +179,18 @@
}
}
if (d.getElementById("LC").readOnly) d.getElementsByName("LC")[0].value = sLC;
if (gId("LC").readOnly) d.getElementsByName("LC")[0].value = sLC;
if (d.activeElement == d.getElementsByName("LC")[0]) {
var o = d.getElementsByClassName("iST");
var i = o.length;
if (i == 1) d.getElementsByName("LC0")[0].value = d.getElementsByName("LC")[0].value;
}
d.getElementById('m0').innerHTML = memu;
gId('m0').innerHTML = memu;
bquot = memu / maxM * 100;
d.getElementById('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
d.getElementById('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
d.getElementById('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
d.getElementById('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>WARNING: Using over ${maxM}B!</b>)` : "") : "800 LEDs per pin";
gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>WARNING: Using over ${maxM}B!</b>)` : "") : "800 LEDs per pin";
var val = Math.ceil((100 + sLC * laprev)/500)/2;
val = (val > 5) ? Math.ceil(val) : val;
@ -210,8 +211,8 @@
var s2 = "(for most effects, ~";
s2 += val2;
s2 += "A is enough)<br>";
d.getElementById('psu').innerHTML = s;
d.getElementById('psu2').innerHTML = isWS2815 ? "" : s2;
gId('psu').innerHTML = s;
gId('psu2').innerHTML = isWS2815 ? "" : s2;
}
function lastEnd(i) {
if (i<1) return 0;
@ -221,14 +222,14 @@
}
function addLEDs(n)
{
if (n>1) {maxB=n; d.getElementById("+").style.display="inline"; return;}
if (n>1) {maxB=n; gId("+").style.display="inline"; return;}
var o = d.getElementsByClassName("iST");
var i = o.length;
if ((n==1 && i>=maxB) || (n==-1 && i==0)) return;
var f = d.getElementById("mLC");
var f = gId("mLC");
if (n==1) {
// npm run build has trouble minimizing spaces inside string
var cn = `<div class="iST">
@ -277,15 +278,31 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
o[--i].remove();--i;
}
d.getElementById("+").style.display = (i<maxB-1) ? "inline":"none";
d.getElementById("-").style.display = (i>0) ? "inline":"none";
gId("+").style.display = (i<maxB-1) ? "inline":"none";
gId("-").style.display = (i>0) ? "inline":"none";
UI();
}
function addBtn(i,p,t) {
var c = gId("btns").innerHTML;
var bt = "BT" + i;
var be = "BE" + i;
c += `Button ${i} pin: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" value="${p}">`;
c += `<select name="${be}">`
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
c += `<option value="2" ${t==3?"selected":""}>Push inverted</option>`;
c += `<option value="4" ${t==4?"selected":""}>Switch</option>`;
c += `<option value="5" ${t==4?"selected":""}>Switch inverted</option>`;
c += `<option value="6" ${t==6?"selected":""}>Touch</option>`;
c += `</select>`;
c += `<span style="cursor: pointer;" onclick="off('${bt}')">&nbsp;&#215;</span><br>`;
gId("btns").innerHTML = c;
}
function GetV()
{
//values injected by server while sending HTML
//maxM=5000;maxPB=1536;d.um_p=[1,6,7,8,9,10,11];addLEDs(3);d.Sf.LC.value=250;addLEDs(1);d.Sf.L00.value=2;d.Sf.L10.value=0;d.Sf.LC0.value=250;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=0;d.Sf.LS0.checked=0;d.Sf.MA.value=5400;d.Sf.LA.value=55;d.getElementsByClassName("pow")[0].innerHTML="350mA";d.Sf.CA.value=40;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=3;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=64;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.SL.checked=0;d.Sf.RL.value=12;d.Sf.RM.checked=0;d.Sf.BT.value=-1;d.Sf.IR.value=-1;d.Sf.AX.value=-1;
//maxM=5000;maxPB=1536;d.um_p=[1,6,7,8,9,10,11];addLEDs(3);d.Sf.LC.value=250;addLEDs(1);d.Sf.L00.value=2;d.Sf.L10.value=0;d.Sf.LC0.value=250;d.Sf.LT0.value=22;d.Sf.CO0.value=0;d.Sf.LS0.value=0;d.Sf.LS0.checked=0;d.Sf.MA.value=5400;d.Sf.LA.value=55;d.getElementsByClassName("pow")[0].innerHTML="350mA";d.Sf.CA.value=40;d.Sf.AW.value=3;d.Sf.BO.checked=0;d.Sf.BP.value=3;d.Sf.GB.checked=0;d.Sf.GC.checked=1;d.Sf.TF.checked=1;d.Sf.TD.value=700;d.Sf.PF.checked=0;d.Sf.BF.value=64;d.Sf.TB.value=0;d.Sf.TL.value=60;d.Sf.TW.value=1;d.Sf.PB.selectedIndex=0;d.Sf.RL.value=12;d.Sf.RM.checked=0;addBtn(0,0,2);addBtn(1,3,4);addBtn(2,-1,0);d.Sf.IR.value=-1;
}
</script>
<style>
@ -338,10 +355,21 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
&#9888; You might run into stability or lag issues.<br>
Use less than <span id="wreason">800 LEDs per pin</span> for the best experience!<br>
</div><hr style="width:260px">
Button pin: <input type="number" min="-1" max="40" name="BT" onchange="UI()"><span style="cursor: pointer;" onclick="off('BT')">&nbsp;&#215;</span><br>
IR pin: <input type="number" min="-1" max="40" name="IR" onchange="UI()"><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#215;</span><br>
Relay pin: <input type="number" min="-1" max="40" name="RL" onchange="UI()"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br>
Active high <input type="checkbox" name="RM">
<div id="btns"></div>
Touch threshold: <input type="number" min="0" max="100" name="TT" required><br>
IR pin: <input type="number" min="-1" max="40" name="IR" onchange="UI()"><select name="IT">
<option value="0">Remote disabled</option>
<option value="1">24-key RGB</option>
<option value="2">24-key with CT</option>
<option value="3">40-key blue</option>
<option value="4">44-key RGB</option>
<option value="5">21-key RGB</option>
<option value="6">6-key black</option>
<option value="7">9-key red</option>
<option value="8">JSON remote</option>
</select><span style="cursor: pointer;" onclick="off('IR')">&nbsp;&#215;</span><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a><br>
Relay pin: <input type="number" min="-1" max="40" name="RL" onchange="UI()"> invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')">&nbsp;&#215;</span><br>
<hr style="width:260px">
<h3>Defaults</h3>
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
@ -374,7 +402,6 @@ Reverse (rotated 180°): <input type="checkbox" name="CV${i}">
<option value="2">Linear (never wrap)</option>
<option value="3">None (not recommended)</option>
</select><br>
<!--Skip first LED: <input type="checkbox" name="SL"><br-->
<span class="wc">
Auto-calculate white channel from RGB:<br>
<select name="AW">

View File

@ -17,6 +17,7 @@ function GetV(){var d=document;}
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
</div>
<h2>Sync setup</h2>
<!--
<h3>Button setup</h3>
Button type:
<select name=BT>
@ -37,6 +38,7 @@ Infrared remote:
<option value=8>JSON remote</option>
</select><br>
<a href="https://github.com/Aircoookie/WLED/wiki/Infrared-Control" target="_blank">IR info</a>
-->
<h3>WLED Broadcast</h3>
UDP Port: <input name="UP" type="number" min="1" max="65535" class="d5" required><br>
2nd Port: <input name="U2" type="number" min="1" max="65535" class="d5" required><br>

View File

@ -75,9 +75,25 @@
gId("W"+i).value=a[i];
}
}
function addRow(i,p,l,d) {
var t = gId("macros"); // table
var rCnt = t.rows.length; // get the number of rows.
var tr = t.insertRow(rCnt); // table row.
var td = document.createElement('td'); // TABLE DEFINITION.
td = tr.insertCell(0);
td.innerHTML = `Button ${i}:`;
td = tr.insertCell(1);
td.innerHTML = `<input name="MP${i}" type="number" min="0" max="250" value="${p}" required>`;
td = tr.insertCell(2);
td.innerHTML = `<input name="ML${i}" type="number" min="0" max="250" value="${l}" required>`;
td = tr.insertCell(3);
td.innerHTML = `<input name="MD${i}" type="number" min="0" max="250" value="${d}" required>`;
}
function GetV()
{
//values injected by server while sending HTML
addRow(0,23,4,0);
}
</script>
<style>
@ -109,12 +125,12 @@
<option value="10">JST(KST)</option>
<option value="11">AEST/AEDT</option>
<option value="12">NZST/NZDT</option>
<option value="13">North Korea</option>
<option value="14">IST (India)</option>
<option value="15">CA-Saskatchewan</option>
<option value="16">ACST</option>
<option value="17">ACST/ACDT</option>
<option value="18">HST (Hawaii)</option>
<option value="13">North Korea</option>
<option value="14">IST (India)</option>
<option value="15">CA-Saskatchewan</option>
<option value="16">ACST</option>
<option value="17">ACST/ACDT</option>
<option value="18">HST (Hawaii)</option>
</select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br>
@ -145,15 +161,26 @@
Year: 20 <input name="CY" class="small" type="number" min="0" max="99" required> Month: <input name="CI" class="small" type="number" min="1" max="12" required> Day: <input name="CD" class="small" type="number" min="1" max="31" required><br>
Hour: <input name="CH" class="small" type="number" min="0" max="23" required> Minute: <input name="CM" class="small" type="number" min="0" max="59" required> Second: <input name="CS" class="small" type="number" min="0" max="59" required><br>
<h3>Macro presets</h3>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i>
<b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>
Just enter the preset id below!</i>
<i>Use 0 for the default action instead of a preset</i><br>
Alexa On/Off Preset: <input name="A0" type="number" min="0" max="250" required> <input name="A1" type="number" min="0" max="250" required><br>
Button short press Preset: <input name="MP" type="number" min="0" max="250" required><br>
Long Press: <input name="ML" type="number" min="0" max="250" required> Double press: <input name="MD" type="number" min="0" max="250" required><br>
Countdown-Over Preset: <input name="MC" type="number" min="0" max="250" required><br>
Timed-Light-Over Presets: <input name="MN" type="number" min="0" max="250" required><br>
<h3>Button actions</h3>
<table style="margin: 0 auto;" id="macros">
<thead>
<tr>
<td>push<br>switch</td>
<td>short<br>on-&gt;off</td>
<td>long<br>off-&gt;on</td>
<td>double<br>N/A</td>
</tr>
</thead>
<tbody>
</tbody>
</table>
<h3>Time-controlled presets</h3>
<div style="display: inline-block">
<table id="TMT">

View File

@ -20,8 +20,8 @@ void handleBlynk();
void updateBlynk();
//button.cpp
void shortPressAction();
bool isButtonPressed();
void shortPressAction(uint8_t b=0);
bool isButtonPressed(uint8_t b=0);
void handleButton();
void handleIO();

File diff suppressed because one or more lines are too long

View File

@ -81,7 +81,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifndef WLED_DISABLE_INFRARED
if (irPin>=0 && pinManager.isPinAllocated(irPin)) pinManager.deallocatePin(irPin);
#endif
if (btnPin>=0 && pinManager.isPinAllocated(btnPin)) pinManager.deallocatePin(btnPin);
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++)
if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s]))
pinManager.deallocatePin(btnPin[s]);
strip.isRgbw = false;
uint8_t colorOrder, type, skip;
@ -135,6 +137,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
} else {
irPin = -1;
}
irEnabled = request->arg(F("IT")).toInt();
#endif
int hw_rly_pin = request->arg(F("RL")).toInt();
@ -145,13 +148,20 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
}
rlyMde = (bool)request->hasArg(F("RM"));
int hw_btn_pin = request->arg(F("BT")).toInt();
if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin = hw_btn_pin;
pinMode(btnPin, INPUT_PULLUP);
} else {
btnPin = -1;
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
char bt[4] = "BT"; bt[2] = 48+i; bt[3] = 0; // button pin
char be[4] = "BE"; be[2] = 48+i; be[3] = 0; // button type
int hw_btn_pin = request->arg(bt).toInt();
if (pinManager.allocatePin(hw_btn_pin,false)) {
btnPin[i] = hw_btn_pin;
pinMode(btnPin[i], INPUT_PULLUP);
buttonType[i] = request->arg(be).toInt();
} else {
btnPin[i] = -1;
buttonType[i] = BTN_TYPE_NONE;
}
}
touchThreshold = request->arg(F("TT")).toInt();
strip.ablMilliampsMax = request->arg(F("MA")).toInt();
strip.milliampsPerLed = request->arg(F("LA")).toInt();
@ -195,8 +205,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
//SYNC
if (subPage == 4)
{
buttonType = request->arg(F("BT")).toInt();
irEnabled = request->arg(F("IR")).toInt();
//buttonType = request->arg(F("BT")).toInt();
//irEnabled = request->arg(F("IR")).toInt();
int t = request->arg(F("UP")).toInt();
if (t > 0) udpPort = t;
t = request->arg(F("U2")).toInt();
@ -324,11 +334,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
macroAlexaOn = request->arg(F("A0")).toInt();
macroAlexaOff = request->arg(F("A1")).toInt();
macroButton = request->arg(F("MP")).toInt();
macroLongPress = request->arg(F("ML")).toInt();
macroCountdown = request->arg(F("MC")).toInt();
macroNl = request->arg(F("MN")).toInt();
macroDoublePress = request->arg(F("MD")).toInt();
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
char mp[4] = "MP"; mp[2] = 48+i; mp[3] = 0; // short
char ml[4] = "ML"; ml[2] = 48+i; ml[3] = 0; // long
char md[4] = "MD"; md[2] = 48+i; md[3] = 0; // double
//if (!request->hasArg(mp)) break;
macroButton[i] = request->arg(mp).toInt(); // these will default to 0 if not present
macroLongPress[i] = request->arg(ml).toInt();
macroDoublePress[i] = request->arg(md).toInt();
}
char k[3]; k[2] = 0;
for (int i = 0; i<10; i++)

File diff suppressed because it is too large Load Diff

View File

@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.12.2-bl2
@version 0.12.2-bl3
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2105191
#define VERSION 2105201
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -208,9 +208,9 @@ WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
// Hardware CONFIG (only changeble HERE, not at runtime)
// LED strip pin, button pin and IR pin changeable in NpbWrapper.h!
#ifndef BTNPIN
WLED_GLOBAL int8_t btnPin _INIT(0);
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({0});
#else
WLED_GLOBAL int8_t btnPin _INIT(BTNPIN);
WLED_GLOBAL int8_t btnPin[WLED_MAX_BUTTONS] _INIT({BTNPIN});
#endif
#ifndef RLYPIN
WLED_GLOBAL int8_t rlyPin _INIT(12);
@ -279,7 +279,7 @@ WLED_GLOBAL NodesMap Nodes;
WLED_GLOBAL bool nodeListEnabled _INIT(true);
WLED_GLOBAL bool nodeBroadcastEnabled _INIT(true);
WLED_GLOBAL byte buttonType _INIT(BTN_TYPE_PUSH);
WLED_GLOBAL byte buttonType[WLED_MAX_BUTTONS] _INIT({BTN_TYPE_PUSH});
WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver
WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port
@ -370,7 +370,9 @@ WLED_GLOBAL byte countdownMin _INIT(0) , countdownSec _INIT(0);
WLED_GLOBAL byte macroNl _INIT(0); // after nightlight delay over
WLED_GLOBAL byte macroCountdown _INIT(0);
WLED_GLOBAL byte macroAlexaOn _INIT(0), macroAlexaOff _INIT(0);
WLED_GLOBAL byte macroButton _INIT(0), macroLongPress _INIT(0), macroDoublePress _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
WLED_GLOBAL bool otaLock _INIT(false); // prevents OTA firmware updates without password. ALWAYS enable if system exposed to any public networks
@ -431,10 +433,11 @@ WLED_GLOBAL byte briLast _INIT(128); // brightness before turned off. U
WLED_GLOBAL byte whiteLast _INIT(128); // white channel before turned off. Used for toggle function
// button
WLED_GLOBAL bool buttonPressedBefore _INIT(false);
WLED_GLOBAL bool buttonLongPressed _INIT(false);
WLED_GLOBAL unsigned long buttonPressedTime _INIT(0);
WLED_GLOBAL unsigned long buttonWaitTime _INIT(0);
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 byte touchThreshold _INIT(TOUCH_THRESHOLD);
// notifications
WLED_GLOBAL bool notifyDirectDefault _INIT(notifyDirect);

View File

@ -93,7 +93,7 @@ void loadSettingsFromEEPROM()
notifyButton = EEPROM.read(230);
notifyTwice = EEPROM.read(231);
buttonType = EEPROM.read(232) ? BTN_TYPE_PUSH : BTN_TYPE_NONE;
buttonType[0] = EEPROM.read(232) ? BTN_TYPE_PUSH : BTN_TYPE_NONE;
staticIP[0] = EEPROM.read(234);
staticIP[1] = EEPROM.read(235);
@ -198,12 +198,12 @@ void loadSettingsFromEEPROM()
//macroBoot = EEPROM.read(2175);
macroAlexaOn = EEPROM.read(2176);
macroAlexaOff = EEPROM.read(2177);
macroButton = EEPROM.read(2178);
macroLongPress = EEPROM.read(2179);
macroButton[0] = EEPROM.read(2178);
macroLongPress[0] = EEPROM.read(2179);
macroCountdown = EEPROM.read(2180);
macroNl = EEPROM.read(2181);
macroDoublePress = EEPROM.read(2182);
if (macroDoublePress > 16) macroDoublePress = 0;
macroDoublePress[0] = EEPROM.read(2182);
if (macroDoublePress[0] > 16) macroDoublePress[0] = 0;
}
if (lastEEPROMversion > 6)

View File

@ -392,8 +392,16 @@ void getSettingsJS(byte subPage, char* dest)
sappend('i',SET_F("PB"),strip.paletteBlend);
sappend('v',SET_F("RL"),rlyPin);
sappend('c',SET_F("RM"),rlyMde);
sappend('v',SET_F("BT"),btnPin);
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
oappend(SET_F("addBtn("));
oappend(itoa(i,nS,10)); oappend(",");
oappend(itoa(btnPin[i],nS,10)); oappend(",");
oappend(itoa(buttonType[i],nS,10));
oappend(SET_F(");"));
}
sappend('v',SET_F("TT"),touchThreshold);
sappend('v',SET_F("IR"),irPin);
sappend('v',SET_F("IT"),irEnabled);
}
if (subPage == 3)
@ -404,8 +412,8 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage == 4)
{
sappend('v',SET_F("BT"),buttonType);
sappend('v',SET_F("IR"),irEnabled);
//sappend('v',SET_F("BT"),buttonType);
//sappend('v',SET_F("IR"),irEnabled);
sappend('v',SET_F("UP"),udpPort);
sappend('v',SET_F("U2"),udpPort2);
sappend('c',SET_F("RB"),receiveNotificationBrightness);
@ -521,11 +529,19 @@ void getSettingsJS(byte subPage, char* dest)
sappend('v',SET_F("A0"),macroAlexaOn);
sappend('v',SET_F("A1"),macroAlexaOff);
sappend('v',SET_F("MP"),macroButton);
sappend('v',SET_F("ML"),macroLongPress);
sappend('v',SET_F("MC"),macroCountdown);
sappend('v',SET_F("MN"),macroNl);
sappend('v',SET_F("MD"),macroDoublePress);
//sappend('v',SET_F("MP"),macroButton);
//sappend('v',SET_F("ML"),macroLongPress);
//sappend('v',SET_F("MD"),macroDoublePress);
for (uint8_t i=0; i<WLED_MAX_BUTTONS; i++) {
oappend(SET_F("addRow("));
oappend(itoa(i,tm,10)); oappend(",");
oappend(itoa(macroButton[i],tm,10)); oappend(",");
oappend(itoa(macroLongPress[i],tm,10)); oappend(",");
oappend(itoa(macroDoublePress[i],tm,10));
oappend(SET_F(");"));
}
char k[4];
k[2] = 0; //Time macros