From e2f11630fa889d345f3a31e56b905019f27ba6cd Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 1 Nov 2020 12:21:34 -0600 Subject: [PATCH] Updated SpanButtton to recognize new Double Press event button(int pin, boolean isLong) changed to button(int pin, int pressType), where pressType can be SpanButton::LONG, SpanButton::SINGLE, or SpanButton::DOUBLE. Updated Example 16 and confirmed everything works as expected. To do: Review all prior examples and update SpanButton when needed. Also need to update Zephyr Vent Hood code! --- .../16-ProgrammableSwitches/DEV_ProgButton.h | 14 ++-- src/HAP.cpp | 8 +- src/HomeSpan.cpp | 13 ++-- src/HomeSpan.h | 15 +++- src/Utils.cpp | 75 +++---------------- src/Utils.h | 44 ++++++----- 6 files changed, 67 insertions(+), 102 deletions(-) diff --git a/examples/16-ProgrammableSwitches/DEV_ProgButton.h b/examples/16-ProgrammableSwitches/DEV_ProgButton.h index b9210cb..1d9a790 100644 --- a/examples/16-ProgrammableSwitches/DEV_ProgButton.h +++ b/examples/16-ProgrammableSwitches/DEV_ProgButton.h @@ -5,7 +5,6 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateless Programmable Switch - int buttonPin; // pin with programmable pushbutton SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic DEV_ProgButton(int buttonPin, int index) : Service::StatelessProgrammableSwitch(){ @@ -13,10 +12,9 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateles switchEvent=new Characteristic::ProgrammableSwitchEvent(); // ProgrammableSwitchEvent Characteristic new Characteristic::ServiceLabelIndex(index); // set service label index - new SpanButton(buttonPin); // create new SpanButton - this->buttonPin=buttonPin; // save button pin number + new SpanButton(buttonPin); // create new SpanButton - Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message + Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message Serial.print(buttonPin); Serial.print(" Index="); Serial.print(index); @@ -24,15 +22,17 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateles } // end constructor - void button(int pin, boolean isLong) override { + void button(int pin, int pressType) override { LOG1("Found button press on pin: "); // always a good idea to log messages LOG1(pin); LOG1(" type: "); - LOG1(isLong?"LONG":"SHORT"); + LOG1(pressType); + LOG1(" "); + LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE"); LOG1("\n"); - switchEvent->setVal(isLong?2:0); // set the value of the switchEvent Characteristic to 2 for long press or 0 for short press + switchEvent->setVal(pressType); // set the value of the switchEvent Characteristic } diff --git a/src/HAP.cpp b/src/HAP.cpp index d5841b8..d161973 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -1209,10 +1209,10 @@ void HAPClient::callServiceLoops(){ void HAPClient::checkPushButtons(){ - for(int i=0;ipushButton->triggered(sb->shortTime,sb->longTime)){ // if the underlying PushButton is triggered - sb->service->button(sb->pin,sb->pushButton->longPress()); // call the Service's button() routine with pin and longPress() as parameters + for(int i=0;ipushButton->triggered(sb->singleTime,sb->longTime,sb->doubleTime)){ // if the underlying PushButton is triggered + sb->service->button(sb->pin,sb->pushButton->type()); // call the Service's button() routine with pin and type as parameters } } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 4d55ec0..2cca102 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -188,7 +188,7 @@ void Span::poll() { if(controlButton.triggered(3000,10000)){ statusLED.off(); - if(controlButton.longPress()){ + if(controlButton.type()==PushButton::LONG){ controlButton.wait(); processSerialCommand("F"); // FACTORY RESET } else { @@ -232,7 +232,7 @@ void Span::commandMode(){ delay(2000); } else if(controlButton.triggered(10,3000)){ - if(!controlButton.longPress()){ + if(controlButton.type()==PushButton::SINGLE){ mode++; if(mode==6) mode=1; @@ -1530,9 +1530,9 @@ SpanRange::SpanRange(int min, int max, int step){ // SpanButton // /////////////////////////////// -SpanButton::SpanButton(int pin, unsigned long longTime, unsigned long shortTime){ +SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t doubleTime){ - homeSpan.configLog+="---->SpanButton: Pin " + String(pin); + homeSpan.configLog+="---->SpanButton: Pin=" + String(pin) + " Long/Single/Double=" + String(longTime) + "/" + String(singleTime) + "/" + String(doubleTime) + " ms"; if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){ homeSpan.configLog+=" *** ERROR! Can't create new PushButton without a defined Service! ***\n"; @@ -1545,11 +1545,12 @@ SpanButton::SpanButton(int pin, unsigned long longTime, unsigned long shortTime) Serial.print("\n"); this->pin=pin; - this->shortTime=shortTime; this->longTime=longTime; + this->singleTime=singleTime; + this->doubleTime=doubleTime; service=homeSpan.Accessories.back()->Services.back(); - if((void(*)(int,boolean))(service->*(&SpanService::button))==(void(*)(int,boolean))(&SpanService::button)) + if((void(*)(int,int))(service->*(&SpanService::button))==(void(*)(int,int))(&SpanService::button)) homeSpan.configLog+=" *** WARNING: No button() method defined for this PushButton! ***"; pushButton=new PushButton(pin); // create underlying PushButton diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 2b18d8e..4cfd920 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -149,7 +149,7 @@ struct SpanService{ virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update virtual void loop(){} // loops for each Service - called every cycle and can be over-ridden with user-defined code - virtual void button(int pin, boolean isLong){} // method called for a Service when a button attached to "pin" has a Short-Press or Long-Press, according to "isLong" + virtual void button(int pin, int pressType){} // method called for a Service when a button attached to "pin" has a Single, Double, or Long Press, according to pressType }; /////////////////////////////// @@ -281,14 +281,21 @@ struct SpanBuf{ // temporary storage buffer for us struct SpanButton{ + enum { + SINGLE=0, + DOUBLE=1, + LONG=2 + }; + int pin; // pin number - unsigned long shortTime; // time (in millis) required to register a short press - unsigned long longTime; // time (in millis) required to register a long press + uint16_t singleTime; // minimum time (in millis) required to register a single press + uint16_t longTime; // minimum time (in millis) required to register a long press + uint16_t doubleTime; // maximum time (in millis) between single presses to register a double press instead SpanService *service; // Service to which this PushButton is attached PushButton *pushButton; // PushButton associated with this SpanButton - SpanButton(int pin, unsigned long longTime=2000, unsigned long shortTime=5); + SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200); }; ///////////////////////////////////////////////// diff --git a/src/Utils.cpp b/src/Utils.cpp index d1488a8..5cf9f0d 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -8,7 +8,7 @@ // Utils::readSerial - reads all characters from Serial port and saves only up to max specified // Utils::mask - masks a string with asterisks (good for displaying passwords) // -// class PushButton - tracks Long and Short presses of a pushbutton that connects a specified pin to ground +// class PushButton - tracks Single, Double, and Long Presses of a pushbutton that connects a specified pin to ground // class Blinker - creates customized blinking patterns on an LED connected to a specified pin // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -77,54 +77,7 @@ void PushButton::init(uint8_t pin){ ////////////////////////////////////// -boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime){ - - switch(status){ - - case 0: - if(!digitalRead(pin)){ // button is pressed - status=1; - shortAlarm=millis()+shortTime; - longAlarm=millis()+longTime; - } - break; - - case 1: - case 2: - if(digitalRead(pin)){ // button is released - status=0; - if(millis()>shortAlarm){ - isLongPress=false; - return(true); - } - } else - - if(millis()>longAlarm){ // button is long-pressed - longAlarm=millis()+longTime; - status=3; - isLongPress=true; - return(true); - } - break; - - case 3: - if(digitalRead(pin)) // button has been released after a long press - status=0; - else if(millis()>longAlarm){ - longAlarm=millis()+longTime; - isLongPress=true; - return(true); - } - break; - - } - - return(false); -} - -////////////////////////////////////// - -boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t doubleTime){ +boolean PushButton::triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime){ unsigned long cTime=millis(); @@ -133,15 +86,15 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do case 0: if(doubleCheck && cTime>doubleAlarm){ doubleCheck=false; - pressType=0; + pressType=SINGLE; return(true); } if(!digitalRead(pin)){ // button is pressed - shortAlarm=cTime+shortTime; + singleAlarm=cTime+singleTime; if(!doubleCheck){ status=1; - doubleAlarm=shortAlarm+doubleTime; + doubleAlarm=singleAlarm+doubleTime; longAlarm=cTime+longTime; } else { status=4; @@ -153,7 +106,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do case 2: if(digitalRead(pin)){ // button is released status=0; - if(cTime>shortAlarm){ + if(cTime>singleAlarm){ doubleCheck=true; } } else @@ -161,7 +114,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do if(cTime>longAlarm){ // button is long-pressed longAlarm=cTime+longTime; status=3; - pressType=1; + pressType=LONG; return(true); } break; @@ -171,7 +124,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do status=0; else if(cTime>longAlarm){ longAlarm=cTime+longTime; - pressType=1; + pressType=LONG; return(true); } break; @@ -181,9 +134,9 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do status=0; } else - if(cTime>shortAlarm){ // button is still pressed + if(cTime>singleAlarm){ // button is still pressed status=5; - pressType=2; + pressType=DOUBLE; doubleCheck=false; return(true); } @@ -203,7 +156,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do boolean PushButton::primed(){ - if(millis()>shortAlarm && status==1){ + if(millis()>singleAlarm && status==1){ status=2; return(true); } @@ -213,12 +166,6 @@ boolean PushButton::primed(){ ////////////////////////////////////// -boolean PushButton::longPress(){ - return(isLongPress); -} - -////////////////////////////////////// - int PushButton::type(){ return(pressType); } diff --git a/src/Utils.h b/src/Utils.h index 2a2d8d6..c8cb0ce 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -44,13 +44,18 @@ class PushButton{ int status; uint8_t pin; boolean doubleCheck; - uint32_t shortAlarm; + uint32_t singleAlarm; uint32_t doubleAlarm; uint32_t longAlarm; int pressType; - boolean isLongPress; public: + + enum { + SINGLE=0, + DOUBLE=1, + LONG=2 + }; PushButton(); PushButton(uint8_t pin); @@ -78,29 +83,34 @@ class PushButton{ // Resets state of PushButton. Should be called once before any loops that will // repeatedly check the button for a trigger event. - boolean triggered(uint16_t shortTime, uint16_t longTime, uint16_t doubleTime); - boolean triggered(uint16_t shortTime, uint16_t longTime); + boolean triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime=0); -// Returns true if button has been triggered by either a Long Press or Short Press, where a -// Long Press is a press and hold for at least longTime milliseconds, and a Short Press is -// a press and release of at least shortTime milliseconds but less than longTime milliseconds. -// -// shortTime: the minimum time required for the button to be pressed before releasing to trigger a Short Press -// longtime: the minimum time required for the button to be pressed and held to trigger a Long Press -// -// If shortTime>longTime, only Long Press triggers will occur. Once triggered() returns true, if will subsequently -// return false until there is a new trigger. After a Long Press, the button must be released to permit a subsequent -// trigger. +// Returns true if button has been triggered by an press event based on the following parameters: + +// singleTime: the minimum time required for the button to be pressed to trigger a Single Press +// doubleTime: the maximum time allowed between button presses to qualify as a Double Press +// longTime: the minimum time required for the button to be pressed and held to trigger a Long Press + +// All times are in milliseconds (ms). Trigger Rules: + +// * If button is pressed and continuously held, a Long Press will be triggered every longTime ms until the +// button is released. +// * If button is pressed for more than singleTime ms but less than longTime ms and then released, a Single Press +// will be triggered, UNLESS +// * The button is pressed a second time within doubleTime ms AND held again for at least singleTime ms, in which case +// a DoublePress will be triggered. No further events will occur until the button is released. +// * If singleTime>longTime, only Long Press triggers can occur. +// * If doubleTime=0, Double Presses cannot occur. +// * Once triggered() returns true, if will subsequently return false until there is a new trigger event. boolean primed(); -// Returns true if button has been pressed and held for greater than shortTime, but has not yet been released. +// Returns true if button has been pressed and held for greater than singleTime, but has not yet been released. // After returning true, subsequent calls will always return false until the button has been released and reset. - boolean longPress(); int type(); -// Returns true if last trigger event was a Long Press, or false if last trigger was a Short Press +// Returns 0=Single Press, 1=Double Press, or 2=Long Press void wait();