From 95df91a03b32eef371fcbbfa7dc961d5cc85e41a Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 19 May 2021 18:39:16 +0200 Subject: [PATCH 1/6] Multi button implementation. --- wled00/button.cpp | 114 +-- wled00/cfg.cpp | 60 +- wled00/const.h | 8 + wled00/data/settings_leds.htm | 99 ++- wled00/data/settings_sync.htm | 2 + wled00/data/settings_time.htm | 49 +- wled00/fcn_declare.h | 4 +- wled00/html_settings.h | 101 ++- wled00/set.cpp | 40 +- wled00/wled.cpp | 1502 ++++++++++++++++----------------- wled00/wled.h | 1326 ++++++++++++++--------------- wled00/wled_eeprom.cpp | 10 +- wled00/xml.cpp | 27 +- 13 files changed, 1736 insertions(+), 1606 deletions(-) diff --git a/wled00/button.cpp b/wled00/button.cpp index bf07335e2..ce8421bc4 100644 --- a/wled00/button.cpp +++ b/wled00/button.cpp @@ -6,20 +6,20 @@ #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; + if (btnPin[i]>=0 && digitalRead(btnPin[i]) == LOW) return true; #ifdef TOUCHPIN if (touchRead(TOUCHPIN) <= TOUCH_THRESHOLD) return true; #endif @@ -27,84 +27,86 @@ bool isButtonPressed() } -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 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); + } } } diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 39a9b177b..aec52c98d 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -131,10 +131,41 @@ 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 -2) { + if (hw_btn_pin > -1) { if (pinManager.allocatePin(hw_btn_pin,false)) { btnPin = hw_btn_pin; pinMode(btnPin, INPUT_PULLUP); @@ -147,7 +178,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 +540,28 @@ void serializeConfig() { ins[F("rgbw")] = bus->isRgbw(); } + // button(s) JsonObject hw_btn = hw.createNestedObject("btn"); - JsonArray hw_btn_ins = hw_btn.createNestedArray("ins"); - // button BTNPIN 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); + // additional buttons + for (uint8_t i=1; i 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 ? ` (WARNING: Using over ${maxM}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 ? ` (WARNING: Using over ${maxM}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)
"; - 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 = `
@@ -277,15 +278,28 @@ Reverse (rotated 180°): o[--i].remove();--i; } - d.getElementById("+").style.display = (i0) ? "inline":"none"; + gId("+").style.display = (i0) ? "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: `; + c += ``; + c += ` ×
`; + 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; }