diff --git a/Other Examples/ServoControl/DEV_DoorsWindows.h b/Other Examples/ServoControl/DEV_DoorsWindows.h new file mode 100644 index 0000000..bc82648 --- /dev/null +++ b/Other Examples/ServoControl/DEV_DoorsWindows.h @@ -0,0 +1,68 @@ + +//////////////////////////////////// +// DEVICE-SPECIFIC LED SERVICES // +//////////////////////////////////// + +#include + +//////////////////////////////////// + +struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Shade with Hold Feature + + SpanCharacteristic *current; // reference to a "generic" Current Position Characteristic (used by a variety of different Service) + SpanCharacteristic *target; // reference to a "generic" Target Position Characteristic (used by a variety of different Service) + SpanCharacteristic *hTiltCurrent; // reference to horizontal tilt of window shade - current position + SpanCharacteristic *hTiltTarget; // reference to horizontal tilt of window shade - target position + + ServoPin *hTiltServo; // reference to Servo Pin to control Horiontal Tilt + + DEV_WindowShade(uint8_t hTiltServoPin) : Service::WindowCovering(){ // constructor() method + + current=new Characteristic::CurrentPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised) + target=new Characteristic::TargetPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised) + target->setRange(0,100,10); // set the allowable target-position range to 0-100 IN STEPS of 10 + + hTiltCurrent=new Characteristic::CurrentHorizontalTiltAngle(); // Tilt Angle is measured in degrees; HAP default is -90 to +90 + hTiltTarget=new Characteristic::TargetHorizontalTiltAngle(); + + // Here we define our Servo using HomeSpan's ServoPin Class. + // See the HomeSpan API Reference for full details and a list of all parameters. + + hTiltServo=new ServoPin(hTiltServoPin); + + Serial.print("Configuring Motorized Window Shade"); // initialization message + Serial.print("\n"); + + } // end constructor + + boolean update(){ // update() method + + if(target->updated()){ // check to see if shade target position was updated + if(target->getNewVal()>current->getVal()){ // if the target-position requested is greater than the current-position, simply log a "raise" message + LOG1("Raising Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows raising is required + } else + if(target->getNewVal()getVal()){ // if the target-position requested is less than the current-position, simply log a "raise" message + LOG1("Lowering Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows lowering is required + } + } + + if(hTiltTarget->updated()){ // check to see if shade tilt angle was updated + hTiltCurrent->setVal(hTiltTarget->getNewVal()); // set current value of tilt to match target value + hTiltServo->set(hTiltTarget->getNewVal()); // <--- update actual servo position with ServoPin->set(degrees) method + } + + return(true); // return true + + } // update + + void loop(){ // loop() method + + // Here we simulate a window shade that takes 5 seconds to move to its new target position + + if(current->getVal()!=target->getVal() && target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... + current->setVal(target->getVal()); // ...set the current position to equal the target position + } + + } // loop + +}; diff --git a/Other Examples/ServoControl/DEV_Identify.h b/Other Examples/ServoControl/DEV_Identify.h new file mode 100644 index 0000000..b8d21d6 --- /dev/null +++ b/Other Examples/ServoControl/DEV_Identify.h @@ -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(const char *name, const char *manu, const char *sn, const char *model, const 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 LED + + pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output + } + + boolean update(){ + + for(int i=0;i :warning: SECURITY WARNING: The purpose of this function is to allow advanced users to *dynamically* set the device's WiFi Credentials using a customized Access Point function specified by `setApFunction(func)`. It it NOT recommended to use this function to hardcode your WiFi SSID and password directly into your sketch. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'W' from the CLI, or launching HomeSpan's Access Point by typing 'A' from the CLI, to set your WiFi credentials without hardcoding them into your sketch. + +* `void setWifiCallback(void (*func)())` + * Sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments + * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string * if unspecified, HomeSpan uses "n/a" as the default version text @@ -86,10 +116,7 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * `const char *getSketchVersion()` * returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set - -* `void setWifiCallback(void (*func)(void))` - * Sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments - + ## *SpanAccessory(uint32_t aid)* Creating an instance of this **class** adds a new HAP Accessory to the HomeSpan HAP Database. @@ -144,15 +171,19 @@ The following methods are supported: * 1=double press (SpanButton::DOUBLE) * 2=long press (SpanButton::LONG) -## *SpanCharacteristic(value)* +## *SpanCharacteristic(value [,boolean nvsStore])* This is a **base class** from which all HomeSpan Characteristics are derived, and should **not** be directly instantiated. Rather, to create a new Characteristic instantiate one of the HomeSpan Characteristics defined in the [Characteristic](ServiceList.md) namespace. * instantiated Characteristics are added to the HomeSpan HAP Database and associated with the last Service instantiated * instantiating a Characteristic without first instantiating a Service throws an error during initialization -* a single, optional argument is used to set the initial value of the Characteristic at startup -* throws a runtime warning if value is outside of the min/max range for the Characteristic, where min/max is either the HAP default, or any new values set via a call to `setRange()` -* example: `new Characteristic::Brightness(50);` +* the first argument optionally allows you to set the initial *value* of the Characteristic at startup. If *value* is not specified, HomeSpan will supply a reasonable default for the Characteristic +* throws a runtime warning if *value* is outside of the min/max range for the Characteristic, where min/max is either the HAP default, or any new values set via a call to `setRange()` +* the second optional argument, if set to `true`, instructs HomeSpan to save updates to this Characteristic's value in the device's non-volative storage (NVS) for restoration at startup if the device should lose power. If not specified, *nvsStore* will default to `false` (no storage) +* examples: + * `new Characteristic::Brightness();` Brightness initialized to default value + * `new Characteristic::Brightness(50);` Brightness initialized to 50 + * `new Characteristic::Brightness(50,true);` Brightness initialized to 50; updates saved in NVS The following methods are supported: @@ -212,6 +243,18 @@ HomeSpan automatically calls the `button(int pin, int pressType)` method of a Se HomeSpan will report a warning, but not an error, during initialization if the user had not overridden the virtual button() method for a Service contaning one or more Buttons; triggers of those Buttons will simply ignored. +## *SpanUserCommand(char c, const char \*s, void (\*f)(const char \*v))* + +Creating an instance of this **class** adds a user-defined command to the HomeSpan Command-Line Interface (CLI), where: + + * *c* is the single-letter name of the user-defined command + * *s* is a description of the user-defined command that is displayed when the user types '?' into the CLI + * *f* is a pointer to a user-defined function that is called when the command is invoked. This function must be of the form `void f(const char *v)`, where *v* points to all characters typed into the CLI, beginning with the single-letter command name *c*. + +To invoke this command from the CLI, preface the single-letter name *c* with '@'. This allows HomeSpan to distinguish user-defined commands from its built-in commands. For example, `new SpanUserCommand('s', "save current configuration",saveConfig)` would add a new command '@s' to the CLI with description "save current configuration" that will call the user-defined function `void saveConfig(const char *v)` when invoked. The argument *v* points to an array of all characters typed into the CLI after the '@'. This allows the user to pass arguments from the CLI to the user-defined function. For example, typing '@s123' into the CLI sets *v* to "s123" when saveConfig is called. + +To create more than one user-defined command, simply create multiple instances of SpanUserCommand, each with its own single-letter name. Note that re-using the same single-letter name in an instance of SpanUserCommand over-rides any previous instances using that same letter. + ## *#define REQUIRED VERSION(major,minor,patch)* If REQUIRED is defined in the main sketch prior to including the HomeSpan library with `#include "HomeSpan.h"`, HomeSpan will throw a compile-time error unless the version of the library included is equal to, or later than, the version specified using the VERSION macro. Example: @@ -221,15 +264,13 @@ If REQUIRED is defined in the main sketch prior to including the HomeSpan librar ``` --- -## *SpanRange(int min, int max, int step)* +#### Deprecated functions (available for backwards compatibility with older sketches): -Creating an instance of this **class** overrides the default HAP range for a Characteristic with the *min*, *max*, and *step* values specified. +*SpanRange(int min, int max, int step)* -* instantiated Ranges are added to the HomeSpan HAP Database and associated with the last Characteristic instantiated -* instantiating a Range without first instantiating a Characteristic throws an error during initialization -* example: `new Characteristic::Brightness(50); new SpanRange(10,100,5);` -* this is a legacy function that is limited to integer-based parameters, and has been re-coded to simply call the more generic `setRange(min, max, step)` method -* **please use** `setRange(min, max, step)` **for all new sketches** + * this legacy function is limited to integer-based parameters and has been re-coded to simply call the more generic `setRange(min, max, step)` method + * last supported version: [v1.2.0](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md#spanrangeint-min-int-max-int-step) + * **please use** `setRange(min, max, step)` **for all new sketches** --- diff --git a/docs/Tutorials.md b/docs/Tutorials.md index 1d7eaf3..76b25d0 100644 --- a/docs/Tutorials.md +++ b/docs/Tutorials.md @@ -35,8 +35,8 @@ Example 5 expands on Example 2 by adding in the code needed to actually control ### [Example 6 - DimmableLED](../examples/06-DimmableLED) Example 6 changes Example 5 so that LED #2 is now dimmable, instead of just on/off. New HomeSpan API topics covered in this example include: -* implementing pulse-width-modulation on any ESP32 pin by instantiating a `PwmPin()` object -* setting the PWM level to control the brightness of an LED using the PwmPin `set()` method +* implementing pulse-width-modulation (PWM) to control an LED attached to any ESP32 pin by instantiating an `LedPin()` object +* setting the brightness of an LED using the LedPin `set()` method * storing similar derived Service classes in the same \*.h file for ease of use ### [Example 7 - IdentifyRoutines](../examples/07-IdentifyRoutines) @@ -92,6 +92,11 @@ Example 17 introduces the HAP concept of Linked Services and demonstrates how th * creating Linked Services using the `addLink()` method +### [Example 18 - SavingStatus](../examples/18-SavingStatus) +Example 18 demonstrates, through the implementaton of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is loses power. New HomeSpan API topics covered in this example include: + +* instructing HomeSpan to store the value of a Characteristic in NVS by setting the second parameter of the constuctor to `true` + --- [↩️](README.md) Back to the Welcome page diff --git a/examples/06-DimmableLED/06-DimmableLED.ino b/examples/06-DimmableLED/06-DimmableLED.ino index f5cc74d..b28fc82 100644 --- a/examples/06-DimmableLED/06-DimmableLED.ino +++ b/examples/06-DimmableLED/06-DimmableLED.ino @@ -54,7 +54,7 @@ void setup() { // and the ESP32 chip has built-in PWM functionality specifically for this purpose. There are numerous libraries // you can download that mimics or reproduces analogWrite() in some form or another. HomeSpan conveniently comes with // it own version of a wrapper around the ESP32 PWM classes that make it very easy to define PWM "channel," attach a pin, - // and set the PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the PwmPin class, as defined in + // and set the PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the LedPin class, as defined in // extras/PwmPin.h. We will include this file in our updated DEV_LED.h for use with DEV_DimmableLED. Serial.begin(115200); @@ -89,7 +89,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new DEV_DimmableLED(0,17); // NEW! create a dimmable LED attached to pin 17 using PWM channel 0. See new code at end of DEV_LED.h + new DEV_DimmableLED(17); // NEW! create a dimmable (PWM-driven) LED attached to pin 17. See new code at end of DEV_LED.h } // end of setup() diff --git a/examples/06-DimmableLED/DEV_LED.h b/examples/06-DimmableLED/DEV_LED.h index ba6f9e2..d9445e9 100644 --- a/examples/06-DimmableLED/DEV_LED.h +++ b/examples/06-DimmableLED/DEV_LED.h @@ -33,34 +33,33 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // NEW! Create reference to PWM Pin instantiated below - int channel; // NEW! Store the PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // NEW! Create reference to LED Pin instantiated below SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // NEW! Create a reference to the Brightness Characteristic instantiated below - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // NEW! Instantiate the Brightness Characteristic with an initial value of 50% (same as we did in Example 4) level->setRange(5,100,1); // NEW! This sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% (different from Example 4 values) - this->channel=channel; // NEW! Save the channel number (from 0-15) - this->pwmPin=new PwmPin(channel, ledPin); // NEW! Configures the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // NEW! Configures a PWM LED for output to the specified pin. Note pinMode() does NOT need to be called in advance } // end constructor boolean update(){ // update() method - // Here we set the duty cycle (brightness) of the LED by callng pwmPin with the appropriate channel. - // The second argument should be a number from 0-100 (representing %brightness). HomeKit sets the on/off - // status of the LED ("power") separately from the brightness of the LED ("level"). This means HomeKit can - // request the LED be turned off, but retain the brightness level so that it does not need to be resent if - // the LED is turned back on. Multiplying the newValue of the power Characteristic (as a boolean) with the - // newValue of the Brightness Characteristic (as an int) is a short-hand way of creating the logic to - // set the PWM level when the LED is off (always zero) or on (whatever the brightness level is). + // Here we set the brightness of the LED by calling ledPin->set(brightness), where brightness=0-100. + // Note HomeKit sets the on/off status of a LightBulb separately from its brightness, which means HomeKit + // can request a LightBulb be turned off, but still retains the brightness level so that it does not need + // to be resent once the LightBulb is turned back on. - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + // Multiplying the newValue of the On Characteristic ("power", which is a boolean) with the newValue of the + // Brightness Characteristic ("level", which is an integer) is a short-hand way of creating the logic to + // set the LED level to zero when the LightBulb is off, or to the current brightness level when it is on. + + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino b/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino index ee71c3b..62d9f5c 100644 --- a/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino +++ b/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino @@ -102,7 +102,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new DEV_DimmableLED(0,17); // NEW! create a dimmable LED attached to pin 17 using PWM channel 0. See new code at end of DEV_LED.h + new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17 } // end of setup() diff --git a/examples/07-IdentifyRoutines/DEV_LED.h b/examples/07-IdentifyRoutines/DEV_LED.h index 0598e22..36c05de 100644 --- a/examples/07-IdentifyRoutines/DEV_LED.h +++ b/examples/07-IdentifyRoutines/DEV_LED.h @@ -31,26 +31,24 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->setRange(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->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin } // end constructor boolean update(){ // update() method - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/08-Bridges/08-Bridges.ino b/examples/08-Bridges/08-Bridges.ino index 2f40e6b..b01a4dd 100644 --- a/examples/08-Bridges/08-Bridges.ino +++ b/examples/08-Bridges/08-Bridges.ino @@ -98,7 +98,7 @@ void setup() { // new Service::HAPProtocolInformation(); - DELETED - NO LONGER NEEDED // new Characteristic::Version("1.1.0"); - DELETED - NO LONGER NEEDED - new DEV_DimmableLED(0,17); // create a dimmable LED attached to pin 17 using PWM channel 0 + new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17 } // end of setup() diff --git a/examples/08-Bridges/DEV_LED.h b/examples/08-Bridges/DEV_LED.h index 0598e22..36c05de 100644 --- a/examples/08-Bridges/DEV_LED.h +++ b/examples/08-Bridges/DEV_LED.h @@ -31,26 +31,24 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->setRange(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->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin } // end constructor boolean update(){ // update() method - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/09-MessageLogging/09-MessageLogging.ino b/examples/09-MessageLogging/09-MessageLogging.ino index 857a837..26f5081 100644 --- a/examples/09-MessageLogging/09-MessageLogging.ino +++ b/examples/09-MessageLogging/09-MessageLogging.ino @@ -85,11 +85,11 @@ void setup() { new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0); new DEV_LED(16); - // Defines a Dimmable LED Accessory attached to pin 17 using PWM channel 0 + // Defines a Dimmable (PWM-driven) LED Accessory attached to pin 17 new SpanAccessory(); new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); } // end of setup() diff --git a/examples/09-MessageLogging/DEV_LED.h b/examples/09-MessageLogging/DEV_LED.h index dc8e0ce..50ad054 100644 --- a/examples/09-MessageLogging/DEV_LED.h +++ b/examples/09-MessageLogging/DEV_LED.h @@ -53,31 +53,26 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED <- NEW!! - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->setRange(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; // LED pin number <- NEW!! - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin // Here we output log messages when the constructor is initially called. // We use Serial.print() since to ensure the message is always output - // regardless of the VERBOSITY setting. + // regardless of the VERBOSITY setting. Note that ledPin has a method getPin() + // that retrieves the pin number so you don't need to store it separately. Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -89,13 +84,8 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // is not functioning as expected. Since it's just for debugging, // we use LOG1() instead of Serial.print(). - // Note that in the prior example we did not save the ledPin number for - // DimmableLED since it was only needed by the constructor for initializing - // PwmPin(). For this example we add ledPin as a saved variable (see the two - // lines marketed NEW!! above) for the sole purpose of this log message. - LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); @@ -121,7 +111,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/10-RGB_LED/10-RGB_LED.ino b/examples/10-RGB_LED/10-RGB_LED.ino index 739ca4b..988dae0 100644 --- a/examples/10-RGB_LED/10-RGB_LED.ino +++ b/examples/10-RGB_LED/10-RGB_LED.ino @@ -59,15 +59,15 @@ void setup() { 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 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 DEV_DimmableLED(17); // Create a Dimmable (PWM-driven) LED using 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 DEV_RgbLED(32,22,23); // Create an RGB LED attached to pins 32,22,23 (for R, G, and B LED anodes) } // end of setup() diff --git a/examples/10-RGB_LED/DEV_LED.h b/examples/10-RGB_LED/DEV_LED.h index d571420..833b8ad 100644 --- a/examples/10-RGB_LED/DEV_LED.h +++ b/examples/10-RGB_LED/DEV_LED.h @@ -45,27 +45,21 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED 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) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->setRange(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 + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -73,12 +67,12 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); 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"); @@ -91,7 +85,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true @@ -102,32 +96,27 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) - PwmPin *redPin, *greenPin, *bluePin; - int redChannel, greenChannel, blueChannel; + LedPin *redPin, *greenPin, *bluePin; 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 + DEV_RgbLED(int red_pin, int green_pin, int blue_pin) : 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% V->setRange(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); - + this->redPin=new LedPin(red_pin); // configures a PWM LED for output to the RED pin + this->greenPin=new LedPin(green_pin); // configures a PWM LED for output to the GREEN pin + this->bluePin=new LedPin(blue_pin); // configures a PWM LED for output to the BLUE pin + char cBuf[128]; - sprintf(cBuf,"Configuring RGB LED: Pins=(%d,%d,%d) Channels=(%d,%d,%d)\n",redPin,greenPin,bluePin,redChannel,greenChannel,blueChannel); + sprintf(cBuf,"Configuring RGB LED: Pins=(%d,%d,%d)\n",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); Serial.print(cBuf); } // end constructor @@ -143,7 +132,7 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) p=power->getVal(); char cBuf[128]; - sprintf(cBuf,"Updating RGB LED on pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); + sprintf(cBuf,"Updating RGB LED: Pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); LOG1(cBuf); if(power->updated()){ @@ -178,24 +167,24 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) } LOG1(cBuf); - // Here we call a static function of PwmPin that converts HSV to RGB. + // Here we call a static function of LedPin 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 + LedPin::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) + R=p*r*100; // since LedPin 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); + redPin->set(R); // update the ledPin channels with new values + greenPin->set(G); + bluePin->set(B); return(true); // return true diff --git a/examples/11-ServiceOptions/11-ServiceOptions.ino b/examples/11-ServiceOptions/11-ServiceOptions.ino index 72b49e9..29764af 100644 --- a/examples/11-ServiceOptions/11-ServiceOptions.ino +++ b/examples/11-ServiceOptions/11-ServiceOptions.ino @@ -83,7 +83,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #1","HomeSpan","123-ABC","20mA LED","0.9",0); - (new DEV_DimmableLED(0,17))->setPrimary(); // Here we specify DEV_DimmableLED as the Primary Service by "chaining" setPrimary() to the pointer return by new. Note parentheses! + (new DEV_DimmableLED(17))->setPrimary(); // Here we specify DEV_DimmableLED as the Primary Service by "chaining" setPrimary() to the pointer return by new. Note parentheses! new Service::Fan(); new Characteristic::Active(); new Characteristic::RotationDirection(); @@ -91,8 +91,8 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #2","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); - (new Service::Fan())->setPrimary(); // Here we specify the Fan as the Primary Service. Again, note how we encapsulated the "new" command in parentheses, then chained setPrimary() + new DEV_DimmableLED(17); + (new Service::Fan())->setPrimary(); // Here we specify the Fan as the Primary Service. Again, note how we encapsulated the "new" command in parentheses, then chained setPrimary() new Characteristic::Active(); new Characteristic::RotationDirection(); new Characteristic::RotationSpeed(0); @@ -121,7 +121,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #3","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); // Here we create a name for the Dimmable LED new DEV_LED(16); new Characteristic::Name("Night Light"); // Here we create a name for the On/Off LED @@ -149,7 +149,7 @@ void setup() { new SpanAccessory(); (new DEV_Identify("Ceiling Fan #4","HomeSpan","123-ABC","20mA LED","0.9",0))->setPrimary(); // specify DEV_Identify as the Primary Service - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); new DEV_LED(16); new Characteristic::Name("Night Light"); @@ -172,7 +172,7 @@ void setup() { new Characteristic::RotationDirection(); new Characteristic::RotationSpeed(0); new Characteristic::Name("Fan"); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); new DEV_LED(16); new Characteristic::Name("Night Light"); diff --git a/examples/11-ServiceOptions/DEV_LED.h b/examples/11-ServiceOptions/DEV_LED.h index 1a9924c..66fe7ac 100644 --- a/examples/11-ServiceOptions/DEV_LED.h +++ b/examples/11-ServiceOptions/DEV_LED.h @@ -45,27 +45,21 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED 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) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->setRange(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 + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -73,12 +67,12 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); 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"); @@ -91,7 +85,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/13-TargetStates/DEV_DoorsWindows.h b/examples/13-TargetStates/DEV_DoorsWindows.h index c4c7a54..a42fa6c 100644 --- a/examples/13-TargetStates/DEV_DoorsWindows.h +++ b/examples/13-TargetStates/DEV_DoorsWindows.h @@ -107,8 +107,8 @@ struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Sha // Here we simulate a window shade that takes 5 seconds to move to its new target posiiton - if(target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... - current->setVal(target->getVal()); // ...set the current position to equal the target position + if(current->getVal()!=target->getVal() && target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... + current->setVal(target->getVal()); // ...set the current position to equal the target position } // Note there is no reason to send continuous updates of the current position to the HomeKit. HomeKit does NOT display the diff --git a/examples/15-RealPushButtons/15-RealPushButtons.ino b/examples/15-RealPushButtons/15-RealPushButtons.ino index 0290bfb..1fd56af 100644 --- a/examples/15-RealPushButtons/15-RealPushButtons.ino +++ b/examples/15-RealPushButtons/15-RealPushButtons.ino @@ -139,7 +139,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("PushButton LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17,23,5,18); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h + new DEV_DimmableLED(17,23,5,18); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h } // end of setup() diff --git a/examples/15-RealPushButtons/DEV_LED.h b/examples/15-RealPushButtons/DEV_LED.h index 2b22f05..ee2ca6f 100644 --- a/examples/15-RealPushButtons/DEV_LED.h +++ b/examples/15-RealPushButtons/DEV_LED.h @@ -16,8 +16,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // raise button: SHORT press increases brightness by 1%; LONG press increases brightness by 10%; DOUBLE press increases brightness to maximum // lower button: SHORT press decreases brightness by 1%; LONG press decreases brightness by 10%; DOUBLE press decreases brightness to minimum - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED + LedPin *ledPin; // reference to Led Pin int powerPin; // NEW! pin with pushbutton to turn on/off LED int raisePin; // NEW! pin with pushbutton to increase brightness int lowerPin; // NEW! pin with pushButton to decrease brightness @@ -26,9 +25,9 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED SpanCharacteristic *level; // reference to the Brightness Characteristic int favoriteLevel=50; // NEW! keep track of a 'favorite' level - // NEW! Consructor includes 3 additionl arguments to specify pin numbers for power, raise, and lower buttons + // NEW! Consructor includes 3 additional arguments to specify pin numbers for power, raise, and lower buttons - DEV_DimmableLED(int channel, int ledPin, int powerPin, int raisePin, int lowerPin) : Service::LightBulb(){ + DEV_DimmableLED(int pin, int powerPin, int raisePin, int lowerPin) : Service::LightBulb(){ power=new Characteristic::On(); @@ -46,15 +45,13 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED new SpanButton(raisePin,500); // NEW! create new SpanButton to increase brightness using pushbutton on pin number "raisePin" new SpanButton(lowerPin,500); // NEW! create new SpanButton to decrease brightness using pushbutton on pin number "lowerPin" - this->channel=channel; // save the channel number (from 0-15) - this->ledPin=ledPin; // save LED pin number this->powerPin=powerPin; // NEW! save power pushbutton pin number this->raisePin=raisePin; // NEW! save increase brightness pushbutton pin number this->lowerPin=lowerPin; // NEW! save decrease brightness pushbutton pin number - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); + Serial.print(ledPin->getPin()); Serial.print(" Channel="); Serial.print(channel); Serial.print("\n"); @@ -64,7 +61,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); @@ -82,7 +79,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true @@ -115,9 +112,9 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("Saved new brightness level="); // ...and output log message LOG1(favoriteLevel); LOG1("\n"); - pwmPin->set(channel,(1-power->getVal())*level->getVal()); // blink the LED to indicate new level has been saved + ledPin->set((1-power->getVal())*level->getVal()); // blink the LED to indicate new level has been saved delay(100); - pwmPin->set(channel,(1-power->getVal())*level->getVal()); + ledPin->set((1-power->getVal())*level->getVal()); } } else @@ -153,7 +150,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // Don't forget to set the new power and level for the actual LED - the above code by itself only changes the values of the Characteristics // within HomeKit! We still need to take an action on the actual LED itself. - // Note the line below is similar to, but not the same as, the pwmPin->set function used in the update() method above. Within the + // Note the line below is similar to, but not the same as, the ledPin->set function used in the update() method above. Within the // update() method we used getNewVal() because we wanted to change the LED to match the NEW VALUES requested by the user via the // HomeKit Controller. We did not need to (and must not) use setVal() to modify these values in the update() method since HomeSpan // automatically does this for us, provided we return StatusCode::OK at the end of the update() method. @@ -164,7 +161,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // as shown below. As usual, HomeSpan will send Event Notifications to all registered HomeKit Controllers letting them know about any changes // we made using setVal(). - pwmPin->set(channel,power->getVal()*level->getVal()); // update the physical LED to reflect the new values + ledPin->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values } diff --git a/examples/18-SavingStatus/18-SavingStatus.ino b/examples/18-SavingStatus/18-SavingStatus.ino new file mode 100644 index 0000000..f9f8031 --- /dev/null +++ b/examples/18-SavingStatus/18-SavingStatus.ino @@ -0,0 +1,115 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2021 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// Example 18: Saving Characteristic Status in NVS // +// * saving the state of two dimmable LEDs // +// // +// // +//////////////////////////////////////////////////////////// + +#include "HomeSpan.h" +#include "DEV_LED.h" +#include "DEV_Identify.h" + +void setup() { + + // In many of the prior examples we saw how Characteristics are initialized when first instantiated. You can either include an argument: + // + // new Characteristic::Brightness(25); + // + // in which case the value of the Brightness Characterisrtic is set to 25 when HomeSpan is powered up, or you can leave the argument blank: + // + // new Characteristic::Brightness(); + // + // in which case HomeSpan will apply a default value. + + // These methods work fine, with the exception that if the HomeSpan device loses power, it will boot up according to the parameters above rather + // than remembering the state of each Characteristic after you've made any changes via the Home App or with any PushButtons. + + // In this Example 18 we will see how to instruct HomeSpan to automatically save the values of one or more Characteristics in non-volatile storage (NVS) + // so that they can be restored to their latest state if the power is cycled. To do so, we call the constructor for a Characteristic with TWO arguments as such: + // + // new Characteristic::Brightness(25, true); + // + // This instructs HomeSpan to set the Brightness to 25 the very first time the device is powered on, but to SAVE any changes to this Characteristic + // in NVS, AND RESTORE the last-saved value whenever the power is cycled! + + // Note that though HomeSpan takes care of all the saving and restoring automatically for any Characteristic in which you set the second argument of + // the constructor to be "true," HomeSpan can't automatically perform any needed initialization of the physical appliance by itself. In other words, + // if you change the Brightness to 55 from the Home App and then sometime later the device loses power, HomeSpan will restore the value of the + // Brightness Characteristic to 55 on start-up, but you'll need to add some code to set the brightness of the actual LED once the value is restored. + + // To see how this works in practice, we'll configure HomeSpan to operate two Dimmable LEDs, each with its own on/off PushButton. As usual, all the code + // is implemented in DEV_LED.h, with comments highlighting all the new features. See DEV_LED.h for full details. + + 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("LED 1","HomeSpan","123-ABC","20mA LED","0.9",0); + new DEV_DimmableLED(17,19); // The first argument specifies the LED pin; the second argument specifies the PushButton pin + + new SpanAccessory(); + new DEV_Identify("LED 2","HomeSpan","123-ABC","20mA LED","0.9",0); + new DEV_DimmableLED(16,18); // The first argument specifies the LED pin; the second argument specifies the PushButton pin + +} // end of setup() + +////////////////////////////////////// + +void loop(){ + + homeSpan.poll(); + +} // end of loop() + +////////////////////////////////////// + +// OPERATING NOTES +// +// When the values of Characteristics are saved in NVS, they are stored based on a unique key that combines the UUID of the Characteristic with its AID and IID. +// If you are actively developing a configuration, adding or subtracting a new SpanAccessory or SpanService can alter the AID and IID of other Characteristics whose +// values were already stored in the NVS. If the new UUID/AID/IID combination is unused, the previously-stored value will not be restored upon the very next +// start-up and instead the value specified in the first argument of the constructor will be used and stored in the NVS as the initial value. +// +// If the new UUID/AID/IID happens to match a combination that was previously used, the value of the Characteristic will restored to whatever is found under that key +// in the NVS. +// +// *** To clear all values stored in the NVS, type 'V' in the HomeSpan CLI. This ensures that there are no stray key/value pairs in the NVS from prior iterations of your +// configuration. +// diff --git a/examples/18-SavingStatus/DEV_Identify.h b/examples/18-SavingStatus/DEV_Identify.h new file mode 100644 index 0000000..b8d21d6 --- /dev/null +++ b/examples/18-SavingStatus/DEV_Identify.h @@ -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(const char *name, const char *manu, const char *sn, const char *model, const 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 LED + + pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output + } + + boolean update(){ + + for(int i=0;isetRange(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(buttonPin); // create a new SpanButton to control power using PushButton on pin number "buttonPin" + + this->LED=new LedPin(ledPin); // configures a PWM LED for output to pin number "ledPin" + + Serial.print("Configuring Dimmable LED: Pin="); // initialization message + Serial.print(LED->getPin()); + Serial.print("\n"); + + LED->set(power->getVal()*level->getVal()); // NEW! IMPORTANT: Set the LED to its initial state at startup. Note we use getVal() here, since it is set upon instantiation. + + } // end constructor + + boolean update(){ // update() method + + LOG1("Updating Dimmable LED on pin="); + LOG1(LED->getPin()); + 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"); + + LED->set(power->getNewVal()*level->getNewVal()); // update the physical LED to reflect the new values + + return(true); // return true + + } // update + + void button(int pin, int pressType) override { + + if(pressType==SpanButton::SINGLE){ // only respond to SINGLE presses + power->setVal(1-power->getVal()); // toggle the value of the power Characteristic + LED->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values + } + + } // button + +}; + +////////////////////////////////// diff --git a/library.properties b/library.properties index 162b33d..2ac5483 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.2.1 +version=1.3.0 author=Gregg maintainer=Gregg sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE. diff --git a/src/HAP.cpp b/src/HAP.cpp index c29658b..6f1f2c1 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -26,7 +26,6 @@ ********************************************************************************/ #include -#include #include #include @@ -39,16 +38,10 @@ void HAPClient::init(){ size_t len; // not used but required to read blobs from NVS - nvs_flash_init(); // initialize non-volatile-storage partition in flash - - nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA data namespace in NVS - if(!nvs_get_blob(wifiNVS,"WIFIDATA",NULL,&len)) // if found WiFi data in NVS - nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data - if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data } else { @@ -1653,7 +1646,6 @@ void Nonce::inc(){ TLV HAPClient::tlv8; nvs_handle HAPClient::hapNVS; -nvs_handle HAPClient::wifiNVS; nvs_handle HAPClient::srpNVS; nvs_handle HAPClient::otaNVS; uint8_t HAPClient::httpBuf[MAX_HTTP+1]; diff --git a/src/HAP.h b/src/HAP.h index 070a170..1d48e89 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -28,7 +28,6 @@ #pragma once #include -#include #include "HomeSpan.h" #include "TLV.h" @@ -80,7 +79,6 @@ struct HAPClient { static TLV tlv8; // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6) static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data - static nvs_handle wifiNVS; // handle for non-volatile-storage of WiFi data static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data static nvs_handle otaNVS; // handle for non-volatile-storage of OTA data static uint8_t httpBuf[MAX_HTTP+1]; // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index c6faa3c..4c4480d 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -65,6 +65,20 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa hapServer=new WiFiServer(tcpPortNum); + nvs_flash_init(); // initialize non-volatile-storage partition in flash + nvs_open("CHAR",NVS_READWRITE,&charNVS); // open Characteristic data namespace in NVS + nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS + + size_t len; + + if(strlen(network.wifiData.ssid)){ // if setWifiCredentials was already called + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS + } else + + if(!nvs_get_blob(wifiNVS,"WIFIDATA",NULL,&len)) // else if found WiFi data in NVS + nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data + delay(2000); Serial.print("\n************************************************************\n" @@ -142,8 +156,14 @@ void Span::poll() { HAPClient::init(); // read NVS and load HAP settings if(!strlen(network.wifiData.ssid)){ - Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - statusLED.start(LED_WIFI_NEEDED); + Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. "); + if(autoStartAPEnabled){ + Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); + processSerialCommand("A"); + } else { + Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); + statusLED.start(LED_WIFI_NEEDED); + } } else { homeSpan.statusLED.start(LED_WIFI_CONNECTING); } @@ -726,9 +746,16 @@ void Span::processSerialCommand(const char *c){ case 'W': { + if(strlen(network.wifiData.ssid)>0){ + Serial.print("*** Stopping all current WiFi services...\n\n"); + hapServer->end(); + MDNS.end(); + WiFi.disconnect(); + } + network.serialConfigure(); - nvs_set_blob(HAPClient::wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data - nvs_commit(HAPClient::wifiNVS); // commit to NVS + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); statusLED.off(); delay(1000); @@ -744,10 +771,15 @@ void Span::processSerialCommand(const char *c){ MDNS.end(); WiFi.disconnect(); } + + if(apFunction){ + apFunction(); + return; + } network.apConfigure(); - nvs_set_blob(HAPClient::wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data - nvs_commit(HAPClient::wifiNVS); // commit to NVS + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** Credentials saved!\n\n"); if(strlen(network.setupCode)){ char s[10]; @@ -767,14 +799,22 @@ void Span::processSerialCommand(const char *c){ case 'X': { statusLED.off(); - nvs_erase_all(HAPClient::wifiNVS); - nvs_commit(HAPClient::wifiNVS); + nvs_erase_all(wifiNVS); + nvs_commit(wifiNVS); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); delay(1000); ESP.restart(); // re-start device } break; + case 'V': { + + nvs_erase_all(charNVS); + nvs_commit(charNVS); + Serial.print("\n*** Values for all saved Characteristics erased!\n\n"); + } + break; + case 'H': { statusLED.off(); @@ -800,8 +840,10 @@ void Span::processSerialCommand(const char *c){ statusLED.off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); - nvs_erase_all(HAPClient::wifiNVS); - nvs_commit(HAPClient::wifiNVS); + nvs_erase_all(wifiNVS); + nvs_commit(wifiNVS); + nvs_erase_all(charNVS); + nvs_commit(charNVS); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); delay(1000); ESP.restart(); @@ -884,6 +926,7 @@ void Span::processSerialCommand(const char *c){ Serial.print(" O - change the OTA password\n"); Serial.print(" A - start the HomeSpan Setup Access Point\n"); Serial.print("\n"); + Serial.print(" V - delete value settings for all saved Characteristics\n"); Serial.print(" U - unpair device by deleting all Controller data\n"); Serial.print(" H - delete HomeKit Device ID as well as all Controller data and restart\n"); Serial.print("\n"); @@ -892,17 +935,33 @@ void Span::processSerialCommand(const char *c){ Serial.print(" E - erase ALL stored data and restart\n"); Serial.print("\n"); Serial.print(" L - change the Log Level setting to \n"); - Serial.print("\n"); - Serial.print(" ? - print this list of commands\n"); - Serial.print("\n"); - Serial.print("\n*** End Commands ***\n\n"); + Serial.print("\n"); + + for(auto uCom=homeSpan.UserCommands.begin(); uCom!=homeSpan.UserCommands.end(); uCom++) // loop over all UserCommands using an iterator + Serial.printf(" @%c %s\n",uCom->first,uCom->second->s); + + if(!homeSpan.UserCommands.empty()) + Serial.print("\n"); + + Serial.print(" ? - print this list of commands\n\n"); + Serial.print("*** End Commands ***\n\n"); } break; + case '@':{ + + auto uCom=UserCommands.find(c[1]); + + if(uCom!=UserCommands.end()){ + uCom->second->userFunction(c+1); + break; + } + } + default: - Serial.print("** Unknown command: '"); + Serial.print("*** Unknown command: '"); Serial.print(c); - Serial.print("' - type '?' for list of commands.\n"); + Serial.print("'. Type '?' for list of commands.\n"); break; } // switch @@ -910,6 +969,17 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// +void Span::setWifiCredentials(const char *ssid, const char *pwd){ + sprintf(network.wifiData.ssid,"%.*s",MAX_SSID,ssid); + sprintf(network.wifiData.pwd,"%.*s",MAX_PWD,pwd); + if(wifiNVS){ // is begin() already called and wifiNVS is open + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS + } +} + +/////////////////////////////// + int Span::sprintfAttributes(char *cBuf){ int nBytes=0; @@ -1113,13 +1183,15 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){ LOG1(pObj[j].characteristic->aid); LOG1(" iid="); LOG1(pObj[j].characteristic->iid); - if(status==StatusCode::OK){ // if status is okay - pObj[j].characteristic->value - =pObj[j].characteristic->newValue; // update characteristic value with new value + if(status==StatusCode::OK){ // if status is okay + pObj[j].characteristic->value=pObj[j].characteristic->newValue; // update characteristic value with new value + if(pObj[j].characteristic->nvsKey){ // if storage key found + nvs_set_blob(charNVS,pObj[j].characteristic->nvsKey,&(pObj[j].characteristic->value),sizeof(pObj[j].characteristic->value)); // store data + nvs_commit(charNVS); + } LOG1(" (okay)\n"); - } else { // if status not okay - pObj[j].characteristic->newValue - =pObj[j].characteristic->value; // replace characteristic new value with original value + } else { // if status not okay + pObj[j].characteristic->newValue=pObj[j].characteristic->value; // replace characteristic new value with original value LOG1(" (failed)\n"); } pObj[j].characteristic->isUpdated=false; // reset isUpdated flag for characteristic @@ -1695,4 +1767,14 @@ SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t homeSpan.PushButtons.push_back(this); } + /////////////////////////////// +// SpanUserCommand // +/////////////////////////////// + +SpanUserCommand::SpanUserCommand(char c, const char *s, void (*f)(const char *v)){ + this->s=s; + userFunction=f; + + homeSpan.UserCommands[c]=this; +} diff --git a/src/HomeSpan.h b/src/HomeSpan.h index fbf2cde..d1e5b14 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -33,6 +33,7 @@ #include #include +#include #include "Settings.h" #include "Utils.h" @@ -66,6 +67,7 @@ struct SpanCharacteristic; struct SpanRange; struct SpanBuf; struct SpanButton; +struct SpanUserCommand; extern Span homeSpan; @@ -105,6 +107,8 @@ struct Span{ boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation) HapQR qrCode; // optional QR Code to use for pairing const char *sketchVersion="n/a"; // version of the sketch + nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data + nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data boolean connected=false; // WiFi connection status unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -122,7 +126,9 @@ struct Span{ char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters boolean otaAuth; // OTA requires password when set to true void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established - + boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found + void (*apFunction)()=NULL; // optional function to invoke when starting Access Point + WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status PushButton controlButton; // controls HomeSpan configuration and resets @@ -134,6 +140,8 @@ struct Span{ vector Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event vector PushButtons; // vector of pointer to all PushButtons unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs) + + unordered_map UserCommands; // map of pointers to all UserCommands void begin(Category catID=DEFAULT_CATEGORY, const char *displayName=DEFAULT_DISPLAY_NAME, @@ -174,6 +182,10 @@ struct Span{ void setSketchVersion(const char *sVer){sketchVersion=sVer;} // set optional sketch version number const char *getSketchVersion(){return sketchVersion;} // get sketch version number void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established + void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point + + void enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found + void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials }; /////////////////////////////// @@ -247,6 +259,7 @@ struct SpanCharacteristic{ boolean staticRange; // Flag that indiates whether Range is static and cannot be changed with setRange() boolean customRange=false; // Flag for custom ranges boolean *ev; // Characteristic Event Notify Enable (per-connection) + char *nvsKey=NULL; // key for NVS storage of Characteristic value uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic boolean isUpdated=false; // set to true when new value has been requested by PUT /characteristic @@ -370,7 +383,9 @@ struct SpanCharacteristic{ } // setRange() - template void init(T val, A min=0, B max=1){ + template void init(T val, boolean nvsStore, A min=0, B max=1){ + + int nvsFlag=0; uvSet(value,val); uvSet(newValue,val); @@ -378,36 +393,60 @@ struct SpanCharacteristic{ uvSet(maxValue,max); uvSet(stepValue,0); - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); - if(format!=STRING && format!=BOOL) - homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; - - boolean valid=false; - - for(int i=0; !valid && iServices.back()->req.size(); i++) - valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); - - for(int i=0; !valid && iServices.back()->opt.size(); i++) - valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type); - - if(!valid){ - homeSpan.configLog+=" *** ERROR! Service does not support this Characteristic. ***"; - homeSpan.nFatalErrors++; - } - - boolean repeated=false; + if(nvsStore){ + nvsKey=(char *)malloc(16); + uint16_t t; + sscanf(type,"%x",&t); + sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); + size_t len; + + if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ + nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); + newValue=value; + nvsFlag=2; + } + else { + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); // commit to NVS + nvsFlag=1; + } + } - for(int i=0; !repeated && iServices.back()->Characteristics.size(); i++) - repeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type); + homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); + if(format!=STRING && format!=BOOL) + homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; + + if(nvsFlag==2) + homeSpan.configLog+=" (restored)"; + else if(nvsFlag==1) + homeSpan.configLog+=" (storing)"; - if(valid && repeated){ - homeSpan.configLog+=" *** ERROR! Characteristic already defined for this Service. ***"; - homeSpan.nFatalErrors++; - } - - homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); - - homeSpan.configLog+="\n"; + boolean valid=false; + + for(int i=0; !valid && iServices.back()->req.size(); i++) + valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); + + for(int i=0; !valid && iServices.back()->opt.size(); i++) + valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type); + + if(!valid){ + homeSpan.configLog+=" *** ERROR! Service does not support this Characteristic. ***"; + homeSpan.nFatalErrors++; + } + + boolean repeated=false; + + for(int i=0; !repeated && iServices.back()->Characteristics.size(); i++) + repeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type); + + if(valid && repeated){ + homeSpan.configLog+=" *** ERROR! Characteristic already defined for this Service. ***"; + homeSpan.nFatalErrors++; + } + + homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); + + homeSpan.configLog+="\n"; } // init() @@ -442,6 +481,11 @@ struct SpanCharacteristic{ char dummy[]=""; sb.val=dummy; // set dummy "val" so that sprintfNotify knows to consider this "update" homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector + + if(nvsKey){ + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); + } } // setVal() @@ -474,7 +518,15 @@ struct SpanButton{ SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200); }; +/////////////////////////////// + +struct SpanUserCommand { + const char *s; // description of command + void (*userFunction)(const char *v); // user-defined function to call + + SpanUserCommand(char c, const char *s, void (*f)(const char *v)); +}; + ///////////////////////////////////////////////// - #include "Span.h" diff --git a/src/Settings.h b/src/Settings.h index f171e7f..ccf0323 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -33,8 +33,8 @@ // HomeSpan Version // #define HS_MAJOR 1 -#define HS_MINOR 2 -#define HS_PATCH 1 +#define HS_MINOR 3 +#define HS_PATCH 0 #define STRINGIFY(x) _STR(x) #define _STR(x) #x diff --git a/src/Span.h b/src/Span.h index f8eb3ce..64695af 100644 --- a/src/Span.h +++ b/src/Span.h @@ -384,7 +384,7 @@ namespace Service { // Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and mix/max value (not applicable for STRING or BOOL which default to min=0, max=1) #define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL) \ - struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,(TYPE)MINVAL,(TYPE)MAXVAL); } }; + struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; namespace Characteristic { diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 6fb67cf..1f85bbe 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -1,6 +1,179 @@ #include "PwmPin.h" -#include + +/////////////////// + +LedPin::LedPin(uint8_t pin, uint8_t level){ + if(numChannels+ServoPin::numChannels>15){ + Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels ***\n\n",pin); + ledChannel.gpio_num=0; + return; + } + + enabled=true; + + if(numChannels==0){ // first instantiation of an LedPin + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_0; + ledTimer.duty_resolution=LEDC_TIMER_10_BIT; + ledTimer.freq_hz=5000; + + ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; // configure both the HIGH-Speed Timer 0 and Low-Speed Timer 0 + ledc_timer_config(&ledTimer); + + ledTimer.speed_mode=LEDC_LOW_SPEED_MODE; + ledc_timer_config(&ledTimer); + } + + ledChannel.gpio_num=pin; + if(numChannels<8){ + ledChannel.speed_mode=LEDC_LOW_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(7-numChannels); + } else { + ledChannel.speed_mode=LEDC_HIGH_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(15-numChannels); + } + + numChannels++; + + ledChannel.intr_type=LEDC_INTR_DISABLE; + ledChannel.timer_sel=LEDC_TIMER_0; + ledChannel.hpoint=0; + ledc_channel_config(&ledChannel); + set(level); +} + +/////////////////// + +void LedPin::set(uint8_t level){ + if(!enabled) + return; + + ledChannel.duty=level*1023; + ledChannel.duty/=100; + ledChannel.duty&=0x03FF; + ledc_channel_config(&ledChannel); + +} + +/////////////////// + +void LedPin::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] + // s = [0,1] + // v = [0,1] + + int i; + float f, p, q, t; + + if( s == 0 ){ + *r = *g = *b = v; + return; + } + + h /= 60; + i = floor( h ) ; + f = h - i; + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + switch( i % 6 ) { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + case 5: + *r = v; + *g = p; + *b = q; + break; + } +} + +//////////////////////////// + +ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ + if(numChannels>7 || numChannels>(15-LedPin::numChannels)){ + Serial.printf("\n*** ERROR: Can't create ServoPin(%d) - no open PWM channels ***\n\n",pin); + servoChannel.gpio_num=0; + return; + } + + enabled=true; + + this->minMicros=minMicros; + this->maxMicros=maxMicros; + this->minDegrees=minDegrees; + microsPerDegree=(double)(maxMicros-minMicros)/(maxDegrees-minDegrees); + + if(numChannels==0){ // first instantiation of a ServoPin + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_1; + ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; + ledTimer.duty_resolution=LEDC_TIMER_16_BIT; + ledTimer.freq_hz=50; + ledc_timer_config(&ledTimer); + } + + servoChannel.gpio_num=pin; + servoChannel.speed_mode=LEDC_HIGH_SPEED_MODE; + servoChannel.channel=(ledc_channel_t)numChannels++; + servoChannel.intr_type=LEDC_INTR_DISABLE; + servoChannel.timer_sel=LEDC_TIMER_1; + servoChannel.hpoint=0; + servoChannel.duty*=micros2duty; + set(initDegrees); +} + +/////////////////// + +void ServoPin::set(double degrees){ + if(!enabled) + return; + + servoChannel.duty=(degrees-minDegrees)*microsPerDegree+minMicros; + + if(servoChannel.dutymaxMicros) + servoChannel.duty=maxMicros; + + servoChannel.duty*=micros2duty; + ledc_channel_config(&servoChannel); +} + +//////////////////////////// + +const double ServoPin::micros2duty=65535.0/20000.0; +uint8_t LedPin::numChannels=0; +uint8_t ServoPin::numChannels=0; + +//******************************************************* +// DEPRECATED - INCLUDED FOR BACKWARDS COMPATIBILITY ONLY +//******************************************************* PwmPin::PwmPin(uint8_t channel, uint8_t pin){ this->channel=channel & 0x0F; @@ -20,9 +193,12 @@ PwmPin::PwmPin(uint8_t channel, uint8_t pin){ ledChannel.timer_sel=LEDC_TIMER_0; ledChannel.duty=0; ledChannel.hpoint=0; + ledc_channel_config(&ledChannel); } +/////////////////// + void PwmPin::set(uint8_t channel, uint8_t level){ ledChannel.duty=level*1023; ledChannel.duty/=100; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 791fd7f..76b79fb 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -1,25 +1,78 @@ -///////////////////////////////////// -// PWM Pin Control // -///////////////////////////////////// - -// A wrapper around the ESP-IDF ledc library to easily set the brightness of an LED from 0-100%. -// Can be used for any device requiring a PWM output (not just an LED). Frequency of PWM -// is hardcoded to 5000 Hz and either High-Speed Timer-0 (for channels 0-7) or Low-Speed Timer-0 -// for channels (8-15) is configured and selected automatically. +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ----- PWM Pin Control ----- +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Wrappers around the ESP-IDF ledc library to control PWM-based devices: +// +// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz +// - use set(level) to set brightness from 0-100% +// +// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz +// - use set(degrees) to set position to degrees +// +// Max number of LedPin instantiations: 16 +// Max number of ServoPin instantiatons: 8 +// Max combined limit (LedPins+ServoPins): 16 +// +// Instantiation of an LedPin or ServoPin that causes any of the maximums above to be exceeded throws +// an error message. The object will still be created, but calls to set(level) or set(degrees) are ignored. +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include #include +///////////////////////////////////// + +class LedPin { + boolean enabled=false; + ledc_channel_config_t ledChannel; + + public: + LedPin(uint8_t pin, uint8_t level=0); // assigns pin to be output of one of 16 PWM channels within initial level + void set(uint8_t level); // sets the PWM duty to level (0-100) + int getPin(){return ledChannel.gpio_num;} // returns the pin number + + static uint8_t numChannels; + static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B +}; + +///////////////////////////////////// + +class ServoPin { + boolean enabled=false; + uint16_t minMicros; + uint16_t maxMicros; + double minDegrees; + double microsPerDegree; + ledc_channel_config_t servoChannel; + + static const double micros2duty; + + public: + ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); + ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {}; + + void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] + int getPin(){return servoChannel.gpio_num;} // returns the pin number + + static uint8_t numChannels; +}; + +//******************************************************* +// DEPRECATED - INCLUDED FOR BACKWARDS COMPATIBILITY ONLY +//******************************************************* + class PwmPin { uint8_t channel; uint8_t pin; ledc_channel_config_t ledChannel; public: - PwmPin(uint8_t channel, uint8_t pin); // assigns pin to be output of one of 16 PWM channels (0-15) - 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 + PwmPin(uint8_t channel, uint8_t pin); // assigns pin to be output of one of 16 PWM channels (0-15) + void set(uint8_t channel, uint8_t level); // sets the PWM duty to level (0-100) + int getPin(){return pin;} // returns the pin number static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B - }; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index d07e894..1bdb34a 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,29 +2,65 @@ // This is a placeholder .ino file that allows you to easily edit the contents of this library using the Arduino IDE, // as well as compile and test from this point. This file is ignored when the library is included in other sketches. -#include "RFControl.h" - +#include "PwmPin.h" + void setup(){ - + Serial.begin(115200); delay(1000); Serial.print("\n\nTest sketch for HomeSpan Extras Library\n\n"); - RFControl rf(4); - Serial.println("Starting..."); + + + LedPin yellow(16,10); + LedPin d1(19); + LedPin d2(19); + LedPin d3(19); + LedPin d4(19); + LedPin d5(19); + LedPin d6(19); + LedPin d7(19); + LedPin d8(19); + LedPin d9(19); + LedPin d10(19); + LedPin d11(19); + LedPin d12(19); + LedPin red(17); + +// ServoPin servo(18,0,500,2200,-90,90); + ServoPin s0(19); + ServoPin servo(18,45); + ServoPin s1(19); + + while(1){ + for(int i=0;i<100;i++){ + yellow.set(i); + delay(10); + } - rf.clear(); - for(int i=0;i<3;i++) - rf.add(2000,2000); - rf.phase(10000,0); - rf.start(5,100); + for(int i=100;i>=0;i--){ + red.set(i); + delay(10); + } + } + + while(1){ + double STEP=1; - Serial.println("Done!"); + for(int i=-100*STEP;i<=100*STEP;i++){ + servo.set((double)i/STEP); + delay(10); + } + for(int i=100*STEP;i>=-100*STEP;i--){ + servo.set((double)i/STEP); + delay(10); + } + } + } void loop(){ - } diff --git a/src/src.ino b/src/src.ino index 3becb91..60385c0 100644 --- a/src/src.ino +++ b/src/src.ino @@ -18,6 +18,12 @@ void setup() { homeSpan.setSketchVersion("Test 1.3.1"); homeSpan.setWifiCallback(wifiEstablished); + new SpanUserCommand('d',"- My Description",userCom1); + new SpanUserCommand('e',"- My second Description",userCom2); + + homeSpan.enableAutoStartAP(); + homeSpan.setApFunction(myWiFiAP); + homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments @@ -30,21 +36,18 @@ void setup() { new Characteristic::FirmwareRevision(HOMESPAN_VERSION); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory) new Characteristic::Identify(); // Create the required Identify -// new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service + new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP new Service::LightBulb(); -// new Characteristic::On(); + new Characteristic::On(0); new Characteristic::Brightness(); new Characteristic::Name("Light 1"); + new Characteristic::ColorTemperature(); new Service::LightBulb(); - new Characteristic::On(2); - (new Characteristic::Brightness(150))->setRange(0,140,5); + new Characteristic::On(0,true); + (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 2"); - (new Service::Switch())->setPrimary(); - new Characteristic::On(); - new Characteristic::Name("Switch 3"); - new SpanButton(17); } // end of setup() @@ -58,6 +61,25 @@ void loop(){ ////////////////////////////////////// +void myWiFiAP(){ + Serial.print("Calling My WIFI AP\n\n"); + homeSpan.setWifiCredentials("MY_NETWORK","MY_PASSWORD"); +} + +////////////////////////////////////// + void wifiEstablished(){ Serial.print("IN CALLBACK FUNCTION\n\n"); } + +////////////////////////////////////// + +void userCom1(const char *v){ + Serial.printf("In User Command 1: '%s'\n\n",v); +} + +////////////////////////////////////// + +void userCom2(const char *v){ + Serial.printf("In User Command 2: '%s'\n\n",v); +}