Created Example 15

Initial implementation of SpanButton PushButton object and check() logic
This commit is contained in:
Gregg 2020-08-20 07:29:15 -05:00
parent 9bbea69816
commit c997ca3462
7 changed files with 254 additions and 2 deletions

View File

@ -0,0 +1,41 @@
////////////////////////////////////////////////////////////
// //
// HomeSpan: A HomeKit implementation for the ESP32 //
// ------------------------------------------------ //
// //
// Example 15: Real PushButtons //
// * manually controlling an LED //
// //
// //
////////////////////////////////////////////////////////////
#include "HomeSpan.h"
#include "DEV_LED.h"
#include "DEV_Identify.h"
void setup() {
Serial.begin(115200);
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
new SpanAccessory();
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
new Service::HAPProtocolInformation();
new Characteristic::Version("1.1.0");
new SpanAccessory();
new DEV_Identify("Switched LED","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_DimmableLED(0,17);
} // end of setup()
//////////////////////////////////////
void loop(){
homeSpan.poll();
} // end of loop()

View File

@ -0,0 +1,40 @@
//////////////////////////////////
// DEVICE-SPECIFIC SERVICES //
//////////////////////////////////
struct DEV_Identify : Service::AccessoryInformation {
int nBlinks; // number of times to blink built-in LED in identify routine
SpanCharacteristic *identify; // reference to the Identify Characteristic
// NEW! modified constructor() method to include optional ServiceType argument
DEV_Identify(char *name, char *manu, char *sn, char *model, char *version, int nBlinks, ServiceType sType=ServiceType::Regular) : Service::AccessoryInformation(sType){
new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments
new Characteristic::Manufacturer(manu);
new Characteristic::SerialNumber(sn);
new Characteristic::Model(model);
new Characteristic::FirmwareRevision(version);
identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below
this->nBlinks=nBlinks; // store the number of times to blink the built-in LED
pinMode(LED_BUILTIN,OUTPUT); // make sure built-in LED is set for output
}
StatusCode update(){
for(int i=0;i<nBlinks;i++){
digitalWrite(LED_BUILTIN,LOW);
delay(250);
digitalWrite(LED_BUILTIN,HIGH);
delay(250);
}
return(StatusCode::OK);
} // update
};

View File

@ -0,0 +1,77 @@
////////////////////////////////////
// DEVICE-SPECIFIC LED SERVICES //
////////////////////////////////////
#include "extras/PwmPin.h" // library of various PWM functions
////////////////////////////////////
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
PwmPin *pwmPin; // reference to PWM Pin
int ledPin; // pin number defined for this LED
int channel; // PWM channel used for this LED (should be unique for each LED)
SpanCharacteristic *power; // reference to the On Characteristic
SpanCharacteristic *level; // reference to the Brightness Characteristic
DEV_DimmableLED(int channel, int ledPin, ServiceType sType=ServiceType::Regular) : Service::LightBulb(sType){ // // NEW! modified constructor() method
power=new Characteristic::On();
level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50%
new SpanRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
new SpanButton(19);
this->channel=channel; // save the channel number (from 0-15)
this->ledPin=ledPin; // save LED pin number
this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin
Serial.print("Configuring Dimmable LED: Pin="); // initialization message
Serial.print(ledPin);
Serial.print(" Channel=");
Serial.print(channel);
Serial.print("\n");
} // end constructor
StatusCode update(){ // update() method
LOG1("Updating Dimmable LED on pin=");
LOG1(ledPin);
LOG1(": Current Power=");
LOG1(power->getVal()?"true":"false");
LOG1(" Current Brightness=");
LOG1(level->getVal());
if(power->updated()){
LOG1(" New Power=");
LOG1(power->getNewVal()?"true":"false");
}
if(level->updated()){
LOG1(" New Brightness=");
LOG1(level->getNewVal());
}
LOG1("\n");
pwmPin->set(channel,power->getNewVal()*level->getNewVal());
return(StatusCode::OK); // return OK status code
} // update
void button(int pin, boolean isLong){
Serial.print("Found button press on pin: ");
Serial.print(pin);
Serial.print(" type: ");
Serial.print(isLong?"LONG":"SHORT");
Serial.print("\n");
}
};
//////////////////////////////////

View File

