ESP + Reorganizacja
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 1: A non-functioning on/off light control //
|
||||
// constructed from basic HomeSpan components //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// WELCOME TO HOMESPAN!
|
||||
|
||||
// This first example introduces the HomeSpan library and demonstrates how to implement a simple on/off light control
|
||||
// using a combination of HomeSpan Accessory, Service, and Characteristic objects. Once this sketch has been uploaded
|
||||
// to your HomeSpan device and the device is paired to your home, a new "lightbulb" tile will appear in the Home App of your iPhone,
|
||||
// iPad, or Mac.
|
||||
|
||||
// Though the tile will be fully operational (i.e. you can change the status of the lightbulb from "on" or "off"), we won't yet connect
|
||||
// an actual light or LED to the HomeSpan device, so nothing real will light up. Instead, in this and the next few examples, we'll focus
|
||||
// on learning about the different ways HomeKit controls can be configured. Starting in Example 5, we'll connect an LED to the device
|
||||
// and introduce the methods that actually turn the LED on and off from your Home App.
|
||||
|
||||
// NOTE: All HomeSpan examples are best understood when reviewed in conjunction with the documentation provided on the HomeSpan GitHub page.
|
||||
// See https://github.com/HomeSpan/HomeSpan for details and references. In particular, you may want to review the HomeSpan API Overview
|
||||
// page before proceeding.
|
||||
|
||||
// LET'S GET STARTED...
|
||||
|
||||
#include "HomeSpan.h" // HomeSpan sketches always begin by including the HomeSpan library
|
||||
|
||||
void setup() { // Your HomeSpan code should be placed within the standard Arduino setup() function
|
||||
|
||||
Serial.begin(115200); // Start a serial connection so you can receive HomeSpan diagnostics and control the device using HomeSpan's Command-Line Interface (CLI)
|
||||
|
||||
// The HomeSpan library creates a global object named "homeSpan" that encapsulates all HomeSpan functionality.
|
||||
// The begin() method is used to initialize HomeSpan and start all HomeSpan processes.
|
||||
|
||||
// The first two parameters are Category and Name, which are used by HomeKit to configure the icon and name
|
||||
// of the device shown in the Home App when initially pairing a HomeSpan device with your iPhone.
|
||||
|
||||
// In addition, the Name you choose below will be used as the "default name" for all Accessory Tiles. When you first
|
||||
// pair the device, the Home App will display this default name and allow you to change it (for each Accessory Tile)
|
||||
// before pairing is complete. However, even after the device is paired you can always change the name of any
|
||||
// Accessory Tile directly from the Home App via the set-up screen for any Tile.
|
||||
|
||||
// IMPORTANT: The Name you choose below MUST BE UNIQUE across all your HomeSpan devices!
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LightBulb"); // initializes a HomeSpan device named "HomeSpan Lightbulb" with Category set to Lighting
|
||||
|
||||
// Next, we construct a simple HAP Accessory Database with a single Accessory containing 3 Services,
|
||||
// each with their own required Characteristics.
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
|
||||
// The only required Characteristic for the Accessory Information Service is the special Identify Characteristic. It takes no arguments:
|
||||
|
||||
new Characteristic::Identify(); // Create the required Identify Characteristic
|
||||
|
||||
// The Accessory Information Service also includes these four OPTIONAL Characteristics. They perform no function and are for
|
||||
// informational purposes only --- their values are displayed in HomeKit's setting panel for each Accessory. Feel free
|
||||
// to uncomment the lines and implement any combination of them, or none at all.
|
||||
|
||||
// new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::SerialNumber("123-ABC"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::Model("120-Volt Lamp"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
// new Characteristic::FirmwareRevision("0.9"); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
|
||||
// *NOTE* HAP requires that the Accessory Information Service always be instantiated BEFORE any other Services, which is why we created it first.
|
||||
|
||||
// Now that the required "informational" Services have been defined, we can finally create our Light Bulb Service
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// That's all that's needed to define a database from scratch, including all required HAP elements, to control a single lightbulb.
|
||||
// Of course this sketch does not yet contain any code to implement the actual operation of the light - there is nothing to
|
||||
// turn on and off. But you'll still see a Light Bulb tile show up in your Home App with an ability to toggle it on and off.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
// The code in setup above implements the Accessory Attribute Database, but performs no operations. HomeSpan itself must be
|
||||
// continuously polled to look for requests from Controllers, such as the Home App on your iPhone. The poll() method below is all that
|
||||
// is needed to perform this continuously in each iteration of loop()
|
||||
|
||||
homeSpan.poll(); // run HomeSpan!
|
||||
|
||||
} // end of loop()
|
||||
|
||||
// Congratulations! You've created your first HomeSpan sketch, ready to be uploaded to your ESP32 board and paired with HomeKit.
|
||||
//
|
||||
//
|
||||
@@ -0,0 +1,93 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 2: Two non-functioning on/off light bulbs //
|
||||
// constructed from basic HomeSpan components //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h" // Always start by including the HomeSpan library
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 2 expands on Example 1 by implementing two LightBulbs, each as their own Accessory
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LightBulb"); // initializes a HomeSpan device named "HomeSpan Lightbulb" with Category set to Lighting
|
||||
|
||||
// Here we create the first LightBulb Accessory just as in Example 1
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// Now we create a second Accessory, which is just a duplicate of the first Accessory
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// That's it - our device now has two Accessories, each displayed up as a separate Tile in the Home App!
|
||||
|
||||
// Note that for a device with multiple Accessories, the Home App generates a default name for each Accessory Tile from the Name
|
||||
// specified in homeSpan.begin(). In this case, the default name for the first Accessory Tile will be "HomeSpan Lightbulb",
|
||||
// just as it was in Example 1, and the default name for the second Accessory Tile will be "HomeSpan Lightbulb 2".
|
||||
|
||||
// You can of course change the name of each Accessory Tile from these defaults when prompted by the Home App during pairing. You
|
||||
// can also change the name of any Accessory Tile, even after pairing, directly from the Home App by opening the settings page
|
||||
// for any given Tile.
|
||||
|
||||
// In Example 7 we will demonstrate how the default names can be changed from within a HomeSpan sketch.
|
||||
|
||||
// IMPORTANT: You should NOT have to re-pair your device with HomeKit when moving from Example 1 to Example 2. HomeSpan will note
|
||||
// that the Attribute Database has been updated, and will broadcast a new configuration number when the program restarts. This should
|
||||
// cause all iOS and MacOS HomeKit Controllers to automatically update and reflect the new configuration above.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll(); // run HomeSpan!
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,107 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 3: A simple on/off ceiling fan with an //
|
||||
// on/off ceiling light //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h" // Always start by including the HomeSpan library
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 3 shows how adding multiple Services to a single Accessory allows us to create a multi-featured Accessory, such as a ceiling fan wih a ceiling light
|
||||
|
||||
Serial.begin(115200); // Start a serial connection - this is needed for you to type in your WiFi credentials
|
||||
|
||||
homeSpan.begin(Category::Fans,"HomeSpan Ceiling Fan"); // Initialize HomeSpan - note the Category has been set to "Fans"
|
||||
|
||||
// We begin by creating a Light Bulb Accessory just as in Examples 1 and 2
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// Now we add a Fan Service within this same Accessory
|
||||
|
||||
new Service::Fan(); // Create the Fan Service
|
||||
new Characteristic::Active(); // This Service requires the "Active" Characterstic to turn the fan on and off
|
||||
|
||||
// Similar to Example 2, we will also implement a LightBulb as a second Accessory
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), no arguments needed
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, with the required Identify Characteristic
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::LightBulb(); // Create the Light Bulb Service
|
||||
new Characteristic::On(); // This Service requires the "On" Characterstic to turn the light on and off
|
||||
|
||||
// If everything worked correctly you should now see two Tiles in the Home App:
|
||||
//
|
||||
// * a Tile named "HomeSpan Ceiling Fan" with an icon of a Fan. Clicking this Tile should open the
|
||||
// control page showing a Fan control on the left, and a Light control on the right
|
||||
//
|
||||
// * a Tile named "HomeSpan Ceiling Fan 2" with an icon of a LightBulb. Clicking this Tile should
|
||||
// toggle the Light On/Off
|
||||
|
||||
// The reason for including the second LightBulb Accessories in this example is to illustrate the impact of the device's Category
|
||||
// on various icons. Setting Category to Fan in homeSpan.begin() serves two purposes. First, it sets the icon for the device itself,
|
||||
// as shown by the Home App during initial pairing, to a Fan. Second, it helps the Home App to determine which icon to use for an
|
||||
// Accessory Tile when there is ambiguity. The second Accessory contains nothing but a LightBulb Service, so the Home App sensibly
|
||||
// uses a LightBulb icon for the Tile. But what icon should the Home App use for the first Accessory containing both a Fan Service
|
||||
// and a LightBulb Service? Either a Fan or LightBulb icon would make sense. Setting the Category of the device to Fan causes
|
||||
// the Home App to choose a Fan icon for the first Accessory.
|
||||
|
||||
// As a test of this, unpair the device; change the Category to Lighting (as in Example 2); re-load the sketch; and re-pair the device.
|
||||
// You should now see the icon for the "HomeSpan Ceiling Fan" Tile is a LightBulb, and the control screen for the Accessory should
|
||||
// show the Light control on the left and the Fan control on the right.
|
||||
|
||||
// IMPORTANT: HomeKit Controllers often cache a lot of information. If your Controller does not update to match the above configuration,
|
||||
// simply select the Accessory in your Controller and under settings, select "Remove Accessory", but BEFORE re-pairing the device, type
|
||||
// 'H' into the HomeSpan CLI. This forces HomeSpan to reboot and generate a new device ID so that it will look "brand new" to the Home App
|
||||
// when you re-pair.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll(); // run HomeSpan!
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,100 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 4: A variable-speed ceiling fan with //
|
||||
// dimmable ceiling light //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h" // Always start by including the HomeSpan library
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 4 expands on the first Accessory in Example 3 by adding Characteristics to set FAN SPEED, FAN DIRECTION, and LIGHT BRIGHTNESS.
|
||||
// For ease of reading, all prior comments have been removed and new comments added to show explicit changes from the previous example.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Fans,"HomeSpan Ceiling Fan");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(true); // NEW: Providing an argument sets its initial value. In this case it means the LightBulb will be turned on at start-up
|
||||
|
||||
// In addition to setting the initial value of a Characteristic, it is also possible to override the default min/max/step range specified by HAP.
|
||||
// We do this with the setRange() method:
|
||||
|
||||
// setRange(min, max, step), where
|
||||
//
|
||||
// min = minimum allowed value
|
||||
// max = maximum allowed value
|
||||
// step = step size (can be left blank, in which case the HAP default is retained)
|
||||
|
||||
// The setRange() method can be called on any numerical-based Characteristic that supports range overrides. The easiest way to apply to method is to call it right
|
||||
// after instantiating a new Characteristic. Don't forget to surround the "new" command in parentheses when chaining a method in this fashion.
|
||||
|
||||
// Here we create a Brightness Characteristic to set the brightness of the LightBulb with an initial value of 50% and an allowable range
|
||||
// from 20-100% in steps of 5%. See Notes 1 and 2 below for more details:
|
||||
|
||||
(new Characteristic::Brightness(50))->setRange(20,100,5);
|
||||
|
||||
new Service::Fan();
|
||||
new Characteristic::Active();
|
||||
new Characteristic::RotationDirection(); // NEW: This allows control of the Rotation Direction of the Fan
|
||||
(new Characteristic::RotationSpeed(50))->setRange(0,100,25); // NEW: This allows control of the Rotation Speed of the Fan, with an initial value of 50% and a range from 0-100 in steps of 25%
|
||||
|
||||
// NOTE 1: Setting the initial value of the Brightness Characteristic to 50% does not by itself cause HomeKit to turn the light on to 50% upon start-up.
|
||||
// Rather, this is governed by the initial value of the On Characteristic, which in this case happens to be set to true. If it were set to false,
|
||||
// or left unspecified (default is false) then the LightBulb will be off at start-up. However, it will jump to 50% brightness as soon as turned on
|
||||
// for the first time. This same logic applies to the Active and RotationSpeed Characteristics for a Fan.
|
||||
|
||||
// NOTE 2: The default range for Characteristics that support a range of values is specified in HAP Section 9. For Brightness, the range defaults
|
||||
// to min=0%, max=100%, step=1%. Using setRange() to change the minimum Brightness from 0% to 20% (or any non-zero value) provides for a better
|
||||
// HomeKit experience. This is because the LightBulb power is controlled by the On Characteristic, and allowing Brightness to be as low as 0%
|
||||
// sometimes results in HomeKit turning on the LightBulb but with Brightness=0%, which is not very intuitive. This can occur when asking Siri
|
||||
// to lower the Brightness all the way, and then turning on the LightBulb. By setting a minumum value of 20%, HomeKit always ensures that there is
|
||||
// some Brightness value whenever the LightBulb is turned on.
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll(); // run HomeSpan!
|
||||
|
||||
} // end of loop()
|
||||
111
ESP32/HomeSpan-master/examples/05-WorkingLED/05-WorkingLED.ino
Normal file
111
ESP32/HomeSpan-master/examples/05-WorkingLED/05-WorkingLED.ino
Normal file
@@ -0,0 +1,111 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 5: Two working on/off LEDs based on the //
|
||||
// LightBulb Service //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h" // NEW! Include this new file, DEV_LED.h, which will be fully explained below
|
||||
|
||||
void setup() {
|
||||
|
||||
// First light! Control an LED from HomeKit!
|
||||
|
||||
// Example 5 expands on Example 2 by adding in the code needed to actually control LEDs connected to the ESP32 from HomeKit.
|
||||
// In Example 2 we built out all the functionality to create a "Tile" Acessories inside HomeKit that displayed an on/off light, but
|
||||
// these control did not actually operate anything on the ESP32. To operate actual devices HomeSpan needs to be programmed to
|
||||
// respond to "update" requests from HomeKit by performing some form of operation.
|
||||
|
||||
// Though HomeKit itself sends "update" requests to individual Characteristics, this is not intuitive and leads to complex coding requirements
|
||||
// when a Service has more than one Characteristic, such as both "On" and "Brightness." To make this MUCH easier for the user, HomeSpan
|
||||
// uses a framework in which Services are updated instead of individual Characteristics. It does so by calling the update() method of
|
||||
// each Service with flags indicating all the Characteristics in that Service that HomeKit requested to update. The user simply
|
||||
// implements code to perform the actual operation, and returns either true or false if the update was successful. HomeSpan takes care of all
|
||||
// the underlying nuts and bolts.
|
||||
|
||||
// Every Service defined in HomeKit, such as Service:LightBulb and Service:Fan (and even Service::AccessoryInformation) implements an update()
|
||||
// method that, as a default, does nothing but returns a value of true. To actually operate real devices you need to over-ride this default update()
|
||||
// method with your own code. The easiest way to do this is by creating a DERIVED class based on one of the built-in HomeSpan Services.
|
||||
// Within this derived class you can perform initial set-up routines (if needed), over-ride the update() method with your own code, and even create
|
||||
// any other methods or class-specific variables you need to fully operate complex devices. Most importantly, the derived class can take arguments
|
||||
// so that you can make them more generic, re-use them multiple times (as will be seen below), and convert them to standalone modules (also shown below).
|
||||
|
||||
// All of the HomeKit Services implemented by HomeSpan can be found in the Services.h file. Any can be used as the parent for a derived Service.
|
||||
|
||||
// We begin by repeating nearly the same code from Example 2, but with a few key changes. For ease of reading, all prior comments have been removed
|
||||
// from lines that simply repeat Example 2, and new comments have been added to explictly show the new code.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// In Example 2 we instantiated a LightBulb Service and its "On" Characteristic here. We are now going to replace these two lines (by commenting them out)...
|
||||
|
||||
// new Service::LightBulb();
|
||||
// new Characteristic::On();
|
||||
|
||||
// ...with a single new line instantiating a new class we will call DEV_LED():
|
||||
|
||||
new DEV_LED(16); // this instantiates a new LED Service. Where is this defined? What happpened to Characteristic::On? Keep reading...
|
||||
|
||||
// The full definition and code for DEV_LED is implemented in a separate file called "DEV_LED.h" that is specified using the #include at the top of this program.
|
||||
// The prefix DEV_ is not required but it's a helpful convention when naming all your device-specific Services. Note that DEV_LED will include all the required
|
||||
// Characterictics of the Service, so you DO NOT have to separately instantiate Characteristic::On --- everything HomeSpan needs for DEV_LED should be implemented
|
||||
// in DEV_LED itself (though it's not all that much). Finally, note that we created DEV_LED to take a single integer argument. If you guessed this is
|
||||
// the number of the Pin to which you have attached an LED, you'd be right. See DEV_LED.h for a complete explanation of how it works.
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// new Service::LightBulb(); // Same as above, this line is deleted...
|
||||
// new Characteristic::On(); // This line is also deleted...
|
||||
|
||||
new DEV_LED(17); // ...and replaced with a single line that instantiates a second DEV_LED Service on Pin 17
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
100
ESP32/HomeSpan-master/examples/05-WorkingLED/DEV_LED.h
Normal file
100
ESP32/HomeSpan-master/examples/05-WorkingLED/DEV_LED.h
Normal file
@@ -0,0 +1,100 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
// HERE'S WHERE WE DEFINE OUR NEW LED SERVICE!
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // First we create a derived class from the HomeSpan LightBulb Service
|
||||
|
||||
int ledPin; // this variable stores the pin number defined for this LED
|
||||
SpanCharacteristic *power; // here we create a generic pointer to a SpanCharacteristic named "power" that we will use below
|
||||
|
||||
// Next we define the constructor for DEV_LED. Note that it takes one argument, ledPin,
|
||||
// which specifies the pin to which the LED is attached.
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On(); // this is where we create the On Characterstic we had previously defined in setup(). Save this in the pointer created above, for use below
|
||||
this->ledPin=ledPin; // don't forget to store ledPin...
|
||||
pinMode(ledPin,OUTPUT); // ...and set the mode for ledPin to be an OUTPUT (standard Arduino function)
|
||||
|
||||
} // end constructor
|
||||
|
||||
// Finally, we over-ride the default update() method with instructions that actually turn on/off the LED. Note update() returns type boolean
|
||||
|
||||
boolean update(){
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal()); // use a standard Arduino function to turn on/off ledPin based on the return of a call to power->getNewVal() (see below for more info)
|
||||
|
||||
return(true); // return true to indicate the update was successful (otherwise create code to return false if some reason you could not turn on the LED)
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// HOW update() WORKS:
|
||||
// ------------------
|
||||
//
|
||||
// Whenever a HomeKit controller requests HomeSpan to update a Characteristic, HomeSpan calls the update() method for the SERVICE that contains the
|
||||
// Characteristic. It calls this only one time, even if multiple Characteristics updates are requested for that Service. For example, if you
|
||||
// direct HomeKit to turn on a light and set it to 50% brightness, it will send HomeSpan two requests: one to update the "On" Characteristic of the
|
||||
// LightBulb Service from "false" to "true" and another to update the "Brightness" Characteristic of that same Service to 50. This is VERY inefficient
|
||||
// and would require the user to process multiple updates to the same Service.
|
||||
//
|
||||
// Instead, HomeSpan combines both requests into a single call to update() for the Service itself, where you can process all of the Characteristics
|
||||
// that change at the same time. In the example above, we only have a single Characteristic to deal with, so this does not mean much. But in later
|
||||
// examples we'll see how this works with multiple Characteristics.
|
||||
|
||||
// HOW TO ACCESS A CHARACTERISTIC'S NEW AND CURRENT VALUES
|
||||
// -------------------------------------------------------
|
||||
//
|
||||
// HomeSpan stores the values for its Characteristics in a union structure that allows for different types, such as floats, booleans, etc. The specific
|
||||
// types are defined by HAP for each Characteristic. Looking up whether a Characteristic is a uint8 or uint16 can be tiresome, so HomeSpan abstracts
|
||||
// all these details. Since C++ adheres to strict variable typing, this is done through the use of template methods. Every Characteristic supports
|
||||
// the following two methods:
|
||||
//
|
||||
// getVal<type>() - returns the CURRENT value of the Characterisic, after casting into "type"
|
||||
// getNewVal<type>() - returns the NEW value (i.e. to be updated) of the Characteritic, after casting into "type"
|
||||
//
|
||||
// For example, MyChar->getVal<int>() returns the current value of SpanCharacterstic MyChar as an int, REGARDLESS of how the value is stored by HomeSpan.
|
||||
// Similarly, MyChar->getVal<double>() returns a value as a double, even it is stored as as a boolean (in which case you'll either get 0.00 or 1.00).
|
||||
// Of course you need to make sure you understand the range of expected values so that you don't try to access a value stored as 2-byte int using getVal<uint8_t>().
|
||||
// But it's perfectly okay to use getVal<int>() to access the value of a Characteristic that HAP insists on storing as a float, even though its range is
|
||||
// strictly between 0 and 100 in steps of 1. Knowing the range and step size is all you need to know in determining you can access this as an <int> or even a <uint8_t>.
|
||||
//
|
||||
// Because most Characteristic values can properly be cast into int, getVal and getNewVal both default to <int> if the template parameter is not specified.
|
||||
// As you can see above, we retrieved the new value HomeKit requested for the On Characteristic that we named "power" by simply calling power->getNewVal().
|
||||
// Since no template parameter is specified, getNewVal() will return an int. And since the On Characteristic is natively stored as a boolean, getNewVal()
|
||||
// will either return a 0 or a 1, depending on whether HomeKit is requesting the Characteristic to be turned off or on.
|
||||
//
|
||||
// You may also note that in the above example we needed to use getNewVal(), but did not use getVal() anywhere. This is because we know exactly what
|
||||
// to do if HomeKit requests an LED to be turned on or off. The current status of the LED (on or off) does not matter. In latter examples we will see
|
||||
// instances where the current state of the device DOES matter, and we will need to access both current and new values.
|
||||
//
|
||||
// Finally, there is one additional method for Characteristics that is not used above but will be in later examples: updated(). This method returns a
|
||||
// boolean indicating whether HomeKit has requested a Characteristic to be updated, which means that getNewVal() will contain the new value it wants to set
|
||||
// for that Characteristic. For a Service with only one Characteristic, as above, we don't need to ask if "power" was updated using power->updated() because
|
||||
// the fact the the update() method for the Service is being called means that HomeKit is requesting an update, and the only thing to update is "power".
|
||||
// But for Services with two or more Characteristics, update() can be called with a request to update only a subset of the Characteristics. We will
|
||||
// find good use for the updated() method in later, multi-Characteristic examples.
|
||||
|
||||
// UNDER THE HOOD: WHAT THE RETURN CODE FOR UPDATE() DOES
|
||||
// ------------------------------------------------------
|
||||
//
|
||||
// HomeKit requires each Characteristic to return a special HAP status code when an attempt to update its value is made. HomeSpan automatically takes care of
|
||||
// most of the errors, such as a Characteristic not being found, or a request to update a Characteristic that is read only. In these cases update() is never
|
||||
// even called. But if it is, HomeSpan needs to return a HAP status code for each of the Characteristics that were to be updated in that Service.
|
||||
// By returning "true" you tell HomeSpan that the newValues requested are okay and you've made the required updates to the physical device. Upon
|
||||
// receiving a true return value, HomeSpan updates the Characteristics themselves by copying the "newValue" data elements into the current "value" data elements.
|
||||
// HomeSpan then sends a message back to HomeKit with a HAP code representing "OK," which lets the Controller know that the new values it requested have been
|
||||
// sucessfully processed. At no point does HomeKit ask for, or allow, a data value to be sent back from HomeSpan indicating the data in a Characteristic.
|
||||
// When requesting an update, HomeKit simply expects a HAP status code of OK, or some other status code representing an error. To tell HomeSpan to send the Controller
|
||||
// an error code, indicating that you were not able to successfully process the update, simply have update() return a value of "false." HomeSpan converts a
|
||||
// return of "false" to the HAP status code representing "UNABLE," which will cause the Controller to show that the device is not responding.
|
||||
|
||||
// There are very few reasons you should need to return "false" since so much checking is done in advance by either HomeSpan or HomeKit
|
||||
// itself. For instance, HomeKit does not allow you to use the Controller, or even Siri, to change the brightness of LightBulb to a value outside the
|
||||
// range of allowable values you specified. This means that any update() requests you receive should only contain newValue data elements that are in-range.
|
||||
//
|
||||
@@ -0,0 +1,85 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 6: One working on/off LED and one working //
|
||||
// dimmable LED, both based on the LightBulb //
|
||||
// Service //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 6 changes Example 5 so that LED #2 is now dimmable, instead of just on/off. This requires us to create a new
|
||||
// derived Service we will name "DEV_DimmableLED" Instead of creating a new file to store this definition, we will simply
|
||||
// tack it on to the end of the DEV_LED.h file that includes the code we created in Example 5 to control an on/off LED.
|
||||
// Grouping similar-style Services in one ".h" file makes it easier to organize your custom devices.
|
||||
|
||||
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
||||
|
||||
// NOTE: The Arduino/ESP32 code base does not include the function analogWrite() which is typically used to create a PWM
|
||||
// output to drive the brightness of an LED. Instead, the ESP32 code base itself includes a set of functions to create PWM output
|
||||
// and the ESP32 chip has built-in PWM functionality specifically for this purpose.
|
||||
|
||||
// HomeSpan wraps all of this PWM functionality into a single integrated class called LedPin, making it very easy to define
|
||||
// dimmable LED pins and set the PWM level (i.e. duty cycle) from 0-100%. Use of this LedPin class is shown in DEV_DimmableLED.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new DEV_LED(16); // create an on/off LED attached to pin 16 (same as in Example 5)
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
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()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
67
ESP32/HomeSpan-master/examples/06-DimmableLED/DEV_LED.h
Normal file
67
ESP32/HomeSpan-master/examples/06-DimmableLED/DEV_LED.h
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// Here's the new code defining DEV_DimmableLED - changes from above are noted in the comments
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable 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 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->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 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.
|
||||
|
||||
// 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
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,96 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 7: Changing an Accessory's default name //
|
||||
// to distinguish On/Off from Dimmable LEDs //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// As discusses in previous examples, the Home App automatically generates default names for each Accessory Tile
|
||||
// based on the Name provided in the second argument of homeSpan.begin(). And though you can change these names
|
||||
// both during, and anytime after, pairing, HAP also allows you to customize the default names themselves, so
|
||||
// something more intuitive is presented to the user when the device is first paired.
|
||||
|
||||
// Changing the default name for an Accessory is done by adding an optional Name Characteristic to the
|
||||
// Accessory Information Service. This causes the Home App to use the value of that Characteristic as the default name
|
||||
// for an Accessory Tile, instead of generating one from the name used in homeSpan.begin().
|
||||
|
||||
// Howevever, there is one caveat: The Name Characteristic has no affect when used in the first Accessory of a device.
|
||||
// Rather, the default name of the first Accessory Tile will always be shown by the Home App as the name specified in
|
||||
// homeSpan.begin() regardless of whether or not the Name Characteristic has been added to the Accessory Information Service.
|
||||
|
||||
// Below is a replay of Example 6 showing how the Name Characteristic can be used to change the default names of the second
|
||||
// and third, but not the first, Accessory Tile.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED"); // Note this results in the default name of "HomeSpan LED", "HomeSpan LED 2", etc. for each Accessory Tile
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED"); // This use of Name() will be ignored by the Home App. The default name for the Accessory will continue to be shown as "HomeSpan LED"
|
||||
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED"); // This DOES change the default name for the Accessory from "HomeSpan LED 2" to "Dimmable LED"
|
||||
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
new SpanAccessory();
|
||||
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(u8"Special chars ÄÖÜß"); // Use UTF-8 coded string for non-ASCII characters
|
||||
|
||||
new DEV_DimmableLED(18);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
67
ESP32/HomeSpan-master/examples/07-AccessoryNames/DEV_LED.h
Normal file
67
ESP32/HomeSpan-master/examples/07-AccessoryNames/DEV_LED.h
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// Here's the new code defining DEV_DimmableLED - changes from above are noted in the comments
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable 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 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->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 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.
|
||||
|
||||
// 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
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
94
ESP32/HomeSpan-master/examples/08-Bridges/08-Bridges.ino
Normal file
94
ESP32/HomeSpan-master/examples/08-Bridges/08-Bridges.ino
Normal file
@@ -0,0 +1,94 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 8: HomeKit Bridges and Bridge Accessories //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// If the only Service defined in the FIRST Accessory of a multi-Accessory device is the required Accessory Information Service,
|
||||
// the device is said to be configured as a "Bridge". Historically there may have been a number of functional differences between bridge
|
||||
// devices and non-bridge devices, but since iOS 15, it's not obvious there are any differences in functionality, with two exceptions:
|
||||
|
||||
// 1. Recall from Example 7 that the use of Characteristic::Name() to change the default name of an Accessory Tile
|
||||
// does not work for the first Accessory defined. The Home App always displays the default name of the first Accessory Tile
|
||||
// as the name of the device specified in homeSpan.begin(). However, this is not an issue when implementing a device
|
||||
// as a Bridge, since the first Accessory is nothing but the Bridge itself - having the default name match the name
|
||||
// of the device in this case makes much more sense. More importantly, you can now use Characteristic::Name() to change the
|
||||
// default name of BOTH the LED Accessory Tiles.
|
||||
|
||||
// 2. Devices configured as a Bridge appear in the Home App under the main settings page that displays all Hubs and Bridges.
|
||||
|
||||
// The sketch below is functionally identical to Example 7, except that instead of defining two Accessories (one for the Simple On/Off
|
||||
// LED and one for the Dimmable LED), we define three Accessories, where the first acts as the Bridge.
|
||||
|
||||
// As usual, all previous comments have been deleted and only new changes from the previous example are shown.
|
||||
|
||||
// NOTE: To see how this works in practice, you'll need to unpair your device and re-pair it once the new code is loaded.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// Below we replace Category::Lighting with Category::Bridges. This changes the icon of the device shown when pairing
|
||||
// with the Home App, but does NOT change the icons of the Accessory Tiles. You can choose any Category you like.
|
||||
// For instance, we could have continued to use Category::Lighting, even though we are configuring the device as a Bridge.
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory(); // This first Accessory is the new "Bridge" Accessory. It contains no functional Services, just the Accessory Information Service
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory(); // This second Accessory is the same as the first Accessory in Example 7, with the exception that Characteristic::Name() now does something
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED"); // Note that unlike in Example 7, this use of Name() is now utilized by the Home App since it is not the first Accessory (the Bridge above is the first)
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory(); // This third Accessory is the same as the second Accessory in Example 7
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
56
ESP32/HomeSpan-master/examples/08-Bridges/DEV_LED.h
Normal file
56
ESP32/HomeSpan-master/examples/08-Bridges/DEV_LED.h
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
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->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,107 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 9: Logging messages to the Serial Monitor //
|
||||
// //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// HomeSpan sends a variety of messages to the Serial Monitor of the Arduino IDE whenever the device is connected
|
||||
// to a computer. Message output can be performed either by the usual Serial.print() or Serial.printf() functions,
|
||||
// or by one of three user macros: LOG0(), LOG1() and LOG2(). These three macros output messages to the Serial Monitor
|
||||
// depending on HomeSpan's Log Level setting:
|
||||
|
||||
// at a setting of 0, only LOG0() message are output; LOG1() and LOG2() messages are ignored
|
||||
// at a setting of 1, both LOG0() and LOG1() messages are output; LOG2() messages are ignored
|
||||
// at a setting of 2, all LOG0(), LOG1(), and LOG2() messages are output
|
||||
|
||||
// Example 9 illustrates how to add such log messages. The code is identical to Example 8 (without comments), except
|
||||
// that LOG0() and LOG1() messages have been added to DEV_LED.h. The LOG0() messages will always be
|
||||
// output to the Arduino Serial Monitor. The LOG1() messages will only be output if the Log Level is set to 1 or 2.
|
||||
|
||||
// The setLogLevel() method of homeSpan can used to change the log level as follows:
|
||||
|
||||
// homeSpan.setLogLevel(0) - sets Log Level to 0
|
||||
// homeSpan.setLogLevel(1) - sets Log Level to 1
|
||||
// homeSpan.setLogLevel(2) - sets Log Level to 2
|
||||
|
||||
// The method should be called BEFORE homeSpan.begin() - see below for proper use. Note that the Log Level
|
||||
// can also be changed dynamically during runtime via the HomeSpan CLI by typing 'L0', 'L1', or 'L2' into the Serial Monitor
|
||||
|
||||
// There are two forms of the LOG0(), LOG1(), and LOG2() macros. The first form takes only a single argument and outputs
|
||||
// messsges using the Serial.print(var) function. This allows you to output any single variable or text message, but does not allow you
|
||||
// to control the format, or to output more than one variable at a time. The second form take multiple arguments, where the first
|
||||
// is a standard C++ formatting string, and any remaining arguments are consumed according to the format string. This form
|
||||
// utilizes the variadic Serial.printf(char *fmt [,var1, var2...]) function.
|
||||
|
||||
// RECOMMENDATION: Since a HomeSpan ESP32 is meant to be physically connected to real-world devices, you may find
|
||||
// yourself with numerous ESP32s each configured with a different set of Accessories. To aid in identification
|
||||
// you may want to add LOG0() statements containing some sort of initialization message to the constructors for
|
||||
// each derived Service, such as DEV_LED. Doing so allows HomeSpan to "report" on its configuration upon start-up. See
|
||||
// DEV_LED for examples.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setLogLevel(1); // NEW - Sets Log Level to 1, which causes LOG1() messages to be output
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED");
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
107
ESP32/HomeSpan-master/examples/09-MessageLogging/DEV_LED.h
Normal file
107
ESP32/HomeSpan-master/examples/09-MessageLogging/DEV_LED.h
Normal file
@@ -0,0 +1,107 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
// Here we output log messages when the constructor is initially called.
|
||||
// We use LOG0() to ensure the message is always output regardless of the
|
||||
// LOG Level setting. Note this uses the single-argument form of LOG(), so
|
||||
// multiple calls are needed to create a complete message
|
||||
|
||||
LOG0("Configuring On/Off LED: Pin="); // initialization message
|
||||
LOG0(ledPin);
|
||||
LOG0("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// Here we output log messages whenever update() is called, which is helpful
|
||||
// for debugging purposes if your physical device is not functioning as expected.
|
||||
// Since it's just for debugging, we use LOG1() instead of LOG0(). Note we can
|
||||
// output both the current as well as the new power settings. We've again
|
||||
// used the single-argument form of LOG() to create this message
|
||||
|
||||
LOG1("Updating On/Off LED on pin=");
|
||||
LOG1(ledPin);
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
LOG1("\n");
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
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%
|
||||
|
||||
// Here we once again output log messages when the constructor is initially called.
|
||||
// However, this time we use the multi-argument form of LOG() that resembles a
|
||||
// standard printf() function, which makes for more compact code.
|
||||
|
||||
LOG0("Configuring Dimmable LED: Pin=%d\n",pin); // initialization message
|
||||
|
||||
this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// Note that since Dimmable_LED has two updateable Characteristics,
|
||||
// HomeKit may be requesting either or both to be updated. We can
|
||||
// use the "isUpdated" flag of each Characteristic to output a message
|
||||
// only if HomeKit actually requested an update for that Characteristic.
|
||||
// Since update() is called whenever there is an update to at least
|
||||
// one of the Characteristics in a Service, either power, level, or both
|
||||
// will have its "isUpdated" flag set.
|
||||
|
||||
// As above, we use the multi-argument form of LOG() to create the messages
|
||||
// Note that for DimmableLED, ledPin has a method getPin() that retrieves the
|
||||
// pin number so you don't need to store it separately.
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=%dL Current Power=%s Current Brightness=%d",ledPin->getPin(),power->getVal()?"true":"false",level->getVal());
|
||||
|
||||
if(power->updated())
|
||||
LOG1(" New Power=%s",power->getNewVal()?"true":"false");
|
||||
|
||||
if(level->updated())
|
||||
LOG1(" New Brightness=%d",level->getNewVal());
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
83
ESP32/HomeSpan-master/examples/10-RGB_LED/10-RGB_LED.ino
Normal file
83
ESP32/HomeSpan-master/examples/10-RGB_LED/10-RGB_LED.ino
Normal file
@@ -0,0 +1,83 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 10: Controlling a full-color RGB LED //
|
||||
// //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 10 illustrates how to control an RGB LED to set any color and brightness.
|
||||
// The configuration below should look familiar by now. We've created a new derived Service,
|
||||
// called DEV_RgbLED to house all the required logic. You'll find all the code in DEV_LED.h.
|
||||
// For completeness, this configuration also contains an on/off LED and a dimmable LED as shown
|
||||
// in prior examples.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Simple LED");
|
||||
new DEV_LED(16); // Create an On/Off LED attached to pin 16
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Dimmable LED");
|
||||
new DEV_DimmableLED(17); // Create a Dimmable (PWM-driven) LED using attached to pin 17
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("RGB LED");
|
||||
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()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
190
ESP32/HomeSpan-master/examples/10-RGB_LED/DEV_LED.h
Normal file
190
ESP32/HomeSpan-master/examples/10-RGB_LED/DEV_LED.h
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb { // ON/OFF LED
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
Serial.print("Configuring On/Off LED: Pin="); // initialization message
|
||||
Serial.print(ledPin);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating On/Off LED on pin=");
|
||||
LOG1(ledPin);
|
||||
LOG1(": Current Power=");
|
||||
LOG1(power->getVal()?"true":"false");
|
||||
LOG1(" New Power=");
|
||||
LOG1(power->getNewVal()?"true":"false");
|
||||
LOG1("\n");
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
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->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->getPin());
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=");
|
||||
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");
|
||||
}
|
||||
|
||||
if(level->updated()){
|
||||
LOG1(" New Brightness=");
|
||||
LOG1(level->getNewVal());
|
||||
}
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
|
||||
|
||||
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 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->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)\n",redPin->getPin(),greenPin->getPin(),bluePin->getPin());
|
||||
Serial.print(cBuf);
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
boolean p;
|
||||
float v, h, s, r, g, b;
|
||||
|
||||
h=H->getVal<float>(); // get and store all current values. Note the use of the <float> template to properly read the values
|
||||
s=S->getVal<float>();
|
||||
v=V->getVal<float>(); // though H and S are defined as FLOAT in HAP, V (which is brightness) is defined as INT, but will be re-cast appropriately
|
||||
p=power->getVal();
|
||||
|
||||
char cBuf[128];
|
||||
sprintf(cBuf,"Updating RGB LED: Pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin());
|
||||
LOG1(cBuf);
|
||||
|
||||
if(power->updated()){
|
||||
p=power->getNewVal();
|
||||
sprintf(cBuf,"Power=%s->%s, ",power->getVal()?"true":"false",p?"true":"false");
|
||||
} else {
|
||||
sprintf(cBuf,"Power=%s, ",p?"true":"false");
|
||||
}
|
||||
LOG1(cBuf);
|
||||
|
||||
if(H->updated()){
|
||||
h=H->getNewVal<float>();
|
||||
sprintf(cBuf,"H=%.0f->%.0f, ",H->getVal<float>(),h);
|
||||
} else {
|
||||
sprintf(cBuf,"H=%.0f, ",h);
|
||||
}
|
||||
LOG1(cBuf);
|
||||
|
||||
if(S->updated()){
|
||||
s=S->getNewVal<float>();
|
||||
sprintf(cBuf,"S=%.0f->%.0f, ",S->getVal<float>(),s);
|
||||
} else {
|
||||
sprintf(cBuf,"S=%.0f, ",s);
|
||||
}
|
||||
LOG1(cBuf);
|
||||
|
||||
if(V->updated()){
|
||||
v=V->getNewVal<float>();
|
||||
sprintf(cBuf,"V=%.0f->%.0f ",V->getVal<float>(),v);
|
||||
} else {
|
||||
sprintf(cBuf,"V=%.0f ",v);
|
||||
}
|
||||
LOG1(cBuf);
|
||||
|
||||
// Here we call a static function of 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
|
||||
|
||||
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 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(R); // update each ledPin with new values
|
||||
greenPin->set(G);
|
||||
bluePin->set(B);
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,174 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 11: Service Names: //
|
||||
// * setting the names of individual Services //
|
||||
// * "changing" the icons in a bridge Accessory //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// INITIAL NOTE: Apple is constantly updating how the Home App Icons are chosen and how/if/where/when the Names for
|
||||
// Accessories and Services are displayed. This example has been tested and verified as of iOS 17.2.1.
|
||||
|
||||
void setup() {
|
||||
|
||||
// As described in previous examples, when pairing a device the Home App will choose default names for each
|
||||
// Accessory Tile, unless you override those default names with your own names by adding a Name Characteristic
|
||||
// to the Accessory Information Service for each Accessory (except the first, which is typically the Bridge Accessory).
|
||||
|
||||
// The same process holds true for the names of the Services in an Accessory with multiple Services: if a Service is not named,
|
||||
// the Home App will generate one. You can of course change the names of individual Services when prompted
|
||||
// during the pairing process, or at any time after pairing from within the appropriate settings pages in the Home App.
|
||||
|
||||
// But more importantly, you can name Services in your sketch so that those name show up when pairing, saving you the need to
|
||||
// rename them from the settings pages in the Home App.
|
||||
|
||||
// Whereas we previously used the *Name* Characteristic to provide names for Accessory Tiles, we use the *ConfiguredName* Characteristic
|
||||
// to provide names for individual Services within each Accessory.
|
||||
|
||||
// One important distinction between Name and ConfigureName is that Name is only used by the Home App during pairing. After that,
|
||||
// any changes you make to the name of an Accessory Tile from within the Home App are never communicated back to HomeSpan, and any changes
|
||||
// you might make to those names in your sketch will not be reflected in the Home App unless you unpair and re-pair the device. In contrast,
|
||||
// ConfiguredName works like any other Characteristic: changes made to ConfiguredName from within a sketch are proporgated to the Home App,
|
||||
// and any edits you make to a Service's name in the Home App trigger a corresponding call to update() in HomeSpan so HomeSpan and the Home App
|
||||
// are always in sync with regard to the names of any Services that includes the ConfiguredName Characteristic.
|
||||
|
||||
// NOTE: Service names (whether those generated by the Home App or specified via the ConfiguredName Characteristic) are only displayed on the
|
||||
// control screen of an Accessory Tile if there are two more more Services of the same type. But even if a Service name does not appear in the Home App,
|
||||
// it will still be used by Siri to control a specific Service within an Accessory by voice.
|
||||
|
||||
// In the example below we create 5 different functional Accessories, each illustrating how names, as well as icons, are chosen by the Home App
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// This device will be configured as a Bridge, with the Category set to Bridges
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
// Our initial Accessory is therefore the "Bridge" Accessory
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// Our first "functional" Accessory is a combination of a LightBulb, Outlet, and Switch. Note that when pairing, the Home App generates
|
||||
// default names of "Light", "Outlet", and "Switch" for these three Services, though these names are NOT displayed on the control screen
|
||||
// of the Accessory since there is only one type of each Service. Also note that the Home App selects a LightBulb icon for the Accessory Tile
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Light First"); // this sets the name of the Accessory Tile
|
||||
new Service::LightBulb(); // the icon of the Accessory Tile will be a Lightbulb, since this is the first functional Service
|
||||
new Characteristic::On();
|
||||
new Service::Outlet();
|
||||
new Characteristic::On();
|
||||
new Characteristic::OutletInUse();
|
||||
new Service::Switch();
|
||||
new Characteristic::On();
|
||||
|
||||
// Our second Accessory is similar to the first, but here we define the Switch Service first. Note that the Home App now selects
|
||||
// a Switch icon for the Accessory Tile
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Switch First"); // this sets the name of the Accessory Tile
|
||||
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||
new Characteristic::On();
|
||||
new Service::Outlet();
|
||||
new Characteristic::On();
|
||||
new Characteristic::OutletInUse();
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
|
||||
// Our third Accessory is similar to the second, but here we define 2 Switches, 2 LightBulbs, but still only 1 Outlet. This time, during pairing
|
||||
// the Home App generates default names of Switch, Switch 2, Light, Light 2, and Outlet. Importantly, note that on the control screen for
|
||||
// this Accessory, the Home App now displays the names of the Switches ("Switch" and "Switch 2") as well as the LightBulbs ("Light" and "Light 2")
|
||||
// under each corresponding control, but it does NOT display the name "Outlet" under the Outlet control since there is only one Outlet Service
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Two Switches"); // this sets the name of the Accessory Tile
|
||||
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||
new Characteristic::On();
|
||||
new Service::Switch();
|
||||
new Characteristic::On();
|
||||
new Service::Outlet();
|
||||
new Characteristic::On();
|
||||
new Characteristic::OutletInUse();
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
|
||||
// Our fourth and final Accessory is the same as the third, but this time we use the ConfiguredName Characteristic to define a name for each Service.
|
||||
// When pairing, you should see the Home App now uses the names below instead of generating default names as it did in the other examples. You
|
||||
// should also see these names displayed under each control on the control screen for the Accessory, with the exception of the Outlet Service.
|
||||
// Though we did provide a name for the Outlet, since there is only one Outlet Service in this Accessory, the Home App does not display its name.
|
||||
// Howevever, if from the settings screen for this Accessory you further navigate to the "Accessories" page, you will indeed see the names for each
|
||||
// Service exactly as specified below, including the Outlet name "Aux Power"
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Central Control"); // this sets the name of the Accessory Tile
|
||||
new Service::Switch(); // the icon of the Accessory Tile will be a Switch, since this is the first functional Service
|
||||
new Characteristic::On();
|
||||
new Characteristic::ConfiguredName("High Voltage"); // this sets the name of the first Switch Service
|
||||
new Service::Switch();
|
||||
new Characteristic::On();
|
||||
new Characteristic::ConfiguredName("Low Voltage"); // this sets the name of the second Switch Service
|
||||
new Service::Outlet();
|
||||
new Characteristic::On();
|
||||
new Characteristic::OutletInUse();
|
||||
new Characteristic::ConfiguredName("Aux Power"); // this sets the name of the Outlet Service
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
new Characteristic::ConfiguredName("Main Lights"); // this sets the name of the first LightBulb Service
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On();
|
||||
new Characteristic::ConfiguredName("Accent Lights"); // this sets the name of the second LightBulb Service
|
||||
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,118 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 12: Service Loops (and Event Notifications) //
|
||||
// * implementing a Temperature Sensor //
|
||||
// * implementing an Air Quality Sensor //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Sensors.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// So far we've seen that HomeSpan allows you to create derived Services with their own constructors and update() methods. For many applications, this
|
||||
// will be all that is needed. However, for many other types of applications you may need to take action or perform some background operations without
|
||||
// any prompting or requests from HomeKit.
|
||||
|
||||
// To perform background operations and actions, every Service implements a loop() method. The default loop() method is to do nothing, which has been
|
||||
// fine for all our prior examples. But if you need to perform some continuous background action, all you need to do is implement a loop() method for
|
||||
// your derived Service. At the end of each HomeSpan polling cycle, the loop() method is called for each Service that implements its own code.
|
||||
// In this fashion, the loop() method is similar to the main loop() method in the Arduino IDE itself - except it can be customized for each Service.
|
||||
|
||||
// In this Example 12 we explore the use of loop() methods to implement two new accessories - a Temperature Sensor and an Air Quality Sensor. Of course
|
||||
// we won't actually have these physical devices attached to the ESP32 for the purpose of this example, but we will simulate "reading" their properties.
|
||||
// This is one of the main purposes of implementing a loop() method. It allows you to read a sensor or perform some sort of repetitive, Service-specific
|
||||
// action.
|
||||
|
||||
// Once you read (or simulate reading) a sensor's values in a loop() method, you need to somehow communicate this back to HomeKit so the new values can be
|
||||
// reflected in the HomeKit Controller. This may be strictly for information purposes (such as a temperature sensor) or could be used by HomeKit itself
|
||||
// to trigger other devices (as might occur if implementing a Door Sensor).
|
||||
|
||||
// Fortunately, HomeSpan makes communicating the values of Characteristics back to HomeKit easy. In prior examples we saw how getVal() and getNewVal()
|
||||
// are used to read current and updated Characteristic values requested by HomeKit. To perform the reverse, we simply use a method called setVal().
|
||||
// Setting the value of a Characteristic with this function does two things. First, it causes HomeSpan to send an Event Notification message back to HomeKit
|
||||
// letting HomeKit know the new value of the Characteristic. Since messages create network traffic, HomeSpan keeps track of all setVal() changes across
|
||||
// all Services and creates one a single Event Notification message reporting all the changes togther at the end of each polling cycle.
|
||||
|
||||
// The second thing that HomeSpan does when you change the value of a Characteristic with setVal() is to reset an internal timer for that Characteristic that
|
||||
// keeps track of how long it's been since the last modification, whether from a previous setVal() instruction, or by HomeKit itself via a call to update().
|
||||
// You can query the time since the last modificaton using the method timeVal() which returns the elapsed time in milliseconds. By calling this function from
|
||||
// within loop() you can determine when it's time for a new sensor read, or when to perform some other action.
|
||||
|
||||
// NOTE: It it NOT recommended to continuously change Characteristic values using setVal() as this will generate a lot of network traffic since HomeSpan
|
||||
// sends Event Notifications bck to all registered HomeKit Controllers. It's fine to perform internal calculations, generate signals on different pins,
|
||||
// and perform any other internal actions you may need as frequently as you require. But limit the use of setVal() to a reasonable frequency, such as maybe
|
||||
// one per minute for a temperature sensor. Do not use setVal() unless the value of the Characteristic changes, but do use it to immediately inform HomeKit of
|
||||
// something time-sensitive, such as a door opening, or a smoke alarm triggering.
|
||||
|
||||
// As usual, all of the logic for this example are encapsulated in new standalone derived Services. You'll find fully-commented definitions for the DEV_TempSensor() and
|
||||
// the DEV_AirQualitySensor() Services instantiated below, in the DEV_Sensors.h file. As noted, this example is for instructional purposes only -- we do not actually
|
||||
// connect a Temperature Sensor or Air Quality Sensor to our ESP32 device. As such, we did not define the Services to take any arguments to specify pin numbers or any
|
||||
// other information needed to implement an actual sensor. Instead, in order to see how real a device would work, we simulate periodic changes by modifying Characteristic
|
||||
// values using setVal() with either a sequence of repeating values, or random values. See DEV_Sensors.h for complete details.
|
||||
|
||||
// Once you understand these examples, you should be able to use implement your own loop() method and utilize setVal() along with timeVal() for any combination of
|
||||
// HomeKit Services with Characteristics that require your device to send periodic update messages to HomeKit Controllers, ranging from Smoke Alarms to Door Sensors.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Temp Sensor");
|
||||
new DEV_TempSensor(); // Create a Temperature Sensor (see DEV_Sensors.h for definition)
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Air Quality");
|
||||
new DEV_AirQualitySensor(); // Create an Air Quality Sensor (see DEV_Sensors.h for definition)
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
117
ESP32/HomeSpan-master/examples/12-ServiceLoops/DEV_Sensors.h
Normal file
117
ESP32/HomeSpan-master/examples/12-ServiceLoops/DEV_Sensors.h
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_TempSensor : Service::TemperatureSensor { // A standalone Temperature sensor
|
||||
|
||||
SpanCharacteristic *temp; // reference to the Current Temperature Characteristic
|
||||
|
||||
DEV_TempSensor() : Service::TemperatureSensor(){ // constructor() method
|
||||
|
||||
// First we instantiate the main Characteristic for a Temperature Sensor, namely the Current Temperature, and set its initial value
|
||||
// to 20 degrees. For a real sensor, we would take a reading and initialize it to that value instead. NOTE: HomeKit uses
|
||||
// Celsius for all temperature settings. HomeKit will DISPLAY temperatures in the HomeKit app according to the settings on your iPhone.
|
||||
// Though the HAP documentation includes a Characteristic that appears to allow the device to over-ride this setting by specifying a display
|
||||
// of Celsius or Fahrenheit for each Service, it does not appear to work as advertised.
|
||||
|
||||
temp=new Characteristic::CurrentTemperature(-10.0); // instantiate the Current Temperature Characteristic
|
||||
temp->setRange(-50,100); // expand the range from the HAP default of 0-100 to -50 to 100 to allow for negative temperatures
|
||||
|
||||
Serial.print("Configuring Temperature Sensor"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
// Next we create the loop() method. This method take no arguments and returns no values. In order to simulate a temperature change
|
||||
// from an actual sensor we will read the current value of the temp Characteristic using the getVal() function, with <float> as the
|
||||
// template parameter; add 0.5 degrees Celsius; and then store the result in a float variable named "temperature." This will simulate
|
||||
// an increment of 0.5 degrees Celsius (a little less than 1 degree F). We will cap the temperature to 35.0 degrees C, after which
|
||||
// it resets to 10.0 and starts over. Most importantly, we will do this once every 5 seconds by checking the elapsed time since the
|
||||
// previous modification using timeVal().
|
||||
|
||||
// All of the action happens in the setVal() line where we set the value of the temp Characteristic to the new value of temperature.
|
||||
// This tells HomeKit to send an Event Notification message to all available Controllers making them aware of the new temperature.
|
||||
// Note that setVal() is NOT a template function and does not require you to specify <float> as a template parameter. This is because
|
||||
// setVal() can determine the type from the argument you specify. If there is any chance of ambiguity, you can always specifically
|
||||
// cast the argument such: setVal((float)temperature).
|
||||
|
||||
void loop(){
|
||||
|
||||
if(temp->timeVal()>5000){ // check time elapsed since last update and proceed only if greater than 5 seconds
|
||||
float temperature=temp->getVal<float>()+0.5; // "simulate" a half-degree temperature change...
|
||||
if(temperature>35.0) // ...but cap the maximum at 35C before starting over at -30C
|
||||
temperature=-30.0;
|
||||
|
||||
temp->setVal(temperature); // set the new temperature; this generates an Event Notification and also resets the elapsed time
|
||||
|
||||
LOG1("Temperature Update: ");
|
||||
LOG1(temperature*9/5+32);
|
||||
LOG1("\n");
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
struct DEV_AirQualitySensor : Service::AirQualitySensor { // A standalone Air Quality sensor
|
||||
|
||||
// An Air Quality Sensor is similar to a Temperature Sensor except that it supports a wide variety of measurements.
|
||||
// We will use three of them. The first is required, the second two are optional.
|
||||
|
||||
SpanCharacteristic *airQuality; // reference to the Air Quality Characteristic, which is an integer from 0 to 5
|
||||
SpanCharacteristic *o3Density; // reference to the Ozone Density Characteristic, which is a float from 0 to 1000
|
||||
SpanCharacteristic *no2Density; // reference to the Nitrogen Dioxide Characteristic, which is a float from 0 to 1000
|
||||
|
||||
DEV_AirQualitySensor() : Service::AirQualitySensor(){ // constructor() method
|
||||
|
||||
airQuality=new Characteristic::AirQuality(1); // instantiate the Air Quality Characteristic and set initial value to 1
|
||||
o3Density=new Characteristic::OzoneDensity(300.0); // instantiate the Ozone Density Characteristic and set initial value to 300.0
|
||||
no2Density=new Characteristic::NitrogenDioxideDensity(700.0); // instantiate the Nitrogen Dioxide Density Characteristic and set initial value to 700.0
|
||||
|
||||
Serial.print("Configuring Air Quality Sensor"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
// Note we are NOT updating the Nitrogen Dioxide Density Characteristic. This should therefore remain steady at its initial value of 700.0
|
||||
|
||||
if(airQuality->timeVal()>5000) // modify the Air Quality Characteristic every 5 seconds
|
||||
airQuality->setVal((airQuality->getVal()+1)%6); // simulate a change in Air Quality by incrementing the current value by one, and keeping in range 0-5
|
||||
|
||||
if(o3Density->timeVal()>10000) // modify the Ozone Density Characteristic value every 10 seconds
|
||||
o3Density->setVal((double)random(200,500)); // simulate a change with a random value between 200 and 499. Note use of (double) cast since random() returns an integer
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// What you should see in your HomeKit Application
|
||||
// -----------------------------------------------
|
||||
|
||||
// If you load the above example, your HomeKit App should display two new tiles: one labeled "Temp Sensor" and the other labeled "Air Quality".
|
||||
// The Temp Sensor tile should indicate a temperature in the range of 10C to 35C (50F to 95F), which automatically increments and updates 0.5C every 5 seconds.
|
||||
// The Air Quality tile should cycle through "quality" states once every 10 seconds. States are displayed in HomeKit as "Unknown", "Excellent", "Good", "Fair",
|
||||
// "Inferior" and "Poor".
|
||||
|
||||
// Note that HomeKit only displays the values of a subset of Characteristics within the tile itself. In the case of an Air Quality Sensor,
|
||||
// only the quality state of the Air Quality is displayed. To see the values of other Characteristics, such as Ozone Density and Nitrogen Dioxide Density, you need to click
|
||||
// on the tile, AND open the settings screen (it would be nicer if HomeKit displayed these values on the control screen instead of making you open the settings screen).
|
||||
// On the setting screen you should see the values of all three of the Characteristics we instantiated: Air Quality, Nitrogen Dioxide Density, and Ozone Density.
|
||||
// Both the Air Quality and Ozone Density should change every 10 seconds. The Nitrogen Dioxide Density should remain steady at the initial value of 700.0, since we
|
||||
// never use setVal() to update this Characteristic.
|
||||
|
||||
// If you run HomeSpan at a VERBOSITY level of 2 (as specified in the library's Settings.h file), you can see that under the hood HomeSpan is sending Event Notification
|
||||
// messages to all registered controllers every 5 seconds for the Temp Sensor, and every 5 and 10 seconds for the Air Quality Sensor. If you look carefully you'll see that
|
||||
// the Event Notification message for the Air Quality Sensor only include two values - one for the Air Quality state and one for the Ozone Density. HomeSpan is NOT
|
||||
// sending a value for the Nitrogen Dioxide Density Characteristic since it has not been changed with a setVal() function.
|
||||
|
||||
// FINAL NOTE: The number of decimals HomeKit displays for temperature in the HomeKit app is independent of the step size of the value itself. This seems to be
|
||||
// hardcoded by HomeKit: for Fahrenheit a Temperature Sensor tile shows no decimals and ROUNDS to the nearest whole degree (e.g. 72, 73, 74 degrees); for Celsius
|
||||
// the tile allows for half-degree resolution and ROUNDS accordingly (e.g. 22.7 is displayed as 22.5 and 22.8 is displayed as 23.0).
|
||||
@@ -0,0 +1,105 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 13: Target States and Current States //
|
||||
// * implementing a Garage Door Opener //
|
||||
// * implementing a motorized Window Shade //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// In Example 12 we saw how to implement the loop() method for a Service to continuously monitor our device and periodically report
|
||||
// changes in one or more Characteristics back to HomeKit using setVal() and timeVal(). In that example we implemented passive sensors
|
||||
// that operated independently and required no input from the user, which meant we did not need to implement any update() methods.
|
||||
|
||||
// In this Example 13 we demonstrate the simultaneous use of both the update() and loop() methods by implementing two new Services:
|
||||
// a Garage Door Opener and a motorized Window Shade. Both examples showcase HomeKit's Target-State/Current-State framework.
|
||||
// For physical devices that take time to operate (such as closing a door), HomeKit Services typically use:
|
||||
|
||||
// * one Characteristic that HomeKit sets via update() requests to HomeSpan, and that represent a desired target state,
|
||||
// such as opened, closed, or in some cases a percentage opened or closed; and
|
||||
|
||||
// * one read-only Characteristic that HomeSpan use to track the current state of the device in the loop() method, as well as
|
||||
// report back changes to HomeKit using setVal().
|
||||
|
||||
// Not all HomeKit Services utilize the same Characteristics to define target and current states. Some Services use Characteristics
|
||||
// that are specific to that one Service, whereas others use more generic Characteristics. The common theme seems to be that HomeKit
|
||||
// guesses the actions a device is taking, and updates it tile's icon accordingly, by comparing the value of the target state
|
||||
// Characteristic it sets, and the current state Characteristic it receives in the form of Event Notifications. When they are the same,
|
||||
// HomeKit assumes the physical device has reached the required position. When they differ, HomeKit assumes something will be opening,
|
||||
// closing, raising, lowering, etc. Sometimes a little experimenting and a lot of trial and error is required to fully understand how
|
||||
// each Service responds to different combinations of Characteristic values.
|
||||
|
||||
// As always, we won't be connecting our ESP32 to an actual garage door or window shade but will instead simulate their responses and
|
||||
// actions for illustrative purposes. In some ways the code is more complicated because of the need to simulate values - it might be
|
||||
// easier if we actually were connecting to a garage door or window shade!
|
||||
|
||||
// Fully commented code for both of our derived Services can be found in DEV_DoorsWindows.h. These examples do not introduce any new
|
||||
// HomeSpan functions, but you will see how to use HomeSpan's ENUMERATED CONSTANTS, instead of just plain integers, to set the values
|
||||
// of Characteristics where the values represent discrete states (e.g. "lowering", "opening").
|
||||
|
||||
// Please see HomeSpan's Services and Characteristics page for a complete list of the enumerated constants available for Characteristics
|
||||
// where they are applicable.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Garage Door");
|
||||
new DEV_GarageDoor(); // Create a Garage Door Opener (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
new DEV_WindowShade(); // Create a motorized Window Shade (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_GarageDoor : Service::GarageDoorOpener { // A Garage Door Opener
|
||||
|
||||
Characteristic::CurrentDoorState *current; // reference to the Current Door State Characteristic (specific to Garage Door Openers)
|
||||
Characteristic::TargetDoorState *target; // reference to the Target Door State Characteristic (specific to Garage Door Openers)
|
||||
SpanCharacteristic *obstruction; // reference to the Obstruction Detected Characteristic (specific to Garage Door Openers)
|
||||
|
||||
DEV_GarageDoor() : Service::GarageDoorOpener(){ // constructor() method
|
||||
|
||||
// Below we use enumerated constants rather than integers to set the values of the Characteristics.
|
||||
// Using enumerated constants means not having to remember the integer code for each state. You'll find
|
||||
// a complete list of all available enumerated constants on HomeSpan's Services and Characteristics page.
|
||||
// Note the use of enumerated constants is optional - you can always use the integer code representing
|
||||
// each state instead.
|
||||
|
||||
current=new Characteristic::CurrentDoorState(Characteristic::CurrentDoorState::CLOSED); // here we use the fully-qualified name of the constant "CLOSED"
|
||||
target=new Characteristic::TargetDoorState(target->CLOSED); // here we use the name of the object instead of the fully-qualified name (much less typing)
|
||||
|
||||
// Below we must use the fully-qualified name of the enumerated constant and cannot use "obstruction->NOT_DETECTED".
|
||||
// Why? Because above we declared "obstruction" to be a pointer to a generic SpanCharacteristic instead of a pointer to
|
||||
// the more specific Characteristic::ObstructionDetected. Either is fine, and it's just a matter of programming preference
|
||||
// (as you can see we use both conventions in this sketch). But the downside of using SpanCharacteristic to declare a
|
||||
// Characteristic that contains enumerated constants is that the object itself does not know about these constants. This is
|
||||
// because all enumerated constants are uniquely defined within their respective specific Characteristic classes, and not in the
|
||||
// generic SpanCharacteristic class from which all specific Characterstics are derived.
|
||||
|
||||
obstruction=new Characteristic::ObstructionDetected(Characteristic::ObstructionDetected::NOT_DETECTED); // this works
|
||||
// obstruction=new Characteristic::ObstructionDetected(obstruction->NOT_DETECTED); // this would produce a compiler error (try it and see)
|
||||
|
||||
Serial.print("Configuring Garage Door Opener"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// see HAP Documentation for details on what each value represents
|
||||
|
||||
if(target->getNewVal()==target->OPEN){ // HomeKit is requesting the door to be in OPEN position
|
||||
LOG1("Opening Garage Door\n");
|
||||
current->setVal(current->OPENING); // set the current-state value to OPENING
|
||||
obstruction->setVal(false); // clear any prior obstruction detection - note we do not bother using an enumerated constant here
|
||||
} else {
|
||||
LOG1("Closing Garage Door\n"); // else HomeKit must be requesting the door to be in the CLOSED position
|
||||
current->setVal(current->CLOSING); // set the current-state value to CLOSING
|
||||
obstruction->setVal(false); // clear any prior obstruction detection
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
void loop(){ // loop() method
|
||||
|
||||
if(current->getVal()==target->getVal()) // if current-state matches target-state there is nothing do -- exit loop()
|
||||
return;
|
||||
|
||||
if(current->getVal()==current->CLOSING && random(100000)==0){ // here we simulate a random obstruction, but only if the door is closing (not opening)
|
||||
current->setVal(current->STOPPED); // if our simulated obstruction is triggered, set the curent-state to STOPPED
|
||||
obstruction->setVal(true); // and set obstruction-detected to true
|
||||
LOG1("Garage Door Obstruction Detected!\n");
|
||||
}
|
||||
|
||||
if(current->getVal()==current->STOPPED) // if the current-state is stopped, there is nothing more to do - exit loop()
|
||||
return;
|
||||
|
||||
// This last bit of code only gets called if the door is in a state that represents actively opening or actively closing.
|
||||
// If there is an obstruction, the door is "stopped" and won't start again until the HomeKit Controller requests a new open or close action
|
||||
|
||||
if(target->timeVal()>5000) // simulate a garage door that takes 5 seconds to operate by monitoring time since target-state was last modified
|
||||
current->setVal(target->getVal()); // set the current-state to the target-state
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
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)
|
||||
|
||||
DEV_WindowShade() : 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
|
||||
|
||||
Serial.print("Configuring Motorized Window Shade"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 posiiton
|
||||
|
||||
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
|
||||
// current position. Rather, it simply compares the value of the current position to the value of target positon as set by the
|
||||
// the user in the Home App. If it finds current and target positions are the same, it knows the shade is stopped. Otherwise
|
||||
// it will report the shade is raising or lowering depending on whether the specified target state is greater or less than
|
||||
// the current state.
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 14: Emulated PushButtons //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_Blinker.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Though HomeKit and the HomeKit Accessory Protocol (HAP) Specification provide a very flexible framework
|
||||
// for creating iOS- and MacOS-controlled devices, they do not contain every possible desired feature.
|
||||
//
|
||||
// One very common Characteristic HomeKit does not seem to contain is a simple pushbutton, like the type you
|
||||
// would find on a remote control. Unlike switches that can be "on" or "off", a pushbutton has no state.
|
||||
// Rather, a pushbutton performs some action when it's pushed, and that's all it does until it's pushed
|
||||
// again.
|
||||
//
|
||||
// Though HomeKit does not contain such a Characteristic, it's easy to emulate in HomeSpan. To do so, simply
|
||||
// define a Service with a boolen Characteristic (such as the On Characteristic), and create an update()
|
||||
// method to peform the operations to be executed when the "pushbutton" is "pressed" (i.e. set to true).
|
||||
//
|
||||
// You could stop there and have something in HomeKit that acts like a pushbutton, but it won't look like a
|
||||
// pushbutton because every time you press the tile for your device in HomeKit, the Controller will toggle
|
||||
// between showing it's on and showing it's off. Pressing a tile that shows the status is already ON, and will
|
||||
// change to OFF, when you actually want to re-trigger some sort of "on" action is not very satisfying.
|
||||
//
|
||||
// Ideally, we'd like HomeKit to acknowledge you've pressed the tile for the device by lighting up, sending
|
||||
// a request to update(), AND THEN resetting itself automatically to the "off" position a second or two later.
|
||||
// This would indeed emulate a light-up pushbutton.
|
||||
//
|
||||
// Fortunately, it is easy to emulate this in HomeSpan through the use of a Service's loop() function. Simply
|
||||
// code a derived Service as you normally would with its own update() method, and implement a loop() method
|
||||
// that "resets" one or more Characteristics after a set period of time. This is similar to what we did in the
|
||||
// with loop() methods in the prior two examples, except a lot simpler since the only logic is to set the value
|
||||
// of a Characteristic to "off" after a few seconds using timeVal() and setVal().
|
||||
//
|
||||
// Example 14 demonstrates this by implementing a "pushbutton" Service to blink an LED three times. By itself, this
|
||||
// is not very useful. But it is a good model for showing how to implement an IR LED that sends a Volume-Up command to
|
||||
// a TV; or an RF Transmitter to control to some remote device, like a ceiling fan.
|
||||
//
|
||||
// All the functionality is wrapped up in a newly-defined "DEV_Blinker" Service, which can be found in DEV_Blinker.h.
|
||||
// This new Service is a copy of the DEV_LED service from Example 9, with modifications to make it into a generic
|
||||
// blinking LED. As usual, changes and new lines between this Example 14, and original Example 9, are notably commented.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
// Defines the Bridge Accessory
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
// *** NEW *** defines an LED Blinker Accessory attached to pin 16 which blinks 3 times
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED Blinker");
|
||||
new DEV_Blinker(16,3); // DEV_Blinker takes two arguments - pin, and number of times to blink
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,89 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
// NOTE: This example is constructed only for the purpose of demonstrating how to
|
||||
// emulate a pushbutton in HomeSpan. The length of the blinking routine is MUCH longer
|
||||
// than HomeSpan should spend on an update(). To see how this effects HomeKit, try changing
|
||||
// the number of blinks to 50, or keep it at 3 and increase the delay times in update() so
|
||||
// that the blink routine takes 10 seconds or more. When activated, HomeKit will think the
|
||||
// device has become non-responsive if it does not receive a return message from update() within
|
||||
// a certain period of time.
|
||||
|
||||
// In practice, pushbuton emulation is used for very short routines, such as driving
|
||||
// an IR LED or an RF transmitter to send a code to a remote device.
|
||||
|
||||
// New and changed lines in comparison with Example 9 are noted as "NEW!"
|
||||
|
||||
struct DEV_Blinker : Service::LightBulb { // LED Blinker
|
||||
|
||||
int ledPin; // pin number defined for this LED
|
||||
int nBlinks; // NEW! number of times to blink
|
||||
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
|
||||
DEV_Blinker(int ledPin, int nBlinks) : Service::LightBulb(){ // constructor() method
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
this->ledPin=ledPin;
|
||||
this->nBlinks=nBlinks; // NEW! number of blinks
|
||||
pinMode(ledPin,OUTPUT);
|
||||
|
||||
Serial.print("Configuring LED Blinker: Pin="); // initialization message
|
||||
Serial.print(ledPin);
|
||||
Serial.print(" Blinks="); // NEW! add output message for number of blinks
|
||||
Serial.print(nBlinks);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
// NEW! Instead of turning on or off the LED according to newValue, we blink it for
|
||||
// the number of times specified, and leave it in the off position when finished.
|
||||
// This line is deleted...
|
||||
|
||||
// digitalWrite(ledPin,power->getNewVal());
|
||||
|
||||
// and is replaced by...
|
||||
|
||||
if(power->getNewVal()){ // check to ensure HomeKit is requesting we "turn on" this device (else ignore)
|
||||
|
||||
LOG1("Activating the LED Blinker on pin=");
|
||||
LOG1(ledPin);
|
||||
LOG1("\n");
|
||||
|
||||
for(int i=0;i<nBlinks;i++){ // loop over number of blinks specified
|
||||
digitalWrite(ledPin,HIGH); // turn pin on
|
||||
delay(100); // wait 100 ms
|
||||
digitalWrite(ledPin,LOW); // turn pin off
|
||||
delay(250); // wait 250 ms
|
||||
}
|
||||
|
||||
} // if newVal=true
|
||||
|
||||
// Note that the delays above of 100ms and 250ms are for illustrative purposes only
|
||||
// (and so you can see the LED blink). In practice, if you were controlling an IR LED
|
||||
// or an RF transmitter, the whole signal would likely transmit in 10ms total.
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
// NEW! Here we implement a very simple loop() method that checks to see if the power Characteristic
|
||||
// is "on" for at least 3 seconds. If so, it resets the value to "off" (false).
|
||||
|
||||
void loop(){
|
||||
|
||||
if(power->getVal() && power->timeVal()>3000){ // check that power is true, and that time since last modification is greater than 3 seconds
|
||||
LOG1("Resetting Blinking LED Control\n"); // log message
|
||||
power->setVal(false); // set power to false
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,267 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 15: Real PushButtons //
|
||||
// * manually controlling a Dimmable LED //
|
||||
// //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// In Example 14 we saw how to emulate a PushButton tile within HomeKit by automatically resetting a Characteristic so that
|
||||
// it "turns off" after a short period of time. However, sometimes we want to be able to physically control a device with actual
|
||||
// PushButtons (or momentary switches) that trigger an action, such as turning on a light or fan, or opening a garage door.
|
||||
// Additionally, we want HomeKit to reflect any changes in the device as a result of such manual actions - HomeKit should know
|
||||
// when the light has been turned on or off manually.
|
||||
|
||||
// One way to accomplish would be via custom code added to the loop() method of your derived Service that monitors a pushbutton,
|
||||
// checks when it is pressed, debounces button noise, performs some actions when pressed, and informs HomeKit of the actions with
|
||||
// the setVal() method. Or you can simply use HomeSpan's built-in SpanButton() object.
|
||||
|
||||
// SpanButton() is a Service-level object, meaning it attaches itself to the last Service you define. Typically you would instantiate
|
||||
// one of more SpanButton() objects directly inside the constructor for your derived Service.
|
||||
|
||||
// SpanButton() supports three types of a triggers: a SINGLE button press, a DOUBLE press, and a LONG (extended) press.
|
||||
|
||||
// The length of the presses needed to trigger these different types can be specified by optional arguments to SpanButton().
|
||||
// Since most buttons create spurious noise when pressed (and then again when released), the default time to trigger a SINGLE press is 5ms.
|
||||
// It's fine to change this to a longer value, but a shorter value is not recommended as this may allow spurious triggers unless
|
||||
// you debounce your switch with hardware.
|
||||
|
||||
// The SpanButton() constructor takes 5 arguments, in the following order:
|
||||
//
|
||||
// pin - the pin number to which the PushButton is attached (required)
|
||||
// longTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a LONG press (optional; default=2000 ms)
|
||||
// singleTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a SINGLE press (optional; default=5 ms)
|
||||
// doubleTime - the maximum length of time (in milliseconds) between button presses to create a DOUBLE press (optional; default=200 ms)
|
||||
// triggerType - the action that causes a trigger on the pin (optional; default=SpanButton::TRIGGER_ON_LOW). Built-in choices include:
|
||||
//
|
||||
// SpanButton::TRIGGER_ON_LOW: used for a button that connects pin to GROUND
|
||||
// SpanButton::TRIGGER_ON_HIGH: used for a button that connects pin to VCC (typically +3.3V)
|
||||
// SpanButton::TRIGGER_ON_TOUCH: used when a pin is connected to a touch pad/sensor
|
||||
|
||||
// When a SpanButton() is first instantiated, HomeSpan configures the specified pin in accordance with the triggerType chosen.
|
||||
|
||||
// Then, HomeSpan continuously polls all pins with associated SpanButton() objects and checks for triggers, which indicates the button was
|
||||
// pressed, but not yet released. It then starts a timer. If the button is released after being pressed for less than singleTime milliseconds,
|
||||
// nothing happens. If the button is released after being pressed for more than singleTime milliseconds, but for less than longTime milliseconds,
|
||||
// a SINGLE press is triggered, unless you press once again within doubleTime milliseconds to trigger a DOUBLE press. If the button is held for more
|
||||
// than longTime milliseconds without being released, a LONG press is triggered. Once a LONG press is triggered the timer resets so that if you keep
|
||||
// holding the button, another LONG press will be triggered in another longTime milliseconds. This continues until you finally release the button.
|
||||
|
||||
// Note if you set longTime > singleTime, SpanButton() will only trigger LONG presses. Also, if you set doubleTime to zero, SpanButton() will not be
|
||||
// able to trigger a DOUBLE press.
|
||||
|
||||
// To use SpanButton() within a derived Service you need to implement a button() method. Similar to the loop() method, your button()
|
||||
// method will typically contain some combination of getVal() functions and setVal() functions, along with code that performs some set
|
||||
// of actions on the physical device (seting pins high or low, turning on fans, etc). However, in contrast to the loop() method, which
|
||||
// is called by HomeSpan every polling cycle, HomeSpan only calls the button() method when a button attached to the Service registers a
|
||||
// SINGLE, DOUBLE, or LONG press.
|
||||
|
||||
// Also in contrast with the loop method, the button() method takes two 'int' arguments, and should defined as follows:
|
||||
//
|
||||
// void button(int pin, int pressType)
|
||||
//
|
||||
// where "pin" is the pin number of the PushButton that was triggered, and pressType is set to 0 for a SINGLE press, 1 for a DOUBLE press,
|
||||
// and 2 for a LONG press. You can also use the pre-defined constants SpanButton::SINGLE, SpanButton::DOUBLE, and SpanButton::LONG in place
|
||||
// of the numbers 0, 1, and 2 (this is recommended, though you will see in Example 16 why these integers can't be replaced by an C++ enum class).
|
||||
|
||||
// Of course you can replace the variables "pin" and "pressType" with your own names. The only requirement is the definition conform to
|
||||
// the "void button(int, int)" signature. When HomeSpan first starts up it checks all Services containing one or more SpanButton() instances to
|
||||
// ensure you've implemented your own button(int, int) method. If not, HomeSpan will print a warning message on the Serial Monitor. Nothing bad
|
||||
// happens if you instantiate a SpanButton() but forget to create the button() method, or you create it with the wrong parameters. But nothing good
|
||||
// happens either - button presses are just ignored.
|
||||
//
|
||||
// C++ Note: For an extra check, you can also place the the contextual keyword "override" after your method definition as such:
|
||||
//
|
||||
// void button(int buttonPin, int pressType) override {...your code...}
|
||||
//
|
||||
// Doing so allows the compiler to check that you are indeed over-riding the base class button() method and not inadvertently creating a new
|
||||
// button() method with an incorrect signature that will never be called by SpanButton(). In fact, you could add "override" to the definition
|
||||
// of your update() and loop() methods as well, since these are always supposed to over-ride the base-class method.
|
||||
|
||||
// To demonstrate how SpanButtons works in practice, we will implement a Dimmable LED starting with the same LED code use in Example 11,
|
||||
// but with 3 SpanButton() objects performing different functions that showcase the different types of presses.
|
||||
//
|
||||
// * A "power" SpanButton that will toggle the power in response a SINGLE press, turn on the power and set the brightness to a "favorite" level
|
||||
// in response to the DOUBLE press, and set a new "favorite" level in response to a LONG press.
|
||||
//
|
||||
// * A "raise brightness" SpanButton that will increase the brightness by 1% in response to a SINGLE press, repeatedly increase the brightness
|
||||
// by 10% in response to a LONG press, and jump to the maximum brightness in response to a DOUBLE press.
|
||||
//
|
||||
// * A "lower brightness" SpanButton that will decrease the brightness by 1% in response to a SINGLE press, repeatedly decrease the brightness
|
||||
// by 10% in response to a LONG press, and jump to the minimum brightness in response to a DOUBLE press.
|
||||
|
||||
// As usual, all the code is implemented in DEV_LED.h, with NEW! comments highlighting changes from Example 11. You'll also notice that we've
|
||||
// extended the constructor for this version of our derived Dimmable LED Service to include the pin numbers for each of our buttons.
|
||||
// See DEV_LED.h for details.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("PushButton LED");
|
||||
|
||||
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()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////// ADDITIONAL NOTES ////////////////////////
|
||||
|
||||
// DEFAULT VALUES AND ALTERNATIVE CONSTRUCTORS
|
||||
// --------------------------------------------
|
||||
|
||||
// As shown in this example, the following creates a SpanButton suitable for connecting pin 23 to GROUND via a pushbutton, and uses
|
||||
// SpanButton's default values for longTime, singleTime, and doubleTime:
|
||||
//
|
||||
// new SpanButton(23);
|
||||
//
|
||||
// This is exactly the same as if you explicitly set each parameter to its default value:
|
||||
//
|
||||
// new SpanButton(23,2000,5,200,SpanButton::TRIGGER_ON_LOW); // equivalent to above
|
||||
//
|
||||
// If instead you want to create a SpanButton that connects pin 23 to VCC via a pushbutton using SpanButton::TRIGGER_ON-HIGH,
|
||||
// you need to explictly set all the other parameters, even if you are satisfied with their default values, since triggerType
|
||||
// is the last argument in the constructor:
|
||||
//
|
||||
// new SpanButton(23,2000,5,200,SpanButton::TRIGGER_ON_HIGH);
|
||||
//
|
||||
// Because this can be cumbersome, SpanButton includes an alternative constructor where triggerType is the second paramater, instead
|
||||
// of the last. In this case triggerType is required, but longTime, singleTime, and doubleTime are still optional.
|
||||
//
|
||||
// For example, the following creates a SpanButton suitable for connecting pin 23 to a touch pad/sensor, and uses
|
||||
// SpanButton's default values for longTime, singleTime, and doubleTime:
|
||||
//
|
||||
// new SpanButton(23,SpanButton::TRIGGER_ON_TOUCH);
|
||||
//
|
||||
// which is of course equivalent to:
|
||||
//
|
||||
// new SpanButton(23,SpanButton::TRIGGER_ON_TOUCH,2000,5,200);
|
||||
|
||||
|
||||
// TOUCH PAD/SENSOR CALIBRATION
|
||||
// ----------------------------
|
||||
|
||||
// SpanButton makes use of the ESP32's internal touch sensor peripheral to monitor pins for "touches". There are a number
|
||||
// of paramaters that must be specified for touches to be accurately detected, depending on the exact size and shape of your
|
||||
// touch pads. Upon instantiation of a SpanButton() with triggerType=SpanButton::TRIGGER_ON_TOUCH, SpanButton will conveniently
|
||||
// perform an automatic calibration that sets an appropriate threshold level for detecting touches.
|
||||
//
|
||||
// However, if you need to, you can override this calibration process using the following two class-level functions:
|
||||
//
|
||||
// SpanButton::setTouchThreshold() - explicitly sets the threshold for detecting touches (i.e. overrides the auto-calibration)
|
||||
// SpanButton::setTouchCycles() - explicitly sets the measurement and sleep times used by the ESP32's internal touch peripheral
|
||||
//
|
||||
// See the SpanButton secion of the Reference API for details on how to use these optional functions.
|
||||
|
||||
|
||||
// THE triggerType FUNCTION
|
||||
// -------------------------
|
||||
|
||||
// Though the three triggerType objects supported by SpanButton (SpanButton::TRIGGER_ON_LOW, etc.) may appear to be nothing more than
|
||||
// constants, they are actually boolean functions that each accept a single integer argument. When SpanButton calls the triggerType function,
|
||||
// it passes the pin number specified in the constructor as the integer argument, and the triggerType function returns TRUE if the
|
||||
// "pushbutton" associated with the pin number is "pressed," or FALSE if it is not.
|
||||
//
|
||||
// For example, the definitions of SpanButton::TRIGGER_ON_LOW and SpanButton::TRIGGER_ON_HIGH are as follows:
|
||||
//
|
||||
// boolean TRIGGER_ON_LOW(int pinArg) { return( !digitalRead(pinArg) ); }
|
||||
// boolean TRIGGER_ON_HIGH(int pinArg) { return( digitalRead(pinArg) ); }
|
||||
//
|
||||
// The definitions for SpanButton::TRIGGER_ON_TOUCH are more complicated since the ESP32 touch sensor library returns either a 2-byte
|
||||
// or 4-byte numeric value when the state of pin configured as a touch sensor is read, rather than a simple 0 or 1. The triggerType
|
||||
// function must therefore compare the value read from the touch sensor pin to some pre-computed "threshold" to determine whether or not
|
||||
// the touch pad has in fact been touched. This is the threshold value that HomeSpan auto-calibrates for you as described above.
|
||||
//
|
||||
// Making things even more complex is that the ESP32 touch pins work in the reverse direction as touch pins on the ESP32-S2 and ESP32-S3.
|
||||
// On the former, the values read from a touch sensor DECREASE when the touch pad is touched. On the latter, the values increase when the
|
||||
// touch pad is touched. This means that for ESP32 devices, HomeSpan uses the following definition for SpanButton::TRIGGER_ON_TOUCH:
|
||||
//
|
||||
// boolean TRIGGER_ON_TOUCH(int pinArg) { return ( touchRead(pinArg) < threshold ); }
|
||||
//
|
||||
// whereas on ESP32-S2 and ESP32-S3 devices, HomeSpan uses a definition that flips the direction of the comparison:
|
||||
//
|
||||
// boolean TRIGGER_ON_TOUCH(int pinArg) { return ( touchRead(pinArg) > threshold ); }
|
||||
//
|
||||
// For ESP32-C3 devices, HomeSpan does not define TRIGGER_ON_TOUCH at all since there are no touch pins on an ESP32-C3 device! The compiler
|
||||
// will throw an error if you try to create a SpanButton with triggerType=SpanButton::TRIGGER_ON_TOUCH, or if you call either of the
|
||||
// calibration functions above.
|
||||
//
|
||||
|
||||
// CREATING YOUR OWN triggerType FUNCTION
|
||||
// --------------------------------------
|
||||
|
||||
// You are not limited to choosing among HomeSpan's three built-in triggerType functions. You can instead create your own triggerType function
|
||||
// and pass it to SpanButton as the triggerType parameter in the SpanButton constructor. Your function must be of the form `boolean func(int)`,
|
||||
// and should return TRUE if the "pushbutton" associated with the pin number that HomeSpan passes to your function as the integer argument
|
||||
// has been "pressed", or FALSE if it has not. This allows you to expand the used of SpanButton to work with pin multiplexers, pin extenders,
|
||||
// or any device that may require custom handling via a third-party library.
|
||||
//
|
||||
// For example, if you were using an MCP I/O Port Expander with the Adafruit mcp library, you could create a triggerType function for a pin
|
||||
// on the MCP device that is connected to ground through a pushbutton as such:
|
||||
//
|
||||
// boolean MCP_READ(int mcpPin) { return ( !mcp.digitalRead(mcpPin) ); }
|
||||
//
|
||||
// And then simply pass MCP_READ to SpanButton as the triggerType parameter using any of the SpanButton constuctors:
|
||||
//
|
||||
// new SpanButton(23,MCP_READ); // uses default longTime, singleTime, and doubleTime
|
||||
// new SpanButton(23,MCP_READ,2000,5,200); // expliclty sets longTime, singleTime, and doubletime
|
||||
// new SpanButton(23,2000,5,200,MCP_READ); // alternative constructor with arguments in a different order
|
||||
//
|
||||
// Alternatively, you can use a lambda function as the triggerType parameter, thus creating your function on the fly when instantiating a SpanButton:
|
||||
//
|
||||
// new SpanButton(23,[](int mcpPin)->boolean{ return ( !mcp.digitalRead(mcpPin) ); });
|
||||
//
|
||||
// Note: If you create your own triggerType function, don't forget to perform any initialization of the "pin", or setup/configuration of a
|
||||
// pin extender, etc., prior to instantiating a SpanButton that uses your custom function. HomeSpan cannot do this for you.
|
||||
//
|
||||
|
||||
|
||||
164
ESP32/HomeSpan-master/examples/15-RealPushButtons/DEV_LED.h
Normal file
164
ESP32/HomeSpan-master/examples/15-RealPushButtons/DEV_LED.h
Normal file
@@ -0,0 +1,164 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||
|
||||
// This version of the Dimmable LED Service is similar to the one last used in Example 11, but now includes support for 3 physical PushButtons
|
||||
// performing the following actions:
|
||||
//
|
||||
// power button: SHORT press toggles power on/off; LONG press saves current brightness as favorite level; DOUBLE press sets brightness to favorite level
|
||||
// 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
|
||||
|
||||
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
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
int favoriteLevel=50; // NEW! keep track of a 'favorite' level
|
||||
|
||||
// NEW! Consructor includes 3 additional arguments to specify pin numbers for power, raise, and lower buttons
|
||||
|
||||
DEV_DimmableLED(int pin, int powerPin, int raisePin, int lowerPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
|
||||
level=new Characteristic::Brightness(favoriteLevel); // Brightness Characteristic with an initial value equal to the favorite level
|
||||
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! Below we create three SpanButton() objects. In the first we specify the pin number, as required, but allow SpanButton() to use
|
||||
// its default values for a LONG press (2000 ms), a SINGLE press (5 ms), and a DOUBLE press (200 ms). In the second and third we change the
|
||||
// default LONG press time to 500 ms, which works well for repeatedly increasing or decreasing the brightness. Since we do not specify
|
||||
// a triggerType, SpanButton uses the default TRIGGER_ON_TOUCH, which is suitable for a pushbutton that connects pin to GROUND when pressed.
|
||||
|
||||
// All of the logic for increasing/decreasing brightness, turning on/off power, and setting/resetting a favorite brightness level is found
|
||||
// in the button() method below.
|
||||
|
||||
new SpanButton(powerPin); // NEW! create new SpanButton to control power using pushbutton on pin number "powerPin"
|
||||
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->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->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->getPin());
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
LOG1("Updating Dimmable LED on pin=");
|
||||
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");
|
||||
}
|
||||
|
||||
if(level->updated()){
|
||||
LOG1(" New Brightness=");
|
||||
LOG1(level->getNewVal());
|
||||
}
|
||||
|
||||
LOG1("\n");
|
||||
|
||||
ledPin->set(power->getNewVal()*level->getNewVal());
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
// NEW! Here is the button() method where all the PushButton actions are defined. Take note of the signature, and use of the word "override"
|
||||
|
||||
void button(int pin, int pressType) override {
|
||||
|
||||
LOG1("Found button press on pin: "); // always a good idea to log messages
|
||||
LOG1(pin);
|
||||
LOG1(" type: ");
|
||||
LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE");
|
||||
LOG1("\n");
|
||||
|
||||
int newLevel;
|
||||
|
||||
if(pin==powerPin){
|
||||
if(pressType==SpanButton::SINGLE){ // if a SINGLE press of the power button...
|
||||
power->setVal(1-power->getVal()); // ...toggle the value of the power Characteristic
|
||||
} else
|
||||
|
||||
if(pressType==SpanButton::DOUBLE){ // if a DOUBLE press of the power button...
|
||||
power->setVal(1); // ...turn on power
|
||||
level->setVal(favoriteLevel); // ...and set brightness to the favorite level
|
||||
} else
|
||||
|
||||
if(pressType==SpanButton::LONG) { // if a LONG press of the power button...
|
||||
favoriteLevel=level->getVal(); // ...save the current brightness level
|
||||
LOG1("Saved new brightness level="); // ...and output log message
|
||||
LOG1(favoriteLevel);
|
||||
LOG1("\n");
|
||||
ledPin->set((1-power->getVal())*level->getVal()); // blink the LED to indicate new level has been saved
|
||||
delay(100);
|
||||
ledPin->set((1-power->getVal())*level->getVal());
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(pin==raisePin){
|
||||
if(pressType==SpanButton::DOUBLE){ // if a DOUBLE press of the raise button...
|
||||
power->setVal(1); // ...turn on power
|
||||
level->setVal(100); // ...and set brightness to the max level
|
||||
} else {
|
||||
|
||||
newLevel=level->getVal()+(pressType==SpanButton::LONG?10:1); // get current brightness level and increase by either 10% (LONG press) or 1% (SINGLE press)
|
||||
if(newLevel>100) // don't allow new level to exceed maximium of 100%
|
||||
newLevel=100;
|
||||
level->setVal(newLevel); // set the value of the brightness Characteristic to this new level
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(pin==lowerPin){
|
||||
if(pressType==SpanButton::DOUBLE){ // if a DOUBLE press of the lower button...
|
||||
power->setVal(1); // ...turn on power
|
||||
level->setVal(5); // ...and set brightness to the min level
|
||||
} else {
|
||||
|
||||
newLevel=level->getVal()-(pressType==SpanButton::LONG?10:1); // get current brightness level and decrease by either 10% (LONG press) or 1% (SINGLE press)
|
||||
if(newLevel<5) // don't allow new level to fall below minimum of 5%
|
||||
newLevel=5;
|
||||
level->setVal(newLevel); // set the value of the brightness Characteristic to this new level
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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 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.
|
||||
|
||||
// But in the button() method, getNewVal() means nothing, since the button() method is not called by HomeKit in response to a user request
|
||||
// from a HomeKit Controller interface. Instead, we are manually changing the values of one or more Characteristic using setVal() in response
|
||||
// to SINGLE, DOUBLE, and LONG SpanButton requests. These changes are instantaneous, so we can retreive the new values with a subsequent call to getVal(),
|
||||
// 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().
|
||||
|
||||
ledPin->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,92 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 16: Stateless Programmable Switches //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_ProgButton.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// Example 16 does not introduce any new HomeSpan functionality, but instead showcases a unique feature of HomeKit that you can readily access with HomeSpan.
|
||||
// In all prior examples we used the ESP32 to control a local appliance - something connected directly to the ESP32 device. We've then seen how you can control
|
||||
// the device via HomeKit's iOS or MacOS Home App, or by the addition of local pushbuttons connected directly to the ESP32 device.
|
||||
|
||||
// In this example we do the opposite, and use buttons on the ESP32 to control OTHER HomeKit devices.
|
||||
|
||||
// To do so, we use HomeKit's Stateless Programmable Switch Service. Similar to other read-only Services, such as the Temperature and Air Quality Sensors
|
||||
// fully explored in Example 12, the Stateless Programmable Switch Service only listens for event notifications coming from HomeSpan and does not try to control
|
||||
// or update anything on the HomeSpan Device. More specifically, the Stateless Programmable Switch Service listens for notifications of a SINGLE, DOUBLE,
|
||||
// or LONG button press coming from HomeSpan.
|
||||
|
||||
// What these button presses mean is outside the control of HomeSpan. Instead, you program their actions directly in the Home App. In this fashion, HomeSpan
|
||||
// becomes a platform for generic buttons that you can program to control any other HomeKit accessory or even trigger HomeKit scenes.
|
||||
|
||||
// Upon running this configuration and pairing to HomeKit, your Home App should reveal a new tile labeled "PushButton Switches." Clicking that tile will open up
|
||||
// a new page where you can program the actions of each of the buttons. These actions can be changed at any time without any need to modify the HomeSpan code,
|
||||
// or even reboot the device.
|
||||
|
||||
// The code for this is quite simple, and as usual we've encapsulated all the functionality in a standalone file: DEV_ProgButton.h. Below we create two generic
|
||||
// buttons, one connected to pin 23, and one connected to pin 5. See DEV_ProgButton.h for complete details.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("PushButton Switches");
|
||||
|
||||
// We've written DEV_ProgButton to take two arguments. The first is a pin number that DEV_ProgButton.h uses to create a SpanButton. The second is an index number
|
||||
// that HomeKit uses as a label when you program the actions of each button in the Home App. The numbers do not have to be sequential, nor start with 1. They just need
|
||||
// to be unique so HomeKit can distinguish them. Note that HomeKit does not require index numbers if you only have one Stateless Programmable Switch Service within any
|
||||
// given Accessory. Since we have two, we must specify two unique index numbers.
|
||||
|
||||
new DEV_ProgButton(23,1); // create Stateless Programmable Switch Service on pin 23 with index=1
|
||||
new DEV_ProgButton(5,2); // create Stateless Programmable Switch Service on pin 5 with index=2
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// DEVICE-SPECIFIC PROGRAMMABLE SWITCH SERVICES //
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
struct DEV_ProgButton : Service::StatelessProgrammableSwitch { // Stateless Programmable Switch
|
||||
|
||||
SpanCharacteristic *switchEvent; // reference to the ProgrammableSwitchEvent Characteristic
|
||||
|
||||
DEV_ProgButton(int buttonPin, int index) : Service::StatelessProgrammableSwitch(){
|
||||
|
||||
switchEvent=new Characteristic::ProgrammableSwitchEvent(); // Programmable Switch Event Characteristic (will be set to SINGLE, DOUBLE or LONG press)
|
||||
new Characteristic::ServiceLabelIndex(index); // set service label index (only required if there is more than one Stateless Programmable Switch per Service)
|
||||
|
||||
new SpanButton(buttonPin); // create new SpanButton
|
||||
|
||||
Serial.print("Configuring Programmable Pushbutton: Pin="); // initialization message
|
||||
Serial.print(buttonPin);
|
||||
Serial.print(" Index=");
|
||||
Serial.print(index);
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
// We do NOT need to implement an update() method or a loop() method - just the button() method:
|
||||
|
||||
void button(int pin, int pressType) override {
|
||||
|
||||
LOG1("Found button press on pin: "); // always a good idea to log messages
|
||||
LOG1(pin);
|
||||
LOG1(" type: ");
|
||||
LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE");
|
||||
LOG1("\n");
|
||||
|
||||
// All the action occurs in this single line below. We simply set the value of the Programmable Switch Event Characteristic
|
||||
// to the value provided by pressType. The values of pressType (0=SpanButton::SINGLE, 1=SpanButton::DOUBLE, and 2=SpanButton::LONG)
|
||||
// were designed to match the required values of the Programmable Switch Event Characteristic.
|
||||
|
||||
switchEvent->setVal(pressType); // set the value of the switchEvent Characteristic
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
@@ -0,0 +1,164 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 17: Linked Services //
|
||||
// * implementing a multi-head Spa Shower //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// HAP normally treats multiple Services created within the same Accessory as independent of one another. However, certain HAP Services are designed to represent a central point
|
||||
// of control over other, more typical Services. For example, you can create an Accessory with one or more Valve Services, each operating independently. But HAP also includes a
|
||||
// Faucet Service that can be used to "control" one or more Valve Services. This is done by LINKING the Faucet Service to one or more Valve Services.
|
||||
//
|
||||
// Only a few types of HAP Services allow/require links to be made to other Services, and only a few types of Services can be selected as a link.
|
||||
//
|
||||
// Linked Services can be created in HomeSpan using the addLink() method. For example, if spaShower is a pointer to a Faucet Service, and showerHead and handSprayer are both
|
||||
// pointers to Valve Services, you can link the faucet to the valves as follows:
|
||||
//
|
||||
// spaShower->addLink(showerHead);
|
||||
// spaShower->addLink(handSprayer);
|
||||
//
|
||||
// The addLink method returns a pointer to the object that called it, which provides you with the option of combining both methods above into a single line as follows:
|
||||
//
|
||||
// spaShower->addLink(showerHead)->addLink(handSprayer);
|
||||
|
||||
// Note that HAP does *not* provide any of the actual logic that's needed for the "controlling" Service to operate the "linked" Services. This must still be programmed by the user.
|
||||
// More so, the logic needs to conform with the behavior HAP expects for the Service as outlined in the HAP documention for the controlling Service. The only thing HAP really does with
|
||||
// Linked Services, besides making you do extra work, is to provide a customized Tile that shows you the controlling Service and the Services to which it is linked.
|
||||
|
||||
// Also as noted above, only a few Services support the Linked Services protcol. If you use the addLink() method with Services that do not support linkages, HAP will simply ignore
|
||||
// the linkage request. But the reverse is not true. If you implement a Service that requires other Linked Services (such as a Faucet) you MUST create those linkages for the
|
||||
// Service to operate properly.
|
||||
|
||||
// Example 17 below demonstrates Linked Services by implementing a multi-head Spa Shower using one HAP Faucet Service and muliple HAP Valve Services. As usual, we will create
|
||||
// our own "child" Services from HAP's Faucet and Valve Services so we can add the logic required to implement our device. However, instead of placing all that logic in separate
|
||||
// *.h files, we include them directly in the main sketch file (below) to illustrate an alternative way of organizing your sketch code.
|
||||
|
||||
// This Example further illustrates yet another coding style option: instead of instantiating all the Services needed in the setup() portion of the sketch, we only instantiate
|
||||
// the Shower Service, and have the Shower Service itself instantiate all the Valve Services. In fact, our entire definition of the Value Service is fully encapsulated
|
||||
// in the definition of the Shower Service.
|
||||
|
||||
// This hopefully provides a good example of the flexibility of HomeSpan. Because all HomeSpan components are defined using standard C++ structures (as opposed to external files
|
||||
// based on some pre-defined format), you can choose whatever coding style you'd like. The style below was chosen since it seemed to fit well for illustating how Linked Services work.
|
||||
// But note that it is only the addLink() method that creates the actual linkages. The fact that the WaterValve Service is defined within the Shower Service is purely a style choice
|
||||
// and does not itself create the linkages. We could have used a standalone structure for the WaterValve definitions and the results would be the same.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// The HAP Valve Service requires both an Active Characteristic and an InUse Characteristic. The Active Characteristic controls whether a Valve is open (active) or closed (inactive).
|
||||
// This Characteristic is normally controlled by the user through the Home App. The InUse Characteristic specifies whether there is water (or gas, etc.) actually flowing
|
||||
// through the Valve. This is because opening a Valve does not necessarily mean water will be flowing. There may be another real-world "master" Valve that also needs to be open
|
||||
// before water can begin flowing. Or there may be another Service that must also be Active to enable water to flow through the Valve. Hence, InUse can either be true or false
|
||||
// if the Valve is open, but it can only be false if the Valve is closed. The Home App cannot change the InUse Characteristic. It is only read by the Home App as a status.
|
||||
|
||||
// It is possible to create a multi-valve Accessory where each Valve is controlled independently from the Home App, and HomeSpan uses internal logic to determine, based
|
||||
// on the combination of Valves that are open or closed, which Valves have water flowing (InUse=true) and which do not (InUse=false).
|
||||
|
||||
// The HAP Faucet Service is used to create a "central control switch" for all the Valves linked to it. The Home App displays each Valve as a small icon on the control
|
||||
// page of the Faucet. Clicking a Valve icon toggles it open/close, and changes the icon accordingly. However, water is not supposed to flow unless the Shower control switch
|
||||
// itself is also turned on. Thus, the logic you need to encode to implement a HAP Faucet is to set the InUse Characteristic of a Valve to true ONLY if the Valve is open
|
||||
// AND the Shower is switched on. If the Shower is then switched off, the Valve remains open, but the InUse Characteristic needs to be reset to false. Similarly, if the Shower
|
||||
// is switched back on, the InUse Characteristic of each Valve that is open needs to be set to true. This mimics how an actual Shower with a central controlling switch
|
||||
// would operate.
|
||||
|
||||
// In addition, the Home App displays one of 4 status messages as you operate the Shower and Valve controls:
|
||||
|
||||
// OFF: The Shower switch is OFF, AND the InUse Characteristic for EVERY Valve is set to FALSE (no water flowing anywhere);
|
||||
// STOPPING: The Shower switch is OFF, BUT at least one Valve still has its InUse Characteristic set to TRUE. Presumably this means the Valve is in the process of turning off;
|
||||
// STARTING: The Shower switch is ON, BUT the InUse Characteristic for EVERY Valve is set to FALSE. This indicates the Shower is waiting for water to start flowing;
|
||||
// RUNNING: The Shower switch in ON, AND at least one of the Valves has its InUse Characteristic set to TRUE. This indicates water is flowing.
|
||||
|
||||
// Note that the Shower Service only monitors the InUse Characteristics of its Linked Valves. It does not monitor the Active Characteristics of the Linked Valves. Also, turning
|
||||
// on and off the Shower Switch should NOT change the Active Characteristic of any Valve. Below is the code that implements all of this HAP-required logic:
|
||||
|
||||
struct Shower : Service::Faucet { // this is our Shower structure, which we define as a child class of the HomeSpan Faucet structure
|
||||
|
||||
SpanCharacteristic *active=new Characteristic::Active(); // our implementation only requires the Active Characteristic
|
||||
|
||||
Shower(int nHeads){ // this is the constructor for Shower. It takes a single argument that specifies the number of spray heads (WaterValves)
|
||||
for(int i=0;i<nHeads;i++) // for each spray head needed ---
|
||||
addLink(new WaterValve(this)); // --- instantiate a new WaterValve AND link it to the Shower. Also, pass the Shower object's pointer to WaterValve constructor. We'll see why below.
|
||||
}
|
||||
|
||||
struct WaterValve : Service::Valve { // here we define our WaterValve structure as a child class of the HomeSpan Valve Service
|
||||
SpanCharacteristic *active=new Characteristic::Active(1);; // the Active Characteristic is used to specify whether the Valve is Active (open) or Inactive (closed)
|
||||
SpanCharacteristic *inUse=new Characteristic::InUse(); // the InUse Characteristic is used to specify whether water is actually flowing through value
|
||||
Shower *shower; // storage for the pointer to the "controlling" Shower Service
|
||||
|
||||
WaterValve(Shower *s){ // this is constructor for WaterValve. It takes a single argument that points to the "controlling" Shower Service
|
||||
shower=s; // store the pointer to the Shower Service
|
||||
new Characteristic::ValveType(Characteristic::ValveType::SHOWER_HEAD); // specify the Valve Type as a Shower Head (note use of constant "Characteristic::ValveType::SHOWER_HEAD")
|
||||
}
|
||||
|
||||
boolean update() override { // HomeSpan calls this whenever the Home App requests a change in a Valve's Active Characteristic
|
||||
if(shower->active->getVal()) // here's where we use the pointer to Shower: ONLY if the Shower object itself is active---
|
||||
inUse->setVal(active->getNewVal()); // --- do we update the InUse Characteristic to reflect a change in the status of flowing water.
|
||||
return(true); // Note that the Valve itself will still change from Active to Inactive (or vice versa) regardless of the status of the Shower
|
||||
}
|
||||
|
||||
void loop() override { // Here we check if the Shower is turned on or off, and determine if that means we need to update the Valve
|
||||
if(shower->active->getVal() && active->getVal() && !inUse->getVal()) // If the Shower is Active, and the Valve is Active but NOT showing InUse...
|
||||
inUse->setVal(1); // ...show Valve as InUse
|
||||
else if(!shower->active->getVal() && inUse->getVal()) // Otherwise, if the Shower is NOT Active but Valve IS showing InUse...
|
||||
inUse->setVal(0); // ...show Valve as NOT InUse
|
||||
}
|
||||
|
||||
}; // WaterValve
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::ShowerSystems,"HomeSpan Shower");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new Shower(4); // Create a Spa Shower with 4 spray heads
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,117 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-2024 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"
|
||||
|
||||
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 Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED 1");
|
||||
new DEV_DimmableLED(17,19); // The first argument specifies the LED pin; the second argument specifies the PushButton pin
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED 2");
|
||||
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.
|
||||
//
|
||||
71
ESP32/HomeSpan-master/examples/18-SavingStatus/DEV_LED.h
Normal file
71
ESP32/HomeSpan-master/examples/18-SavingStatus/DEV_LED.h
Normal file
@@ -0,0 +1,71 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
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
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
89
ESP32/HomeSpan-master/examples/19-WebLog/19-WebLog.ino
Normal file
89
ESP32/HomeSpan-master/examples/19-WebLog/19-WebLog.ino
Normal file
@@ -0,0 +1,89 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 19: Web Logging with time-keeping //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_LED.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// This is a duplicate of Example 5 (Two Working LEDs) with the addition of HomeSpan Web Logging
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// Below we enable Web Logging. The first parameter sets the maximum number of log messages to save. As the
|
||||
// log fills with messages, older ones are replaced by newer ones. The second parameter specifies a Timer Server
|
||||
// that HomeSpan calls to set the device clock. Setting the clock is optional, and you can leave this
|
||||
// argument blank (or set to NULL) if you don't care about setting the absolute time of the device. The third
|
||||
// argument defines the Time Zone used for setting the device clock. The fourth argument specifies the URL page
|
||||
// of the Web Log. See the HomeSpan API Reference for complete details, as well as additional options, related
|
||||
// to this function call.
|
||||
|
||||
homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog
|
||||
|
||||
// The full URL of the Web Log will be shown in the Serial Monitor at boot time for reference.
|
||||
// The Web Log output displays a variety of device parameters, plus any log messages you choose
|
||||
// to provide with the WEBLOG() macro (see DEV_LED.h)
|
||||
|
||||
// Note the rest of the sketch below is identical to Example 5. All of the Web Logging occurs in DEV_LED.h
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDs"); // Note we can set Category to Lighting even if device is configured as a Bridge
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED #1");
|
||||
new DEV_LED(16);
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("LED #2");
|
||||
new DEV_LED(17);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
50
ESP32/HomeSpan-master/examples/19-WebLog/DEV_LED.h
Normal file
50
ESP32/HomeSpan-master/examples/19-WebLog/DEV_LED.h
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_LED : Service::LightBulb {
|
||||
|
||||
int ledPin;
|
||||
SpanCharacteristic *power;
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
WEBLOG("Configuring LED on Pin %d",ledPin); // NEW! This creates a Web Log message announcing the configuration of the device
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
WEBLOG("LED on Pin %d: %s",ledPin,power->getNewVal()?"ON":"OFF"); // NEW! This creates a Web Log message whenever an LED is turned ON or OFF
|
||||
return(true);
|
||||
|
||||
} // update
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
// SOME MORE ABOUT WEB LOGGING
|
||||
// ---------------------------
|
||||
//
|
||||
// * The WEBLOG() macro operates by calling Serial.printf(), so the first argument always needs to be a text string containing printf-like format instructions.
|
||||
// The rest of the arguments, if any, are the variables to print. For example, you cannot simply write WEBLOG(ledPin). This will cause errors at compile time,
|
||||
// though you can write LOG1(ledPin) or LOG2(ledPin) to output log messages just to the Serial Monitor.
|
||||
//
|
||||
// * You do NOT need to include a "\n" at the end of your format string since all Web Log messages are formatted into an HTML table when presented, and HTML ignores "\n".
|
||||
//
|
||||
// * Every Web Log message is recorded with TWO timestamps. The first timestamp is relative to when the device first booted, and is presented as DAYS:HH:MM:SS. This timestamp
|
||||
// is always present. The second timestamp is an absolute clock time, in standard Unix form, such as "Mon Aug 10 13:52:48 2020". This timestamp will only be present
|
||||
// if the clock time of the device was set, else it will be shown as "Unknown". Note that in the example above, the first Web Log message ("Configuring...") will
|
||||
// have a clock timestamp of "Unknown" even though we enabled Web Logging with a Time Server. This is because the Time Server cannot be configured until WiFi has
|
||||
// been established, and the first Web Log message above is created during initial configuratin of the device, BEFORE a WiFi connection is made. This is perfectly fine to do.
|
||||
//
|
||||
// * Every Web Log message also includes the IP Address of the Client that made the request, unless the Web Log message was generated independently of any Client request,
|
||||
// such as in the first message above. In these cases the IP Address will be displayed as 0.0.0.0.
|
||||
//
|
||||
// * Web Log messages are printed to the Serial Monitor whenever the HomeSpan Log Level is set to 1 or greater. Hence there is no reason to duplicate the same message
|
||||
// using WEBLOG() and LOG1() at the same time.
|
||||
@@ -0,0 +1,251 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 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 20: Demonstrates various advance HomeSpan functions //
|
||||
// by implementing a Bridge in which one or more //
|
||||
// Lightbulb Accessories can be added and deleted //
|
||||
// *dynamically* without needing to restart the //
|
||||
// device //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// In Example 20 we will implement a bridge device supporting up to 10 Lightbulb Accessories. However, rather than pre-specifying the number of Lights, we
|
||||
// will allow Light Accessories to be added and deleted dynamically by the user via the CLI. Changes are reflected in the Home App without the need to restart
|
||||
// the device! Note this example uses a variety of advanced HomeSpan functions, as well as some detailed features of both the ESP32-IDF and C++ that have not been used
|
||||
// in any of the previous examples.
|
||||
|
||||
// We will use a C++ array with 10 elements containing integers representing the Light "ID" of each Lightbulb Accessory implemented. An ID of zero means there is no
|
||||
// Light defined in that element.
|
||||
|
||||
#include <array> // include the C++ standard library array container
|
||||
|
||||
std::array<int,10> lights; // declare "lights" to be an array of 10 integers
|
||||
|
||||
using std::fill; // place the std library function fill, remove, and find, into the global namespace so we can use them below without prefacing with "std::"
|
||||
using std::remove;
|
||||
using std::find;
|
||||
|
||||
// We will use non-volatile storage (NVS) to store the lights array so that the device can restore the current configuration upon rebooting
|
||||
|
||||
nvs_handle savedData; // declare savdData as a handle to be used with the NVS (see the ESP32-IDF for details on how to use NVS storage)
|
||||
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
fill(lights.begin(),lights.end(),0); // initialize lights array with zeros in each of the 10 elements (no Light Accessories defined)
|
||||
|
||||
size_t len;
|
||||
nvs_open("SAVED_DATA",NVS_READWRITE,&savedData); // open a new namespace called SAVED_DATA in the NVS
|
||||
if(!nvs_get_blob(savedData,"LIGHTS",NULL,&len)) // if LIGHTS data found
|
||||
nvs_get_blob(savedData,"LIGHTS",&lights,&len); // retrieve data
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Lights");
|
||||
|
||||
// We begin by creating the Bridge Accessory
|
||||
|
||||
new SpanAccessory(1); // here we specified the AID=1 for clarity (it would default to 1 anyway if left blank)
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Model("HomeSpan Dynamic Bridge"); // defining the Model is optional
|
||||
|
||||
// Now we create Light Accessories based on what is recorded in the lights array
|
||||
// We'll use C++ iterators to loop over all elements until we reach the end of the array, or find an element with a value of zero
|
||||
|
||||
for(auto it=lights.begin(); it!=lights.end() && *it!=0; it++) // loop over all elements (stopping when we get to the end, or hit an element with a value of zero)
|
||||
addLight(*it); // call addLight (defined further below) with an argument equal to the integer stored in that element
|
||||
|
||||
// Next we create four user-defined CLI commands so we can add and delete Light Accessories from the CLI.
|
||||
// The functions for each command are defined further below.
|
||||
|
||||
new SpanUserCommand('a',"<num> - add a new light accessory with id=<num>",addAccessory);
|
||||
new SpanUserCommand('d',"<num> - delete a light accessory with id=<num>",deleteAccessory);
|
||||
new SpanUserCommand('D'," - delete ALL light accessories",deleteAllAccessories);
|
||||
new SpanUserCommand('u',"- update accessories database",updateAccessories);
|
||||
|
||||
// Finally we call autoPoll to start polling the background. Note this is purely optional and only used here to illustrate how to
|
||||
// use autoPoll - you could instead have called the usual homeSpan.poll() function by including it inside the Arduino loop() function
|
||||
|
||||
homeSpan.autoPoll();
|
||||
|
||||
} // end of setup()
|
||||
|
||||
// Usually the Arduino loop() function would be defined somewhere here. But since we used autoPoll in the setup() function,
|
||||
// we don't have to define the loop() function at all in this sketch! Why don't we get an error? Because HomeSpan includes
|
||||
// a default loop() function, which prevents the compiler from complaining about loop() being undefined.
|
||||
|
||||
///////////////////////////
|
||||
|
||||
// This function creates a new Light Accessory with n as the "ID".
|
||||
// It is called initially in setup() above to create Light Accessories based
|
||||
// on what was stored in the lights array. It is also called in response to
|
||||
// typing 'a' into the CLI (see below), which dynamically adds a new Light Accessory
|
||||
// while the device is running.
|
||||
|
||||
void addLight(int n){
|
||||
|
||||
char name[32];
|
||||
sprintf(name,"Light-%d",n); // create the name of the device using the specified "ID"
|
||||
char sNum[32];
|
||||
sprintf(sNum,"%010d",n); // create serial number from the ID - this is helpful in case we rename the Light to something else using the Home App
|
||||
|
||||
Serial.printf("Adding Accessory: %s\n",name);
|
||||
|
||||
new SpanAccessory(n+1); // IMPORTANT: add 1, since first Accessory with AID=1 is already used by the Bridge Accessory
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(name);
|
||||
new Characteristic::SerialNumber(sNum);
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
// This function is called in response to typing '@a <num>' into the CLI.
|
||||
// It adds a new Light Accessory with ID=num, by calling addLight(num) above.
|
||||
|
||||
void addAccessory(const char *buf){
|
||||
|
||||
int n=atoi(buf+1); // read the value of <num> specified
|
||||
|
||||
if(n<1){ // ensure <num> is greater than 0
|
||||
Serial.printf("Invalid Accessory number!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(find(lights.begin(),lights.end(),n)!=lights.end()){ // search for this ID in the existing lights array - if found, report an error and return
|
||||
Serial.printf("Accessory Light-%d already implemented!\n",n);
|
||||
return;
|
||||
}
|
||||
|
||||
auto it=find(lights.begin(),lights.end(),0); // find the next "free" element in the light array (the first element with a value of zero)
|
||||
|
||||
if(it==lights.end()){ // if there were no elements with a zero, the array is full and no new Lights can be added
|
||||
Serial.printf("Can't add any more lights - max is %d!\n",lights.size());
|
||||
return;
|
||||
}
|
||||
|
||||
*it=n; // save light number
|
||||
nvs_set_blob(savedData,"LIGHTS",&lights,sizeof(lights)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
addLight(n); // add light accessory by calling the function above!
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
// This function deletes an existing Light Accessory and is called
|
||||
// in response to typing '@d <num>' into the CLI.
|
||||
|
||||
void deleteAccessory(const char *buf){
|
||||
|
||||
int n=atoi(buf+1); // same as above, we read the specified <num> and check that it is valid (i.e. greater than 0)
|
||||
|
||||
if(n<1){
|
||||
Serial.printf("Invalid Accessory number!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Below we use the homeSpan method deleteAccessory(aid) to completely delete the Accessory with AID=n+1.
|
||||
// We add 1 because the AID of the first Light Accessory is 2, since the Bridge Accessory has an AID of 1.
|
||||
// The deleteAccessory() method returns true if an Accessory with matching AID is found, otherwise it returns false.
|
||||
// When deleting an Accessory, HomeSpan will print a delete message for every Service, Characteristic, loop() method,
|
||||
// button() method, and SpanButton, associated with that Accessory. These are Level-1 Log messages, so you'll need
|
||||
// to have the Log Level in the sketch set to 1 or 2 to receive the output.
|
||||
|
||||
if(homeSpan.deleteAccessory(n+1)){ // if deleteAccessory() is true, a match has been found
|
||||
Serial.printf("Deleting Accessory: Light-%d\n",n);
|
||||
|
||||
fill(remove(lights.begin(),lights.end(),n),lights.end(),0); // remove entry from lights array and fill any undefined elements with zero
|
||||
nvs_set_blob(savedData,"LIGHTS",&lights,sizeof(lights)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
} else {
|
||||
Serial.printf("No such Accessory: Light-%d\n",n);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAllAccessories(const char *buf){
|
||||
|
||||
// This function is called in response to typing '@D' into the CLI.
|
||||
// It deletes all Light Accessories
|
||||
|
||||
if(lights[0]==0){ // first check that there is at least one Light Accessory by checking for a non-zero ID in lights[0]
|
||||
Serial.printf("There are no Light Accessories to delete!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto it=lights.begin(); it!=lights.end() && *it!=0; it++) // use an iterator to loop over all non-zero elements in the lights array...
|
||||
homeSpan.deleteAccessory(*it+1); // ... and delete the matching Light Accessory (don't forgot to add 1 to the Light ID to form the AID)
|
||||
|
||||
fill(lights.begin(),lights.end(),0); // zero out all the elements in the lights array, since all Light Accessories have been deleted
|
||||
nvs_set_blob(savedData,"LIGHTS",&lights,sizeof(lights)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
Serial.printf("All Light Accessories deleted!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
// Lastly we have the all-important updateAccessories function.
|
||||
// This is called in response to typing '@u' into the CLI.
|
||||
// Though the above functions can be used to add and delete Light Accessories
|
||||
// dyammically, Controllers such as the Home App that are already connected to
|
||||
// the device don't yet know additional Light Accessories have been added to (or
|
||||
// deleted from) the overall Accessories datase. To let them know, HomeSpan needs
|
||||
// to increment the HAP Configuration Number and re-broadcast it via MDNS so all
|
||||
// connected Controllers are aware that they need to request a refresh from the device.
|
||||
|
||||
// When you type '@u' into the CLI, you should see a lot of activity between the device
|
||||
// and any connected Controllers as they request a refresh. Be patient - it can take up to a
|
||||
// minute for changes to be properly reflected in the Home App on your iPhone or Mac.
|
||||
|
||||
void updateAccessories(const char *buf){
|
||||
|
||||
// note the updateDatabase() method returns true if the database has indeed changed (e.g. one or more new Light Accessories were added), or false if nothing has changed
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
Serial.printf("Accessories Database updated. New configuration number broadcasted...\n");
|
||||
else
|
||||
Serial.printf("Nothing to update - no changes were made!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
@@ -0,0 +1,173 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 21: Using the Identify Characteristic //
|
||||
// //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// This sketch is similar to Example 5, in which we implemented two simple Lightbulb Accessories,
|
||||
// except now we will also add functionality for the Identify Characteristic (we will also configure
|
||||
// the device as a Bridge Accessory, instead of two standalone Accessories).
|
||||
|
||||
// Recall that the Identify Characteristic has been instantiated in every example sketch since it
|
||||
// is a required Characteristic of the AccessoryInformation Service, and that Service is itself
|
||||
// required to be present for every Accessory. Thus, every Accessory (including the Bridge
|
||||
// Accessory if used), has its own instant of the Identify Characteristic.
|
||||
|
||||
// Though not typically used during normal operation of an Accessory, the Identify Characteristic
|
||||
// can be useful when first pairing your device to HomeKit. You may have noticed when pairing your
|
||||
// device using the Home App that there is the word "Identify" at the bottom of each of the screens
|
||||
// that ask you what you want to name each Accessory, what room the Accessory should be assigned to, etc.
|
||||
|
||||
// Clicking "Identify" on any of those screens causes HomeKit to send an update request to the
|
||||
// Identify Characteristic associated with the corresponding Accessory. As with any Characteristic that
|
||||
// is updated via the Home App, this will trigger a call to the update() method for the enclosing Service.
|
||||
|
||||
// The purpose of this is so that your device can run some sort of "identification routine" when requested,
|
||||
// allowing you to visually confirm that you are indeed pairing the correct device. For example, if you
|
||||
// have three separate devices wired to three different lights or appliances, you want to make sure that when
|
||||
// you start pairing each of them to the Home App you are connected to the device you intend.
|
||||
|
||||
// The identification routine can be anything you choose. The only HAP requirement is that it should not take
|
||||
// longer than 5 seconds to run. In the sketch below we have created an identification routine that logs a
|
||||
// message to the Serial Monitor and blinks the LED associated with the Accessory 3 times whenever its
|
||||
// Identify Characteristic is updated.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// Below is the same DEV_LED Lightbulb Service we've used in many of the previous examples
|
||||
|
||||
struct DEV_LED : Service::LightBulb {
|
||||
|
||||
int ledPin;
|
||||
SpanCharacteristic *power;
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
LOG0("LED %d: Power %s\n",ledPin,power->getNewVal()?"ON":"OFF");
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// NEW: Here we derive a new class, DEV_INFO, from the Accessory Information Service
|
||||
|
||||
// This structure takes a single argument (ledPin), creates a name from it, and assigns
|
||||
// it to the Name Characteristic.
|
||||
|
||||
// It also instantiates the required Identify Characteristic, and implements an update() method
|
||||
// that logs a message to the Serial Monitor and blinks the associated LED three times.
|
||||
|
||||
// Note that in the update() method we do not bother to check which Characteristic has been updated.
|
||||
// This is because the only possibility is the Identify Characteristic.
|
||||
|
||||
// Also, we do not need to use getNewVal() to check the value. The Home App always sends a value of 1,
|
||||
// since it is just trying to trigger the identification routine (the value itself is meaningless).
|
||||
|
||||
struct DEV_INFO : Service::AccessoryInformation {
|
||||
|
||||
int ledPin;
|
||||
|
||||
DEV_INFO(int ledPin) : Service::AccessoryInformation(){
|
||||
|
||||
new Characteristic::Identify();
|
||||
char c[64];
|
||||
sprintf(c,"LED-%d",ledPin);
|
||||
new Characteristic::Name(c);
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
LOG0("Running Identification for LED %d\n",ledPin);
|
||||
for(int i=0;i<3;i++){
|
||||
digitalWrite(ledPin,HIGH);
|
||||
delay(500);
|
||||
digitalWrite(ledPin,LOW);
|
||||
delay(500);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LEDS");
|
||||
|
||||
// Here we replace the usual construct:
|
||||
|
||||
// new SpanAccessory();
|
||||
// new Service::AccessoryInformation();
|
||||
// new Characteristic::Identify();
|
||||
|
||||
// with this:
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_INFO(13); // instantiate a new DEV_INFO structure that will run our custom identification routine to blink an LED on pin 13 three times
|
||||
|
||||
new SpanAccessory();
|
||||
new DEV_INFO(16); // Note we instantiate a new DEV_INFO structure for each Accessory in this device
|
||||
new DEV_LED(16); // Here we instantiate the usual DEV_LED structure that controls the LED during normal operation
|
||||
|
||||
new SpanAccessory(); // Here we add a second LED Accessory
|
||||
new DEV_INFO(17);
|
||||
new DEV_LED(17);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
homeSpan.poll();
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// NOTE: Once a device has been paired, it is no longer possible to trigger the Identify Characteristic from the Home App.
|
||||
// Apple assumes that the identification routine is no longer needed since you can always identify the device by simply operating it.
|
||||
// However, the Eve for HomeKit app DOES provide an "ID" button in the interface for each Accessory that can be used to trigger
|
||||
// the identification routine for that Accessory at any time after the device has been paired.
|
||||
@@ -0,0 +1,159 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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 22: Demonstrates the use of the TLV8 Library //
|
||||
// by implementing DisplayOrder, an optional //
|
||||
// TLV8 Characteristic used with the TV Service //
|
||||
// to set the order in which TV Inputs are //
|
||||
// displayed for selection in the Home App //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// NOTE: Please see the "Other Examples -> Television" sketch for complete details on how to implement a Television Service. The focus
|
||||
// of this sketch is solely to demonstrate how to use the TLV8 Library to create TLV8 data for use with the DisplayOrder Characteristic.
|
||||
|
||||
// First we define a simple Television Input Source Service
|
||||
|
||||
struct TVInput : Service::InputSource {
|
||||
|
||||
SpanCharacteristic *inputID;
|
||||
SpanCharacteristic *inputName;
|
||||
|
||||
TVInput(uint32_t id, const char *name) : Service::InputSource() {
|
||||
|
||||
inputID = new Characteristic::Identifier(id);
|
||||
inputName = new Characteristic::ConfiguredName(name);
|
||||
new Characteristic::IsConfigured(Characteristic::IsConfigured::CONFIGURED);
|
||||
new Characteristic::CurrentVisibilityState(Characteristic::CurrentVisibilityState::VISIBLE);
|
||||
}
|
||||
};
|
||||
|
||||
// Next we define a simple Television Service
|
||||
|
||||
struct HomeSpanTV : Service::Television {
|
||||
|
||||
SpanCharacteristic *active = new Characteristic::Active(0);
|
||||
SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(10);
|
||||
|
||||
SpanCharacteristic *displayOrder; // Create a pointer to use for the new TLV8 DisplayOrder Characteristic, which will be instantiated below once we build the TLV8 record
|
||||
|
||||
HomeSpanTV() : Service::Television() {
|
||||
|
||||
// Before we instantiate displayOrder, we need to build a TLV8 object with the information required
|
||||
// by the DisplayOrder Characteristic. The (undocumented by Apple!) TLV8 specifications for the
|
||||
// DisplayOrder Characteristic are as follows:
|
||||
|
||||
// TAG NAME FORMAT DESCRIPTION
|
||||
// ---- ------------- ------ --------------------------------------------
|
||||
// 0x01 inputSourceID uint32 ID of the Input Source to be displayed first
|
||||
// 0x00 separator none Empty element to separate the inputSourceIDs
|
||||
// 0x01 inputSourceID uint32 ID of the Input Source to be displayed second
|
||||
// 0x00 separator none Empty element to separate the inputSourceIDs
|
||||
// 0x01 inputSourceID uint32 ID of the Input Source to be displayed third
|
||||
// 0x00 separator none Empty element to separate the inputSourceIDs
|
||||
// etc...
|
||||
|
||||
// To start, instantiate a new TLV8 object
|
||||
|
||||
TLV8 orderTLV; // creates an empty TLV8 object
|
||||
|
||||
// Next, fill it with TAGS and VALUES based on the above specification. The easiest, though
|
||||
// not necessarily most elegant, way to do this is by simply adding each TAG/VALUE as follows:
|
||||
|
||||
orderTLV.add(1,10); // TAG=1, VALUE=ID of first Input Source to be displayed
|
||||
orderTLV.add(0); // TAG=0 (no value)
|
||||
orderTLV.add(1,20); // TAG=1, VALUE=ID of the second Input Source to be displayed
|
||||
orderTLV.add(0); // TAG=0 (no value)
|
||||
orderTLV.add(1,50); // TAG=1, VALUE=ID of the third Input Source to be displayed
|
||||
orderTLV.add(0); // TAG=0 (no value)
|
||||
orderTLV.add(1,30); // TAG=1, VALUE=ID of the fourth Input Source to be displayed
|
||||
orderTLV.add(0); // TAG=0 (no value)
|
||||
orderTLV.add(1,40); // TAG=1, VALUE=ID of the fifth Input Source to be displayed
|
||||
|
||||
// Based on the above structure, we expect the Home App to display our input sources based on their IDs
|
||||
// in the following order: 10, 20, 50, 30, 40. These IDs must of course match the IDs you choose
|
||||
// for your input sources when you create them at the end of this sketch in setup()
|
||||
|
||||
// Now we can instantiate displayOrder using the TLV8 object created above as its initial value
|
||||
|
||||
displayOrder = new Characteristic::DisplayOrder(orderTLV); // set the "value" of DisplayOrder to be the orderTLV object we just created
|
||||
|
||||
// That's it - you've created your first TLV8 Characteristic!
|
||||
}
|
||||
|
||||
// Below we define the usual update() loop. There is nothing "TLV-specific" about this part of the code
|
||||
|
||||
boolean update() override {
|
||||
|
||||
if(active->updated()){
|
||||
LOG0("Set TV Power to: %s\n",active->getNewVal()?"ON":"OFF");
|
||||
}
|
||||
|
||||
if(activeID->updated()){
|
||||
LOG0("Set Input Source to ID=%d\n",activeID->getNewVal());
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setLogLevel(2);
|
||||
|
||||
homeSpan.begin(Category::Television,"HomeSpan Television");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
|
||||
(new HomeSpanTV()) // Define a Television Service and link in the InputSources!
|
||||
->addLink(new TVInput(10,"Xfinity"))
|
||||
->addLink(new TVInput(20,"BlueRay Disc"))
|
||||
->addLink(new TVInput(30,"Amazon Prime"))
|
||||
->addLink(new TVInput(40,"Netflix"))
|
||||
->addLink(new TVInput(50,"Hulu"))
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
homeSpan.poll();
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,138 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 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 the use of a custom Partition Scheme file: "partitions.csv"
|
||||
|
||||
// During compilation, if a file with this EXACT name is placed in the sketch folder,
|
||||
// the esptool performing the compilation will use the partition scheme found
|
||||
// in "partitions.csv" regardless of what partition scheme you selected in the Arduino IDE.
|
||||
|
||||
// Note if you change the partition scheme it is highly recommended that you fully erase the flash
|
||||
// upon your next compile/upload by enabling the "Erase All Flash" option from the Arduino IDE menu.
|
||||
// NOTE: remember to turn OFF this option after you've successully uploaded a sketch with the new
|
||||
// partition scheme, else you will continue to erase everything saved in the NVS every time you upload
|
||||
// a new sketch (which is likely NOT what you want to occur).
|
||||
|
||||
// The main reason for wanting to create your own partition scheme is to expand the NVS space.
|
||||
// All of the pre-configured partition scheme you can select from the Arduino IDE provide
|
||||
// for 504 records of NVS space. This is usuall sufficient for most HomeSpan projects, but if
|
||||
// you have a LOT of Accessories (as per below) AND you are saving their states in NVS, you can
|
||||
// use up all the NVS space. If this occurs, HomeSpan will warn you of low NVS space upon boot-up.
|
||||
|
||||
// The custom partition scheme included in this sketch folder solves this problem by eliminating
|
||||
// the SPIFFs partition (which is generally not used by HomeSpan) and using this portion of the flash
|
||||
// to provide an NVS space with 3906 records --- more than enough for even the largest projects.
|
||||
|
||||
// For reference, in addition to HomeSpan's internal use of NVS (about 32 records), saving a
|
||||
// numerical Characteristic consumes one additional NVS record, and saving a string Characteristic (of
|
||||
// less than 32 characters) consumes two NVS records. Also, the ESP32 WiFi stack consumes about 130
|
||||
// additional NVS records once initialized. As such, the sketch below requires:
|
||||
|
||||
// 32 records (internal HomeSpan use)
|
||||
// + 320 records (80 Accessories * 4 saved numerical Characterstics)
|
||||
// + 160 records (80 Accessories * 2 records per saved string Characterstic)
|
||||
// + 130 records (with WiFi initialized)
|
||||
// ----------------------------------------
|
||||
// = 642 NVS records needed (which exceeds the normal 504 limit, unless a custom partition scheme is used)
|
||||
|
||||
// Note that once HomeSpan is paired with HomeKit, additional NVS records will be consumed to store the
|
||||
// pairing information for each verified HomeKit Controller.
|
||||
|
||||
// Note also that when compiling under the Arduino IDE, the IDE reports the size of partition based on the
|
||||
// Partition Scheme you selected in the IDE menu, even though that scheme is not actually used if you have your
|
||||
// own "partition.csv" file, as in this example. This may lead the IDE to report an incorrect partition size.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
#define MAX_LIGHTS 80 // configure for 80 Light Accessories
|
||||
|
||||
struct RGB_Light : Service::LightBulb {
|
||||
|
||||
Characteristic::On power{0,true}; // save these 4 numerical Characteristics (4*80 = 320 NVS records)
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{0,true};
|
||||
|
||||
int lightNumber;
|
||||
|
||||
RGB_Light(int n) : Service::LightBulb(){
|
||||
|
||||
lightNumber=n;
|
||||
LOG0("Configured RGB Light-%0d\n",lightNumber);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
if(power.updated())
|
||||
LOG0("Light-%d: Power=%s",lightNumber,power.getNewVal()?"ON":"OFF");
|
||||
|
||||
if(H.updated())
|
||||
LOG0("Light-%d: Hue=%d",lightNumber,H.getNewVal());
|
||||
|
||||
if(S.updated())
|
||||
LOG0("Light-%d: Saturation=%d",lightNumber,S.getNewVal());
|
||||
|
||||
if(V.updated())
|
||||
LOG0("Light-%d: Brightness=%d",lightNumber,V.getNewVal());
|
||||
|
||||
return(false);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Max");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
for(int i=1;i<=MAX_LIGHTS;i++){
|
||||
char c[60];
|
||||
sprintf(c,"Light-%02d",i);
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(c,true); // save this string Characteristic (2*80 = 160 NVS records)
|
||||
|
||||
new RGB_Light(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# --- Custom Partition Table for HomeSpan ---
|
||||
#
|
||||
# Similar to min_spiffs, except that the 128K SPIFF block at the end
|
||||
# is replaced by a 128K NVS block, and the initial 20K NVS block
|
||||
# is no longer used. Note this table is designed for use with 4MB Flash.
|
||||
# To use with 8MB Flash, increase app0 and app1 by 2048K to become 3968K each.
|
||||
# To use with 16MB Flash, increase app0 and app1 by 6144K to become 8064K each
|
||||
#
|
||||
unused_nvs,data,nvs,,20K,
|
||||
otadata,data,ota,,8K,
|
||||
app0,app,ota_0,,1920K,
|
||||
app1,app,ota_1,,1920K,
|
||||
nvs,data,nvs,,128K,
|
||||
coredump,data,coredump,,64K,
|
||||
|
@@ -0,0 +1,110 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// Apple's HomeKit does not provide any native services or characteristics for measuring atmospheric pressure.
|
||||
// However, Eve for HomeKit does support pressure measurements.
|
||||
|
||||
// This brief sketch demonstrates how you can use HomeSpan's Custom Service and Custom Characteristic features
|
||||
// to create a Pressure Sensor Accessory that will be recognized by the Eve for HomeKit App. Note that the
|
||||
// Apple Home App will show this as a "Not Supported" Accessory Tile indicating it cannot be used in the Home App.
|
||||
// However, this does not create any problems or errors in the Home App.
|
||||
|
||||
// Step 1:
|
||||
|
||||
// Use the CUSTOM_SERV macro to create a new service named AtmosphericPressureSensor with
|
||||
// a UUID=E863F00A-079E-48FF-8F27-9C2605A29F52. This new service will be added to HomeSpan's Service namespace
|
||||
// and can be accessed using the fully-qualified name Service::AtmosphericPressureSensor. The UUID specified
|
||||
// will not be recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you
|
||||
// do NOT enclose either of the parameters in quotes!
|
||||
|
||||
CUSTOM_SERV(AtmosphericPressureSensor, E863F00A-079E-48FF-8F27-9C2605A29F52);
|
||||
|
||||
// Step 2:
|
||||
|
||||
// Use the CUSTOM_CHAR macro to create a new characteristic named AtmosphericPressure with
|
||||
// a UUID=E863F10F-079E-48FF-8F27-9C2605A29F52. This new characteristic will be added to HomeSpan's Characteristic namespace
|
||||
// and can be accessed using the fully-qualified name Characteristic::AtmosphericPressure. The UUID specified will not be
|
||||
// recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you do NOT enclose any of the
|
||||
// parameters in quotes!
|
||||
//
|
||||
// The meaning of the parmameters are as follows:
|
||||
//
|
||||
// PR+EV: sets permission for "read" and "notify"
|
||||
// FLOAT: sets the format to floating-point decimal number
|
||||
// 1013: sets the default starting value to 1013, which is 1 atm in millibars
|
||||
// 700: sets the default lower range of allowed values to 700 millibars
|
||||
// 1200: sets the default upper range of allowed values to 1200 millibars
|
||||
// false: sets the "static range" flag to false, indicating that users CAN override the default range setRange() if desired
|
||||
|
||||
CUSTOM_CHAR(AtmosphericPressure, E863F10F-079E-48FF-8F27-9C2605A29F52, PR+EV, FLOAT, 1013, 700, 1200, false);
|
||||
|
||||
// Now that AtmosphericPressureSensor and AtmosphericPressure have been created, they can be used just as any other native HomeSpan
|
||||
// Service and Characteristic.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
struct PressureSensor : Service::AtmosphericPressureSensor { // A standalone Air Pressure Sensor
|
||||
|
||||
Characteristic::AtmosphericPressure pressure; // Eve Air Pressure with range 700-1200 hPa (millibars), where 1 atm=1013 hPa
|
||||
|
||||
PressureSensor() : Service::AtmosphericPressureSensor{} {
|
||||
|
||||
Serial.print("Configuring Air Pressure Sensor"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(pressure.timeVal()>5000) // here we simulate an actual sensor by generating a random pressure reading every 5 seconds
|
||||
pressure.setVal((double)random(900,1100));
|
||||
|
||||
} // end loop
|
||||
|
||||
}; // end PressureSensor
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Sensors,"Eve Air Pressure");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
new PressureSensor();
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// Sometimes you need to access Characteristics from outside of the Service structure
|
||||
// in which they were created so you can read and/or modify them in other parts of
|
||||
// a sketch, such as from within the main Arduino loop().
|
||||
|
||||
// This sketch is basically the same as Tutorial Example 5, in which we created
|
||||
// two working LEDs attached to pins 16, and 17. However, in this sketch we will
|
||||
// create global pointers to the LED Services that we can then use in the main loop() to
|
||||
// do something unique.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// First we define our DEV_LED Service exactly the same as in Tutorial Example 5.
|
||||
// This Service contains a single Characteristic named "power" of type Chacracteristic::On
|
||||
|
||||
struct DEV_LED : Service::LightBulb {
|
||||
|
||||
int ledPin;
|
||||
SpanCharacteristic *power;
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// Next we create two pointers to the DEV_LED Service. These are created
|
||||
// outside of any class or function so they are globally-scoped and can be
|
||||
// accessed from anywhere else in this sketch.
|
||||
|
||||
// Note that there are just POINTERS to DEV_LED objects. The objects themselves
|
||||
// are not yet created.
|
||||
|
||||
DEV_LED *led16; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 16
|
||||
DEV_LED *led17; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 17
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
led16=new DEV_LED(16); // this is the key step - we SAVE the pointer returned by 'new DEV_LED(16)' in the global variable led16 created above
|
||||
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
led17=new DEV_LED(17); // also save the pointer to the second LED object using the global variable led17 created above
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
// Because the pointers led16 and led17 were created in the global scope, they will still exist even after setup() is completed.
|
||||
// This means we can use them to access the Characteristics within each of those Services.
|
||||
|
||||
// Here we access the power Characteristic of both Services and check to see if they are BOTH on, and if so,
|
||||
// we turn them both off and print a "power overload" message.
|
||||
|
||||
// Note how you can use all the same methods, such as getVal() and setVal(), just as you would do in the Service itself.
|
||||
// Caution: always use getVal(), not getNewVal(), which is only formally defined from within the Service update() method.
|
||||
|
||||
if(led16->power->getVal() && led17->power->getVal()){
|
||||
Serial.printf("Power overload! Can't have both LED's on at the same time. Turn off both LEDs...\n");
|
||||
led16->power->setVal(false);
|
||||
led17->power->setVal(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,112 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 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 Fading-LED Example. Demonstrates use of:
|
||||
//
|
||||
// LedPin::fade() and LedPin::fadeStatus() methods
|
||||
//
|
||||
// In this sketch we control a single dimmable LED using the Home App as well as a SpanButton.
|
||||
// You can control the brightness of the LED from the Home App, but the SpanButton only turns the
|
||||
// LED either fully on (if it's off) or fully off (if it's already on).
|
||||
//
|
||||
// Rather than set the LED to a specific brightness, this sketch uses the ESP32's hardware-based fading
|
||||
// functionality to fade the LED from one level to the next. We set the timing for each fade to be 2000 ms,
|
||||
// proportional to the difference between the current brightness and the desired brightness. This means it
|
||||
// will take a full 2 seconds to fade the LED from 0-100, but only 1 second to fade from half-brightness to
|
||||
// off.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct FadingLED : Service::LightBulb {
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
FadingLED(int _ledPin, int _buttonPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
level=new Characteristic::Brightness(0);
|
||||
ledPin=new LedPin(_ledPin);
|
||||
new SpanButton(_buttonPin);
|
||||
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
ledPin->fade(power->getNewVal()*level->getNewVal(),2000,LedPin::PROPORTIONAL); // use fade() to set new level; timing=2 seconds, proportional scale
|
||||
while(ledPin->fadeStatus()==LedPin::FADING); // wait until fading is completed
|
||||
|
||||
return(true);
|
||||
|
||||
}
|
||||
|
||||
void button(int pin, int pressType) override {
|
||||
|
||||
// Below we turn LED fully on or off depending on whether power is on
|
||||
// Unlike above, we will NOT wait for the fading to complete, but will return immediately
|
||||
|
||||
if(ledPin->fade(100-(power->getVal())*100,2000,LedPin::PROPORTIONAL)!=0) // use fade to either turn fully on or fully off; check return status to see if call was successful
|
||||
Serial.printf("Button Press Ignored\n");
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
|
||||
// Below we set power and level once fading from a button press is completed
|
||||
|
||||
if(ledPin->fadeStatus()==LedPin::COMPLETED){
|
||||
power->setVal(1-power->getVal());
|
||||
level->setVal(power->getVal()?100:0);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"Fading LED");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new FadingLED(26,4); // first argument is LED Pin, second argument is PushButton Pin
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 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 real-world Stepper Motors using HomeSpan's
|
||||
// StepperControl Class through the implemention of a Motorized Window Shade Accessory.
|
||||
|
||||
// The sketch below is based on the more-fully commented WindowShade Accessory included in
|
||||
// Tutorial Example 13 (this sketch only contains comments related to the use of the stepper motors).
|
||||
|
||||
// The Accessory we will use two stepper motors:
|
||||
|
||||
// * one motor to open/close the window shade, driven by an Adafruit TB6612 driver board (https://www.adafruit.com/product/2448)
|
||||
// * one motor to tilt the window shade slats, driven by a SparkFun A3967 driver board (https://www.sparkfun.com/products/12779)
|
||||
|
||||
// See HomeSpan's StepperControl documentation for details on the classes used to control these driver boards,
|
||||
// as well as for instructions on how you can easily extend StepperControl to create a custom driver for any board.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_WindowShade : Service::WindowCovering {
|
||||
|
||||
Characteristic::CurrentPosition currentPos{0,true};
|
||||
Characteristic::TargetPosition targetPos{0,true};
|
||||
Characteristic::CurrentHorizontalTiltAngle currentTilt{0,true};
|
||||
Characteristic::TargetHorizontalTiltAngle targetTilt{0,true};
|
||||
|
||||
StepperControl *mainMotor; // motor to open/close shade
|
||||
StepperControl *slatMotor; // motor to tilt shade slats
|
||||
|
||||
DEV_WindowShade(StepperControl *mainMotor, StepperControl *slatMotor) : Service::WindowCovering(){
|
||||
|
||||
this->mainMotor=mainMotor; // save pointers to the motors
|
||||
this->slatMotor=slatMotor;
|
||||
|
||||
mainMotor->setAccel(10,20); // set acceleration parameters for main motor
|
||||
mainMotor->setStepType(StepperControl::HALF_STEP); // set step type to HALF STEP for main motor
|
||||
|
||||
LOG0("Initial Open/Close Position: %d\n",currentPos.getVal());
|
||||
LOG0("Initial Slat Position: %d\n",currentTilt.getVal());
|
||||
|
||||
mainMotor->setPosition(currentPos.getVal()*20); // define initial position of main motor
|
||||
slatMotor->setPosition(currentTilt.getVal()*11.47); // define initial position of slat motor
|
||||
}
|
||||
|
||||
///////////
|
||||
|
||||
boolean update(){
|
||||
|
||||
if(targetPos.updated()){
|
||||
|
||||
// Move motor to absolute position, assuming 400 steps per revolution and 5 revolutions for full open/close travel,
|
||||
// for a total of 2000 steps of full travel. Specify that motor should enter the BRAKE state upon reaching to desired position.
|
||||
// Must multiply targetPos, which ranges from 0-100, by 20 to scale to number of motor steps needed
|
||||
|
||||
mainMotor->moveTo(targetPos.getNewVal()*20,5,StepperControl::BRAKE);
|
||||
LOG1("Setting Shade Position=%d\n",targetPos.getNewVal());
|
||||
}
|
||||
|
||||
if(targetTilt.updated()){
|
||||
|
||||
// Move motor to absolute position, assuming 2064 steps per revolution and 1/2 revolution for full travel of slat tilt in either direction
|
||||
// Must multiply targetPos, which ranges from -90 to 90, by 11.47 to scale number of motor steps needed
|
||||
// Note this driver board for this motor does not support a "short brake" state
|
||||
|
||||
slatMotor->moveTo(targetTilt.getNewVal()*11.47,5);
|
||||
LOG1("Setting Shade Position=%d\n",targetTilt.getNewVal());
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
///////////
|
||||
|
||||
void loop(){
|
||||
|
||||
// If the current window shade position or tilt does NOT equal the target position, BUT the motor has stopped moving,
|
||||
// we must have reached the target position, so set the current position equal to the target position
|
||||
|
||||
if(currentPos.getVal()!=targetPos.getVal() && !mainMotor->stepsRemaining()){
|
||||
currentPos.setVal(targetPos.getVal());
|
||||
LOG1("Main Motor Stopped at Shade Position=%d\n",currentPos.getVal());
|
||||
}
|
||||
|
||||
if(currentTilt.getVal()!=targetTilt.getVal() && !slatMotor->stepsRemaining()){
|
||||
currentTilt.setVal(targetTilt.getVal());
|
||||
LOG1("Slat Motor Stopped at Shade Tilt=%d\n",currentTilt.getVal());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::WindowCoverings,"Motorized Shade");
|
||||
|
||||
// MAKE SURE TO CHANGE THE PINS NUMBERS BELOW TO MATCH YOUR ESP32 DEVICE!!!
|
||||
// THE PINS NUMBER SPECIFIED IN THIS EXAMPLE WORK WITH THE ORIGINAL ESP32, BUT WILL LIKELY CRASH AN ESP32-S2, -S3, or -C3.
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new DEV_WindowShade(new Stepper_TB6612(23,32,22,14,33,27), new Stepper_A3967(18,21,5,4,19)); // instantiate drivers for each board and specify pins used on ESP32
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
203
ESP32/HomeSpan-master/examples/Other Examples/Pixel/Pixel.ino
Normal file
203
ESP32/HomeSpan-master/examples/Other Examples/Pixel/Pixel.ino
Normal file
@@ -0,0 +1,203 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 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 Addressable RGB LED Examples. Demonstrates use of:
|
||||
//
|
||||
// * HomeSpan Pixel Class that provides for control of single-wire addressable RGB and RGBW LEDs, such as the WS2812 and SK6812
|
||||
// * HomeSpan Dot Class that provides for control of two-wire addressable RGB LEDs, such as the APA102 and SK9822
|
||||
//
|
||||
// IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD
|
||||
//
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 26
|
||||
#define NEOPIXEL_RGBW_PIN 32
|
||||
#define DOTSTAR_DATA_PIN 33
|
||||
#define DOTSTAR_CLOCK_PIN 27
|
||||
#define DEVICE_SUFFIX ""
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 17
|
||||
#define NEOPIXEL_RGBW_PIN 38
|
||||
#define DOTSTAR_DATA_PIN 3
|
||||
#define DOTSTAR_CLOCK_PIN 7
|
||||
#define DEVICE_SUFFIX "-S2"
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 0
|
||||
#define NEOPIXEL_RGBW_PIN 3
|
||||
#define DOTSTAR_DATA_PIN 7
|
||||
#define DOTSTAR_CLOCK_PIN 2
|
||||
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#endif
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct NeoPixel_RGB : Service::LightBulb { // Addressable single-wire RGB LED Strand (e.g. NeoPixel)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Pixel *pixel;
|
||||
int nPixels;
|
||||
|
||||
NeoPixel_RGB(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
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%
|
||||
pixel=new Pixel(pin); // creates Pixel RGB LED on specified pin
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float h=H.getNewVal<float>(); // range = [0,360]
|
||||
float s=S.getNewVal<float>(); // range = [0,100]
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
|
||||
Pixel::Color color;
|
||||
|
||||
pixel->set(color.HSV(h*p, s*p, v*p),nPixels); // sets all nPixels to the same HSV color
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct NeoPixel_RGBW : Service::LightBulb { // Addressable single-wire RGBW LED Strand (e.g. NeoPixel)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Characteristic::ColorTemperature T{140,true};
|
||||
Pixel *pixel;
|
||||
int nPixels;
|
||||
|
||||
NeoPixel_RGBW(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
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%
|
||||
pixel=new Pixel(pin,PixelType::GRBW); // creates Pixel RGBW LED on specified pin (with order of colors chnanged to reflect this specific NeoPixel device)
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
float t=T.getNewVal<float>(); // range = [140,500] (140=coldest, 500=warmest)
|
||||
|
||||
float hue=240-(t-140)/3; // add in a splash of color between blue and green to simulated change of color temperature
|
||||
|
||||
// Pixel::Color color; // if static HSV method is used (below), there is no need to first create a Color object
|
||||
|
||||
pixel->set(pixel->HSV(hue, 100, v*p, v*p),nPixels); // sets all nPixels to the same HSV color (note use of static method pixel->HSV, instead of defining and setting Pixel::Color)
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED Strand (e.g. DotStar)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Dot *pixel;
|
||||
int nPixels;
|
||||
|
||||
DotStar_RGB(uint8_t dataPin, uint8_t clockPin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
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%
|
||||
pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
update(); // call second update() a second time - DotStar seems to need to be "refreshed" upon start-up
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float h=H.getNewVal<float>(); // range = [0,360]
|
||||
float s=S.getNewVal<float>(); // range = [0,100]
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
|
||||
Dot::Color color[nPixels]; // create an arrary of Colors
|
||||
|
||||
float hueStep=360.0/nPixels; // step size for change in hue from one pixel to the next
|
||||
|
||||
for(int i=0;i<nPixels;i++)
|
||||
color[i].HSV(h+i*hueStep,s,100,v*p); // create spectrum of all hues starting with specified Hue; use current-limiting parameter (4th argument) to control overall brightness, instead of PWM
|
||||
|
||||
pixel->set(color,nPixels); // set the colors according to the array
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"Pixel LEDS" DEVICE_SUFFIX);
|
||||
|
||||
SPAN_ACCESSORY(); // create Bridge (note this sketch uses the SPAN_ACCESSORY() macro, introduced in v1.5.1 --- see the HomeSpan API Reference for details on this convenience macro)
|
||||
|
||||
SPAN_ACCESSORY("Neo RGB");
|
||||
new NeoPixel_RGB(NEOPIXEL_RGB_PIN,8); // create 8-LED NeoPixel RGB Strand with full color control
|
||||
|
||||
SPAN_ACCESSORY("Neo RGBW");
|
||||
new NeoPixel_RGBW(NEOPIXEL_RGBW_PIN,60); // create 60-LED NeoPixel RGBW Strand with simulated color temperature control
|
||||
|
||||
SPAN_ACCESSORY("Dot RGB");
|
||||
new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand displaying a spectrum of colors and using the current-limiting feature of DotStars to create flicker-free dimming
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void loop() {
|
||||
homeSpan.poll();
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
@@ -0,0 +1,131 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/////////////////////// PIXEL TESTER //////////////////////////
|
||||
|
||||
// This sketch is designed to help identify the proper settings to use for a NeoPixel, NeoPixel Strip,
|
||||
// or any device containing one or more single-wire addressable RGB or RGBW LEDs (the "Pixel Device").
|
||||
|
||||
// Before compiling, set PIXEL_PIN to the ESP32 pin that is connected to your Pixel Device, and set NPIXELS to
|
||||
// the numnber of Pixels in the Pixel Device. Note that for some strips a single chip controls more than one LED,
|
||||
// in which case NPIXELS should be set to the number of controlling chips, NOT the number of LEDs.
|
||||
|
||||
// To start, the second argument of the Pixel constructor for the testPixel object below should remain
|
||||
// set to PixelType::RGBW
|
||||
|
||||
// When run, the sketch will repeatedly cycle colors by setting ALL pixels in the device first to RED, then GREEN,
|
||||
// followed by BLUE, and then finally WHITE. After a short pause, the cycle repeats.
|
||||
|
||||
// For each color the brightness will increase from 0 through MAX_BRIGHTNESS, and then back to 0. You can change
|
||||
// MAX_BRIGHTNESS to something lower than 255 if you want to limit how bright the pixels get.
|
||||
|
||||
// For Pixel Devices with more than one pixel, diagnostics are as follows:
|
||||
//
|
||||
// * If all 4 colors repeatedly flash in the order expected, this means the base setting of PixelType::RGBW is correct!
|
||||
//
|
||||
// * If instead of each pixel being set to the same color, the pixels in the strip each light up with a different color
|
||||
// (or no color at all), this means you have an RGB LED, not an RGBW LED. Change the second parameter of the constructor
|
||||
// to PixelType::RGB and re-run the sketch.
|
||||
//
|
||||
// * If all of the pixels are being set to the same color, but the sequence is NOT in the order RED, GREEN, BLUE, change
|
||||
// the second parameter of the constructor so that the order of the PixelType colors match the sequence of the colors
|
||||
// that appear on the Pixel Device. For example, if your RGBW Pixel Device flashes GREEN, RED, BLUE, and than WHITE, use
|
||||
// PixelType::GRBW.
|
||||
|
||||
// For Pixel Devices with only a single pixel, diagnostics are as follows:
|
||||
|
||||
// * If all 4 colors repeatedly flash in the order expected, this means the base setting of PixelType::RGBW is correct!
|
||||
//
|
||||
// * If the pixel does not light at all when set to WHITE this means you have an RGB LED, not an RGBW LED. Change the
|
||||
// second parameter of the constructor to PixelType::RGB and re-run the sketch.
|
||||
//
|
||||
// * If all of the pixels are being set to the same color, but the sequence is NOT in the order RED, GREEN, BLUE, change
|
||||
// the second parameter of the constructor so that the order of the PixelType colors match the sequence of the colors
|
||||
// that appear on the Pixel Device. For example, if your RGB Pixel Device flashes GREEN, RED, and then BLUE, use
|
||||
// PixelType::GRB.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
#define MAX_BRIGHTNESS 255 // maximum brightness when flashing RGBW [0-255]
|
||||
|
||||
#define PIXEL_PIN 26 // set this to whatever pin you are using - note pin cannot be "input only"
|
||||
#define NPIXELS 8 // set to number of pixels in strip
|
||||
|
||||
Pixel testPixel(PIXEL_PIN, PixelType::RGBW); // change the second argument until device operates with correct colors
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.printf("\n\nPixel Test on pin %d with %d pixels\n\n",PIXEL_PIN,NPIXELS);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void flashColor(boolean r, boolean g, boolean b, boolean w){
|
||||
|
||||
for(int i=0;i<MAX_BRIGHTNESS;i++){
|
||||
testPixel.set(Pixel::RGB(i*r,i*g,i*b,i*w),NPIXELS);
|
||||
delay(4);
|
||||
}
|
||||
|
||||
for(int i=MAX_BRIGHTNESS;i>=0;i--){
|
||||
testPixel.set(Pixel::RGB(i*r,i*g,i*b,i*w),NPIXELS);
|
||||
delay(4);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
Serial.printf("Red...");
|
||||
flashColor(1,0,0,0);
|
||||
|
||||
Serial.printf("Green...");
|
||||
flashColor(0,1,0,0);
|
||||
|
||||
Serial.printf("Blue...");
|
||||
flashColor(0,0,1,0);
|
||||
|
||||
if(testPixel.isRGBW()){
|
||||
Serial.printf("White...");
|
||||
flashColor(0,0,0,1);
|
||||
}
|
||||
|
||||
Serial.printf("Pausing.\n");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,388 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 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 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to implement a Web Server alongside //
|
||||
// of HomeSpan to create a Programmable Hub serving up to //
|
||||
// 12 Configurable Lights. Allows for dynamic changes //
|
||||
// to Accessories without needing to reboot //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include <WebServer.h> // include WebServer library
|
||||
|
||||
WebServer webServer(80); // create WebServer on port 80
|
||||
|
||||
#define MAX_LIGHTS 12
|
||||
#define MAX_NAME_LENGTH 32
|
||||
#define HUB_NAME "lighthub"
|
||||
|
||||
enum colorType_t : uint8_t {
|
||||
NO_COLOR,
|
||||
TEMPERATURE_ONLY,
|
||||
FULL_RGB
|
||||
};
|
||||
|
||||
uint32_t aidStore=2; // keep track of unique AID numbers - start with AID=2
|
||||
|
||||
struct lightData_t {
|
||||
char name[MAX_NAME_LENGTH+1]="";
|
||||
uint32_t aid=0;
|
||||
boolean isDimmable:1;
|
||||
colorType_t colorType:2;
|
||||
} lightData[MAX_LIGHTS];
|
||||
|
||||
nvs_handle savedData;
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
size_t len;
|
||||
nvs_open("SAVED_DATA",NVS_READWRITE,&savedData); // open a new namespace called SAVED_DATA in the NVS
|
||||
|
||||
if(!nvs_get_blob(savedData,"LIGHTDATA",NULL,&len)) // if LIGHTDATA data found
|
||||
nvs_get_blob(savedData,"LIGHTDATA",&lightData,&len); // retrieve data
|
||||
|
||||
nvs_get_u32(savedData,"AID",&aidStore); // get AID, if it exists
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.setHostNameSuffix(""); // use null string for suffix (rather than the HomeSpan device ID)
|
||||
homeSpan.setPortNum(1201); // change port number for HomeSpan so we can use port 80 for the Web Server
|
||||
homeSpan.setWifiCallback(setupWeb); // need to start Web Server after WiFi is established
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Light Hub",HUB_NAME);
|
||||
|
||||
new SpanAccessory(1); // here we specified the AID=1 for clarity (it would default to 1 anyway if left blank)
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Model("HomeSpan Programmable Hub");
|
||||
new Characteristic::AccessoryFlags();
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){ // create Light Accessories based on saved data
|
||||
if(lightData[i].aid)
|
||||
addLight(i);
|
||||
}
|
||||
|
||||
new SpanUserCommand('a',"<name> - add non-dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,false,NO_COLOR);});
|
||||
new SpanUserCommand('A',"<name> - add dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,true,NO_COLOR);});
|
||||
new SpanUserCommand('t',"<name> - add non-dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,false,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('T',"<name> - add dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,true,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('r',"<name> - add non-dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,false,FULL_RGB);});
|
||||
new SpanUserCommand('R',"<name> - add dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,true,FULL_RGB);});
|
||||
|
||||
new SpanUserCommand('l'," - list all light accessories",listAccessories);
|
||||
new SpanUserCommand('d',"<index> - delete a light accessory with index=<index>",[](const char *buf){deleteAccessory(atoi(buf+1));});
|
||||
new SpanUserCommand('D'," - delete ALL light accessories",deleteAllAccessories);
|
||||
new SpanUserCommand('u',"- update accessories database",updateAccessories);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void loop(){
|
||||
homeSpan.poll();
|
||||
webServer.handleClient(); // handle incoming web server traffic
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void addLight(int index){
|
||||
|
||||
Serial.printf("Adding Light Accessory: Name='%s' Dimmable=%s Color=%s\n",
|
||||
lightData[index].name,lightData[index].isDimmable?"YES":"NO",lightData[index].colorType==NO_COLOR?"NONE":(lightData[index].colorType==TEMPERATURE_ONLY?"TEMPERATURE_ONLY":"FULL_RGB"));
|
||||
|
||||
new SpanAccessory(lightData[index].aid);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(lightData[index].name);
|
||||
char sNum[32];
|
||||
sprintf(sNum,"Light-%02d",index);
|
||||
new Characteristic::SerialNumber(sNum);
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
if(lightData[index].isDimmable)
|
||||
new Characteristic::Brightness(100,true);
|
||||
if(lightData[index].colorType==TEMPERATURE_ONLY)
|
||||
new Characteristic::ColorTemperature(200,true);
|
||||
if(lightData[index].colorType==FULL_RGB){
|
||||
new Characteristic::Hue(0,true);
|
||||
new Characteristic::Saturation(0,true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
int addLight(const char *name, boolean isDimmable, colorType_t colorType){
|
||||
|
||||
int index=0;
|
||||
for(index=0;index<MAX_LIGHTS && lightData[index].aid;index++);
|
||||
|
||||
if(index==MAX_LIGHTS){
|
||||
Serial.printf("Can't add Light Accessory - maximum number of %d are already defined.\n",MAX_LIGHTS);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int n=strncpy_trim(lightData[index].name,name,sizeof(lightData[index].name));
|
||||
|
||||
if(n==1){
|
||||
Serial.printf("Can't add Light Accessory without a name specified.\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if(n>sizeof(lightData[index].name))
|
||||
Serial.printf("Warning - name trimmed to max length of %d characters.\n",MAX_NAME_LENGTH);
|
||||
|
||||
lightData[index].isDimmable=isDimmable;
|
||||
lightData[index].colorType=colorType;
|
||||
lightData[index].aid=aidStore++;
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_set_u32(savedData,"AID",aidStore);
|
||||
nvs_commit(savedData);
|
||||
|
||||
addLight(index);
|
||||
return(index);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
size_t strncpy_trim(char *dest, const char *src, size_t dSize){
|
||||
|
||||
while(*src==' ') // skip over any leading spaces
|
||||
src++;
|
||||
|
||||
size_t sLen=strlen(src); // string length of src after skipping over leading spaces
|
||||
while(sLen>0 && src[sLen-1]==' ') // shorten length to remove trailing spaces
|
||||
sLen--;
|
||||
|
||||
size_t sSize=sLen+1; // add room for null terminator
|
||||
|
||||
if(dest!=NULL)
|
||||
*stpncpy(dest,src,(dSize<sSize?dSize:sSize)-1)='\0';
|
||||
|
||||
return(sSize); // return total size needed for entire trimmed string, including null terminator
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAccessory(int index){
|
||||
|
||||
if(index<0 || index>=MAX_LIGHTS){
|
||||
Serial.printf("Invalid Light Accessory index - must be between 0 and %d.\n",MAX_LIGHTS-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(homeSpan.deleteAccessory(lightData[index].aid)){ // if deleteAccessory() is true, a match has been found
|
||||
Serial.printf("Deleting Light Accessory: Name='%s'\n",lightData[index].name);
|
||||
|
||||
lightData[index].aid=0;
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
} else {
|
||||
Serial.printf("Nothing to delete - there is no Light Accessory at index=%d.\n",index);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAllAccessories(const char *buf){
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
homeSpan.deleteAccessory(lightData[i].aid);
|
||||
lightData[i].aid=0;
|
||||
}
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
Serial.printf("All Light Accessories deleted!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void updateAccessories(const char *buf){
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
Serial.printf("Accessories Database updated. New configuration number broadcasted...\n");
|
||||
else
|
||||
Serial.printf("Nothing to update - no changes were made!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void listAccessories(const char *buf){
|
||||
|
||||
Serial.printf("\nIndex Dimmable Color Name\n");
|
||||
Serial.printf("----- -------- ----- ");
|
||||
|
||||
for(int i=0;i<MAX_NAME_LENGTH;i++)
|
||||
Serial.printf("-");
|
||||
Serial.printf("\n");
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid)
|
||||
Serial.printf("%5d %8s %5s %-s\n",i,lightData[i].isDimmable?"YES":"NO",lightData[i].colorType==NO_COLOR?"NONE":(lightData[i].colorType==TEMPERATURE_ONLY?"TEMP":"RGB"),lightData[i].name);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void setupWeb(){
|
||||
|
||||
Serial.printf("Starting Light Server Hub at %s.local\n\n",HUB_NAME);
|
||||
webServer.begin();
|
||||
|
||||
webServer.on("/", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title>";
|
||||
response += "<style>table, th, td {border: 1px solid black; border-collapse: collapse;} th, td { padding: 5px; text-align: center; } </style></head>\n";
|
||||
response += "<body><h2>HomeSpan Lights</h2>";
|
||||
response += "<form action='/addLight' method='get'>";
|
||||
response += "<table><tr><th style='text-align:left;'>Accessory</th><th>Dim?</th><th>Color Control</th><th>Action</th></tr>";
|
||||
|
||||
int openSlots=MAX_LIGHTS;
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid){
|
||||
response += "<tr><td style='text-align:left;'>" + String(lightData[i].name) + "</td>";
|
||||
response += "<td><input type='checkbox' disabled " + String(lightData[i].isDimmable?"checked>":">") + "</td>";
|
||||
response += "<td><input type='radio' disabled " + String(lightData[i].colorType==NO_COLOR?"checked>":">") + " NONE ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==TEMPERATURE_ONLY?"checked>":">") + " TEMP ONLY ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==FULL_RGB?"checked>":">") + " FULL COLOR </td>";
|
||||
response += "<td><button type='button' onclick=\"document.location='/deleteLight?index=" + String(i) + "'\">Delete Light</button></td>";
|
||||
response += "</tr>";
|
||||
openSlots--;
|
||||
}
|
||||
}
|
||||
|
||||
response += "<tr><td style='text-align:left;'><input type='text' name='name' required placeholder='Type accessory name here...' size='"
|
||||
+ String(MAX_NAME_LENGTH) + "' maxlength='" + String(MAX_NAME_LENGTH) + "'></td>";
|
||||
response += "<td><input type='checkbox' name='isDimmable'></td>";
|
||||
response += "<td><input type='radio' checked name='colorType' for='no_color' value='" + String(NO_COLOR) + "'><label for='no_color'> NONE </label>";
|
||||
response += "<input type='radio' name='colorType' for='temp_only' value='" + String(TEMPERATURE_ONLY) + "'><label for='temp_only'> TEMP ONLY </label>";
|
||||
response += "<input type='radio' name='colorType' for='full_rgb' value='" + String(FULL_RGB) + "'><label for='full_rgb'> FULL COLOR </label></td>";
|
||||
response += "<td><input type='submit' value='Add Light'" + String(openSlots?"":" disabled") + "></td>";
|
||||
response += "</tr>";
|
||||
|
||||
response += "</table>";
|
||||
response += "</form>";
|
||||
|
||||
if(!openSlots)
|
||||
response += "<p>Can't add any more Light Accessories. Max="+ String(MAX_LIGHTS) + "</p>";
|
||||
|
||||
response += "<p>Press here to delete ALL Light Accessories: <button type='button' onclick=\"document.location='/deleteAll'\">Delete All Lights</button></p>";
|
||||
response += "<p>Press here to update the Home App when finished making changes: <button type='button' onclick=\"document.location='/update'\">Upddate HomeKit</button></p>";
|
||||
|
||||
response += "</body></html>";
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteLight", []() {
|
||||
|
||||
int index=atoi(webServer.arg(0).c_str());
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting Light Accessory '" + String(lightData[index].name) + "'...</body></html>";
|
||||
|
||||
deleteAccessory(index);
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteAll", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting All Light Accessories...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
deleteAllAccessories("");
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/update", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
response += "Accessories Database updated. New configuration number broadcasted...";
|
||||
else
|
||||
response += "Nothing to update - no changes were made...";
|
||||
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/addLight", []() {
|
||||
|
||||
colorType_t colorType=NO_COLOR;
|
||||
boolean isDimmable=false;
|
||||
int iName=-1;
|
||||
|
||||
for(int i=0;i<webServer.args();i++){
|
||||
if(!webServer.argName(i).compareTo(String("colorType")))
|
||||
colorType=(colorType_t)webServer.arg(i).toInt();
|
||||
else if(!webServer.argName(i).compareTo(String("isDimmable")))
|
||||
isDimmable=true;
|
||||
else if(!webServer.argName(i).compareTo(String("name")))
|
||||
iName=i;
|
||||
}
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
if(iName!=-1){
|
||||
int index=addLight(webServer.arg(iName).c_str(),isDimmable,colorType);
|
||||
response += "Adding Light Accessory '" + String(lightData[index].name) + "'";
|
||||
} else
|
||||
response += "Error - bad URL request";
|
||||
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h" // include the HomeSpan library
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200); // start the Serial interface
|
||||
Serial.flush();
|
||||
delay(1000); // wait for interface to flush
|
||||
|
||||
Serial.print("\n\nHomeSpan RF Transmitter Example\n\n");
|
||||
|
||||
RFControl rf(13); // create an instance of RFControl with signal output to pin 13 of the ESP32
|
||||
|
||||
rf.clear(); // clear the pulse train memory buffer
|
||||
|
||||
rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses
|
||||
rf.add(5000,5000);
|
||||
rf.add(5000,10000); // double duration of final low period
|
||||
|
||||
Serial.print("Starting 4 cycles of three 500 ms on pulses...");
|
||||
|
||||
rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds
|
||||
|
||||
Serial.print("Done!\n");
|
||||
|
||||
delay(2000);
|
||||
|
||||
rf.clear();
|
||||
|
||||
for(int i=1000;i<10000;i+=1000)
|
||||
rf.add(i,10000-i);
|
||||
rf.add(10000,10000);
|
||||
|
||||
Serial.print("Starting 3 cycles of 100-1000 ms pulses...");
|
||||
|
||||
rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds
|
||||
|
||||
Serial.print("Done!\n");
|
||||
|
||||
Serial.print("\nEnd Example");
|
||||
|
||||
} // end of setup()
|
||||
|
||||
void loop(){
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,121 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to use SpanPoint() to implement //
|
||||
// two remote temperature sensors on separate ESP32 //
|
||||
// devices. //
|
||||
// //
|
||||
// This sketch is for the MAIN DEVICE that contains //
|
||||
// all the usual HomeSpan logic, plus two instances //
|
||||
// of SpanPoint to read temperatures from two other //
|
||||
// remote devices. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
struct RemoteTempSensor : Service::TemperatureSensor {
|
||||
|
||||
SpanCharacteristic *temp;
|
||||
SpanCharacteristic *fault;
|
||||
SpanPoint *remoteTemp;
|
||||
const char *name;
|
||||
float temperature;
|
||||
|
||||
RemoteTempSensor(const char *name, const char*macAddress) : Service::TemperatureSensor(){
|
||||
|
||||
this->name=name;
|
||||
|
||||
temp=new Characteristic::CurrentTemperature(-10.0); // set initial temperature
|
||||
temp->setRange(-50,100); // expand temperature range to allow negative values
|
||||
|
||||
fault=new Characteristic::StatusFault(1); // set initial state = fault
|
||||
|
||||
remoteTemp=new SpanPoint(macAddress,0,sizeof(float)); // create a SpanPoint with send size=0 and receive size=sizeof(float)
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(remoteTemp->get(&temperature)){ // if there is data from the remote sensor
|
||||
temp->setVal(temperature); // update temperature
|
||||
fault->setVal(0); // clear fault
|
||||
|
||||
LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32);
|
||||
|
||||
} else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault
|
||||
fault->setVal(1); // set fault state
|
||||
LOG1("Sensor %s update: FAULT\n",name);
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"Sensor Hub");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Indoor Temp");
|
||||
new RemoteTempSensor("Device 1","AC:67:B2:77:42:20"); // pass MAC Address of Remote Device
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Outdoor Temp");
|
||||
new RemoteTempSensor("Device 2","84:CC:A8:11:B4:84"); // pass MAC Address of Remote Device
|
||||
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to use SpanPoint() to implement //
|
||||
// two remote temperature sensors on separate ESP32 //
|
||||
// devices. //
|
||||
// //
|
||||
// This sketch is for the REMOTE DEVICES. They are //
|
||||
// very simple and don't need any of the normal //
|
||||
// HomeSpan logic (except for SpanPoint). //
|
||||
// //
|
||||
// Note this sketch only SIMULATES a temperature //
|
||||
// sensor by slowly setting the temperature from //
|
||||
// -30.0 to 35.0 C in steps of 0.5 C. The sketch //
|
||||
// does not contain logic for an actual physical //
|
||||
// temperature sensor. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
float temperature=-10.0;
|
||||
SpanPoint *mainDevice;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.printf("\n\nThis is a REMOTE Device with MAC Address = %s\n",WiFi.macAddress().c_str());
|
||||
Serial.printf("NOTE: This MAC Address must be entered into the corresponding SpanPoint() call of the MAIN Device.\n\n");
|
||||
|
||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||
|
||||
mainDevice=new SpanPoint("84:CC:A8:11:B4:84",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
boolean success = mainDevice->send(&temperature); // this will show as success as long as the MAIN DEVICE is running
|
||||
Serial.printf("Send %s\n",success?"Succeded":"Failed");
|
||||
temperature+=0.5;
|
||||
if(temperature>35.0)
|
||||
temperature=-30.0;
|
||||
|
||||
delay(20000);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP8266
|
||||
#error ERROR: THIS SKETCH IS DESIGNED FOR ESP8266 MICROCONTROLLERS!
|
||||
#endif
|
||||
|
||||
// *** THIS SKETCH IS FOR AN ESP8266, NOT AN ESP32 *** //
|
||||
|
||||
// This sketch is similar to HomeSpan's RemoteDevice.ino example (designed for an ESP32 running HomeSpan) in which we simulate
|
||||
// a Remote Temperature Sensor using HomeSpan's SpanPoint class. However, since neither HomeSpan nor SpanPoint is designed to
|
||||
// run on an ESP8266, we will implement the BASIC communication functionality of SpanPoint by directly calling the equivalent
|
||||
// ESP-NOW commands that are supported by the ESP8266. This sketch does NOT seek to replicate all of SpanPoint's features, and
|
||||
// does not include automatic channel calibration or queue management.
|
||||
|
||||
// Start by including the following ESP8266 libraries
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <espnow.h>
|
||||
#include <Crypto.h> // this library is needed to implement the hash-code process SpanPoint uses to generate ESP-NOW encryption keys
|
||||
|
||||
float temp=-10.0; // this global variable represents our "simulated" temperature (in degrees C)
|
||||
|
||||
// Below we encode the MAC Address of the Main ESP32 Device running HomeSpan to which this ESP8266 device will connect
|
||||
|
||||
// IMPORTANT: ESP32 devices have TWO MAC Addresses. One is used when the ESP32 is operating in Station (STA) mode. It is the address returned
|
||||
// by the WiFi.macAddress() function. The other is used when the ESP32 is operating in Access Point (AP) mode. This address is returned by the
|
||||
// WiFi.softAPmacAddress() function. HomeSpan normally operates the ESP32 with both modes (STA+AP), so both MAC Addresses are active.
|
||||
|
||||
// On ESP32 devices, ESP-NOW seems to work fine when each device sends data to other devices via their STA MAC Address. The same is true for ESP8266
|
||||
// devices sending data to an ESP32 device via ESP-NOW with one critical exception: Once the ESP32 connects (via STA mode) to a WiFi network, which it must
|
||||
// do to run HomeSpan, for some reason ESP8266 devices can no longer send data via ESP-NOW to the ESP32 using its STA MAC Address.
|
||||
|
||||
// The solution is to instead have the ESP8266 send data via ESP-NOW to the ESP32's AP MAC Address. This seems to work regardless of whether or not
|
||||
// the ESP32 is connected to a central WiFi newtork. To support such use on the ESP32, the SpanPoint constructor includes a fifth, optional parameter
|
||||
// called "useAPaddress". When creating SpanPoint links of the ESP32 using HomeSpan, set useAPaddress to TRUE if the Remote Device SpanPoint is connecting
|
||||
// to is an ESP8266. Set "useAPaddress" to FALSE (or leave unspecified, since FALSE is the default) if the Remote Device is an ESP32.
|
||||
|
||||
// When HomeSpan first starts (and whenever you type 'i' into the CLI), the Serial Monitor will display the details of each SpanPoint object you instantiated
|
||||
// in your ESP32 sketch. This output includes the MAC Address at which SpanPoint will be listening for incoming data from Remote Devices. The MAC Address
|
||||
// shown for the instance of SpanPoint corresponding to this Remote Deivce (i.e. this sketch) is the MAC Address you should use below.
|
||||
|
||||
uint8_t main_mac[6]={0x84,0xCC,0xA8,0x11,0xB4,0x85}; // this is the **AP MAC Address** of the Main Device running HomeSpan on an ESP32 as reported in the HomeSpan Serial Monitor
|
||||
|
||||
// Next we create a simple, standard ESP-NOW callback function to report on the status of each data transmission
|
||||
|
||||
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
|
||||
Serial.printf("Last Packet Send Status: %s\n",sendStatus==0?"Success":"Fail");
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.printf("\nMAC Address: %s\n",WiFi.macAddress().c_str()); // enter this MAC address as the first argument of the matching SpanPoint object on the ESP32 running HomeSpan
|
||||
|
||||
WiFi.mode(WIFI_STA); // set the mode to Station
|
||||
wifi_set_channel(6); // you also need to manually set the channel to match whatever channel is used by the ESP32 after it connects to your WiFi network
|
||||
|
||||
// Hint: As an alterntive, you can add code to this sketch to connect to the same WiFi network that HomeSpan uses. Though this sketch won't make any use of that WiFi network,
|
||||
// by establishing the connection the ESP8266 automatically configures the channel, which will now match the ESP32.
|
||||
|
||||
// Next, initialize ESP-NOW
|
||||
|
||||
if (esp_now_init() != 0) {
|
||||
Serial.println("Error initializing ESP-NOW");
|
||||
return;
|
||||
}
|
||||
|
||||
// SpanPoint uses ESP-NOW encryption for all communication. This encrpytion is based on two 16-byte keys: a local master key (LMK) and a primary master key (PMK). To generate
|
||||
// these keys, SpanPoint takes a text-based password (the default is the word "HomeSpan"), creates a 32 byte (256 bit) hash of the text (using the SHA256 method), and uses
|
||||
// the first 16 bytes as the LMK and the last 16 bytes as the PMK. This is easily replicated as follows:
|
||||
|
||||
uint8_t hash[32]; // create space to store as 32-byte hash code
|
||||
char password[]="HomeSpan"; // specify the password
|
||||
|
||||
experimental::crypto::SHA256::hash(password,strlen(password),hash); // create the hash code to be used further below
|
||||
|
||||
esp_now_register_send_cb(OnDataSent); // register the callback function we defined above
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); // set the role of this device to be a controller (i.e. it sends data to the ESP32)
|
||||
|
||||
esp_now_set_kok(hash+16,16); // next we set the PMK. For some reason this is called KOK on the ESP8266. Note you must set the PMK BEFORE adding any peers
|
||||
|
||||
esp_now_add_peer(main_mac, ESP_NOW_ROLE_COMBO, 0, hash, 16); // now we add in the peer, set its role, and specify the LMK
|
||||
|
||||
// Hint: The third argument above is the WiFi Channel. However, this is only a reference number stored by ESP-NOW. ESP-NOW does NOT actually set the channel for you.
|
||||
// We already set the WiFi channel above. To make things easier, ESP-NOW allows you to set the channel as zero, which means ESP-NOW should expect the channel to be whatever was
|
||||
// already set for the WiFi controller. Recommend always setting this to zero to avoid having any mismatches if you instead specified a real channel.
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void loop() {
|
||||
|
||||
Serial.printf("Sending Temperature: %f\n",temp);
|
||||
esp_now_send(main_mac, (uint8_t *)&temp, sizeof(temp)); // Send the Data to the Main Device!
|
||||
|
||||
temp+=0.5; // increment the "temperature" by 0.5 C
|
||||
if(temp>35.0)
|
||||
temp=-10.0;
|
||||
|
||||
delay(5000); // wait 5 seconds before sending another update
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// This sketch is for a Remote Temperature Sensor to //
|
||||
// be used in conjunction with the "MainDevice.ino" //
|
||||
// sketch running on a separate ESP32 //
|
||||
// //
|
||||
// The purpose of these sketches is to demonstrate //
|
||||
// how to use SpanPoint() to communication between //
|
||||
// a remote ESP32 device that takes measurements from //
|
||||
// a sensor, and a separate "main" ESP32 device that //
|
||||
// is running the full HomeSpan code, and thus //
|
||||
// connects to HomeKit. //
|
||||
// //
|
||||
// This sketch implements an Adafruit ADT7410 I2C //
|
||||
// Temperature Sensor. If you don't have such a //
|
||||
// device, please use the sketch "RemoteDevice.ino" //
|
||||
// instead. That sketch SIMULATES a temperature //
|
||||
// sensor and therefore allows you to learn how //
|
||||
// SpanPoint() works even though the temperature data //
|
||||
// itself isn't real. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include <Wire.h> // include the I2C library
|
||||
|
||||
#define DIAGNOSTIC_MODE
|
||||
|
||||
#define SAMPLE_TIME 30000 // Time between temperature samples (in milliseconds)
|
||||
#define I2C_ADD 0x48 // I2C Address to use for the Adafruit ADT7410
|
||||
|
||||
SpanPoint *mainDevice;
|
||||
|
||||
void setup() {
|
||||
|
||||
setCpuFrequencyMhz(80); // reduce CPU frequency to save battery power
|
||||
|
||||
#if defined(DIAGNOSTIC_MODE)
|
||||
homeSpan.setLogLevel(1);
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.printf("Starting Remote Temperature Sensor. MAC Address of this device = %s\n",WiFi.macAddress().c_str());
|
||||
#endif
|
||||
|
||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||
|
||||
mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
|
||||
Wire.begin(); // start I2C in Controller Mode
|
||||
|
||||
#if defined(DIAGNOSTIC_MODE)
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x0B); // ADT7410 Identification Register
|
||||
Wire.endTransmission(0); // transmit and leave in restart mode to allow reading
|
||||
Wire.requestFrom(I2C_ADD,1); // request read of single byte
|
||||
uint8_t id = Wire.read(); // receive a byte
|
||||
LOG1("Configuring Temperature Sensor ADT7410 version 0x%02X with address 0x%02X.\n",id,I2C_ADD); // initialization message
|
||||
#endif
|
||||
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x03); // ADT740 Configuration Register
|
||||
Wire.write(0xC0); // set 16-bit temperature resolution, 1 sample per second
|
||||
Wire.endTransmission(); // transmit
|
||||
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x00); // ADT7410 2-byte Temperature
|
||||
Wire.endTransmission(0); // transmit and leave in restart mode to allow reading
|
||||
Wire.requestFrom(I2C_ADD,2); // request read of two bytes
|
||||
|
||||
int16_t iTemp = ((int16_t)Wire.read()<<8)+Wire.read();
|
||||
float temperature = iTemp/128.0;
|
||||
|
||||
boolean success = mainDevice->send(&temperature); // send temperature to main device
|
||||
|
||||
LOG1("Send temp update of %0.2f F: %s\n",temperature*9/5+32,success?"Succeded":"Failed");
|
||||
|
||||
esp_deep_sleep(SAMPLE_TIME*1000); // enter deep sleep mode -- reboot after resuming
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
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
|
||||
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
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()
|
||||
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h" // include the HomeSpan library
|
||||
|
||||
struct TableLamp : Service::LightBulb{
|
||||
|
||||
int lampPin; // store the pin number connected to a hypothetical relay that turns the Table Lamp on/off
|
||||
SpanCharacteristic *lampPower; // store a reference to the On Characteristic
|
||||
|
||||
TableLamp(int lampPin) : Service::LightBulb(){ // constructor() method for TableLamp defined with one parameter. Note we also call the constructor() method for the LightBulb Service.
|
||||
|
||||
lampPower=new Characteristic::On(); // instantiate the On Characteristic and save it as lampPower
|
||||
this->lampPin=lampPin; // save the pin number for the hypothetical relay
|
||||
pinMode(lampPin,OUTPUT); // configure the pin as an output using the standard Arduino pinMode function
|
||||
|
||||
} // end constructor()
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(lampPin,lampPower->getNewVal()); // use standard Arduino digitalWrite function to change the ledPin to either high or low based on the value requested by HomeKit
|
||||
|
||||
return(true); // return true to let HomeKit (and the Home App Client) know the update was successful
|
||||
|
||||
} // end update()
|
||||
|
||||
};
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200); // start the Serial interface
|
||||
|
||||
homeSpan.begin(); // initialize HomeSpan
|
||||
|
||||
new SpanAccessory(); // Table Lamp Accessory
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
new Characteristic::Identify(); // HAP requires the Accessory Information Service to include the Identify Characteristic
|
||||
|
||||
new TableLamp(17); // instantiate the TableLamp Service (defined below) with lampPin set to 17
|
||||
|
||||
} // end of setup()
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,216 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-2022 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 Television Service Example
|
||||
|
||||
// Covers all Characteristics of the Television Service that appear to
|
||||
// be supported in the iOS 15 version of the Home App. Note these Services
|
||||
// are not documented by Apple and are not officially part HAP-R2.
|
||||
//
|
||||
// For Service::Television():
|
||||
//
|
||||
// * Characteristic::Active()
|
||||
// * Characteristic::ConfiguredName()
|
||||
// * Characteristic::ActiveIdentifier()
|
||||
// * Characteristic::RemoteKey()
|
||||
// * Characteristic::PowerModeSelection()
|
||||
//
|
||||
// For Service::InputSource():
|
||||
//
|
||||
// * Characteristic::ConfiguredName()
|
||||
// * Characteristic::ConfiguredNameStatic() // a HomeSpan-specific variation of ConfiguredName()
|
||||
// * Characteristic::Identifier()
|
||||
// * Characteristic::IsConfigured()
|
||||
// * Characteristic::CurrentVisibilityState()
|
||||
// * Characteristic::TargetVisibilityState()
|
||||
|
||||
// NOTE: This example is only designed to demonstrate how Television Services and Characteristics
|
||||
// appear in the Home App, and what they each control. To keep things simple, actions for the
|
||||
// Input Sources have NOT been implemented in the code below. For example, the code below does not include
|
||||
// any logic to update CurrentVisibilityState when the TargetVisibilityState checkboxes are clicked.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
struct HomeSpanTV : Service::Television {
|
||||
|
||||
SpanCharacteristic *active = new Characteristic::Active(0); // TV On/Off (set to Off at start-up)
|
||||
SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up
|
||||
SpanCharacteristic *remoteKey = new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget
|
||||
SpanCharacteristic *settingsKey = new Characteristic::PowerModeSelection(); // Adds "View TV Setting" option to Selection Screen
|
||||
|
||||
HomeSpanTV(const char *name) : Service::Television() {
|
||||
new Characteristic::ConfiguredName(name); // Name of TV
|
||||
Serial.printf("Configured TV: %s\n",name);
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
if(active->updated()){
|
||||
Serial.printf("Set TV Power to: %s\n",active->getNewVal()?"ON":"OFF");
|
||||
}
|
||||
|
||||
if(activeID->updated()){
|
||||
Serial.printf("Set Input Source to HDMI-%d\n",activeID->getNewVal());
|
||||
}
|
||||
|
||||
if(settingsKey->updated()){
|
||||
Serial.printf("Received request to \"View TV Settings\"\n");
|
||||
}
|
||||
|
||||
if(remoteKey->updated()){
|
||||
Serial.printf("Remote Control key pressed: ");
|
||||
switch(remoteKey->getNewVal()){
|
||||
case 4:
|
||||
Serial.printf("UP ARROW\n");
|
||||
break;
|
||||
case 5:
|
||||
Serial.printf("DOWN ARROW\n");
|
||||
break;
|
||||
case 6:
|
||||
Serial.printf("LEFT ARROW\n");
|
||||
break;
|
||||
case 7:
|
||||
Serial.printf("RIGHT ARROW\n");
|
||||
break;
|
||||
case 8:
|
||||
Serial.printf("SELECT\n");
|
||||
break;
|
||||
case 9:
|
||||
Serial.printf("BACK\n");
|
||||
break;
|
||||
case 11:
|
||||
Serial.printf("PLAY/PAUSE\n");
|
||||
break;
|
||||
case 15:
|
||||
Serial.printf("INFO\n");
|
||||
break;
|
||||
default:
|
||||
Serial.print("UNKNOWN KEY\n");
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Television,"HomeSpan Television");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
|
||||
// Below we define 10 different InputSource Services using different combinations
|
||||
// of Characteristics to demonstrate how they interact and appear to the user in the Home App
|
||||
|
||||
SpanService *hdmi1 = new Service::InputSource(); // Source included in Selection List, but excluded from Settings Screen
|
||||
new Characteristic::ConfiguredName("HDMI 1");
|
||||
new Characteristic::Identifier(1);
|
||||
|
||||
SpanService *hdmi2 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 2");
|
||||
new Characteristic::Identifier(2);
|
||||
new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen
|
||||
|
||||
SpanService *hdmi3 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 3");
|
||||
new Characteristic::Identifier(3);
|
||||
new Characteristic::IsConfigured(1); // Source included in both the Selection List and the Settings Screen
|
||||
|
||||
SpanService *hdmi4 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 4");
|
||||
new Characteristic::Identifier(4);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List
|
||||
|
||||
SpanService *hdmi5 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 5");
|
||||
new Characteristic::Identifier(5);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List
|
||||
|
||||
SpanService *hdmi6 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 6");
|
||||
new Characteristic::Identifier(6);
|
||||
new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen
|
||||
new Characteristic::CurrentVisibilityState(0); // If IsConfigured(0) is specified, CurrentVisibilityState() has no effect
|
||||
|
||||
SpanService *hdmi7 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 7");
|
||||
new Characteristic::Identifier(7);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List...
|
||||
new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *hdmi8 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 8");
|
||||
new Characteristic::Identifier(8);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List...
|
||||
new Characteristic::TargetVisibilityState(1); // ...and an "un-checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *hdmi9 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 9");
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...but without an Identifier() set, the Source is excluded from the Selection List regardless of CurrentVisibilityState(0)
|
||||
new Characteristic::TargetVisibilityState(0);
|
||||
|
||||
SpanService *hdmi10 = new Service::InputSource();
|
||||
(new Characteristic::ConfiguredName("HDMI 10"))->removePerms(PW); // Source Name permissions changed and now cannot be edited in Settings Screen
|
||||
new Characteristic::Identifier(10);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List...
|
||||
new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *speaker = new Service::TelevisionSpeaker();
|
||||
new Characteristic::VolumeSelector();
|
||||
new Characteristic::VolumeControlType(3);
|
||||
|
||||
(new HomeSpanTV("Test TV")) // Define a Television Service. Must link in InputSources!
|
||||
->addLink(hdmi1)
|
||||
->addLink(hdmi2)
|
||||
->addLink(hdmi3)
|
||||
->addLink(hdmi4)
|
||||
->addLink(hdmi5)
|
||||
->addLink(hdmi6)
|
||||
->addLink(hdmi7)
|
||||
->addLink(hdmi8)
|
||||
->addLink(hdmi9)
|
||||
->addLink(hdmi10)
|
||||
->addLink(speaker)
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void loop() {
|
||||
homeSpan.poll();
|
||||
}
|
||||
Reference in New Issue
Block a user