Created Example 13

Created checkEvents(), which is a simplified (better) version of checkTimedResets().  To do:  re-write checkTimedResets() to mimic checkEvents();
This commit is contained in:
Gregg 2020-08-09 22:15:58 -05:00
parent e7c2fc46f7
commit 2e3daef92a
9 changed files with 404 additions and 7 deletions

View File

@ -0,0 +1,60 @@
////////////////////////////////////////////////////////////
// //
// HomeSpan: A HomeKit implementation for the ESP32 //
// ------------------------------------------------ //
// //
// Example 13: Event Notifications //
// - implementing a temperature sensor //
// //
////////////////////////////////////////////////////////////
#include "HomeSpan.h"
#include "DEV_LED.h"
#include "DEV_Identify.h"
#include "DEV_Temperature.h"
void setup() {
// Example 11 illustrates how to control an RGB LED to set any color and brightness.
// The configuration below should look familiar by now. We've created a new derived Service,
// call RgbLED to house all the required logic. You'll find all the code in DEV_LED.h.
// For completeness, this configuration also contains an on/off LED and a dimmable LED as shown
// in prior examples.
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("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_LED(16); // Create an On/Off LED attached to pin 16
new SpanAccessory();
new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_DimmableLED(0,17); // Create a Dimmable LED using PWM channel 0, attached to pin 17
new SpanAccessory();
new DEV_Identify("RGB LED","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_RgbLED(1,2,3,32,22,23); // Create an RGB LED using PWM channels 1,2,3, attached to pins 32,22,23 (for R, G, and B LED anodes)
new SpanAccessory();
new DEV_Identify("Temp Sensor","HomeSpan","123-ABC","Celsius","0.9",0);
new DEV_TempSensor(); // Create a Temperature Sensor
new SpanEvent(60000);
} // end of setup()
//////////////////////////////////////
void loop(){
homeSpan.poll();
} // end of loop()

View File

@ -0,0 +1,38 @@
//////////////////////////////////
// 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
DEV_Identify(char *name, char *manu, char *sn, char *model, char *version, int nBlinks) : Service::AccessoryInformation(){
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,205 @@
////////////////////////////////////
// DEVICE-SPECIFIC LED SERVICES //
////////////////////////////////////
#include "extras/PwmPin.h" // library of various PWM functions
////////////////////////////////////
struct DEV_LED : Service::LightBulb { // ON/OFF LED
int ledPin; // pin number defined for this LED
SpanCharacteristic *power; // reference to the On Characteristic
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
power=new Characteristic::On();
this->ledPin=ledPin;
pinMode(ledPin,OUTPUT);
Serial.print("Configuring On/Off LED: Pin="); // initialization message
Serial.print(ledPin);
Serial.print("\n");
} // end constructor
StatusCode update(){ // update() method
LOG1("Updating On/Off LED on pin=");
LOG1(ledPin);
LOG1(": Current Power=");
LOG1(power->getVal()?"true":"false");
LOG1(" New Power=");
LOG1(power->getNewVal()?"true":"false");
LOG1("\n");
digitalWrite(ledPin,power->getNewVal());
return(StatusCode::OK); // return OK status code
} // update
};
//////////////////////////////////
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) : Service::LightBulb(){ // 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%
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
};
//////////////////////////////////
struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
PwmPin *redPin, *greenPin, *bluePin;
int redChannel, greenChannel, blueChannel;
SpanCharacteristic *power; // reference to the On Characteristic
SpanCharacteristic *H; // reference to the Hue Characteristic
SpanCharacteristic *S; // reference to the Saturation Characteristic
SpanCharacteristic *V; // reference to the Brightness Characteristic
DEV_RgbLED(int redChannel, int greenChannel, int blueChannel, int redPin, int greenPin, int bluePin) : Service::LightBulb(){ // constructor() method
power=new Characteristic::On();
H=new Characteristic::Hue(0); // instantiate the Hue Characteristic with an initial value of 0 out of 360
S=new Characteristic::Saturation(0); // instantiate the Saturation Characteristic with an initial value of 0%
V=new Characteristic::Brightness(100); // instantiate the Brightness Characteristic with an initial value of 100%
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%
this->redChannel=redChannel; // save the channel number (from 0-15)
this->greenChannel=greenChannel;
this->blueChannel=blueChannel;
this->redPin=new PwmPin(redChannel, redPin); // instantiate the PWM channel and attach the specified pin
this->greenPin=new PwmPin(greenChannel, greenPin);
this->bluePin=new PwmPin(blueChannel, bluePin);
char cBuf[128];
sprintf(cBuf,"Configuring RGB LED: Pins=(%d,%d,%d) Channels=(%d,%d,%d)\n",redPin,greenPin,bluePin,redChannel,greenChannel,blueChannel);
Serial.print(cBuf);
} // end constructor
StatusCode update(){ // update() method
boolean p;
float v, h, s, r, g, b;
h=H->getVal<float>(); // get and store all current values. Note the use of the <float> template to properly read the values
s=S->getVal<float>();
v=V->getVal<float>(); // though H and S are defined as FLOAT in HAP, V (which is brightness) is defined as INT, but will be re-cast appropriately
p=power->getVal();
char cBuf[128];
sprintf(cBuf,"Updating RGB LED on pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin());
LOG1(cBuf);
if(power->updated()){
p=power->getNewVal();
sprintf(cBuf,"Power=%s->%s, ",power->getVal()?"true":"false",p?"true":"false");
} else {
sprintf(cBuf,"Power=%s, ",p?"true":"false");
}
LOG1(cBuf);
if(H->updated()){
h=H->getNewVal<float>();
sprintf(cBuf,"H=%.0f->%.0f, ",H->getVal<float>(),h);
} else {
sprintf(cBuf,"H=%.0f, ",h);
}
LOG1(cBuf);
if(S->updated()){
s=S->getNewVal<float>();
sprintf(cBuf,"S=%.0f->%.0f, ",S->getVal<float>(),s);
} else {
sprintf(cBuf,"S=%.0f, ",s);
}
LOG1(cBuf);
if(V->updated()){
v=V->getNewVal<float>();
sprintf(cBuf,"V=%.0f->%.0f ",V->getVal<float>(),v);
} else {
sprintf(cBuf,"V=%.0f ",v);
}
LOG1(cBuf);
// Here we call a static function of PwmPin that converts HSV to RGB.
// Parameters must all be floats in range of H[0,360], S[0,1], and V[0,1]
// R, G, B, returned [0,1] range as well
PwmPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); // since HomeKit provides S and V in percent, scale down by 100
int R, G, B;
R=p*r*100; // since PwmPin uses percent, scale back up by 100, and multiple by status fo power (either 0 or 1)
G=p*g*100;
B=p*b*100;
sprintf(cBuf,"RGB=(%d,%d,%d)\n",R,G,B);
LOG1(cBuf);
redPin->set(redChannel,R); // update the PWM channels with new values
greenPin->set(greenChannel,G);
bluePin->set(blueChannel,B);
return(StatusCode::OK); // return OK status code
} // update
};
//////////////////////////////////

