Completed Example 11

Transtioned to all getter methods: getVal(), getNewVal(), updated(), using templates for all floats.

Finalized templates for getVal and getNewVal, including making <int>  default so it does not have to be set for most getVal() and getNewVal() calls.  Works for booleans as well.  TO DO:  Re-work and check ALL prior examples to ensure they use getVal, etc., and DON'T access value, newValue, isUpdated, directly.
This commit is contained in:
Gregg 2020-08-04 07:56:53 -05:00
parent 3e4ada5912
commit 1fd493aa31
6 changed files with 60 additions and 69 deletions

View File

@ -18,6 +18,8 @@ void setup() {
// Example 11 illustrates how to control an RGB LED to set any color and brightness.
// The config 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, the config also contains an on/off LED and a dimmable LED as shown
// in prior examples.
Serial.begin(115200);
@ -30,8 +32,16 @@ void setup() {
new Characteristic::Version("1.1.0");
new SpanAccessory();
new DEV_Identify("LED Blinker","HomeSpan","123-ABC","20mA LED","0.9",0);
new DEV_RgbLED(0,1,2,32,22,23); // An RGB LED requires three PWM channels and three pins to be specified
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)
} // end of setup()

View File

@ -3,17 +3,11 @@
// DEVICE-SPECIFIC SERVICES //
//////////////////////////////////
// Here we define the DEV_Identify Service as derived class of AccessoryInformation
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
// Next we define the constructor using all the arguments needed to implement the required Characteristics
// of AccessoryInformation, plus one extra argument at the end called "nBlinks" we will use to specify how many
// times HomeSpan should blink the built-in LED when HomeKit calls this device's Identify routine during pairing.
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
@ -28,25 +22,6 @@ struct DEV_Identify : Service::AccessoryInformation {
pinMode(LED_BUILTIN,OUTPUT); // make sure built-in LED is set for output
}
// How HomeKit Identifies Devices:
//
// When HomeKit first pairs with a new device it "calls" that device's identify routine for every defined Accessory.
// To do so, HomeKit requests the Identify Characteristic for each defined AccessoryInformation Service to be set to "true".
// The Identify Characteristic is write-only, so no value is ever stored, even though HomeKit is requesting its value
// be updated. We can therefore use the same update() method as if the Identify Characteristic was the same as any
// other boolean Characteristic.
// There are many ways to implement some form of identification. For an LED, you could blink it one or more times.
// For a LightBulb, you can flash it on and off. For window shade, you could raise and lower it.
// Most commerical devices don't do anything. Because HomeSpan can be used to control many different types of
// device, below we implement a very generic routine that simply blinks the internal LED of the ESP32 the
// number of times specified above. In principle, this code could call a user-defined routine that is different
// for each physcially-attached device (light, shade, fan, etc), but in practice this is overkill.
// Note that the blink routine below starts by turning off the built-in LED and then leaves it on once it has blinked
// the specified number of times. This is because when HomeSpan starts up if confirms to user that it has connected
// to the WiFi network by turning on the built-in LED. Thus we want to leave it on when blinking is completed.
StatusCode update(){
for(int i=0;i<nBlinks;i++){

View File

@ -27,12 +27,12 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED
LOG1("Updating On/Off LED on pin=");
LOG1(ledPin);
LOG1(": Current Power=");
LOG1(power->value.BOOL?"true":"false");
LOG1(power->getVal()?"true":"false");
LOG1(" New Power=");
LOG1(power->newValue.BOOL?"true":"false");
LOG1(power->getNewVal()?"true":"false");
LOG1("\n");
digitalWrite(ledPin,power->newValue.BOOL);
digitalWrite(ledPin,power->getNewVal());
return(StatusCode::OK); // return OK status code
@ -73,24 +73,23 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
LOG1("Updating Dimmable LED on pin=");
LOG1(ledPin);
LOG1(": Current Power=");
LOG1(power->value.BOOL?"true":"false");
LOG1(power->getVal<boolean>()?"true":"false");
LOG1(power->getVal()?"true":"false");
LOG1(" Current Brightness=");
LOG1(level->getVal<int>());
LOG1(level->getVal());
if(power->isUpdated){
if(power->updated()){
LOG1(" New Power=");
LOG1(power->getNewVal<boolean>()?"true":"false");
LOG1(power->getNewVal()?"true":"false");
}
if(level->isUpdated){
if(level->updated()){
LOG1(" New Brightness=");
LOG1(level->getNewVal<boolean>());
LOG1(level->getNewVal());
}
LOG1("\n");
pwmPin->set(channel,power->getNewVal<boolean>()*level->getNewVal<boolean>());
pwmPin->set(channel,power->getNewVal()*level->getNewVal());
return(StatusCode::OK); // return OK status code
@ -98,7 +97,8 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
};
//////////////////////////////////
struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
PwmPin *redPin;
PwmPin *greenPin;
@ -133,64 +133,68 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
} // end constructor
StatusCode update(){ // update() method
StatusCode update(){ // update() method
boolean p;
double v, h, s, r, g, b;
float v, h, s, r, g, b;
h=H->getVal<double>(); // get all current values
s=S->getVal<double>();
v=V->getVal<double>();
p=power->getVal<boolean>();
h=H->getVal<float>(); // get and store all current values
s=S->getVal<float>();
v=V->getVal<float>();
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->isUpdated){
p=power->getNewVal<boolean>();
sprintf(cBuf,"Power=%s->%s, ",power->getVal<boolean>()?"true":"false",p?"true":"false");
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->isUpdated){
h=H->getNewVal<double>();
sprintf(cBuf,"H=%d->%d, ",(int)H->getVal<double>(),(int)h);
if(H->updated()){
h=H->getNewVal<float>();
sprintf(cBuf,"H=%.0f->%.0f, ",H->getVal<float>(),h);
} else {
sprintf(cBuf,"H=%d, ",(int)h);
sprintf(cBuf,"H=%.0f, ",h);
}
LOG1(cBuf);
if(S->isUpdated){
s=S->getNewVal<double>();
sprintf(cBuf,"S=%d->%d, ",(int)S->getVal<double>(),(int)s);
if(S->updated()){
s=S->getNewVal<float>();
sprintf(cBuf,"S=%.0f->%.0f, ",S->getVal<float>(),s);
} else {
sprintf(cBuf,"S=%d, ",(int)s);
sprintf(cBuf,"S=%.0f, ",s);
}
LOG1(cBuf);
if(V->isUpdated){
v=V->getNewVal<double>();
sprintf(cBuf,"V=%d->%d ",(int)V->getVal<double>(),(int)v);
if(V->updated()){
v=V->getNewVal<float>();
sprintf(cBuf,"V=%.0f->%.0f ",V->getVal<float>(),v);
} else {
sprintf(cBuf,"V=%d ",(int)v);
sprintf(cBuf,"V=%.0f ",v);
}
LOG1(cBuf);
PwmPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b);
// 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;
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);
redPin->set(redChannel,R); // update the PWM channels with new values
greenPin->set(greenChannel,G);
bluePin->set(blueChannel,B);

View File

@ -165,9 +165,11 @@ struct SpanCharacteristic{
int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator
StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intiial HAP status code (checks to see if characteristic is found, is writable, etc.)
template <class T> T getVal(){return(getValue<T>(value));} // returns UVal value
template <class T> T getNewVal(){return(getValue<T>(newValue));} // returns UVal newValue
template <class T> T getValue(UVal v); // returns UVal v
template <class T=int> T getVal(){return(getValue<T>(value));} // returns UVal value
template <class T=int> T getNewVal(){return(getValue<T>(newValue));} // returns UVal newValue
template <class T> T getValue(UVal v); // returns UVal v
boolean updated(){return(isUpdated);} // returns isUpdated
};

View File

@ -33,7 +33,7 @@ void PwmPin::set(uint8_t channel, uint8_t level){
///////////////////
void PwmPin::HSVtoRGB(double h, double s, double v, double *r, double *g, double *b ){
void PwmPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
// The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html
// h = [0,360]
@ -41,7 +41,7 @@ void PwmPin::HSVtoRGB(double h, double s, double v, double *r, double *g, double
// v = [0,1]
int i;
double f, p, q, t;
float f, p, q, t;
if( s == 0 ){
*r = *g = *b = v;

View File

@ -20,6 +20,6 @@ class PwmPin {
void set(uint8_t channel, uint8_t level); // sets the PWM duty of channel to level (0-100)
int getPin(){return pin;} // returns the pin number
static void HSVtoRGB(double h, double s, double v, double *r, double *g, double *b );
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b );
};