ESP + Reorganizacja
This commit is contained in:
88
ESP32/06-DimmableLED/06-DimmableLED.ino
Normal file
88
ESP32/06-DimmableLED/06-DimmableLED.ino
Normal file
@@ -0,0 +1,88 @@
|
||||
/*********************************************************************************
|
||||
* 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.setPairingCode("11122333");
|
||||
homeSpan.setQRID("111-22-333");
|
||||
|
||||
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(2); // 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/06-DimmableLED/DEV_LED.h
Normal file
67
ESP32/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
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
119
ESP32/12-ServiceLoops/12-ServiceLoops.ino
Normal file
119
ESP32/12-ServiceLoops/12-ServiceLoops.ino
Normal file
@@ -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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 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.setPairingCode("11122333");
|
||||
homeSpan.setQRID("111-22-333");
|
||||
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/12-ServiceLoops/DEV_Sensors.h
Normal file
117
ESP32/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).
|
||||
85
ESP32/ESP32-HomeSpan/06-DimmableLED.ino
Normal file
85
ESP32/ESP32-HomeSpan/06-DimmableLED.ino
Normal file
@@ -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()
|
||||
19
ESP32/ESP32-HomeSpan/DEV_RELAY.h
Normal file
19
ESP32/ESP32-HomeSpan/DEV_RELAY.h
Normal file
@@ -0,0 +1,19 @@
|
||||
struct DEV_RELAY : Service::Switch { // deriving from the HomeSpan Switch service
|
||||
|
||||
int relayPin;
|
||||
SpanCharacteristic *power;
|
||||
|
||||
DEV_RELAY(int relayPin) : Service::Switch() {
|
||||
|
||||
power = new Characteristic::On();
|
||||
this->relayPin = relayPin;
|
||||
pinMode(relayPin, OUTPUT);
|
||||
}
|
||||
|
||||
boolean update() {
|
||||
|
||||
digitalWrite(relayPin, power->getNewVal());
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
33
ESP32/ESP32-HomeSpan/ESP32-HomeSpan.ino
Normal file
33
ESP32/ESP32-HomeSpan/ESP32-HomeSpan.ino
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "LED.h"
|
||||
#include "DEV_RELAY.h"
|
||||
|
||||
void setup() {
|
||||
// put your setup code here, to run once:
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setPairingCode("11122333");
|
||||
homeSpan.setQRID("111-22-333");
|
||||
|
||||
homeSpan.begin(Category::Bridges, "HomeSpan Bridge");
|
||||
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new LED(LED_BUILTIN);
|
||||
|
||||
|
||||
// Accessory 2: Relay Switch
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new DEV_RELAY(4); // instantiates a new relay
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
homeSpan.poll();
|
||||
}
|
||||
28
ESP32/ESP32-HomeSpan/LED.h
Normal file
28
ESP32/ESP32-HomeSpan/LED.h
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
struct 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.
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
21
ESP32/HomeSpan-master/LICENSE
Normal file
21
ESP32/HomeSpan-master/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2023 Gregg E. Berman
|
||||
|
||||
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.
|
||||
194
ESP32/HomeSpan-master/README.md
Normal file
194
ESP32/HomeSpan-master/README.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Welcome!
|
||||
|
||||
Welcome to HomeSpan - a robust and extremely easy-to-use Arduino library for creating your own [ESP32-based](https://www.espressif.com/en/products/modules/esp32) HomeKit devices entirely within the [Arduino IDE](http://www.arduino.cc).
|
||||
|
||||
HomeSpan provides a microcontroller-focused implementation of Apple's HomeKit Accessory Protocol Specification Release R2 (HAP-R2) designed specifically for the Espressif ESP32 microcontroller running within the Arduino IDE. HomeSpan pairs directly to HomeKit via your home WiFi network without the need for any external bridges or components. With HomeSpan you can use the full power of the ESP32's I/O functionality to create custom control software and/or hardware to automatically operate external devices from the Home App on your iPhone, iPad, or Mac, or with Siri.
|
||||
|
||||
HomeSpan requires version 2 of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips.
|
||||
|
||||
HomeSpan is currently NOT compatible with version 3.X of the Arduino-ESP32 Board Manager, since version 3 contains many breaking changes and is not backwards-compatible with version 2.X of the Arduino-ESP32 Board Manager. At present, HomeSpan can only be compiled under version 2.X of the Board Manager.
|
||||
|
||||
> [!NOTE]
|
||||
> Apple's new HomeKit architecture [requires the use of a Home Hub](https://support.apple.com/en-us/HT207057) (either a HomePod or Apple TV) for full and proper operation of any HomeKit device, including those based on HomeSpan. Without a Home Hub, HomeSpan cannot send notifications to the Home App - things like pushbuttons and temperature sensors will not be able to transmit updates to the Home App. Use of HomeSpan without a Home Hub is NOT recommended.
|
||||
|
||||
### HomeSpan Highlights
|
||||
|
||||
* Provides a natural, intuitive, and **very** easy-to-use framework
|
||||
* Utilizes a unique *Service-Centric* approach to creating HomeKit devices
|
||||
* Takes full advantage of the widely-popular Arduino IDE
|
||||
* 100% HAP-R2 compliance
|
||||
* Dozens of integrated HomeKit Services
|
||||
* Operates in either Accessory or Bridge mode
|
||||
* Supports pairing with Setup Codes or QR Codes
|
||||
|
||||
### For the HomeSpan Developer
|
||||
|
||||
* Extensive use of the Arduino Serial Monitor
|
||||
* Real-time, easy-to-understand diagnostics
|
||||
* Complete transparency to every underlying HomeKit action, data request, and data response
|
||||
* Command-line interface with a variety of info, debugging, and configuration commands
|
||||
* Built-in database validation to ensure your configuration meets all HAP requirements
|
||||
* Dedicated classes that utilize the ESP32's 16-channel PWM peripheral for easy control of:
|
||||
* LED Brightness (including auto-fading)
|
||||
* Servo Motors
|
||||
* Integrated Push Button and Toggle Switch functionality supporting single, double, and long presses of:
|
||||
* Physical pushbuttons that connect an ESP32 pin to either ground or VCC
|
||||
* Touch pads/sensors connected to an ESP32 pin (for ESP32 devices that support touch pads)
|
||||
* Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals
|
||||
* Dedicated classes to control one- and two-wire addressable RGB and RGBW LEDs and LED strips
|
||||
* Dedicated classes to control stepper motors that can run smoothly in the background without interfering with HomeSpan
|
||||
* Dedicated class that faciliates seamless point-to-point communication between ESP32 devices using ESP-NOW
|
||||
* Integrated Web Log for user-defined log messages
|
||||
* Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics
|
||||
* Additional examples and projects showcasing real-world implementations of HomeSpan
|
||||
* A complete set of documentation explaining every aspect of the HomeSpan API
|
||||
|
||||
### For the HomeSpan End-User
|
||||
|
||||
* Embedded WiFi Access Point and Web Interface to allow end-users (non-developers) to:
|
||||
* Set up Homespan with their own home WiFi Credentials
|
||||
* Create their own HomeKit Pairing Setup Code
|
||||
* Status LED and Control Button to allow end-users to:
|
||||
* Force-unpair the device from HomeKit
|
||||
* Perform a Factory Reset
|
||||
* Launch the WiFi Access Point
|
||||
* A standalone, detailed End-User Guide
|
||||
|
||||
## ❗Latest Update - HomeSpan 1.9.1 (07/03/2024)
|
||||
|
||||
* **HomeSpan now supports *Tag-Length-Value ("TLV8")* Characteristics!**
|
||||
|
||||
* adds new, fully-integrated `TLV8()` class library for the creation and management of TLV8 objects
|
||||
* includes methods to handle standard byte-stream VALUES as well as strings, numerical values, zero-length tags, and sub-TLVs
|
||||
* utilizes standard C++ iterators for easy access to reading and writing TLV8 records
|
||||
* adds new `Characteristic` methods `getTLV()`, `getNewTLV()`, and `setTLV()`
|
||||
* adds new `CUSTOM_CHAR_TLV8()` that allows for easy creation of custom TLV8 Characteristics
|
||||
* includes new [Tutorial Example 22 - TLV8 Characteristics](examples/22-TLV8_Characteristics) demonstrating use of the `TLV8()` class and TLV8 Characteristics
|
||||
* see the new [TLV8 Characteristics](docs/TLV8.md) page for complete details and documentation
|
||||
|
||||
* **New *DisplayOrder* TLV8 Characteristic**
|
||||
|
||||
* utlizes HomeSpan's new `TLV8()` library
|
||||
* allows you to specify the exact order in which the Input Sources for a Television Service are displayed in the Home App
|
||||
* see [Tutorial Example 22 - TLV8 Characteristics](examples/22-TLV8_Characteristics) for details
|
||||
|
||||
* **New *AccessoryIdentifier* Tutorial**
|
||||
|
||||
* demonstrates how to trigger an Accessory's Identifier Characteristic, optionally used to help identify a device during initial pairing
|
||||
* see [Tutorial Example 21 - AccessoryIdentifier](examples/21-AccessoryIdentifier)
|
||||
|
||||
* **Added support for customizing Pixel chips**
|
||||
|
||||
* new constructor `Pixel(uint8_t pin, [pixelType_t pixelType])` allows your to set the order in which colors are transmitted to the pixel chip, where *pixelType* is one of the following:
|
||||
* PixelType::RGB, PixelType::RBG, PixelType::BRG, PixelType::BGR, PixelType::GBR, PixelType::GRB
|
||||
* PixelType::RGBW, PixelType::RBGW, PixelType::BRGW, PixelType::BGRW, PixelType::GBRW, PixelType::GRBW*
|
||||
* deprecated previous constructor `Pixel(uint8_t pin, boolean isRGBW)`
|
||||
* this constructor will continue to work, but you will receive a warning during compilation that it has been deprecated
|
||||
* users should switch to the new constructor to avoid potential compatibility issues with future versions of HomeSpan
|
||||
* added new method `boolean isRGBW()`
|
||||
* returns *true* if Pixel was constructed as RGBW, else *false* if constructed as RGB only (i.e. no white LED)
|
||||
* added new [PixelTester](examples/Other%20Examples/PixelTester) sketch (found under *Other Examples*) to aid in determining the *pixelType* for any LED Strip
|
||||
* see the [Adressable RGB LEDs](docs/Pixels.md) page for details
|
||||
|
||||
* **New ability to read and set the IIDs of Services and Characteristics**
|
||||
|
||||
* adds new `SpanService` method `getIID()` that returns the IID of a Service
|
||||
* adds new `SpanCharacteristic` method `getIID()` that returns the IID of a Characteristic
|
||||
* adds new `homeSpan` method `resetIID(int newIID)` that resets the IID count for the current Accessory
|
||||
* see the [API Reference](docs/Reference.md) for details
|
||||
|
||||
* **New ability to read Controller pairing data (for advanced use-cases only)**
|
||||
|
||||
* adds new `homeSpan` methods `controllerListBegin()` and `controllerListEnd()` that returns iterators to HomeSpan's internal linked-list of Controller data records
|
||||
* adds new methods to read each Controller's pairing data:
|
||||
* `getID()` - returns a pointer to the 36-byte Device ID of the controller
|
||||
* `getLTPK()` - a pointer to the 32-byte Long-Term Public Key of the controller
|
||||
* `isAdmin()` - returns true if the controller has admin permission, else returns false
|
||||
* adds new `homeSpan` method `setControllerCallback()` to set optional callback function that HomeSpan calls whenever a controller is added, removed, or updated
|
||||
* see the [API Reference](docs/Reference.md) for details
|
||||
|
||||
* **HomeSpan now supports the *write-response ("WR")* protocol**
|
||||
* added automated handling of the HomeKits's *write-response ("WR")* protocol*
|
||||
* not needed for any Characteristics that are currently supported by HomeSpan, but useful for experimentation and work with Custom Characteristics
|
||||
* added extra checks when using `setVal()`
|
||||
* a warning message is output on the Serial Monitor if `setVal()` is called to change the value of a Characteristic from within the `update()` method at the same time the Home App is sending an update request for that value
|
||||
* does not apply if `setVal()` is called from within `update()` to change the value of a Characteristic in response to a *write-response* request from the Home App
|
||||
|
||||
* **Converted the `getLinks()` SpanService method to a template function**
|
||||
* allows user to automatically cast the elements of the returned vector into any specific Service type
|
||||
* also adds an optional parameter to restrict the elements of the returned vector to match a specified HomeSpan Service
|
||||
* see the [API Reference](docs/Reference.md) for details
|
||||
|
||||
* **New ability to halt the pulse generation for a ServoPin**
|
||||
* calling `set(NAN)` for a ServoPin halts the pulse generation, which (for most analog servos) allows the motor to be freely rotated
|
||||
* calling `set(position)`, where *position* equal the desired number of degrees, restarts the pulse generation and sets the servo position accordingly
|
||||
|
||||
* **Refactored client/slot management to save memory and prepare for future integration of Ethernet support**
|
||||
* fixed-array of Client/Socket connections replaced by dynamic linked-list
|
||||
* serial interface now only shows active client connections (rather than a fixed list of client slots)
|
||||
* **deprecated** `homeSpan.reserveSocketConnections()` since it is no longer needed
|
||||
|
||||
* **Fixed bug introduced in 1.9.0 that prevented `homeSpan.setPairingCode()` from saving (and subsequently using) the request Setup Pairing Code**
|
||||
* this method now operates silently, unless an invalid pairing code is provided, in which case an error is reported to the Serial Monitor and *the sketch is halted*
|
||||
* the process for setting the Pairing Code using the CLI 'S' command or via the Access Point are unchanged - confirmation messages are still output to the Serial Monitor and errors do *not* cause the sketch to halt
|
||||
|
||||
* **Fixed memory leak introduced in 1.9.0 that would fail to free a small temporary memory block created when verifying a new connection**
|
||||
* had no practical impact when using a Home Hub since Home Kit only creates a few permanent connections
|
||||
* had significant impact when not using a Home Hub in cases where the Home App repeatedly drops and re-establishes connections, resulting in slow erosion of heap memory and then out-of-memory failure of the device after a few days (note use of HomeSpan without a Home Hub is not formally supported)
|
||||
|
||||
* **Fixed latent bug in SpanPoint**
|
||||
* HomeSpan would crash when printing **SpanPoint** configuration information to the Serial Monitor (the 'i' CLI command) if any of the instances of SpanPoint had *receiveSize=0*
|
||||
* this bug never surfaced before since all the **SpanPoint examples** were based on receiving data and therefore had a non-zero *receiveSize*
|
||||
|
||||
* **Deleted `homeSpan.setMaxConnections()`, which had been *deprecated* many versions ago**
|
||||
|
||||
* **Deleted stand-alone `SpanRange` structure, which had been *deprecated* many versions ago**
|
||||
* this has no impact on standard use of the Characteristic method `setRange()`
|
||||
|
||||
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
||||
|
||||
# HomeSpan Resources
|
||||
|
||||
HomeSpan includes the following documentation:
|
||||
|
||||
* [Getting Started with HomeSpan](docs/GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices
|
||||
* [HomeSpan API Overview](docs/Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch
|
||||
* [HomeSpan Tutorials](docs/Tutorials.md) - a guide to HomeSpan's tutorial-sketches
|
||||
* [HomeSpan Services and Characteristics](docs/ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan
|
||||
* [HomeSpan Accessory Categories](docs/Categories.md) - a list of all HAP Accessory Categories defined by HomeSpan
|
||||
* [HomeSpan Command-Line Interface (CLI)](docs/CLI.md) - configure a HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, monitor and update its status, and access detailed, real-time device diagnostics from the Arduino IDE Serial Monitor
|
||||
* [HomeSpan User Guide](docs/UserGuide.md) - turnkey instructions on how to configure an already-programmed HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, and pair the device to HomeKit. No computer needed!
|
||||
* [HomeSpan API Reference](docs/Reference.md) - a complete guide to the HomeSpan Library API
|
||||
* [HomeSpan QR Codes](docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices
|
||||
* [HomeSpan OTA](docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection
|
||||
* [HomeSpan PWM](docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral
|
||||
* [HomeSpan RFControl](docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral
|
||||
* [HomeSpan Pixels](docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips
|
||||
* [HomeSpan Stepper Motor Control](docs/Stepper.md) - integrated control of stepper motors, including PWM microstepping
|
||||
* [HomeSpan SpanPoint](docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW
|
||||
* [HomeSpan Television Services](docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics
|
||||
* [HomeSpan Message Logging](docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page
|
||||
* [HomeSpan TLV8 Characteristics](docs/TLV8.md) - classes and methods for creating TLV8 objects to use with TLV8-based Characteristics
|
||||
* [HomeSpan Device Cloning](docs/Cloning.md) - seamlessly swap a broken device for a new one without needing to re-pair and lose HomeKit automations
|
||||
* [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library
|
||||
* [HomeSpan FAQ](docs/FAQ.md) - answers to frequently-asked questions
|
||||
* [Solutions to Common Problems](docs/Solutions.md) - resolutions to some common problems when using/compiling HomeSpan
|
||||
* [HomeSpan Reference Sketches](https://github.com/HomeSpan/HomeSpanReferenceSketches) - a collection of self-contained Reference Sketches showcasing some of the more complex HomeKit Services, such as Thermostats and Irrigation Systems
|
||||
|
||||
Note that all documentation is version-controlled and tied to each branch. The *master* branch generally points to the latest release. The *dev* branch, when available, will contain code under active development.
|
||||
|
||||
# External Resources
|
||||
|
||||
In addition to HomeSpan resources, developers who are new to HomeKit programming may find useful Chapters 8 and 9 of Apple's HomeKit Accessory Protocol Specification, Non-Commercial Version, Release R2 (HAP-R2). This document is unfortunately no longer available from Apple (perhaps because it was last updated July, 2019, and is now somewhat out-of-date). However, you may be able find copies of this document elsewhere on the web. Note Apple has not replaced the HAP-R2 document with any other versions for non-commercial use, and Apple's open-source [HomeKit ADK](https://github.com/apple/HomeKitADK) only reflects the original HAP-R2 specs (rather than all the latest Services and Characteristics available in HomeKit for commercial devices).
|
||||
|
||||
---
|
||||
|
||||
### Feedback or Questions?
|
||||
|
||||
Please post bug reports or other problems with HomeSpan to the [Issues Board](https://github.com/HomeSpan/HomeSpan/issues). Please post all other questions about HomeSpan (use, functionality, specs, examples, etc.) or any ideas or recommendations you may have for new functionality, or any general feedback about HomeSpan or HomeKit, to the [Discussion Board](https://github.com/HomeSpan/HomeSpan/discussions). For more general questions or feedback not related to a specific HomeSpan issue or function, you can email me directly at [homespan@icloud.com](mailto:homespan@icloud.com).
|
||||
|
||||
### About the Author
|
||||
|
||||
HomeSpan was developed and continues to be maintained and supported by Gregg Berman. It was originally conceived to solve the pesky problem of not being able to operate an RF-controlled kitchen vent hood with Siri. I hope you find it useful as well as fun to use.
|
||||
|
||||
This is my second large-scale open-source project --- my first was the design of an open-source sytem for operating model railroads using nothing more than an Arduino Uno and Arduino Motor Shield to generate digital command and control (DCC) signals. Though I have not been involved with the model railroading hobby for many years, videos showcasing my original system (dubbed DCC++), along with detailed tutorials of how it works, are still available on the [DCC++ YouTube Channel](https://www.youtube.com/@dcc2840/videos).
|
||||
@@ -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();
|
||||
}
|
||||
9
ESP32/HomeSpan-master/library.properties
Normal file
9
ESP32/HomeSpan-master/library.properties
Normal file
@@ -0,0 +1,9 @@
|
||||
name=HomeSpan
|
||||
version=1.9.1
|
||||
author=Gregg <homespan@icloud.com>
|
||||
maintainer=Gregg <homespan@icloud.com>
|
||||
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.
|
||||
paragraph=This library provides a microcontroller-focused implementation of Apple's HomeKit Accessory Protocol (HAP - Release R2) designed specifically for the ESP32 running on the Arduino IDE. HomeSpan pairs directly to iOS Home via WiFi without the need for any external bridges or components. Supports ESP32, ESP32-S2, ESP32-C3, and ESP32-S3.
|
||||
url=https://github.com/HomeSpan/HomeSpan
|
||||
architectures=esp32
|
||||
includes=HomeSpan.h
|
||||
192
ESP32/HomeSpan-master/src/Characteristics.h
Normal file
192
ESP32/HomeSpan-master/src/Characteristics.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
//////////////////////////////////////////
|
||||
// HAP CHARACTERISTICS (HAP Chapter 9) //
|
||||
//////////////////////////////////////////
|
||||
|
||||
enum PERMS{ // create bitflags based on HAP Table 6-4
|
||||
PR=1,
|
||||
PW=2,
|
||||
EV=4,
|
||||
AA=8,
|
||||
TW=16,
|
||||
HD=32,
|
||||
WR=64,
|
||||
NV=128 // this is a non-HAP flag used to specify that no value should be provided (should be a HAP flag!)
|
||||
};
|
||||
|
||||
enum FORMAT { // HAP Table 6-5
|
||||
BOOL=0,
|
||||
UINT8=1,
|
||||
UINT16=2,
|
||||
UINT32=3,
|
||||
UINT64=4,
|
||||
INT=5,
|
||||
FLOAT=6,
|
||||
STRING=7,
|
||||
DATA=8,
|
||||
TLV_ENC=9
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct HapChar {
|
||||
const char *type;
|
||||
const char *hapName;
|
||||
PERMS perms;
|
||||
FORMAT format;
|
||||
boolean staticRange;
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
#define HAPCHAR(hapName,type,perms,format,staticRange) HapChar hapName {#type,#hapName,(PERMS)(perms),format,staticRange}
|
||||
|
||||
struct HapCharacteristics {
|
||||
|
||||
HAPCHAR( AccessoryFlags, A6, PR+EV, UINT32, true );
|
||||
HAPCHAR( Active, B0, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( ActiveIdentifier, E7, PW+PR+EV, UINT32, true );
|
||||
HAPCHAR( AirQuality, 95, PR+EV, UINT8, true );
|
||||
HAPCHAR( BatteryLevel, 68, PR+EV, UINT8, false );
|
||||
HAPCHAR( Brightness, 8, PR+PW+EV, INT, false );
|
||||
HAPCHAR( CarbonMonoxideLevel, 90, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonMonoxidePeakLevel, 91, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonDioxideDetected, 92, PR+EV, UINT8, true );
|
||||
HAPCHAR( CarbonDioxideLevel, 93, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonDioxidePeakLevel, 94, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonMonoxideDetected, 69, PR+EV, UINT8, true );
|
||||
HAPCHAR( ChargingState, 8F, PR+EV, UINT8, true );
|
||||
HAPCHAR( ClosedCaptions, DD, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( CoolingThresholdTemperature, D, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( ColorTemperature, CE, PR+PW+EV, UINT32, false );
|
||||
HAPCHAR( ConfiguredName, E3, PW+PR+EV, STRING, false );
|
||||
HAPCHAR( ContactSensorState, 6A, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentAmbientLightLevel, 6B, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentHorizontalTiltAngle, 6C, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentAirPurifierState, A9, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentSlatState, AA, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentPosition, 6D, PR+EV, UINT8, false );
|
||||
HAPCHAR( CurrentVerticalTiltAngle, 6E, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentHumidifierDehumidifierState, B3, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentDoorState, E, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentFanState, AF, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentHeatingCoolingState, F, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentHeaterCoolerState, B1, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentMediaState, E0, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentRelativeHumidity, 10, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentTemperature, 11, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentTiltAngle, C1, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentVisibilityState, 135, PR+EV, UINT8, true );
|
||||
HAPCHAR( DisplayOrder, 136, PR+EV, TLV_ENC, true );
|
||||
HAPCHAR( FilterLifeLevel, AB, PR+EV, FLOAT, false );
|
||||
HAPCHAR( FilterChangeIndication, AC, PR+EV, UINT8, true );
|
||||
HAPCHAR( FirmwareRevision, 52, PR+EV, STRING, true );
|
||||
HAPCHAR( HardwareRevision, 53, PR, STRING, true );
|
||||
HAPCHAR( HeatingThresholdTemperature, 12, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( HoldPosition, 6F, PW, BOOL, true );
|
||||
HAPCHAR( Hue, 13, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( Identify, 14, PW, BOOL, true );
|
||||
HAPCHAR( Identifier, E6, PR, UINT32, true );
|
||||
HAPCHAR( InputDeviceType, DC, PR+EV, UINT8, true );
|
||||
HAPCHAR( InputSourceType, DB, PR+EV, UINT8, true );
|
||||
HAPCHAR( InUse, D2, PR+EV, UINT8, true );
|
||||
HAPCHAR( IsConfigured, D6, PR+EV, UINT8, true );
|
||||
HAPCHAR( LeakDetected, 70, PR+EV, UINT8, true );
|
||||
HAPCHAR( LockCurrentState, 1D, PR+EV, UINT8, true );
|
||||
HAPCHAR( LockPhysicalControls, A7, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( LockTargetState, 1E, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( Manufacturer, 20, PR, STRING, true );
|
||||
HAPCHAR( Model, 21, PR, STRING, true );
|
||||
HAPCHAR( MotionDetected, 22, PR+EV, BOOL, true );
|
||||
HAPCHAR( Mute, 11A, PW+PR+EV, BOOL, true );
|
||||
HAPCHAR( Name, 23, PR, STRING, true );
|
||||
HAPCHAR( NitrogenDioxideDensity, C4, PR+EV, FLOAT, false );
|
||||
HAPCHAR( ObstructionDetected, 24, PR+EV, BOOL, true );
|
||||
HAPCHAR( PM25Density, C6, PR+EV, FLOAT, false );
|
||||
HAPCHAR( OccupancyDetected, 71, PR+EV, UINT8, true );
|
||||
HAPCHAR( OutletInUse, 26, PR+EV, BOOL, true );
|
||||
HAPCHAR( On, 25, PR+PW+EV, BOOL, true );
|
||||
HAPCHAR( OzoneDensity, C3, PR+EV, FLOAT, false );
|
||||
HAPCHAR( PM10Density, C7, PR+EV, FLOAT, false );
|
||||
HAPCHAR( PictureMode, E2, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( PositionState, 72, PR+EV, UINT8, true );
|
||||
HAPCHAR( PowerModeSelection, DF, PW, UINT8, true );
|
||||
HAPCHAR( ProgramMode, D1, PR+EV, UINT8, true );
|
||||
HAPCHAR( ProgrammableSwitchEvent, 73, PR+EV+NV, UINT8, true );
|
||||
HAPCHAR( RelativeHumidityDehumidifierThreshold, C9, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( RelativeHumidityHumidifierThreshold, CA, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( RemainingDuration, D4, PR+EV, UINT32, false );
|
||||
HAPCHAR( RemoteKey, E1, PW, UINT8, true );
|
||||
HAPCHAR( ResetFilterIndication, AD, PW, UINT8, true );
|
||||
HAPCHAR( RotationDirection, 28, PR+PW+EV, INT, true );
|
||||
HAPCHAR( RotationSpeed, 29, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( Saturation, 2F, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( SecuritySystemAlarmType, 8E, PR+EV, UINT8, true );
|
||||
HAPCHAR( SecuritySystemCurrentState, 66, PR+EV, UINT8, true );
|
||||
HAPCHAR( SecuritySystemTargetState, 67, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( SerialNumber, 30, PR, STRING, true );
|
||||
HAPCHAR( ServiceLabelIndex, CB, PR, UINT8, true );
|
||||
HAPCHAR( ServiceLabelNamespace, CD, PR, UINT8, true );
|
||||
HAPCHAR( SlatType, C0, PR, UINT8, true );
|
||||
HAPCHAR( SleepDiscoveryMode, E8, PR+EV, UINT8, true );
|
||||
HAPCHAR( SmokeDetected, 76, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusActive, 75, PR+EV, BOOL, true );
|
||||
HAPCHAR( StatusFault, 77, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusJammed, 78, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusLowBattery, 79, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusTampered, 7A, PR+EV, UINT8, true );
|
||||
HAPCHAR( SulphurDioxideDensity, C5, PR+EV, FLOAT, false );
|
||||
HAPCHAR( SwingMode, B6, PR+EV+PW, UINT8, true );
|
||||
HAPCHAR( TargetAirPurifierState, A8, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetFanState, BF, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetTiltAngle, C2, PW+PR+EV, INT, false );
|
||||
HAPCHAR( TargetHeaterCoolerState, B2, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( SetDuration, D3, PW+PR+EV, UINT32, false );
|
||||
HAPCHAR( TargetHorizontalTiltAngle, 7B, PW+PR+EV, INT, false );
|
||||
HAPCHAR( TargetHumidifierDehumidifierState, B4, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetPosition, 7C, PW+PR+EV, UINT8, false );
|
||||
HAPCHAR( TargetDoorState, 32, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetHeatingCoolingState, 33, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetMediaState, 137, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetRelativeHumidity, 34, PW+PR+EV, FLOAT, false );
|
||||
HAPCHAR( TargetTemperature, 35, PW+PR+EV, FLOAT, false );
|
||||
HAPCHAR( TargetVisibilityState, 134, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TemperatureDisplayUnits, 36, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetVerticalTiltAngle, 7D, PW+PR+EV, INT, false );
|
||||
HAPCHAR( ValveType, D5, PR+EV, UINT8, true );
|
||||
HAPCHAR( Version, 37, PR, STRING, true );
|
||||
HAPCHAR( VOCDensity, C8, PR+EV, FLOAT, false );
|
||||
HAPCHAR( Volume, 119, PW+PR+EV, UINT8, false );
|
||||
HAPCHAR( VolumeControlType, E9, PR+EV, UINT8, true );
|
||||
HAPCHAR( VolumeSelector, EA, PW, UINT8, true );
|
||||
HAPCHAR( WaterLevel, B5, PR+EV, FLOAT, false );
|
||||
|
||||
};
|
||||
|
||||
extern HapCharacteristics hapChars;
|
||||
72
ESP32/HomeSpan-master/src/FeatherPins.h
Normal file
72
ESP32/HomeSpan-master/src/FeatherPins.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// For developer use and testing only - provides a common set of pin numbers mapped to the Adafruit Feather Board.
|
||||
// Facilitates the testing of identical code on an ESP32, ESP32-S2, and ESP32-C3 using a common jig without rewiring
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_FEATHER_ESP32)
|
||||
enum {
|
||||
F13=13,F12=12,F27=27,F15=15,F32=32,F14=14,F16=16,F17=17,F21=21, // Digital Only (9 pins)
|
||||
F26=26,F25=25,F34=34,F39=39,F36=36,F4=4, // A0-A5
|
||||
F22=22,F23=23, // I2C SCL/SDA
|
||||
F5=5,F18=18,F19=19,F33=33 // SPI SCK/SDO/SDI/CS
|
||||
};
|
||||
#define DEVICE_SUFFIX ""
|
||||
|
||||
#elif defined(ARDUINO_ESP32S2_DEV)
|
||||
enum {
|
||||
F13=1,F12=3,F27=7,F15=10,F32=42,F14=11,F16=20,F17=21,F21=16, // Digital Only (9 pins)
|
||||
F26=17,F25=14,F34=13,F39=12,F36=18,F4=19, // A0-A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=36,F18=35,F19=37,F33=34, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=18 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-S2"
|
||||
|
||||
#elif defined(ARDUINO_ESP32C3_DEV)
|
||||
enum {
|
||||
F27=19,F32=2,F14=10,F16=20,F17=21,F21=18, // Digital Only (6 pins)
|
||||
F26=0,F25=1,F4=3, // A0/A1/A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=4,F18=6,F19=5,F33=7, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=8 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#elif defined(ARDUINO_ESP32S3_DEV)
|
||||
enum {
|
||||
F13=5,F12=6,F27=7,F15=16,F32=17,F14=18,F16=37,F17=36,F21=38, // Digital Only (9 pins)
|
||||
F26=1,F25=2,F34=20,F39=19,F36=15,F4=4, // A0-A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=12,F18=11,F19=13,F33=10, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=48 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-S3"
|
||||
|
||||
#endif
|
||||
1696
ESP32/HomeSpan-master/src/HAP.cpp
Normal file
1696
ESP32/HomeSpan-master/src/HAP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
239
ESP32/HomeSpan-master/src/HAP.h
Normal file
239
ESP32/HomeSpan-master/src/HAP.h
Normal file
@@ -0,0 +1,239 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "HAPConstants.h"
|
||||
#include "HKDF.h"
|
||||
#include "SRP.h"
|
||||
|
||||
const TLV8_names HAP_Names[] = {
|
||||
{kTLVType_Separator,"SEPARATOR"},
|
||||
{kTLVType_State,"STATE"},
|
||||
{kTLVType_PublicKey,"PUBKEY"},
|
||||
{kTLVType_Method,"METHOD"},
|
||||
{kTLVType_Salt,"SALT"},
|
||||
{kTLVType_Error,"ERROR"},
|
||||
{kTLVType_Proof,"PROOF"},
|
||||
{kTLVType_EncryptedData,"ENC.DATA"},
|
||||
{kTLVType_Signature,"SIGNATURE"},
|
||||
{kTLVType_Identifier,"IDENTIFIER"},
|
||||
{kTLVType_Permissions,"PERMISSION"}
|
||||
};
|
||||
|
||||
#define hap_controller_IDBYTES 36
|
||||
#define hap_accessory_IDBYTES 17
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// NONCE Structure (HAP used last 64 of 96 bits)
|
||||
|
||||
struct Nonce {
|
||||
uint8_t x[12];
|
||||
Nonce();
|
||||
void zero();
|
||||
uint8_t *get();
|
||||
void inc();
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Accessory Structure for Permanently-Stored Data
|
||||
|
||||
struct Accessory {
|
||||
uint8_t ID[hap_accessory_IDBYTES]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" (no null terminator)
|
||||
uint8_t LTSK[crypto_sign_SECRETKEYBYTES]; // Long Term Ed2519 Secret Key
|
||||
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Paired Controller Structure for Permanently-Stored Data
|
||||
|
||||
class Controller {
|
||||
friend class HAPClient;
|
||||
|
||||
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
|
||||
boolean admin; // Controller has admin privileges
|
||||
uint8_t ID[36]; // Pairing ID
|
||||
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
|
||||
|
||||
public:
|
||||
|
||||
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
|
||||
allocated=true;
|
||||
admin=ad;
|
||||
memcpy(ID,id,36);
|
||||
memcpy(LTPK,ltpk,32);
|
||||
}
|
||||
|
||||
Controller(){}
|
||||
|
||||
const uint8_t *getID() const {return(ID);}
|
||||
const uint8_t *getLTPK() const {return(LTPK);}
|
||||
boolean isAdmin() const {return(admin);}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HAPClient Structure
|
||||
// Reads and Writes from each HAP Client connection
|
||||
|
||||
struct HAPClient {
|
||||
|
||||
// common structures and data shared across all HAP Clients
|
||||
|
||||
static const int MAX_HTTP=8096; // max number of bytes allowed for HTTP message
|
||||
static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16)
|
||||
static const int MAX_ACCESSORIES=150; // maximum number of allowed Accessories (HAP limit=150)
|
||||
|
||||
static pairState pairStatus; // tracks pair-setup status
|
||||
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys - permanently stored
|
||||
static list<Controller, Mallocator<Controller>> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
|
||||
|
||||
// individual structures and data defined for each Hap Client connection
|
||||
|
||||
WiFiClient client; // handle to client
|
||||
int clientNumber; // client number
|
||||
Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
||||
|
||||
// These temporary Curve25519 keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
||||
|
||||
struct tempKeys_t {
|
||||
uint8_t publicCurveKey[crypto_box_PUBLICKEYBYTES]; // Accessory's Curve25519 Public Key
|
||||
uint8_t sharedCurveKey[crypto_box_PUBLICKEYBYTES]; // Shared-Secret Curve25519 Key derived from Accessory's Secret Key and Controller's Public Key
|
||||
uint8_t sessionKey[crypto_box_PUBLICKEYBYTES]; // Session Key Curve25519 (derived with various HKDF calls)
|
||||
uint8_t iosCurveKey[crypto_box_PUBLICKEYBYTES]; // Controller's Curve25519 Public Key
|
||||
} temp;
|
||||
|
||||
// CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open
|
||||
|
||||
uint8_t a2cKey[32]; // AccessoryToControllerKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2)
|
||||
uint8_t c2aKey[32]; // ControllerToAccessoryKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2)
|
||||
Nonce a2cNonce; // encryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED)
|
||||
Nonce c2aNonce; // decryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED)
|
||||
|
||||
// define member methods
|
||||
|
||||
void processRequest(); // process HAP request
|
||||
int postPairSetupURL(uint8_t *content, size_t len); // POST /pair-setup (HAP Section 5.6)
|
||||
int postPairVerifyURL(uint8_t *content, size_t len); // POST /pair-verify (HAP Section 5.7)
|
||||
int postPairingsURL(uint8_t *content, size_t len); // POST /pairings (HAP Sections 5.10-5.12)
|
||||
int getAccessoriesURL(); // GET /accessories (HAP Section 6.6)
|
||||
int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4)
|
||||
int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2)
|
||||
int putPrepareURL(char *json); // PUT /prepare (HAP Section 6.7.2.4)
|
||||
|
||||
void tlvRespond(TLV8 &tlv8); // respond to client with HTTP OK header and all defined TLV data records
|
||||
int receiveEncrypted(uint8_t *httpBuf, int messageSize); // decrypt HTTP request (HAP Section 6.5)
|
||||
|
||||
int notFoundError(); // return 404 error
|
||||
int badRequestError(); // return 400 error
|
||||
int unauthorizedError(); // return 470 error
|
||||
|
||||
// define static methods
|
||||
|
||||
static void init(); // initialize HAP after start-up
|
||||
|
||||
static void hexPrintColumn(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level
|
||||
static void hexPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level
|
||||
static void charPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
||||
|
||||
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
|
||||
static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any)
|
||||
static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements.
|
||||
static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level
|
||||
static void saveControllers(); // saves Controller list in NVS
|
||||
static int nAdminControllers(); // returns number of admin Controller
|
||||
static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL
|
||||
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
|
||||
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
|
||||
static void eventNotify(SpanBuf *pObj, int nObj, HAPClient *ignore=NULL); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
|
||||
|
||||
static void getStatusURL(HAPClient *, void (*)(const char *, void *), void *); // GET / status (an optional, non-HAP feature)
|
||||
|
||||
class HAPTLV : public TLV8 { // dedicated class for HAP TLV8 records
|
||||
public:
|
||||
HAPTLV() : TLV8(HAP_Names,11){}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HapOut Structure
|
||||
|
||||
class HapOut : public std::ostream {
|
||||
|
||||
private:
|
||||
|
||||
struct HapStreamBuffer : public std::streambuf {
|
||||
|
||||
const size_t bufSize=1024; // max allowed for HAP encrypted records
|
||||
char *buffer;
|
||||
uint8_t *encBuf;
|
||||
HAPClient *hapClient=NULL;
|
||||
int logLevel=255; // default is NOT to print anything
|
||||
boolean enablePrettyPrint=false;
|
||||
size_t byteCount=0;
|
||||
size_t indent=0;
|
||||
uint8_t *hash;
|
||||
mbedtls_sha512_context *ctx;
|
||||
void (*callBack)(const char *, void *)=NULL;
|
||||
void *callBackUserData = NULL;
|
||||
|
||||
void flushBuffer();
|
||||
int_type overflow(int_type c) override;
|
||||
int sync() override;
|
||||
size_t getSize(){return(byteCount+pptr()-pbase());}
|
||||
void printFormatted(char *buf, size_t nChars, size_t nsp);
|
||||
|
||||
HapStreamBuffer();
|
||||
~HapStreamBuffer();
|
||||
|
||||
};
|
||||
|
||||
HapStreamBuffer hapBuffer;
|
||||
|
||||
public:
|
||||
|
||||
HapOut() : std::ostream(&hapBuffer){}
|
||||
|
||||
HapOut& setHapClient(HAPClient *hapClient){hapBuffer.hapClient=hapClient;return(*this);}
|
||||
HapOut& setLogLevel(int logLevel){hapBuffer.logLevel=logLevel;return(*this);}
|
||||
HapOut& prettyPrint(){hapBuffer.enablePrettyPrint=true;hapBuffer.logLevel=0;return(*this);}
|
||||
HapOut& setCallback(void(*f)(const char *, void *)){hapBuffer.callBack=f;return(*this);}
|
||||
HapOut& setCallbackUserData(void *userData){hapBuffer.callBackUserData=userData;return(*this);}
|
||||
|
||||
uint8_t *getHash(){return(hapBuffer.hash);}
|
||||
size_t getSize(){return(hapBuffer.getSize());}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Extern Variables
|
||||
|
||||
extern HapOut hapOut;
|
||||
89
ESP32/HomeSpan-master/src/HAPConstants.h
Normal file
89
ESP32/HomeSpan-master/src/HAPConstants.h
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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// HAP TLV Types (HAP Table 5-6)
|
||||
|
||||
typedef enum {
|
||||
kTLVType_Method=0x00,
|
||||
kTLVType_Identifier=0x01,
|
||||
kTLVType_Salt=0x02,
|
||||
kTLVType_PublicKey=0x03,
|
||||
kTLVType_Proof=0x04,
|
||||
kTLVType_EncryptedData=0x05,
|
||||
kTLVType_State=0x06,
|
||||
kTLVType_Error=0x07,
|
||||
kTLVType_RetryDelay=0x08,
|
||||
kTLVType_Certificate=0x09,
|
||||
kTLVType_Signature=0x0A,
|
||||
kTLVType_Permissions=0x0B,
|
||||
kTLVType_FragmentData=0x0C,
|
||||
kTLVType_FragmentLast=0x0D,
|
||||
kTLVType_Flags=0x13,
|
||||
kTLVType_Separator=0xFF
|
||||
} kTLVType;
|
||||
|
||||
|
||||
// HAP Error Codes (HAP Table 5-5)
|
||||
|
||||
typedef enum {
|
||||
tagError_None=0x00,
|
||||
tagError_Unknown=0x01,
|
||||
tagError_Authentication=0x02,
|
||||
tagError_Backoff=0x03,
|
||||
tagError_MaxPeers=0x04,
|
||||
tagError_MaxTries=0x05,
|
||||
tagError_Unavailable=0x06,
|
||||
tagError_Busy=0x07
|
||||
} tagError;
|
||||
|
||||
|
||||
// Pair-Setup and Pair-Verify States
|
||||
|
||||
typedef enum {
|
||||
pairState_M0=0,
|
||||
pairState_M1=1,
|
||||
pairState_M2=2,
|
||||
pairState_M3=3,
|
||||
pairState_M4=4,
|
||||
pairState_M5=5,
|
||||
pairState_M6=6
|
||||
} pairState;
|
||||
|
||||
// HAP Status Codes (HAP Table 6-11)
|
||||
|
||||
enum class StatusCode {
|
||||
OK=0,
|
||||
Unable=-70402,
|
||||
ReadOnly=-70404,
|
||||
WriteOnly=-70405,
|
||||
NotifyNotAllowed=-70406,
|
||||
UnknownResource=-70409,
|
||||
InvalidValue=-70410,
|
||||
TBD=-1 // status To-Be-Determined (TBD) once service.update() called - internal use only
|
||||
};
|
||||
217
ESP32/HomeSpan-master/src/HKDF.cpp
Normal file
217
ESP32/HomeSpan-master/src/HKDF.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include <mbedtls/hkdf.h>
|
||||
#include <mbedtls/platform_util.h>
|
||||
|
||||
#include "HKDF.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Wrapper function to call mbedtls_hkdf, below, with
|
||||
// HAP-specific parameters and assumptions
|
||||
|
||||
int HKDF::create(uint8_t *outputKey, uint8_t *inputKey, int inputLen, const char *salt, const char *info){
|
||||
|
||||
return(mbedtls_hkdf( mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
|
||||
(uint8_t *) salt, (size_t) strlen(salt),
|
||||
inputKey, (size_t) inputLen,
|
||||
(uint8_t *) info, (size_t) strlen(info),
|
||||
outputKey, 32 ));
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// CODE FOR HKDF IS MISSING FROM THE MBEDTLS LIBRARY INCLUDED WITH THE
|
||||
// ARDUINO-ESP32 LIBRARY. THE CODE BELOW IS SOURCE DIRECTLY FROM THE MBEDTLS
|
||||
// GITHUB. SEE THE MBEDTLS GITHUB SITE FOR LICENSING TERMS:
|
||||
//
|
||||
// https://github.com/ARMmbed/mbedtls/blob/development/LICENSE
|
||||
//
|
||||
//
|
||||
|
||||
int mbedtls_hkdf( const mbedtls_md_info_t *md, const unsigned char *salt,
|
||||
size_t salt_len, const unsigned char *ikm, size_t ikm_len,
|
||||
const unsigned char *info, size_t info_len,
|
||||
unsigned char *okm, size_t okm_len )
|
||||
{
|
||||
int ret;
|
||||
unsigned char prk[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
ret = mbedtls_hkdf_extract( md, salt, salt_len, ikm, ikm_len, prk );
|
||||
|
||||
if( ret == 0 )
|
||||
{
|
||||
ret = mbedtls_hkdf_expand( md, prk, mbedtls_md_get_size( md ),
|
||||
info, info_len, okm, okm_len );
|
||||
}
|
||||
|
||||
mbedtls_platform_zeroize( prk, sizeof( prk ) );
|
||||
|
||||
return( ret );
|
||||
}
|
||||
|
||||
int mbedtls_hkdf_extract( const mbedtls_md_info_t *md,
|
||||
const unsigned char *salt, size_t salt_len,
|
||||
const unsigned char *ikm, size_t ikm_len,
|
||||
unsigned char *prk )
|
||||
{
|
||||
unsigned char null_salt[MBEDTLS_MD_MAX_SIZE] = { '\0' };
|
||||
|
||||
if( salt == NULL )
|
||||
{
|
||||
size_t hash_len;
|
||||
|
||||
if( salt_len != 0 )
|
||||
{
|
||||
return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
hash_len = mbedtls_md_get_size( md );
|
||||
|
||||
if( hash_len == 0 )
|
||||
{
|
||||
return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
salt = null_salt;
|
||||
salt_len = hash_len;
|
||||
}
|
||||
|
||||
return( mbedtls_md_hmac( md, salt, salt_len, ikm, ikm_len, prk ) );
|
||||
}
|
||||
|
||||
int mbedtls_hkdf_expand( const mbedtls_md_info_t *md, const unsigned char *prk,
|
||||
size_t prk_len, const unsigned char *info,
|
||||
size_t info_len, unsigned char *okm, size_t okm_len )
|
||||
{
|
||||
size_t hash_len;
|
||||
size_t where = 0;
|
||||
size_t n;
|
||||
size_t t_len = 0;
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
mbedtls_md_context_t ctx;
|
||||
unsigned char t[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
if( okm == NULL )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
hash_len = mbedtls_md_get_size( md );
|
||||
|
||||
if( prk_len < hash_len || hash_len == 0 )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
if( info == NULL )
|
||||
{
|
||||
info = (const unsigned char *) "";
|
||||
info_len = 0;
|
||||
}
|
||||
|
||||
n = okm_len / hash_len;
|
||||
|
||||
if( okm_len % hash_len != 0 )
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per RFC 5869 Section 2.3, okm_len must not exceed
|
||||
* 255 times the hash length
|
||||
*/
|
||||
if( n > 255 )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
mbedtls_md_init( &ctx );
|
||||
|
||||
if( ( ret = mbedtls_md_setup( &ctx, md, 1 ) ) != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset( t, 0, hash_len );
|
||||
|
||||
/*
|
||||
* Compute T = T(1) | T(2) | T(3) | ... | T(N)
|
||||
* Where T(N) is defined in RFC 5869 Section 2.3
|
||||
*/
|
||||
for( i = 1; i <= n; i++ )
|
||||
{
|
||||
size_t num_to_copy;
|
||||
unsigned char c = i & 0xff;
|
||||
|
||||
ret = mbedtls_md_hmac_starts( &ctx, prk, prk_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_update( &ctx, t, t_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_update( &ctx, info, info_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* The constant concatenated to the end of each T(n) is a single octet.
|
||||
* */
|
||||
ret = mbedtls_md_hmac_update( &ctx, &c, 1 );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_finish( &ctx, t );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
num_to_copy = i != n ? hash_len : okm_len - where;
|
||||
memcpy( okm + where, t, num_to_copy );
|
||||
where += hash_len;
|
||||
t_len = hash_len;
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_md_free( &ctx );
|
||||
mbedtls_platform_zeroize( t, sizeof( t ) );
|
||||
|
||||
return( ret );
|
||||
}
|
||||
43
ESP32/HomeSpan-master/src/HKDF.h
Normal file
43
ESP32/HomeSpan-master/src/HKDF.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HKDF-SHA-512 Structure
|
||||
//
|
||||
// This is a wrapper around mbedtls_hkdf, which is NOT
|
||||
// included in the normal Arduino-ESP32 library.
|
||||
// Code was instead sourced directly from MBED GitHub and
|
||||
// incorporated under hkdf.cpp, with a wrapper to always
|
||||
// use SHA-512 with 32 bytes of output as required by HAP.
|
||||
|
||||
namespace HKDF{
|
||||
int create(uint8_t *outputKey, uint8_t *inputKey, int inputLen, const char *salt, const char *info); // output of HKDF is always a 32-byte key derived from an input key, a salt string, and an info string
|
||||
};
|
||||
59
ESP32/HomeSpan-master/src/HapQR.h
Normal file
59
ESP32/HomeSpan-master/src/HapQR.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
class HapQR {
|
||||
private:
|
||||
char qrCode[21];
|
||||
|
||||
public:
|
||||
enum {
|
||||
NFC=1,
|
||||
IP=2,
|
||||
BLTE=4
|
||||
};
|
||||
|
||||
char *get(uint32_t setupCode, const char *setupID, uint8_t category, uint8_t protocols=IP, uint8_t qVersion=0, uint8_t qReserved=0){
|
||||
|
||||
setupCode&=0x07FFFFFF; // valid values: 0-99999999
|
||||
qVersion&=0x7; // valid values: 0-7
|
||||
qReserved&=0xF; // valid values: 0-15
|
||||
protocols&=0x7; // valid values: 0-7
|
||||
|
||||
uint64_t n=((uint64_t) qVersion<<43) | ((uint64_t) qReserved<<39) | ((uint64_t) category<<31) | (protocols<<27) | setupCode;
|
||||
sprintf(qrCode,"X-HM://");
|
||||
|
||||
for(int i=15;i>=7;i--){
|
||||
qrCode[i]=n%36+48;
|
||||
if(qrCode[i]>57)
|
||||
qrCode[i]+=7;
|
||||
n/=36;
|
||||
}
|
||||
|
||||
sprintf(qrCode+16,"%-4.4s",setupID);
|
||||
return(qrCode);
|
||||
} // create
|
||||
};
|
||||
2757
ESP32/HomeSpan-master/src/HomeSpan.cpp
Normal file
2757
ESP32/HomeSpan-master/src/HomeSpan.cpp
Normal file
File diff suppressed because it is too large
Load Diff
832
ESP32/HomeSpan-master/src/HomeSpan.h
Normal file
832
ESP32/HomeSpan-master/src/HomeSpan.h
Normal file
@@ -0,0 +1,832 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wpmf-conversions" // eliminates warning messages from use of pointers to member functions to detect whether update() and loop() are overridden by user
|
||||
#pragma GCC diagnostic ignored "-Wunused-result" // eliminates warning message regarded unused result from call to crypto_scalarmult_curve25519()
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <nvs.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_now.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include "src/extras/Blinker.h"
|
||||
#include "src/extras/Pixel.h"
|
||||
#include "src/extras/RFControl.h"
|
||||
#include "src/extras/PwmPin.h"
|
||||
#include "src/extras/StepperControl.h"
|
||||
|
||||
#include "Settings.h"
|
||||
#include "Utils.h"
|
||||
#include "Network.h"
|
||||
#include "HAPConstants.h"
|
||||
#include "HapQR.h"
|
||||
#include "Characteristics.h"
|
||||
#include "TLV8.h"
|
||||
|
||||
using std::vector;
|
||||
using std::unordered_map;
|
||||
using std::list;
|
||||
|
||||
enum {
|
||||
GET_AID=1,
|
||||
GET_META=2,
|
||||
GET_PERMS=4,
|
||||
GET_TYPE=8,
|
||||
GET_EV=16,
|
||||
GET_DESC=32,
|
||||
GET_NV=64,
|
||||
GET_VALUE=128,
|
||||
GET_STATUS=256
|
||||
};
|
||||
|
||||
typedef boolean BOOL_t;
|
||||
typedef uint8_t UINT8_t;
|
||||
typedef uint16_t UINT16_t;
|
||||
typedef uint32_t UINT32_t;
|
||||
typedef uint64_t UINT64_t;
|
||||
typedef int32_t INT_t;
|
||||
typedef double FLOAT_t;
|
||||
typedef const char * STRING_t;
|
||||
typedef const TLV8 & TLV_ENC_t;
|
||||
typedef std::pair<const uint8_t *, size_t> DATA_t;
|
||||
|
||||
static DATA_t NULL_DATA={NULL,0};
|
||||
static TLV8 NULL_TLV{};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {homeSpan.statusLED->LED_UPDATE;if(homeSpan.statusCallback)homeSpan.statusCallback(MESSAGE_UPDATE);}
|
||||
|
||||
enum HS_STATUS {
|
||||
HS_WIFI_NEEDED, // WiFi Credentials have not yet been set/stored
|
||||
HS_WIFI_CONNECTING, // HomeSpan is trying to connect to the network specified in the stored WiFi Credentials
|
||||
HS_PAIRING_NEEDED, // HomeSpan is connected to central WiFi network, but device has not yet been paired to HomeKit
|
||||
HS_PAIRED, // HomeSpan is connected to central WiFi network and ther device has been paired to HomeKit
|
||||
HS_ENTERING_CONFIG_MODE, // User has requested the device to enter into Command Mode
|
||||
HS_CONFIG_MODE_EXIT, // HomeSpan is in Command Mode with "Exit Command Mode" specified as choice
|
||||
HS_CONFIG_MODE_REBOOT, // HomeSpan is in Command Mode with "Reboot" specified as choice
|
||||
HS_CONFIG_MODE_LAUNCH_AP, // HomeSpan is in Command Mode with "Launch Access Point" specified as choice
|
||||
HS_CONFIG_MODE_UNPAIR, // HomeSpan is in Command Mode with "Unpair Device" specified as choice
|
||||
HS_CONFIG_MODE_ERASE_WIFI, // HomeSpan is in Command Mode with "Erase WiFi Credentials" specified as choice
|
||||
HS_CONFIG_MODE_EXIT_SELECTED, // User has selected "Exit Command Mode"
|
||||
HS_CONFIG_MODE_REBOOT_SELECTED, // User has select "Reboot" from the Command Mode
|
||||
HS_CONFIG_MODE_LAUNCH_AP_SELECTED, // User has selected "Launch AP Access" from the Command Mode
|
||||
HS_CONFIG_MODE_UNPAIR_SELECTED, // User has seleected "Unpair Device" from the Command Mode
|
||||
HS_CONFIG_MODE_ERASE_WIFI_SELECTED, // User has selected "Erase WiFi Credentials" from the Command Mode
|
||||
HS_REBOOTING, // HomeSpan is in the process of rebooting the device
|
||||
HS_FACTORY_RESET, // HomeSpan is in the process of performing a Factory Reset of device
|
||||
HS_AP_STARTED, // HomeSpan has started the Access Point but no one has yet connected
|
||||
HS_AP_CONNECTED, // The Access Point is started and a user device has been connected
|
||||
HS_AP_TERMINATED, // HomeSpan has terminated the Access Point
|
||||
HS_OTA_STARTED // HomeSpan is in the process of recveived an Over-the-Air software update
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
// Forward-Declarations
|
||||
|
||||
struct Span;
|
||||
struct SpanAccessory;
|
||||
struct SpanService;
|
||||
struct SpanCharacteristic;
|
||||
struct SpanBuf;
|
||||
struct SpanButton;
|
||||
struct SpanUserCommand;
|
||||
|
||||
struct HAPClient;
|
||||
class Controller;
|
||||
|
||||
extern Span homeSpan;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// INTERNAL HOMESPAN STRUCTURES - NOT FOR USER ACCESS //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
struct SpanPartition{
|
||||
char magicCookie[32];
|
||||
uint8_t reserved[224];
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanConfig{
|
||||
int configNumber=0; // configuration number - broadcast as Bonjour "c#" (computed automatically)
|
||||
uint8_t hashCode[48]={0}; // SHA-384 hash of Span Database stored as a form of unique "signature" to know when to update the config number upon changes
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanBuf{ // temporary storage buffer for use with putCharacteristicsURL() and checkTimedResets()
|
||||
uint32_t aid=0; // updated aid
|
||||
uint32_t iid=0; // updated iid
|
||||
boolean wr=false; // flag to indicate write-response has been requested
|
||||
char *val=NULL; // updated value (optional, though either at least 'val' or 'ev' must be specified)
|
||||
char *ev=NULL; // updated event notification flag (optional, though either at least 'val' or 'ev' must be specified)
|
||||
StatusCode status; // return status (HAP Table 6-11)
|
||||
SpanCharacteristic *characteristic=NULL; // Characteristic to update (NULL if not found)
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanWebLog{ // optional web status/log data
|
||||
boolean isEnabled=false; // flag to inidicate WebLog has been enabled
|
||||
uint16_t maxEntries=0; // max number of log entries;
|
||||
int nEntries=0; // total cumulative number of log entries
|
||||
const char *timeServer=NULL; // optional time server to use for acquiring clock time
|
||||
const char *timeZone; // optional time-zone specification
|
||||
boolean timeInit=false; // flag to indicate time has been initialized
|
||||
char bootTime[33]="Unknown"; // boot time
|
||||
String statusURL; // URL of status log
|
||||
uint32_t waitTime=120000; // number of milliseconds to wait for initial connection to time server
|
||||
String css=""; // optional user-defined style sheet for web log
|
||||
|
||||
struct log_t { // log entry type
|
||||
uint64_t upTime; // number of seconds since booting
|
||||
struct tm clockTime; // clock time
|
||||
char *message; // pointers to log entries of arbitrary size
|
||||
String clientIP; // IP address of client making request (or "0.0.0.0" if not applicable)
|
||||
} *log=NULL; // array of log entries
|
||||
|
||||
void init(uint16_t maxEntries, const char *serv, const char *tz, const char *url);
|
||||
static void initTime(void *args);
|
||||
void vLog(boolean sysMsg, const char *fmr, va_list ap);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanOTA{ // manages OTA process
|
||||
|
||||
char otaPwd[33]=""; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
||||
|
||||
static boolean enabled; // enables OTA - default if not enabled
|
||||
static boolean auth; // indicates whether OTA password is required
|
||||
static int otaPercent;
|
||||
static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch
|
||||
|
||||
int init(boolean auth, boolean safeLoad, const char *pwd);
|
||||
int setPassword(const char *pwd);
|
||||
static void start();
|
||||
static void end();
|
||||
static void progress(uint32_t progress, uint32_t total);
|
||||
static void error(ota_error_t err);
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
// USER API CLASSES BEGINS HERE //
|
||||
//////////////////////////////////////
|
||||
|
||||
class Span{
|
||||
|
||||
friend class SpanAccessory;
|
||||
friend class SpanService;
|
||||
friend class SpanCharacteristic;
|
||||
friend class SpanUserCommand;
|
||||
friend class SpanButton;
|
||||
friend class SpanWebLog;
|
||||
friend class SpanOTA;
|
||||
friend class Network;
|
||||
friend class HAPClient;
|
||||
|
||||
char *displayName; // display name for this device - broadcast as part of Bonjour MDNS
|
||||
char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended
|
||||
char *hostNameSuffix=NULL; // optional "suffix" of hostName of this device. If specified, will be used as the hostName suffix instead of the 6-byte accessoryID
|
||||
char *hostName=NULL; // derived full hostname
|
||||
char *modelName; // model name of this device - broadcast as Bonjour field "md"
|
||||
char category[3]=""; // category ID of primary accessory - broadcast as Bonjour field "ci" (HAP Section 13)
|
||||
unsigned long snapTime; // current time (in millis) snapped before entering Service loops() or updates()
|
||||
boolean isInitialized=false; // flag indicating HomeSpan has been initialized
|
||||
boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation)
|
||||
HapQR qrCode; // optional QR Code to use for pairing
|
||||
const char *sketchVersion="n/a"; // version of the sketch
|
||||
char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode()
|
||||
String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel
|
||||
boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256)
|
||||
boolean serialInputDisabled=false; // flag indiating that serial input is disabled
|
||||
uint8_t rebootCount=0; // counts number of times device was rebooted (used in optional Reboot callback)
|
||||
uint32_t rebootCallbackTime; // length of time to wait (in milliseconds) before calling optional Reboot callback
|
||||
|
||||
nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data
|
||||
nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data
|
||||
nvs_handle otaNVS; // handle for non-volatile storage of OTA data
|
||||
nvs_handle srpNVS; // handle for non-volatile storage of SRP data
|
||||
nvs_handle hapNVS; // handle for non-volatile-storage of HAP data
|
||||
|
||||
int connected=0; // WiFi connection status (increments upon each connect and disconnect)
|
||||
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
|
||||
unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again
|
||||
|
||||
const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing
|
||||
uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED
|
||||
int logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor
|
||||
unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations
|
||||
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
|
||||
char qrID[5]=""; // Setup ID used for pairing with QR Code
|
||||
void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is initially established
|
||||
void (*wifiCallbackAll)(int)=NULL; // optional callback function to invoke every time WiFi connectivity is established or re-established
|
||||
void (*weblogCallback)(String &)=NULL; // optional callback function to invoke after header table in Web Log is produced
|
||||
void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false)
|
||||
boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found
|
||||
void (*apFunction)()=NULL; // optional function to invoke when starting Access Point
|
||||
void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes
|
||||
void (*rebootCallback)(uint8_t)=NULL; // optional callback when device reboots
|
||||
void (*controllerCallback)()=NULL; // optional callback when Controller is added/removed/changed
|
||||
|
||||
WiFiServer *hapServer; // pointer to the HAP Server connection
|
||||
Blinker *statusLED; // indicates HomeSpan status
|
||||
Blinkable *statusDevice = NULL; // the device used for the Blinker
|
||||
PushButton *controlButton = NULL; // controls HomeSpan configuration and resets
|
||||
Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
|
||||
SpanWebLog webLog; // optional web status/log
|
||||
TaskHandle_t pollTaskHandle = NULL; // optional task handle to use for poll() function
|
||||
TaskHandle_t loopTaskHandle; // Arduino Loop Task handle
|
||||
boolean verboseWifiReconnect = true; // set to false to not print WiFi reconnect attempts messages
|
||||
|
||||
SpanOTA spanOTA; // manages OTA process
|
||||
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
|
||||
|
||||
list<HAPClient, Mallocator<HAPClient>> hapList; // linked-list of HAPClient structures containing HTTP client connections, parsing routines, and state variables
|
||||
list<HAPClient, Mallocator<HAPClient>>::iterator currentClient; // iterator to current client
|
||||
vector<SpanAccessory *, Mallocator<SpanAccessory *>> Accessories; // vector of pointers to all Accessories
|
||||
vector<SpanService *, Mallocator<SpanService *>> Loops; // vector of pointer to all Services that have over-ridden loop() methods
|
||||
vector<SpanBuf, Mallocator<SpanBuf>> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
|
||||
vector<SpanButton *, Mallocator<SpanButton *>> PushButtons; // vector of pointer to all PushButtons
|
||||
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
|
||||
unordered_map<char, SpanUserCommand *> UserCommands; // map of pointers to all UserCommands
|
||||
|
||||
void pollTask(); // poll HAP Clients and process any new HAP requests
|
||||
void checkConnect(); // check WiFi connection; connect if needed
|
||||
void commandMode(); // allows user to control and reset HomeSpan settings with the control button
|
||||
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
|
||||
void reboot(); // reboots device
|
||||
|
||||
void printfAttributes(int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // writes Attributes JSON database to hapOut stream
|
||||
|
||||
SpanCharacteristic *find(uint32_t aid, uint32_t iid); // return Characteristic with matching aid and iid (else NULL if not found)
|
||||
int countCharacteristics(char *buf); // return number of characteristic objects referenced in PUT /characteristics JSON request
|
||||
int updateCharacteristics(char *buf, SpanBuf *pObj); // parses PUT /characteristics JSON request 'buf into 'pObj' and updates referenced characteristics; returns 1 on success, 0 on fail
|
||||
void printfAttributes(SpanBuf *pObj, int nObj); // writes SpanBuf objects to hapOut stream
|
||||
boolean printfAttributes(char **ids, int numIDs, int flags); // writes accessory requested characteristic ids to hapOut stream - returns true if all characteristics are found and readable, else returns false
|
||||
void clearNotify(HAPClient *hc); // clear all notifications related to specific client connection
|
||||
void printfNotify(SpanBuf *pObj, int nObj, HAPClient *hc); // writes notification JSON to hapOut stream based on SpanBuf objects and specified connection
|
||||
|
||||
static boolean invalidUUID(const char *uuid){
|
||||
int x=0;
|
||||
sscanf(uuid,"%*8[0-9a-fA-F]%n",&x); // check for short-form of UUID
|
||||
if(strlen(uuid)==x && uuid[0]!='0')
|
||||
return(false);
|
||||
sscanf(uuid,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x);
|
||||
return(strlen(uuid)!=36 || x!=36);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Span(); // constructor
|
||||
|
||||
void begin(Category catID=DEFAULT_CATEGORY,
|
||||
const char *displayName=DEFAULT_DISPLAY_NAME,
|
||||
const char *hostNameBase=DEFAULT_HOST_NAME,
|
||||
const char *modelName=DEFAULT_MODEL_NAME);
|
||||
|
||||
void poll(); // calls pollTask() with some error checking
|
||||
void processSerialCommand(const char *c); // process command 'c' (typically from readSerial, though can be called with any 'c')
|
||||
|
||||
boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed
|
||||
boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false
|
||||
|
||||
Span& setControlPin(uint8_t pin, PushButton::triggerType_t triggerType=PushButton::TRIGGER_ON_LOW){ // sets Control Pin, with optional trigger type
|
||||
controlButton=new PushButton(pin, triggerType);
|
||||
return(*this);
|
||||
}
|
||||
|
||||
int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined)
|
||||
|
||||
Span& setStatusPin(uint8_t pin){statusDevice=new GenericLED(pin);return(*this);} // sets Status Device to a simple LED on specified pin
|
||||
Span& setStatusPixel(uint8_t pin,float h=0,float s=100,float v=100){ // sets Status Device to an RGB Pixel on specified pin
|
||||
statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v)));
|
||||
return(*this);
|
||||
}
|
||||
Span& setStatusDevice(Blinkable *sDev){statusDevice=sDev;return(*this);} // sets Status Device to a generic Blinkable object
|
||||
|
||||
Span& setStatusAutoOff(uint16_t duration){autoOffLED=duration;return(*this);} // sets Status LED auto off (seconds)
|
||||
int getStatusPin(){return(statusLED->getPin());} // get Status Pin (returns -1 if undefined)
|
||||
void refreshStatusDevice(){if(statusLED)statusLED->refresh();} // refreshes state of Status LED
|
||||
|
||||
Span& setApSSID(const char *ssid){network.apSSID=ssid;return(*this);} // sets Access Point SSID
|
||||
Span& setApPassword(const char *pwd){network.apPassword=pwd;return(*this);} // sets Access Point Password
|
||||
Span& setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;return(*this);} // sets Access Point Timeout (seconds)
|
||||
Span& setCommandTimeout(uint16_t nSec){comModeLife=nSec*1000;return(*this);} // sets Command Mode Timeout (seconds)
|
||||
Span& setLogLevel(int level){logLevel=level;return(*this);} // sets Log Level for log messages (0=baseline, 1=intermediate, 2=all, -1=disable all serial input/output)
|
||||
int getLogLevel(){return(logLevel);} // get Log Level
|
||||
Span& setSerialInputDisable(boolean val){serialInputDisabled=val;return(*this);} // sets whether serial input is disabled (true) or enabled (false)
|
||||
boolean getSerialInputDisable(){return(serialInputDisabled);} // returns true if serial input is disabled, or false if serial input in enabled
|
||||
Span& setPortNum(uint16_t port){tcpPortNum=port;return(*this);} // sets the TCP port number to use for communications between HomeKit and HomeSpan
|
||||
Span& setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
|
||||
Span& setSketchVersion(const char *sVer){sketchVersion=sVer;return(*this);} // set optional sketch version number
|
||||
const char *getSketchVersion(){return sketchVersion;} // get sketch version number
|
||||
Span& setWifiCallback(void (*f)()){wifiCallback=f;return(*this);} // sets an optional user-defined function to call once WiFi connectivity is initially established
|
||||
Span& setWifiCallbackAll(void (*f)(int)){wifiCallbackAll=f;return(*this);} // sets an optional user-defined function to call every time WiFi connectivity is established or re-established
|
||||
Span& setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;return(*this);} // sets an optional user-defined function to call when Pairing is established (true) or lost (false)
|
||||
Span& setApFunction(void (*f)()){apFunction=f;return(*this);} // sets an optional user-defined function to call when activating the WiFi Access Point
|
||||
Span& enableAutoStartAP(){autoStartAPEnabled=true;return(*this);} // enables auto start-up of Access Point when WiFi Credentials not found
|
||||
Span& setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials
|
||||
Span& setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;return(*this);} // sets an optional user-defined function to call when HomeSpan status changes
|
||||
const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages
|
||||
Span& setPairingCode(const char *s, boolean progCall=true); // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
||||
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
||||
Span& resetIID(uint32_t newIID); // resets the IID count for the current Accessory to start at newIID
|
||||
Span& setControllerCallback(void (*f)()){controllerCallback=f;return(*this);} // sets an optional user-defined function to call whenever a Controller is added/removed/changed
|
||||
|
||||
Span& setHostNameSuffix(const char *suffix){asprintf(&hostNameSuffix,"%s",suffix);return(*this);} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
|
||||
|
||||
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
|
||||
|
||||
Span& enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
||||
webLog.init(maxEntries, serv, tz, url);
|
||||
return(*this);
|
||||
}
|
||||
|
||||
void addWebLog(boolean sysMsg, const char *fmt, ...){ // add Web Log entry
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
webLog.vLog(sysMsg,fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
Span& setWebLogCSS(const char *css){webLog.css="\n" + String(css) + "\n";return(*this);}
|
||||
Span& setWebLogCallback(void (*f)(String &)){weblogCallback=f;return(*this);}
|
||||
void getWebLog(void (*f)(const char *, void *), void *);
|
||||
|
||||
Span& setVerboseWifiReconnect(bool verbose=true){verboseWifiReconnect=verbose;return(*this);}
|
||||
|
||||
Span& setRebootCallback(void (*f)(uint8_t),uint32_t t=DEFAULT_REBOOT_CALLBACK_TIME){rebootCallback=f;rebootCallbackTime=t;return(*this);}
|
||||
|
||||
void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask()
|
||||
xTaskCreateUniversal([](void *parms){
|
||||
for(;;){
|
||||
homeSpan.pollTask();
|
||||
vTaskDelay(5);
|
||||
}
|
||||
},
|
||||
"pollTask", stackSize, NULL, priority, &pollTaskHandle, cpu);
|
||||
LOG0("\n*** AutoPolling Task started with priority=%d\n\n",uxTaskPriorityGet(pollTaskHandle));
|
||||
}
|
||||
|
||||
TaskHandle_t getAutoPollTask(){return(pollTaskHandle);}
|
||||
|
||||
Span& setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;return(*this);} // sets wait time (in seconds) for optional web log time server to connect
|
||||
|
||||
list<Controller, Mallocator<Controller>>::const_iterator controllerListBegin();
|
||||
list<Controller, Mallocator<Controller>>::const_iterator controllerListEnd();
|
||||
|
||||
[[deprecated("This function has been deprecated (it is not needed) and no longer does anything. Please remove from sketch to ensure backwards compatilibilty with future versions.")]]
|
||||
Span& reserveSocketConnections(uint8_t n){return(*this);}
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanAccessory{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
friend class SpanCharacteristic;
|
||||
friend class SpanButton;
|
||||
|
||||
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
|
||||
uint32_t iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
|
||||
vector<SpanService *, Mallocator<SpanService*>> Services; // vector of pointers to all Services in this Accessory
|
||||
|
||||
void printfAttributes(int flags); // writes Accessory JSON to hapOut stream
|
||||
|
||||
protected:
|
||||
|
||||
~SpanAccessory(); // destructor
|
||||
|
||||
public:
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
SpanAccessory(uint32_t aid=0); // constructor
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanService{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanAccessory;
|
||||
friend class SpanCharacteristic;
|
||||
|
||||
uint32_t iid=0; // Instance ID (HAP Table 6-2)
|
||||
const char *type; // Service Type
|
||||
const char *hapName; // HAP Name
|
||||
boolean hidden=false; // optional property indicating service is hidden
|
||||
boolean primary=false; // optional property indicating service is primary
|
||||
vector<SpanCharacteristic *, Mallocator<SpanCharacteristic*>> Characteristics; // vector of pointers to all Characteristics in this Service
|
||||
vector<SpanService *, Mallocator<SpanService *>> linkedServices; // vector of pointers to any optional linked Services
|
||||
boolean isCustom; // flag to indicate this is a Custom Service
|
||||
SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service
|
||||
|
||||
void printfAttributes(int flags); // writes Service JSON to hapOut stream
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~SpanService(); // destructor
|
||||
vector<HapChar *, Mallocator<HapChar*>> req; // vector of pointers to all required HAP Characteristic Types for this Service
|
||||
vector<HapChar *, Mallocator<HapChar*>> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
|
||||
|
||||
public:
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
SpanService(const char *type, const char *hapName, boolean isCustom=false); // constructor
|
||||
SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self
|
||||
SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self
|
||||
SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self
|
||||
|
||||
template <typename T=SpanService *> vector<T, Mallocator<T>> getLinks(const char *hapName=NULL){ // returns linkedServices vector, mapped to <T>, for use as range in "for-each" loops
|
||||
vector<T, Mallocator<T>> v;
|
||||
for(auto svc : linkedServices){
|
||||
if(hapName==NULL || !strcmp(hapName,svc->hapName))
|
||||
v.push_back(static_cast<T>(svc));
|
||||
}
|
||||
return(v);
|
||||
}
|
||||
|
||||
uint32_t getIID(){return(iid);} // returns IID of Service
|
||||
|
||||
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
|
||||
virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code
|
||||
virtual void button(int pin, int pressType){} // method called for a Service when a button attached to "pin" has a Single, Double, or Long Press, according to pressType
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanCharacteristic{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
|
||||
union UVal {
|
||||
boolean BOOL;
|
||||
uint8_t UINT8;
|
||||
uint16_t UINT16;
|
||||
uint32_t UINT32;
|
||||
uint64_t UINT64;
|
||||
int32_t INT;
|
||||
double FLOAT;
|
||||
char * STRING = NULL;
|
||||
};
|
||||
|
||||
class EVLIST : public vector<HAPClient *, Mallocator<HAPClient *>>{ // vector of current connections that have subscribed to EV notifications for this Characteristic
|
||||
public:
|
||||
boolean has(HAPClient *hc); // returns true if pointer to connection hc is subscribed, else returns false
|
||||
void add(HAPClient *hc); // adds connection hc as new subscriber, IF not already a subscriber
|
||||
void remove(HAPClient *hc); // removes connection hc as a subscriber; okay to remove even if hc was not already a subscriber
|
||||
};
|
||||
|
||||
uint32_t iid=0; // Instance ID (HAP Table 6-3)
|
||||
HapChar *hapChar; // pointer to HAP Characteristic structure
|
||||
const char *type; // Characteristic Type
|
||||
const char *hapName; // HAP Name
|
||||
UVal value; // Characteristic Value
|
||||
uint8_t perms; // Characteristic Permissions
|
||||
FORMAT format; // Characteristic Format
|
||||
char *desc=NULL; // Characteristic Description (optional)
|
||||
char *unit=NULL; // Characteristic Unit (optional)
|
||||
UVal minValue; // Characteristic minimum (not applicable for STRING)
|
||||
UVal maxValue; // Characteristic maximum (not applicable for STRING)
|
||||
UVal stepValue; // Characteristic step size (not applicable for STRING)
|
||||
boolean staticRange; // Flag that indicates whether Range is static and cannot be changed with setRange()
|
||||
boolean customRange=false; // Flag for custom ranges
|
||||
char *validValues=NULL; // Optional JSON array of valid values. Applicable only to uint8 Characteristics
|
||||
char *nvsKey=NULL; // key for NVS storage of Characteristic value
|
||||
boolean isCustom; // flag to indicate this is a Custom Characteristic
|
||||
boolean setRangeError=false; // flag to indicate attempt to set Range on Characteristic that does not support changes to Range
|
||||
boolean setValidValuesError=false; // flag to indicate attempt to set Valid Values on Characteristic that does not support changes to Valid Values
|
||||
|
||||
uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic
|
||||
uint8_t updateFlag=0; // set to either 1 (for normal write) or 2 (for write-response) inside update() when Characteristic is successfully updated via Home App
|
||||
unsigned long updateTime=0; // last time value was updated (in millis) either by PUT /characteristic OR by setVal()
|
||||
UVal newValue; // the updated value requested by PUT /characteristic
|
||||
SpanService *service=NULL; // pointer to Service containing this Characteristic
|
||||
EVLIST evList; // vector of current connections that have subscribed to EV notifications for this Characteristic
|
||||
|
||||
void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream
|
||||
StatusCode loadUpdate(char *val, char *ev, boolean wr); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.)
|
||||
String uvPrint(UVal &u); // returns "printable" String for any type of Characteristic
|
||||
|
||||
void uvSet(UVal &dest, UVal &src); // copies UVal src into UVal dest
|
||||
void uvSet(UVal &u, STRING_t val); // copies string val into UVal u
|
||||
void uvSet(UVal &u, DATA_t data); // copies DATA data into UVal u (after transforming to a char *)
|
||||
void uvSet(UVal &u, TLV_ENC_t tlv); // copies TLV8 tlv into UVal u (after transforming to a char *)
|
||||
|
||||
template <typename T> void uvSet(UVal &u, T val){ // copies numeric val into UVal u
|
||||
switch(format){
|
||||
case FORMAT::BOOL:
|
||||
u.BOOL=(boolean)val;
|
||||
break;
|
||||
case FORMAT::INT:
|
||||
u.INT=(int)val;
|
||||
break;
|
||||
case FORMAT::UINT8:
|
||||
u.UINT8=(uint8_t)val;
|
||||
break;
|
||||
case FORMAT::UINT16:
|
||||
u.UINT16=(uint16_t)val;
|
||||
break;
|
||||
case FORMAT::UINT32:
|
||||
u.UINT32=(uint32_t)val;
|
||||
break;
|
||||
case FORMAT::UINT64:
|
||||
u.UINT64=(uint64_t)val;
|
||||
break;
|
||||
case FORMAT::FLOAT:
|
||||
u.FLOAT=(double)val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
char *getStringGeneric(UVal &val); // gets the specified UVal for string-based Characteristics
|
||||
size_t getDataGeneric(uint8_t *data, size_t len, UVal &val); // gets the specified UVal for data-based Characteristics
|
||||
size_t getTLVGeneric(TLV8 &tlv, UVal &val); // gets the specified UVal for tlv8-based Characteristics
|
||||
|
||||
template <class T> T uvGet(UVal &u){ // gets the specified UVal for numeric-based Characteristics
|
||||
|
||||
switch(format){
|
||||
case FORMAT::BOOL:
|
||||
return((T) u.BOOL);
|
||||
case FORMAT::INT:
|
||||
return((T) u.INT);
|
||||
case FORMAT::UINT8:
|
||||
return((T) u.UINT8);
|
||||
case FORMAT::UINT16:
|
||||
return((T) u.UINT16);
|
||||
case FORMAT::UINT32:
|
||||
return((T) u.UINT32);
|
||||
case FORMAT::UINT64:
|
||||
return((T) u.UINT64);
|
||||
case FORMAT::FLOAT:
|
||||
return((T) u.FLOAT);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return((T)0); // included to prevent compiler warnings
|
||||
}
|
||||
|
||||
void setValCheck(); // initial check before setting value of any Characteristic
|
||||
void setValFinish(boolean notify); // final processing after setting value of any Characteristic
|
||||
|
||||
protected:
|
||||
|
||||
~SpanCharacteristic(); // destructor
|
||||
|
||||
template <typename T> void init(T val, boolean nvsStore, T min, T max){
|
||||
|
||||
uvSet(value,val);
|
||||
|
||||
if(nvsStore){
|
||||
nvsKey=(char *)HS_MALLOC(16);
|
||||
uint16_t t;
|
||||
sscanf(type,"%hx",&t);
|
||||
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
|
||||
size_t len;
|
||||
|
||||
if(format<FORMAT::STRING){
|
||||
if(nvs_get_u64(homeSpan.charNVS,nvsKey,&(value.UINT64))!=ESP_OK) {
|
||||
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
}
|
||||
} else {
|
||||
if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){
|
||||
value.STRING = (char *)HS_REALLOC(value.STRING,len);
|
||||
nvs_get_str(homeSpan.charNVS,nvsKey,value.STRING,&len);
|
||||
}
|
||||
else {
|
||||
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uvSet(newValue,value);
|
||||
|
||||
if(format<FORMAT::STRING){
|
||||
uvSet(minValue,min);
|
||||
uvSet(maxValue,max);
|
||||
uvSet(stepValue,0);
|
||||
}
|
||||
|
||||
} // init()
|
||||
|
||||
public:
|
||||
|
||||
SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // SpanCharacteristic constructor
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
|
||||
template <class T=int> T getVal(){return(uvGet<T>(value));} // gets the value for numeric-based Characteristics
|
||||
char *getString(){return(getStringGeneric(value));} // gets the value for string-based Characteristics
|
||||
size_t getData(uint8_t *data, size_t len){return(getDataGeneric(data,len,value));} // gets the value for data-based Characteristics
|
||||
size_t getTLV(TLV8 &tlv){return(getTLVGeneric(tlv,value));} // gets the value for tlv8-based Characteristics
|
||||
|
||||
template <class T=int> T getNewVal(){return(uvGet<T>(newValue));} // gets the newValue for numeric-based Characteristics
|
||||
char *getNewString(){return(getStringGeneric(newValue));} // gets the newValue for string-based Characteristics
|
||||
size_t getNewData(uint8_t *data, size_t len){return(getDataGeneric(data,len,newValue));} // gets the newValue for data-based Characteristics
|
||||
size_t getNewTLV(TLV8 &tlv){return(getTLVGeneric(tlv,newValue));} // gets the newValue for tlv8-based Characteristics
|
||||
|
||||
void setString(const char *val, boolean notify=true); // sets the value and newValue for string-based Characteristic
|
||||
void setData(const uint8_t *data, size_t len, boolean notify=true); // sets the value and newValue for data-based Characteristic
|
||||
void setTLV(const TLV8 &tlv, boolean notify=true); // sets the value and newValue for tlv8-based Characteristic
|
||||
|
||||
template <typename T> void setVal(T val, boolean notify=true){ // sets the value and newValue for numeric-based Characteristics
|
||||
|
||||
setValCheck();
|
||||
|
||||
if(!((val >= uvGet<T>(minValue)) && (val <= uvGet<T>(maxValue)))){
|
||||
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-responsive!\n\n",
|
||||
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
|
||||
}
|
||||
|
||||
uvSet(value,val);
|
||||
uvSet(newValue,value);
|
||||
|
||||
updateTime=homeSpan.snapTime;
|
||||
|
||||
if(notify){
|
||||
if(updateFlag!=2){ // do not broadcast EV if update is being done in context of write-response
|
||||
SpanBuf sb; // create SpanBuf object
|
||||
sb.characteristic=this; // set characteristic
|
||||
sb.status=StatusCode::OK; // set status
|
||||
char dummy[]="";
|
||||
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
|
||||
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
||||
}
|
||||
|
||||
if(nvsKey){
|
||||
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||
nvs_commit(homeSpan.charNVS);
|
||||
}
|
||||
}
|
||||
|
||||
} // setVal()
|
||||
|
||||
boolean updated(); // returns true within update() if Characteristic was updated by Home App
|
||||
unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated, either by Home App or by using setVal()
|
||||
uint32_t getIID(); // returns IID of Characteristic
|
||||
|
||||
SpanCharacteristic *setPerms(uint8_t perms); // sets permissions of a Characteristic
|
||||
SpanCharacteristic *addPerms(uint8_t dPerms); // add permissions of a Characteristic
|
||||
SpanCharacteristic *removePerms(uint8_t dPerms); // removes permissions of a Characteristic
|
||||
SpanCharacteristic *setDescription(const char *c); // sets description of a Characteristic
|
||||
SpanCharacteristic *setUnit(const char *c); // set unit of a Characteristic
|
||||
SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic - only applicable if format=INT, UINT8, UINT16, or UINT32
|
||||
|
||||
template <typename A, typename B, typename S=int> SpanCharacteristic *setRange(A min, B max, S step=0){ // sets the allowed range of a Characteristic
|
||||
|
||||
if(!staticRange){
|
||||
uvSet(minValue,min);
|
||||
uvSet(maxValue,max);
|
||||
uvSet(stepValue,step);
|
||||
customRange=true;
|
||||
} else
|
||||
setRangeError=true;
|
||||
|
||||
return(this);
|
||||
|
||||
} // setRange()
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanButton : public PushButton {
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
|
||||
uint16_t singleTime; // minimum time (in millis) required to register a single press
|
||||
uint16_t longTime; // minimum time (in millis) required to register a long press
|
||||
uint16_t doubleTime; // maximum time (in millis) between single presses to register a double press instead
|
||||
SpanService *service; // Service to which this PushButton is attached
|
||||
|
||||
void check(); // check PushButton and call button() if "pressed"
|
||||
|
||||
protected:
|
||||
|
||||
enum buttonType_t {
|
||||
HS_BUTTON,
|
||||
HS_TOGGLE
|
||||
};
|
||||
|
||||
buttonType_t buttonType=HS_BUTTON; // type of SpanButton
|
||||
|
||||
public:
|
||||
|
||||
SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||
SpanButton(int pin, triggerType_t triggerType, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200) : SpanButton(pin,longTime,singleTime,doubleTime,triggerType){};
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanToggle : public SpanButton {
|
||||
|
||||
public:
|
||||
|
||||
SpanToggle(int pin, triggerType_t triggerType=TRIGGER_ON_LOW, uint16_t toggleTime=5) : SpanButton(pin,triggerType,toggleTime){buttonType=HS_TOGGLE;};
|
||||
int position(){return(pressType);}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanUserCommand {
|
||||
|
||||
friend class Span;
|
||||
|
||||
const char *s; // description of command
|
||||
void (*userFunction1)(const char *v)=NULL; // user-defined function to call
|
||||
void (*userFunction2)(const char *v, void *arg)=NULL; // user-defined function to call with user-defined arg
|
||||
void *userArg;
|
||||
|
||||
public:
|
||||
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *));
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *, void *), void *arg);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanPoint {
|
||||
|
||||
friend class Span;
|
||||
|
||||
int receiveSize; // size (in bytes) of messages to receive
|
||||
int sendSize; // size (in bytes) of messages to send
|
||||
esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data
|
||||
QueueHandle_t receiveQueue; // queue to store data after it is received
|
||||
uint32_t receiveTime=0; // time (in millis) of most recent data received
|
||||
|
||||
static uint8_t lmk[16];
|
||||
static boolean initialized;
|
||||
static boolean isHub;
|
||||
static boolean useEncryption;
|
||||
static vector<SpanPoint *, Mallocator<SpanPoint *>> SpanPoints;
|
||||
static uint16_t channelMask; // channel mask (only used for remote devices)
|
||||
static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send
|
||||
static nvs_handle pointNVS; // NVS storage for channel number (only used for remote devices)
|
||||
|
||||
static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len);
|
||||
static void init(const char *password="HomeSpan");
|
||||
static void setAsHub(){isHub=true;}
|
||||
static uint8_t nextChannel();
|
||||
|
||||
static void dataSent(const uint8_t *mac, esp_now_send_status_t status) {
|
||||
xQueueOverwrite( statusQueue, &status );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1, boolean useAPaddress=false);
|
||||
static void setPassword(const char *pwd){init(pwd);}
|
||||
static void setChannelMask(uint16_t mask);
|
||||
static void setEncryption(boolean encrypt){useEncryption=encrypt;}
|
||||
boolean get(void *dataBuf);
|
||||
boolean send(const void *data);
|
||||
uint32_t time(){return(millis()-receiveTime);}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
#include "Span.h"
|
||||
421
ESP32/HomeSpan-master/src/Network.cpp
Normal file
421
ESP32/HomeSpan-master/src/Network.cpp
Normal file
@@ -0,0 +1,421 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include <DNSServer.h>
|
||||
|
||||
#include "Network.h"
|
||||
#include "HomeSpan.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::scan(){
|
||||
|
||||
int n=WiFi.scanNetworks();
|
||||
|
||||
free(ssidList);
|
||||
ssidList=(char **)HS_CALLOC(n,sizeof(char *));
|
||||
numSSID=0;
|
||||
|
||||
for(int i=0;i<n;i++){
|
||||
boolean found=false;
|
||||
for(int j=0;j<numSSID;j++){
|
||||
if(!strcmp(WiFi.SSID(i).c_str(),ssidList[j]))
|
||||
found=true;
|
||||
}
|
||||
if(!found){
|
||||
ssidList[numSSID]=(char *)HS_CALLOC(WiFi.SSID(i).length()+1,sizeof(char));
|
||||
sprintf(ssidList[numSSID],"%s",WiFi.SSID(i).c_str());
|
||||
numSSID++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::serialConfigure(){
|
||||
|
||||
wifiData.ssid[0]='\0';
|
||||
wifiData.pwd[0]='\0';
|
||||
|
||||
LOG0("*** WiFi Setup - Scanning for Networks...\n\n");
|
||||
|
||||
scan(); // scan for networks
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
LOG0(" %d) %s\n",i+1,ssidList[i]);
|
||||
|
||||
while(!strlen(wifiData.ssid)){
|
||||
LOG0("\n>>> WiFi SSID: ");
|
||||
readSerial(wifiData.ssid,MAX_SSID);
|
||||
if(atoi(wifiData.ssid)>0 && atoi(wifiData.ssid)<=numSSID){
|
||||
strcpy(wifiData.ssid,ssidList[atoi(wifiData.ssid)-1]);
|
||||
}
|
||||
LOG0("%s\n",wifiData.ssid);
|
||||
}
|
||||
|
||||
while(!strlen(wifiData.pwd)){
|
||||
LOG0(">>> WiFi PASS: ");
|
||||
readSerial(wifiData.pwd,MAX_PWD);
|
||||
LOG0("%s\n",mask(wifiData.pwd,2).c_str());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
boolean Network::allowedCode(char *s){
|
||||
return(
|
||||
strcmp(s,"00000000") && strcmp(s,"11111111") && strcmp(s,"22222222") && strcmp(s,"33333333") &&
|
||||
strcmp(s,"44444444") && strcmp(s,"55555555") && strcmp(s,"66666666") && strcmp(s,"77777777") &&
|
||||
strcmp(s,"88888888") && strcmp(s,"99999999") && strcmp(s,"12345678") && strcmp(s,"87654321"));
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::apConfigure(){
|
||||
|
||||
LOG0("*** Starting Access Point: %s / %s\n",apSSID,apPassword);
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_STARTED),HS_AP_STARTED)
|
||||
|
||||
LOG0("\nScanning for Networks...\n\n");
|
||||
|
||||
scan(); // scan for networks
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
LOG0(" %d) %s\n",i+1,ssidList[i]);
|
||||
|
||||
WiFiServer apServer(80);
|
||||
client=0;
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
IPAddress apIP(192, 168, 4, 1);
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(apSSID,apPassword); // start access point
|
||||
dnsServer.start(DNS_PORT, "*", apIP); // start DNS server that resolves every request to the address of this device
|
||||
apServer.begin();
|
||||
|
||||
alarmTimeOut=millis()+lifetime; // Access Point will shut down when alarmTimeOut is reached
|
||||
apStatus=0; // status will be "timed out" unless changed
|
||||
|
||||
LOG0("\nReady.\n");
|
||||
|
||||
while(1){ // loop until we get timed out (which will be accelerated if save/cancel selected)
|
||||
|
||||
if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){
|
||||
LOG0("\n*** Access Point Terminated. Restarting...\n\n");
|
||||
STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED)
|
||||
homeSpan.controlButton->wait();
|
||||
homeSpan.reboot();
|
||||
}
|
||||
|
||||
if(millis()>alarmTimeOut){
|
||||
WiFi.softAPdisconnect(true); // terminate connections and shut down captive access point
|
||||
delay(100);
|
||||
if(apStatus==1){
|
||||
LOG0("\n*** Access Point: Exiting and Saving Settings\n\n");
|
||||
return;
|
||||
} else {
|
||||
if(apStatus==0)
|
||||
LOG0("\n*** Access Point: Timed Out (%ld seconds).",lifetime/1000);
|
||||
else
|
||||
LOG0("\n*** Access Point: Configuration Cancelled.");
|
||||
LOG0(" Restarting...\n\n");
|
||||
STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED)
|
||||
homeSpan.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
dnsServer.processNextRequest();
|
||||
|
||||
if(client=apServer.available()){ // found a new HTTP client
|
||||
LOG2("=======================================\n");
|
||||
LOG1("** Access Point Client Connected: (");
|
||||
LOG1(millis()/1000);
|
||||
LOG1(" sec) ");
|
||||
LOG1(client.remoteIP());
|
||||
LOG1("\n");
|
||||
LOG2("\n");
|
||||
delay(50); // pause to allow data buffer to begin to populate
|
||||
}
|
||||
|
||||
if(client && client.available()){ // if connection exists and data is available
|
||||
|
||||
LOG2("<<<<<<<<< ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" <<<<<<<<<\n");
|
||||
|
||||
int messageSize=client.available();
|
||||
|
||||
if(messageSize>MAX_HTTP){ // exceeded maximum number of bytes allowed
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: HTTP message of %d bytes exceeds maximum allowed (%d)\n\n",messageSize,MAX_HTTP);
|
||||
continue;
|
||||
}
|
||||
|
||||
TempBuffer<uint8_t> httpBuf(messageSize+1); // leave room for null character added below
|
||||
|
||||
int nBytes=client.read(httpBuf,messageSize); // read all available bytes up to maximum allowed+1
|
||||
|
||||
if(nBytes!=messageSize || client.available()!=0){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: HTTP message not read correctly. Expected %d bytes, read %d bytes, %d bytes remaining\n\n",messageSize,nBytes,client.available());
|
||||
continue;
|
||||
}
|
||||
|
||||
httpBuf[nBytes]='\0'; // add null character to enable string functions
|
||||
char *body=(char *)httpBuf.get(); // char pointer to start of HTTP Body
|
||||
char *p; // char pointer used for searches
|
||||
|
||||
if(!(p=strstr((char *)httpBuf.get(),"\r\n\r\n"))){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: Malformed HTTP request (can't find blank line indicating end of BODY)\n\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
*p='\0'; // null-terminate end of HTTP Body to faciliate additional string processing
|
||||
uint8_t *content=(uint8_t *)p+4; // byte pointer to start of optional HTTP Content
|
||||
int cLen=0; // length of optional HTTP Content
|
||||
|
||||
if((p=strstr(body,"Content-Length: "))) // Content-Length is specified
|
||||
cLen=atoi(p+16);
|
||||
if(nBytes!=strlen(body)+4+cLen){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: Malformed HTTP request (Content-Length plus Body Length does not equal total number of bytes read)\n\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG2(body);
|
||||
LOG2("\n------------ END BODY! ------------\n");
|
||||
|
||||
content[cLen]='\0'; // add a trailing null on end of any contents, which should always be text-based
|
||||
|
||||
processRequest(body, (char *)content); // process request
|
||||
|
||||
LOG2("\n");
|
||||
|
||||
} // process Client
|
||||
|
||||
} // while 1
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::processRequest(char *body, char *formData){
|
||||
|
||||
String responseHead="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n";
|
||||
|
||||
String responseBody="<html><meta charset=\"utf-8\"><head><style>"
|
||||
"p{font-size:300%; margin:1em}"
|
||||
"label{font-size:300%; margin:1em}"
|
||||
"input{font-size:250%; margin:1em}"
|
||||
"button{font-size:250%; margin:1em}"
|
||||
"</style></head>"
|
||||
"<body style=\"background-color:lightyellow;\">"
|
||||
"<center><p><b>HomeSpan Setup</b></p></center>";
|
||||
|
||||
if(!strncmp(body,"POST /configure ",16) && // POST CONFIGURE
|
||||
strstr(body,"Content-Type: application/x-www-form-urlencoded")){ // check that content is from a form
|
||||
|
||||
LOG2(formData); // print form data
|
||||
LOG2("\n------------ END DATA! ------------\n");
|
||||
|
||||
LOG1("In Post Configure...\n");
|
||||
|
||||
getFormValue(formData,"network",wifiData.ssid,MAX_SSID);
|
||||
getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD);
|
||||
|
||||
STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING)
|
||||
|
||||
responseBody+="<meta http-equiv = \"refresh\" content = \"" + String(waitTime) + "; url = /wifi-status\" />"
|
||||
"<p>Initiating WiFi connection to:</p><p><b>" + String(wifiData.ssid) + "</p>";
|
||||
|
||||
WiFi.begin(wifiData.ssid,wifiData.pwd);
|
||||
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"POST /save ",11)){ // GET SAVE
|
||||
getFormValue(formData,"code",setupCode,8);
|
||||
|
||||
if(allowedCode(setupCode)){
|
||||
responseBody+="<p><b>Settings saved!</b></p><p>Restarting HomeSpan.</p><p>Closing window...</p>";
|
||||
alarmTimeOut=millis()+2000;
|
||||
apStatus=1;
|
||||
|
||||
} else {
|
||||
responseBody+="<meta http-equiv = \"refresh\" content = \"4; url = /wifi-status\" />"
|
||||
"<p><b>Disallowed Setup Code - too simple!</b></p><p>Returning to configuration page...</p>";
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"GET /cancel ",12)){ // GET CANCEL
|
||||
responseBody+="<p><b>Configuration Canceled!</b></p><p>Restarting HomeSpan.</p><p>Closing window...</p>";
|
||||
alarmTimeOut=millis()+2000;
|
||||
apStatus=-1;
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"GET /wifi-status ",17)){ // GET WIFI-STATUS
|
||||
|
||||
LOG1("In Get WiFi Status...\n");
|
||||
|
||||
if(WiFi.status()!=WL_CONNECTED){
|
||||
waitTime+=2;
|
||||
if(waitTime==12)
|
||||
waitTime=2;
|
||||
responseHead+="Refresh: " + String(waitTime) + "\r\n";
|
||||
responseBody+="<p>Re-initiating connection to:</p><p><b>" + String(wifiData.ssid) + "</b></p>";
|
||||
responseBody+="<p>(waiting " + String(waitTime) + " seconds to check for response)</p>";
|
||||
responseBody+="<p>Access Point termination in " + String((alarmTimeOut-millis())/1000) + " seconds.</p>";
|
||||
responseBody+="<center><button onclick=\"document.location='/hotspot-detect.html'\">Cancel</button></center>";
|
||||
WiFi.begin(wifiData.ssid,wifiData.pwd);
|
||||
|
||||
} else {
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED)
|
||||
|
||||
responseBody+="<p>SUCCESS! Connected to:</p><p><b>" + String(wifiData.ssid) + "</b></p>";
|
||||
responseBody+="<p>You may enter new 8-digit Setup Code below, or leave blank to retain existing code.</p>";
|
||||
|
||||
responseBody+="<form action=\"/save\" method=\"post\">"
|
||||
"<label for=\"code\">Setup Code:</label>"
|
||||
"<center><input size=\"32\" type=\"tel\" id=\"code\" name=\"code\" placeholder=\"12345678\" pattern=\"[0-9]{8}\" maxlength=8></center>"
|
||||
"<center><input style=\"font-size:300%\" type=\"submit\" value=\"SAVE Settings\"></center>"
|
||||
"</form>";
|
||||
|
||||
responseBody+="<center><button style=\"font-size:300%\" onclick=\"document.location='/cancel'\">CANCEL Configuration</button></center>";
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(!strstr(body,"wispr") && !strncmp(body,"GET /hotspot-detect.html ",25)){ // GET LANDING-PAGE, but only if request does NOT contain "wispr" user agent
|
||||
|
||||
LOG1("In Landing Page...\n");
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED)
|
||||
waitTime=2;
|
||||
|
||||
responseBody+="<p>Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.</p>"
|
||||
"<p>The LED on this device should be <em>double-blinking</em> during this configuration.</p>"
|
||||
"<form action=\"/configure\" method=\"post\">"
|
||||
"<label for=\"ssid\">WiFi Network:</label>"
|
||||
"<center><input size=\"32\" list=\"network\" name=\"network\" placeholder=\"Choose or Type\" required maxlength=" + String(MAX_SSID) + "></center>"
|
||||
"<datalist id=\"network\">";
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
responseBody+="<option value=\"" + String(ssidList[i]) + "\">" + String(ssidList[i]) + "</option>";
|
||||
|
||||
responseBody+="</datalist><br><br>"
|
||||
"<label for=\"pwd\">WiFi Password:</label>"
|
||||
"<center><input size=\"32\" type=\"password\" id=\"pwd\" name=\"pwd\" required maxlength=" + String(MAX_PWD) + "></center>"
|
||||
"<br><br>";
|
||||
|
||||
responseBody+="<center><input style=\"font-size:300%\" type=\"submit\" value=\"SUBMIT\"></center>"
|
||||
"</form>";
|
||||
|
||||
responseBody+="<center><button style=\"font-size:300%\" onclick=\"document.location='/cancel'\">CANCEL Configuration</button></center>";
|
||||
|
||||
}
|
||||
|
||||
responseHead+="\r\n"; // add blank line between reponse header and body
|
||||
responseBody+="</body></html>"; // close out body and html tags
|
||||
|
||||
LOG2("\n>>>>>>>>>> ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" >>>>>>>>>>\n");
|
||||
LOG2(responseHead);
|
||||
LOG2(responseBody);
|
||||
LOG2("\n");
|
||||
client.print(responseHead);
|
||||
client.print(responseBody);
|
||||
LOG2("------------ SENT! --------------\n");
|
||||
|
||||
} // processRequest
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Network::getFormValue(char *formData, const char *tag, char *value, int maxSize){
|
||||
|
||||
char *s=strstr(formData,tag); // find start of tag
|
||||
|
||||
if(!s) // if not found, return -1
|
||||
return(-1);
|
||||
|
||||
char *v=index(s,'='); // find '='
|
||||
|
||||
if(!v) // if not found, return -1 (this should not happen)
|
||||
return(-1);
|
||||
|
||||
v++; // point to begining of value
|
||||
int len=0; // track length of value
|
||||
|
||||
while(*v!='\0' && *v!='&' && len<maxSize){ // copy the value until null, '&', or maxSize is reached
|
||||
if(*v=='%'){ // this is an escaped character of form %XX
|
||||
v++;
|
||||
sscanf(v,"%2x",(unsigned int *)value++);
|
||||
v+=2;
|
||||
} else {
|
||||
*value++=(*v=='+'?' ':*v); // HTML Forms use '+' for spaces (and '+' signs are escaped)
|
||||
v++;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
*value='\0'; // add terminating null
|
||||
return(len);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Network::badRequestError(){
|
||||
|
||||
char s[]="HTTP/1.1 400 Bad Request\r\n\r\n";
|
||||
LOG2("\n>>>>>>>>>> ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" >>>>>>>>>>\n");
|
||||
LOG2(s);
|
||||
client.print(s);
|
||||
LOG2("------------ SENT! --------------\n");
|
||||
|
||||
delay(1);
|
||||
client.stop();
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
69
ESP32/HomeSpan-master/src/Network.h
Normal file
69
ESP32/HomeSpan-master/src/Network.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WiFi.h>
|
||||
#include "Settings.h"
|
||||
|
||||
const int MAX_SSID=32; // max number of characters in WiFi SSID
|
||||
const int MAX_PWD=64; // max number of characters in WiFi Password
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct Network {
|
||||
|
||||
const int MAX_HTTP=4095; // max number of bytes in HTTP message
|
||||
|
||||
const char *apSSID=DEFAULT_AP_SSID; // Access Point SSID
|
||||
const char *apPassword=DEFAULT_AP_PASSWORD; // Access Point password (does not need to be secret - only used to ensure encrypted WiFi connection)
|
||||
unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and restarting
|
||||
|
||||
char **ssidList=NULL;
|
||||
int numSSID;
|
||||
|
||||
WiFiClient client; // client used for HTTP calls
|
||||
int waitTime; // time to wait between HTTP refreshed when checking for WiFi connection
|
||||
unsigned long alarmTimeOut; // alarm time after which access point is shut down and HomeSpan is re-started
|
||||
int apStatus; // tracks access point status (0=timed-out, -1=cancel, 1=save)
|
||||
|
||||
struct {
|
||||
char ssid[MAX_SSID+1]="";
|
||||
char pwd[MAX_PWD+1]="";
|
||||
} wifiData;
|
||||
|
||||
char setupCode[8+1];
|
||||
|
||||
void scan(); // scan for WiFi networks and save only those with unique SSIDs
|
||||
void serialConfigure(); // configure homeSpan WiFi from serial monitor
|
||||
boolean allowedCode(char *s); // checks if Setup Code is allowed (HAP defines a list of disallowed codes)
|
||||
void apConfigure(); // configure homeSpan WiFi and Setup Code using temporary Captive Access Point; only returns if sucessful, else ESP restarts
|
||||
void processRequest(char *body, char *formData); // process the HTTP request
|
||||
int getFormValue(char *formData, const char *tag, char *value, int maxSize); // search for 'tag' in 'formData' and copy result into 'value' up to 'maxSize' characters; returns number of characters, else -1 if 'tag' not found
|
||||
int badRequestError(); // return 400 error
|
||||
|
||||
};
|
||||
64
ESP32/HomeSpan-master/src/PSRAM.h
Normal file
64
ESP32/HomeSpan-master/src/PSRAM.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef HS_MALLOC
|
||||
|
||||
#if defined(BOARD_HAS_PSRAM)
|
||||
#define HS_MALLOC ps_malloc
|
||||
#define HS_CALLOC ps_calloc
|
||||
#define HS_REALLOC ps_realloc
|
||||
#define ps_new(X) new(ps_malloc(sizeof(X)))X
|
||||
#else
|
||||
#define HS_MALLOC malloc
|
||||
#define HS_CALLOC calloc
|
||||
#define HS_REALLOC realloc
|
||||
#define ps_new(X) new X
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct Mallocator {
|
||||
typedef T value_type;
|
||||
Mallocator() = default;
|
||||
template <class U> constexpr Mallocator(const Mallocator<U>&) {}
|
||||
[[nodiscard]] T* allocate(std::size_t n) {
|
||||
auto p = static_cast<T*>(HS_MALLOC(n*sizeof(T)));
|
||||
if(p==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",n*sizeof(T));
|
||||
while(1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
|
||||
};
|
||||
template <class T, class U>
|
||||
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
|
||||
template <class T, class U>
|
||||
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
|
||||
|
||||
#endif
|
||||
260
ESP32/HomeSpan-master/src/SRP.cpp
Normal file
260
ESP32/HomeSpan-master/src/SRP.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include <sodium.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "SRP.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SRP6A::SRP6A(){
|
||||
|
||||
// initialize MPI structures
|
||||
|
||||
mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&g);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_mpi_init(&x);
|
||||
mbedtls_mpi_init(&v);
|
||||
mbedtls_mpi_init(&A);
|
||||
mbedtls_mpi_init(&b);
|
||||
mbedtls_mpi_init(&B);
|
||||
mbedtls_mpi_init(&S);
|
||||
mbedtls_mpi_init(&k);
|
||||
mbedtls_mpi_init(&u);
|
||||
mbedtls_mpi_init(&_rr);
|
||||
mbedtls_mpi_init(&t1);
|
||||
mbedtls_mpi_init(&t2);
|
||||
mbedtls_mpi_init(&t3);
|
||||
|
||||
// load N and g into MPI structures
|
||||
|
||||
mbedtls_mpi_read_string(&N,16,N3072);
|
||||
mbedtls_mpi_lset(&g,g3072);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
SRP6A::~SRP6A(){
|
||||
|
||||
mbedtls_mpi_free(&N);
|
||||
mbedtls_mpi_free(&g);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&x);
|
||||
mbedtls_mpi_free(&v);
|
||||
mbedtls_mpi_free(&A);
|
||||
mbedtls_mpi_free(&b);
|
||||
mbedtls_mpi_free(&B);
|
||||
mbedtls_mpi_free(&S);
|
||||
mbedtls_mpi_free(&k);
|
||||
mbedtls_mpi_free(&u);
|
||||
mbedtls_mpi_free(&_rr);
|
||||
mbedtls_mpi_free(&t1);
|
||||
mbedtls_mpi_free(&t2);
|
||||
mbedtls_mpi_free(&t3);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createVerifyCode(const char *setupCode, Verification *vData){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(80); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
char *icp; // storage for I:P
|
||||
|
||||
// generate random salt, s
|
||||
|
||||
randombytes_buf(vData->salt,16); // generate 16 random bytes for salt
|
||||
|
||||
// create I:P
|
||||
|
||||
asprintf(&icp,"%s:%.3s-%.2s-%.3s",I,setupCode,setupCode+3,setupCode+5);
|
||||
|
||||
// compute x = SHA512( s | SHA512( I | ":" | P ) )
|
||||
|
||||
memcpy(tBuf,vData->salt,16); // write salt into first 16 bytes of staging buffer
|
||||
mbedtls_sha512_ret((uint8_t *)icp,strlen(icp),tBuf+16,0); // create hash of username:password and write into last 64 bytes of staging buffer
|
||||
mbedtls_sha512_ret(tBuf,80,tHash,0); // create second hash of salted, hashed username:password
|
||||
mbedtls_mpi_read_binary(&x,tHash,64); // load hash result into x
|
||||
|
||||
// compute v = g^x %N
|
||||
|
||||
mbedtls_mpi_exp_mod(&v,&g,&x,&N,&_rr); // create verifier, v (_rr is an internal "helper" structure that mbedtls uses to speed up subsequent exponential calculations)
|
||||
mbedtls_mpi_write_binary(&v,vData->verifyCode,384); // write v into verifyCode (padding with initial zeros is less than 384 bytes)
|
||||
|
||||
free(icp);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createPublicKey(const Verification *vData, uint8_t *publicKey){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
TempBuffer<uint8_t> privateKey(32); // temporary buffer for generating private key random numbers
|
||||
|
||||
// load stored salt, s, and verification code, v
|
||||
|
||||
mbedtls_mpi_read_binary(&s,vData->salt,16); // load salt into s for use in later steps
|
||||
mbedtls_mpi_read_binary(&v,vData->verifyCode,384); // load verifyCode into v for use below
|
||||
|
||||
// generate random private key, b
|
||||
|
||||
randombytes_buf(privateKey,32); // generate 32 random bytes for private key
|
||||
mbedtls_mpi_read_binary(&b,privateKey,32); // load private key into b
|
||||
|
||||
// compute k = SHA512( N | PAD(g) )
|
||||
|
||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into first half of staging buffer
|
||||
mbedtls_mpi_write_binary(&g,tBuf+384,384); // write g into second half of staging buffer (fully padded with leading zeros)
|
||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||
mbedtls_mpi_read_binary(&k,tHash,64); // load hash result into k
|
||||
|
||||
// compute B = (k*v + g^b) %N
|
||||
|
||||
mbedtls_mpi_mul_mpi(&t1,&k,&v); // t1 = k*v
|
||||
mbedtls_mpi_exp_mod(&t2,&g,&b,&N,&_rr); // t2 = g^b %N
|
||||
mbedtls_mpi_add_mpi(&t3,&t1,&t2); // t3 = t1 + t2
|
||||
mbedtls_mpi_mod_mpi(&B,&t3,&N); // B = t3 %N = ACCESSORY PUBLIC KEY
|
||||
|
||||
mbedtls_mpi_write_binary(&B,publicKey,384); // write B into publicKey (padding with initial zeros is less than 384 bytes)
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createSessionKey(const uint8_t *publicKey, size_t len){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
|
||||
mbedtls_mpi_read_binary(&A,publicKey,len); // load client PublicKey into A
|
||||
|
||||
// compute u = SHA512( PAD(A) | PAD(B) )
|
||||
|
||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||
mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||
mbedtls_mpi_read_binary(&u,tHash,64); // load hash result into mpi structure u
|
||||
|
||||
// compute S = (A * v^u)^b %N
|
||||
|
||||
mbedtls_mpi_exp_mod(&t1,&v,&u,&N,&_rr); // t1 = v^u %N
|
||||
mbedtls_mpi_mul_mpi(&t2,&A,&t1); // t2 = A*t1
|
||||
mbedtls_mpi_mod_mpi(&t1,&t2,&N); // t1 = t2 %N (this is needed to reduce size of t2 before next calculation)
|
||||
mbedtls_mpi_exp_mod(&S,&t1,&b,&N,&_rr); // S = t1^b %N
|
||||
|
||||
// compute K = SHA512( PAD(S) )
|
||||
|
||||
mbedtls_mpi_write_binary(&S,tBuf,384); // write S into staging buffer (only first half of buffer will be used)
|
||||
mbedtls_sha512_ret(tBuf,384,K,0); // create hash of data - this is the SRP SHARED SESSION KEY, K
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
int SRP6A::verifyClientProof(const uint8_t *proof){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(976); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
|
||||
memcpy(M1,proof,64); // load client Proof into M1
|
||||
|
||||
size_t count=0; // total number of bytes for final hash
|
||||
size_t sLen;
|
||||
|
||||
// compute M1V = SHA512( SHA512(N) xor SHA512(g) | SHA512(I) | s | A | B | K )
|
||||
|
||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into staging buffer
|
||||
mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data
|
||||
mbedtls_sha512_ret(&g3072,1,tBuf,0); // create hash of g, but place output directly into staging buffer
|
||||
|
||||
for(int i=0;i<64;i++) // H(g) -> H(g) XOR H(N), with results in first 64 bytes of staging buffer
|
||||
tBuf[i]^=tHash[i];
|
||||
|
||||
mbedtls_sha512_ret((uint8_t *)I,strlen(I),tBuf+64,0); // create hash of userName and concatenate result to end of staging buffer
|
||||
|
||||
mbedtls_mpi_write_binary(&s,tBuf+128,16); // concatenate s to staging buffer
|
||||
|
||||
sLen=mbedtls_mpi_size(&A); // get actual size of A
|
||||
mbedtls_mpi_write_binary(&A,tBuf+144,sLen); // concatenate A to staging buffer. Note A is NOT padded with leading zeros (so may be less than 384 bytes)
|
||||
count=144+sLen; // total bytes written to staging buffer so far
|
||||
|
||||
sLen=mbedtls_mpi_size(&B); // get actual size of B
|
||||
mbedtls_mpi_write_binary(&B,tBuf+count,sLen); // concatenate B to staging buffer. Note B is NOT padded with leading zeros (so may be less than 384 bytes)
|
||||
count+=sLen; // increment total bytes written to staging buffer
|
||||
|
||||
memcpy(tBuf+count,K,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value)
|
||||
count+=64; // final total of bytes written to staging buffer
|
||||
|
||||
mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data - this is M1V
|
||||
|
||||
if(!memcmp(M1,tHash,64)) // check that client Proof M1 matches M1V
|
||||
return(1); // success - proof from HAP Client is verified
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createAccProof(uint8_t *proof){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(512); // temporary buffer for staging
|
||||
|
||||
// compute M2 = SHA512( A | M1 | K )
|
||||
|
||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into staging buffer
|
||||
memcpy(tBuf+384,M1,64); // concatenate M1 (now verified) to staging buffer
|
||||
memcpy(tBuf+448,K,64); // concatenate K to staging buffer
|
||||
mbedtls_sha512_ret(tBuf,512,proof,0); // create hash of data writing directly to proof - this is M2
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::print(mbedtls_mpi *mpi){
|
||||
|
||||
size_t sLen;
|
||||
|
||||
mbedtls_mpi_write_string(mpi,16,NULL,0,&sLen);
|
||||
TempBuffer<char> sBuf(sLen);
|
||||
mbedtls_mpi_write_string(mpi,16,sBuf,sLen,&sLen);
|
||||
|
||||
Serial.printf("%d %s\n",(sLen-1)/2,sBuf.get()); // subtract 1 for null-terminator, and then divide by 2 to get number of bytes (e.g. 4F = 2 characters, but represents just one mpi byte)
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
constexpr char SRP6A::N3072[];
|
||||
constexpr char SRP6A::I[];
|
||||
const uint8_t SRP6A::g3072;
|
||||
102
ESP32/HomeSpan-master/src/SRP.h
Normal file
102
ESP32/HomeSpan-master/src/SRP.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mbedtls/sha512.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Pair-Setup Code Verification Data and Salt
|
||||
|
||||
struct Verification {
|
||||
uint8_t salt[16];
|
||||
uint8_t verifyCode[384];
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// SRP-6A Structure from RFC 5054 (Nov 2007)
|
||||
// ** HAP uses N=3072-bit Group specified in RFC 5054 with Generator g=5
|
||||
// ** HAP replaces H=SHA-1 with H=SHA-512 (HAP Section 5.5)
|
||||
//
|
||||
// I = SRP-6A username, defined by HAP to be the word "Pair-Setup"
|
||||
// P = SRP-6A password, defined to be equal to the accessory's 8-digit setup code in the format "XXX-XX-XXX"
|
||||
|
||||
struct SRP6A {
|
||||
|
||||
static constexpr char N3072[]="FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74"
|
||||
"020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437"
|
||||
"4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05"
|
||||
"98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB"
|
||||
"9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
|
||||
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33"
|
||||
"A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864"
|
||||
"D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2"
|
||||
"08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
|
||||
|
||||
static const uint8_t g3072=5;
|
||||
static constexpr char I[]="Pair-Setup";
|
||||
|
||||
mbedtls_mpi N; // N - 3072-bit Group pre-defined prime used for all SRP-6A calculations (384 bytes)
|
||||
mbedtls_mpi g; // g - pre-defined generator for the specified 3072-bit Group (g=5)
|
||||
mbedtls_mpi k; // k = H(N | PAD(g)) - SRP-6A multiplier (which is different from versions SRP-6 or SRP-3)
|
||||
mbedtls_mpi s; // s - randomly-generated salt (16 bytes)
|
||||
mbedtls_mpi x; // x = H(s | H(I | ":" | P)) - salted, double-hash of username and password (64 bytes)
|
||||
mbedtls_mpi v; // v = g^x %N - SRP-6A verifier (max 384 bytes)
|
||||
mbedtls_mpi b; // b - randomly-generated private key for this HAP accessory (i.e. the SRP Server) (32 bytes)
|
||||
mbedtls_mpi B; // B = k*v + g^b %N - public key for this accessory (max 384 bytes)
|
||||
mbedtls_mpi A; // A - public key RECEIVED from HAP Client (max 384 bytes)
|
||||
mbedtls_mpi u; // u = H(PAD(A) | PAB(B)) - "u-factor" (64 bytes)
|
||||
mbedtls_mpi S; // S = (A*v^u)^b %N - SRP shared "premaster" key, based on accessory private key and client public key (max 384 bytes)
|
||||
uint8_t K[64]; // K = H(S) - SRP SHARED SECRET KEY (64 bytes)
|
||||
uint8_t M1[64]; // M1 - proof RECEIVED from HAP Client (64 bytes)
|
||||
mbedtls_mpi t1; // temp1 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi t2; // temp2 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi t3; // temp3 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi _rr; // _rr - temporary "helper" for large exponential modulus calculations
|
||||
|
||||
|
||||
SRP6A(); // initializes N, G, and computes k
|
||||
~SRP6A();
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
|
||||
void createVerifyCode(const char *setupCode, Verification *vData); // generates random s and computes v; writes back resulting Verification Data
|
||||
void createPublicKey(const Verification *vData, uint8_t *publicKey); // generates random b and computes k and B; writes back resulting Accessory Public Key
|
||||
void createSessionKey(const uint8_t *publicKey, size_t len); // computes u, S, and K from Client Public Key, A (of variable length)
|
||||
int verifyClientProof(const uint8_t *proof); // verifies Client Proof, M1, received from HAP client (return 1 on success, 0 on failure)
|
||||
void createAccProof(uint8_t *proof); // computes M2; write back resulting Accessory Proof
|
||||
|
||||
void print(mbedtls_mpi *mpi); // prints size of mpi (in bytes), followed by the mpi itself (as a hex character string)
|
||||
|
||||
};
|
||||
116
ESP32/HomeSpan-master/src/Settings.h
Normal file
116
ESP32/HomeSpan-master/src/Settings.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// DEFAULT SETTINGS //
|
||||
|
||||
#define DEFAULT_CATEGORY Category::Lighting // change with optional first argument in homeSpan.begin()
|
||||
#define DEFAULT_DISPLAY_NAME "HomeSpan Server" // change with optional second argument in homeSpan.begin()
|
||||
#define DEFAULT_HOST_NAME "HomeSpan" // change with optional third argument in homeSpan.begin()
|
||||
#define DEFAULT_MODEL_NAME "HomeSpan-ESP32" // change with optional fourth argument in homeSpan.begin()
|
||||
|
||||
#define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command
|
||||
|
||||
#define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID);
|
||||
|
||||
#define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(ssid)
|
||||
#define DEFAULT_AP_PASSWORD "homespan" // change with homeSpan.setApPassword(pwd)
|
||||
#define DEFAULT_OTA_PASSWORD "homespan-ota" // change with 'O' command
|
||||
|
||||
#define DEFAULT_AP_TIMEOUT 300 // change with homeSpan.setApTimeout(nSeconds)
|
||||
#define DEFAULT_COMMAND_TIMEOUT 120 // change with homeSpan.setCommandTimeout(nSeconds)
|
||||
|
||||
#define DEFAULT_LOG_LEVEL 0 // change with homeSpan.setLogLevel(level)
|
||||
|
||||
#define DEFAULT_TCP_PORT 80 // change with homeSpan.setPort(port);
|
||||
|
||||
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
|
||||
|
||||
#define DEFAULT_LOW_MEM_THRESHOLD 80000 // default low watermark memory (for internal RAM) threshold that triggers warning
|
||||
|
||||
#define DEFAULT_REBOOT_CALLBACK_TIME 5000 // default time (in milliseconds) to check for reboot callback
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// OTA PARTITION INFO //
|
||||
|
||||
#define HOMESPAN_MAGIC_COOKIE "HomeSpanMagicCookie##2022"
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// STATUS LED SETTINGS //
|
||||
|
||||
#define LED_WIFI_NEEDED 300,0.5,1,2700 // slow single-blink
|
||||
#define LED_PAIRING_NEEDED 300,0.5,2,2400 // slow double-blink
|
||||
#define LED_ALERT 100 // rapid flashing
|
||||
#define LED_WIFI_CONNECTING 2000 // slow flashing
|
||||
#define LED_AP_STARTED 100,0.5,2,300 // rapid double-blink
|
||||
#define LED_AP_CONNECTED 300,0.5,2,400 // medium double-blink
|
||||
#define LED_OTA_STARTED 300,0.5,3,400 // medium triple-blink
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Message Log Level Control Macros //
|
||||
// 0=Minimal, 1=Informative, 2=All //
|
||||
|
||||
#define LOG0(format,...) do{ if(homeSpan.getLogLevel()>=0)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
#define LOG1(format,...) do{ if(homeSpan.getLogLevel()>=1)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
#define LOG2(format,...) do{ if(homeSpan.getLogLevel()>=2)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
|
||||
#define WEBLOG(format,...) homeSpan.addWebLog(false, format __VA_OPT__(,) __VA_ARGS__);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Types of Accessory Categories //
|
||||
// Reference: HAP Section 13 //
|
||||
|
||||
enum class Category {
|
||||
Other=1,
|
||||
Bridges=2,
|
||||
Fans=3,
|
||||
GarageDoorOpeners=4,
|
||||
Lighting=5,
|
||||
Locks=6,
|
||||
Outlets=7,
|
||||
Switches=8,
|
||||
Thermostats=9,
|
||||
Sensors=10,
|
||||
SecuritySystems=11,
|
||||
Doors=12,
|
||||
Windows=13,
|
||||
WindowCoverings=14,
|
||||
ProgrammableSwitches=15,
|
||||
IPCameras=17,
|
||||
VideoDoorbells=18,
|
||||
AirPurifiers=19,
|
||||
Heaters=20,
|
||||
AirConditioners=21,
|
||||
Humidifiers=22,
|
||||
Dehumidifiers=23,
|
||||
Sprinklers=28,
|
||||
Faucets=29,
|
||||
ShowerSystems=30,
|
||||
Television=31
|
||||
};
|
||||
636
ESP32/HomeSpan-master/src/Span.h
Normal file
636
ESP32/HomeSpan-master/src/Span.h
Normal file
@@ -0,0 +1,636 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
///////////////////////////////////
|
||||
// SPAN SERVICES (HAP Chapter 8) //
|
||||
///////////////////////////////////
|
||||
|
||||
// Macros to define Services, along with vectors of required and optional Characteristics for each Span Service structure.
|
||||
//
|
||||
// NOTE: these macros are parsed by an external awk script to auto-generate Services and Characteristics documentation.
|
||||
//
|
||||
// The CREATE_SERV_DEP() macro is the same as the CREATE_SERV() macro, except that it is used for deprecated Services that will not
|
||||
// be included in documentation. The REQ_DEP and OPT_DEP() macros are the same as the REQ() and OPT() macros, except that they are used
|
||||
// for deprecated Characteristics that will not be included in documentation.
|
||||
|
||||
#define CREATE_SERV(NAME,_UUID) struct NAME : SpanService { static constexpr const char *UUID=#_UUID; NAME() : SpanService{#_UUID,#NAME}{
|
||||
#define CREATE_SERV_DEP(NAME,_UUID) struct NAME : SpanService { static constexpr const char *UUID=#_UUID; NAME() : SpanService{#_UUID,#NAME}{
|
||||
#define END_SERV }};
|
||||
|
||||
#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||
#define REQ_DEP(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||
#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||
#define OPT_DEP(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||
|
||||
#define SERVICES_GROUP
|
||||
|
||||
namespace Service {
|
||||
|
||||
SERVICES_GROUP // Mandatory Services
|
||||
|
||||
CREATE_SERV(AccessoryInformation,3E) // Required Identification Information. For each Accessory in a HomeSpan device this must be included as the first Service.
|
||||
REQ(Identify);
|
||||
OPT(Name);
|
||||
OPT(FirmwareRevision);
|
||||
OPT(Manufacturer);
|
||||
OPT(Model);
|
||||
OPT(SerialNumber);
|
||||
OPT(HardwareRevision);
|
||||
OPT_DEP(AccessoryFlags);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Lights, Power, and Switches
|
||||
|
||||
CREATE_SERV(BatteryService,96) // Defines a standalone Battery Service.
|
||||
REQ(BatteryLevel);
|
||||
REQ(ChargingState);
|
||||
REQ(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LightBulb,43) // Defines any type of Light.
|
||||
REQ(On);
|
||||
OPT(Brightness);
|
||||
OPT(Hue);
|
||||
OPT(Saturation);
|
||||
OPT(ColorTemperature);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Outlet,47) // Defines a controllable Outlet used to power any light or appliance.
|
||||
REQ(On);
|
||||
REQ(OutletInUse);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(StatelessProgrammableSwitch,89) // Defines a "Stateless" Programmable Switch that can be used to trigger actions in the Home App.
|
||||
REQ(ProgrammableSwitchEvent);
|
||||
OPT(ServiceLabelIndex);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Switch,49) // Defines a generic Switch.
|
||||
REQ(On);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Heating, Ventilation, and Air Conditioning (HVAC)
|
||||
|
||||
CREATE_SERV(AirPurifier,BB) // Defines a basic Air Purifier with an optional fan and swing mode. Optional Linked Services: <b>FilterMaintenance</b>. Combine with an <b>AirSensor</b> Service for automated operations.
|
||||
REQ(Active);
|
||||
REQ(CurrentAirPurifierState);
|
||||
REQ(TargetAirPurifierState);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Fan,B7) // Defines a Fan. Combine with a <b>LightBulb</b> Service to create a Lighted Ceiling Fan.
|
||||
REQ(Active);
|
||||
OPT(CurrentFanState);
|
||||
OPT(TargetFanState);
|
||||
OPT(RotationDirection);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(FilterMaintenance,BA) // Defines a Filter Maintainence check. Use only as a Linked Service for the <b>AirPurifier</b> Service.
|
||||
REQ(FilterChangeIndication);
|
||||
OPT(FilterLifeLevel);
|
||||
OPT(ResetFilterIndication);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HeaterCooler,BC) // Defines a standalone Heater, Cooler, or combined Heater/Cooler.
|
||||
REQ(Active);
|
||||
REQ(CurrentTemperature);
|
||||
REQ(CurrentHeaterCoolerState);
|
||||
REQ(TargetHeaterCoolerState);
|
||||
OPT(RotationSpeed);
|
||||
OPT(TemperatureDisplayUnits);
|
||||
OPT(SwingMode);
|
||||
OPT(CoolingThresholdTemperature);
|
||||
OPT(HeatingThresholdTemperature);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HumidifierDehumidifier,BD) // Defines a Humidifer, Dehumidifier, or combined Humidifer/Dehumidifier.
|
||||
REQ(Active);
|
||||
REQ(CurrentRelativeHumidity);
|
||||
REQ(CurrentHumidifierDehumidifierState);
|
||||
REQ(TargetHumidifierDehumidifierState);
|
||||
OPT(RelativeHumidityDehumidifierThreshold);
|
||||
OPT(RelativeHumidityHumidifierThreshold);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(WaterLevel);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Slat,B9) // Defines a motorized ventilation Slat(s).
|
||||
REQ(CurrentSlatState);
|
||||
REQ(SlatType);
|
||||
OPT(SwingMode);
|
||||
OPT(CurrentTiltAngle);
|
||||
OPT(TargetTiltAngle);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Thermostat,4A) // Defines a Thermostat used to control a furnace, air conditioner, or both.
|
||||
REQ(CurrentHeatingCoolingState);
|
||||
REQ(TargetHeatingCoolingState);
|
||||
REQ(CurrentTemperature);
|
||||
REQ(TargetTemperature);
|
||||
REQ(TemperatureDisplayUnits);
|
||||
OPT(CoolingThresholdTemperature);
|
||||
OPT(CurrentRelativeHumidity);
|
||||
OPT(HeatingThresholdTemperature);
|
||||
OPT(TargetRelativeHumidity);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Standalone Sensors
|
||||
|
||||
CREATE_SERV(AirQualitySensor,8D) // Defines an Air Quality Sensor.
|
||||
REQ(AirQuality);
|
||||
OPT(OzoneDensity);
|
||||
OPT(NitrogenDioxideDensity);
|
||||
OPT(SulphurDioxideDensity);
|
||||
OPT(PM25Density);
|
||||
OPT(PM10Density);
|
||||
OPT(VOCDensity);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(CarbonDioxideSensor,97) // Defines a Carbon Dioxide Sensor.
|
||||
REQ(CarbonDioxideDetected);
|
||||
OPT(CarbonDioxideLevel);
|
||||
OPT(CarbonDioxidePeakLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(CarbonMonoxideSensor,7F) // Defines a Carbon Monoxide Sensor.
|
||||
REQ(CarbonMonoxideDetected);
|
||||
OPT(CarbonMonoxideLevel);
|
||||
OPT(CarbonMonoxidePeakLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(ContactSensor,80) // Defines a Contact Sensor.
|
||||
REQ(ContactSensorState);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HumiditySensor,82) // Defines a Humidity Sensor.
|
||||
REQ(CurrentRelativeHumidity);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LeakSensor,83) // Defines a Leak Sensor.
|
||||
REQ(LeakDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LightSensor,84) // Defines a Light Sensor.
|
||||
REQ(CurrentAmbientLightLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(MotionSensor,85) // Defines a Motion Sensor.
|
||||
REQ(MotionDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(OccupancySensor,86) // Defines and Occupancy Sensor.
|
||||
REQ(OccupancyDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(SmokeSensor,87) // Defines a Smoke Sensor.
|
||||
REQ(SmokeDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(TemperatureSensor,8A) // Defines a Temperature Sensor.
|
||||
REQ(CurrentTemperature);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Doors, Locks, and Windows
|
||||
|
||||
CREATE_SERV(Door,81) // Defines a motorized Door.
|
||||
REQ(CurrentPosition);
|
||||
REQ(TargetPosition);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Doorbell,121) // Defines a Doorbell. Can be used on a standalone basis or in conjunction with a <b>LockMechanism</b> Service.
|
||||
REQ(ProgrammableSwitchEvent);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(Brightness);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(GarageDoorOpener,41) // Defines a motorized Garage Door Opener.
|
||||
REQ(CurrentDoorState);
|
||||
REQ(TargetDoorState);
|
||||
REQ(ObstructionDetected);
|
||||
OPT(LockCurrentState);
|
||||
OPT(LockTargetState);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LockMechanism,45) // Defines an electronic Lock.
|
||||
REQ(LockCurrentState);
|
||||
REQ(LockTargetState);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Window,8B) // Defines a motorized Window.
|
||||
REQ(CurrentPosition);
|
||||
REQ(TargetPosition);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(WindowCovering,8C) // Defines a motorized Window Shade, Screen, Awning, etc.
|
||||
REQ(TargetPosition);
|
||||
REQ(CurrentPosition);
|
||||
OPT(CurrentHorizontalTiltAngle);
|
||||
OPT(TargetHorizontalTiltAngle);
|
||||
OPT(CurrentVerticalTiltAngle);
|
||||
OPT(TargetVerticalTiltAngle);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Water Systems
|
||||
|
||||
CREATE_SERV(Faucet,D7) // Defines the master control for a multi-Valve appliance. Linked Services: <b>Valve</b> (at least one required), and <b>HeaterCooler</b> (optional).
|
||||
REQ(Active);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(IrrigationSystem,CF) // Defines an Irrigation System. Linked Services: <b>Valve</b> Service (at least one required).
|
||||
REQ(Active);
|
||||
REQ(ProgramMode);
|
||||
REQ(InUse);
|
||||
OPT(RemainingDuration);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Valve,D0) // Defines an electronic Valve. Can be used standalone or as a Linked Service for either a <b>Faucet</b> or <b>IrrigationSystem</b> Service.
|
||||
REQ(Active);
|
||||
REQ(InUse);
|
||||
REQ(ValveType);
|
||||
OPT(SetDuration);
|
||||
OPT(RemainingDuration);
|
||||
OPT(IsConfigured);
|
||||
OPT(ServiceLabelIndex);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Security Systems
|
||||
|
||||
CREATE_SERV(SecuritySystem,7E) // Defines a Security System. Often used in combination with <b>MotionSensor</b> and <b>ContactSensor</b> Services.
|
||||
REQ(SecuritySystemCurrentState);
|
||||
REQ(SecuritySystemTargetState);
|
||||
OPT(SecuritySystemAlarmType);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Televisions
|
||||
|
||||
CREATE_SERV(InputSource,D9) // Defines an Input Source for a TV. Use only as a Linked Service for the <b>Television</b> Service.
|
||||
REQ(Identifier);
|
||||
OPT(ConfiguredName);
|
||||
OPT(IsConfigured);
|
||||
OPT(CurrentVisibilityState);
|
||||
OPT(TargetVisibilityState);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Television,D8) // Defines a TV. Optional Linked Services: <b>InputSource</b> and <b>TelevisionSpeaker</b>.
|
||||
REQ(Active);
|
||||
OPT(ActiveIdentifier);
|
||||
OPT(DisplayOrder);
|
||||
OPT(RemoteKey);
|
||||
OPT(PowerModeSelection);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(TelevisionSpeaker,113) // Defines a Television Speaker that can be controlled via the Remote Control widget on an iPhone. Use only as a Linked Service for the <b>Television</b> Service.
|
||||
REQ(VolumeControlType);
|
||||
REQ(VolumeSelector);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Miscellaneous
|
||||
|
||||
CREATE_SERV(ServiceLabel,CC) // Defines a naming scheme for un-nameable Services, such as a <b>StatelessProgrammableSwitch</b>, by Linking them to this Service. When used, those other Services must each include a <b>ServiceLabelIndex</b> Characteristic with a unique value.
|
||||
REQ(ServiceLabelNamespace);
|
||||
END_SERV
|
||||
|
||||
// Deprecated or unsupported Services
|
||||
|
||||
CREATE_SERV_DEP(HAPProtocolInformation,A2)
|
||||
REQ_DEP(Version);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV_DEP(Microphone,112)
|
||||
REQ_DEP(Mute);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV_DEP(Speaker,113)
|
||||
REQ_DEP(Mute);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// SPAN CHARACTERISTICS (HAP Chapter 9) //
|
||||
//////////////////////////////////////////
|
||||
|
||||
// Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and min/max value (not applicable for STRING or BOOL which default to min=0, max=1)
|
||||
|
||||
#define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL,...) \
|
||||
struct HAPCHAR : SpanCharacteristic { __VA_OPT__(enum{) __VA_ARGS__ __VA_OPT__(};) HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init<TYPE>(val,nvsStore,MINVAL,MAXVAL); } };
|
||||
|
||||
namespace Characteristic {
|
||||
|
||||
CREATE_CHAR(UINT32_t,AccessoryFlags,1,1,1); // not applicable for HomeSpan
|
||||
CREATE_CHAR(UINT8_t,Active,0,0,1,INACTIVE,ACTIVE); // indicates if the Service is active/on
|
||||
CREATE_CHAR(UINT32_t,ActiveIdentifier,0,0,255); // numerical Identifier of the <b>InputSource</b> selected in the Home App.
|
||||
CREATE_CHAR(UINT8_t,AirQuality,0,0,5,UNKNOWN,EXCELLENT,GOOD,FAIR,INFERIOR,POOR); // a subjective description
|
||||
CREATE_CHAR(UINT8_t,BatteryLevel,100,0,100); // measured as a percentage
|
||||
CREATE_CHAR(INT_t,Brightness,0,0,100); // measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,CarbonMonoxideLevel,0,0,100); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(FLOAT_t,CarbonMonoxidePeakLevel,0,0,100); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(UINT8_t,CarbonMonoxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||
CREATE_CHAR(FLOAT_t,CarbonDioxideLevel,0,0,100000); // measured on parts per million (ppm)
|
||||
CREATE_CHAR(FLOAT_t,CarbonDioxidePeakLevel,0,0,100000); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(UINT8_t,CarbonDioxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||
CREATE_CHAR(UINT8_t,ChargingState,0,0,2,NOT_CHARGING,CHARGING,NOT_CHARGEABLE); // indicates state of battery charging
|
||||
CREATE_CHAR(UINT8_t,ClosedCaptions,0,0,1); // unused by any Service
|
||||
CREATE_CHAR(FLOAT_t,CoolingThresholdTemperature,10,10,35); // cooling turns on when temperature (in Celsius) rises above this threshold
|
||||
CREATE_CHAR(UINT32_t,ColorTemperature,200,140,500); // measured in inverse megaKelvin (= 1,000,000 / Kelvin)
|
||||
CREATE_CHAR(UINT8_t,ContactSensorState,1,0,1,DETECTED,NOT_DETECTED); // indictates if contact is detected (i.e. closed)
|
||||
CREATE_CHAR(STRING_t,ConfiguredName,"unnamed",NULL,NULL); // default display name of this Service
|
||||
CREATE_CHAR(FLOAT_t,CurrentAmbientLightLevel,1,0.0001,100000); // measured in Lux (lumens/m<sup>2</sup>
|
||||
CREATE_CHAR(INT_t,CurrentHorizontalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||
CREATE_CHAR(UINT8_t,CurrentAirPurifierState,0,0,2,INACTIVE,IDLE,PURIFYING); // indicates current state of air purification
|
||||
CREATE_CHAR(UINT8_t,CurrentSlatState,0,0,2,FIXED,JAMMED,SWINGING); // indicates current state of slats
|
||||
CREATE_CHAR(UINT8_t,CurrentPosition,0,0,100); // current position (as a percentage) from fully closed (0) to full open (100)
|
||||
CREATE_CHAR(INT_t,CurrentVerticalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||
CREATE_CHAR(UINT8_t,CurrentVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // current visibility of the Service, as selectable on the Settings Page of the Home App
|
||||
CREATE_CHAR(UINT8_t,CurrentHumidifierDehumidifierState,1,0,3,INACTIVE,IDLE,HUMIDIFYING,DEHUMIDIFYING); // indicates current state of humidifier/dehumidifer
|
||||
CREATE_CHAR(UINT8_t,CurrentDoorState,1,0,4,OPEN,CLOSED,OPENING,CLOSING,STOPPED); // indicates current state of a door
|
||||
CREATE_CHAR(UINT8_t,CurrentFanState,1,0,2,INACTIVE,IDLE,BLOWING); // indicates current state of a fan
|
||||
CREATE_CHAR(UINT8_t,CurrentHeatingCoolingState,0,0,2,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, or just idle
|
||||
CREATE_CHAR(UINT8_t,CurrentHeaterCoolerState,1,0,3,INACTIVE,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, idle, or off
|
||||
CREATE_CHAR(UINT8_t,CurrentMediaState,0,0,5); // not used
|
||||
CREATE_CHAR(FLOAT_t,CurrentRelativeHumidity,0,0,100); // current humidity measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,CurrentTemperature,0,0,100); // current temperature measured in Celsius
|
||||
CREATE_CHAR(INT_t,CurrentTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||
CREATE_CHAR(TLV_ENC_t,DisplayOrder,NULL_TLV,NULL_TLV,NULL_TLV); // specifies the order in which the TV inputs are displayed for selection in the Home App
|
||||
CREATE_CHAR(FLOAT_t,FilterLifeLevel,100,0,100); // measured as a percentage of remaining life
|
||||
CREATE_CHAR(UINT8_t,FilterChangeIndication,0,0,1,NO_CHANGE_NEEDED,CHANGE_NEEDED); // indicates state of filter
|
||||
CREATE_CHAR(STRING_t,FirmwareRevision,"1.0.0",NULL,NULL); // must be in form x[.y[.z]] - informational only
|
||||
CREATE_CHAR(STRING_t,HardwareRevision,"1.0.0",NULL,NULL); // must be in form x[.y[.z]] - informational only
|
||||
CREATE_CHAR(FLOAT_t,HeatingThresholdTemperature,16,0,25); // heating turns on when temperature (in Celsius) falls below this threshold
|
||||
CREATE_CHAR(BOOL_t,HoldPosition,false,0,1); // deprecated
|
||||
CREATE_CHAR(FLOAT_t,Hue,0,0,360); // color (in degrees) from red (0) to green (120) to blue (240) and back to red (360)
|
||||
CREATE_CHAR(BOOL_t,Identify,1,1,1,RUN_ID=1); // triggers an update when HomeKit wants HomeSpan to run its identification routine for an Accessory
|
||||
CREATE_CHAR(UINT32_t,Identifier,0,0,255); // numerical Identifer of the <b>InputSource</b>.
|
||||
CREATE_CHAR(UINT8_t,InputDeviceType,0,0,6); // not used
|
||||
CREATE_CHAR(UINT8_t,InputSourceType,0,0,10); // not used
|
||||
CREATE_CHAR(UINT8_t,InUse,0,0,1,NOT_IN_USE,IN_USE); // if Service is set to active, this indictes whether it is currently in use
|
||||
CREATE_CHAR(UINT8_t,IsConfigured,0,0,1,NOT_CONFIGURED,CONFIGURED); // indicates if a predefined Service has been configured
|
||||
CREATE_CHAR(UINT8_t,LeakDetected,0,0,1,NOT_DETECTED,DETECTED); // indictates if a leak is detected
|
||||
CREATE_CHAR(UINT8_t,LockCurrentState,0,0,3,UNLOCKED,LOCKED,JAMMED,UNKNOWN); // indicates state of a lock
|
||||
CREATE_CHAR(UINT8_t,LockPhysicalControls,0,0,1,CONTROL_LOCK_DISABLED,CONTROL_LOCK_ENABLED); // indicates if local control lock is enabled
|
||||
CREATE_CHAR(UINT8_t,LockTargetState,0,0,1,UNLOCK,LOCK); // indicates desired state of lock
|
||||
CREATE_CHAR(STRING_t,Manufacturer,"HomeSpan",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(STRING_t,Model,"HomeSpan-ESP32",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(BOOL_t,MotionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if motion is detected
|
||||
CREATE_CHAR(BOOL_t,Mute,0,0,1,OFF,ON); // not used
|
||||
CREATE_CHAR(STRING_t,Name,"unnamed",NULL,NULL); // default display name of the Accessory
|
||||
CREATE_CHAR(FLOAT_t,NitrogenDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(BOOL_t,ObstructionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if obstruction is detected
|
||||
CREATE_CHAR(FLOAT_t,PM25Density,0,0,1000); // 2.5-micron particulate density, measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,OccupancyDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if occupanccy is detected
|
||||
CREATE_CHAR(BOOL_t,OutletInUse,0,0,1,NOT_IN_USE,IN_USE); // indicates if an appliance or light is plugged into the outlet, regardless of whether on or off
|
||||
CREATE_CHAR(BOOL_t,On,0,0,1,OFF,ON); // indicates if the Service is active/on
|
||||
CREATE_CHAR(FLOAT_t,OzoneDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,PictureMode,0,0,13); // not used
|
||||
CREATE_CHAR(FLOAT_t,PM10Density,0,0,1000); // 10-micron particulate density, measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,PositionState,2,0,2,GOING_TO_MINIMUM,GOING_TO_MAXIMUM,STOPPED); // deprecated
|
||||
CREATE_CHAR(UINT8_t,PowerModeSelection,0,0,0,VIEW_SETTINGS); // when defined, creates a "View TV Settings" button in the Home App that triggers an update to this Characteristic when pressed
|
||||
CREATE_CHAR(UINT8_t,ProgramMode,0,0,2,NONE,SCHEDULED,SCHEDULE_OVERRIDEN); // indicates if pre-scheduled program is running
|
||||
CREATE_CHAR(UINT8_t,ProgrammableSwitchEvent,0,0,2,SINGLE_PRESS,DOUBLE_PRESS,LONG_PRESS); // specifies type of button press
|
||||
CREATE_CHAR(FLOAT_t,RelativeHumidityDehumidifierThreshold,50,0,100); // dehumidfier turns on when humidity rises above this threshold
|
||||
CREATE_CHAR(FLOAT_t,RelativeHumidityHumidifierThreshold,50,0,100); // humidfier turns on when humidity falls below this threshold
|
||||
CREATE_CHAR(UINT32_t,RemainingDuration,60,0,3600); // duration (in seconds) remaining for Service to be active/on
|
||||
CREATE_CHAR(UINT8_t,RemoteKey,4,4,15,UP=4,DOWN,LEFT,RIGHT,CENTER,BACK,PLAY_PAUSE=11,INFO=15); // triggers an update when the corresponding key is pressed in the Remote Control widget on an iPhone
|
||||
CREATE_CHAR(UINT8_t,ResetFilterIndication,1,1,1,RESET_FILTER=1); // triggers an update when the user chooses to reset the <b>FilterChangeIndication</b> (only appears in Eve App, not Home App)
|
||||
CREATE_CHAR(INT_t,RotationDirection,0,0,1,CLOCKWISE,COUNTERCLOCKWISE); // indicates the rotation direction of a fan
|
||||
CREATE_CHAR(FLOAT_t,RotationSpeed,0,0,100); // measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,Saturation,0,0,100); // color saturation, measured as a percentage
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemAlarmType,0,0,1,KNOWN,UNKNOWN); // indicates whether alarm was triggered for known reason
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemCurrentState,3,0,4,ARMED_STAY,ARMED_AWAY,ARMED_NIGHT,DISARMED,ALARM_TRIGGERED); // indicates current state of the security system
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemTargetState,3,0,3,ARM_STAY,ARM_AWAY,ARM_NIGHT,DISARM); // indicates desired state of the security system
|
||||
CREATE_CHAR(STRING_t,SerialNumber,"HS-12345",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(UINT8_t,ServiceLabelIndex,1,1,255); // numerical index used to distinguish multiple copies of the same Service within an Accessory
|
||||
CREATE_CHAR(UINT8_t,ServiceLabelNamespace,1,0,1,DOTS,NUMERALS); // indicates how un-named Services linked together with a <b>ServiceLabel</b> Service should be displayed in the Home App
|
||||
CREATE_CHAR(UINT8_t,SlatType,0,0,1,HORIZONTAL,VERTICAL); // indicates the direction of a slat or group of slats
|
||||
CREATE_CHAR(UINT8_t,SleepDiscoveryMode,0,0,1); // not used
|
||||
CREATE_CHAR(UINT8_t,SmokeDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if smoke is detected
|
||||
CREATE_CHAR(BOOL_t,StatusActive,1,0,1,NOT_FUNCTIONING,FUNCTIONING); // indicates whether the Service is properly functioning
|
||||
CREATE_CHAR(UINT8_t,StatusFault,0,0,1,NO_FAULT,FAULT); // indicates whether the Service has a fault (only appears in Eve App, not Home App)
|
||||
CREATE_CHAR(UINT8_t,StatusJammed,0,0,1,NOT_JAMMED,JAMMED); // indicates whether the Service has been "jammed"
|
||||
CREATE_CHAR(UINT8_t,StatusLowBattery,0,0,1,NOT_LOW_BATTERY,LOW_BATTERY); // indicates state of battery
|
||||
CREATE_CHAR(UINT8_t,StatusTampered,0,0,1,NOT_TAMPERED,TAMPERED); // indicates whether the Service has been tampered with
|
||||
CREATE_CHAR(FLOAT_t,SulphurDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,SwingMode,0,0,1,SWING_DISABLED,SWING_ENABLED); // indicates whether swing-mode is enabled
|
||||
CREATE_CHAR(UINT8_t,TargetAirPurifierState,1,0,1,MANUAL,AUTO); // indicates desired state of air purifier
|
||||
CREATE_CHAR(UINT8_t,TargetFanState,1,0,1,MANUAL,AUTO); // indicates desired state of fan
|
||||
CREATE_CHAR(INT_t,TargetTiltAngle,0,-90,90); // indicated desired angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||
CREATE_CHAR(UINT8_t,TargetHeaterCoolerState,0,0,2,AUTO,HEAT,COOL); // indicates desired state of heater/cooler
|
||||
CREATE_CHAR(UINT32_t,SetDuration,60,0,3600); // specifies the duration (in seconds) for a Service to remain on once activated
|
||||
CREATE_CHAR(INT_t,TargetHorizontalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||
CREATE_CHAR(UINT8_t,TargetHumidifierDehumidifierState,0,0,2,AUTO,HUMIDIFY,DEHUMIDIFY); // indicates desired state of humidifier/dehumidifier
|
||||
CREATE_CHAR(UINT8_t,TargetPosition,0,0,100); // indicates target position (as a percentage) from fully closed (0) to full open (100)
|
||||
CREATE_CHAR(UINT8_t,TargetDoorState,1,0,1,OPEN,CLOSED); // indicates desired state of door
|
||||
CREATE_CHAR(UINT8_t,TargetHeatingCoolingState,0,0,3,OFF,HEAT,COOL,AUTO); // indicates desired state of appliance
|
||||
CREATE_CHAR(UINT8_t,TargetMediaState,0,0,2); // unused
|
||||
CREATE_CHAR(FLOAT_t,TargetRelativeHumidity,0,0,100); // indicates desired humidity measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,TargetTemperature,16,10,38); // indicates desired temperature measures in Celsius
|
||||
CREATE_CHAR(UINT8_t,TargetVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // indicates desired visibility of the Service, as selectable on the Settings Page of the Home App
|
||||
CREATE_CHAR(UINT8_t,TemperatureDisplayUnits,0,0,1,CELSIUS,FAHRENHEIT); // indicates the desired units to display the temperature on the device itself (has no effect on Home App)
|
||||
CREATE_CHAR(INT_t,TargetVerticalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||
CREATE_CHAR(UINT8_t,ValveType,0,0,3,GENERIC,IRRIGATION,SHOWER_HEAD,FAUCET); // indicates the type of valve
|
||||
CREATE_CHAR(STRING_t,Version,"1.0.0",NULL,NULL); // unused
|
||||
CREATE_CHAR(FLOAT_t,VOCDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,Volume,0,0,100); // unused
|
||||
CREATE_CHAR(UINT8_t,VolumeControlType,3,0,3,NONE,RELATIVE,RELATIVE_CURRENT,ABSOLUTE); // indicates the type of volume control
|
||||
CREATE_CHAR(UINT8_t,VolumeSelector,0,0,1,VOLUME_UP,VOLUME_DOWN); // triggered by presses to the iPhone's volume up/down buttons when TV is selected in the Remote Control widget
|
||||
CREATE_CHAR(FLOAT_t,WaterLevel,0,0,100); // measured as a percentage
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef CUSTOM_CHAR_HEADER
|
||||
|
||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init<FORMAT##_t>(val,nvsStore,MINVAL,MAXVAL); } }; }
|
||||
|
||||
#else
|
||||
|
||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||
extern HapChar _CUSTOM_##NAME; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init<FORMAT##_t>(val,nvsStore,MINVAL,MAXVAL); } }; }
|
||||
|
||||
#endif
|
||||
|
||||
#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) CUSTOM_CHAR(NAME,UUID,PERMISISONS,STRING,DEFVAL,NULL,NULL,true);
|
||||
#define CUSTOM_CHAR_TLV8(NAME,UUID,PERMISISONS) CUSTOM_CHAR(NAME,UUID,PERMISISONS,TLV_ENC,NULL_TLV,NULL_TLV,NULL_TLV,true);
|
||||
#define CUSTOM_CHAR_DATA(NAME,UUID,PERMISISONS) CUSTOM_CHAR(NAME,UUID,PERMISISONS,DATA,NULL_DATA,NULL_DATA,NULL_DATA,true);
|
||||
|
||||
#define CUSTOM_SERV(NAME,UUID) \
|
||||
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD A NEW ACCESSORY WITH OPTIONAL NAME //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#define SPAN_ACCESSORY(...) new SpanAccessory(); new Service::AccessoryInformation(); new Characteristic::Identify(); __VA_OPT__(new Characteristic::Name(__VA_ARGS__));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
299
ESP32/HomeSpan-master/src/TLV8.cpp
Normal file
299
ESP32/HomeSpan-master/src/TLV8.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "TLV8.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
tlv8_t::tlv8_t(uint8_t tag, size_t len, const uint8_t* val) : tag{tag}, len{len} {
|
||||
if(len>0){
|
||||
this->val=std::unique_ptr<uint8_t>((uint8_t *)HS_MALLOC(len));
|
||||
if(val!=NULL)
|
||||
memcpy((this->val).get(),val,len);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void tlv8_t::update(size_t addLen, const uint8_t *addVal){
|
||||
if(addLen>0){
|
||||
uint8_t *p=val.release();
|
||||
p=(uint8_t *)HS_REALLOC(p,len+addLen);
|
||||
val=std::unique_ptr<uint8_t>(p);
|
||||
if(addVal!=NULL)
|
||||
memcpy(p+len,addVal,addLen);
|
||||
len+=addLen;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
void tlv8_t::osprint(std::ostream& os) const {
|
||||
|
||||
uint8_t *p=val.get(); // starting pointer
|
||||
uint8_t *pend=p+len; // ending pointer (may equal starting if len=0)
|
||||
|
||||
do{
|
||||
uint8_t nBytes=(pend-p)>255?255:(pend-p); // max is 255 bytes per TLV record
|
||||
os.write((char *)&tag,1);
|
||||
os.write((char *)&nBytes,1);
|
||||
os.write((char *)p,nBytes);
|
||||
p+=nBytes;
|
||||
} while(p<pend);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, size_t len, const uint8_t* val) {
|
||||
|
||||
if(!empty() && back().getTag()==tag)
|
||||
back().update(len,val);
|
||||
else
|
||||
emplace_back(tag,len,val);
|
||||
|
||||
return(--end());
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, TLV8 &subTLV){
|
||||
|
||||
auto it=add(tag,subTLV.pack_size(),NULL); // create space for inserting sub TLV and store iterator to new element
|
||||
subTLV.pack(*it); // pack subTLV into new element
|
||||
return(--end());
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, uint64_t val){
|
||||
|
||||
uint8_t *p=reinterpret_cast<uint8_t *>(&val);
|
||||
size_t nBytes=sizeof(uint64_t);
|
||||
while(nBytes>1 && p[nBytes-1]==0) // TLV requires little endian of size 1, 2, 4, or 8 bytes (include trailing zeros as needed)
|
||||
nBytes--;
|
||||
if(nBytes==3) // need to include a trailing zero so that total bytes=4
|
||||
nBytes=4;
|
||||
else if(nBytes>4) // need to include multiple trailing zeros so that total bytes=8
|
||||
nBytes=8;
|
||||
return(add(tag, nBytes, p));
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::find(uint8_t tag, TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
auto it=it1;
|
||||
while(it!=it2 && it->getTag()!=tag)
|
||||
it++;
|
||||
return(it);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
size_t TLV8::pack_size(TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
size_t nBytes=0;
|
||||
|
||||
while(it1!=it2){
|
||||
nBytes+=2+(*it1).getLen();
|
||||
if((*it1).getLen()>255)
|
||||
nBytes+=2*(((*it1).getLen()-1)/255);
|
||||
it1++;
|
||||
}
|
||||
|
||||
return(nBytes);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
size_t TLV8::pack(uint8_t *buf, size_t bufSize) const {
|
||||
|
||||
size_t nBytes=0;
|
||||
|
||||
while(nBytes<bufSize && currentPackIt!=endPackIt){
|
||||
switch(currentPackPhase){
|
||||
|
||||
case 0:
|
||||
currentPackBuf=*currentPackIt;
|
||||
endPackBuf=(*currentPackIt)+currentPackIt->getLen();
|
||||
currentPackPhase=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*buf++=currentPackIt->getTag();
|
||||
nBytes++;
|
||||
currentPackPhase=2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
currentPackLen=endPackBuf-currentPackBuf;
|
||||
if(currentPackLen>255)
|
||||
currentPackLen=255;
|
||||
*buf++=currentPackLen;
|
||||
nBytes++;
|
||||
currentPackPhase=3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(currentPackLen==0){
|
||||
if(endPackBuf==currentPackBuf){
|
||||
currentPackIt++;
|
||||
currentPackPhase=0;
|
||||
} else {
|
||||
currentPackPhase=1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
size_t copyBytes=(currentPackLen<(bufSize-nBytes)) ? currentPackLen : (bufSize-nBytes);
|
||||
memcpy(buf,currentPackBuf,copyBytes);
|
||||
buf+=copyBytes;
|
||||
currentPackBuf+=copyBytes;
|
||||
currentPackLen-=copyBytes;
|
||||
nBytes+=copyBytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(nBytes);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
int TLV8::unpack(uint8_t *buf, size_t bufSize){
|
||||
|
||||
if(bufSize==0)
|
||||
return(-1);
|
||||
|
||||
if(empty())
|
||||
unpackPhase=0;
|
||||
|
||||
while(bufSize>0){
|
||||
switch(unpackPhase){
|
||||
|
||||
case 0:
|
||||
unpackTag=*buf++;
|
||||
bufSize--;
|
||||
add(unpackTag);
|
||||
unpackPhase=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
unpackBytes=*buf++;
|
||||
bufSize--;
|
||||
if(unpackBytes==0)
|
||||
unpackPhase=0;
|
||||
else
|
||||
unpackPhase=2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
size_t copyBytes=unpackBytes<bufSize ? unpackBytes : bufSize;
|
||||
add(unpackTag,copyBytes,buf);
|
||||
buf+=copyBytes;
|
||||
unpackBytes-=copyBytes;
|
||||
bufSize-=copyBytes;
|
||||
if(unpackBytes==0)
|
||||
unpackPhase=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return(unpackPhase);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
int TLV8::unpack(TLV8_itc it){
|
||||
|
||||
if(it==end())
|
||||
return(0);
|
||||
|
||||
return(unpack(*it,it->getLen()));
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
const char *TLV8::getName(uint8_t tag) const {
|
||||
|
||||
if(names==NULL)
|
||||
return(NULL);
|
||||
|
||||
for(int i=0;i<nNames;i++){
|
||||
if(names[i].tag==tag)
|
||||
return(names[i].name);
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
void TLV8::print(TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
while(it1!=it2){
|
||||
const char *name=getName(it1->getTag());
|
||||
if(name)
|
||||
Serial.printf("%s",name);
|
||||
else
|
||||
Serial.printf("%d",it1->getTag());
|
||||
Serial.printf("(%d) ",it1->getLen());
|
||||
for(int i=0;i<it1->getLen();i++)
|
||||
Serial.printf("%02X",(*it1)[i]);
|
||||
if(it1->getLen()==0)
|
||||
Serial.printf(" [null]");
|
||||
else if(it1->getLen()<=4)
|
||||
Serial.printf(" [%u]",it1->getVal());
|
||||
else if(it1->getLen()<=8)
|
||||
Serial.printf(" [%llu]",it1->getVal<uint64_t>());
|
||||
Serial.printf("\n");
|
||||
it1++;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void TLV8::printAll_r(String label) const{
|
||||
|
||||
for(auto it=begin();it!=end();it++){
|
||||
Serial.printf("%s",label.c_str());
|
||||
print(it);
|
||||
TLV8 tlv;
|
||||
if(tlv.unpack(*it,(*it).getLen())==0)
|
||||
tlv.printAll_r(label+String((*it).getTag())+"-");
|
||||
}
|
||||
Serial.printf("%sDONE\n",label.c_str());
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void TLV8::osprint(std::ostream& os, TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
for(auto it=it1;it!=it2;it++)
|
||||
(*it).osprint(os);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
147
ESP32/HomeSpan-master/src/TLV8.h
Normal file
147
ESP32/HomeSpan-master/src/TLV8.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "PSRAM.h"
|
||||
|
||||
class tlv8_t {
|
||||
|
||||
private:
|
||||
|
||||
uint8_t tag;
|
||||
size_t len;
|
||||
std::unique_ptr<uint8_t> val;
|
||||
|
||||
public:
|
||||
|
||||
tlv8_t(uint8_t tag, size_t len, const uint8_t* val);
|
||||
void update(size_t addLen, const uint8_t *addVal);
|
||||
void osprint(std::ostream& os) const;
|
||||
|
||||
operator uint8_t*() const {
|
||||
return(val.get());
|
||||
}
|
||||
|
||||
uint8_t & operator[](int index) const {
|
||||
return(val.get()[index]);
|
||||
}
|
||||
|
||||
uint8_t *get() const {
|
||||
return(val.get());
|
||||
}
|
||||
|
||||
size_t getLen() const {
|
||||
return(len);
|
||||
}
|
||||
|
||||
uint8_t getTag() const {
|
||||
return(tag);
|
||||
}
|
||||
|
||||
template<class T=uint32_t> T getVal() const {
|
||||
T iVal=0;
|
||||
for(int i=0;i<len;i++)
|
||||
iVal|=static_cast<T>(val.get()[i])<<(i*8);
|
||||
return(iVal);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
typedef std::list<tlv8_t, Mallocator<tlv8_t>>::const_iterator TLV8_itc;
|
||||
typedef struct { const uint8_t tag; const char *name; } TLV8_names;
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class TLV8 : public std::list<tlv8_t, Mallocator<tlv8_t>> {
|
||||
|
||||
TLV8_itc mutable currentPackIt;
|
||||
TLV8_itc mutable endPackIt;
|
||||
uint8_t mutable *currentPackBuf;
|
||||
uint8_t mutable *endPackBuf;
|
||||
int mutable currentPackPhase;
|
||||
size_t mutable currentPackLen;
|
||||
|
||||
uint8_t unpackTag;
|
||||
size_t unpackBytes;
|
||||
int unpackPhase;
|
||||
|
||||
const TLV8_names *names=NULL;
|
||||
int nNames=0;
|
||||
|
||||
void printAll_r(String label) const;
|
||||
|
||||
public:
|
||||
|
||||
TLV8(){};
|
||||
TLV8(const TLV8_names *names, int nNames) : names{names}, nNames{nNames} {};
|
||||
|
||||
TLV8_itc add(uint8_t tag, size_t len, const uint8_t *val);
|
||||
TLV8_itc add(uint8_t tag, uint64_t val);
|
||||
TLV8_itc add(uint8_t tag, TLV8 &subTLV);
|
||||
TLV8_itc add(uint8_t tag){return(add(tag, 0, NULL));}
|
||||
TLV8_itc add(uint8_t tag, const char *val){return(add(tag, strlen(val), reinterpret_cast<const uint8_t*>(val)));}
|
||||
|
||||
TLV8_itc find(uint8_t tag, TLV8_itc it1, TLV8_itc it2) const;
|
||||
TLV8_itc find(uint8_t tag, TLV8_itc it1) const {return(find(tag, it1, end()));}
|
||||
TLV8_itc find(uint8_t tag) const {return(find(tag, begin(), end()));}
|
||||
|
||||
int len(TLV8_itc it) const {return(it==end()?-1:(*it).getLen());}
|
||||
|
||||
size_t pack_size(TLV8_itc it1, TLV8_itc it2) const;
|
||||
size_t pack_size() const {return(pack_size(begin(), end()));}
|
||||
|
||||
void pack_init(TLV8_itc it1, TLV8_itc it2) const {currentPackIt=it1; endPackIt=it2; currentPackPhase=0;}
|
||||
void pack_init(TLV8_itc it1) const {pack_init(it1, std::next(it1));}
|
||||
void pack_init() const {pack_init(begin(),end());}
|
||||
|
||||
size_t pack(uint8_t *buf, size_t bufSize) const;
|
||||
size_t pack(uint8_t *buf) const {pack_init(); return(pack(buf,pack_size()));}
|
||||
|
||||
const char *getName(uint8_t tag) const;
|
||||
|
||||
void print(TLV8_itc it1, TLV8_itc it2) const;
|
||||
void print(TLV8_itc it1) const {print(it1, std::next(it1));}
|
||||
void print() const {print(begin(), end());}
|
||||
void printAll() const {printAll_r("");}
|
||||
|
||||
void osprint(std::ostream& os, TLV8_itc it1, TLV8_itc it2) const;
|
||||
void osprint(std::ostream& os, TLV8_itc it1) const {osprint(os, it1, std::next(it1));}
|
||||
void osprint(std::ostream& os) const {osprint(os, begin(), end());}
|
||||
|
||||
int unpack(uint8_t *buf, size_t bufSize);
|
||||
int unpack(TLV8_itc it);
|
||||
|
||||
void wipe() {std::list<tlv8_t, Mallocator<tlv8_t>>().swap(*this);}
|
||||
};
|
||||
295
ESP32/HomeSpan-master/src/Utils.cpp
Normal file
295
ESP32/HomeSpan-master/src/Utils.cpp
Normal file
@@ -0,0 +1,295 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "Utils.h"
|
||||
#include "HomeSpan.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Contains various generic utility functions and classes:
|
||||
//
|
||||
// Utils::readSerial - reads all characters from Serial port and saves only up to max specified
|
||||
// Utils::mask - masks a string with asterisks (good for displaying passwords)
|
||||
//
|
||||
// class PushButton - tracks Single, Double, and Long Presses of a pushbutton that connects a specified pin to ground
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char *Utils::readSerial(char *c, int max){
|
||||
|
||||
if(homeSpan.getSerialInputDisable()){
|
||||
c[0]='\0';
|
||||
return(c);
|
||||
}
|
||||
|
||||
int i=0;
|
||||
char buf;
|
||||
|
||||
while(1){
|
||||
|
||||
while(!Serial.available()); // wait until there is a new character
|
||||
|
||||
buf=Serial.read();
|
||||
|
||||
if(buf=='\n'){ // exit upon newline
|
||||
if(i>0) // characters have been typed
|
||||
c[i]='\0'; // replace newline with string terminator
|
||||
return(c); // return updated string
|
||||
}
|
||||
|
||||
if(buf!='\r'){ // save any character except carriage return
|
||||
c[i]=buf; // store new character
|
||||
if(i<max) // do not store more than max characters (excluding string terminator)
|
||||
i++;
|
||||
}
|
||||
|
||||
} // while(1)
|
||||
|
||||
} // readSerial
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
char *Utils::stripBackslash(char *c){
|
||||
|
||||
size_t n=strlen(c);
|
||||
char *p=c;
|
||||
for(int i=0;i<=n;i++){
|
||||
*p=c[i];
|
||||
if(*p!='\\')
|
||||
p++;
|
||||
}
|
||||
return(c);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
String Utils::mask(char *c, int n){
|
||||
String s="";
|
||||
int len=strlen(c);
|
||||
|
||||
for(int i=0;i<len;i++){
|
||||
if(i<n || i>=len-n)
|
||||
s+=c[i];
|
||||
else
|
||||
s+='*';
|
||||
}
|
||||
|
||||
return(s);
|
||||
} // mask
|
||||
|
||||
////////////////////////////////
|
||||
// PushButton //
|
||||
////////////////////////////////
|
||||
|
||||
PushButton::PushButton(int pin, triggerType_t triggerType){
|
||||
|
||||
this->pin=pin;
|
||||
this->triggerType=triggerType;
|
||||
|
||||
status=0;
|
||||
doubleCheck=false;
|
||||
|
||||
if(triggerType==TRIGGER_ON_LOW)
|
||||
pinMode(pin, INPUT_PULLUP);
|
||||
else if(triggerType==TRIGGER_ON_HIGH)
|
||||
pinMode(pin, INPUT_PULLDOWN);
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
else if (triggerType==TRIGGER_ON_TOUCH && threshold==0){
|
||||
for(int i=0;i<calibCount;i++)
|
||||
threshold+=touchRead(pin);
|
||||
threshold/=calibCount;
|
||||
#if SOC_TOUCH_VERSION_1
|
||||
threshold/=2;
|
||||
LOG0("Touch Sensor at pin=%d used for calibration. Triggers when sensor reading < %d.\n",pin,threshold);
|
||||
#elif SOC_TOUCH_VERSION_2
|
||||
threshold*=2;
|
||||
LOG0("Touch Sensor at pin=%d used for calibration. Triggers when sensor reading > %d.\n",pin,threshold);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if(triggerType(pin)){
|
||||
pressType=CLOSED;
|
||||
toggleStatus=2;
|
||||
} else {
|
||||
pressType=OPEN;
|
||||
toggleStatus=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime){
|
||||
|
||||
unsigned long cTime=millis();
|
||||
|
||||
switch(status){
|
||||
|
||||
case 0:
|
||||
if(doubleCheck && cTime>doubleAlarm){
|
||||
doubleCheck=false;
|
||||
pressType=SINGLE;
|
||||
return(true);
|
||||
}
|
||||
|
||||
if(triggerType(pin)){ // button is "pressed"
|
||||
singleAlarm=cTime+singleTime;
|
||||
if(!doubleCheck){
|
||||
status=1;
|
||||
doubleAlarm=singleAlarm+doubleTime;
|
||||
longAlarm=cTime+longTime;
|
||||
} else {
|
||||
status=4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if(!triggerType(pin)){ // button is released
|
||||
status=0;
|
||||
if(cTime>singleAlarm){
|
||||
doubleCheck=true;
|
||||
}
|
||||
} else
|
||||
|
||||
if(cTime>longAlarm){ // button is long-pressed
|
||||
longAlarm=cTime+longTime;
|
||||
status=3;
|
||||
pressType=LONG;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(!triggerType(pin)) // button has been released after a long press
|
||||
status=0;
|
||||
else if(cTime>longAlarm){
|
||||
longAlarm=cTime+longTime;
|
||||
pressType=LONG;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(!triggerType(pin)){ // button is released
|
||||
status=0;
|
||||
} else
|
||||
|
||||
if(cTime>singleAlarm){ // button is still pressed
|
||||
status=5;
|
||||
pressType=DOUBLE;
|
||||
doubleCheck=false;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if(!triggerType(pin)) // button has been released after double-click
|
||||
status=0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::toggled(uint16_t toggleTime){
|
||||
|
||||
unsigned long cTime=millis();
|
||||
|
||||
switch(toggleStatus){
|
||||
|
||||
case 0:
|
||||
if(triggerType(pin)){ // switch is toggled CLOSED
|
||||
singleAlarm=cTime+toggleTime;
|
||||
toggleStatus=1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(!triggerType(pin)){ // switch is toggled back OPEN too soon
|
||||
toggleStatus=0;
|
||||
}
|
||||
|
||||
else if(cTime>singleAlarm){ // switch has been in CLOSED state for sufficient time
|
||||
toggleStatus=2;
|
||||
pressType=CLOSED;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(!triggerType(pin)){ // switch is toggled OPEN after being in CLOSED state
|
||||
toggleStatus=0;
|
||||
pressType=OPEN;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
} // switch
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::primed(){
|
||||
|
||||
if(millis()>singleAlarm && status==1){
|
||||
status=2;
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int PushButton::type(){
|
||||
return(pressType);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void PushButton::wait(){
|
||||
while(triggerType(pin));
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void PushButton::reset(){
|
||||
status=0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
PushButton::touch_value_t PushButton::threshold=0;
|
||||
237
ESP32/HomeSpan-master/src/Utils.h
Normal file
237
ESP32/HomeSpan-master/src/Utils.h
Normal file
@@ -0,0 +1,237 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "PSRAM.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
char *readSerial(char *c, int max); // read serial port into 'c' until <newline>, but storing only first 'max' characters (the rest are discarded)
|
||||
String mask(char *c, int n); // simply utility that creates a String from 'c' with all except the first and last 'n' characters replaced by '*'
|
||||
char *stripBackslash(char *c); // strips backslashes out of c (Apple unecessesarily "escapes" forward slashes in JSON)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Creates a temporary buffer that is freed after
|
||||
// going out of scope
|
||||
|
||||
template <class bufType>
|
||||
class TempBuffer {
|
||||
|
||||
private:
|
||||
|
||||
bufType *buf=NULL;
|
||||
size_t nElements;
|
||||
|
||||
public:
|
||||
|
||||
TempBuffer(size_t _nElements=1) : nElements(_nElements) {
|
||||
buf=(bufType *)HS_MALLOC(nElements*sizeof(bufType));
|
||||
if(buf==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
|
||||
TempBuffer(bufType *addBuf...) : nElements(0) {
|
||||
va_list args;
|
||||
va_start(args,addBuf);
|
||||
while(addBuf!=NULL){
|
||||
size_t addElements=va_arg(args,size_t);
|
||||
buf=(bufType *)HS_REALLOC(buf,(nElements+addElements)*sizeof(bufType));
|
||||
if(buf==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||
while(1);
|
||||
}
|
||||
memcpy(buf+nElements,addBuf,addElements*sizeof(bufType));
|
||||
nElements+=addElements;
|
||||
addBuf=va_arg(args,bufType *);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
~TempBuffer(){
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int len(){
|
||||
return(nElements*sizeof(bufType));
|
||||
}
|
||||
|
||||
int size(){
|
||||
return(nElements);
|
||||
}
|
||||
|
||||
bufType *get(){
|
||||
return(buf);
|
||||
}
|
||||
|
||||
operator bufType*() const{
|
||||
return(buf);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// PushButton //
|
||||
////////////////////////////////
|
||||
|
||||
class PushButton{
|
||||
|
||||
int status;
|
||||
int toggleStatus;
|
||||
boolean doubleCheck;
|
||||
uint32_t singleAlarm;
|
||||
uint32_t doubleAlarm;
|
||||
uint32_t longAlarm;
|
||||
|
||||
#if SOC_TOUCH_VERSION_2
|
||||
typedef uint32_t touch_value_t;
|
||||
#else
|
||||
typedef uint16_t touch_value_t;
|
||||
#endif
|
||||
|
||||
static touch_value_t threshold;
|
||||
static const int calibCount=20;
|
||||
|
||||
public:
|
||||
|
||||
typedef boolean (*triggerType_t)(int pin);
|
||||
|
||||
protected:
|
||||
|
||||
int pressType;
|
||||
int pin;
|
||||
triggerType_t triggerType;
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
SINGLE=0, // applicable only for push button
|
||||
DOUBLE=1, // applicable only for push button
|
||||
LONG=2, // applicable only for push button
|
||||
CLOSED=3, // applicable only for toggle switch
|
||||
OPEN=4 // applicable only for toggle switch
|
||||
};
|
||||
|
||||
static boolean TRIGGER_ON_LOW(int pin){return(!digitalRead(pin));}
|
||||
static boolean TRIGGER_ON_HIGH(int pin){return(digitalRead(pin));}
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
#if SOC_TOUCH_VERSION_2
|
||||
static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin)>threshold);}
|
||||
#else
|
||||
static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin)<threshold);}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PushButton(int pin, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||
|
||||
// Creates a push-button/toggle-switch of specified type on specified pin
|
||||
//
|
||||
// pin: pin number to which the button is connected
|
||||
// triggerType: a function of of the form 'boolean f(int)' that is passed
|
||||
// the parameter *pin* and returns TRUE if the button associated
|
||||
// with *pin* is pressed/on, or FALSE if not. Can choose from 3 pre-specifed
|
||||
// triggerType_t functions (TRIGGER_ON_LOW, TRIGGER_ON_HIGH, and TRIGGER_ON_TOUCH),
|
||||
// or write your own custom handler
|
||||
|
||||
void reset();
|
||||
|
||||
// Resets state of PushButton. Should be called once before any loops that will
|
||||
// repeatedly check the button for a trigger() event.
|
||||
|
||||
boolean triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime=0);
|
||||
|
||||
// Returns true if button has been triggered by an press event based on the following parameters:
|
||||
|
||||
// singleTime: the minimum time required for the button to be pressed to trigger a Single Press
|
||||
// doubleTime: the maximum time allowed between button presses to qualify as a Double Press
|
||||
// longTime: the minimum time required for the button to be pressed and held to trigger a Long Press
|
||||
|
||||
// All times are in milliseconds (ms). Trigger Rules:
|
||||
|
||||
// * If button is pressed and continuously held, a Long Press will be triggered every longTime ms until the
|
||||
// button is released.
|
||||
// * If button is pressed for more than singleTime ms but less than longTime ms and then released, a Single Press
|
||||
// will be triggered, UNLESS
|
||||
// * The button is pressed a second time within doubleTime ms AND held again for at least singleTime ms, in which case
|
||||
// a DoublePress will be triggered. No further events will occur until the button is released.
|
||||
// * If singleTime>longTime, only Long Press triggers can occur.
|
||||
// * If doubleTime=0, Double Presses cannot occur.
|
||||
// * Once triggered() returns true, if will subsequently return false until there is a new trigger event.
|
||||
|
||||
boolean primed();
|
||||
|
||||
// Returns true if button has been pressed and held for greater than singleTime, but has not yet been released.
|
||||
// After returning true, subsequent calls will always return false until the button has been released and reset.
|
||||
|
||||
int type();
|
||||
|
||||
// Returns the press type based on the whether triggered() or toggled() is called:
|
||||
|
||||
// * For a push button, returns the last trigger event: 0=Single Press, 1=Double Press, 2=Long Press
|
||||
// * For a toggle switch, returns the current state of the switch: 4=ON, 5=OFF
|
||||
|
||||
void wait();
|
||||
|
||||
// Waits for button to be released. Use after Long Press if button release confirmation is desired
|
||||
|
||||
boolean toggled(uint16_t toggleTime);
|
||||
|
||||
// Returns true if switch has been toggled, where
|
||||
|
||||
// toggleTime: the minimum time (in milliseconds) a switch needs to be ON to register a toggle event
|
||||
|
||||
// Once toggled() returns true, if will subsequently return false until the switch is toggled again.
|
||||
|
||||
int getPin(){return(pin);}
|
||||
|
||||
// Returns pin number
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
|
||||
static void setTouchCycles(uint16_t measureTime, uint16_t sleepTime){touchSetCycles(measureTime,sleepTime);}
|
||||
|
||||
// Sets the measure time and sleep time touch cycles , and lower threshold that triggers a touch - used only when triggerType=PushButton::TRIGGER_ON_TOUCH
|
||||
|
||||
// measureTime: duration of measurement time of all touch sensors in number of clock cycles
|
||||
// sleepTime: duration of sleep time (between measurements) of all touch sensors number of clock cycles
|
||||
|
||||
static void setTouchThreshold(touch_value_t thresh){threshold=thresh;}
|
||||
|
||||
// Sets the threshold that triggers a touch - used only when triggerType=TRIGGER_ON_TOUCH
|
||||
|
||||
// thresh: the read value of touch sensors, beyond which which sensors are considered touched (i.e. "pressed").
|
||||
// This is a class-level value applied to all touch sensor buttons.
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
30
ESP32/HomeSpan-master/src/extras/Blinker.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Blinker.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Blinker.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Pixel.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Pixel.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Pixel.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/PwmPin.h
Normal file
30
ESP32/HomeSpan-master/src/extras/PwmPin.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/PwmPin.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/RFControl.h
Normal file
30
ESP32/HomeSpan-master/src/extras/RFControl.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/RFControl.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/StepperControl.h
Normal file
30
ESP32/HomeSpan-master/src/extras/StepperControl.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/StepperControl.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Stepper_A3967.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Stepper_A3967.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Stepper_A3967.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Stepper_TB6612.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Stepper_TB6612.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Stepper_TB6612.h"
|
||||
149
ESP32/HomeSpan-master/src/src/extras/Blinker.cpp
Normal file
149
ESP32/HomeSpan-master/src/src/extras/Blinker.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "Blinker.h"
|
||||
|
||||
////////////////////////////////
|
||||
// Blinker //
|
||||
////////////////////////////////
|
||||
|
||||
Blinker::Blinker(Blinkable *led, uint16_t autoOffDuration){
|
||||
this->led=led;
|
||||
pauseDuration=autoOffDuration*1000;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::blinkTask(void *arg){
|
||||
|
||||
Blinker *b=(Blinker *)arg;
|
||||
|
||||
for(;;){
|
||||
for(int i=0;i<b->nBlinks;i++){
|
||||
b->led->on();
|
||||
delay(b->onTime);
|
||||
b->led->off();
|
||||
delay(b->offTime);
|
||||
}
|
||||
delay(b->delayTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::start(int period, float dutyCycle){
|
||||
|
||||
start(period, dutyCycle, 1, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
onTime=dutyCycle*period;
|
||||
offTime=period-onTime;
|
||||
this->delayTime=delayTime+offTime;
|
||||
this->nBlinks=nBlinks;
|
||||
|
||||
stop();
|
||||
xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 2, &blinkHandle );
|
||||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::BLINKING;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::stop(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
if(blinkHandle!=NULL){
|
||||
vTaskDelete(blinkHandle);
|
||||
blinkHandle=NULL;
|
||||
}
|
||||
|
||||
isPaused=true;
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::on(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
stop();
|
||||
led->on();
|
||||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::ON;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::off(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
stop();
|
||||
led->off();
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::check(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
if(pauseDuration==0 || isPaused || (millis()-pauseTime)<pauseDuration)
|
||||
return;
|
||||
|
||||
ESP_LOGI(BLINKER_TAG,"Pausing LED");
|
||||
off();
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Blinker::getPin(){
|
||||
|
||||
if(!led)
|
||||
return(-1);
|
||||
|
||||
return(led->getPin());
|
||||
}
|
||||
137
ESP32/HomeSpan-master/src/src/extras/Blinker.h
Normal file
137
ESP32/HomeSpan-master/src/src/extras/Blinker.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/timer.h>
|
||||
|
||||
[[maybe_unused]] static const char* BLINKER_TAG = "Blinker";
|
||||
|
||||
////////////////////////////////
|
||||
// Blinkable Interface //
|
||||
////////////////////////////////
|
||||
|
||||
class Blinkable {
|
||||
public:
|
||||
|
||||
virtual void on()=0;
|
||||
virtual void off()=0;
|
||||
virtual int getPin()=0;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// Blinker //
|
||||
////////////////////////////////
|
||||
|
||||
class Blinker {
|
||||
|
||||
enum STATUS {OFF, BLINKING, ON};
|
||||
|
||||
TaskHandle_t blinkHandle = NULL;
|
||||
Blinkable *led;
|
||||
|
||||
int nBlinks;
|
||||
int onTime;
|
||||
int offTime;
|
||||
int delayTime;
|
||||
STATUS status=STATUS::OFF;
|
||||
|
||||
unsigned long pauseDuration;
|
||||
unsigned long pauseTime;
|
||||
boolean isPaused=false;
|
||||
|
||||
static void blinkTask(void *arg);
|
||||
|
||||
public:
|
||||
|
||||
Blinker(Blinkable *led, uint16_t autoOffDuration=0);
|
||||
|
||||
// Creates a generic blinking LED in a separate task thread
|
||||
//
|
||||
// led: An initialized LED device that implements the Blinkable Interface
|
||||
////
|
||||
// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed
|
||||
// Blinker will resume normal operation upon next call to start(), on(), or off()
|
||||
// Program must periodically call check() for auto-off functionality to work
|
||||
|
||||
void start(int period, float dutyCycle=0.5);
|
||||
|
||||
// Starts simple ON/OFF blinking.
|
||||
//
|
||||
// period: ON/OFF blinking period, in milliseconds
|
||||
// dutyCycle: Fraction of period that LED is ON (default=50%)
|
||||
|
||||
void start(int period, float dutyCycle, int nBlinks, int delayTime);
|
||||
|
||||
// Starts ON/OFF blinking pattern.
|
||||
//
|
||||
// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern
|
||||
// dutyCycle: Fraction of period that LED is ON (default=50%)
|
||||
// nBlinks: Number of blinks in blinking portion of pattern
|
||||
// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern
|
||||
|
||||
void stop();
|
||||
|
||||
// Stops current blinking pattern.
|
||||
|
||||
void on();
|
||||
|
||||
// Stops current blinking pattern and turns on LED
|
||||
|
||||
void off();
|
||||
|
||||
// Stops current blinking pattern and turns off LED
|
||||
|
||||
void refresh(){if(status==STATUS::ON)on();}
|
||||
|
||||
// Refreshes LED color by turning device ON if status=ON (if status=BLINKING, new color is automatically used at next blink)
|
||||
|
||||
void check();
|
||||
|
||||
// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0)
|
||||
|
||||
int getPin();
|
||||
|
||||
// Returns pin number of connected LED
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// GenericLED //
|
||||
////////////////////////////////
|
||||
|
||||
class GenericLED : public Blinkable {
|
||||
int pin;
|
||||
|
||||
public:
|
||||
|
||||
GenericLED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);}
|
||||
void on() {digitalWrite(pin,HIGH);}
|
||||
void off() {digitalWrite(pin,LOW);}
|
||||
int getPin() {return(pin);}
|
||||
};
|
||||
205
ESP32/HomeSpan-master/src/src/extras/Pixel.cpp
Normal file
205
ESP32/HomeSpan-master/src/src/extras/Pixel.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Addressable LEDs //
|
||||
////////////////////////////////////////////
|
||||
|
||||
#include "Pixel.h"
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Single-Wire RGB/RGBW NeoPixels //
|
||||
////////////////////////////////////////////
|
||||
|
||||
Pixel::Pixel(int pin, pixelType_t pixelType){
|
||||
|
||||
rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver
|
||||
if(!*rf)
|
||||
return;
|
||||
|
||||
map=pixelType;
|
||||
|
||||
if(map[3])
|
||||
bytesPerPixel=4;
|
||||
else
|
||||
bytesPerPixel=3;
|
||||
|
||||
setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels)
|
||||
|
||||
rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler
|
||||
|
||||
rmt_set_tx_thr_intr_en(rf->getChannel(),false,8); // disable threshold interrupt
|
||||
txThrMask=RMT.int_ena.val; // save interrupt enable vector
|
||||
rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt to trigger every 8 pulses
|
||||
txThrMask^=RMT.int_ena.val; // find bit that flipped and save as threshold mask for this channel
|
||||
|
||||
rmt_set_tx_intr_en(rf->getChannel(),false); // disable end-of-transmission interrupt
|
||||
txEndMask=RMT.int_ena.val; // save interrupt enable vector
|
||||
rmt_set_tx_intr_en(rf->getChannel(),true); // enable end-of-transmission interrupt
|
||||
txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel
|
||||
|
||||
onColor.HSV(0,100,100,0);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset){
|
||||
|
||||
pattern[0]=RF_PULSE(high0*80+0.5,low0*80+0.5);
|
||||
pattern[1]=RF_PULSE(high1*80+0.5,low1*80+0.5);
|
||||
resetTime=lowReset;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Pixel::set(Color *c, int nPixels, boolean multiColor){
|
||||
|
||||
if(!*rf || nPixels==0)
|
||||
return;
|
||||
|
||||
status.nPixels=nPixels;
|
||||
status.color=c;
|
||||
status.iMem=0;
|
||||
status.started=true;
|
||||
status.px=this;
|
||||
status.multiColor=multiColor;
|
||||
status.iByte=0;
|
||||
|
||||
loadData(this); // load first two bytes of data to get started
|
||||
loadData(this);
|
||||
|
||||
rmt_tx_start(rf->getChannel(),true);
|
||||
|
||||
while(status.started); // wait for transmission to be complete
|
||||
delayMicroseconds(resetTime); // end-of-marker delay
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void IRAM_ATTR Pixel::loadData(void *arg){
|
||||
|
||||
if(RMT.int_st.val & status.px->txEndMask){
|
||||
RMT.int_clr.val=status.px->txEndMask;
|
||||
status.started=false;
|
||||
return;
|
||||
}
|
||||
|
||||
RMT.int_clr.val=status.px->txThrMask; // if loadData() is called and it is NOT because of an END interrupt (above) then must either be a pre-load, or a threshold trigger
|
||||
|
||||
if(status.nPixels==0){
|
||||
RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem].val=0;
|
||||
return;
|
||||
}
|
||||
|
||||
int startBit=status.px->map[status.iByte];
|
||||
int endBit=startBit-8;
|
||||
|
||||
for(int iBit=startBit;iBit>endBit;iBit--)
|
||||
RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(status.color->val>>iBit)&1];
|
||||
|
||||
if(++status.iByte==status.px->bytesPerPixel){
|
||||
status.iByte=0;
|
||||
status.color+=status.multiColor;
|
||||
status.nPixels--;
|
||||
}
|
||||
|
||||
status.iMem%=status.px->memSize;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
volatile Pixel::pixel_status_t Pixel::status;
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Two-Wire RGB DotStars //
|
||||
////////////////////////////////////////////
|
||||
|
||||
Dot::Dot(uint8_t dataPin, uint8_t clockPin){
|
||||
|
||||
pinMode(dataPin,OUTPUT);
|
||||
pinMode(clockPin,OUTPUT);
|
||||
digitalWrite(dataPin,LOW);
|
||||
digitalWrite(clockPin,LOW);
|
||||
|
||||
dataMask=1<<(dataPin%32);
|
||||
clockMask=1<<(clockPin%32);
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
dataSetReg=&GPIO.out_w1ts.val;
|
||||
dataClearReg=&GPIO.out_w1tc.val;
|
||||
clockSetReg=&GPIO.out_w1ts.val;
|
||||
clockClearReg=&GPIO.out_w1tc.val;
|
||||
#else
|
||||
if(dataPin<32){
|
||||
dataSetReg=&GPIO.out_w1ts;
|
||||
dataClearReg=&GPIO.out_w1tc;
|
||||
} else {
|
||||
dataSetReg=&GPIO.out1_w1ts.val;
|
||||
dataClearReg=&GPIO.out1_w1tc.val;
|
||||
}
|
||||
|
||||
if(clockPin<32){
|
||||
clockSetReg=&GPIO.out_w1ts;
|
||||
clockClearReg=&GPIO.out_w1tc;
|
||||
} else {
|
||||
clockSetReg=&GPIO.out1_w1ts.val;
|
||||
clockClearReg=&GPIO.out1_w1tc.val;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Dot::set(Color *c, int nPixels, boolean multiColor){
|
||||
|
||||
*dataClearReg=dataMask; // send all zeros
|
||||
for(int j=0;j<31;j++){
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
|
||||
for(int i=0;i<nPixels;i++){
|
||||
for(int b=31;b>=0;b--){
|
||||
if((c->val>>b)&1)
|
||||
*dataSetReg=dataMask;
|
||||
else
|
||||
*dataClearReg=dataMask;
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
c+=multiColor;
|
||||
}
|
||||
|
||||
*dataClearReg=dataMask; // send all zeros
|
||||
for(int j=0;j<31;j++){
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
286
ESP32/HomeSpan-master/src/src/extras/Pixel.h
Normal file
286
ESP32/HomeSpan-master/src/src/extras/Pixel.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Addressable LEDs //
|
||||
////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RFControl.h"
|
||||
#include "PwmPin.h"
|
||||
#include "Blinker.h"
|
||||
|
||||
[[maybe_unused]] static const char* PIXEL_TAG = "Pixel";
|
||||
|
||||
typedef const uint8_t pixelType_t[];
|
||||
|
||||
namespace PixelType {
|
||||
|
||||
pixelType_t RGB={31,23,15,0};
|
||||
pixelType_t RBG={31,15,23,0};
|
||||
pixelType_t BRG={23,15,31,0};
|
||||
pixelType_t BGR={15,23,31,0};
|
||||
pixelType_t GBR={15,31,23,0};
|
||||
pixelType_t GRB={23,31,15,0};
|
||||
pixelType_t RGBW={31,23,15,7};
|
||||
pixelType_t RBGW={31,15,23,7};
|
||||
pixelType_t BRGW={23,15,31,7};
|
||||
pixelType_t BGRW={15,23,31,7};
|
||||
pixelType_t GBRW={15,31,23,7};
|
||||
pixelType_t GRBW={23,31,15,7};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Single-Wire RGB/RGBW NeoPixels //
|
||||
////////////////////////////////////////////
|
||||
|
||||
class Pixel : public Blinkable {
|
||||
|
||||
public:
|
||||
struct Color {
|
||||
union{
|
||||
struct {
|
||||
uint8_t white:8;
|
||||
uint8_t blue:8;
|
||||
uint8_t green:8;
|
||||
uint8_t red:8;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){ // returns Color based on provided RGB(W) values where r/g/b/w=[0-255]
|
||||
this->red=r;
|
||||
this->green=g;
|
||||
this->blue=b;
|
||||
this->white=w;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color HSV(float h, float s, float v, double w=0){ // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100]
|
||||
float r,g,b;
|
||||
LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b);
|
||||
this->red=r*255;
|
||||
this->green=g*255;
|
||||
this->blue=b*255;
|
||||
this->white=w*2.555;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
bool operator==(const Color& color){
|
||||
return(val==color.val);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& color){
|
||||
return(val!=color.val);
|
||||
}
|
||||
|
||||
Color operator+(const Color& color){
|
||||
Color newColor;
|
||||
newColor.white=white+color.white;
|
||||
newColor.blue=blue+color.blue;
|
||||
newColor.red=red+color.red;
|
||||
newColor.green=green+color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator+=(const Color& color){
|
||||
white+=color.white;
|
||||
red+=color.red;
|
||||
blue+=color.blue;
|
||||
green+=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color operator-(const Color& color){
|
||||
Color newColor;
|
||||
newColor.white=white-color.white;
|
||||
newColor.blue=blue-color.blue;
|
||||
newColor.red=red-color.red;
|
||||
newColor.green=green-color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator-=(const Color& color){
|
||||
white-=color.white;
|
||||
red-=color.red;
|
||||
blue-=color.blue;
|
||||
green-=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
}; // Color
|
||||
|
||||
private:
|
||||
struct pixel_status_t {
|
||||
int nPixels;
|
||||
Color *color;
|
||||
int iMem;
|
||||
boolean started;
|
||||
Pixel *px;
|
||||
boolean multiColor;
|
||||
int iByte;
|
||||
};
|
||||
|
||||
RFControl *rf; // Pixel utilizes RFControl
|
||||
uint32_t pattern[2]; // storage for zero-bit and one-bit pulses
|
||||
uint32_t resetTime; // minimum time (in usec) between pulse trains
|
||||
uint32_t txEndMask; // mask for end-of-transmission interrupt
|
||||
uint32_t txThrMask; // mask for threshold interrupt
|
||||
uint8_t bytesPerPixel; // RGBW=4; RGB=3
|
||||
const uint8_t *map; // color map representing order in which color bytes are transmitted
|
||||
Color onColor; // color used for on() command
|
||||
|
||||
const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel
|
||||
|
||||
static void loadData(void *arg); // interrupt handler
|
||||
volatile static pixel_status_t status; // storage for volatile information modified in interupt handler
|
||||
|
||||
public:
|
||||
|
||||
Pixel(int pin, pixelType_t pixelType=PixelType::GRB); // creates addressable single-wire LED of pixelType connected to pin (such as the SK68 or WS28)
|
||||
void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels
|
||||
void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c
|
||||
|
||||
static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){return(Color().RGB(r,g,b,w));} // an alternative method for returning an RGB Color
|
||||
static Color HSV(float h, float s, float v, double w=0){return(Color().HSV(h,s,v,w));} // an alternative method for returning an HSV Color
|
||||
|
||||
int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1
|
||||
boolean isRGBW(){return(bytesPerPixel==4);} // returns true if RGBW LED, else false if RGB LED
|
||||
void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(*rf);
|
||||
}
|
||||
|
||||
void on() {set(onColor);}
|
||||
void off() {set(RGB(0,0,0,0));}
|
||||
Pixel *setOnColor(Color c){onColor=c;return(this);}
|
||||
|
||||
[[deprecated("Please use Pixel(int pin, pixelType_t pixelType) constructor instead.")]]
|
||||
Pixel(int pin, boolean isRGBW):Pixel(pin,isRGBW?PixelType::GRBW:PixelType::GRB){};
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Two-Wire RGB DotStars //
|
||||
////////////////////////////////////////////
|
||||
|
||||
class Dot {
|
||||
|
||||
public:
|
||||
struct Color {
|
||||
union{
|
||||
struct {
|
||||
uint8_t red:8;
|
||||
uint8_t green:8;
|
||||
uint8_t blue:8;
|
||||
uint8_t drive:5;
|
||||
uint8_t flags:3;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){ // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive level=[0,31]
|
||||
this->red=r;
|
||||
this->green=g;
|
||||
this->blue=b;
|
||||
this->drive=driveLevel;
|
||||
this->flags=7;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color HSV(float h, float s, float v, double drivePercent=100){ // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive percent=[0,100]
|
||||
float r,g,b;
|
||||
LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b);
|
||||
this->red=r*255;
|
||||
this->green=g*255;
|
||||
this->blue=b*255;
|
||||
this->drive=drivePercent*0.315;
|
||||
this->flags=7;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
bool operator==(const Color& color){
|
||||
return(val==color.val);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& color){
|
||||
return(val!=color.val);
|
||||
}
|
||||
|
||||
Color operator+(const Color& color){
|
||||
Color newColor;
|
||||
newColor.blue=blue+color.blue;
|
||||
newColor.red=red+color.red;
|
||||
newColor.green=green+color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator+=(const Color& color){
|
||||
red+=color.red;
|
||||
blue+=color.blue;
|
||||
green+=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color operator-(const Color& color){
|
||||
Color newColor;
|
||||
newColor.blue=blue-color.blue;
|
||||
newColor.red=red-color.red;
|
||||
newColor.green=green-color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator-=(const Color& color){
|
||||
red-=color.red;
|
||||
blue-=color.blue;
|
||||
green-=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t dataMask;
|
||||
uint32_t clockMask;
|
||||
volatile uint32_t *dataSetReg;
|
||||
volatile uint32_t *dataClearReg;
|
||||
volatile uint32_t *clockSetReg;
|
||||
volatile uint32_t *clockClearReg;
|
||||
|
||||
public:
|
||||
Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102)
|
||||
void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels
|
||||
void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c
|
||||
|
||||
static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){return(Color().RGB(r,g,b,driveLevel));} // an alternative method for returning an RGB Color
|
||||
static Color HSV(float h, float s, float v, double drivePercent=100){return(Color().HSV(h,s,v,drivePercent));} // an alternative method for returning an HSV Color
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
275
ESP32/HomeSpan-master/src/src/extras/PwmPin.cpp
Normal file
275
ESP32/HomeSpan-master/src/src/extras/PwmPin.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "PwmPin.h"
|
||||
|
||||
///////////////////
|
||||
|
||||
LedC::LedC(uint8_t pin, uint16_t freq, boolean invert){
|
||||
|
||||
if(freq==0)
|
||||
freq=DEFAULT_PWM_FREQ;
|
||||
|
||||
for(int nMode=0;nMode<LEDC_SPEED_MODE_MAX;nMode++){
|
||||
for(int nChannel=0;nChannel<LEDC_CHANNEL_MAX;nChannel++){
|
||||
for(int nTimer=0;nTimer<LEDC_TIMER_MAX;nTimer++){
|
||||
if(!channelList[nChannel][nMode]){
|
||||
|
||||
if(!timerList[nTimer][nMode]){ // if this timer slot is free, use it
|
||||
timerList[nTimer][nMode]=new ledc_timer_config_t; // create new timer instance
|
||||
timerList[nTimer][nMode]->speed_mode=(ledc_mode_t)nMode;
|
||||
timerList[nTimer][nMode]->timer_num=(ledc_timer_t)nTimer;
|
||||
timerList[nTimer][nMode]->freq_hz=freq;
|
||||
timerList[nTimer][nMode]->clk_cfg=LEDC_USE_APB_CLK;
|
||||
|
||||
int res=LEDC_TIMER_BIT_MAX-1; // find the maximum possible resolution
|
||||
while(getApbFrequency()/(freq*pow(2,res))<1)
|
||||
res--;
|
||||
|
||||
timerList[nTimer][nMode]->duty_resolution=(ledc_timer_bit_t)res;
|
||||
if(ledc_timer_config(timerList[nTimer][nMode])!=0){
|
||||
ESP_LOGE(PWM_TAG,"Frequency=%d Hz is out of allowed range ---",freq);
|
||||
delete timerList[nTimer][nMode];
|
||||
timerList[nTimer][nMode]=NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(timerList[nTimer][nMode]->freq_hz==freq){ // if timer matches desired frequency (always true if newly-created above)
|
||||
channelList[nChannel][nMode]=new ledc_channel_config_t; // create new channel instance
|
||||
channelList[nChannel][nMode]->speed_mode=(ledc_mode_t)nMode;
|
||||
channelList[nChannel][nMode]->channel=(ledc_channel_t)nChannel;
|
||||
channelList[nChannel][nMode]->timer_sel=(ledc_timer_t)nTimer;
|
||||
channelList[nChannel][nMode]->intr_type=LEDC_INTR_DISABLE;
|
||||
channelList[nChannel][nMode]->flags.output_invert=invert;
|
||||
channelList[nChannel][nMode]->hpoint=0;
|
||||
channelList[nChannel][nMode]->gpio_num=pin;
|
||||
timer=timerList[nTimer][nMode];
|
||||
channel=channelList[nChannel][nMode];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(pin, freq, invert){
|
||||
|
||||
if(!channel){
|
||||
ESP_LOGE(PWM_TAG,"Can't create LedPin(%d) - no open PWM channels and/or Timers",pin);
|
||||
return;
|
||||
}
|
||||
else
|
||||
ESP_LOGI(PWM_TAG,"LedPin=%d: mode=%d, channel=%d, timer=%d, freq=%d Hz, resolution=%d bits %s",
|
||||
channel->gpio_num,
|
||||
channel->speed_mode,
|
||||
channel->channel,
|
||||
channel->timer_sel,
|
||||
timer->freq_hz,
|
||||
timer->duty_resolution,
|
||||
channel->flags.output_invert?"(inverted)":""
|
||||
);
|
||||
|
||||
ledc_fade_func_install(0);
|
||||
ledc_cbs_t fadeCallbackList = {.fade_cb = fadeCallback}; // for some reason, ledc_cb_register requires the function to be wrapped in a structure
|
||||
ledc_cb_register(channel->speed_mode,channel->channel,&fadeCallbackList,this);
|
||||
|
||||
set(level);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void LedPin::set(float level){
|
||||
|
||||
if(!channel)
|
||||
return;
|
||||
|
||||
if(level>100)
|
||||
level=100;
|
||||
|
||||
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||
|
||||
channel->duty=d;
|
||||
ledc_channel_config(channel);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
int LedPin::fade(float level, uint32_t fadeTime, int fadeType){
|
||||
|
||||
if(!channel)
|
||||
return(1);
|
||||
|
||||
if(fadeState==FADING) // fading already in progress
|
||||
return(1); // return error
|
||||
|
||||
if(level>100)
|
||||
level=100;
|
||||
|
||||
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||
|
||||
if(fadeType==PROPORTIONAL)
|
||||
fadeTime*=fabs((float)ledc_get_duty(channel->speed_mode,channel->channel)-d)/(float)(pow(2,(int)timer->duty_resolution)-1);
|
||||
|
||||
fadeState=FADING;
|
||||
ledc_set_fade_time_and_start(channel->speed_mode,channel->channel,d,fadeTime,LEDC_FADE_NO_WAIT);
|
||||
return(0);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
int LedPin::fadeStatus(){
|
||||
if(fadeState==COMPLETED){
|
||||
fadeState=NOT_FADING;
|
||||
return(COMPLETED);
|
||||
}
|
||||
|
||||
return(fadeState);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
bool IRAM_ATTR LedPin::fadeCallback(const ledc_cb_param_t *param, void *arg){
|
||||
((LedPin *)arg)->fadeState=COMPLETED;
|
||||
return(false);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
|
||||
void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
|
||||
|
||||
// The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||
// h = [0,360]
|
||||
// s = [0,1]
|
||||
// v = [0,1]
|
||||
|
||||
int i;
|
||||
float f, p, q, t;
|
||||
|
||||
if( s == 0 ){
|
||||
*r = *g = *b = v;
|
||||
return;
|
||||
}
|
||||
|
||||
h /= 60;
|
||||
i = floor( h ) ;
|
||||
f = h - i;
|
||||
p = v * ( 1 - s );
|
||||
q = v * ( 1 - s * f );
|
||||
t = v * ( 1 - s * ( 1 - f ) );
|
||||
switch( i % 6 ) {
|
||||
case 0:
|
||||
*r = v;
|
||||
*g = t;
|
||||
*b = p;
|
||||
break;
|
||||
case 1:
|
||||
*r = q;
|
||||
*g = v;
|
||||
*b = p;
|
||||
break;
|
||||
case 2:
|
||||
*r = p;
|
||||
*g = v;
|
||||
*b = t;
|
||||
break;
|
||||
case 3:
|
||||
*r = p;
|
||||
*g = q;
|
||||
*b = v;
|
||||
break;
|
||||
case 4:
|
||||
*r = t;
|
||||
*g = p;
|
||||
*b = v;
|
||||
break;
|
||||
case 5:
|
||||
*r = v;
|
||||
*g = p;
|
||||
*b = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
|
||||
ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees) : LedC(pin, 50){
|
||||
|
||||
if(!channel)
|
||||
ESP_LOGE(PWM_TAG,"Can't create ServoPin(%d) - no open PWM channels and/or Timers",pin);
|
||||
else
|
||||
ESP_LOGI(PWM_TAG,"ServoPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits",
|
||||
channel->gpio_num,
|
||||
channel->speed_mode,
|
||||
channel->channel,
|
||||
channel->timer_sel,
|
||||
timer->freq_hz,
|
||||
timer->duty_resolution
|
||||
);
|
||||
|
||||
this->minMicros=minMicros;
|
||||
this->maxMicros=maxMicros;
|
||||
this->minDegrees=minDegrees;
|
||||
microsPerDegree=(double)(maxMicros-minMicros)/(maxDegrees-minDegrees);
|
||||
|
||||
set(initDegrees);
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void ServoPin::set(double degrees){
|
||||
|
||||
if(!channel)
|
||||
return;
|
||||
|
||||
if(!isnan(degrees)){
|
||||
double usec=(degrees-minDegrees)*microsPerDegree+minMicros;
|
||||
|
||||
if(usec<minMicros)
|
||||
usec=minMicros;
|
||||
else if(usec>maxMicros)
|
||||
usec=maxMicros;
|
||||
|
||||
usec*=timer->freq_hz/1e6*(pow(2,(int)timer->duty_resolution)-1);
|
||||
|
||||
channel->duty=usec;
|
||||
} else {
|
||||
channel->duty=0;
|
||||
}
|
||||
|
||||
ledc_channel_config(channel);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
|
||||
ledc_channel_config_t *LedC::channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX]={};
|
||||
ledc_timer_config_t *LedC::timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX]={};
|
||||
121
ESP32/HomeSpan-master/src/src/extras/PwmPin.h
Normal file
121
ESP32/HomeSpan-master/src/src/extras/PwmPin.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ----- PWM Pin Control -----
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Wrappers around the ESP-IDF ledc library to control PWM-based devices:
|
||||
//
|
||||
// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz
|
||||
// - use set(level) to set brightness from 0-100%
|
||||
//
|
||||
// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz
|
||||
// - use set(degrees) to set position to degrees
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/ledc.h>
|
||||
#include "Blinker.h"
|
||||
|
||||
[[maybe_unused]] static const char* PWM_TAG = "PwmPin";
|
||||
|
||||
#define DEFAULT_PWM_FREQ 5000
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class LedC {
|
||||
|
||||
protected:
|
||||
static ledc_channel_config_t *channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX];
|
||||
static ledc_timer_config_t *timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX];
|
||||
|
||||
ledc_channel_config_t *channel=NULL;
|
||||
ledc_timer_config_t *timer;
|
||||
|
||||
LedC(uint8_t pin, uint16_t freq, boolean invert=false);
|
||||
|
||||
public:
|
||||
int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(channel);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class LedPin : public LedC {
|
||||
|
||||
public:
|
||||
enum {
|
||||
NOT_FADING,
|
||||
COMPLETED,
|
||||
FADING
|
||||
};
|
||||
|
||||
enum {
|
||||
ABSOLUTE,
|
||||
PROPORTIONAL
|
||||
};
|
||||
|
||||
private:
|
||||
int fadeState=NOT_FADING;
|
||||
static bool fadeCallback(const ledc_cb_param_t *param, void *arg);
|
||||
|
||||
public:
|
||||
LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ, boolean invert=false); // assigns pin to be output of one of 16 PWM channels initial level and frequency
|
||||
void set(float level); // sets the PWM duty to level (0-100)
|
||||
int fade(float level, uint32_t fadeTime, int fadeType=ABSOLUTE); // sets the PWM duty to level (0-100) within fadeTime in milliseconds, returns success (0) or fail (1)
|
||||
int fadeStatus(); // returns fading state
|
||||
|
||||
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// ServoPin //
|
||||
////////////////////////////////
|
||||
|
||||
class ServoPin : public LedC {
|
||||
uint16_t minMicros;
|
||||
uint16_t maxMicros;
|
||||
double minDegrees;
|
||||
double microsPerDegree;
|
||||
|
||||
public:
|
||||
ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees);
|
||||
ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {};
|
||||
|
||||
void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees]
|
||||
};
|
||||
164
ESP32/HomeSpan-master/src/src/extras/RFControl.cpp
Normal file
164
ESP32/HomeSpan-master/src/src/extras/RFControl.cpp
Normal file
@@ -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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "RFControl.h"
|
||||
|
||||
///////////////////
|
||||
|
||||
RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if(nChannels==RMT_CHANNEL_MAX/2){
|
||||
#else
|
||||
if(nChannels==RMT_CHANNEL_MAX){
|
||||
#endif
|
||||
ESP_LOGE(RFControl_TAG,"Can't create RFControl(%d) - no open channels",pin);
|
||||
return;
|
||||
}
|
||||
|
||||
config=new rmt_config_t;
|
||||
|
||||
config->rmt_mode=RMT_MODE_TX;
|
||||
config->tx_config.carrier_en=false;
|
||||
config->channel=(rmt_channel_t)nChannels;
|
||||
config->flags=0;
|
||||
config->clk_div = 1;
|
||||
config->mem_block_num=1;
|
||||
config->gpio_num=(gpio_num_t)pin;
|
||||
config->tx_config.idle_output_en=false;
|
||||
config->tx_config.idle_level=RMT_IDLE_LEVEL_LOW;
|
||||
config->tx_config.loop_en=false;
|
||||
|
||||
rmt_config(config);
|
||||
|
||||
if(installDriver)
|
||||
rmt_driver_install(config->channel,0,0);
|
||||
|
||||
// If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock
|
||||
|
||||
this->refClock=refClock;
|
||||
|
||||
if(refClock)
|
||||
#ifdef RMT_SYS_CONF_REG
|
||||
REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 and ESP32-S3 do not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider
|
||||
#else
|
||||
rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2
|
||||
#endif
|
||||
|
||||
nChannels++;
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::start(uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from internal data structure, repeated for nCycles, where each tick in pulse is tickTime microseconds long
|
||||
start(data.data(), data.size(), nCycles, tickTime);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from specified data pointer, repeated for nCycles, where each tick in pulse is tickTime microseconds long
|
||||
|
||||
if(!config || nData==0)
|
||||
return;
|
||||
|
||||
rmt_set_clk_div(config->channel,tickTime); // set clock divider
|
||||
|
||||
for(int i=0;i<nCycles;i++) // loop over nCycles
|
||||
rmt_write_items(config->channel, (rmt_item32_t *) data, nData, true); // start transmission and wait until completed before returning
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::clear(){
|
||||
data.clear();
|
||||
lowWord=true;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::add(uint32_t onTime, uint32_t offTime){
|
||||
|
||||
phase(onTime,HIGH);
|
||||
phase(offTime,LOW);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::phase(uint32_t nTicks, uint8_t phase){
|
||||
|
||||
while(nTicks>0){ // create as many repeated phases as needed to accomodate duration of nTicks
|
||||
uint32_t ticks=nTicks>0x7FFF?0x7FFF:nTicks;
|
||||
nTicks-=ticks;
|
||||
|
||||
if(lowWord)
|
||||
data.push_back(ticks | (phase?(1<<15):0));
|
||||
else
|
||||
data.back()|=ticks<<16 | (phase?(1<<31):0);
|
||||
|
||||
lowWord=!lowWord;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::enableCarrier(uint32_t freq, float duty){
|
||||
|
||||
if(duty<0)
|
||||
duty=0;
|
||||
if(duty>1)
|
||||
duty=1;
|
||||
|
||||
if(freq>0){
|
||||
float period=1.0e6/freq*(refClock?1:80);
|
||||
uint32_t highTime=period*duty+0.5;
|
||||
uint32_t lowTime=period*(1.0-duty)+0.5;
|
||||
|
||||
if(highTime>0xFFFF || lowTime>0xFFFF){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Frequency is too low!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
if(highTime==0){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too low or frequency is too high!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
if(lowTime==0){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too high or frequency is too high!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
rmt_set_tx_carrier(config->channel,true,highTime,lowTime,RMT_CARRIER_LEVEL_HIGH);
|
||||
} else {
|
||||
rmt_set_tx_carrier(config->channel,false,0,0,RMT_CARRIER_LEVEL_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
uint8_t RFControl::nChannels=0;
|
||||
78
ESP32/HomeSpan-master/src/src/extras/RFControl.h
Normal file
78
ESP32/HomeSpan-master/src/src/extras/RFControl.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////
|
||||
// RF Control Module //
|
||||
////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <soc/rmt_reg.h>
|
||||
#include "driver/rmt.h"
|
||||
#include <vector>
|
||||
|
||||
[[maybe_unused]] static const char* RFControl_TAG = "RFControl";
|
||||
|
||||
using std::vector;
|
||||
|
||||
class RFControl {
|
||||
friend class Pixel;
|
||||
|
||||
private:
|
||||
rmt_config_t *config=NULL;
|
||||
vector<uint32_t> data;
|
||||
boolean lowWord=true;
|
||||
boolean refClock;
|
||||
static uint8_t nChannels;
|
||||
|
||||
RFControl(uint8_t pin, boolean refClock, boolean installDriver); // private constructor (only used by Pixel class)
|
||||
|
||||
public:
|
||||
RFControl(uint8_t pin, boolean refClock=true):RFControl(pin,refClock,true){}; // public constructor to create transmitter on pin, using 1-MHz Ref Tick clock or 80-MHz APB clock
|
||||
|
||||
void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long
|
||||
void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long
|
||||
|
||||
void clear(); // clears transmitter memory
|
||||
void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW
|
||||
void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks
|
||||
void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1
|
||||
void disableCarrier(){enableCarrier(0);} // disables carrier wave
|
||||
|
||||
int getPin(){return(config?config->gpio_num:-1);} // returns the pin number, or -1 if no channel defined
|
||||
rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(config);
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above
|
||||
|
||||
#define RF_PULSE(highTicks,lowTicks) (1 << 15 | uint32_t(highTicks) | uint32_t(lowTicks) << 16)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user