View File

@ -0,0 +1,34 @@
////////////////////////////////////
// DEVICE-SPECIFIC LED SERVICES //
////////////////////////////////////
struct DEV_TempSensor : Service::TemperatureSensor { // A standalone temperature sensor
SpanCharacteristic *temp; // reference to the Current Temperature Characteristic
float step=0.5;
DEV_TempSensor(ServiceType sType=ServiceType::Regular) : Service::TemperatureSensor(sType){ // constructor() method
temp=new Characteristic::CurrentTemperature();
temp->value.FLOAT=22.0;
Serial.print("Configuring Temperature Sensor"); // initialization message
Serial.print("\n");
} // end constructor
SpanCharacteristic *event(){
temp->value.FLOAT+=step;
if(temp->value.FLOAT>28.0)
step=-step;
else if(temp->value.FLOAT<18.0)
step=-step;
return(temp);
} // event
};
//////////////////////////////////

View File

@ -1090,7 +1090,33 @@ int HAPClient::putCharacteristicsURL(char *json){
//////////////////////////////////////
void HAPClient::checkNotifications(){
void HAPClient::checkEvents(){
unsigned long cTime=millis(); // current time
int nObj=0;
SpanBuf pObj[homeSpan.Events.size()]; // maximum number of objects needed if every Event is triggered and each requires a Notification
for(int i=0;i<homeSpan.Events.size();i++){ // loop over all defined Events
if(cTime>homeSpan.Events[i]->alarmTime){ // if alarm time has passed
homeSpan.Events[i]->alarmTime=cTime+homeSpan.Events[i]->period; // set new alarm time to current time plus alarm period
SpanCharacteristic *characteristic=homeSpan.Events[i]->service->event(); // check service for new EVENT
if(characteristic){ // if the service has responded with a characteristic to update
pObj[nObj].status=StatusCode::OK; // populate pObj
pObj[nObj].characteristic=characteristic;
pObj[nObj].val=""; // dummy object needed to ensure sprintfNotify knows to consider this "update"
nObj++; // increment number of characteristics found that need to be turned off
}
}
}
if(nObj>0)
eventNotify(pObj,nObj); // transmit EVENT Notification for "n" pObj objects
}
//////////////////////////////////////
void HAPClient::checkTimedResets(){
int n=0;
SpanTimedReset *tReset;
@ -1107,7 +1133,7 @@ void HAPClient::checkNotifications(){
}
else if(millis()>tReset->alarmTime){ // else characteristic is on, timer is started, and timer is expired
tReset->trigger=true; // set trigger
n++; // increment number of Push Buttons found that need to be turned off
n++; // increment number of characteristics found that need to be turned off
}
}
@ -1132,7 +1158,7 @@ void HAPClient::checkNotifications(){
pObj[n].status=StatusCode::OK; // populate pObj
pObj[n].characteristic=tReset->characteristic;
pObj[n].val=""; // dummy object needed to ensure sprintfNotify knows to consider this "update"
n++; // increment number of Push Buttons found that need to be turned off
n++; // increment number of characteristics found that need to be turned off
}
}

View File

@ -111,7 +111,8 @@ struct HAPClient {
static void removeControllers(); // removes all Controllers (sets allocated flags to false for all slots)
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 checkNotifications(); // checks for notifications and reports to controllers as needed (HAP Section 6.8)
static void checkTimedResets(); // checks for Timed Resets and reports to controllers as needed (HAP Section 6.8)
static void checkEvents(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
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

@ -147,7 +147,8 @@ void Span::poll() {
} // process HAP Client
} // for-loop over connection slots
HAPClient::checkNotifications();
HAPClient::checkTimedResets();
HAPClient::checkEvents();
} // poll
@ -1075,6 +1076,23 @@ SpanTimedReset::SpanTimedReset(int waitTime){
}
///////////////////////////////
// SpanEvent //
///////////////////////////////
SpanEvent::SpanEvent(int period){
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty() ){
Serial.print("*** FATAL ERROR: Can't create new Timed Reset without a defined Service. Program halted!\n\n");
while(1);
}
this->service=homeSpan.Accessories.back()->Services.back();
this->period=period;
homeSpan.Events.push_back(this);
}
///////////////////////////////
// SpanRange //
///////////////////////////////

