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!
This commit is contained in:
Gregg 2020-11-01 12:21:34 -06:00
parent bde63bf79d
commit e2f11630fa
6 changed files with 67 additions and 102 deletions

View File

@ -5,7 +5,6 @@
struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateless Programmable Switch struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateless Programmable Switch
int buttonPin; // pin with programmable pushbutton
SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic
DEV_ProgButton(int buttonPin, int index) : Service::StatelessProgrammableSwitch(){ DEV_ProgButton(int buttonPin, int index) : Service::StatelessProgrammableSwitch(){
@ -13,10 +12,9 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateles
switchEvent=new Characteristic::ProgrammableSwitchEvent(); // ProgrammableSwitchEvent Characteristic switchEvent=new Characteristic::ProgrammableSwitchEvent(); // ProgrammableSwitchEvent Characteristic
new Characteristic::ServiceLabelIndex(index); // set service label index new Characteristic::ServiceLabelIndex(index); // set service label index
new SpanButton(buttonPin); // create new SpanButton new SpanButton(buttonPin); // create new SpanButton
this->buttonPin=buttonPin; // save button pin number
Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message
Serial.print(buttonPin); Serial.print(buttonPin);
Serial.print(" Index="); Serial.print(" Index=");
Serial.print(index); Serial.print(index);
@ -24,15 +22,17 @@ struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateles
} // end constructor } // 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("Found button press on pin: "); // always a good idea to log messages
LOG1(pin); LOG1(pin);
LOG1(" type: "); LOG1(" type: ");
LOG1(isLong?"LONG":"SHORT"); LOG1(pressType);
LOG1(" ");
LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE");
LOG1("\n"); 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
} }

View File

