Merge pull request #100 from HomeSpan/release-1.3.0

Release 1.3.0
This commit is contained in:
HomeSpan 2021-06-20 18:07:25 -05:00 committed by GitHub
commit 5e8d8ed76f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1141 additions and 259 deletions

View File

@ -0,0 +1,68 @@
////////////////////////////////////
// DEVICE-SPECIFIC LED SERVICES //
////////////////////////////////////
#include <extras/PwmPin.h>
////////////////////////////////////
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()<current->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
};

View File

@ -0,0 +1,38 @@
//////////////////////////////////
// DEVICE-SPECIFIC SERVICES //
//////////////////////////////////
struct DEV_Identify : Service::AccessoryInformation {
int nBlinks; // number of times to blink built-in LED in identify routine
SpanCharacteristic *identify; // reference to the Identify Characteristic
DEV_Identify(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<nBlinks;i++){
digitalWrite(homeSpan.getStatusPin(),LOW);
delay(250);
digitalWrite(homeSpan.getStatusPin(),HIGH);
delay(250);
}
return(true); // return true
} // update
};

View File

@ -0,0 +1,60 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2020 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.
*
********************************************************************************/
// This example demonstrates how to control a real-world Servo Motor using HomeSpan's
// ServoPin Class, as included in "extras/PwmPin.h" The code builds upon the
// WindowShade Accessory from Example 13 by adding a Horizontal Tilt Characteristic that
// is controlled by a Servo connected to the ESP32.
#include "HomeSpan.h"
#include "DEV_Identify.h"
#include "DEV_DoorsWindows.h"
void setup() {
Serial.begin(115200);
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
new SpanAccessory();
new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3);
new Service::HAPProtocolInformation();
new Characteristic::Version("1.1.0");
new SpanAccessory();
new DEV_Identify("Window Shade","HomeSpan","123-ABC","Shade","0.9",0);
new DEV_WindowShade(18); // Create a motorized Window Shade with a Servo attached to Pin 18 that controls the Horizontal Tilt of the Shade
} // end of setup()
//////////////////////////////////////
void loop(){
homeSpan.poll();
} // end of loop()

View File

