ESP + Reorganizacja

This commit is contained in:
Kamil Siejka
2024-10-03 10:05:46 +02:00
parent 61df70df2c
commit d5e3929a12
124 changed files with 18835 additions and 0 deletions

View File

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

View File

@@ -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,
1 # --- Custom Partition Table for HomeSpan ---
2 #
3 # Similar to min_spiffs, except that the 128K SPIFF block at the end
4 # is replaced by a 128K NVS block, and the initial 20K NVS block
5 # is no longer used. Note this table is designed for use with 4MB Flash.
6 # To use with 8MB Flash, increase app0 and app1 by 2048K to become 3968K each.
7 # To use with 16MB Flash, increase app0 and app1 by 6144K to become 8064K each
8 #
9 unused_nvs,data,nvs,,20K,
10 otadata,data,ota,,8K,
11 app0,app,ota_0,,1920K,
12 app1,app,ota_1,,1920K,
13 nvs,data,nvs,,128K,
14 coredump,data,coredump,,64K,

View File

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

View File

@@ -0,0 +1,119 @@
/*********************************************************************************
* MIT License
*
* Copyright (c) 2020-2024 Gregg E. Berman
*
* https://github.com/HomeSpan/HomeSpan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
********************************************************************************/
// 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);
}
}
//////////////////////////////////////

View File

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

View File

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

View 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();
}
///////////////////////////////

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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