ESP + Reorganizacja
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// This example demonstrates the use of a custom Partition Scheme file: "partitions.csv"
|
||||
|
||||
// During compilation, if a file with this EXACT name is placed in the sketch folder,
|
||||
// the esptool performing the compilation will use the partition scheme found
|
||||
// in "partitions.csv" regardless of what partition scheme you selected in the Arduino IDE.
|
||||
|
||||
// Note if you change the partition scheme it is highly recommended that you fully erase the flash
|
||||
// upon your next compile/upload by enabling the "Erase All Flash" option from the Arduino IDE menu.
|
||||
// NOTE: remember to turn OFF this option after you've successully uploaded a sketch with the new
|
||||
// partition scheme, else you will continue to erase everything saved in the NVS every time you upload
|
||||
// a new sketch (which is likely NOT what you want to occur).
|
||||
|
||||
// The main reason for wanting to create your own partition scheme is to expand the NVS space.
|
||||
// All of the pre-configured partition scheme you can select from the Arduino IDE provide
|
||||
// for 504 records of NVS space. This is usuall sufficient for most HomeSpan projects, but if
|
||||
// you have a LOT of Accessories (as per below) AND you are saving their states in NVS, you can
|
||||
// use up all the NVS space. If this occurs, HomeSpan will warn you of low NVS space upon boot-up.
|
||||
|
||||
// The custom partition scheme included in this sketch folder solves this problem by eliminating
|
||||
// the SPIFFs partition (which is generally not used by HomeSpan) and using this portion of the flash
|
||||
// to provide an NVS space with 3906 records --- more than enough for even the largest projects.
|
||||
|
||||
// For reference, in addition to HomeSpan's internal use of NVS (about 32 records), saving a
|
||||
// numerical Characteristic consumes one additional NVS record, and saving a string Characteristic (of
|
||||
// less than 32 characters) consumes two NVS records. Also, the ESP32 WiFi stack consumes about 130
|
||||
// additional NVS records once initialized. As such, the sketch below requires:
|
||||
|
||||
// 32 records (internal HomeSpan use)
|
||||
// + 320 records (80 Accessories * 4 saved numerical Characterstics)
|
||||
// + 160 records (80 Accessories * 2 records per saved string Characterstic)
|
||||
// + 130 records (with WiFi initialized)
|
||||
// ----------------------------------------
|
||||
// = 642 NVS records needed (which exceeds the normal 504 limit, unless a custom partition scheme is used)
|
||||
|
||||
// Note that once HomeSpan is paired with HomeKit, additional NVS records will be consumed to store the
|
||||
// pairing information for each verified HomeKit Controller.
|
||||
|
||||
// Note also that when compiling under the Arduino IDE, the IDE reports the size of partition based on the
|
||||
// Partition Scheme you selected in the IDE menu, even though that scheme is not actually used if you have your
|
||||
// own "partition.csv" file, as in this example. This may lead the IDE to report an incorrect partition size.
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
#define MAX_LIGHTS 80 // configure for 80 Light Accessories
|
||||
|
||||
struct RGB_Light : Service::LightBulb {
|
||||
|
||||
Characteristic::On power{0,true}; // save these 4 numerical Characteristics (4*80 = 320 NVS records)
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{0,true};
|
||||
|
||||
int lightNumber;
|
||||
|
||||
RGB_Light(int n) : Service::LightBulb(){
|
||||
|
||||
lightNumber=n;
|
||||
LOG0("Configured RGB Light-%0d\n",lightNumber);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
if(power.updated())
|
||||
LOG0("Light-%d: Power=%s",lightNumber,power.getNewVal()?"ON":"OFF");
|
||||
|
||||
if(H.updated())
|
||||
LOG0("Light-%d: Hue=%d",lightNumber,H.getNewVal());
|
||||
|
||||
if(S.updated())
|
||||
LOG0("Light-%d: Saturation=%d",lightNumber,S.getNewVal());
|
||||
|
||||
if(V.updated())
|
||||
LOG0("Light-%d: Brightness=%d",lightNumber,V.getNewVal());
|
||||
|
||||
return(false);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Max");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
for(int i=1;i<=MAX_LIGHTS;i++){
|
||||
char c[60];
|
||||
sprintf(c,"Light-%02d",i);
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(c,true); // save this string Characteristic (2*80 = 160 NVS records)
|
||||
|
||||
new RGB_Light(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# --- Custom Partition Table for HomeSpan ---
|
||||
#
|
||||
# Similar to min_spiffs, except that the 128K SPIFF block at the end
|
||||
# is replaced by a 128K NVS block, and the initial 20K NVS block
|
||||
# is no longer used. Note this table is designed for use with 4MB Flash.
|
||||
# To use with 8MB Flash, increase app0 and app1 by 2048K to become 3968K each.
|
||||
# To use with 16MB Flash, increase app0 and app1 by 6144K to become 8064K each
|
||||
#
|
||||
unused_nvs,data,nvs,,20K,
|
||||
otadata,data,ota,,8K,
|
||||
app0,app,ota_0,,1920K,
|
||||
app1,app,ota_1,,1920K,
|
||||
nvs,data,nvs,,128K,
|
||||
coredump,data,coredump,,64K,
|
||||
|
@@ -0,0 +1,110 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
// Apple's HomeKit does not provide any native services or characteristics for measuring atmospheric pressure.
|
||||
// However, Eve for HomeKit does support pressure measurements.
|
||||
|
||||
// This brief sketch demonstrates how you can use HomeSpan's Custom Service and Custom Characteristic features
|
||||
// to create a Pressure Sensor Accessory that will be recognized by the Eve for HomeKit App. Note that the
|
||||
// Apple Home App will show this as a "Not Supported" Accessory Tile indicating it cannot be used in the Home App.
|
||||
// However, this does not create any problems or errors in the Home App.
|
||||
|
||||
// Step 1:
|
||||
|
||||
// Use the CUSTOM_SERV macro to create a new service named AtmosphericPressureSensor with
|
||||
// a UUID=E863F00A-079E-48FF-8F27-9C2605A29F52. This new service will be added to HomeSpan's Service namespace
|
||||
// and can be accessed using the fully-qualified name Service::AtmosphericPressureSensor. The UUID specified
|
||||
// will not be recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you
|
||||
// do NOT enclose either of the parameters in quotes!
|
||||
|
||||
CUSTOM_SERV(AtmosphericPressureSensor, E863F00A-079E-48FF-8F27-9C2605A29F52);
|
||||
|
||||
// Step 2:
|
||||
|
||||
// Use the CUSTOM_CHAR macro to create a new characteristic named AtmosphericPressure with
|
||||
// a UUID=E863F10F-079E-48FF-8F27-9C2605A29F52. This new characteristic will be added to HomeSpan's Characteristic namespace
|
||||
// and can be accessed using the fully-qualified name Characteristic::AtmosphericPressure. The UUID specified will not be
|
||||
// recognized by Apple's Home App, but will be recognized by the Eve for HomeKit App. Note you do NOT enclose any of the
|
||||
// parameters in quotes!
|
||||
//
|
||||
// The meaning of the parmameters are as follows:
|
||||
//
|
||||
// PR+EV: sets permission for "read" and "notify"
|
||||
// FLOAT: sets the format to floating-point decimal number
|
||||
// 1013: sets the default starting value to 1013, which is 1 atm in millibars
|
||||
// 700: sets the default lower range of allowed values to 700 millibars
|
||||
// 1200: sets the default upper range of allowed values to 1200 millibars
|
||||
// false: sets the "static range" flag to false, indicating that users CAN override the default range setRange() if desired
|
||||
|
||||
CUSTOM_CHAR(AtmosphericPressure, E863F10F-079E-48FF-8F27-9C2605A29F52, PR+EV, FLOAT, 1013, 700, 1200, false);
|
||||
|
||||
// Now that AtmosphericPressureSensor and AtmosphericPressure have been created, they can be used just as any other native HomeSpan
|
||||
// Service and Characteristic.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
struct PressureSensor : Service::AtmosphericPressureSensor { // A standalone Air Pressure Sensor
|
||||
|
||||
Characteristic::AtmosphericPressure pressure; // Eve Air Pressure with range 700-1200 hPa (millibars), where 1 atm=1013 hPa
|
||||
|
||||
PressureSensor() : Service::AtmosphericPressureSensor{} {
|
||||
|
||||
Serial.print("Configuring Air Pressure Sensor"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(pressure.timeVal()>5000) // here we simulate an actual sensor by generating a random pressure reading every 5 seconds
|
||||
pressure.setVal((double)random(900,1100));
|
||||
|
||||
} // end loop
|
||||
|
||||
}; // end PressureSensor
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Sensors,"Eve Air Pressure");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
new PressureSensor();
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// Sometimes you need to access Characteristics from outside of the Service structure
|
||||
// in which they were created so you can read and/or modify them in other parts of
|
||||
// a sketch, such as from within the main Arduino loop().
|
||||
|
||||
// This sketch is basically the same as Tutorial Example 5, in which we created
|
||||
// two working LEDs attached to pins 16, and 17. However, in this sketch we will
|
||||
// create global pointers to the LED Services that we can then use in the main loop() to
|
||||
// do something unique.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// First we define our DEV_LED Service exactly the same as in Tutorial Example 5.
|
||||
// This Service contains a single Characteristic named "power" of type Chacracteristic::On
|
||||
|
||||
struct DEV_LED : Service::LightBulb {
|
||||
|
||||
int ledPin;
|
||||
SpanCharacteristic *power;
|
||||
|
||||
DEV_LED(int ledPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
this->ledPin=ledPin;
|
||||
pinMode(ledPin,OUTPUT);
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
digitalWrite(ledPin,power->getNewVal());
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
// Next we create two pointers to the DEV_LED Service. These are created
|
||||
// outside of any class or function so they are globally-scoped and can be
|
||||
// accessed from anywhere else in this sketch.
|
||||
|
||||
// Note that there are just POINTERS to DEV_LED objects. The objects themselves
|
||||
// are not yet created.
|
||||
|
||||
DEV_LED *led16; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 16
|
||||
DEV_LED *led17; // pointer to a DEV_LED structure to be used below to reference a DEV_LED object assigned to pin 17
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan LED");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
led16=new DEV_LED(16); // this is the key step - we SAVE the pointer returned by 'new DEV_LED(16)' in the global variable led16 created above
|
||||
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
led17=new DEV_LED(17); // also save the pointer to the second LED object using the global variable led17 created above
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
// Because the pointers led16 and led17 were created in the global scope, they will still exist even after setup() is completed.
|
||||
// This means we can use them to access the Characteristics within each of those Services.
|
||||
|
||||
// Here we access the power Characteristic of both Services and check to see if they are BOTH on, and if so,
|
||||
// we turn them both off and print a "power overload" message.
|
||||
|
||||
// Note how you can use all the same methods, such as getVal() and setVal(), just as you would do in the Service itself.
|
||||
// Caution: always use getVal(), not getNewVal(), which is only formally defined from within the Service update() method.
|
||||
|
||||
if(led16->power->getVal() && led17->power->getVal()){
|
||||
Serial.printf("Power overload! Can't have both LED's on at the same time. Turn off both LEDs...\n");
|
||||
led16->power->setVal(false);
|
||||
led17->power->setVal(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,112 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// HomeSpan Fading-LED Example. Demonstrates use of:
|
||||
//
|
||||
// LedPin::fade() and LedPin::fadeStatus() methods
|
||||
//
|
||||
// In this sketch we control a single dimmable LED using the Home App as well as a SpanButton.
|
||||
// You can control the brightness of the LED from the Home App, but the SpanButton only turns the
|
||||
// LED either fully on (if it's off) or fully off (if it's already on).
|
||||
//
|
||||
// Rather than set the LED to a specific brightness, this sketch uses the ESP32's hardware-based fading
|
||||
// functionality to fade the LED from one level to the next. We set the timing for each fade to be 2000 ms,
|
||||
// proportional to the difference between the current brightness and the desired brightness. This means it
|
||||
// will take a full 2 seconds to fade the LED from 0-100, but only 1 second to fade from half-brightness to
|
||||
// off.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct FadingLED : Service::LightBulb {
|
||||
|
||||
LedPin *ledPin; // reference to Led Pin
|
||||
SpanCharacteristic *power; // reference to the On Characteristic
|
||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||
|
||||
FadingLED(int _ledPin, int _buttonPin) : Service::LightBulb(){
|
||||
|
||||
power=new Characteristic::On();
|
||||
level=new Characteristic::Brightness(0);
|
||||
ledPin=new LedPin(_ledPin);
|
||||
new SpanButton(_buttonPin);
|
||||
|
||||
}
|
||||
|
||||
boolean update(){
|
||||
|
||||
ledPin->fade(power->getNewVal()*level->getNewVal(),2000,LedPin::PROPORTIONAL); // use fade() to set new level; timing=2 seconds, proportional scale
|
||||
while(ledPin->fadeStatus()==LedPin::FADING); // wait until fading is completed
|
||||
|
||||
return(true);
|
||||
|
||||
}
|
||||
|
||||
void button(int pin, int pressType) override {
|
||||
|
||||
// Below we turn LED fully on or off depending on whether power is on
|
||||
// Unlike above, we will NOT wait for the fading to complete, but will return immediately
|
||||
|
||||
if(ledPin->fade(100-(power->getVal())*100,2000,LedPin::PROPORTIONAL)!=0) // use fade to either turn fully on or fully off; check return status to see if call was successful
|
||||
Serial.printf("Button Press Ignored\n");
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
|
||||
// Below we set power and level once fading from a button press is completed
|
||||
|
||||
if(ledPin->fadeStatus()==LedPin::COMPLETED){
|
||||
power->setVal(1-power->getVal());
|
||||
level->setVal(power->getVal()?100:0);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"Fading LED");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new FadingLED(26,4); // first argument is LED Pin, second argument is PushButton Pin
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// This example demonstrates how to control real-world Stepper Motors using HomeSpan's
|
||||
// StepperControl Class through the implemention of a Motorized Window Shade Accessory.
|
||||
|
||||
// The sketch below is based on the more-fully commented WindowShade Accessory included in
|
||||
// Tutorial Example 13 (this sketch only contains comments related to the use of the stepper motors).
|
||||
|
||||
// The Accessory we will use two stepper motors:
|
||||
|
||||
// * one motor to open/close the window shade, driven by an Adafruit TB6612 driver board (https://www.adafruit.com/product/2448)
|
||||
// * one motor to tilt the window shade slats, driven by a SparkFun A3967 driver board (https://www.sparkfun.com/products/12779)
|
||||
|
||||
// See HomeSpan's StepperControl documentation for details on the classes used to control these driver boards,
|
||||
// as well as for instructions on how you can easily extend StepperControl to create a custom driver for any board.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_WindowShade : Service::WindowCovering {
|
||||
|
||||
Characteristic::CurrentPosition currentPos{0,true};
|
||||
Characteristic::TargetPosition targetPos{0,true};
|
||||
Characteristic::CurrentHorizontalTiltAngle currentTilt{0,true};
|
||||
Characteristic::TargetHorizontalTiltAngle targetTilt{0,true};
|
||||
|
||||
StepperControl *mainMotor; // motor to open/close shade
|
||||
StepperControl *slatMotor; // motor to tilt shade slats
|
||||
|
||||
DEV_WindowShade(StepperControl *mainMotor, StepperControl *slatMotor) : Service::WindowCovering(){
|
||||
|
||||
this->mainMotor=mainMotor; // save pointers to the motors
|
||||
this->slatMotor=slatMotor;
|
||||
|
||||
mainMotor->setAccel(10,20); // set acceleration parameters for main motor
|
||||
mainMotor->setStepType(StepperControl::HALF_STEP); // set step type to HALF STEP for main motor
|
||||
|
||||
LOG0("Initial Open/Close Position: %d\n",currentPos.getVal());
|
||||
LOG0("Initial Slat Position: %d\n",currentTilt.getVal());
|
||||
|
||||
mainMotor->setPosition(currentPos.getVal()*20); // define initial position of main motor
|
||||
slatMotor->setPosition(currentTilt.getVal()*11.47); // define initial position of slat motor
|
||||
}
|
||||
|
||||
///////////
|
||||
|
||||
boolean update(){
|
||||
|
||||
if(targetPos.updated()){
|
||||
|
||||
// Move motor to absolute position, assuming 400 steps per revolution and 5 revolutions for full open/close travel,
|
||||
// for a total of 2000 steps of full travel. Specify that motor should enter the BRAKE state upon reaching to desired position.
|
||||
// Must multiply targetPos, which ranges from 0-100, by 20 to scale to number of motor steps needed
|
||||
|
||||
mainMotor->moveTo(targetPos.getNewVal()*20,5,StepperControl::BRAKE);
|
||||
LOG1("Setting Shade Position=%d\n",targetPos.getNewVal());
|
||||
}
|
||||
|
||||
if(targetTilt.updated()){
|
||||
|
||||
// Move motor to absolute position, assuming 2064 steps per revolution and 1/2 revolution for full travel of slat tilt in either direction
|
||||
// Must multiply targetPos, which ranges from -90 to 90, by 11.47 to scale number of motor steps needed
|
||||
// Note this driver board for this motor does not support a "short brake" state
|
||||
|
||||
slatMotor->moveTo(targetTilt.getNewVal()*11.47,5);
|
||||
LOG1("Setting Shade Position=%d\n",targetTilt.getNewVal());
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
///////////
|
||||
|
||||
void loop(){
|
||||
|
||||
// If the current window shade position or tilt does NOT equal the target position, BUT the motor has stopped moving,
|
||||
// we must have reached the target position, so set the current position equal to the target position
|
||||
|
||||
if(currentPos.getVal()!=targetPos.getVal() && !mainMotor->stepsRemaining()){
|
||||
currentPos.setVal(targetPos.getVal());
|
||||
LOG1("Main Motor Stopped at Shade Position=%d\n",currentPos.getVal());
|
||||
}
|
||||
|
||||
if(currentTilt.getVal()!=targetTilt.getVal() && !slatMotor->stepsRemaining()){
|
||||
currentTilt.setVal(targetTilt.getVal());
|
||||
LOG1("Slat Motor Stopped at Shade Tilt=%d\n",currentTilt.getVal());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::WindowCoverings,"Motorized Shade");
|
||||
|
||||
// MAKE SURE TO CHANGE THE PINS NUMBERS BELOW TO MATCH YOUR ESP32 DEVICE!!!
|
||||
// THE PINS NUMBER SPECIFIED IN THIS EXAMPLE WORK WITH THE ORIGINAL ESP32, BUT WILL LIKELY CRASH AN ESP32-S2, -S3, or -C3.
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new DEV_WindowShade(new Stepper_TB6612(23,32,22,14,33,27), new Stepper_A3967(18,21,5,4,19)); // instantiate drivers for each board and specify pins used on ESP32
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
}
|
||||
203
ESP32/HomeSpan-master/examples/Other Examples/Pixel/Pixel.ino
Normal file
203
ESP32/HomeSpan-master/examples/Other Examples/Pixel/Pixel.ino
Normal file
@@ -0,0 +1,203 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// HomeSpan Addressable RGB LED Examples. Demonstrates use of:
|
||||
//
|
||||
// * HomeSpan Pixel Class that provides for control of single-wire addressable RGB and RGBW LEDs, such as the WS2812 and SK6812
|
||||
// * HomeSpan Dot Class that provides for control of two-wire addressable RGB LEDs, such as the APA102 and SK9822
|
||||
//
|
||||
// IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD
|
||||
//
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 26
|
||||
#define NEOPIXEL_RGBW_PIN 32
|
||||
#define DOTSTAR_DATA_PIN 33
|
||||
#define DOTSTAR_CLOCK_PIN 27
|
||||
#define DEVICE_SUFFIX ""
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 17
|
||||
#define NEOPIXEL_RGBW_PIN 38
|
||||
#define DOTSTAR_DATA_PIN 3
|
||||
#define DOTSTAR_CLOCK_PIN 7
|
||||
#define DEVICE_SUFFIX "-S2"
|
||||
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
|
||||
#define NEOPIXEL_RGB_PIN 0
|
||||
#define NEOPIXEL_RGBW_PIN 3
|
||||
#define DOTSTAR_DATA_PIN 7
|
||||
#define DOTSTAR_CLOCK_PIN 2
|
||||
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#endif
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct NeoPixel_RGB : Service::LightBulb { // Addressable single-wire RGB LED Strand (e.g. NeoPixel)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Pixel *pixel;
|
||||
int nPixels;
|
||||
|
||||
NeoPixel_RGB(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||
pixel=new Pixel(pin); // creates Pixel RGB LED on specified pin
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float h=H.getNewVal<float>(); // range = [0,360]
|
||||
float s=S.getNewVal<float>(); // range = [0,100]
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
|
||||
Pixel::Color color;
|
||||
|
||||
pixel->set(color.HSV(h*p, s*p, v*p),nPixels); // sets all nPixels to the same HSV color
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct NeoPixel_RGBW : Service::LightBulb { // Addressable single-wire RGBW LED Strand (e.g. NeoPixel)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Characteristic::ColorTemperature T{140,true};
|
||||
Pixel *pixel;
|
||||
int nPixels;
|
||||
|
||||
NeoPixel_RGBW(uint8_t pin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||
pixel=new Pixel(pin,PixelType::GRBW); // creates Pixel RGBW LED on specified pin (with order of colors chnanged to reflect this specific NeoPixel device)
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
float t=T.getNewVal<float>(); // range = [140,500] (140=coldest, 500=warmest)
|
||||
|
||||
float hue=240-(t-140)/3; // add in a splash of color between blue and green to simulated change of color temperature
|
||||
|
||||
// Pixel::Color color; // if static HSV method is used (below), there is no need to first create a Color object
|
||||
|
||||
pixel->set(pixel->HSV(hue, 100, v*p, v*p),nPixels); // sets all nPixels to the same HSV color (note use of static method pixel->HSV, instead of defining and setting Pixel::Color)
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED Strand (e.g. DotStar)
|
||||
|
||||
Characteristic::On power{0,true};
|
||||
Characteristic::Hue H{0,true};
|
||||
Characteristic::Saturation S{0,true};
|
||||
Characteristic::Brightness V{100,true};
|
||||
Dot *pixel;
|
||||
int nPixels;
|
||||
|
||||
DotStar_RGB(uint8_t dataPin, uint8_t clockPin, int nPixels) : Service::LightBulb(){
|
||||
|
||||
V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1%
|
||||
pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins
|
||||
this->nPixels=nPixels; // save number of Pixels in this LED Strand
|
||||
update(); // manually call update() to set pixel with restored initial values
|
||||
update(); // call second update() a second time - DotStar seems to need to be "refreshed" upon start-up
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
int p=power.getNewVal();
|
||||
|
||||
float h=H.getNewVal<float>(); // range = [0,360]
|
||||
float s=S.getNewVal<float>(); // range = [0,100]
|
||||
float v=V.getNewVal<float>(); // range = [0,100]
|
||||
|
||||
Dot::Color color[nPixels]; // create an arrary of Colors
|
||||
|
||||
float hueStep=360.0/nPixels; // step size for change in hue from one pixel to the next
|
||||
|
||||
for(int i=0;i<nPixels;i++)
|
||||
color[i].HSV(h+i*hueStep,s,100,v*p); // create spectrum of all hues starting with specified Hue; use current-limiting parameter (4th argument) to control overall brightness, instead of PWM
|
||||
|
||||
pixel->set(color,nPixels); // set the colors according to the array
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Lighting,"Pixel LEDS" DEVICE_SUFFIX);
|
||||
|
||||
SPAN_ACCESSORY(); // create Bridge (note this sketch uses the SPAN_ACCESSORY() macro, introduced in v1.5.1 --- see the HomeSpan API Reference for details on this convenience macro)
|
||||
|
||||
SPAN_ACCESSORY("Neo RGB");
|
||||
new NeoPixel_RGB(NEOPIXEL_RGB_PIN,8); // create 8-LED NeoPixel RGB Strand with full color control
|
||||
|
||||
SPAN_ACCESSORY("Neo RGBW");
|
||||
new NeoPixel_RGBW(NEOPIXEL_RGBW_PIN,60); // create 60-LED NeoPixel RGBW Strand with simulated color temperature control
|
||||
|
||||
SPAN_ACCESSORY("Dot RGB");
|
||||
new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand displaying a spectrum of colors and using the current-limiting feature of DotStars to create flicker-free dimming
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void loop() {
|
||||
homeSpan.poll();
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
@@ -0,0 +1,131 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/////////////////////// PIXEL TESTER //////////////////////////
|
||||
|
||||
// This sketch is designed to help identify the proper settings to use for a NeoPixel, NeoPixel Strip,
|
||||
// or any device containing one or more single-wire addressable RGB or RGBW LEDs (the "Pixel Device").
|
||||
|
||||
// Before compiling, set PIXEL_PIN to the ESP32 pin that is connected to your Pixel Device, and set NPIXELS to
|
||||
// the numnber of Pixels in the Pixel Device. Note that for some strips a single chip controls more than one LED,
|
||||
// in which case NPIXELS should be set to the number of controlling chips, NOT the number of LEDs.
|
||||
|
||||
// To start, the second argument of the Pixel constructor for the testPixel object below should remain
|
||||
// set to PixelType::RGBW
|
||||
|
||||
// When run, the sketch will repeatedly cycle colors by setting ALL pixels in the device first to RED, then GREEN,
|
||||
// followed by BLUE, and then finally WHITE. After a short pause, the cycle repeats.
|
||||
|
||||
// For each color the brightness will increase from 0 through MAX_BRIGHTNESS, and then back to 0. You can change
|
||||
// MAX_BRIGHTNESS to something lower than 255 if you want to limit how bright the pixels get.
|
||||
|
||||
// For Pixel Devices with more than one pixel, diagnostics are as follows:
|
||||
//
|
||||
// * If all 4 colors repeatedly flash in the order expected, this means the base setting of PixelType::RGBW is correct!
|
||||
//
|
||||
// * If instead of each pixel being set to the same color, the pixels in the strip each light up with a different color
|
||||
// (or no color at all), this means you have an RGB LED, not an RGBW LED. Change the second parameter of the constructor
|
||||
// to PixelType::RGB and re-run the sketch.
|
||||
//
|
||||
// * If all of the pixels are being set to the same color, but the sequence is NOT in the order RED, GREEN, BLUE, change
|
||||
// the second parameter of the constructor so that the order of the PixelType colors match the sequence of the colors
|
||||
// that appear on the Pixel Device. For example, if your RGBW Pixel Device flashes GREEN, RED, BLUE, and than WHITE, use
|
||||
// PixelType::GRBW.
|
||||
|
||||
// For Pixel Devices with only a single pixel, diagnostics are as follows:
|
||||
|
||||
// * If all 4 colors repeatedly flash in the order expected, this means the base setting of PixelType::RGBW is correct!
|
||||
//
|
||||
// * If the pixel does not light at all when set to WHITE this means you have an RGB LED, not an RGBW LED. Change the
|
||||
// second parameter of the constructor to PixelType::RGB and re-run the sketch.
|
||||
//
|
||||
// * If all of the pixels are being set to the same color, but the sequence is NOT in the order RED, GREEN, BLUE, change
|
||||
// the second parameter of the constructor so that the order of the PixelType colors match the sequence of the colors
|
||||
// that appear on the Pixel Device. For example, if your RGB Pixel Device flashes GREEN, RED, and then BLUE, use
|
||||
// PixelType::GRB.
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
#define MAX_BRIGHTNESS 255 // maximum brightness when flashing RGBW [0-255]
|
||||
|
||||
#define PIXEL_PIN 26 // set this to whatever pin you are using - note pin cannot be "input only"
|
||||
#define NPIXELS 8 // set to number of pixels in strip
|
||||
|
||||
Pixel testPixel(PIXEL_PIN, PixelType::RGBW); // change the second argument until device operates with correct colors
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.printf("\n\nPixel Test on pin %d with %d pixels\n\n",PIXEL_PIN,NPIXELS);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void flashColor(boolean r, boolean g, boolean b, boolean w){
|
||||
|
||||
for(int i=0;i<MAX_BRIGHTNESS;i++){
|
||||
testPixel.set(Pixel::RGB(i*r,i*g,i*b,i*w),NPIXELS);
|
||||
delay(4);
|
||||
}
|
||||
|
||||
for(int i=MAX_BRIGHTNESS;i>=0;i--){
|
||||
testPixel.set(Pixel::RGB(i*r,i*g,i*b,i*w),NPIXELS);
|
||||
delay(4);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
Serial.printf("Red...");
|
||||
flashColor(1,0,0,0);
|
||||
|
||||
Serial.printf("Green...");
|
||||
flashColor(0,1,0,0);
|
||||
|
||||
Serial.printf("Blue...");
|
||||
flashColor(0,0,1,0);
|
||||
|
||||
if(testPixel.isRGBW()){
|
||||
Serial.printf("White...");
|
||||
flashColor(0,0,0,1);
|
||||
}
|
||||
|
||||
Serial.printf("Pausing.\n");
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,388 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to implement a Web Server alongside //
|
||||
// of HomeSpan to create a Programmable Hub serving up to //
|
||||
// 12 Configurable Lights. Allows for dynamic changes //
|
||||
// to Accessories without needing to reboot //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include <WebServer.h> // include WebServer library
|
||||
|
||||
WebServer webServer(80); // create WebServer on port 80
|
||||
|
||||
#define MAX_LIGHTS 12
|
||||
#define MAX_NAME_LENGTH 32
|
||||
#define HUB_NAME "lighthub"
|
||||
|
||||
enum colorType_t : uint8_t {
|
||||
NO_COLOR,
|
||||
TEMPERATURE_ONLY,
|
||||
FULL_RGB
|
||||
};
|
||||
|
||||
uint32_t aidStore=2; // keep track of unique AID numbers - start with AID=2
|
||||
|
||||
struct lightData_t {
|
||||
char name[MAX_NAME_LENGTH+1]="";
|
||||
uint32_t aid=0;
|
||||
boolean isDimmable:1;
|
||||
colorType_t colorType:2;
|
||||
} lightData[MAX_LIGHTS];
|
||||
|
||||
nvs_handle savedData;
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
size_t len;
|
||||
nvs_open("SAVED_DATA",NVS_READWRITE,&savedData); // open a new namespace called SAVED_DATA in the NVS
|
||||
|
||||
if(!nvs_get_blob(savedData,"LIGHTDATA",NULL,&len)) // if LIGHTDATA data found
|
||||
nvs_get_blob(savedData,"LIGHTDATA",&lightData,&len); // retrieve data
|
||||
|
||||
nvs_get_u32(savedData,"AID",&aidStore); // get AID, if it exists
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.setHostNameSuffix(""); // use null string for suffix (rather than the HomeSpan device ID)
|
||||
homeSpan.setPortNum(1201); // change port number for HomeSpan so we can use port 80 for the Web Server
|
||||
homeSpan.setWifiCallback(setupWeb); // need to start Web Server after WiFi is established
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Light Hub",HUB_NAME);
|
||||
|
||||
new SpanAccessory(1); // here we specified the AID=1 for clarity (it would default to 1 anyway if left blank)
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Model("HomeSpan Programmable Hub");
|
||||
new Characteristic::AccessoryFlags();
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){ // create Light Accessories based on saved data
|
||||
if(lightData[i].aid)
|
||||
addLight(i);
|
||||
}
|
||||
|
||||
new SpanUserCommand('a',"<name> - add non-dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,false,NO_COLOR);});
|
||||
new SpanUserCommand('A',"<name> - add dimmable light accessory using name=<name>",[](const char *c){addLight(c+1,true,NO_COLOR);});
|
||||
new SpanUserCommand('t',"<name> - add non-dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,false,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('T',"<name> - add dimmable light accessory with color-temperature control using name=<name>",[](const char *c){addLight(c+1,true,TEMPERATURE_ONLY);});
|
||||
new SpanUserCommand('r',"<name> - add non-dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,false,FULL_RGB);});
|
||||
new SpanUserCommand('R',"<name> - add dimmable light accessory with full RGB color control using name=<name>",[](const char *c){addLight(c+1,true,FULL_RGB);});
|
||||
|
||||
new SpanUserCommand('l'," - list all light accessories",listAccessories);
|
||||
new SpanUserCommand('d',"<index> - delete a light accessory with index=<index>",[](const char *buf){deleteAccessory(atoi(buf+1));});
|
||||
new SpanUserCommand('D'," - delete ALL light accessories",deleteAllAccessories);
|
||||
new SpanUserCommand('u',"- update accessories database",updateAccessories);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void loop(){
|
||||
homeSpan.poll();
|
||||
webServer.handleClient(); // handle incoming web server traffic
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void addLight(int index){
|
||||
|
||||
Serial.printf("Adding Light Accessory: Name='%s' Dimmable=%s Color=%s\n",
|
||||
lightData[index].name,lightData[index].isDimmable?"YES":"NO",lightData[index].colorType==NO_COLOR?"NONE":(lightData[index].colorType==TEMPERATURE_ONLY?"TEMPERATURE_ONLY":"FULL_RGB"));
|
||||
|
||||
new SpanAccessory(lightData[index].aid);
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name(lightData[index].name);
|
||||
char sNum[32];
|
||||
sprintf(sNum,"Light-%02d",index);
|
||||
new Characteristic::SerialNumber(sNum);
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
if(lightData[index].isDimmable)
|
||||
new Characteristic::Brightness(100,true);
|
||||
if(lightData[index].colorType==TEMPERATURE_ONLY)
|
||||
new Characteristic::ColorTemperature(200,true);
|
||||
if(lightData[index].colorType==FULL_RGB){
|
||||
new Characteristic::Hue(0,true);
|
||||
new Characteristic::Saturation(0,true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
int addLight(const char *name, boolean isDimmable, colorType_t colorType){
|
||||
|
||||
int index=0;
|
||||
for(index=0;index<MAX_LIGHTS && lightData[index].aid;index++);
|
||||
|
||||
if(index==MAX_LIGHTS){
|
||||
Serial.printf("Can't add Light Accessory - maximum number of %d are already defined.\n",MAX_LIGHTS);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int n=strncpy_trim(lightData[index].name,name,sizeof(lightData[index].name));
|
||||
|
||||
if(n==1){
|
||||
Serial.printf("Can't add Light Accessory without a name specified.\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if(n>sizeof(lightData[index].name))
|
||||
Serial.printf("Warning - name trimmed to max length of %d characters.\n",MAX_NAME_LENGTH);
|
||||
|
||||
lightData[index].isDimmable=isDimmable;
|
||||
lightData[index].colorType=colorType;
|
||||
lightData[index].aid=aidStore++;
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_set_u32(savedData,"AID",aidStore);
|
||||
nvs_commit(savedData);
|
||||
|
||||
addLight(index);
|
||||
return(index);
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
size_t strncpy_trim(char *dest, const char *src, size_t dSize){
|
||||
|
||||
while(*src==' ') // skip over any leading spaces
|
||||
src++;
|
||||
|
||||
size_t sLen=strlen(src); // string length of src after skipping over leading spaces
|
||||
while(sLen>0 && src[sLen-1]==' ') // shorten length to remove trailing spaces
|
||||
sLen--;
|
||||
|
||||
size_t sSize=sLen+1; // add room for null terminator
|
||||
|
||||
if(dest!=NULL)
|
||||
*stpncpy(dest,src,(dSize<sSize?dSize:sSize)-1)='\0';
|
||||
|
||||
return(sSize); // return total size needed for entire trimmed string, including null terminator
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAccessory(int index){
|
||||
|
||||
if(index<0 || index>=MAX_LIGHTS){
|
||||
Serial.printf("Invalid Light Accessory index - must be between 0 and %d.\n",MAX_LIGHTS-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(homeSpan.deleteAccessory(lightData[index].aid)){ // if deleteAccessory() is true, a match has been found
|
||||
Serial.printf("Deleting Light Accessory: Name='%s'\n",lightData[index].name);
|
||||
|
||||
lightData[index].aid=0;
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
} else {
|
||||
Serial.printf("Nothing to delete - there is no Light Accessory at index=%d.\n",index);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void deleteAllAccessories(const char *buf){
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
homeSpan.deleteAccessory(lightData[i].aid);
|
||||
lightData[i].aid=0;
|
||||
}
|
||||
|
||||
nvs_set_blob(savedData,"LIGHTDATA",&lightData,sizeof(lightData)); // update data in the NVS
|
||||
nvs_commit(savedData);
|
||||
|
||||
Serial.printf("All Light Accessories deleted!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void updateAccessories(const char *buf){
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
Serial.printf("Accessories Database updated. New configuration number broadcasted...\n");
|
||||
else
|
||||
Serial.printf("Nothing to update - no changes were made!\n");
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void listAccessories(const char *buf){
|
||||
|
||||
Serial.printf("\nIndex Dimmable Color Name\n");
|
||||
Serial.printf("----- -------- ----- ");
|
||||
|
||||
for(int i=0;i<MAX_NAME_LENGTH;i++)
|
||||
Serial.printf("-");
|
||||
Serial.printf("\n");
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid)
|
||||
Serial.printf("%5d %8s %5s %-s\n",i,lightData[i].isDimmable?"YES":"NO",lightData[i].colorType==NO_COLOR?"NONE":(lightData[i].colorType==TEMPERATURE_ONLY?"TEMP":"RGB"),lightData[i].name);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
|
||||
void setupWeb(){
|
||||
|
||||
Serial.printf("Starting Light Server Hub at %s.local\n\n",HUB_NAME);
|
||||
webServer.begin();
|
||||
|
||||
webServer.on("/", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title>";
|
||||
response += "<style>table, th, td {border: 1px solid black; border-collapse: collapse;} th, td { padding: 5px; text-align: center; } </style></head>\n";
|
||||
response += "<body><h2>HomeSpan Lights</h2>";
|
||||
response += "<form action='/addLight' method='get'>";
|
||||
response += "<table><tr><th style='text-align:left;'>Accessory</th><th>Dim?</th><th>Color Control</th><th>Action</th></tr>";
|
||||
|
||||
int openSlots=MAX_LIGHTS;
|
||||
|
||||
for(int i=0;i<MAX_LIGHTS;i++){
|
||||
if(lightData[i].aid){
|
||||
response += "<tr><td style='text-align:left;'>" + String(lightData[i].name) + "</td>";
|
||||
response += "<td><input type='checkbox' disabled " + String(lightData[i].isDimmable?"checked>":">") + "</td>";
|
||||
response += "<td><input type='radio' disabled " + String(lightData[i].colorType==NO_COLOR?"checked>":">") + " NONE ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==TEMPERATURE_ONLY?"checked>":">") + " TEMP ONLY ";
|
||||
response += "<input type='radio' disabled " + String(lightData[i].colorType==FULL_RGB?"checked>":">") + " FULL COLOR </td>";
|
||||
response += "<td><button type='button' onclick=\"document.location='/deleteLight?index=" + String(i) + "'\">Delete Light</button></td>";
|
||||
response += "</tr>";
|
||||
openSlots--;
|
||||
}
|
||||
}
|
||||
|
||||
response += "<tr><td style='text-align:left;'><input type='text' name='name' required placeholder='Type accessory name here...' size='"
|
||||
+ String(MAX_NAME_LENGTH) + "' maxlength='" + String(MAX_NAME_LENGTH) + "'></td>";
|
||||
response += "<td><input type='checkbox' name='isDimmable'></td>";
|
||||
response += "<td><input type='radio' checked name='colorType' for='no_color' value='" + String(NO_COLOR) + "'><label for='no_color'> NONE </label>";
|
||||
response += "<input type='radio' name='colorType' for='temp_only' value='" + String(TEMPERATURE_ONLY) + "'><label for='temp_only'> TEMP ONLY </label>";
|
||||
response += "<input type='radio' name='colorType' for='full_rgb' value='" + String(FULL_RGB) + "'><label for='full_rgb'> FULL COLOR </label></td>";
|
||||
response += "<td><input type='submit' value='Add Light'" + String(openSlots?"":" disabled") + "></td>";
|
||||
response += "</tr>";
|
||||
|
||||
response += "</table>";
|
||||
response += "</form>";
|
||||
|
||||
if(!openSlots)
|
||||
response += "<p>Can't add any more Light Accessories. Max="+ String(MAX_LIGHTS) + "</p>";
|
||||
|
||||
response += "<p>Press here to delete ALL Light Accessories: <button type='button' onclick=\"document.location='/deleteAll'\">Delete All Lights</button></p>";
|
||||
response += "<p>Press here to update the Home App when finished making changes: <button type='button' onclick=\"document.location='/update'\">Upddate HomeKit</button></p>";
|
||||
|
||||
response += "</body></html>";
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteLight", []() {
|
||||
|
||||
int index=atoi(webServer.arg(0).c_str());
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting Light Accessory '" + String(lightData[index].name) + "'...</body></html>";
|
||||
|
||||
deleteAccessory(index);
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/deleteAll", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head>";
|
||||
response += "<body>Deleting All Light Accessories...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
deleteAllAccessories("");
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/update", []() {
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
if(homeSpan.updateDatabase())
|
||||
response += "Accessories Database updated. New configuration number broadcasted...";
|
||||
else
|
||||
response += "Nothing to update - no changes were made...";
|
||||
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
webServer.on("/addLight", []() {
|
||||
|
||||
colorType_t colorType=NO_COLOR;
|
||||
boolean isDimmable=false;
|
||||
int iName=-1;
|
||||
|
||||
for(int i=0;i<webServer.args();i++){
|
||||
if(!webServer.argName(i).compareTo(String("colorType")))
|
||||
colorType=(colorType_t)webServer.arg(i).toInt();
|
||||
else if(!webServer.argName(i).compareTo(String("isDimmable")))
|
||||
isDimmable=true;
|
||||
else if(!webServer.argName(i).compareTo(String("name")))
|
||||
iName=i;
|
||||
}
|
||||
|
||||
String response = "<html><head><title>HomeSpan Programmable Light Hub</title><meta http-equiv='refresh' content = '3; url=/'/></head><body>";
|
||||
|
||||
if(iName!=-1){
|
||||
int index=addLight(webServer.arg(iName).c_str(),isDimmable,colorType);
|
||||
response += "Adding Light Accessory '" + String(lightData[index].name) + "'";
|
||||
} else
|
||||
response += "Error - bad URL request";
|
||||
|
||||
response += "...</body></html>";
|
||||
|
||||
webServer.send(200, "text/html", response);
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h" // include the HomeSpan library
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200); // start the Serial interface
|
||||
Serial.flush();
|
||||
delay(1000); // wait for interface to flush
|
||||
|
||||
Serial.print("\n\nHomeSpan RF Transmitter Example\n\n");
|
||||
|
||||
RFControl rf(13); // create an instance of RFControl with signal output to pin 13 of the ESP32
|
||||
|
||||
rf.clear(); // clear the pulse train memory buffer
|
||||
|
||||
rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses
|
||||
rf.add(5000,5000);
|
||||
rf.add(5000,10000); // double duration of final low period
|
||||
|
||||
Serial.print("Starting 4 cycles of three 500 ms on pulses...");
|
||||
|
||||
rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds
|
||||
|
||||
Serial.print("Done!\n");
|
||||
|
||||
delay(2000);
|
||||
|
||||
rf.clear();
|
||||
|
||||
for(int i=1000;i<10000;i+=1000)
|
||||
rf.add(i,10000-i);
|
||||
rf.add(10000,10000);
|
||||
|
||||
Serial.print("Starting 3 cycles of 100-1000 ms pulses...");
|
||||
|
||||
rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds
|
||||
|
||||
Serial.print("Done!\n");
|
||||
|
||||
Serial.print("\nEnd Example");
|
||||
|
||||
} // end of setup()
|
||||
|
||||
void loop(){
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,121 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to use SpanPoint() to implement //
|
||||
// two remote temperature sensors on separate ESP32 //
|
||||
// devices. //
|
||||
// //
|
||||
// This sketch is for the MAIN DEVICE that contains //
|
||||
// all the usual HomeSpan logic, plus two instances //
|
||||
// of SpanPoint to read temperatures from two other //
|
||||
// remote devices. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
struct RemoteTempSensor : Service::TemperatureSensor {
|
||||
|
||||
SpanCharacteristic *temp;
|
||||
SpanCharacteristic *fault;
|
||||
SpanPoint *remoteTemp;
|
||||
const char *name;
|
||||
float temperature;
|
||||
|
||||
RemoteTempSensor(const char *name, const char*macAddress) : Service::TemperatureSensor(){
|
||||
|
||||
this->name=name;
|
||||
|
||||
temp=new Characteristic::CurrentTemperature(-10.0); // set initial temperature
|
||||
temp->setRange(-50,100); // expand temperature range to allow negative values
|
||||
|
||||
fault=new Characteristic::StatusFault(1); // set initial state = fault
|
||||
|
||||
remoteTemp=new SpanPoint(macAddress,0,sizeof(float)); // create a SpanPoint with send size=0 and receive size=sizeof(float)
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(remoteTemp->get(&temperature)){ // if there is data from the remote sensor
|
||||
temp->setVal(temperature); // update temperature
|
||||
fault->setVal(0); // clear fault
|
||||
|
||||
LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32);
|
||||
|
||||
} else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault
|
||||
fault->setVal(1); // set fault state
|
||||
LOG1("Sensor %s update: FAULT\n",name);
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"Sensor Hub");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Indoor Temp");
|
||||
new RemoteTempSensor("Device 1","AC:67:B2:77:42:20"); // pass MAC Address of Remote Device
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Outdoor Temp");
|
||||
new RemoteTempSensor("Device 2","84:CC:A8:11:B4:84"); // pass MAC Address of Remote Device
|
||||
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Demonstrates how to use SpanPoint() to implement //
|
||||
// two remote temperature sensors on separate ESP32 //
|
||||
// devices. //
|
||||
// //
|
||||
// This sketch is for the REMOTE DEVICES. They are //
|
||||
// very simple and don't need any of the normal //
|
||||
// HomeSpan logic (except for SpanPoint). //
|
||||
// //
|
||||
// Note this sketch only SIMULATES a temperature //
|
||||
// sensor by slowly setting the temperature from //
|
||||
// -30.0 to 35.0 C in steps of 0.5 C. The sketch //
|
||||
// does not contain logic for an actual physical //
|
||||
// temperature sensor. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
float temperature=-10.0;
|
||||
SpanPoint *mainDevice;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.printf("\n\nThis is a REMOTE Device with MAC Address = %s\n",WiFi.macAddress().c_str());
|
||||
Serial.printf("NOTE: This MAC Address must be entered into the corresponding SpanPoint() call of the MAIN Device.\n\n");
|
||||
|
||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||
|
||||
mainDevice=new SpanPoint("84:CC:A8:11:B4:84",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
boolean success = mainDevice->send(&temperature); // this will show as success as long as the MAIN DEVICE is running
|
||||
Serial.printf("Send %s\n",success?"Succeded":"Failed");
|
||||
temperature+=0.5;
|
||||
if(temperature>35.0)
|
||||
temperature=-30.0;
|
||||
|
||||
delay(20000);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP8266
|
||||
#error ERROR: THIS SKETCH IS DESIGNED FOR ESP8266 MICROCONTROLLERS!
|
||||
#endif
|
||||
|
||||
// *** THIS SKETCH IS FOR AN ESP8266, NOT AN ESP32 *** //
|
||||
|
||||
// This sketch is similar to HomeSpan's RemoteDevice.ino example (designed for an ESP32 running HomeSpan) in which we simulate
|
||||
// a Remote Temperature Sensor using HomeSpan's SpanPoint class. However, since neither HomeSpan nor SpanPoint is designed to
|
||||
// run on an ESP8266, we will implement the BASIC communication functionality of SpanPoint by directly calling the equivalent
|
||||
// ESP-NOW commands that are supported by the ESP8266. This sketch does NOT seek to replicate all of SpanPoint's features, and
|
||||
// does not include automatic channel calibration or queue management.
|
||||
|
||||
// Start by including the following ESP8266 libraries
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <espnow.h>
|
||||
#include <Crypto.h> // this library is needed to implement the hash-code process SpanPoint uses to generate ESP-NOW encryption keys
|
||||
|
||||
float temp=-10.0; // this global variable represents our "simulated" temperature (in degrees C)
|
||||
|
||||
// Below we encode the MAC Address of the Main ESP32 Device running HomeSpan to which this ESP8266 device will connect
|
||||
|
||||
// IMPORTANT: ESP32 devices have TWO MAC Addresses. One is used when the ESP32 is operating in Station (STA) mode. It is the address returned
|
||||
// by the WiFi.macAddress() function. The other is used when the ESP32 is operating in Access Point (AP) mode. This address is returned by the
|
||||
// WiFi.softAPmacAddress() function. HomeSpan normally operates the ESP32 with both modes (STA+AP), so both MAC Addresses are active.
|
||||
|
||||
// On ESP32 devices, ESP-NOW seems to work fine when each device sends data to other devices via their STA MAC Address. The same is true for ESP8266
|
||||
// devices sending data to an ESP32 device via ESP-NOW with one critical exception: Once the ESP32 connects (via STA mode) to a WiFi network, which it must
|
||||
// do to run HomeSpan, for some reason ESP8266 devices can no longer send data via ESP-NOW to the ESP32 using its STA MAC Address.
|
||||
|
||||
// The solution is to instead have the ESP8266 send data via ESP-NOW to the ESP32's AP MAC Address. This seems to work regardless of whether or not
|
||||
// the ESP32 is connected to a central WiFi newtork. To support such use on the ESP32, the SpanPoint constructor includes a fifth, optional parameter
|
||||
// called "useAPaddress". When creating SpanPoint links of the ESP32 using HomeSpan, set useAPaddress to TRUE if the Remote Device SpanPoint is connecting
|
||||
// to is an ESP8266. Set "useAPaddress" to FALSE (or leave unspecified, since FALSE is the default) if the Remote Device is an ESP32.
|
||||
|
||||
// When HomeSpan first starts (and whenever you type 'i' into the CLI), the Serial Monitor will display the details of each SpanPoint object you instantiated
|
||||
// in your ESP32 sketch. This output includes the MAC Address at which SpanPoint will be listening for incoming data from Remote Devices. The MAC Address
|
||||
// shown for the instance of SpanPoint corresponding to this Remote Deivce (i.e. this sketch) is the MAC Address you should use below.
|
||||
|
||||
uint8_t main_mac[6]={0x84,0xCC,0xA8,0x11,0xB4,0x85}; // this is the **AP MAC Address** of the Main Device running HomeSpan on an ESP32 as reported in the HomeSpan Serial Monitor
|
||||
|
||||
// Next we create a simple, standard ESP-NOW callback function to report on the status of each data transmission
|
||||
|
||||
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
|
||||
Serial.printf("Last Packet Send Status: %s\n",sendStatus==0?"Success":"Fail");
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.printf("\nMAC Address: %s\n",WiFi.macAddress().c_str()); // enter this MAC address as the first argument of the matching SpanPoint object on the ESP32 running HomeSpan
|
||||
|
||||
WiFi.mode(WIFI_STA); // set the mode to Station
|
||||
wifi_set_channel(6); // you also need to manually set the channel to match whatever channel is used by the ESP32 after it connects to your WiFi network
|
||||
|
||||
// Hint: As an alterntive, you can add code to this sketch to connect to the same WiFi network that HomeSpan uses. Though this sketch won't make any use of that WiFi network,
|
||||
// by establishing the connection the ESP8266 automatically configures the channel, which will now match the ESP32.
|
||||
|
||||
// Next, initialize ESP-NOW
|
||||
|
||||
if (esp_now_init() != 0) {
|
||||
Serial.println("Error initializing ESP-NOW");
|
||||
return;
|
||||
}
|
||||
|
||||
// SpanPoint uses ESP-NOW encryption for all communication. This encrpytion is based on two 16-byte keys: a local master key (LMK) and a primary master key (PMK). To generate
|
||||
// these keys, SpanPoint takes a text-based password (the default is the word "HomeSpan"), creates a 32 byte (256 bit) hash of the text (using the SHA256 method), and uses
|
||||
// the first 16 bytes as the LMK and the last 16 bytes as the PMK. This is easily replicated as follows:
|
||||
|
||||
uint8_t hash[32]; // create space to store as 32-byte hash code
|
||||
char password[]="HomeSpan"; // specify the password
|
||||
|
||||
experimental::crypto::SHA256::hash(password,strlen(password),hash); // create the hash code to be used further below
|
||||
|
||||
esp_now_register_send_cb(OnDataSent); // register the callback function we defined above
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); // set the role of this device to be a controller (i.e. it sends data to the ESP32)
|
||||
|
||||
esp_now_set_kok(hash+16,16); // next we set the PMK. For some reason this is called KOK on the ESP8266. Note you must set the PMK BEFORE adding any peers
|
||||
|
||||
esp_now_add_peer(main_mac, ESP_NOW_ROLE_COMBO, 0, hash, 16); // now we add in the peer, set its role, and specify the LMK
|
||||
|
||||
// Hint: The third argument above is the WiFi Channel. However, this is only a reference number stored by ESP-NOW. ESP-NOW does NOT actually set the channel for you.
|
||||
// We already set the WiFi channel above. To make things easier, ESP-NOW allows you to set the channel as zero, which means ESP-NOW should expect the channel to be whatever was
|
||||
// already set for the WiFi controller. Recommend always setting this to zero to avoid having any mismatches if you instead specified a real channel.
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void loop() {
|
||||
|
||||
Serial.printf("Sending Temperature: %f\n",temp);
|
||||
esp_now_send(main_mac, (uint8_t *)&temp, sizeof(temp)); // Send the Data to the Main Device!
|
||||
|
||||
temp+=0.5; // increment the "temperature" by 0.5 C
|
||||
if(temp>35.0)
|
||||
temp=-10.0;
|
||||
|
||||
delay(5000); // wait 5 seconds before sending another update
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// This sketch is for a Remote Temperature Sensor to //
|
||||
// be used in conjunction with the "MainDevice.ino" //
|
||||
// sketch running on a separate ESP32 //
|
||||
// //
|
||||
// The purpose of these sketches is to demonstrate //
|
||||
// how to use SpanPoint() to communication between //
|
||||
// a remote ESP32 device that takes measurements from //
|
||||
// a sensor, and a separate "main" ESP32 device that //
|
||||
// is running the full HomeSpan code, and thus //
|
||||
// connects to HomeKit. //
|
||||
// //
|
||||
// This sketch implements an Adafruit ADT7410 I2C //
|
||||
// Temperature Sensor. If you don't have such a //
|
||||
// device, please use the sketch "RemoteDevice.ino" //
|
||||
// instead. That sketch SIMULATES a temperature //
|
||||
// sensor and therefore allows you to learn how //
|
||||
// SpanPoint() works even though the temperature data //
|
||||
// itself isn't real. //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include <Wire.h> // include the I2C library
|
||||
|
||||
#define DIAGNOSTIC_MODE
|
||||
|
||||
#define SAMPLE_TIME 30000 // Time between temperature samples (in milliseconds)
|
||||
#define I2C_ADD 0x48 // I2C Address to use for the Adafruit ADT7410
|
||||
|
||||
SpanPoint *mainDevice;
|
||||
|
||||
void setup() {
|
||||
|
||||
setCpuFrequencyMhz(80); // reduce CPU frequency to save battery power
|
||||
|
||||
#if defined(DIAGNOSTIC_MODE)
|
||||
homeSpan.setLogLevel(1);
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.printf("Starting Remote Temperature Sensor. MAC Address of this device = %s\n",WiFi.macAddress().c_str());
|
||||
#endif
|
||||
|
||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||
|
||||
mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
|
||||
Wire.begin(); // start I2C in Controller Mode
|
||||
|
||||
#if defined(DIAGNOSTIC_MODE)
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x0B); // ADT7410 Identification Register
|
||||
Wire.endTransmission(0); // transmit and leave in restart mode to allow reading
|
||||
Wire.requestFrom(I2C_ADD,1); // request read of single byte
|
||||
uint8_t id = Wire.read(); // receive a byte
|
||||
LOG1("Configuring Temperature Sensor ADT7410 version 0x%02X with address 0x%02X.\n",id,I2C_ADD); // initialization message
|
||||
#endif
|
||||
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x03); // ADT740 Configuration Register
|
||||
Wire.write(0xC0); // set 16-bit temperature resolution, 1 sample per second
|
||||
Wire.endTransmission(); // transmit
|
||||
|
||||
Wire.beginTransmission(I2C_ADD); // setup transmission
|
||||
Wire.write(0x00); // ADT7410 2-byte Temperature
|
||||
Wire.endTransmission(0); // transmit and leave in restart mode to allow reading
|
||||
Wire.requestFrom(I2C_ADD,2); // request read of two bytes
|
||||
|
||||
int16_t iTemp = ((int16_t)Wire.read()<<8)+Wire.read();
|
||||
float temperature = iTemp/128.0;
|
||||
|
||||
boolean success = mainDevice->send(&temperature); // send temperature to main device
|
||||
|
||||
LOG1("Send temp update of %0.2f F: %s\n",temperature*9/5+32,success?"Succeded":"Failed");
|
||||
|
||||
esp_deep_sleep(SAMPLE_TIME*1000); // enter deep sleep mode -- reboot after resuming
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Shade with Hold Feature
|
||||
|
||||
SpanCharacteristic *current; // reference to a "generic" Current Position Characteristic (used by a variety of different Service)
|
||||
SpanCharacteristic *target; // reference to a "generic" Target Position Characteristic (used by a variety of different Service)
|
||||
SpanCharacteristic *hTiltCurrent; // reference to horizontal tilt of window shade - current position
|
||||
SpanCharacteristic *hTiltTarget; // reference to horizontal tilt of window shade - target position
|
||||
|
||||
ServoPin *hTiltServo; // reference to Servo Pin to control Horiontal Tilt
|
||||
|
||||
DEV_WindowShade(uint8_t hTiltServoPin) : Service::WindowCovering(){ // constructor() method
|
||||
|
||||
current=new Characteristic::CurrentPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised)
|
||||
target=new Characteristic::TargetPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised)
|
||||
target->setRange(0,100,10); // set the allowable target-position range to 0-100 IN STEPS of 10
|
||||
|
||||
hTiltCurrent=new Characteristic::CurrentHorizontalTiltAngle(); // Tilt Angle is measured in degrees; HAP default is -90 to +90
|
||||
hTiltTarget=new Characteristic::TargetHorizontalTiltAngle();
|
||||
|
||||
// Here we define our Servo using HomeSpan's ServoPin Class.
|
||||
// See the HomeSpan API Reference for full details and a list of all parameters.
|
||||
|
||||
hTiltServo=new ServoPin(hTiltServoPin);
|
||||
|
||||
Serial.print("Configuring Motorized Window Shade"); // initialization message
|
||||
Serial.print("\n");
|
||||
|
||||
} // end constructor
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
if(target->updated()){ // check to see if shade target position was updated
|
||||
if(target->getNewVal()>current->getVal()){ // if the target-position requested is greater than the current-position, simply log a "raise" message
|
||||
LOG1("Raising Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows raising is required
|
||||
} else
|
||||
if(target->getNewVal()<current->getVal()){ // if the target-position requested is less than the current-position, simply log a "raise" message
|
||||
LOG1("Lowering Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows lowering is required
|
||||
}
|
||||
}
|
||||
|
||||
if(hTiltTarget->updated()){ // check to see if shade tilt angle was updated
|
||||
hTiltCurrent->setVal(hTiltTarget->getNewVal()); // set current value of tilt to match target value
|
||||
hTiltServo->set(hTiltTarget->getNewVal()); // <--- update actual servo position with ServoPin->set(degrees) method
|
||||
}
|
||||
|
||||
return(true); // return true
|
||||
|
||||
} // update
|
||||
|
||||
void loop(){ // loop() method
|
||||
|
||||
// Here we simulate a window shade that takes 5 seconds to move to its new target position
|
||||
|
||||
if(current->getVal()!=target->getVal() && target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified...
|
||||
current->setVal(target->getVal()); // ...set the current position to equal the target position
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// This example demonstrates how to control a real-world Servo Motor using HomeSpan's
|
||||
// ServoPin Class, as included in "extras/PwmPin.h" The code builds upon the
|
||||
// WindowShade Accessory from Example 13 by adding a Horizontal Tilt Characteristic that
|
||||
// is controlled by a Servo connected to the ESP32.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
new DEV_WindowShade(18); // Create a motorized Window Shade with a Servo attached to Pin 18 that controls the Horizontal Tilt of the Shade
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "HomeSpan.h" // include the HomeSpan library
|
||||
|
||||
struct TableLamp : Service::LightBulb{
|
||||
|
||||
int lampPin; // store the pin number connected to a hypothetical relay that turns the Table Lamp on/off
|
||||
SpanCharacteristic *lampPower; // store a reference to the On Characteristic
|
||||
|
||||
TableLamp(int lampPin) : Service::LightBulb(){ // constructor() method for TableLamp defined with one parameter. Note we also call the constructor() method for the LightBulb Service.
|
||||
|
||||
lampPower=new Characteristic::On(); // instantiate the On Characteristic and save it as lampPower
|
||||
this->lampPin=lampPin; // save the pin number for the hypothetical relay
|
||||
pinMode(lampPin,OUTPUT); // configure the pin as an output using the standard Arduino pinMode function
|
||||
|
||||
} // end constructor()
|
||||
|
||||
boolean update(){ // update() method
|
||||
|
||||
digitalWrite(lampPin,lampPower->getNewVal()); // use standard Arduino digitalWrite function to change the ledPin to either high or low based on the value requested by HomeKit
|
||||
|
||||
return(true); // return true to let HomeKit (and the Home App Client) know the update was successful
|
||||
|
||||
} // end update()
|
||||
|
||||
};
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200); // start the Serial interface
|
||||
|
||||
homeSpan.begin(); // initialize HomeSpan
|
||||
|
||||
new SpanAccessory(); // Table Lamp Accessory
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service
|
||||
new Characteristic::Identify(); // HAP requires the Accessory Information Service to include the Identify Characteristic
|
||||
|
||||
new TableLamp(17); // instantiate the TableLamp Service (defined below) with lampPin set to 17
|
||||
|
||||
} // end of setup()
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,216 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// HomeSpan Television Service Example
|
||||
|
||||
// Covers all Characteristics of the Television Service that appear to
|
||||
// be supported in the iOS 15 version of the Home App. Note these Services
|
||||
// are not documented by Apple and are not officially part HAP-R2.
|
||||
//
|
||||
// For Service::Television():
|
||||
//
|
||||
// * Characteristic::Active()
|
||||
// * Characteristic::ConfiguredName()
|
||||
// * Characteristic::ActiveIdentifier()
|
||||
// * Characteristic::RemoteKey()
|
||||
// * Characteristic::PowerModeSelection()
|
||||
//
|
||||
// For Service::InputSource():
|
||||
//
|
||||
// * Characteristic::ConfiguredName()
|
||||
// * Characteristic::ConfiguredNameStatic() // a HomeSpan-specific variation of ConfiguredName()
|
||||
// * Characteristic::Identifier()
|
||||
// * Characteristic::IsConfigured()
|
||||
// * Characteristic::CurrentVisibilityState()
|
||||
// * Characteristic::TargetVisibilityState()
|
||||
|
||||
// NOTE: This example is only designed to demonstrate how Television Services and Characteristics
|
||||
// appear in the Home App, and what they each control. To keep things simple, actions for the
|
||||
// Input Sources have NOT been implemented in the code below. For example, the code below does not include
|
||||
// any logic to update CurrentVisibilityState when the TargetVisibilityState checkboxes are clicked.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
|
||||
struct HomeSpanTV : Service::Television {
|
||||
|
||||
SpanCharacteristic *active = new Characteristic::Active(0); // TV On/Off (set to Off at start-up)
|
||||
SpanCharacteristic *activeID = new Characteristic::ActiveIdentifier(3); // Sets HDMI 3 on start-up
|
||||
SpanCharacteristic *remoteKey = new Characteristic::RemoteKey(); // Used to receive button presses from the Remote Control widget
|
||||
SpanCharacteristic *settingsKey = new Characteristic::PowerModeSelection(); // Adds "View TV Setting" option to Selection Screen
|
||||
|
||||
HomeSpanTV(const char *name) : Service::Television() {
|
||||
new Characteristic::ConfiguredName(name); // Name of TV
|
||||
Serial.printf("Configured TV: %s\n",name);
|
||||
}
|
||||
|
||||
boolean update() override {
|
||||
|
||||
if(active->updated()){
|
||||
Serial.printf("Set TV Power to: %s\n",active->getNewVal()?"ON":"OFF");
|
||||
}
|
||||
|
||||
if(activeID->updated()){
|
||||
Serial.printf("Set Input Source to HDMI-%d\n",activeID->getNewVal());
|
||||
}
|
||||
|
||||
if(settingsKey->updated()){
|
||||
Serial.printf("Received request to \"View TV Settings\"\n");
|
||||
}
|
||||
|
||||
if(remoteKey->updated()){
|
||||
Serial.printf("Remote Control key pressed: ");
|
||||
switch(remoteKey->getNewVal()){
|
||||
case 4:
|
||||
Serial.printf("UP ARROW\n");
|
||||
break;
|
||||
case 5:
|
||||
Serial.printf("DOWN ARROW\n");
|
||||
break;
|
||||
case 6:
|
||||
Serial.printf("LEFT ARROW\n");
|
||||
break;
|
||||
case 7:
|
||||
Serial.printf("RIGHT ARROW\n");
|
||||
break;
|
||||
case 8:
|
||||
Serial.printf("SELECT\n");
|
||||
break;
|
||||
case 9:
|
||||
Serial.printf("BACK\n");
|
||||
break;
|
||||
case 11:
|
||||
Serial.printf("PLAY/PAUSE\n");
|
||||
break;
|
||||
case 15:
|
||||
Serial.printf("INFO\n");
|
||||
break;
|
||||
default:
|
||||
Serial.print("UNKNOWN KEY\n");
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Television,"HomeSpan Television");
|
||||
|
||||
SPAN_ACCESSORY();
|
||||
|
||||
// Below we define 10 different InputSource Services using different combinations
|
||||
// of Characteristics to demonstrate how they interact and appear to the user in the Home App
|
||||
|
||||
SpanService *hdmi1 = new Service::InputSource(); // Source included in Selection List, but excluded from Settings Screen
|
||||
new Characteristic::ConfiguredName("HDMI 1");
|
||||
new Characteristic::Identifier(1);
|
||||
|
||||
SpanService *hdmi2 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 2");
|
||||
new Characteristic::Identifier(2);
|
||||
new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen
|
||||
|
||||
SpanService *hdmi3 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 3");
|
||||
new Characteristic::Identifier(3);
|
||||
new Characteristic::IsConfigured(1); // Source included in both the Selection List and the Settings Screen
|
||||
|
||||
SpanService *hdmi4 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 4");
|
||||
new Characteristic::Identifier(4);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List
|
||||
|
||||
SpanService *hdmi5 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 5");
|
||||
new Characteristic::Identifier(5);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List
|
||||
|
||||
SpanService *hdmi6 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 6");
|
||||
new Characteristic::Identifier(6);
|
||||
new Characteristic::IsConfigured(0); // Source excluded from both the Selection List and the Settings Screen
|
||||
new Characteristic::CurrentVisibilityState(0); // If IsConfigured(0) is specified, CurrentVisibilityState() has no effect
|
||||
|
||||
SpanService *hdmi7 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 7");
|
||||
new Characteristic::Identifier(7);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List...
|
||||
new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *hdmi8 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 8");
|
||||
new Characteristic::Identifier(8);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(1); // ...but excluded from the Selection List...
|
||||
new Characteristic::TargetVisibilityState(1); // ...and an "un-checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *hdmi9 = new Service::InputSource();
|
||||
new Characteristic::ConfiguredName("HDMI 9");
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...but without an Identifier() set, the Source is excluded from the Selection List regardless of CurrentVisibilityState(0)
|
||||
new Characteristic::TargetVisibilityState(0);
|
||||
|
||||
SpanService *hdmi10 = new Service::InputSource();
|
||||
(new Characteristic::ConfiguredName("HDMI 10"))->removePerms(PW); // Source Name permissions changed and now cannot be edited in Settings Screen
|
||||
new Characteristic::Identifier(10);
|
||||
new Characteristic::IsConfigured(1); // Source included in the Settings Screen...
|
||||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List...
|
||||
new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *speaker = new Service::TelevisionSpeaker();
|
||||
new Characteristic::VolumeSelector();
|
||||
new Characteristic::VolumeControlType(3);
|
||||
|
||||
(new HomeSpanTV("Test TV")) // Define a Television Service. Must link in InputSources!
|
||||
->addLink(hdmi1)
|
||||
->addLink(hdmi2)
|
||||
->addLink(hdmi3)
|
||||
->addLink(hdmi4)
|
||||
->addLink(hdmi5)
|
||||
->addLink(hdmi6)
|
||||
->addLink(hdmi7)
|
||||
->addLink(hdmi8)
|
||||
->addLink(hdmi9)
|
||||
->addLink(hdmi10)
|
||||
->addLink(speaker)
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void loop() {
|
||||
homeSpan.poll();
|
||||
}
|
||||
Reference in New Issue
Block a user