@ -59,6 +59,9 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p
* **A** - start the HomeSpan Setup Access Point
* This command starts HomeSpan's temporary Access Point, which provides users with an alternate methods for configuring a device's WiFi Credentials and HomeKit Setup Code. Starting the Access Point with this command is identical to starting it via the Control Button. See the [HomeSpan User Guide](UserGuide.md) for complete details.
* **V** - erases the values of any saved Characteristics
* As Characteristics are updated via the Home App, their latest values can be (optionally) saved in the device's non-volatile storage (NVS). If the device should ever lose power this allows HomeSpan to restore the latest values of saved Characteristic upon the next start-up. Typing 'V' from the CLI deletes all previously-saved Characteristic values from the NVS, though it does not alter the current values of those Characteristics. This is useful in the event that saved Characteristics become out-of-sync with their stored values during the development phase of your sketch when adding, deleting, and changing the configuration of new Accessories, Services, and Characteristics.
* **U** - unpair device by deleting all Controller data
* This deletes all data stored about Controllers that have been paired with the device, which forces HomeSpan to reset its internal state to unpaired. Normally, unpairing is done by HomeKit at the direction of an end-user via the Home App on an iPhone. However, HomeKit requests to unpair a device are not subject to any confirmation from that device. HomeKit simply assumes that once it requests a device to unpair, the device has received the message and has reset its pairing state accordingly. In the event that HomeKit unpairs a HomeSpan device, but the device does not receive or properly process the request, its pairing status will be out of sync with HomeKit. Forcing HomeKit to reset its internal state to unpaired using this command resolves the issue and allows HomeSpan to be re-paired with HomeKit.
* Note that if you run this command when HomeKit thinks it is still paired to the device, pairing status will be out of sync in the opposite direction. HomeKit Controllers will continue to send HAP requests to the device, thinking it is paired, but HomeSpan will ignore all these requests since it no longer recognizes any of the Controllers as being paired. To resolve this issue, you must instruct HomeKit to unpair the device via the Home App, after which you can re-pair the device if needed.
@ -82,8 +85,14 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p
* 0 (minimal diagnostics),
* 1 (all diagnostics), and
* 2 (all diagnostics plus a real-time stream of all HAP communication between HomeSpan and any connected HomeKit Controllers).
* **?** - prints a menu of all CLI commands
### User-Defined Commands
---
You can extend the HomeSpan CLI with custom functions using `SpanUserCommand()`. This class allows you to assign a single-character name to any custom function that will be called when you type the '@' symbol following by the single-character name into the CLI. For example, if you assigned the character 'K' to a custom function, you would type '@K' into the CLI to invoke it. This allows you to use any single-character name, even if that character is already used by HomeSpan for its built-in commands. The `SpanUserCommand` class also allows you to include a short text description of your function that will be added to the menu of commands when you type '?' into the CLI. See the the [API Reference](Reference.md#spanusercommandchar-c-const-char-s-void-fconst-char-v) for full details.
---
[↩️](README.md) Back to the Welcome page

View File

@ -4,28 +4,23 @@ HomeSpan includes integrated access to a number of ESP32 features you'll likely
## Pulse Width Modulation (PWM)
PWM on the ESP32 is more flexible, but slighly more complicated, than PWM on most Arduino devices (like the Uno or Mega). On the ESP32, you use one of 16 built-in timer-channels to create a PWM signal, and then link that channel to any ESP32 pin. HomeSpan includes a library that makes this very easy, and is accessed as by placing the following near the top of your sketch:
The ESP32 has 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessd by placing the following near the top of your sketch:
`#include "extras/PwmPin.h"`
### *PwmPin(uint8_t channel, uint8_t pin)*
### *LedPin(uint8_t pin)*
Creating an instance of this **class** links one of 16 timer-channels to an ESP32 pin.
Creating an instance of this **class** configures the specified *pin* to output a 5000 Hz PWM signal, which is suitable for dimming LEDs. The following methods are supported:
* *channel* - the ESP32 timer-channel number (0-15) to generate the PWM signal
* *pin* - the ESP32 pin that will output the PWM signal produced by the channel
* `void set(uint8_t level)`
The following methods are supported:
* `void set(uint8_t channel, uint8_t level)`
* sets the PWM %duty-cycle of timer-channel *channel* (0-15) to *level*, where *level* ranges from 0 (off) to 100 (steady on)
* sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on)
* `int getPin()`
* returns the pin number
PwmPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS).
LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS).
* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)`
@ -36,7 +31,38 @@ PwmPin also includes a static class function that converts Hue/Saturation/Bright
* *g* - output Green value, range 0-1
* *b* - output Blue value, range 0-1
See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using PwmPin to control an RGB LED.
See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED.
### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])*
Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows:
* *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin
* *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0°)
* *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s)
* *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s)
* *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90°)
* *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90°)
The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported:
* `void set(uint8_t position)`
* sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*.
* `int getPin()`
* returns the pin number
A worked example showing how ServoPin can be used to control the Horizontal Tilt of a motorized Window Shade can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → ServoControl*](https://github.com/HomeSpan/HomeSpan/tree/dev/Other%20Examples/ServoControl).
Resource limitations:
* A maximum of 16 LedPin objects can be instantiated
* A maximum of 8 ServoPin objects can be instantiated
* A maximum combined total of 16 LedPin and ServoPin objects can be instantiated (for example 10 LedPin and 6 ServoPin objects)
HomeSpan will report a non-fatal error message to the Arduino Serial Monitor for each LedPin or ServoPin that instantiated beyond these limits. Calls to the `set()` method for objects that exceed these limits are ignored.
## Remote Control Radio Frequency / Infrared Signal Generation
@ -76,11 +102,11 @@ Since most RF/IR signals repeat the same train of pulses more than once, the dur
* starts the transmission of the pulse train stored in the pulse train memory buffer. The signal will be output on the *pin* specified when RFControl was instantiated. Note this is a blocking call—the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long
* *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions)
* *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions)
* *tickTime* - the duration, in **microseconds**, of a *tick*. This is an optional argument with a default of 1𝛍s if not specified. Valid range is 1-255𝛍s, or set to 0 for 256𝛍s
Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under *File → Examples → HomeSpan → Other Examples → RemoteControl*.
Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteControl*](https://github.com/HomeSpan/HomeSpan/tree/dev/Other%20Examples/RemoteControl).
```C++
/* HomeSpan Remote Control Example */
@ -135,4 +161,20 @@ void loop(){
---
#### Deprecated functions (available for backwards compatibility with older sketches):
*PwmPin(uint8_t channel, uint8_t pin)*
* this legacy function was used to generically control the ESP32's built-in PWM generators to drive a dimmable LED and required the user to keep track of individual PWM channels. It has been replaced by two specific (and much easier-to-use) methods:
* *LedPin(uint8_t pin)* - drives a dimmable LED
* *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* - drives a Servo Motor
* last supported version: [v1.2.1](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.1/docs/Extras.md#pwmpinuint8_t-channel-uint8_t-pin)
* **please use** `LedPin` and `ServoPin` **for all new sketches**
---
[↩️](README.md) Back to the Welcome page

View File

@ -32,6 +32,10 @@
* Though HomeKit now supports Television controls, at the time of this posting Apple has not publicly released any specifications, SDKs, or ADKs, related to HAP Television Services or any HAP Television Characteristics. It appears that for now these Services are meant only for commercial use. Support for HAP Television Services and Characteristics will be added to HomeSpan as soon as Apple makes the specifications publicly available in some form or another.
#### Can you use HomeSpan with an Ethernet connection instead of a WiFi connection?
* Not as present. Though with a compatible Ethernet board the ESP32 can be configured to run as an Ethernet Server, using MDNS over Ethernet does not work on the ESP32 due to some apparent problems with the Ethernet UDP stack. Unfortunately, HomeSpan and HAP-R2 require MDNS to operate. If anyone has managed to get an Ethernet version of MDNS working on an ESP32 please let me know - it would be great to add Ethernet support to HomeSpan.
---
[↩️](README.md) Back to the Welcome page

View File

@ -56,7 +56,7 @@ The use of these two components to configure a standalone HomeSpan device, inclu
## What Next?
If you've not yet read through the [HomeSpan API Overview](Overview.md) page, you should do so now so you'll have a good understanding of the framework for developing HomeSpan sketches. If you are new to HomeKit development, you may also want to read through HomeSpan's [HomeKit Primer](HomeKitPrimer.md), which should provide you with some important foundational knowledge about HomeKit itself.
If you've not yet read through the [HomeSpan API Overview](Overview.md) page, you should do so now so you'll have a good understanding of the framework for developing HomeSpan sketches.
Next, explore the tutorial sketches, upload a few, and see how they work. The examples start simple and grow in complexity, taking you through all the functions and features of HomeSpan. Along the way you'll also learn a lot of HomeKit tips and tricks. See [HomeSpan Tutorials](Tutorials.md) for a summary of all the included examples. Find something in a sketch you don't understand? Visit the [HomeSpan API Reference](Reference.md) for details on all HomeSpan objects, functions, and methods. Have a more general question? See if it's been answered on the [HomeSpan FAQ](FAQ.md) page or any of the [Disussion](https://github.com/HomeSpan/HomeSpan/discussions) or [Issues](https://github.com/HomeSpan/HomeSpan/issues) pages. If not, feel free to join the Discusion by adding a new question.

View File

@ -21,10 +21,12 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A
* Complete transparency to every underlying HomeKit action, data request, and data response
* Command-line interface with a variety of info, debugging, and configuration commands
* Built-in database validation to ensure your configuration meets all HAP requirements
* Integrated PWM functionality supporting pulse-wave-modulation on any ESP32 pin
* Dedicated classes that utilize the ESP32's 16-channel PWM peripheral for easy control of:
* LED Brightness
* Servo Motors
* Integrated Push Button functionality supporting single, double, and long presses
* Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals
* 17 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks
* 18 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks
### For the HomeSpan End-User
@ -37,16 +39,25 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A
* Launch the WiFi Access Point
* A standalone, detailed End-User Guide
## Latest Update (3/13/2021)
## Latest Update - HomeSpan 1.3.0 (6/20/2021)
* HomeSpan 1.2.1 - This update adds run-time range-checking for all Characteristics and will warn you if the initial value you set for a Characteristic, or any subsequent changes you make to that value, are outside the Characteristic's allowable min/max range. This helps diagnosis "No Response" errors in the Home App. This update also introduces `setRange(min,max,step)` as a new and more robust method for changing a Characteristic's range. See [Release](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update.
This update brings a number of new features and enhancements:
* The PWM library has been—
* upgraded to allow for much easier control of up to 16 dimmable LEDs, ***and***
* extended with a dedicated class to simultaneously operate up to 8 Servo Motors!
* Characteristic values can be automatically saved in non-volatile storage for retention in the event of a power loss. When power is restored your Accessories will automatically revert to their most recent state!
* The HomeSpan CLI can now be customized — extend the CLI with your own functions and commands!
* Enable the automatic launch of HomeSpan's WiFi Access Point upon start-up whenever WiFi Credentials are not found.
* For advanced users: create your own custom WiFi Access Point and set your WiFi Credentials programmatically.
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update.
# HomeSpan Resources
HomeSpan includes the following documentation:
* [Getting Started with HomeSpan](https://github.com/HomeSpan/HomeSpan/blob/master/docs/GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices
* [HomeKit Primer](https://github.com/HomeSpan/HomeSpan/blob/master/docs/HomeKitPrimer.md) - a gentle introduction to Apple HomeKit and HAP terminology :construction:
* [HomeSpan API Overview](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch
* [HomeSpan Tutorials](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Tutorials.md) - a guide to HomeSpan's tutorial-sketches
* [HomeSpan Services and Characteristics](https://github.com/HomeSpan/HomeSpan/blob/master/docs/ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan
@ -56,7 +67,7 @@ HomeSpan includes the following documentation:
* [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) - a complete guide to the HomeSpan Library API
* [HomeSpan QR Codes](https://github.com/HomeSpan/HomeSpan/blob/master/docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices
* [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection
* [HomeSpan Extras](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Extras.md) - integrated access to the ESP32's on-chip PWM and Remote Control peripherals!
* [HomeSpan Extras](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Extras.md) - integrated access to the ESP32's on-chip LED, Servo Motor, and Remote Control peripherals!
* [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library
* [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions

View File

@ -24,6 +24,8 @@ At runtime this HomeSpan will create a global **object** named `homeSpan` that s
* checks for HAP requests, local commands, and device activity
* **must** be called repeatedly in each sketch and is typically placed at the top of the Arduino `loop()` method
---
The following **optional** `homeSpan` methods override various HomeSpan initialization parameters used in `begin()`, and therefore **should** be called before `begin()` to take effect. If a method is *not* called, HomeSpan uses the default parameter indicated below:
* `void setControlPin(uint8_t pin)`
@ -72,12 +74,40 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
* the HomeSpan default is "HSPN" unless permanently changed for the device via the [HomeSpan CLI](CLI.md) using the 'Q' command
* *id* must be exactly 4 alphanumeric characters (0-9, A-Z, and a-z). If not, the request to change the Setup ID is silently ignored and the default is used instead
---
The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment:
* `void enableOTA(boolean auth=true)`
* enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled
* HomeSpan OTA requires an authorizing password unless *auth* is specified and set to *false*
* the default OTA password for new HomeSpan devices is "homespan-ota"
* this can be changed via the [HomeSpan CLI](CLI.md) using the 'O' command
* `void enableAutoStartAP()`
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
* methods to alter the behavior of HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect
* `void setApFunction(void (*func)())`
* replaces HomeSpan's built-in WiFi Access Point with user-defined function *func*
* *func* must be of type *void* and have no arguments
* *func* will be called instead of HomeSpan's built-in WiFi Access Point whenever the Access Point is launched:
* via the CLI by typing 'A', or
* via the Control Button using option 3 of the Command Mode, or
* automatically upon start-up if `enableAutoStartAP()` is set and there are no stored WiFi Credentials
* after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values
* it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved
* `void setWifiCredentials(const char *ssid, const char *pwd)`
* sets the SSID (*ssid*) and password (*pwd*) of the WiFi network to which HomeSpan will connect
* *ssid* and *pwd* are automatically saved in HomeSpan's non-volatile storage (NVS) for retrieval when the device restarts
* note that the saved values are truncated if they exceed the maximum allowable characters (ssid=32; pwd=64)
> :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**
---

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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
}

View File

@ -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.
//

View File

@ -0,0 +1,38 @@
//////////////////////////////////
// DEVICE-SPECIFIC SERVICES //
//////////////////////////////////
struct DEV_Identify : Service::AccessoryInformation {
int nBlinks; // number of times to blink built-in LED in identify routine
SpanCharacteristic *identify; // reference to the Identify Characteristic
DEV_Identify(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<nBlinks;i++){
digitalWrite(homeSpan.getStatusPin(),LOW);
delay(250);
digitalWrite(homeSpan.getStatusPin(),HIGH);
delay(250);
}
return(true); // return true
} // update
};

View File

@ -0,0 +1,75 @@
////////////////////////////////////
// DEVICE-SPECIFIC LED SERVICES //
////////////////////////////////////
#include "extras/PwmPin.h" // library of various PWM functions
////////////////////////////////////
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
// This version of the Dimmable LED Service includes a PushButton that can be used to turn on/off the LED. Status of both the
// power state and the brightness of the LED are stored in NVS for restoration if the device reboots.
LedPin *LED; // reference to an LedPin
SpanCharacteristic *power; // reference to the On Characteristic
SpanCharacteristic *level; // reference to the Brightness Characteristic
DEV_DimmableLED(int ledPin, int buttonPin) : Service::LightBulb(){
power=new Characteristic::On(0,true); // NEW! Second argument is true, so the value of the On Characteristic (initially set to 0) will be saved in NVS
level=new Characteristic::Brightness(5,true); // NEW! Second argument is true, so the value of the Brightness Characteristic (initially set to 5) will be saved in NVS
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%
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
};
//////////////////////////////////

View File

@ -1,5 +1,5 @@
name=HomeSpan
version=1.2.1
version=1.3.0
author=Gregg <homespan@icloud.com>
maintainer=Gregg <homespan@icloud.com>
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.

View File

@ -26,7 +26,6 @@
********************************************************************************/
#include <ESPmDNS.h>
#include <nvs_flash.h>
#include <sodium.h>
#include <MD5Builder.h>
@ -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<kTLVType,10> 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];

View File

@ -28,7 +28,6 @@
#pragma once
#include <WiFi.h>
#include <nvs.h>
#include "HomeSpan.h"
#include "TLV.h"
@ -80,7 +79,6 @@ struct HAPClient {
static TLV<kTLVType,10> 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)

View File

@ -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 <RETURN>'.\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 <RETURN>'.\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 <level> - change the Log Level setting to <level>\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;
}

View File

@ -33,6 +33,7 @@
#include <Arduino.h>
#include <unordered_map>
#include <nvs.h>
#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<SpanBuf> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
vector<SpanButton *> PushButtons; // vector of pointer to all PushButtons
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
unordered_map<char, SpanUserCommand *> 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 <typename T, typename A=boolean, typename B=boolean> void init(T val, A min=0, B max=1){
template <typename T, typename A=boolean, typename B=boolean> 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 && i<homeSpan.Accessories.back()->Services.back()->req.size(); i++)
valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type);
for(int i=0; !valid && i<homeSpan.Accessories.back()->Services.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 && i<homeSpan.Accessories.back()->Services.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 && i<homeSpan.Accessories.back()->Services.back()->req.size(); i++)
valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type);
for(int i=0; !valid && i<homeSpan.Accessories.back()->Services.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 && i<homeSpan.Accessories.back()->Services.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"

View File

@ -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

View File

@ -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 {

View File

@ -1,6 +1,179 @@
#include "PwmPin.h"
#include <Arduino.h>
///////////////////
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.duty<minMicros)
servoChannel.duty=minMicros;
else if(servoChannel.duty>maxMicros)
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;

View File

@ -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 <Arduino.h>
#include <driver/ledc.h>
/////////////////////////////////////
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
};

View File

@ -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(){
}

View File

@ -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);
}