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
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
}

View File

@ -1209,10 +1209,10 @@ void HAPClient::callServiceLoops(){
void HAPClient::checkPushButtons(){
for(int i=0;i<homeSpan.PushButtons.size();i++){ // loop over all defined pushbuttons
SpanButton *sb=homeSpan.PushButtons[i]; // temporary pointer to SpanButton
if(sb->pushButton->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;i<homeSpan.PushButtons.size();i++){ // loop over all defined pushbuttons
SpanButton *sb=homeSpan.PushButtons[i]; // temporary pointer to SpanButton
if(sb->pushButton->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
}
}

View File

@ -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

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 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);
};
/////////////////////////////////////////////////

View File

@ -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);
}

View File

@ -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();