View File

@ -24,6 +24,7 @@ struct SpanCharacteristic;
struct SpanRange;
struct SpanBuf;
struct SpanTimedReset;
struct SpanEvent;
///////////////////////////////
@ -46,6 +47,7 @@ struct Span{
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
vector<SpanTimedReset *> TimedResets; // vector of pointers to all TimedResets
vector<SpanEvent *> Events; // vector of pointer to all Events
void begin(Category catID,
char *displayName="HomeSpan Server",
@ -100,6 +102,7 @@ struct SpanService{
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 SpanCharacteristic* event(){return(NULL);} // event generation for Services that create their own events and need to notify HomeKit of a new Characteristic value
};
///////////////////////////////
@ -211,7 +214,7 @@ struct SpanRange{
///////////////////////////////
struct SpanBuf{ // temporary storage buffer for use with putCharacteristicsURL() and checkNotifications()
struct SpanBuf{ // temporary storage buffer for use with putCharacteristicsURL() and checkTimedResets()
int aid; // updated aid
int iid; // updated iid
char *val=NULL; // updated value (optional, though either at least 'val' or 'ev' must be specified)
@ -232,6 +235,16 @@ struct SpanTimedReset{
SpanTimedReset(int waitTime);
};
///////////////////////////////
struct SpanEvent{
SpanService *service; // service to check for events
int period; // time period between checks (in milliseconds)
unsigned long alarmTime=0; // alarm time to trigger next check
SpanEvent(int period);
};
/////////////////////////////////////////////////
// Extern Variables

View File

@ -109,6 +109,8 @@ namespace Characteristic {
struct SwingMode : SpanCharacteristic { SwingMode(uint8_t value=0) : SpanCharacteristic{"B6",PR+PW+EV,(uint8_t)value}{} };
struct CurrentTemperature : SpanCharacteristic { CurrentTemperature(double value=0) : SpanCharacteristic{"11",PR+EV,(double)value}{} };
struct Version : SpanCharacteristic { Version(char *value) : SpanCharacteristic{"37",PR,(char *)value}{} };
}