@ -102,7 +102,16 @@ void HAPClient::init(){
homeSpan.Loops.push_back(s); homeSpan.Loops.push_back(s);
} }
} }
for(int i=0;i<homeSpan.PushButtons.size();i++){
Serial.print("PushButton Found on pin: ");
Serial.print(homeSpan.PushButtons[i]->pin);
Serial.print(" iid: ");
Serial.print(homeSpan.PushButtons[i]->service->iid);
Serial.print("\n");
}
} }
////////////////////////////////////// //////////////////////////////////////
@ -1173,6 +1182,16 @@ void HAPClient::callServiceLoops(){
homeSpan.Loops[i]->loop(); // call the loop() method homeSpan.Loops[i]->loop(); // call the loop() method
} }
//////////////////////////////////////
void HAPClient::checkPushButtons(){
for(int i=0;i<homeSpan.PushButtons.size();i++) // loop over all defined pushbuttons
homeSpan.PushButtons[i]->check(); // check if long- or short-pressed, which calls button() method in attached Service if needed
}
////////////////////////////////////// //////////////////////////////////////
void HAPClient::checkNotifications(){ void HAPClient::checkNotifications(){

View File

@ -113,6 +113,7 @@ struct HAPClient {
static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements. static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements.
static void printControllers(); // prints IDs of all allocated (paired) Controller static void printControllers(); // prints IDs of all allocated (paired) Controller
static void callServiceLoops(); // call the loop() method for any Service with that over-rode the default method static void callServiceLoops(); // call the loop() method for any Service with that over-rode the default method
static void checkPushButtons(); // checks for PushButton presses and calls button() method of attached Services when found
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8) static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4) static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client

View File

@ -148,6 +148,7 @@ void Span::poll() {
} // for-loop over connection slots } // for-loop over connection slots
HAPClient::callServiceLoops(); HAPClient::callServiceLoops();
HAPClient::checkPushButtons();
HAPClient::checkNotifications(); HAPClient::checkNotifications();
HAPClient::checkTimedWrites(); HAPClient::checkTimedWrites();
@ -1152,4 +1153,52 @@ SpanRange::SpanRange(int min, int max, int step){
homeSpan.Accessories.back()->Services.back()->Characteristics.back()->range=this; homeSpan.Accessories.back()->Services.back()->Characteristics.back()->range=this;
} }
///////////////////////////////
// SpanButton //
///////////////////////////////
SpanButton::SpanButton(int pin, unsigned long longTime, unsigned long shortTime){
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
Serial.print("*** FATAL ERROR: Can't create new PushButton without a defined Service. Program halted!\n\n");
while(1);
}
Serial.print("Configuring PushButton: Pin="); // initialization message
Serial.print(pin);
Serial.print("\n");
this->pin=pin;
this->shortTime=shortTime;
this->longTime=longTime;
service=homeSpan.Accessories.back()->Services.back();
homeSpan.PushButtons.push_back(this);
pinMode(pin,INPUT_PULLUP);
}
///////////////////////////////
void SpanButton::check(){
if(state==UNTRIGGERED && !digitalRead(pin)){
state=TRIGGERED;
unsigned long cTime=millis();
shortAlarm=cTime+shortTime;
longAlarm=cTime+longTime;
} else
if(state==TRIGGERED && digitalRead(pin)){
unsigned long cTime=millis();
if(cTime>longAlarm)
service->button(pin, true);
else if(cTime>shortAlarm)
service->button(pin, false);
state=UNTRIGGERED;
}
}
/////////////////////////////// ///////////////////////////////

View File

@ -25,6 +25,7 @@ struct SpanService;
struct SpanCharacteristic; struct SpanCharacteristic;
struct SpanRange; struct SpanRange;
struct SpanBuf; struct SpanBuf;
struct SpanButton;
/////////////////////////////// ///////////////////////////////
@ -49,6 +50,7 @@ struct Span{
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods
vector<SpanBuf> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event vector<SpanBuf> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
vector<SpanButton *> PushButtons; // vector of pointer to all PushButtons
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs) unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
void begin(Category catID, void begin(Category catID,
@ -105,7 +107,8 @@ struct SpanService{
int sprintfAttributes(char *cBuf); // prints Service JSON records into buf; return number of characters printed, excluding null terminator int sprintfAttributes(char *cBuf); // prints Service JSON records into buf; return number of characters printed, excluding null terminator
virtual StatusCode update() {return(StatusCode::OK);} // update Service and return final statusCode based on updated Characteristics - should be overridden by DEVICE-SPECIFIC Services virtual StatusCode update() {return(StatusCode::OK);} // update Service and return final statusCode based on updated Characteristics - should be overridden by DEVICE-SPECIFIC Services
virtual void event(){} // event generation for Services that create their own events and need to notify HomeKit of a new Characteristic value(s) virtual void event(){} // event generation for Services that create their own events and need to notify HomeKit of a new Characteristic value(s)
virtual void loop(){} // loops for each service virtual void loop(){} // loops for each Service
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"
}; };
/////////////////////////////// ///////////////////////////////
@ -231,6 +234,28 @@ struct SpanBuf{ // temporary storage buffer for us
SpanCharacteristic *characteristic=NULL; // Characteristic to update (NULL if not found) SpanCharacteristic *characteristic=NULL; // Characteristic to update (NULL if not found)
}; };
///////////////////////////////
struct SpanButton{
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
unsigned long shortAlarm; // alarm time to trigger a short press
unsigned long longAlarm; // alarm time to triger a long press
SpanService *service; // Service to which this PushButton is attached
enum {
UNTRIGGERED,
TRIGGERED,
SHORT,
LONG
} state=UNTRIGGERED;
SpanButton(int pin, unsigned long longTime=2000, unsigned long shortTime=5);
void check();
};
///////////////////////////////////////////////// /////////////////////////////////////////////////
// Extern Variables // Extern Variables