@ -1209,10 +1209,10 @@ void HAPClient::callServiceLoops(){
void HAPClient::checkPushButtons(){ void HAPClient::checkPushButtons(){
for(int i=0;i<homeSpan.PushButtons.size();i++){ // loop over all defined pushbuttons for(int i=0;i<homeSpan.PushButtons.size();i++){ // loop over all defined pushbuttons
SpanButton *sb=homeSpan.PushButtons[i]; // temporary pointer to SpanButton SpanButton *sb=homeSpan.PushButtons[i]; // temporary pointer to SpanButton
if(sb->pushButton->triggered(sb->shortTime,sb->longTime)){ // if the underlying PushButton is triggered if(sb->pushButton->triggered(sb->singleTime,sb->longTime,sb->doubleTime)){ // 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 sb->service->button(sb->pin,sb->pushButton->type()); // call the Service's button() routine with pin and type as parameters
} }
} }

View File

@ -188,7 +188,7 @@ void Span::poll() {
if(controlButton.triggered(3000,10000)){ if(controlButton.triggered(3000,10000)){
statusLED.off(); statusLED.off();
if(controlButton.longPress()){ if(controlButton.type()==PushButton::LONG){
controlButton.wait(); controlButton.wait();
processSerialCommand("F"); // FACTORY RESET processSerialCommand("F"); // FACTORY RESET
} else { } else {
@ -232,7 +232,7 @@ void Span::commandMode(){
delay(2000); delay(2000);
} else } else
if(controlButton.triggered(10,3000)){ if(controlButton.triggered(10,3000)){
if(!controlButton.longPress()){ if(controlButton.type()==PushButton::SINGLE){
mode++; mode++;
if(mode==6) if(mode==6)
mode=1; mode=1;
@ -1530,9 +1530,9 @@ SpanRange::SpanRange(int min, int max, int step){
// SpanButton // // 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()){ if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
homeSpan.configLog+=" *** ERROR! Can't create new PushButton without a defined Service! ***\n"; 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"); Serial.print("\n");
this->pin=pin; this->pin=pin;
this->shortTime=shortTime;
this->longTime=longTime; this->longTime=longTime;
this->singleTime=singleTime;
this->doubleTime=doubleTime;
service=homeSpan.Accessories.back()->Services.back(); 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! ***"; homeSpan.configLog+=" *** WARNING: No button() method defined for this PushButton! ***";
pushButton=new PushButton(pin); // create underlying PushButton pushButton=new PushButton(pin); // create underlying PushButton

View File

@ -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 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 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{ struct SpanButton{
enum {
SINGLE=0,
DOUBLE=1,
LONG=2
};
int pin; // pin number int pin; // pin number
unsigned long shortTime; // time (in millis) required to register a short press uint16_t singleTime; // minimum time (in millis) required to register a single press
unsigned long longTime; // time (in millis) required to register a long 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 SpanService *service; // Service to which this PushButton is attached
PushButton *pushButton; // PushButton associated with this SpanButton 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);
}; };
///////////////////////////////////////////////// /////////////////////////////////////////////////

View File

@ -8,7 +8,7 @@
// Utils::readSerial - reads all characters from Serial port and saves only up to max specified // 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) // 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 // 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){ boolean PushButton::triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime){
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){
unsigned long cTime=millis(); unsigned long cTime=millis();
@ -133,15 +86,15 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
case 0: case 0:
if(doubleCheck && cTime>doubleAlarm){ if(doubleCheck && cTime>doubleAlarm){
doubleCheck=false; doubleCheck=false;
pressType=0; pressType=SINGLE;
return(true); return(true);
} }
if(!digitalRead(pin)){ // button is pressed if(!digitalRead(pin)){ // button is pressed
shortAlarm=cTime+shortTime; singleAlarm=cTime+singleTime;
if(!doubleCheck){ if(!doubleCheck){
status=1; status=1;
doubleAlarm=shortAlarm+doubleTime; doubleAlarm=singleAlarm+doubleTime;
longAlarm=cTime+longTime; longAlarm=cTime+longTime;
} else { } else {
status=4; status=4;
@ -153,7 +106,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
case 2: case 2:
if(digitalRead(pin)){ // button is released if(digitalRead(pin)){ // button is released
status=0; status=0;
if(cTime>shortAlarm){ if(cTime>singleAlarm){
doubleCheck=true; doubleCheck=true;
} }
} else } else
@ -161,7 +114,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
if(cTime>longAlarm){ // button is long-pressed if(cTime>longAlarm){ // button is long-pressed
longAlarm=cTime+longTime; longAlarm=cTime+longTime;
status=3; status=3;
pressType=1; pressType=LONG;
return(true); return(true);
} }
break; break;
@ -171,7 +124,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
status=0; status=0;
else if(cTime>longAlarm){ else if(cTime>longAlarm){
longAlarm=cTime+longTime; longAlarm=cTime+longTime;
pressType=1; pressType=LONG;
return(true); return(true);
} }
break; break;
@ -181,9 +134,9 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
status=0; status=0;
} else } else
if(cTime>shortAlarm){ // button is still pressed if(cTime>singleAlarm){ // button is still pressed
status=5; status=5;
pressType=2; pressType=DOUBLE;
doubleCheck=false; doubleCheck=false;
return(true); return(true);
} }
@ -203,7 +156,7 @@ boolean PushButton::triggered(uint16_t shortTime, uint16_t longTime, uint16_t do
boolean PushButton::primed(){ boolean PushButton::primed(){
if(millis()>shortAlarm && status==1){ if(millis()>singleAlarm && status==1){
status=2; status=2;
return(true); return(true);
} }
@ -213,12 +166,6 @@ boolean PushButton::primed(){
////////////////////////////////////// //////////////////////////////////////
boolean PushButton::longPress(){
return(isLongPress);
}
//////////////////////////////////////
int PushButton::type(){ int PushButton::type(){
return(pressType); return(pressType);
} }

View File

@ -44,13 +44,18 @@ class PushButton{
int status; int status;
uint8_t pin; uint8_t pin;
boolean doubleCheck; boolean doubleCheck;
uint32_t shortAlarm; uint32_t singleAlarm;
uint32_t doubleAlarm; uint32_t doubleAlarm;
uint32_t longAlarm; uint32_t longAlarm;
int pressType; int pressType;
boolean isLongPress;
public: public:
enum {
SINGLE=0,
DOUBLE=1,
LONG=2
};
PushButton(); PushButton();
PushButton(uint8_t pin); PushButton(uint8_t pin);
@ -78,29 +83,34 @@ class PushButton{
// Resets state of PushButton. Should be called once before any loops that will // Resets state of PushButton. Should be called once before any loops that will
// repeatedly check the button for a trigger event. // repeatedly check the button for a trigger event.
boolean triggered(uint16_t shortTime, uint16_t longTime, uint16_t doubleTime); boolean triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime=0);
boolean triggered(uint16_t shortTime, uint16_t longTime);
// Returns true if button has been triggered by either a Long Press or Short Press, where a // Returns true if button has been triggered by an press event based on the following parameters:
// 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. // 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
// 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
// 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 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 // * If button is pressed and continuously held, a Long Press will be triggered every longTime ms until the
// trigger. // 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(); 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. // After returning true, subsequent calls will always return false until the button has been released and reset.
boolean longPress();
int type(); 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(); void wait();