Created modified Example 10NEW
This uses loop() method to auto-reset the blinking LED controller after 3 seconds instead of requiring the use of a TimedReset object. To do: eliminate all TimedReset code as it is no longer needed. Will also need to renumber examples, since new Example 10 uses loop() which is not introduced until after current Example 13.
This commit is contained in:
parent
9cecc0a31a
commit
6e8e11b0b1
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 10: Timed Resets - emulating a "pushbutton" //
|
||||
// in HomeKit //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Blinker.h"
|
||||
#include "DEV_Identify.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Though HomeKit and the HomeKit Accessory Protocol (HAP) Specification provide a very flexible framework
|
||||
// for creating iOS- and MacOS-controlled devices, they does not contain every possible desired feature.
|
||||
//
|
||||
// One very common Characteristic HomeKit does not seem to contain is a simple pushbutton, like the type you
|
||||
// would find on a remote control. Unlike switches that can be "on" or "off", a pushbutton has no state.
|
||||
// Rather, a pushbutton performs some action when it's pushed, and that's all it does until it's pushed
|
||||
// again.
|
||||
//
|
||||
// Though HomeKit does not contain such a Characteristic, it's easy to emulate in HomeSpan. To do so, simply
|
||||
// define a Service with a boolen Characteristic (such as the On Characteristic), and create an update()
|
||||
// method to peform the operations to be executed when the "pushbutton" is "pressed". The update() method
|
||||
// should ignore the newValue requested by HomeKit, since the only thing that matters is that update() is called.
|
||||
//
|
||||
// You could stop there and have something in HomeKit that acts like a pushbutton, but it won't look like a
|
||||
// pushbutton because every time you press the tile for your device in HomeKit, the Controller will toggle
|
||||
// between showing it's on and showing it's off. Pressing a tile that shows the status is already on, in order
|
||||
// to cause HomeKit to trigger the update() to perform a new action, is not very satisfying.
|
||||
//
|
||||
// Ideally, we'd like HomeKit to acknowledge you've pressed the tile for the device, maybe by lighting up for a
|
||||
// second or so, and then it should reset to the "off" position. This would emulate a light-up pushbutton.
|
||||
//
|
||||
// Fortunately, HomeSpan includes a way of doing exactly this, using an object called SpanTimedReset(). Similar
|
||||
// to SpanRange(), you create a new SpanTimedReset() object with a single argument representing the number of
|
||||
// milliseconds HomeSpan should wait before telling HomeKit to reset, or "turn off", the device tile it just turned
|
||||
// on when you pressed it. How does SpanTimedReset() know which Characteristic it should attach itself to?
|
||||
// Similar to all other HomeSpan objects, SpanTimedReset() attaches to the last object you instantiated (and
|
||||
// will throw an error message at start-up if you try to instantiate a new SpanTimedReset() object without having just
|
||||
// instantiated a boolean Characteristic of some type).
|
||||
//
|
||||
// In Example 10 below we create a single pushbutton that blinks an LED three times. This is not very useful, but
|
||||
// you can think about the LED as an IR LED that is transmitting a Volume-Up command to a TV, or an RF signal to
|
||||
// some remote device, like a ceiling fan.
|
||||
//
|
||||
// All the functionality is wrapped up in a newly-defined "DEV_Blinker" Service, which can be found in DEV_Blinker.h.
|
||||
// This new Service is a copy of the DEV_LED service we've been working so far, with modifications to make it into
|
||||
// a generic blinking LED. As usual, changes and new lines are notably commented.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
// Defines the Bridge Accessory
|
||||
|
||||
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 *** defines an LED Blinker Accessory attached to pin 16 which blinks 3 times
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_Identify("LED Blinker","HomeSpan","123-ABC","20mA LED","0.9",0);
|
||||
new DEV_Blinker(16,3); // DEV_Blinker takes two arguments - pin, and number of times to blink
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
// NOTE: This example is constructed only for the purpose of demonstrating how to
|
||||
// use SpanTimedReset() to emulate a pushbutton in HomeSpan. The length of the blinking
|
||||
// routine is much longer than HomeSpan should spend on an update(). To see how this
|
||||
// effects HomeKit, try changing the number of blinks to 50, or keep it at 3 and
|
||||
// increase the delay times in update() so that the blink routine takes 10 seconds or more.
|
||||
// When activated, HomeKit will think the device has become non-responsive.
|
||||
//
|
||||
// In practice, pushbuton emulation is used for very short routines, such as driving
|
||||
// an IR LED or an RF transmitter to send a code to a remote device.
|
||||
|
||||
struct DEV_Blinker : Service::LightBulb { // LED Blinker
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
int nBlinks; // NEW! number of times to blink
|
||||
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_Blinker(int ledPin, int nBlinks) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
// Here we create a new Timed Reset of 2000 milliseconds. Similar to SpanRange(), SpanTimedReset() automatically
|
||||
// attaches to the last Characteristic instantiated, which in this case the the "power" Characteristic::On above.
|
||||
// SpanTimedReset() will notify HomeKit that the Characteristic has been turned off by HomeSpan 2000 milliseconds
|
||||
// after HomeKit requests it be turned on. This DOES NOT cause HomeKit to send an "off" request to HomeSpan (with
|
||||
// one exception --- see * below). Rather, HomeSpan is notifying HomeKit that HomeSpan itself has turned "off" the
|
||||
// Characteristic, and that HomeKit should reflect this new "off" status in the Tile shown for this device in the
|
||||
// HomeKit Controller.
|
||||
//
|
||||
// Note that in practice you'll want to set the reset time to 500ms or less to better emulate a pushbutton.
|
||||
// We've used a full 2 seconds in this example for illustrative purposes only.
|
||||
|
||||
this->ledPin=ledPin;
|
||||
this->nBlinks=nBlinks; // NEW! number of blinks
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
Serial.print("Configuring LED Blinker: Pin="); // initialization message
|
||||
Serial.print(ledPin);
|
||||
Serial.print(" Blinks="); // NEW! add output message for number of blinks
|
||||
Serial.print(nBlinks);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
StatusCode update(){ // update() method
|
||||
|
||||
// Instead of turning on or off the LED according to newValue, we blink it for
|
||||
// the number of times specified, and leave it in the off position when finished.
|
||||
// This line is deleted...
|
||||
|
||||
// digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
// and is replaced by...
|
||||
|
||||
if(power->getNewVal()){ // check to ensure HomeKit is requesting we "turn on" this device (else ignore)
|
||||
|
||||
LOG1("Activating the LED Blinker on pin=");
|
||||
LOG1(ledPin);
|
||||
LOG1("\n");
|
||||
|
||||
for(int i=0;i<nBlinks;i++){ // loop over number of blinks specified
|
||||
digitalWrite(ledPin,HIGH); // turn pin on
|
||||
delay(100); // wait 100 ms
|
||||
digitalWrite(ledPin,LOW); // turn pin off
|
||||
delay(250); // wait 250 ms
|
||||
}
|
||||
|
||||
} // if newVal=true
|
||||
|
||||
// Note that the delays above of 100ms and 250ms are for illustrative purposes only
|
||||
// (and so you can see the LED blink). In practice, if you were controlling an IR LED
|
||||
// or an RF transmitter, the whole signal would likely transmit in 10ms total.
|
||||
|
||||
return(StatusCode::OK); // return OK status code
|
||||
|
||||
} // update
|
||||
|
||||
void loop(){
|
||||
|
||||
if(power->getVal() && power->timeVal()>3000){
|
||||
LOG1("Resetting Blinking LED Control\n");
|
||||
power->setVal(false);
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// * EXCEPTION: There is an apparent bug in HomeKit such that if you have an Accessory with three or more
|
||||
// Services, and the Accessory receives a notification message from the device, AND the HomeKit interface is
|
||||
// open to show the detailed control for Service in the Accessory, then for some reason HomeKit tries to
|
||||
// update the device with the same status it just received from the device, even though this is contrary to
|
||||
// the purpose of notification requests. This is why it's a good idea to check that newValue.BOOL==true. It
|
||||
// avoids triggering the device if for some reason HomeKit should send a reqeust to update newValue to false.
|
||||
|
|
@ -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
|
||||
|
||||
};
|
||||
Loading…
Reference in New Issue