ESP + Reorganizacja
This commit is contained in:
192
ESP32/HomeSpan-master/src/Characteristics.h
Normal file
192
ESP32/HomeSpan-master/src/Characteristics.h
Normal file
@@ -0,0 +1,192 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
//////////////////////////////////////////
|
||||
// HAP CHARACTERISTICS (HAP Chapter 9) //
|
||||
//////////////////////////////////////////
|
||||
|
||||
enum PERMS{ // create bitflags based on HAP Table 6-4
|
||||
PR=1,
|
||||
PW=2,
|
||||
EV=4,
|
||||
AA=8,
|
||||
TW=16,
|
||||
HD=32,
|
||||
WR=64,
|
||||
NV=128 // this is a non-HAP flag used to specify that no value should be provided (should be a HAP flag!)
|
||||
};
|
||||
|
||||
enum FORMAT { // HAP Table 6-5
|
||||
BOOL=0,
|
||||
UINT8=1,
|
||||
UINT16=2,
|
||||
UINT32=3,
|
||||
UINT64=4,
|
||||
INT=5,
|
||||
FLOAT=6,
|
||||
STRING=7,
|
||||
DATA=8,
|
||||
TLV_ENC=9
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct HapChar {
|
||||
const char *type;
|
||||
const char *hapName;
|
||||
PERMS perms;
|
||||
FORMAT format;
|
||||
boolean staticRange;
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
#define HAPCHAR(hapName,type,perms,format,staticRange) HapChar hapName {#type,#hapName,(PERMS)(perms),format,staticRange}
|
||||
|
||||
struct HapCharacteristics {
|
||||
|
||||
HAPCHAR( AccessoryFlags, A6, PR+EV, UINT32, true );
|
||||
HAPCHAR( Active, B0, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( ActiveIdentifier, E7, PW+PR+EV, UINT32, true );
|
||||
HAPCHAR( AirQuality, 95, PR+EV, UINT8, true );
|
||||
HAPCHAR( BatteryLevel, 68, PR+EV, UINT8, false );
|
||||
HAPCHAR( Brightness, 8, PR+PW+EV, INT, false );
|
||||
HAPCHAR( CarbonMonoxideLevel, 90, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonMonoxidePeakLevel, 91, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonDioxideDetected, 92, PR+EV, UINT8, true );
|
||||
HAPCHAR( CarbonDioxideLevel, 93, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonDioxidePeakLevel, 94, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CarbonMonoxideDetected, 69, PR+EV, UINT8, true );
|
||||
HAPCHAR( ChargingState, 8F, PR+EV, UINT8, true );
|
||||
HAPCHAR( ClosedCaptions, DD, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( CoolingThresholdTemperature, D, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( ColorTemperature, CE, PR+PW+EV, UINT32, false );
|
||||
HAPCHAR( ConfiguredName, E3, PW+PR+EV, STRING, false );
|
||||
HAPCHAR( ContactSensorState, 6A, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentAmbientLightLevel, 6B, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentHorizontalTiltAngle, 6C, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentAirPurifierState, A9, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentSlatState, AA, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentPosition, 6D, PR+EV, UINT8, false );
|
||||
HAPCHAR( CurrentVerticalTiltAngle, 6E, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentHumidifierDehumidifierState, B3, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentDoorState, E, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentFanState, AF, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentHeatingCoolingState, F, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentHeaterCoolerState, B1, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentMediaState, E0, PR+EV, UINT8, true );
|
||||
HAPCHAR( CurrentRelativeHumidity, 10, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentTemperature, 11, PR+EV, FLOAT, false );
|
||||
HAPCHAR( CurrentTiltAngle, C1, PR+EV, INT, false );
|
||||
HAPCHAR( CurrentVisibilityState, 135, PR+EV, UINT8, true );
|
||||
HAPCHAR( DisplayOrder, 136, PR+EV, TLV_ENC, true );
|
||||
HAPCHAR( FilterLifeLevel, AB, PR+EV, FLOAT, false );
|
||||
HAPCHAR( FilterChangeIndication, AC, PR+EV, UINT8, true );
|
||||
HAPCHAR( FirmwareRevision, 52, PR+EV, STRING, true );
|
||||
HAPCHAR( HardwareRevision, 53, PR, STRING, true );
|
||||
HAPCHAR( HeatingThresholdTemperature, 12, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( HoldPosition, 6F, PW, BOOL, true );
|
||||
HAPCHAR( Hue, 13, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( Identify, 14, PW, BOOL, true );
|
||||
HAPCHAR( Identifier, E6, PR, UINT32, true );
|
||||
HAPCHAR( InputDeviceType, DC, PR+EV, UINT8, true );
|
||||
HAPCHAR( InputSourceType, DB, PR+EV, UINT8, true );
|
||||
HAPCHAR( InUse, D2, PR+EV, UINT8, true );
|
||||
HAPCHAR( IsConfigured, D6, PR+EV, UINT8, true );
|
||||
HAPCHAR( LeakDetected, 70, PR+EV, UINT8, true );
|
||||
HAPCHAR( LockCurrentState, 1D, PR+EV, UINT8, true );
|
||||
HAPCHAR( LockPhysicalControls, A7, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( LockTargetState, 1E, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( Manufacturer, 20, PR, STRING, true );
|
||||
HAPCHAR( Model, 21, PR, STRING, true );
|
||||
HAPCHAR( MotionDetected, 22, PR+EV, BOOL, true );
|
||||
HAPCHAR( Mute, 11A, PW+PR+EV, BOOL, true );
|
||||
HAPCHAR( Name, 23, PR, STRING, true );
|
||||
HAPCHAR( NitrogenDioxideDensity, C4, PR+EV, FLOAT, false );
|
||||
HAPCHAR( ObstructionDetected, 24, PR+EV, BOOL, true );
|
||||
HAPCHAR( PM25Density, C6, PR+EV, FLOAT, false );
|
||||
HAPCHAR( OccupancyDetected, 71, PR+EV, UINT8, true );
|
||||
HAPCHAR( OutletInUse, 26, PR+EV, BOOL, true );
|
||||
HAPCHAR( On, 25, PR+PW+EV, BOOL, true );
|
||||
HAPCHAR( OzoneDensity, C3, PR+EV, FLOAT, false );
|
||||
HAPCHAR( PM10Density, C7, PR+EV, FLOAT, false );
|
||||
HAPCHAR( PictureMode, E2, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( PositionState, 72, PR+EV, UINT8, true );
|
||||
HAPCHAR( PowerModeSelection, DF, PW, UINT8, true );
|
||||
HAPCHAR( ProgramMode, D1, PR+EV, UINT8, true );
|
||||
HAPCHAR( ProgrammableSwitchEvent, 73, PR+EV+NV, UINT8, true );
|
||||
HAPCHAR( RelativeHumidityDehumidifierThreshold, C9, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( RelativeHumidityHumidifierThreshold, CA, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( RemainingDuration, D4, PR+EV, UINT32, false );
|
||||
HAPCHAR( RemoteKey, E1, PW, UINT8, true );
|
||||
HAPCHAR( ResetFilterIndication, AD, PW, UINT8, true );
|
||||
HAPCHAR( RotationDirection, 28, PR+PW+EV, INT, true );
|
||||
HAPCHAR( RotationSpeed, 29, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( Saturation, 2F, PR+PW+EV, FLOAT, false );
|
||||
HAPCHAR( SecuritySystemAlarmType, 8E, PR+EV, UINT8, true );
|
||||
HAPCHAR( SecuritySystemCurrentState, 66, PR+EV, UINT8, true );
|
||||
HAPCHAR( SecuritySystemTargetState, 67, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( SerialNumber, 30, PR, STRING, true );
|
||||
HAPCHAR( ServiceLabelIndex, CB, PR, UINT8, true );
|
||||
HAPCHAR( ServiceLabelNamespace, CD, PR, UINT8, true );
|
||||
HAPCHAR( SlatType, C0, PR, UINT8, true );
|
||||
HAPCHAR( SleepDiscoveryMode, E8, PR+EV, UINT8, true );
|
||||
HAPCHAR( SmokeDetected, 76, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusActive, 75, PR+EV, BOOL, true );
|
||||
HAPCHAR( StatusFault, 77, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusJammed, 78, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusLowBattery, 79, PR+EV, UINT8, true );
|
||||
HAPCHAR( StatusTampered, 7A, PR+EV, UINT8, true );
|
||||
HAPCHAR( SulphurDioxideDensity, C5, PR+EV, FLOAT, false );
|
||||
HAPCHAR( SwingMode, B6, PR+EV+PW, UINT8, true );
|
||||
HAPCHAR( TargetAirPurifierState, A8, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetFanState, BF, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetTiltAngle, C2, PW+PR+EV, INT, false );
|
||||
HAPCHAR( TargetHeaterCoolerState, B2, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( SetDuration, D3, PW+PR+EV, UINT32, false );
|
||||
HAPCHAR( TargetHorizontalTiltAngle, 7B, PW+PR+EV, INT, false );
|
||||
HAPCHAR( TargetHumidifierDehumidifierState, B4, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetPosition, 7C, PW+PR+EV, UINT8, false );
|
||||
HAPCHAR( TargetDoorState, 32, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetHeatingCoolingState, 33, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetMediaState, 137, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetRelativeHumidity, 34, PW+PR+EV, FLOAT, false );
|
||||
HAPCHAR( TargetTemperature, 35, PW+PR+EV, FLOAT, false );
|
||||
HAPCHAR( TargetVisibilityState, 134, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TemperatureDisplayUnits, 36, PW+PR+EV, UINT8, true );
|
||||
HAPCHAR( TargetVerticalTiltAngle, 7D, PW+PR+EV, INT, false );
|
||||
HAPCHAR( ValveType, D5, PR+EV, UINT8, true );
|
||||
HAPCHAR( Version, 37, PR, STRING, true );
|
||||
HAPCHAR( VOCDensity, C8, PR+EV, FLOAT, false );
|
||||
HAPCHAR( Volume, 119, PW+PR+EV, UINT8, false );
|
||||
HAPCHAR( VolumeControlType, E9, PR+EV, UINT8, true );
|
||||
HAPCHAR( VolumeSelector, EA, PW, UINT8, true );
|
||||
HAPCHAR( WaterLevel, B5, PR+EV, FLOAT, false );
|
||||
|
||||
};
|
||||
|
||||
extern HapCharacteristics hapChars;
|
||||
72
ESP32/HomeSpan-master/src/FeatherPins.h
Normal file
72
ESP32/HomeSpan-master/src/FeatherPins.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// For developer use and testing only - provides a common set of pin numbers mapped to the Adafruit Feather Board.
|
||||
// Facilitates the testing of identical code on an ESP32, ESP32-S2, and ESP32-C3 using a common jig without rewiring
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(ARDUINO_FEATHER_ESP32)
|
||||
enum {
|
||||
F13=13,F12=12,F27=27,F15=15,F32=32,F14=14,F16=16,F17=17,F21=21, // Digital Only (9 pins)
|
||||
F26=26,F25=25,F34=34,F39=39,F36=36,F4=4, // A0-A5
|
||||
F22=22,F23=23, // I2C SCL/SDA
|
||||
F5=5,F18=18,F19=19,F33=33 // SPI SCK/SDO/SDI/CS
|
||||
};
|
||||
#define DEVICE_SUFFIX ""
|
||||
|
||||
#elif defined(ARDUINO_ESP32S2_DEV)
|
||||
enum {
|
||||
F13=1,F12=3,F27=7,F15=10,F32=42,F14=11,F16=20,F17=21,F21=16, // Digital Only (9 pins)
|
||||
F26=17,F25=14,F34=13,F39=12,F36=18,F4=19, // A0-A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=36,F18=35,F19=37,F33=34, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=18 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-S2"
|
||||
|
||||
#elif defined(ARDUINO_ESP32C3_DEV)
|
||||
enum {
|
||||
F27=19,F32=2,F14=10,F16=20,F17=21,F21=18, // Digital Only (6 pins)
|
||||
F26=0,F25=1,F4=3, // A0/A1/A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=4,F18=6,F19=5,F33=7, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=8 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-C3"
|
||||
|
||||
#elif defined(ARDUINO_ESP32S3_DEV)
|
||||
enum {
|
||||
F13=5,F12=6,F27=7,F15=16,F32=17,F14=18,F16=37,F17=36,F21=38, // Digital Only (9 pins)
|
||||
F26=1,F25=2,F34=20,F39=19,F36=15,F4=4, // A0-A5
|
||||
F22=9,F23=8, // I2C SCL/SDA
|
||||
F5=12,F18=11,F19=13,F33=10, // SPI SCK/SDO/SDI/CS
|
||||
BUILTIN_PIXEL=48 // Built-in Neo-Pixel
|
||||
};
|
||||
#define DEVICE_SUFFIX "-S3"
|
||||
|
||||
#endif
|
||||
1696
ESP32/HomeSpan-master/src/HAP.cpp
Normal file
1696
ESP32/HomeSpan-master/src/HAP.cpp
Normal file
File diff suppressed because it is too large
Load Diff
239
ESP32/HomeSpan-master/src/HAP.h
Normal file
239
ESP32/HomeSpan-master/src/HAP.h
Normal file
@@ -0,0 +1,239 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "HAPConstants.h"
|
||||
#include "HKDF.h"
|
||||
#include "SRP.h"
|
||||
|
||||
const TLV8_names HAP_Names[] = {
|
||||
{kTLVType_Separator,"SEPARATOR"},
|
||||
{kTLVType_State,"STATE"},
|
||||
{kTLVType_PublicKey,"PUBKEY"},
|
||||
{kTLVType_Method,"METHOD"},
|
||||
{kTLVType_Salt,"SALT"},
|
||||
{kTLVType_Error,"ERROR"},
|
||||
{kTLVType_Proof,"PROOF"},
|
||||
{kTLVType_EncryptedData,"ENC.DATA"},
|
||||
{kTLVType_Signature,"SIGNATURE"},
|
||||
{kTLVType_Identifier,"IDENTIFIER"},
|
||||
{kTLVType_Permissions,"PERMISSION"}
|
||||
};
|
||||
|
||||
#define hap_controller_IDBYTES 36
|
||||
#define hap_accessory_IDBYTES 17
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// NONCE Structure (HAP used last 64 of 96 bits)
|
||||
|
||||
struct Nonce {
|
||||
uint8_t x[12];
|
||||
Nonce();
|
||||
void zero();
|
||||
uint8_t *get();
|
||||
void inc();
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Accessory Structure for Permanently-Stored Data
|
||||
|
||||
struct Accessory {
|
||||
uint8_t ID[hap_accessory_IDBYTES]; // Pairing ID in form "XX:XX:XX:XX:XX:XX" (no null terminator)
|
||||
uint8_t LTSK[crypto_sign_SECRETKEYBYTES]; // Long Term Ed2519 Secret Key
|
||||
uint8_t LTPK[crypto_sign_PUBLICKEYBYTES]; // Long Term Ed2519 Public Key
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// Paired Controller Structure for Permanently-Stored Data
|
||||
|
||||
class Controller {
|
||||
friend class HAPClient;
|
||||
|
||||
boolean allocated=false; // DEPRECATED (but needed for backwards compatability with original NVS storage of Controller info)
|
||||
boolean admin; // Controller has admin privileges
|
||||
uint8_t ID[36]; // Pairing ID
|
||||
uint8_t LTPK[32]; // Long Term Ed2519 Public Key
|
||||
|
||||
public:
|
||||
|
||||
Controller(uint8_t *id, uint8_t *ltpk, boolean ad){
|
||||
allocated=true;
|
||||
admin=ad;
|
||||
memcpy(ID,id,36);
|
||||
memcpy(LTPK,ltpk,32);
|
||||
}
|
||||
|
||||
Controller(){}
|
||||
|
||||
const uint8_t *getID() const {return(ID);}
|
||||
const uint8_t *getLTPK() const {return(LTPK);}
|
||||
boolean isAdmin() const {return(admin);}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HAPClient Structure
|
||||
// Reads and Writes from each HAP Client connection
|
||||
|
||||
struct HAPClient {
|
||||
|
||||
// common structures and data shared across all HAP Clients
|
||||
|
||||
static const int MAX_HTTP=8096; // max number of bytes allowed for HTTP message
|
||||
static const int MAX_CONTROLLERS=16; // maximum number of paired controllers (HAP requires at least 16)
|
||||
static const int MAX_ACCESSORIES=150; // maximum number of allowed Accessories (HAP limit=150)
|
||||
|
||||
static pairState pairStatus; // tracks pair-setup status
|
||||
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys - permanently stored
|
||||
static list<Controller, Mallocator<Controller>> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
|
||||
|
||||
// individual structures and data defined for each Hap Client connection
|
||||
|
||||
WiFiClient client; // handle to client
|
||||
int clientNumber; // client number
|
||||
Controller *cPair=NULL; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
||||
|
||||
// These temporary Curve25519 keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
||||
|
||||
struct tempKeys_t {
|
||||
uint8_t publicCurveKey[crypto_box_PUBLICKEYBYTES]; // Accessory's Curve25519 Public Key
|
||||
uint8_t sharedCurveKey[crypto_box_PUBLICKEYBYTES]; // Shared-Secret Curve25519 Key derived from Accessory's Secret Key and Controller's Public Key
|
||||
uint8_t sessionKey[crypto_box_PUBLICKEYBYTES]; // Session Key Curve25519 (derived with various HKDF calls)
|
||||
uint8_t iosCurveKey[crypto_box_PUBLICKEYBYTES]; // Controller's Curve25519 Public Key
|
||||
} temp;
|
||||
|
||||
// CurveKey and CurveKey Nonces are created once each new session is verified in /pair-verify. Keys persist for as long as connection is open
|
||||
|
||||
uint8_t a2cKey[32]; // AccessoryToControllerKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2)
|
||||
uint8_t c2aKey[32]; // ControllerToAccessoryKey derived from HKDF-SHA-512 of sharedCurveKey (HAP Section 6.5.2)
|
||||
Nonce a2cNonce; // encryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED)
|
||||
Nonce c2aNonce; // decryption nonce (starts at zero at end of each Pair-Verify and increment every encryption - NOT DOCUMENTED)
|
||||
|
||||
// define member methods
|
||||
|
||||
void processRequest(); // process HAP request
|
||||
int postPairSetupURL(uint8_t *content, size_t len); // POST /pair-setup (HAP Section 5.6)
|
||||
int postPairVerifyURL(uint8_t *content, size_t len); // POST /pair-verify (HAP Section 5.7)
|
||||
int postPairingsURL(uint8_t *content, size_t len); // POST /pairings (HAP Sections 5.10-5.12)
|
||||
int getAccessoriesURL(); // GET /accessories (HAP Section 6.6)
|
||||
int getCharacteristicsURL(char *urlBuf); // GET /characteristics (HAP Section 6.7.4)
|
||||
int putCharacteristicsURL(char *json); // PUT /characteristics (HAP Section 6.7.2)
|
||||
int putPrepareURL(char *json); // PUT /prepare (HAP Section 6.7.2.4)
|
||||
|
||||
void tlvRespond(TLV8 &tlv8); // respond to client with HTTP OK header and all defined TLV data records
|
||||
int receiveEncrypted(uint8_t *httpBuf, int messageSize); // decrypt HTTP request (HAP Section 6.5)
|
||||
|
||||
int notFoundError(); // return 404 error
|
||||
int badRequestError(); // return 400 error
|
||||
int unauthorizedError(); // return 470 error
|
||||
|
||||
// define static methods
|
||||
|
||||
static void init(); // initialize HAP after start-up
|
||||
|
||||
static void hexPrintColumn(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, one byte per row, subject to specified minimum log level
|
||||
static void hexPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as HEX, all on one row, subject to specified minimum log level
|
||||
static void charPrintRow(const uint8_t *buf, int n, int minLogLevel=0); // prints 'n' bytes of *buf as CHAR, all on one row, subject to specified minimum log level
|
||||
|
||||
static Controller *findController(uint8_t *id); // returns pointer to controller with matching ID (or NULL if no match)
|
||||
static tagError addController(uint8_t *id, uint8_t *ltpk, boolean admin); // stores data for new Controller with specified data. Returns tagError (if any)
|
||||
static void removeController(uint8_t *id); // removes specific Controller. If no remaining admin Controllers, remove all others (if any) as per HAP requirements.
|
||||
static void printControllers(int minLogLevel=0); // prints IDs of all allocated (paired) Controller, subject to specified minimum log level
|
||||
static void saveControllers(); // saves Controller list in NVS
|
||||
static int nAdminControllers(); // returns number of admin Controller
|
||||
static void tearDown(uint8_t *id); // tears down connections using Controller with ID=id; tears down all connections if id=NULL
|
||||
static void checkNotifications(); // checks for Event Notifications and reports to controllers as needed (HAP Section 6.8)
|
||||
static void checkTimedWrites(); // checks for expired Timed Write PIDs, and clears any found (HAP Section 6.7.2.4)
|
||||
static void eventNotify(SpanBuf *pObj, int nObj, HAPClient *ignore=NULL); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client
|
||||
|
||||
static void getStatusURL(HAPClient *, void (*)(const char *, void *), void *); // GET / status (an optional, non-HAP feature)
|
||||
|
||||
class HAPTLV : public TLV8 { // dedicated class for HAP TLV8 records
|
||||
public:
|
||||
HAPTLV() : TLV8(HAP_Names,11){}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HapOut Structure
|
||||
|
||||
class HapOut : public std::ostream {
|
||||
|
||||
private:
|
||||
|
||||
struct HapStreamBuffer : public std::streambuf {
|
||||
|
||||
const size_t bufSize=1024; // max allowed for HAP encrypted records
|
||||
char *buffer;
|
||||
uint8_t *encBuf;
|
||||
HAPClient *hapClient=NULL;
|
||||
int logLevel=255; // default is NOT to print anything
|
||||
boolean enablePrettyPrint=false;
|
||||
size_t byteCount=0;
|
||||
size_t indent=0;
|
||||
uint8_t *hash;
|
||||
mbedtls_sha512_context *ctx;
|
||||
void (*callBack)(const char *, void *)=NULL;
|
||||
void *callBackUserData = NULL;
|
||||
|
||||
void flushBuffer();
|
||||
int_type overflow(int_type c) override;
|
||||
int sync() override;
|
||||
size_t getSize(){return(byteCount+pptr()-pbase());}
|
||||
void printFormatted(char *buf, size_t nChars, size_t nsp);
|
||||
|
||||
HapStreamBuffer();
|
||||
~HapStreamBuffer();
|
||||
|
||||
};
|
||||
|
||||
HapStreamBuffer hapBuffer;
|
||||
|
||||
public:
|
||||
|
||||
HapOut() : std::ostream(&hapBuffer){}
|
||||
|
||||
HapOut& setHapClient(HAPClient *hapClient){hapBuffer.hapClient=hapClient;return(*this);}
|
||||
HapOut& setLogLevel(int logLevel){hapBuffer.logLevel=logLevel;return(*this);}
|
||||
HapOut& prettyPrint(){hapBuffer.enablePrettyPrint=true;hapBuffer.logLevel=0;return(*this);}
|
||||
HapOut& setCallback(void(*f)(const char *, void *)){hapBuffer.callBack=f;return(*this);}
|
||||
HapOut& setCallbackUserData(void *userData){hapBuffer.callBackUserData=userData;return(*this);}
|
||||
|
||||
uint8_t *getHash(){return(hapBuffer.hash);}
|
||||
size_t getSize(){return(hapBuffer.getSize());}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Extern Variables
|
||||
|
||||
extern HapOut hapOut;
|
||||
89
ESP32/HomeSpan-master/src/HAPConstants.h
Normal file
89
ESP32/HomeSpan-master/src/HAPConstants.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// HAP TLV Types (HAP Table 5-6)
|
||||
|
||||
typedef enum {
|
||||
kTLVType_Method=0x00,
|
||||
kTLVType_Identifier=0x01,
|
||||
kTLVType_Salt=0x02,
|
||||
kTLVType_PublicKey=0x03,
|
||||
kTLVType_Proof=0x04,
|
||||
kTLVType_EncryptedData=0x05,
|
||||
kTLVType_State=0x06,
|
||||
kTLVType_Error=0x07,
|
||||
kTLVType_RetryDelay=0x08,
|
||||
kTLVType_Certificate=0x09,
|
||||
kTLVType_Signature=0x0A,
|
||||
kTLVType_Permissions=0x0B,
|
||||
kTLVType_FragmentData=0x0C,
|
||||
kTLVType_FragmentLast=0x0D,
|
||||
kTLVType_Flags=0x13,
|
||||
kTLVType_Separator=0xFF
|
||||
} kTLVType;
|
||||
|
||||
|
||||
// HAP Error Codes (HAP Table 5-5)
|
||||
|
||||
typedef enum {
|
||||
tagError_None=0x00,
|
||||
tagError_Unknown=0x01,
|
||||
tagError_Authentication=0x02,
|
||||
tagError_Backoff=0x03,
|
||||
tagError_MaxPeers=0x04,
|
||||
tagError_MaxTries=0x05,
|
||||
tagError_Unavailable=0x06,
|
||||
tagError_Busy=0x07
|
||||
} tagError;
|
||||
|
||||
|
||||
// Pair-Setup and Pair-Verify States
|
||||
|
||||
typedef enum {
|
||||
pairState_M0=0,
|
||||
pairState_M1=1,
|
||||
pairState_M2=2,
|
||||
pairState_M3=3,
|
||||
pairState_M4=4,
|
||||
pairState_M5=5,
|
||||
pairState_M6=6
|
||||
} pairState;
|
||||
|
||||
// HAP Status Codes (HAP Table 6-11)
|
||||
|
||||
enum class StatusCode {
|
||||
OK=0,
|
||||
Unable=-70402,
|
||||
ReadOnly=-70404,
|
||||
WriteOnly=-70405,
|
||||
NotifyNotAllowed=-70406,
|
||||
UnknownResource=-70409,
|
||||
InvalidValue=-70410,
|
||||
TBD=-1 // status To-Be-Determined (TBD) once service.update() called - internal use only
|
||||
};
|
||||
217
ESP32/HomeSpan-master/src/HKDF.cpp
Normal file
217
ESP32/HomeSpan-master/src/HKDF.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include <mbedtls/hkdf.h>
|
||||
#include <mbedtls/platform_util.h>
|
||||
|
||||
#include "HKDF.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// Wrapper function to call mbedtls_hkdf, below, with
|
||||
// HAP-specific parameters and assumptions
|
||||
|
||||
int HKDF::create(uint8_t *outputKey, uint8_t *inputKey, int inputLen, const char *salt, const char *info){
|
||||
|
||||
return(mbedtls_hkdf( mbedtls_md_info_from_type(MBEDTLS_MD_SHA512),
|
||||
(uint8_t *) salt, (size_t) strlen(salt),
|
||||
inputKey, (size_t) inputLen,
|
||||
(uint8_t *) info, (size_t) strlen(info),
|
||||
outputKey, 32 ));
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// CODE FOR HKDF IS MISSING FROM THE MBEDTLS LIBRARY INCLUDED WITH THE
|
||||
// ARDUINO-ESP32 LIBRARY. THE CODE BELOW IS SOURCE DIRECTLY FROM THE MBEDTLS
|
||||
// GITHUB. SEE THE MBEDTLS GITHUB SITE FOR LICENSING TERMS:
|
||||
//
|
||||
// https://github.com/ARMmbed/mbedtls/blob/development/LICENSE
|
||||
//
|
||||
//
|
||||
|
||||
int mbedtls_hkdf( const mbedtls_md_info_t *md, const unsigned char *salt,
|
||||
size_t salt_len, const unsigned char *ikm, size_t ikm_len,
|
||||
const unsigned char *info, size_t info_len,
|
||||
unsigned char *okm, size_t okm_len )
|
||||
{
|
||||
int ret;
|
||||
unsigned char prk[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
ret = mbedtls_hkdf_extract( md, salt, salt_len, ikm, ikm_len, prk );
|
||||
|
||||
if( ret == 0 )
|
||||
{
|
||||
ret = mbedtls_hkdf_expand( md, prk, mbedtls_md_get_size( md ),
|
||||
info, info_len, okm, okm_len );
|
||||
}
|
||||
|
||||
mbedtls_platform_zeroize( prk, sizeof( prk ) );
|
||||
|
||||
return( ret );
|
||||
}
|
||||
|
||||
int mbedtls_hkdf_extract( const mbedtls_md_info_t *md,
|
||||
const unsigned char *salt, size_t salt_len,
|
||||
const unsigned char *ikm, size_t ikm_len,
|
||||
unsigned char *prk )
|
||||
{
|
||||
unsigned char null_salt[MBEDTLS_MD_MAX_SIZE] = { '\0' };
|
||||
|
||||
if( salt == NULL )
|
||||
{
|
||||
size_t hash_len;
|
||||
|
||||
if( salt_len != 0 )
|
||||
{
|
||||
return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
hash_len = mbedtls_md_get_size( md );
|
||||
|
||||
if( hash_len == 0 )
|
||||
{
|
||||
return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
salt = null_salt;
|
||||
salt_len = hash_len;
|
||||
}
|
||||
|
||||
return( mbedtls_md_hmac( md, salt, salt_len, ikm, ikm_len, prk ) );
|
||||
}
|
||||
|
||||
int mbedtls_hkdf_expand( const mbedtls_md_info_t *md, const unsigned char *prk,
|
||||
size_t prk_len, const unsigned char *info,
|
||||
size_t info_len, unsigned char *okm, size_t okm_len )
|
||||
{
|
||||
size_t hash_len;
|
||||
size_t where = 0;
|
||||
size_t n;
|
||||
size_t t_len = 0;
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
mbedtls_md_context_t ctx;
|
||||
unsigned char t[MBEDTLS_MD_MAX_SIZE];
|
||||
|
||||
if( okm == NULL )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
hash_len = mbedtls_md_get_size( md );
|
||||
|
||||
if( prk_len < hash_len || hash_len == 0 )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
if( info == NULL )
|
||||
{
|
||||
info = (const unsigned char *) "";
|
||||
info_len = 0;
|
||||
}
|
||||
|
||||
n = okm_len / hash_len;
|
||||
|
||||
if( okm_len % hash_len != 0 )
|
||||
{
|
||||
n++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per RFC 5869 Section 2.3, okm_len must not exceed
|
||||
* 255 times the hash length
|
||||
*/
|
||||
if( n > 255 )
|
||||
{
|
||||
return( MBEDTLS_ERR_HKDF_BAD_INPUT_DATA );
|
||||
}
|
||||
|
||||
mbedtls_md_init( &ctx );
|
||||
|
||||
if( ( ret = mbedtls_md_setup( &ctx, md, 1 ) ) != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset( t, 0, hash_len );
|
||||
|
||||
/*
|
||||
* Compute T = T(1) | T(2) | T(3) | ... | T(N)
|
||||
* Where T(N) is defined in RFC 5869 Section 2.3
|
||||
*/
|
||||
for( i = 1; i <= n; i++ )
|
||||
{
|
||||
size_t num_to_copy;
|
||||
unsigned char c = i & 0xff;
|
||||
|
||||
ret = mbedtls_md_hmac_starts( &ctx, prk, prk_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_update( &ctx, t, t_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_update( &ctx, info, info_len );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* The constant concatenated to the end of each T(n) is a single octet.
|
||||
* */
|
||||
ret = mbedtls_md_hmac_update( &ctx, &c, 1 );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_md_hmac_finish( &ctx, t );
|
||||
if( ret != 0 )
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
num_to_copy = i != n ? hash_len : okm_len - where;
|
||||
memcpy( okm + where, t, num_to_copy );
|
||||
where += hash_len;
|
||||
t_len = hash_len;
|
||||
}
|
||||
|
||||
exit:
|
||||
mbedtls_md_free( &ctx );
|
||||
mbedtls_platform_zeroize( t, sizeof( t ) );
|
||||
|
||||
return( ret );
|
||||
}
|
||||
43
ESP32/HomeSpan-master/src/HKDF.h
Normal file
43
ESP32/HomeSpan-master/src/HKDF.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// HKDF-SHA-512 Structure
|
||||
//
|
||||
// This is a wrapper around mbedtls_hkdf, which is NOT
|
||||
// included in the normal Arduino-ESP32 library.
|
||||
// Code was instead sourced directly from MBED GitHub and
|
||||
// incorporated under hkdf.cpp, with a wrapper to always
|
||||
// use SHA-512 with 32 bytes of output as required by HAP.
|
||||
|
||||
namespace HKDF{
|
||||
int create(uint8_t *outputKey, uint8_t *inputKey, int inputLen, const char *salt, const char *info); // output of HKDF is always a 32-byte key derived from an input key, a salt string, and an info string
|
||||
};
|
||||
59
ESP32/HomeSpan-master/src/HapQR.h
Normal file
59
ESP32/HomeSpan-master/src/HapQR.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
class HapQR {
|
||||
private:
|
||||
char qrCode[21];
|
||||
|
||||
public:
|
||||
enum {
|
||||
NFC=1,
|
||||
IP=2,
|
||||
BLTE=4
|
||||
};
|
||||
|
||||
char *get(uint32_t setupCode, const char *setupID, uint8_t category, uint8_t protocols=IP, uint8_t qVersion=0, uint8_t qReserved=0){
|
||||
|
||||
setupCode&=0x07FFFFFF; // valid values: 0-99999999
|
||||
qVersion&=0x7; // valid values: 0-7
|
||||
qReserved&=0xF; // valid values: 0-15
|
||||
protocols&=0x7; // valid values: 0-7
|
||||
|
||||
uint64_t n=((uint64_t) qVersion<<43) | ((uint64_t) qReserved<<39) | ((uint64_t) category<<31) | (protocols<<27) | setupCode;
|
||||
sprintf(qrCode,"X-HM://");
|
||||
|
||||
for(int i=15;i>=7;i--){
|
||||
qrCode[i]=n%36+48;
|
||||
if(qrCode[i]>57)
|
||||
qrCode[i]+=7;
|
||||
n/=36;
|
||||
}
|
||||
|
||||
sprintf(qrCode+16,"%-4.4s",setupID);
|
||||
return(qrCode);
|
||||
} // create
|
||||
};
|
||||
2757
ESP32/HomeSpan-master/src/HomeSpan.cpp
Normal file
2757
ESP32/HomeSpan-master/src/HomeSpan.cpp
Normal file
File diff suppressed because it is too large
Load Diff
832
ESP32/HomeSpan-master/src/HomeSpan.h
Normal file
832
ESP32/HomeSpan-master/src/HomeSpan.h
Normal file
@@ -0,0 +1,832 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wpmf-conversions" // eliminates warning messages from use of pointers to member functions to detect whether update() and loop() are overridden by user
|
||||
#pragma GCC diagnostic ignored "-Wunused-result" // eliminates warning message regarded unused result from call to crypto_scalarmult_curve25519()
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <nvs.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_now.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include "src/extras/Blinker.h"
|
||||
#include "src/extras/Pixel.h"
|
||||
#include "src/extras/RFControl.h"
|
||||
#include "src/extras/PwmPin.h"
|
||||
#include "src/extras/StepperControl.h"
|
||||
|
||||
#include "Settings.h"
|
||||
#include "Utils.h"
|
||||
#include "Network.h"
|
||||
#include "HAPConstants.h"
|
||||
#include "HapQR.h"
|
||||
#include "Characteristics.h"
|
||||
#include "TLV8.h"
|
||||
|
||||
using std::vector;
|
||||
using std::unordered_map;
|
||||
using std::list;
|
||||
|
||||
enum {
|
||||
GET_AID=1,
|
||||
GET_META=2,
|
||||
GET_PERMS=4,
|
||||
GET_TYPE=8,
|
||||
GET_EV=16,
|
||||
GET_DESC=32,
|
||||
GET_NV=64,
|
||||
GET_VALUE=128,
|
||||
GET_STATUS=256
|
||||
};
|
||||
|
||||
typedef boolean BOOL_t;
|
||||
typedef uint8_t UINT8_t;
|
||||
typedef uint16_t UINT16_t;
|
||||
typedef uint32_t UINT32_t;
|
||||
typedef uint64_t UINT64_t;
|
||||
typedef int32_t INT_t;
|
||||
typedef double FLOAT_t;
|
||||
typedef const char * STRING_t;
|
||||
typedef const TLV8 & TLV_ENC_t;
|
||||
typedef std::pair<const uint8_t *, size_t> DATA_t;
|
||||
|
||||
static DATA_t NULL_DATA={NULL,0};
|
||||
static TLV8 NULL_TLV{};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {homeSpan.statusLED->LED_UPDATE;if(homeSpan.statusCallback)homeSpan.statusCallback(MESSAGE_UPDATE);}
|
||||
|
||||
enum HS_STATUS {
|
||||
HS_WIFI_NEEDED, // WiFi Credentials have not yet been set/stored
|
||||
HS_WIFI_CONNECTING, // HomeSpan is trying to connect to the network specified in the stored WiFi Credentials
|
||||
HS_PAIRING_NEEDED, // HomeSpan is connected to central WiFi network, but device has not yet been paired to HomeKit
|
||||
HS_PAIRED, // HomeSpan is connected to central WiFi network and ther device has been paired to HomeKit
|
||||
HS_ENTERING_CONFIG_MODE, // User has requested the device to enter into Command Mode
|
||||
HS_CONFIG_MODE_EXIT, // HomeSpan is in Command Mode with "Exit Command Mode" specified as choice
|
||||
HS_CONFIG_MODE_REBOOT, // HomeSpan is in Command Mode with "Reboot" specified as choice
|
||||
HS_CONFIG_MODE_LAUNCH_AP, // HomeSpan is in Command Mode with "Launch Access Point" specified as choice
|
||||
HS_CONFIG_MODE_UNPAIR, // HomeSpan is in Command Mode with "Unpair Device" specified as choice
|
||||
HS_CONFIG_MODE_ERASE_WIFI, // HomeSpan is in Command Mode with "Erase WiFi Credentials" specified as choice
|
||||
HS_CONFIG_MODE_EXIT_SELECTED, // User has selected "Exit Command Mode"
|
||||
HS_CONFIG_MODE_REBOOT_SELECTED, // User has select "Reboot" from the Command Mode
|
||||
HS_CONFIG_MODE_LAUNCH_AP_SELECTED, // User has selected "Launch AP Access" from the Command Mode
|
||||
HS_CONFIG_MODE_UNPAIR_SELECTED, // User has seleected "Unpair Device" from the Command Mode
|
||||
HS_CONFIG_MODE_ERASE_WIFI_SELECTED, // User has selected "Erase WiFi Credentials" from the Command Mode
|
||||
HS_REBOOTING, // HomeSpan is in the process of rebooting the device
|
||||
HS_FACTORY_RESET, // HomeSpan is in the process of performing a Factory Reset of device
|
||||
HS_AP_STARTED, // HomeSpan has started the Access Point but no one has yet connected
|
||||
HS_AP_CONNECTED, // The Access Point is started and a user device has been connected
|
||||
HS_AP_TERMINATED, // HomeSpan has terminated the Access Point
|
||||
HS_OTA_STARTED // HomeSpan is in the process of recveived an Over-the-Air software update
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
// Forward-Declarations
|
||||
|
||||
struct Span;
|
||||
struct SpanAccessory;
|
||||
struct SpanService;
|
||||
struct SpanCharacteristic;
|
||||
struct SpanBuf;
|
||||
struct SpanButton;
|
||||
struct SpanUserCommand;
|
||||
|
||||
struct HAPClient;
|
||||
class Controller;
|
||||
|
||||
extern Span homeSpan;
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// INTERNAL HOMESPAN STRUCTURES - NOT FOR USER ACCESS //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
struct SpanPartition{
|
||||
char magicCookie[32];
|
||||
uint8_t reserved[224];
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanConfig{
|
||||
int configNumber=0; // configuration number - broadcast as Bonjour "c#" (computed automatically)
|
||||
uint8_t hashCode[48]={0}; // SHA-384 hash of Span Database stored as a form of unique "signature" to know when to update the config number upon changes
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanBuf{ // temporary storage buffer for use with putCharacteristicsURL() and checkTimedResets()
|
||||
uint32_t aid=0; // updated aid
|
||||
uint32_t iid=0; // updated iid
|
||||
boolean wr=false; // flag to indicate write-response has been requested
|
||||
char *val=NULL; // updated value (optional, though either at least 'val' or 'ev' must be specified)
|
||||
char *ev=NULL; // updated event notification flag (optional, though either at least 'val' or 'ev' must be specified)
|
||||
StatusCode status; // return status (HAP Table 6-11)
|
||||
SpanCharacteristic *characteristic=NULL; // Characteristic to update (NULL if not found)
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanWebLog{ // optional web status/log data
|
||||
boolean isEnabled=false; // flag to inidicate WebLog has been enabled
|
||||
uint16_t maxEntries=0; // max number of log entries;
|
||||
int nEntries=0; // total cumulative number of log entries
|
||||
const char *timeServer=NULL; // optional time server to use for acquiring clock time
|
||||
const char *timeZone; // optional time-zone specification
|
||||
boolean timeInit=false; // flag to indicate time has been initialized
|
||||
char bootTime[33]="Unknown"; // boot time
|
||||
String statusURL; // URL of status log
|
||||
uint32_t waitTime=120000; // number of milliseconds to wait for initial connection to time server
|
||||
String css=""; // optional user-defined style sheet for web log
|
||||
|
||||
struct log_t { // log entry type
|
||||
uint64_t upTime; // number of seconds since booting
|
||||
struct tm clockTime; // clock time
|
||||
char *message; // pointers to log entries of arbitrary size
|
||||
String clientIP; // IP address of client making request (or "0.0.0.0" if not applicable)
|
||||
} *log=NULL; // array of log entries
|
||||
|
||||
void init(uint16_t maxEntries, const char *serv, const char *tz, const char *url);
|
||||
static void initTime(void *args);
|
||||
void vLog(boolean sysMsg, const char *fmr, va_list ap);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct SpanOTA{ // manages OTA process
|
||||
|
||||
char otaPwd[33]=""; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
||||
|
||||
static boolean enabled; // enables OTA - default if not enabled
|
||||
static boolean auth; // indicates whether OTA password is required
|
||||
static int otaPercent;
|
||||
static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch
|
||||
|
||||
int init(boolean auth, boolean safeLoad, const char *pwd);
|
||||
int setPassword(const char *pwd);
|
||||
static void start();
|
||||
static void end();
|
||||
static void progress(uint32_t progress, uint32_t total);
|
||||
static void error(ota_error_t err);
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
// USER API CLASSES BEGINS HERE //
|
||||
//////////////////////////////////////
|
||||
|
||||
class Span{
|
||||
|
||||
friend class SpanAccessory;
|
||||
friend class SpanService;
|
||||
friend class SpanCharacteristic;
|
||||
friend class SpanUserCommand;
|
||||
friend class SpanButton;
|
||||
friend class SpanWebLog;
|
||||
friend class SpanOTA;
|
||||
friend class Network;
|
||||
friend class HAPClient;
|
||||
|
||||
char *displayName; // display name for this device - broadcast as part of Bonjour MDNS
|
||||
char *hostNameBase; // base of hostName of this device - full host name broadcast by Bonjour MDNS will have 6-byte accessoryID as well as '.local' automatically appended
|
||||
char *hostNameSuffix=NULL; // optional "suffix" of hostName of this device. If specified, will be used as the hostName suffix instead of the 6-byte accessoryID
|
||||
char *hostName=NULL; // derived full hostname
|
||||
char *modelName; // model name of this device - broadcast as Bonjour field "md"
|
||||
char category[3]=""; // category ID of primary accessory - broadcast as Bonjour field "ci" (HAP Section 13)
|
||||
unsigned long snapTime; // current time (in millis) snapped before entering Service loops() or updates()
|
||||
boolean isInitialized=false; // flag indicating HomeSpan has been initialized
|
||||
boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation)
|
||||
HapQR qrCode; // optional QR Code to use for pairing
|
||||
const char *sketchVersion="n/a"; // version of the sketch
|
||||
char pairingCodeCommand[12]=""; // user-specified Pairing Code - only needed if Pairing Setup Code is specified in sketch using setPairingCode()
|
||||
String lastClientIP="0.0.0.0"; // IP address of last client accessing device through encrypted channel
|
||||
boolean newCode; // flag indicating new application code has been loaded (based on keeping track of app SHA256)
|
||||
boolean serialInputDisabled=false; // flag indiating that serial input is disabled
|
||||
uint8_t rebootCount=0; // counts number of times device was rebooted (used in optional Reboot callback)
|
||||
uint32_t rebootCallbackTime; // length of time to wait (in milliseconds) before calling optional Reboot callback
|
||||
|
||||
nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data
|
||||
nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data
|
||||
nvs_handle otaNVS; // handle for non-volatile storage of OTA data
|
||||
nvs_handle srpNVS; // handle for non-volatile storage of SRP data
|
||||
nvs_handle hapNVS; // handle for non-volatile-storage of HAP data
|
||||
|
||||
int connected=0; // WiFi connection status (increments upon each connect and disconnect)
|
||||
unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts
|
||||
unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again
|
||||
|
||||
const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing
|
||||
uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED
|
||||
int logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor
|
||||
unsigned long comModeLife=DEFAULT_COMMAND_TIMEOUT*1000; // length of time (in milliseconds) to keep Command Mode alive before resuming normal operations
|
||||
uint16_t tcpPortNum=DEFAULT_TCP_PORT; // port for TCP communications between HomeKit and HomeSpan
|
||||
char qrID[5]=""; // Setup ID used for pairing with QR Code
|
||||
void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is initially established
|
||||
void (*wifiCallbackAll)(int)=NULL; // optional callback function to invoke every time WiFi connectivity is established or re-established
|
||||
void (*weblogCallback)(String &)=NULL; // optional callback function to invoke after header table in Web Log is produced
|
||||
void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false)
|
||||
boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found
|
||||
void (*apFunction)()=NULL; // optional function to invoke when starting Access Point
|
||||
void (*statusCallback)(HS_STATUS status)=NULL; // optional callback when HomeSpan status changes
|
||||
void (*rebootCallback)(uint8_t)=NULL; // optional callback when device reboots
|
||||
void (*controllerCallback)()=NULL; // optional callback when Controller is added/removed/changed
|
||||
|
||||
WiFiServer *hapServer; // pointer to the HAP Server connection
|
||||
Blinker *statusLED; // indicates HomeSpan status
|
||||
Blinkable *statusDevice = NULL; // the device used for the Blinker
|
||||
PushButton *controlButton = NULL; // controls HomeSpan configuration and resets
|
||||
Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point
|
||||
SpanWebLog webLog; // optional web status/log
|
||||
TaskHandle_t pollTaskHandle = NULL; // optional task handle to use for poll() function
|
||||
TaskHandle_t loopTaskHandle; // Arduino Loop Task handle
|
||||
boolean verboseWifiReconnect = true; // set to false to not print WiFi reconnect attempts messages
|
||||
|
||||
SpanOTA spanOTA; // manages OTA process
|
||||
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
|
||||
|
||||
list<HAPClient, Mallocator<HAPClient>> hapList; // linked-list of HAPClient structures containing HTTP client connections, parsing routines, and state variables
|
||||
list<HAPClient, Mallocator<HAPClient>>::iterator currentClient; // iterator to current client
|
||||
vector<SpanAccessory *, Mallocator<SpanAccessory *>> Accessories; // vector of pointers to all Accessories
|
||||
vector<SpanService *, Mallocator<SpanService *>> Loops; // vector of pointer to all Services that have over-ridden loop() methods
|
||||
vector<SpanBuf, Mallocator<SpanBuf>> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
|
||||
vector<SpanButton *, Mallocator<SpanButton *>> PushButtons; // vector of pointer to all PushButtons
|
||||
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
|
||||
unordered_map<char, SpanUserCommand *> UserCommands; // map of pointers to all UserCommands
|
||||
|
||||
void pollTask(); // poll HAP Clients and process any new HAP requests
|
||||
void checkConnect(); // check WiFi connection; connect if needed
|
||||
void commandMode(); // allows user to control and reset HomeSpan settings with the control button
|
||||
void resetStatus(); // resets statusLED and calls statusCallback based on current HomeSpan status
|
||||
void reboot(); // reboots device
|
||||
|
||||
void printfAttributes(int flags=GET_VALUE|GET_META|GET_PERMS|GET_TYPE|GET_DESC); // writes Attributes JSON database to hapOut stream
|
||||
|
||||
SpanCharacteristic *find(uint32_t aid, uint32_t iid); // return Characteristic with matching aid and iid (else NULL if not found)
|
||||
int countCharacteristics(char *buf); // return number of characteristic objects referenced in PUT /characteristics JSON request
|
||||
int updateCharacteristics(char *buf, SpanBuf *pObj); // parses PUT /characteristics JSON request 'buf into 'pObj' and updates referenced characteristics; returns 1 on success, 0 on fail
|
||||
void printfAttributes(SpanBuf *pObj, int nObj); // writes SpanBuf objects to hapOut stream
|
||||
boolean printfAttributes(char **ids, int numIDs, int flags); // writes accessory requested characteristic ids to hapOut stream - returns true if all characteristics are found and readable, else returns false
|
||||
void clearNotify(HAPClient *hc); // clear all notifications related to specific client connection
|
||||
void printfNotify(SpanBuf *pObj, int nObj, HAPClient *hc); // writes notification JSON to hapOut stream based on SpanBuf objects and specified connection
|
||||
|
||||
static boolean invalidUUID(const char *uuid){
|
||||
int x=0;
|
||||
sscanf(uuid,"%*8[0-9a-fA-F]%n",&x); // check for short-form of UUID
|
||||
if(strlen(uuid)==x && uuid[0]!='0')
|
||||
return(false);
|
||||
sscanf(uuid,"%*8[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*4[0-9a-fA-F]-%*12[0-9a-fA-F]%n",&x);
|
||||
return(strlen(uuid)!=36 || x!=36);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Span(); // constructor
|
||||
|
||||
void begin(Category catID=DEFAULT_CATEGORY,
|
||||
const char *displayName=DEFAULT_DISPLAY_NAME,
|
||||
const char *hostNameBase=DEFAULT_HOST_NAME,
|
||||
const char *modelName=DEFAULT_MODEL_NAME);
|
||||
|
||||
void poll(); // calls pollTask() with some error checking
|
||||
void processSerialCommand(const char *c); // process command 'c' (typically from readSerial, though can be called with any 'c')
|
||||
|
||||
boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed
|
||||
boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false
|
||||
|
||||
Span& setControlPin(uint8_t pin, PushButton::triggerType_t triggerType=PushButton::TRIGGER_ON_LOW){ // sets Control Pin, with optional trigger type
|
||||
controlButton=new PushButton(pin, triggerType);
|
||||
return(*this);
|
||||
}
|
||||
|
||||
int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined)
|
||||
|
||||
Span& setStatusPin(uint8_t pin){statusDevice=new GenericLED(pin);return(*this);} // sets Status Device to a simple LED on specified pin
|
||||
Span& setStatusPixel(uint8_t pin,float h=0,float s=100,float v=100){ // sets Status Device to an RGB Pixel on specified pin
|
||||
statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v)));
|
||||
return(*this);
|
||||
}
|
||||
Span& setStatusDevice(Blinkable *sDev){statusDevice=sDev;return(*this);} // sets Status Device to a generic Blinkable object
|
||||
|
||||
Span& setStatusAutoOff(uint16_t duration){autoOffLED=duration;return(*this);} // sets Status LED auto off (seconds)
|
||||
int getStatusPin(){return(statusLED->getPin());} // get Status Pin (returns -1 if undefined)
|
||||
void refreshStatusDevice(){if(statusLED)statusLED->refresh();} // refreshes state of Status LED
|
||||
|
||||
Span& setApSSID(const char *ssid){network.apSSID=ssid;return(*this);} // sets Access Point SSID
|
||||
Span& setApPassword(const char *pwd){network.apPassword=pwd;return(*this);} // sets Access Point Password
|
||||
Span& setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;return(*this);} // sets Access Point Timeout (seconds)
|
||||
Span& setCommandTimeout(uint16_t nSec){comModeLife=nSec*1000;return(*this);} // sets Command Mode Timeout (seconds)
|
||||
Span& setLogLevel(int level){logLevel=level;return(*this);} // sets Log Level for log messages (0=baseline, 1=intermediate, 2=all, -1=disable all serial input/output)
|
||||
int getLogLevel(){return(logLevel);} // get Log Level
|
||||
Span& setSerialInputDisable(boolean val){serialInputDisabled=val;return(*this);} // sets whether serial input is disabled (true) or enabled (false)
|
||||
boolean getSerialInputDisable(){return(serialInputDisabled);} // returns true if serial input is disabled, or false if serial input in enabled
|
||||
Span& setPortNum(uint16_t port){tcpPortNum=port;return(*this);} // sets the TCP port number to use for communications between HomeKit and HomeSpan
|
||||
Span& setQRID(const char *id); // sets the Setup ID for optional pairing with a QR Code
|
||||
Span& setSketchVersion(const char *sVer){sketchVersion=sVer;return(*this);} // set optional sketch version number
|
||||
const char *getSketchVersion(){return sketchVersion;} // get sketch version number
|
||||
Span& setWifiCallback(void (*f)()){wifiCallback=f;return(*this);} // sets an optional user-defined function to call once WiFi connectivity is initially established
|
||||
Span& setWifiCallbackAll(void (*f)(int)){wifiCallbackAll=f;return(*this);} // sets an optional user-defined function to call every time WiFi connectivity is established or re-established
|
||||
Span& setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;return(*this);} // sets an optional user-defined function to call when Pairing is established (true) or lost (false)
|
||||
Span& setApFunction(void (*f)()){apFunction=f;return(*this);} // sets an optional user-defined function to call when activating the WiFi Access Point
|
||||
Span& enableAutoStartAP(){autoStartAPEnabled=true;return(*this);} // enables auto start-up of Access Point when WiFi Credentials not found
|
||||
Span& setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials
|
||||
Span& setStatusCallback(void (*f)(HS_STATUS status)){statusCallback=f;return(*this);} // sets an optional user-defined function to call when HomeSpan status changes
|
||||
const char* statusString(HS_STATUS s); // returns char string for HomeSpan status change messages
|
||||
Span& setPairingCode(const char *s, boolean progCall=true); // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
||||
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
||||
Span& resetIID(uint32_t newIID); // resets the IID count for the current Accessory to start at newIID
|
||||
Span& setControllerCallback(void (*f)()){controllerCallback=f;return(*this);} // sets an optional user-defined function to call whenever a Controller is added/removed/changed
|
||||
|
||||
Span& setHostNameSuffix(const char *suffix){asprintf(&hostNameSuffix,"%s",suffix);return(*this);} // sets the hostName suffix to be used instead of the 6-byte AccessoryID
|
||||
|
||||
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
|
||||
|
||||
Span& enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
||||
webLog.init(maxEntries, serv, tz, url);
|
||||
return(*this);
|
||||
}
|
||||
|
||||
void addWebLog(boolean sysMsg, const char *fmt, ...){ // add Web Log entry
|
||||
va_list ap;
|
||||
va_start(ap,fmt);
|
||||
webLog.vLog(sysMsg,fmt,ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
Span& setWebLogCSS(const char *css){webLog.css="\n" + String(css) + "\n";return(*this);}
|
||||
Span& setWebLogCallback(void (*f)(String &)){weblogCallback=f;return(*this);}
|
||||
void getWebLog(void (*f)(const char *, void *), void *);
|
||||
|
||||
Span& setVerboseWifiReconnect(bool verbose=true){verboseWifiReconnect=verbose;return(*this);}
|
||||
|
||||
Span& setRebootCallback(void (*f)(uint8_t),uint32_t t=DEFAULT_REBOOT_CALLBACK_TIME){rebootCallback=f;rebootCallbackTime=t;return(*this);}
|
||||
|
||||
void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask()
|
||||
xTaskCreateUniversal([](void *parms){
|
||||
for(;;){
|
||||
homeSpan.pollTask();
|
||||
vTaskDelay(5);
|
||||
}
|
||||
},
|
||||
"pollTask", stackSize, NULL, priority, &pollTaskHandle, cpu);
|
||||
LOG0("\n*** AutoPolling Task started with priority=%d\n\n",uxTaskPriorityGet(pollTaskHandle));
|
||||
}
|
||||
|
||||
TaskHandle_t getAutoPollTask(){return(pollTaskHandle);}
|
||||
|
||||
Span& setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;return(*this);} // sets wait time (in seconds) for optional web log time server to connect
|
||||
|
||||
list<Controller, Mallocator<Controller>>::const_iterator controllerListBegin();
|
||||
list<Controller, Mallocator<Controller>>::const_iterator controllerListEnd();
|
||||
|
||||
[[deprecated("This function has been deprecated (it is not needed) and no longer does anything. Please remove from sketch to ensure backwards compatilibilty with future versions.")]]
|
||||
Span& reserveSocketConnections(uint8_t n){return(*this);}
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanAccessory{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
friend class SpanCharacteristic;
|
||||
friend class SpanButton;
|
||||
|
||||
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
|
||||
uint32_t iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
|
||||
vector<SpanService *, Mallocator<SpanService*>> Services; // vector of pointers to all Services in this Accessory
|
||||
|
||||
void printfAttributes(int flags); // writes Accessory JSON to hapOut stream
|
||||
|
||||
protected:
|
||||
|
||||
~SpanAccessory(); // destructor
|
||||
|
||||
public:
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
SpanAccessory(uint32_t aid=0); // constructor
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanService{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanAccessory;
|
||||
friend class SpanCharacteristic;
|
||||
|
||||
uint32_t iid=0; // Instance ID (HAP Table 6-2)
|
||||
const char *type; // Service Type
|
||||
const char *hapName; // HAP Name
|
||||
boolean hidden=false; // optional property indicating service is hidden
|
||||
boolean primary=false; // optional property indicating service is primary
|
||||
vector<SpanCharacteristic *, Mallocator<SpanCharacteristic*>> Characteristics; // vector of pointers to all Characteristics in this Service
|
||||
vector<SpanService *, Mallocator<SpanService *>> linkedServices; // vector of pointers to any optional linked Services
|
||||
boolean isCustom; // flag to indicate this is a Custom Service
|
||||
SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service
|
||||
|
||||
void printfAttributes(int flags); // writes Service JSON to hapOut stream
|
||||
|
||||
protected:
|
||||
|
||||
virtual ~SpanService(); // destructor
|
||||
vector<HapChar *, Mallocator<HapChar*>> req; // vector of pointers to all required HAP Characteristic Types for this Service
|
||||
vector<HapChar *, Mallocator<HapChar*>> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
|
||||
|
||||
public:
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
SpanService(const char *type, const char *hapName, boolean isCustom=false); // constructor
|
||||
SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self
|
||||
SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self
|
||||
SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self
|
||||
|
||||
template <typename T=SpanService *> vector<T, Mallocator<T>> getLinks(const char *hapName=NULL){ // returns linkedServices vector, mapped to <T>, for use as range in "for-each" loops
|
||||
vector<T, Mallocator<T>> v;
|
||||
for(auto svc : linkedServices){
|
||||
if(hapName==NULL || !strcmp(hapName,svc->hapName))
|
||||
v.push_back(static_cast<T>(svc));
|
||||
}
|
||||
return(v);
|
||||
}
|
||||
|
||||
uint32_t getIID(){return(iid);} // returns IID of Service
|
||||
|
||||
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
|
||||
virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code
|
||||
virtual void button(int pin, int pressType){} // method called for a Service when a button attached to "pin" has a Single, Double, or Long Press, according to pressType
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanCharacteristic{
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
|
||||
union UVal {
|
||||
boolean BOOL;
|
||||
uint8_t UINT8;
|
||||
uint16_t UINT16;
|
||||
uint32_t UINT32;
|
||||
uint64_t UINT64;
|
||||
int32_t INT;
|
||||
double FLOAT;
|
||||
char * STRING = NULL;
|
||||
};
|
||||
|
||||
class EVLIST : public vector<HAPClient *, Mallocator<HAPClient *>>{ // vector of current connections that have subscribed to EV notifications for this Characteristic
|
||||
public:
|
||||
boolean has(HAPClient *hc); // returns true if pointer to connection hc is subscribed, else returns false
|
||||
void add(HAPClient *hc); // adds connection hc as new subscriber, IF not already a subscriber
|
||||
void remove(HAPClient *hc); // removes connection hc as a subscriber; okay to remove even if hc was not already a subscriber
|
||||
};
|
||||
|
||||
uint32_t iid=0; // Instance ID (HAP Table 6-3)
|
||||
HapChar *hapChar; // pointer to HAP Characteristic structure
|
||||
const char *type; // Characteristic Type
|
||||
const char *hapName; // HAP Name
|
||||
UVal value; // Characteristic Value
|
||||
uint8_t perms; // Characteristic Permissions
|
||||
FORMAT format; // Characteristic Format
|
||||
char *desc=NULL; // Characteristic Description (optional)
|
||||
char *unit=NULL; // Characteristic Unit (optional)
|
||||
UVal minValue; // Characteristic minimum (not applicable for STRING)
|
||||
UVal maxValue; // Characteristic maximum (not applicable for STRING)
|
||||
UVal stepValue; // Characteristic step size (not applicable for STRING)
|
||||
boolean staticRange; // Flag that indicates whether Range is static and cannot be changed with setRange()
|
||||
boolean customRange=false; // Flag for custom ranges
|
||||
char *validValues=NULL; // Optional JSON array of valid values. Applicable only to uint8 Characteristics
|
||||
char *nvsKey=NULL; // key for NVS storage of Characteristic value
|
||||
boolean isCustom; // flag to indicate this is a Custom Characteristic
|
||||
boolean setRangeError=false; // flag to indicate attempt to set Range on Characteristic that does not support changes to Range
|
||||
boolean setValidValuesError=false; // flag to indicate attempt to set Valid Values on Characteristic that does not support changes to Valid Values
|
||||
|
||||
uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic
|
||||
uint8_t updateFlag=0; // set to either 1 (for normal write) or 2 (for write-response) inside update() when Characteristic is successfully updated via Home App
|
||||
unsigned long updateTime=0; // last time value was updated (in millis) either by PUT /characteristic OR by setVal()
|
||||
UVal newValue; // the updated value requested by PUT /characteristic
|
||||
SpanService *service=NULL; // pointer to Service containing this Characteristic
|
||||
EVLIST evList; // vector of current connections that have subscribed to EV notifications for this Characteristic
|
||||
|
||||
void printfAttributes(int flags); // writes Characteristic JSON to hapOut stream
|
||||
StatusCode loadUpdate(char *val, char *ev, boolean wr); // load updated val/ev from PUT /characteristic JSON request. Return intitial HAP status code (checks to see if characteristic is found, is writable, etc.)
|
||||
String uvPrint(UVal &u); // returns "printable" String for any type of Characteristic
|
||||
|
||||
void uvSet(UVal &dest, UVal &src); // copies UVal src into UVal dest
|
||||
void uvSet(UVal &u, STRING_t val); // copies string val into UVal u
|
||||
void uvSet(UVal &u, DATA_t data); // copies DATA data into UVal u (after transforming to a char *)
|
||||
void uvSet(UVal &u, TLV_ENC_t tlv); // copies TLV8 tlv into UVal u (after transforming to a char *)
|
||||
|
||||
template <typename T> void uvSet(UVal &u, T val){ // copies numeric val into UVal u
|
||||
switch(format){
|
||||
case FORMAT::BOOL:
|
||||
u.BOOL=(boolean)val;
|
||||
break;
|
||||
case FORMAT::INT:
|
||||
u.INT=(int)val;
|
||||
break;
|
||||
case FORMAT::UINT8:
|
||||
u.UINT8=(uint8_t)val;
|
||||
break;
|
||||
case FORMAT::UINT16:
|
||||
u.UINT16=(uint16_t)val;
|
||||
break;
|
||||
case FORMAT::UINT32:
|
||||
u.UINT32=(uint32_t)val;
|
||||
break;
|
||||
case FORMAT::UINT64:
|
||||
u.UINT64=(uint64_t)val;
|
||||
break;
|
||||
case FORMAT::FLOAT:
|
||||
u.FLOAT=(double)val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
char *getStringGeneric(UVal &val); // gets the specified UVal for string-based Characteristics
|
||||
size_t getDataGeneric(uint8_t *data, size_t len, UVal &val); // gets the specified UVal for data-based Characteristics
|
||||
size_t getTLVGeneric(TLV8 &tlv, UVal &val); // gets the specified UVal for tlv8-based Characteristics
|
||||
|
||||
template <class T> T uvGet(UVal &u){ // gets the specified UVal for numeric-based Characteristics
|
||||
|
||||
switch(format){
|
||||
case FORMAT::BOOL:
|
||||
return((T) u.BOOL);
|
||||
case FORMAT::INT:
|
||||
return((T) u.INT);
|
||||
case FORMAT::UINT8:
|
||||
return((T) u.UINT8);
|
||||
case FORMAT::UINT16:
|
||||
return((T) u.UINT16);
|
||||
case FORMAT::UINT32:
|
||||
return((T) u.UINT32);
|
||||
case FORMAT::UINT64:
|
||||
return((T) u.UINT64);
|
||||
case FORMAT::FLOAT:
|
||||
return((T) u.FLOAT);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return((T)0); // included to prevent compiler warnings
|
||||
}
|
||||
|
||||
void setValCheck(); // initial check before setting value of any Characteristic
|
||||
void setValFinish(boolean notify); // final processing after setting value of any Characteristic
|
||||
|
||||
protected:
|
||||
|
||||
~SpanCharacteristic(); // destructor
|
||||
|
||||
template <typename T> void init(T val, boolean nvsStore, T min, T max){
|
||||
|
||||
uvSet(value,val);
|
||||
|
||||
if(nvsStore){
|
||||
nvsKey=(char *)HS_MALLOC(16);
|
||||
uint16_t t;
|
||||
sscanf(type,"%hx",&t);
|
||||
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
|
||||
size_t len;
|
||||
|
||||
if(format<FORMAT::STRING){
|
||||
if(nvs_get_u64(homeSpan.charNVS,nvsKey,&(value.UINT64))!=ESP_OK) {
|
||||
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
}
|
||||
} else {
|
||||
if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){
|
||||
value.STRING = (char *)HS_REALLOC(value.STRING,len);
|
||||
nvs_get_str(homeSpan.charNVS,nvsKey,value.STRING,&len);
|
||||
}
|
||||
else {
|
||||
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uvSet(newValue,value);
|
||||
|
||||
if(format<FORMAT::STRING){
|
||||
uvSet(minValue,min);
|
||||
uvSet(maxValue,max);
|
||||
uvSet(stepValue,0);
|
||||
}
|
||||
|
||||
} // init()
|
||||
|
||||
public:
|
||||
|
||||
SpanCharacteristic(HapChar *hapChar, boolean isCustom=false); // SpanCharacteristic constructor
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
|
||||
template <class T=int> T getVal(){return(uvGet<T>(value));} // gets the value for numeric-based Characteristics
|
||||
char *getString(){return(getStringGeneric(value));} // gets the value for string-based Characteristics
|
||||
size_t getData(uint8_t *data, size_t len){return(getDataGeneric(data,len,value));} // gets the value for data-based Characteristics
|
||||
size_t getTLV(TLV8 &tlv){return(getTLVGeneric(tlv,value));} // gets the value for tlv8-based Characteristics
|
||||
|
||||
template <class T=int> T getNewVal(){return(uvGet<T>(newValue));} // gets the newValue for numeric-based Characteristics
|
||||
char *getNewString(){return(getStringGeneric(newValue));} // gets the newValue for string-based Characteristics
|
||||
size_t getNewData(uint8_t *data, size_t len){return(getDataGeneric(data,len,newValue));} // gets the newValue for data-based Characteristics
|
||||
size_t getNewTLV(TLV8 &tlv){return(getTLVGeneric(tlv,newValue));} // gets the newValue for tlv8-based Characteristics
|
||||
|
||||
void setString(const char *val, boolean notify=true); // sets the value and newValue for string-based Characteristic
|
||||
void setData(const uint8_t *data, size_t len, boolean notify=true); // sets the value and newValue for data-based Characteristic
|
||||
void setTLV(const TLV8 &tlv, boolean notify=true); // sets the value and newValue for tlv8-based Characteristic
|
||||
|
||||
template <typename T> void setVal(T val, boolean notify=true){ // sets the value and newValue for numeric-based Characteristics
|
||||
|
||||
setValCheck();
|
||||
|
||||
if(!((val >= uvGet<T>(minValue)) && (val <= uvGet<T>(maxValue)))){
|
||||
LOG0("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-responsive!\n\n",
|
||||
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
|
||||
}
|
||||
|
||||
uvSet(value,val);
|
||||
uvSet(newValue,value);
|
||||
|
||||
updateTime=homeSpan.snapTime;
|
||||
|
||||
if(notify){
|
||||
if(updateFlag!=2){ // do not broadcast EV if update is being done in context of write-response
|
||||
SpanBuf sb; // create SpanBuf object
|
||||
sb.characteristic=this; // set characteristic
|
||||
sb.status=StatusCode::OK; // set status
|
||||
char dummy[]="";
|
||||
sb.val=dummy; // set dummy "val" so that printfNotify knows to consider this "update"
|
||||
homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector
|
||||
}
|
||||
|
||||
if(nvsKey){
|
||||
nvs_set_u64(homeSpan.charNVS,nvsKey,value.UINT64); // store data as uint64_t regardless of actual type (it will be read correctly when access through uvGet())
|
||||
nvs_commit(homeSpan.charNVS);
|
||||
}
|
||||
}
|
||||
|
||||
} // setVal()
|
||||
|
||||
boolean updated(); // returns true within update() if Characteristic was updated by Home App
|
||||
unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated, either by Home App or by using setVal()
|
||||
uint32_t getIID(); // returns IID of Characteristic
|
||||
|
||||
SpanCharacteristic *setPerms(uint8_t perms); // sets permissions of a Characteristic
|
||||
SpanCharacteristic *addPerms(uint8_t dPerms); // add permissions of a Characteristic
|
||||
SpanCharacteristic *removePerms(uint8_t dPerms); // removes permissions of a Characteristic
|
||||
SpanCharacteristic *setDescription(const char *c); // sets description of a Characteristic
|
||||
SpanCharacteristic *setUnit(const char *c); // set unit of a Characteristic
|
||||
SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic - only applicable if format=INT, UINT8, UINT16, or UINT32
|
||||
|
||||
template <typename A, typename B, typename S=int> SpanCharacteristic *setRange(A min, B max, S step=0){ // sets the allowed range of a Characteristic
|
||||
|
||||
if(!staticRange){
|
||||
uvSet(minValue,min);
|
||||
uvSet(maxValue,max);
|
||||
uvSet(stepValue,step);
|
||||
customRange=true;
|
||||
} else
|
||||
setRangeError=true;
|
||||
|
||||
return(this);
|
||||
|
||||
} // setRange()
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanButton : public PushButton {
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
|
||||
uint16_t singleTime; // minimum time (in millis) required to register a single press
|
||||
uint16_t longTime; // minimum time (in millis) required to register a long press
|
||||
uint16_t doubleTime; // maximum time (in millis) between single presses to register a double press instead
|
||||
SpanService *service; // Service to which this PushButton is attached
|
||||
|
||||
void check(); // check PushButton and call button() if "pressed"
|
||||
|
||||
protected:
|
||||
|
||||
enum buttonType_t {
|
||||
HS_BUTTON,
|
||||
HS_TOGGLE
|
||||
};
|
||||
|
||||
buttonType_t buttonType=HS_BUTTON; // type of SpanButton
|
||||
|
||||
public:
|
||||
|
||||
SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||
SpanButton(int pin, triggerType_t triggerType, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200) : SpanButton(pin,longTime,singleTime,doubleTime,triggerType){};
|
||||
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanToggle : public SpanButton {
|
||||
|
||||
public:
|
||||
|
||||
SpanToggle(int pin, triggerType_t triggerType=TRIGGER_ON_LOW, uint16_t toggleTime=5) : SpanButton(pin,triggerType,toggleTime){buttonType=HS_TOGGLE;};
|
||||
int position(){return(pressType);}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanUserCommand {
|
||||
|
||||
friend class Span;
|
||||
|
||||
const char *s; // description of command
|
||||
void (*userFunction1)(const char *v)=NULL; // user-defined function to call
|
||||
void (*userFunction2)(const char *v, void *arg)=NULL; // user-defined function to call with user-defined arg
|
||||
void *userArg;
|
||||
|
||||
public:
|
||||
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *));
|
||||
SpanUserCommand(char c, const char *s, void (*f)(const char *, void *), void *arg);
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanPoint {
|
||||
|
||||
friend class Span;
|
||||
|
||||
int receiveSize; // size (in bytes) of messages to receive
|
||||
int sendSize; // size (in bytes) of messages to send
|
||||
esp_now_peer_info_t peerInfo; // structure for all ESP-NOW peer data
|
||||
QueueHandle_t receiveQueue; // queue to store data after it is received
|
||||
uint32_t receiveTime=0; // time (in millis) of most recent data received
|
||||
|
||||
static uint8_t lmk[16];
|
||||
static boolean initialized;
|
||||
static boolean isHub;
|
||||
static boolean useEncryption;
|
||||
static vector<SpanPoint *, Mallocator<SpanPoint *>> SpanPoints;
|
||||
static uint16_t channelMask; // channel mask (only used for remote devices)
|
||||
static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send
|
||||
static nvs_handle pointNVS; // NVS storage for channel number (only used for remote devices)
|
||||
|
||||
static void dataReceived(const uint8_t *mac, const uint8_t *incomingData, int len);
|
||||
static void init(const char *password="HomeSpan");
|
||||
static void setAsHub(){isHub=true;}
|
||||
static uint8_t nextChannel();
|
||||
|
||||
static void dataSent(const uint8_t *mac, esp_now_send_status_t status) {
|
||||
xQueueOverwrite( statusQueue, &status );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1, boolean useAPaddress=false);
|
||||
static void setPassword(const char *pwd){init(pwd);}
|
||||
static void setChannelMask(uint16_t mask);
|
||||
static void setEncryption(boolean encrypt){useEncryption=encrypt;}
|
||||
boolean get(void *dataBuf);
|
||||
boolean send(const void *data);
|
||||
uint32_t time(){return(millis()-receiveTime);}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
#include "Span.h"
|
||||
421
ESP32/HomeSpan-master/src/Network.cpp
Normal file
421
ESP32/HomeSpan-master/src/Network.cpp
Normal file
@@ -0,0 +1,421 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include <DNSServer.h>
|
||||
|
||||
#include "Network.h"
|
||||
#include "HomeSpan.h"
|
||||
#include "Utils.h"
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::scan(){
|
||||
|
||||
int n=WiFi.scanNetworks();
|
||||
|
||||
free(ssidList);
|
||||
ssidList=(char **)HS_CALLOC(n,sizeof(char *));
|
||||
numSSID=0;
|
||||
|
||||
for(int i=0;i<n;i++){
|
||||
boolean found=false;
|
||||
for(int j=0;j<numSSID;j++){
|
||||
if(!strcmp(WiFi.SSID(i).c_str(),ssidList[j]))
|
||||
found=true;
|
||||
}
|
||||
if(!found){
|
||||
ssidList[numSSID]=(char *)HS_CALLOC(WiFi.SSID(i).length()+1,sizeof(char));
|
||||
sprintf(ssidList[numSSID],"%s",WiFi.SSID(i).c_str());
|
||||
numSSID++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::serialConfigure(){
|
||||
|
||||
wifiData.ssid[0]='\0';
|
||||
wifiData.pwd[0]='\0';
|
||||
|
||||
LOG0("*** WiFi Setup - Scanning for Networks...\n\n");
|
||||
|
||||
scan(); // scan for networks
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
LOG0(" %d) %s\n",i+1,ssidList[i]);
|
||||
|
||||
while(!strlen(wifiData.ssid)){
|
||||
LOG0("\n>>> WiFi SSID: ");
|
||||
readSerial(wifiData.ssid,MAX_SSID);
|
||||
if(atoi(wifiData.ssid)>0 && atoi(wifiData.ssid)<=numSSID){
|
||||
strcpy(wifiData.ssid,ssidList[atoi(wifiData.ssid)-1]);
|
||||
}
|
||||
LOG0("%s\n",wifiData.ssid);
|
||||
}
|
||||
|
||||
while(!strlen(wifiData.pwd)){
|
||||
LOG0(">>> WiFi PASS: ");
|
||||
readSerial(wifiData.pwd,MAX_PWD);
|
||||
LOG0("%s\n",mask(wifiData.pwd,2).c_str());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
boolean Network::allowedCode(char *s){
|
||||
return(
|
||||
strcmp(s,"00000000") && strcmp(s,"11111111") && strcmp(s,"22222222") && strcmp(s,"33333333") &&
|
||||
strcmp(s,"44444444") && strcmp(s,"55555555") && strcmp(s,"66666666") && strcmp(s,"77777777") &&
|
||||
strcmp(s,"88888888") && strcmp(s,"99999999") && strcmp(s,"12345678") && strcmp(s,"87654321"));
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::apConfigure(){
|
||||
|
||||
LOG0("*** Starting Access Point: %s / %s\n",apSSID,apPassword);
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_STARTED),HS_AP_STARTED)
|
||||
|
||||
LOG0("\nScanning for Networks...\n\n");
|
||||
|
||||
scan(); // scan for networks
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
LOG0(" %d) %s\n",i+1,ssidList[i]);
|
||||
|
||||
WiFiServer apServer(80);
|
||||
client=0;
|
||||
|
||||
const byte DNS_PORT = 53;
|
||||
DNSServer dnsServer;
|
||||
IPAddress apIP(192, 168, 4, 1);
|
||||
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(apSSID,apPassword); // start access point
|
||||
dnsServer.start(DNS_PORT, "*", apIP); // start DNS server that resolves every request to the address of this device
|
||||
apServer.begin();
|
||||
|
||||
alarmTimeOut=millis()+lifetime; // Access Point will shut down when alarmTimeOut is reached
|
||||
apStatus=0; // status will be "timed out" unless changed
|
||||
|
||||
LOG0("\nReady.\n");
|
||||
|
||||
while(1){ // loop until we get timed out (which will be accelerated if save/cancel selected)
|
||||
|
||||
if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){
|
||||
LOG0("\n*** Access Point Terminated. Restarting...\n\n");
|
||||
STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED)
|
||||
homeSpan.controlButton->wait();
|
||||
homeSpan.reboot();
|
||||
}
|
||||
|
||||
if(millis()>alarmTimeOut){
|
||||
WiFi.softAPdisconnect(true); // terminate connections and shut down captive access point
|
||||
delay(100);
|
||||
if(apStatus==1){
|
||||
LOG0("\n*** Access Point: Exiting and Saving Settings\n\n");
|
||||
return;
|
||||
} else {
|
||||
if(apStatus==0)
|
||||
LOG0("\n*** Access Point: Timed Out (%ld seconds).",lifetime/1000);
|
||||
else
|
||||
LOG0("\n*** Access Point: Configuration Cancelled.");
|
||||
LOG0(" Restarting...\n\n");
|
||||
STATUS_UPDATE(start(LED_ALERT),HS_AP_TERMINATED)
|
||||
homeSpan.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
dnsServer.processNextRequest();
|
||||
|
||||
if(client=apServer.available()){ // found a new HTTP client
|
||||
LOG2("=======================================\n");
|
||||
LOG1("** Access Point Client Connected: (");
|
||||
LOG1(millis()/1000);
|
||||
LOG1(" sec) ");
|
||||
LOG1(client.remoteIP());
|
||||
LOG1("\n");
|
||||
LOG2("\n");
|
||||
delay(50); // pause to allow data buffer to begin to populate
|
||||
}
|
||||
|
||||
if(client && client.available()){ // if connection exists and data is available
|
||||
|
||||
LOG2("<<<<<<<<< ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" <<<<<<<<<\n");
|
||||
|
||||
int messageSize=client.available();
|
||||
|
||||
if(messageSize>MAX_HTTP){ // exceeded maximum number of bytes allowed
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: HTTP message of %d bytes exceeds maximum allowed (%d)\n\n",messageSize,MAX_HTTP);
|
||||
continue;
|
||||
}
|
||||
|
||||
TempBuffer<uint8_t> httpBuf(messageSize+1); // leave room for null character added below
|
||||
|
||||
int nBytes=client.read(httpBuf,messageSize); // read all available bytes up to maximum allowed+1
|
||||
|
||||
if(nBytes!=messageSize || client.available()!=0){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: HTTP message not read correctly. Expected %d bytes, read %d bytes, %d bytes remaining\n\n",messageSize,nBytes,client.available());
|
||||
continue;
|
||||
}
|
||||
|
||||
httpBuf[nBytes]='\0'; // add null character to enable string functions
|
||||
char *body=(char *)httpBuf.get(); // char pointer to start of HTTP Body
|
||||
char *p; // char pointer used for searches
|
||||
|
||||
if(!(p=strstr((char *)httpBuf.get(),"\r\n\r\n"))){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: Malformed HTTP request (can't find blank line indicating end of BODY)\n\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
*p='\0'; // null-terminate end of HTTP Body to faciliate additional string processing
|
||||
uint8_t *content=(uint8_t *)p+4; // byte pointer to start of optional HTTP Content
|
||||
int cLen=0; // length of optional HTTP Content
|
||||
|
||||
if((p=strstr(body,"Content-Length: "))) // Content-Length is specified
|
||||
cLen=atoi(p+16);
|
||||
if(nBytes!=strlen(body)+4+cLen){
|
||||
badRequestError();
|
||||
LOG0("\n*** ERROR: Malformed HTTP request (Content-Length plus Body Length does not equal total number of bytes read)\n\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG2(body);
|
||||
LOG2("\n------------ END BODY! ------------\n");
|
||||
|
||||
content[cLen]='\0'; // add a trailing null on end of any contents, which should always be text-based
|
||||
|
||||
processRequest(body, (char *)content); // process request
|
||||
|
||||
LOG2("\n");
|
||||
|
||||
} // process Client
|
||||
|
||||
} // while 1
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
void Network::processRequest(char *body, char *formData){
|
||||
|
||||
String responseHead="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n";
|
||||
|
||||
String responseBody="<html><meta charset=\"utf-8\"><head><style>"
|
||||
"p{font-size:300%; margin:1em}"
|
||||
"label{font-size:300%; margin:1em}"
|
||||
"input{font-size:250%; margin:1em}"
|
||||
"button{font-size:250%; margin:1em}"
|
||||
"</style></head>"
|
||||
"<body style=\"background-color:lightyellow;\">"
|
||||
"<center><p><b>HomeSpan Setup</b></p></center>";
|
||||
|
||||
if(!strncmp(body,"POST /configure ",16) && // POST CONFIGURE
|
||||
strstr(body,"Content-Type: application/x-www-form-urlencoded")){ // check that content is from a form
|
||||
|
||||
LOG2(formData); // print form data
|
||||
LOG2("\n------------ END DATA! ------------\n");
|
||||
|
||||
LOG1("In Post Configure...\n");
|
||||
|
||||
getFormValue(formData,"network",wifiData.ssid,MAX_SSID);
|
||||
getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD);
|
||||
|
||||
STATUS_UPDATE(start(LED_WIFI_CONNECTING),HS_WIFI_CONNECTING)
|
||||
|
||||
responseBody+="<meta http-equiv = \"refresh\" content = \"" + String(waitTime) + "; url = /wifi-status\" />"
|
||||
"<p>Initiating WiFi connection to:</p><p><b>" + String(wifiData.ssid) + "</p>";
|
||||
|
||||
WiFi.begin(wifiData.ssid,wifiData.pwd);
|
||||
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"POST /save ",11)){ // GET SAVE
|
||||
getFormValue(formData,"code",setupCode,8);
|
||||
|
||||
if(allowedCode(setupCode)){
|
||||
responseBody+="<p><b>Settings saved!</b></p><p>Restarting HomeSpan.</p><p>Closing window...</p>";
|
||||
alarmTimeOut=millis()+2000;
|
||||
apStatus=1;
|
||||
|
||||
} else {
|
||||
responseBody+="<meta http-equiv = \"refresh\" content = \"4; url = /wifi-status\" />"
|
||||
"<p><b>Disallowed Setup Code - too simple!</b></p><p>Returning to configuration page...</p>";
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"GET /cancel ",12)){ // GET CANCEL
|
||||
responseBody+="<p><b>Configuration Canceled!</b></p><p>Restarting HomeSpan.</p><p>Closing window...</p>";
|
||||
alarmTimeOut=millis()+2000;
|
||||
apStatus=-1;
|
||||
} else
|
||||
|
||||
if(!strncmp(body,"GET /wifi-status ",17)){ // GET WIFI-STATUS
|
||||
|
||||
LOG1("In Get WiFi Status...\n");
|
||||
|
||||
if(WiFi.status()!=WL_CONNECTED){
|
||||
waitTime+=2;
|
||||
if(waitTime==12)
|
||||
waitTime=2;
|
||||
responseHead+="Refresh: " + String(waitTime) + "\r\n";
|
||||
responseBody+="<p>Re-initiating connection to:</p><p><b>" + String(wifiData.ssid) + "</b></p>";
|
||||
responseBody+="<p>(waiting " + String(waitTime) + " seconds to check for response)</p>";
|
||||
responseBody+="<p>Access Point termination in " + String((alarmTimeOut-millis())/1000) + " seconds.</p>";
|
||||
responseBody+="<center><button onclick=\"document.location='/hotspot-detect.html'\">Cancel</button></center>";
|
||||
WiFi.begin(wifiData.ssid,wifiData.pwd);
|
||||
|
||||
} else {
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED)
|
||||
|
||||
responseBody+="<p>SUCCESS! Connected to:</p><p><b>" + String(wifiData.ssid) + "</b></p>";
|
||||
responseBody+="<p>You may enter new 8-digit Setup Code below, or leave blank to retain existing code.</p>";
|
||||
|
||||
responseBody+="<form action=\"/save\" method=\"post\">"
|
||||
"<label for=\"code\">Setup Code:</label>"
|
||||
"<center><input size=\"32\" type=\"tel\" id=\"code\" name=\"code\" placeholder=\"12345678\" pattern=\"[0-9]{8}\" maxlength=8></center>"
|
||||
"<center><input style=\"font-size:300%\" type=\"submit\" value=\"SAVE Settings\"></center>"
|
||||
"</form>";
|
||||
|
||||
responseBody+="<center><button style=\"font-size:300%\" onclick=\"document.location='/cancel'\">CANCEL Configuration</button></center>";
|
||||
}
|
||||
|
||||
} else
|
||||
|
||||
if(!strstr(body,"wispr") && !strncmp(body,"GET /hotspot-detect.html ",25)){ // GET LANDING-PAGE, but only if request does NOT contain "wispr" user agent
|
||||
|
||||
LOG1("In Landing Page...\n");
|
||||
|
||||
STATUS_UPDATE(start(LED_AP_CONNECTED),HS_AP_CONNECTED)
|
||||
waitTime=2;
|
||||
|
||||
responseBody+="<p>Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.</p>"
|
||||
"<p>The LED on this device should be <em>double-blinking</em> during this configuration.</p>"
|
||||
"<form action=\"/configure\" method=\"post\">"
|
||||
"<label for=\"ssid\">WiFi Network:</label>"
|
||||
"<center><input size=\"32\" list=\"network\" name=\"network\" placeholder=\"Choose or Type\" required maxlength=" + String(MAX_SSID) + "></center>"
|
||||
"<datalist id=\"network\">";
|
||||
|
||||
for(int i=0;i<numSSID;i++)
|
||||
responseBody+="<option value=\"" + String(ssidList[i]) + "\">" + String(ssidList[i]) + "</option>";
|
||||
|
||||
responseBody+="</datalist><br><br>"
|
||||
"<label for=\"pwd\">WiFi Password:</label>"
|
||||
"<center><input size=\"32\" type=\"password\" id=\"pwd\" name=\"pwd\" required maxlength=" + String(MAX_PWD) + "></center>"
|
||||
"<br><br>";
|
||||
|
||||
responseBody+="<center><input style=\"font-size:300%\" type=\"submit\" value=\"SUBMIT\"></center>"
|
||||
"</form>";
|
||||
|
||||
responseBody+="<center><button style=\"font-size:300%\" onclick=\"document.location='/cancel'\">CANCEL Configuration</button></center>";
|
||||
|
||||
}
|
||||
|
||||
responseHead+="\r\n"; // add blank line between reponse header and body
|
||||
responseBody+="</body></html>"; // close out body and html tags
|
||||
|
||||
LOG2("\n>>>>>>>>>> ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" >>>>>>>>>>\n");
|
||||
LOG2(responseHead);
|
||||
LOG2(responseBody);
|
||||
LOG2("\n");
|
||||
client.print(responseHead);
|
||||
client.print(responseBody);
|
||||
LOG2("------------ SENT! --------------\n");
|
||||
|
||||
} // processRequest
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Network::getFormValue(char *formData, const char *tag, char *value, int maxSize){
|
||||
|
||||
char *s=strstr(formData,tag); // find start of tag
|
||||
|
||||
if(!s) // if not found, return -1
|
||||
return(-1);
|
||||
|
||||
char *v=index(s,'='); // find '='
|
||||
|
||||
if(!v) // if not found, return -1 (this should not happen)
|
||||
return(-1);
|
||||
|
||||
v++; // point to begining of value
|
||||
int len=0; // track length of value
|
||||
|
||||
while(*v!='\0' && *v!='&' && len<maxSize){ // copy the value until null, '&', or maxSize is reached
|
||||
if(*v=='%'){ // this is an escaped character of form %XX
|
||||
v++;
|
||||
sscanf(v,"%2x",(unsigned int *)value++);
|
||||
v+=2;
|
||||
} else {
|
||||
*value++=(*v=='+'?' ':*v); // HTML Forms use '+' for spaces (and '+' signs are escaped)
|
||||
v++;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
*value='\0'; // add terminating null
|
||||
return(len);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Network::badRequestError(){
|
||||
|
||||
char s[]="HTTP/1.1 400 Bad Request\r\n\r\n";
|
||||
LOG2("\n>>>>>>>>>> ");
|
||||
LOG2(client.remoteIP());
|
||||
LOG2(" >>>>>>>>>>\n");
|
||||
LOG2(s);
|
||||
client.print(s);
|
||||
LOG2("------------ SENT! --------------\n");
|
||||
|
||||
delay(1);
|
||||
client.stop();
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
69
ESP32/HomeSpan-master/src/Network.h
Normal file
69
ESP32/HomeSpan-master/src/Network.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <WiFi.h>
|
||||
#include "Settings.h"
|
||||
|
||||
const int MAX_SSID=32; // max number of characters in WiFi SSID
|
||||
const int MAX_PWD=64; // max number of characters in WiFi Password
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
struct Network {
|
||||
|
||||
const int MAX_HTTP=4095; // max number of bytes in HTTP message
|
||||
|
||||
const char *apSSID=DEFAULT_AP_SSID; // Access Point SSID
|
||||
const char *apPassword=DEFAULT_AP_PASSWORD; // Access Point password (does not need to be secret - only used to ensure encrypted WiFi connection)
|
||||
unsigned long lifetime=DEFAULT_AP_TIMEOUT*1000; // length of time (in milliseconds) to keep Access Point alive before shutting down and restarting
|
||||
|
||||
char **ssidList=NULL;
|
||||
int numSSID;
|
||||
|
||||
WiFiClient client; // client used for HTTP calls
|
||||
int waitTime; // time to wait between HTTP refreshed when checking for WiFi connection
|
||||
unsigned long alarmTimeOut; // alarm time after which access point is shut down and HomeSpan is re-started
|
||||
int apStatus; // tracks access point status (0=timed-out, -1=cancel, 1=save)
|
||||
|
||||
struct {
|
||||
char ssid[MAX_SSID+1]="";
|
||||
char pwd[MAX_PWD+1]="";
|
||||
} wifiData;
|
||||
|
||||
char setupCode[8+1];
|
||||
|
||||
void scan(); // scan for WiFi networks and save only those with unique SSIDs
|
||||
void serialConfigure(); // configure homeSpan WiFi from serial monitor
|
||||
boolean allowedCode(char *s); // checks if Setup Code is allowed (HAP defines a list of disallowed codes)
|
||||
void apConfigure(); // configure homeSpan WiFi and Setup Code using temporary Captive Access Point; only returns if sucessful, else ESP restarts
|
||||
void processRequest(char *body, char *formData); // process the HTTP request
|
||||
int getFormValue(char *formData, const char *tag, char *value, int maxSize); // search for 'tag' in 'formData' and copy result into 'value' up to 'maxSize' characters; returns number of characters, else -1 if 'tag' not found
|
||||
int badRequestError(); // return 400 error
|
||||
|
||||
};
|
||||
64
ESP32/HomeSpan-master/src/PSRAM.h
Normal file
64
ESP32/HomeSpan-master/src/PSRAM.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef HS_MALLOC
|
||||
|
||||
#if defined(BOARD_HAS_PSRAM)
|
||||
#define HS_MALLOC ps_malloc
|
||||
#define HS_CALLOC ps_calloc
|
||||
#define HS_REALLOC ps_realloc
|
||||
#define ps_new(X) new(ps_malloc(sizeof(X)))X
|
||||
#else
|
||||
#define HS_MALLOC malloc
|
||||
#define HS_CALLOC calloc
|
||||
#define HS_REALLOC realloc
|
||||
#define ps_new(X) new X
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
struct Mallocator {
|
||||
typedef T value_type;
|
||||
Mallocator() = default;
|
||||
template <class U> constexpr Mallocator(const Mallocator<U>&) {}
|
||||
[[nodiscard]] T* allocate(std::size_t n) {
|
||||
auto p = static_cast<T*>(HS_MALLOC(n*sizeof(T)));
|
||||
if(p==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",n*sizeof(T));
|
||||
while(1);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
|
||||
};
|
||||
template <class T, class U>
|
||||
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
|
||||
template <class T, class U>
|
||||
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
|
||||
|
||||
#endif
|
||||
260
ESP32/HomeSpan-master/src/SRP.cpp
Normal file
260
ESP32/HomeSpan-master/src/SRP.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include <sodium.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "SRP.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SRP6A::SRP6A(){
|
||||
|
||||
// initialize MPI structures
|
||||
|
||||
mbedtls_mpi_init(&N);
|
||||
mbedtls_mpi_init(&g);
|
||||
mbedtls_mpi_init(&s);
|
||||
mbedtls_mpi_init(&x);
|
||||
mbedtls_mpi_init(&v);
|
||||
mbedtls_mpi_init(&A);
|
||||
mbedtls_mpi_init(&b);
|
||||
mbedtls_mpi_init(&B);
|
||||
mbedtls_mpi_init(&S);
|
||||
mbedtls_mpi_init(&k);
|
||||
mbedtls_mpi_init(&u);
|
||||
mbedtls_mpi_init(&_rr);
|
||||
mbedtls_mpi_init(&t1);
|
||||
mbedtls_mpi_init(&t2);
|
||||
mbedtls_mpi_init(&t3);
|
||||
|
||||
// load N and g into MPI structures
|
||||
|
||||
mbedtls_mpi_read_string(&N,16,N3072);
|
||||
mbedtls_mpi_lset(&g,g3072);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
SRP6A::~SRP6A(){
|
||||
|
||||
mbedtls_mpi_free(&N);
|
||||
mbedtls_mpi_free(&g);
|
||||
mbedtls_mpi_free(&s);
|
||||
mbedtls_mpi_free(&x);
|
||||
mbedtls_mpi_free(&v);
|
||||
mbedtls_mpi_free(&A);
|
||||
mbedtls_mpi_free(&b);
|
||||
mbedtls_mpi_free(&B);
|
||||
mbedtls_mpi_free(&S);
|
||||
mbedtls_mpi_free(&k);
|
||||
mbedtls_mpi_free(&u);
|
||||
mbedtls_mpi_free(&_rr);
|
||||
mbedtls_mpi_free(&t1);
|
||||
mbedtls_mpi_free(&t2);
|
||||
mbedtls_mpi_free(&t3);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createVerifyCode(const char *setupCode, Verification *vData){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(80); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
char *icp; // storage for I:P
|
||||
|
||||
// generate random salt, s
|
||||
|
||||
randombytes_buf(vData->salt,16); // generate 16 random bytes for salt
|
||||
|
||||
// create I:P
|
||||
|
||||
asprintf(&icp,"%s:%.3s-%.2s-%.3s",I,setupCode,setupCode+3,setupCode+5);
|
||||
|
||||
// compute x = SHA512( s | SHA512( I | ":" | P ) )
|
||||
|
||||
memcpy(tBuf,vData->salt,16); // write salt into first 16 bytes of staging buffer
|
||||
mbedtls_sha512_ret((uint8_t *)icp,strlen(icp),tBuf+16,0); // create hash of username:password and write into last 64 bytes of staging buffer
|
||||
mbedtls_sha512_ret(tBuf,80,tHash,0); // create second hash of salted, hashed username:password
|
||||
mbedtls_mpi_read_binary(&x,tHash,64); // load hash result into x
|
||||
|
||||
// compute v = g^x %N
|
||||
|
||||
mbedtls_mpi_exp_mod(&v,&g,&x,&N,&_rr); // create verifier, v (_rr is an internal "helper" structure that mbedtls uses to speed up subsequent exponential calculations)
|
||||
mbedtls_mpi_write_binary(&v,vData->verifyCode,384); // write v into verifyCode (padding with initial zeros is less than 384 bytes)
|
||||
|
||||
free(icp);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createPublicKey(const Verification *vData, uint8_t *publicKey){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
TempBuffer<uint8_t> privateKey(32); // temporary buffer for generating private key random numbers
|
||||
|
||||
// load stored salt, s, and verification code, v
|
||||
|
||||
mbedtls_mpi_read_binary(&s,vData->salt,16); // load salt into s for use in later steps
|
||||
mbedtls_mpi_read_binary(&v,vData->verifyCode,384); // load verifyCode into v for use below
|
||||
|
||||
// generate random private key, b
|
||||
|
||||
randombytes_buf(privateKey,32); // generate 32 random bytes for private key
|
||||
mbedtls_mpi_read_binary(&b,privateKey,32); // load private key into b
|
||||
|
||||
// compute k = SHA512( N | PAD(g) )
|
||||
|
||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into first half of staging buffer
|
||||
mbedtls_mpi_write_binary(&g,tBuf+384,384); // write g into second half of staging buffer (fully padded with leading zeros)
|
||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||
mbedtls_mpi_read_binary(&k,tHash,64); // load hash result into k
|
||||
|
||||
// compute B = (k*v + g^b) %N
|
||||
|
||||
mbedtls_mpi_mul_mpi(&t1,&k,&v); // t1 = k*v
|
||||
mbedtls_mpi_exp_mod(&t2,&g,&b,&N,&_rr); // t2 = g^b %N
|
||||
mbedtls_mpi_add_mpi(&t3,&t1,&t2); // t3 = t1 + t2
|
||||
mbedtls_mpi_mod_mpi(&B,&t3,&N); // B = t3 %N = ACCESSORY PUBLIC KEY
|
||||
|
||||
mbedtls_mpi_write_binary(&B,publicKey,384); // write B into publicKey (padding with initial zeros is less than 384 bytes)
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createSessionKey(const uint8_t *publicKey, size_t len){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(768); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
|
||||
mbedtls_mpi_read_binary(&A,publicKey,len); // load client PublicKey into A
|
||||
|
||||
// compute u = SHA512( PAD(A) | PAD(B) )
|
||||
|
||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into first half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||
mbedtls_mpi_write_binary(&B,tBuf+384,384); // write B into second half of staging buffer (padding with initial zeros is less than 384 bytes)
|
||||
mbedtls_sha512_ret(tBuf,768,tHash,0); // create hash of data
|
||||
mbedtls_mpi_read_binary(&u,tHash,64); // load hash result into mpi structure u
|
||||
|
||||
// compute S = (A * v^u)^b %N
|
||||
|
||||
mbedtls_mpi_exp_mod(&t1,&v,&u,&N,&_rr); // t1 = v^u %N
|
||||
mbedtls_mpi_mul_mpi(&t2,&A,&t1); // t2 = A*t1
|
||||
mbedtls_mpi_mod_mpi(&t1,&t2,&N); // t1 = t2 %N (this is needed to reduce size of t2 before next calculation)
|
||||
mbedtls_mpi_exp_mod(&S,&t1,&b,&N,&_rr); // S = t1^b %N
|
||||
|
||||
// compute K = SHA512( PAD(S) )
|
||||
|
||||
mbedtls_mpi_write_binary(&S,tBuf,384); // write S into staging buffer (only first half of buffer will be used)
|
||||
mbedtls_sha512_ret(tBuf,384,K,0); // create hash of data - this is the SRP SHARED SESSION KEY, K
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
|
||||
int SRP6A::verifyClientProof(const uint8_t *proof){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(976); // temporary buffer for staging
|
||||
TempBuffer<uint8_t> tHash(64); // temporary buffer for storing SHA-512 results
|
||||
|
||||
memcpy(M1,proof,64); // load client Proof into M1
|
||||
|
||||
size_t count=0; // total number of bytes for final hash
|
||||
size_t sLen;
|
||||
|
||||
// compute M1V = SHA512( SHA512(N) xor SHA512(g) | SHA512(I) | s | A | B | K )
|
||||
|
||||
mbedtls_mpi_write_binary(&N,tBuf,384); // write N into staging buffer
|
||||
mbedtls_sha512_ret(tBuf,384,tHash,0); // create hash of data
|
||||
mbedtls_sha512_ret(&g3072,1,tBuf,0); // create hash of g, but place output directly into staging buffer
|
||||
|
||||
for(int i=0;i<64;i++) // H(g) -> H(g) XOR H(N), with results in first 64 bytes of staging buffer
|
||||
tBuf[i]^=tHash[i];
|
||||
|
||||
mbedtls_sha512_ret((uint8_t *)I,strlen(I),tBuf+64,0); // create hash of userName and concatenate result to end of staging buffer
|
||||
|
||||
mbedtls_mpi_write_binary(&s,tBuf+128,16); // concatenate s to staging buffer
|
||||
|
||||
sLen=mbedtls_mpi_size(&A); // get actual size of A
|
||||
mbedtls_mpi_write_binary(&A,tBuf+144,sLen); // concatenate A to staging buffer. Note A is NOT padded with leading zeros (so may be less than 384 bytes)
|
||||
count=144+sLen; // total bytes written to staging buffer so far
|
||||
|
||||
sLen=mbedtls_mpi_size(&B); // get actual size of B
|
||||
mbedtls_mpi_write_binary(&B,tBuf+count,sLen); // concatenate B to staging buffer. Note B is NOT padded with leading zeros (so may be less than 384 bytes)
|
||||
count+=sLen; // increment total bytes written to staging buffer
|
||||
|
||||
memcpy(tBuf+count,K,64); // concatenate K to staging buffer (should always be 64 bytes since it is a hashed value)
|
||||
count+=64; // final total of bytes written to staging buffer
|
||||
|
||||
mbedtls_sha512_ret(tBuf,count,tHash,0); // create hash of data - this is M1V
|
||||
|
||||
if(!memcmp(M1,tHash,64)) // check that client Proof M1 matches M1V
|
||||
return(1); // success - proof from HAP Client is verified
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::createAccProof(uint8_t *proof){
|
||||
|
||||
TempBuffer<uint8_t> tBuf(512); // temporary buffer for staging
|
||||
|
||||
// compute M2 = SHA512( A | M1 | K )
|
||||
|
||||
mbedtls_mpi_write_binary(&A,tBuf,384); // write A into staging buffer
|
||||
memcpy(tBuf+384,M1,64); // concatenate M1 (now verified) to staging buffer
|
||||
memcpy(tBuf+448,K,64); // concatenate K to staging buffer
|
||||
mbedtls_sha512_ret(tBuf,512,proof,0); // create hash of data writing directly to proof - this is M2
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void SRP6A::print(mbedtls_mpi *mpi){
|
||||
|
||||
size_t sLen;
|
||||
|
||||
mbedtls_mpi_write_string(mpi,16,NULL,0,&sLen);
|
||||
TempBuffer<char> sBuf(sLen);
|
||||
mbedtls_mpi_write_string(mpi,16,sBuf,sLen,&sLen);
|
||||
|
||||
Serial.printf("%d %s\n",(sLen-1)/2,sBuf.get()); // subtract 1 for null-terminator, and then divide by 2 to get number of bytes (e.g. 4F = 2 characters, but represents just one mpi byte)
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
constexpr char SRP6A::N3072[];
|
||||
constexpr char SRP6A::I[];
|
||||
const uint8_t SRP6A::g3072;
|
||||
102
ESP32/HomeSpan-master/src/SRP.h
Normal file
102
ESP32/HomeSpan-master/src/SRP.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mbedtls/sha512.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Pair-Setup Code Verification Data and Salt
|
||||
|
||||
struct Verification {
|
||||
uint8_t salt[16];
|
||||
uint8_t verifyCode[384];
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// SRP-6A Structure from RFC 5054 (Nov 2007)
|
||||
// ** HAP uses N=3072-bit Group specified in RFC 5054 with Generator g=5
|
||||
// ** HAP replaces H=SHA-1 with H=SHA-512 (HAP Section 5.5)
|
||||
//
|
||||
// I = SRP-6A username, defined by HAP to be the word "Pair-Setup"
|
||||
// P = SRP-6A password, defined to be equal to the accessory's 8-digit setup code in the format "XXX-XX-XXX"
|
||||
|
||||
struct SRP6A {
|
||||
|
||||
static constexpr char N3072[]="FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74"
|
||||
"020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437"
|
||||
"4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"
|
||||
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05"
|
||||
"98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB"
|
||||
"9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"
|
||||
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
|
||||
"3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33"
|
||||
"A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"
|
||||
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864"
|
||||
"D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2"
|
||||
"08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
|
||||
|
||||
static const uint8_t g3072=5;
|
||||
static constexpr char I[]="Pair-Setup";
|
||||
|
||||
mbedtls_mpi N; // N - 3072-bit Group pre-defined prime used for all SRP-6A calculations (384 bytes)
|
||||
mbedtls_mpi g; // g - pre-defined generator for the specified 3072-bit Group (g=5)
|
||||
mbedtls_mpi k; // k = H(N | PAD(g)) - SRP-6A multiplier (which is different from versions SRP-6 or SRP-3)
|
||||
mbedtls_mpi s; // s - randomly-generated salt (16 bytes)
|
||||
mbedtls_mpi x; // x = H(s | H(I | ":" | P)) - salted, double-hash of username and password (64 bytes)
|
||||
mbedtls_mpi v; // v = g^x %N - SRP-6A verifier (max 384 bytes)
|
||||
mbedtls_mpi b; // b - randomly-generated private key for this HAP accessory (i.e. the SRP Server) (32 bytes)
|
||||
mbedtls_mpi B; // B = k*v + g^b %N - public key for this accessory (max 384 bytes)
|
||||
mbedtls_mpi A; // A - public key RECEIVED from HAP Client (max 384 bytes)
|
||||
mbedtls_mpi u; // u = H(PAD(A) | PAB(B)) - "u-factor" (64 bytes)
|
||||
mbedtls_mpi S; // S = (A*v^u)^b %N - SRP shared "premaster" key, based on accessory private key and client public key (max 384 bytes)
|
||||
uint8_t K[64]; // K = H(S) - SRP SHARED SECRET KEY (64 bytes)
|
||||
uint8_t M1[64]; // M1 - proof RECEIVED from HAP Client (64 bytes)
|
||||
mbedtls_mpi t1; // temp1 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi t2; // temp2 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi t3; // temp3 - temporary mpi structures for intermediate results
|
||||
mbedtls_mpi _rr; // _rr - temporary "helper" for large exponential modulus calculations
|
||||
|
||||
|
||||
SRP6A(); // initializes N, G, and computes k
|
||||
~SRP6A();
|
||||
|
||||
void *operator new(size_t size){return(HS_MALLOC(size));} // override new operator to use PSRAM when available
|
||||
|
||||
void createVerifyCode(const char *setupCode, Verification *vData); // generates random s and computes v; writes back resulting Verification Data
|
||||
void createPublicKey(const Verification *vData, uint8_t *publicKey); // generates random b and computes k and B; writes back resulting Accessory Public Key
|
||||
void createSessionKey(const uint8_t *publicKey, size_t len); // computes u, S, and K from Client Public Key, A (of variable length)
|
||||
int verifyClientProof(const uint8_t *proof); // verifies Client Proof, M1, received from HAP client (return 1 on success, 0 on failure)
|
||||
void createAccProof(uint8_t *proof); // computes M2; write back resulting Accessory Proof
|
||||
|
||||
void print(mbedtls_mpi *mpi); // prints size of mpi (in bytes), followed by the mpi itself (as a hex character string)
|
||||
|
||||
};
|
||||
116
ESP32/HomeSpan-master/src/Settings.h
Normal file
116
ESP32/HomeSpan-master/src/Settings.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// DEFAULT SETTINGS //
|
||||
|
||||
#define DEFAULT_CATEGORY Category::Lighting // change with optional first argument in homeSpan.begin()
|
||||
#define DEFAULT_DISPLAY_NAME "HomeSpan Server" // change with optional second argument in homeSpan.begin()
|
||||
#define DEFAULT_HOST_NAME "HomeSpan" // change with optional third argument in homeSpan.begin()
|
||||
#define DEFAULT_MODEL_NAME "HomeSpan-ESP32" // change with optional fourth argument in homeSpan.begin()
|
||||
|
||||
#define DEFAULT_SETUP_CODE "46637726" // changed during network setup or with 'S' command
|
||||
|
||||
#define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID);
|
||||
|
||||
#define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(ssid)
|
||||
#define DEFAULT_AP_PASSWORD "homespan" // change with homeSpan.setApPassword(pwd)
|
||||
#define DEFAULT_OTA_PASSWORD "homespan-ota" // change with 'O' command
|
||||
|
||||
#define DEFAULT_AP_TIMEOUT 300 // change with homeSpan.setApTimeout(nSeconds)
|
||||
#define DEFAULT_COMMAND_TIMEOUT 120 // change with homeSpan.setCommandTimeout(nSeconds)
|
||||
|
||||
#define DEFAULT_LOG_LEVEL 0 // change with homeSpan.setLogLevel(level)
|
||||
|
||||
#define DEFAULT_TCP_PORT 80 // change with homeSpan.setPort(port);
|
||||
|
||||
#define DEFAULT_WEBLOG_URL "status" // change with optional fourth argument in homeSpan.enableWebLog()
|
||||
|
||||
#define DEFAULT_LOW_MEM_THRESHOLD 80000 // default low watermark memory (for internal RAM) threshold that triggers warning
|
||||
|
||||
#define DEFAULT_REBOOT_CALLBACK_TIME 5000 // default time (in milliseconds) to check for reboot callback
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// OTA PARTITION INFO //
|
||||
|
||||
#define HOMESPAN_MAGIC_COOKIE "HomeSpanMagicCookie##2022"
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// STATUS LED SETTINGS //
|
||||
|
||||
#define LED_WIFI_NEEDED 300,0.5,1,2700 // slow single-blink
|
||||
#define LED_PAIRING_NEEDED 300,0.5,2,2400 // slow double-blink
|
||||
#define LED_ALERT 100 // rapid flashing
|
||||
#define LED_WIFI_CONNECTING 2000 // slow flashing
|
||||
#define LED_AP_STARTED 100,0.5,2,300 // rapid double-blink
|
||||
#define LED_AP_CONNECTED 300,0.5,2,400 // medium double-blink
|
||||
#define LED_OTA_STARTED 300,0.5,3,400 // medium triple-blink
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// Message Log Level Control Macros //
|
||||
// 0=Minimal, 1=Informative, 2=All //
|
||||
|
||||
#define LOG0(format,...) do{ if(homeSpan.getLogLevel()>=0)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
#define LOG1(format,...) do{ if(homeSpan.getLogLevel()>=1)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
#define LOG2(format,...) do{ if(homeSpan.getLogLevel()>=2)Serial.print ##__VA_OPT__(f)(format __VA_OPT__(,) __VA_ARGS__); }while(0)
|
||||
|
||||
#define WEBLOG(format,...) homeSpan.addWebLog(false, format __VA_OPT__(,) __VA_ARGS__);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Types of Accessory Categories //
|
||||
// Reference: HAP Section 13 //
|
||||
|
||||
enum class Category {
|
||||
Other=1,
|
||||
Bridges=2,
|
||||
Fans=3,
|
||||
GarageDoorOpeners=4,
|
||||
Lighting=5,
|
||||
Locks=6,
|
||||
Outlets=7,
|
||||
Switches=8,
|
||||
Thermostats=9,
|
||||
Sensors=10,
|
||||
SecuritySystems=11,
|
||||
Doors=12,
|
||||
Windows=13,
|
||||
WindowCoverings=14,
|
||||
ProgrammableSwitches=15,
|
||||
IPCameras=17,
|
||||
VideoDoorbells=18,
|
||||
AirPurifiers=19,
|
||||
Heaters=20,
|
||||
AirConditioners=21,
|
||||
Humidifiers=22,
|
||||
Dehumidifiers=23,
|
||||
Sprinklers=28,
|
||||
Faucets=29,
|
||||
ShowerSystems=30,
|
||||
Television=31
|
||||
};
|
||||
636
ESP32/HomeSpan-master/src/Span.h
Normal file
636
ESP32/HomeSpan-master/src/Span.h
Normal file
@@ -0,0 +1,636 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
///////////////////////////////////
|
||||
// SPAN SERVICES (HAP Chapter 8) //
|
||||
///////////////////////////////////
|
||||
|
||||
// Macros to define Services, along with vectors of required and optional Characteristics for each Span Service structure.
|
||||
//
|
||||
// NOTE: these macros are parsed by an external awk script to auto-generate Services and Characteristics documentation.
|
||||
//
|
||||
// The CREATE_SERV_DEP() macro is the same as the CREATE_SERV() macro, except that it is used for deprecated Services that will not
|
||||
// be included in documentation. The REQ_DEP and OPT_DEP() macros are the same as the REQ() and OPT() macros, except that they are used
|
||||
// for deprecated Characteristics that will not be included in documentation.
|
||||
|
||||
#define CREATE_SERV(NAME,_UUID) struct NAME : SpanService { static constexpr const char *UUID=#_UUID; NAME() : SpanService{#_UUID,#NAME}{
|
||||
#define CREATE_SERV_DEP(NAME,_UUID) struct NAME : SpanService { static constexpr const char *UUID=#_UUID; NAME() : SpanService{#_UUID,#NAME}{
|
||||
#define END_SERV }};
|
||||
|
||||
#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||
#define REQ_DEP(HAPCHAR) req.push_back(&hapChars.HAPCHAR)
|
||||
#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||
#define OPT_DEP(HAPCHAR) opt.push_back(&hapChars.HAPCHAR)
|
||||
|
||||
#define SERVICES_GROUP
|
||||
|
||||
namespace Service {
|
||||
|
||||
SERVICES_GROUP // Mandatory Services
|
||||
|
||||
CREATE_SERV(AccessoryInformation,3E) // Required Identification Information. For each Accessory in a HomeSpan device this must be included as the first Service.
|
||||
REQ(Identify);
|
||||
OPT(Name);
|
||||
OPT(FirmwareRevision);
|
||||
OPT(Manufacturer);
|
||||
OPT(Model);
|
||||
OPT(SerialNumber);
|
||||
OPT(HardwareRevision);
|
||||
OPT_DEP(AccessoryFlags);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Lights, Power, and Switches
|
||||
|
||||
CREATE_SERV(BatteryService,96) // Defines a standalone Battery Service.
|
||||
REQ(BatteryLevel);
|
||||
REQ(ChargingState);
|
||||
REQ(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LightBulb,43) // Defines any type of Light.
|
||||
REQ(On);
|
||||
OPT(Brightness);
|
||||
OPT(Hue);
|
||||
OPT(Saturation);
|
||||
OPT(ColorTemperature);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Outlet,47) // Defines a controllable Outlet used to power any light or appliance.
|
||||
REQ(On);
|
||||
REQ(OutletInUse);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(StatelessProgrammableSwitch,89) // Defines a "Stateless" Programmable Switch that can be used to trigger actions in the Home App.
|
||||
REQ(ProgrammableSwitchEvent);
|
||||
OPT(ServiceLabelIndex);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Switch,49) // Defines a generic Switch.
|
||||
REQ(On);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Heating, Ventilation, and Air Conditioning (HVAC)
|
||||
|
||||
CREATE_SERV(AirPurifier,BB) // Defines a basic Air Purifier with an optional fan and swing mode. Optional Linked Services: <b>FilterMaintenance</b>. Combine with an <b>AirSensor</b> Service for automated operations.
|
||||
REQ(Active);
|
||||
REQ(CurrentAirPurifierState);
|
||||
REQ(TargetAirPurifierState);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Fan,B7) // Defines a Fan. Combine with a <b>LightBulb</b> Service to create a Lighted Ceiling Fan.
|
||||
REQ(Active);
|
||||
OPT(CurrentFanState);
|
||||
OPT(TargetFanState);
|
||||
OPT(RotationDirection);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(FilterMaintenance,BA) // Defines a Filter Maintainence check. Use only as a Linked Service for the <b>AirPurifier</b> Service.
|
||||
REQ(FilterChangeIndication);
|
||||
OPT(FilterLifeLevel);
|
||||
OPT(ResetFilterIndication);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HeaterCooler,BC) // Defines a standalone Heater, Cooler, or combined Heater/Cooler.
|
||||
REQ(Active);
|
||||
REQ(CurrentTemperature);
|
||||
REQ(CurrentHeaterCoolerState);
|
||||
REQ(TargetHeaterCoolerState);
|
||||
OPT(RotationSpeed);
|
||||
OPT(TemperatureDisplayUnits);
|
||||
OPT(SwingMode);
|
||||
OPT(CoolingThresholdTemperature);
|
||||
OPT(HeatingThresholdTemperature);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HumidifierDehumidifier,BD) // Defines a Humidifer, Dehumidifier, or combined Humidifer/Dehumidifier.
|
||||
REQ(Active);
|
||||
REQ(CurrentRelativeHumidity);
|
||||
REQ(CurrentHumidifierDehumidifierState);
|
||||
REQ(TargetHumidifierDehumidifierState);
|
||||
OPT(RelativeHumidityDehumidifierThreshold);
|
||||
OPT(RelativeHumidityHumidifierThreshold);
|
||||
OPT(RotationSpeed);
|
||||
OPT(SwingMode);
|
||||
OPT(WaterLevel);
|
||||
OPT(LockPhysicalControls);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Slat,B9) // Defines a motorized ventilation Slat(s).
|
||||
REQ(CurrentSlatState);
|
||||
REQ(SlatType);
|
||||
OPT(SwingMode);
|
||||
OPT(CurrentTiltAngle);
|
||||
OPT(TargetTiltAngle);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Thermostat,4A) // Defines a Thermostat used to control a furnace, air conditioner, or both.
|
||||
REQ(CurrentHeatingCoolingState);
|
||||
REQ(TargetHeatingCoolingState);
|
||||
REQ(CurrentTemperature);
|
||||
REQ(TargetTemperature);
|
||||
REQ(TemperatureDisplayUnits);
|
||||
OPT(CoolingThresholdTemperature);
|
||||
OPT(CurrentRelativeHumidity);
|
||||
OPT(HeatingThresholdTemperature);
|
||||
OPT(TargetRelativeHumidity);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Standalone Sensors
|
||||
|
||||
CREATE_SERV(AirQualitySensor,8D) // Defines an Air Quality Sensor.
|
||||
REQ(AirQuality);
|
||||
OPT(OzoneDensity);
|
||||
OPT(NitrogenDioxideDensity);
|
||||
OPT(SulphurDioxideDensity);
|
||||
OPT(PM25Density);
|
||||
OPT(PM10Density);
|
||||
OPT(VOCDensity);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(CarbonDioxideSensor,97) // Defines a Carbon Dioxide Sensor.
|
||||
REQ(CarbonDioxideDetected);
|
||||
OPT(CarbonDioxideLevel);
|
||||
OPT(CarbonDioxidePeakLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(CarbonMonoxideSensor,7F) // Defines a Carbon Monoxide Sensor.
|
||||
REQ(CarbonMonoxideDetected);
|
||||
OPT(CarbonMonoxideLevel);
|
||||
OPT(CarbonMonoxidePeakLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(ContactSensor,80) // Defines a Contact Sensor.
|
||||
REQ(ContactSensorState);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(HumiditySensor,82) // Defines a Humidity Sensor.
|
||||
REQ(CurrentRelativeHumidity);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LeakSensor,83) // Defines a Leak Sensor.
|
||||
REQ(LeakDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LightSensor,84) // Defines a Light Sensor.
|
||||
REQ(CurrentAmbientLightLevel);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(MotionSensor,85) // Defines a Motion Sensor.
|
||||
REQ(MotionDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(OccupancySensor,86) // Defines and Occupancy Sensor.
|
||||
REQ(OccupancyDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(SmokeSensor,87) // Defines a Smoke Sensor.
|
||||
REQ(SmokeDetected);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(TemperatureSensor,8A) // Defines a Temperature Sensor.
|
||||
REQ(CurrentTemperature);
|
||||
OPT(StatusActive);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(StatusLowBattery);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Doors, Locks, and Windows
|
||||
|
||||
CREATE_SERV(Door,81) // Defines a motorized Door.
|
||||
REQ(CurrentPosition);
|
||||
REQ(TargetPosition);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Doorbell,121) // Defines a Doorbell. Can be used on a standalone basis or in conjunction with a <b>LockMechanism</b> Service.
|
||||
REQ(ProgrammableSwitchEvent);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(Brightness);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(GarageDoorOpener,41) // Defines a motorized Garage Door Opener.
|
||||
REQ(CurrentDoorState);
|
||||
REQ(TargetDoorState);
|
||||
REQ(ObstructionDetected);
|
||||
OPT(LockCurrentState);
|
||||
OPT(LockTargetState);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(LockMechanism,45) // Defines an electronic Lock.
|
||||
REQ(LockCurrentState);
|
||||
REQ(LockTargetState);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Window,8B) // Defines a motorized Window.
|
||||
REQ(CurrentPosition);
|
||||
REQ(TargetPosition);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(WindowCovering,8C) // Defines a motorized Window Shade, Screen, Awning, etc.
|
||||
REQ(TargetPosition);
|
||||
REQ(CurrentPosition);
|
||||
OPT(CurrentHorizontalTiltAngle);
|
||||
OPT(TargetHorizontalTiltAngle);
|
||||
OPT(CurrentVerticalTiltAngle);
|
||||
OPT(TargetVerticalTiltAngle);
|
||||
OPT(ObstructionDetected);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
OPT_DEP(PositionState);
|
||||
OPT_DEP(HoldPosition);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Water Systems
|
||||
|
||||
CREATE_SERV(Faucet,D7) // Defines the master control for a multi-Valve appliance. Linked Services: <b>Valve</b> (at least one required), and <b>HeaterCooler</b> (optional).
|
||||
REQ(Active);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(IrrigationSystem,CF) // Defines an Irrigation System. Linked Services: <b>Valve</b> Service (at least one required).
|
||||
REQ(Active);
|
||||
REQ(ProgramMode);
|
||||
REQ(InUse);
|
||||
OPT(RemainingDuration);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Valve,D0) // Defines an electronic Valve. Can be used standalone or as a Linked Service for either a <b>Faucet</b> or <b>IrrigationSystem</b> Service.
|
||||
REQ(Active);
|
||||
REQ(InUse);
|
||||
REQ(ValveType);
|
||||
OPT(SetDuration);
|
||||
OPT(RemainingDuration);
|
||||
OPT(IsConfigured);
|
||||
OPT(ServiceLabelIndex);
|
||||
OPT(StatusFault);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Security Systems
|
||||
|
||||
CREATE_SERV(SecuritySystem,7E) // Defines a Security System. Often used in combination with <b>MotionSensor</b> and <b>ContactSensor</b> Services.
|
||||
REQ(SecuritySystemCurrentState);
|
||||
REQ(SecuritySystemTargetState);
|
||||
OPT(SecuritySystemAlarmType);
|
||||
OPT(StatusFault);
|
||||
OPT(StatusTampered);
|
||||
OPT(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Televisions
|
||||
|
||||
CREATE_SERV(InputSource,D9) // Defines an Input Source for a TV. Use only as a Linked Service for the <b>Television</b> Service.
|
||||
REQ(Identifier);
|
||||
OPT(ConfiguredName);
|
||||
OPT(IsConfigured);
|
||||
OPT(CurrentVisibilityState);
|
||||
OPT(TargetVisibilityState);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(Television,D8) // Defines a TV. Optional Linked Services: <b>InputSource</b> and <b>TelevisionSpeaker</b>.
|
||||
REQ(Active);
|
||||
OPT(ActiveIdentifier);
|
||||
OPT(DisplayOrder);
|
||||
OPT(RemoteKey);
|
||||
OPT(PowerModeSelection);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV(TelevisionSpeaker,113) // Defines a Television Speaker that can be controlled via the Remote Control widget on an iPhone. Use only as a Linked Service for the <b>Television</b> Service.
|
||||
REQ(VolumeControlType);
|
||||
REQ(VolumeSelector);
|
||||
OPT(ConfiguredName);
|
||||
END_SERV
|
||||
|
||||
SERVICES_GROUP // Miscellaneous
|
||||
|
||||
CREATE_SERV(ServiceLabel,CC) // Defines a naming scheme for un-nameable Services, such as a <b>StatelessProgrammableSwitch</b>, by Linking them to this Service. When used, those other Services must each include a <b>ServiceLabelIndex</b> Characteristic with a unique value.
|
||||
REQ(ServiceLabelNamespace);
|
||||
END_SERV
|
||||
|
||||
// Deprecated or unsupported Services
|
||||
|
||||
CREATE_SERV_DEP(HAPProtocolInformation,A2)
|
||||
REQ_DEP(Version);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV_DEP(Microphone,112)
|
||||
REQ_DEP(Mute);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
CREATE_SERV_DEP(Speaker,113)
|
||||
REQ_DEP(Mute);
|
||||
OPT_DEP(Volume);
|
||||
OPT_DEP(ConfiguredName);
|
||||
OPT_DEP(Name);
|
||||
END_SERV
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////
|
||||
// SPAN CHARACTERISTICS (HAP Chapter 9) //
|
||||
//////////////////////////////////////////
|
||||
|
||||
// Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and min/max value (not applicable for STRING or BOOL which default to min=0, max=1)
|
||||
|
||||
#define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL,...) \
|
||||
struct HAPCHAR : SpanCharacteristic { __VA_OPT__(enum{) __VA_ARGS__ __VA_OPT__(};) HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init<TYPE>(val,nvsStore,MINVAL,MAXVAL); } };
|
||||
|
||||
namespace Characteristic {
|
||||
|
||||
CREATE_CHAR(UINT32_t,AccessoryFlags,1,1,1); // not applicable for HomeSpan
|
||||
CREATE_CHAR(UINT8_t,Active,0,0,1,INACTIVE,ACTIVE); // indicates if the Service is active/on
|
||||
CREATE_CHAR(UINT32_t,ActiveIdentifier,0,0,255); // numerical Identifier of the <b>InputSource</b> selected in the Home App.
|
||||
CREATE_CHAR(UINT8_t,AirQuality,0,0,5,UNKNOWN,EXCELLENT,GOOD,FAIR,INFERIOR,POOR); // a subjective description
|
||||
CREATE_CHAR(UINT8_t,BatteryLevel,100,0,100); // measured as a percentage
|
||||
CREATE_CHAR(INT_t,Brightness,0,0,100); // measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,CarbonMonoxideLevel,0,0,100); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(FLOAT_t,CarbonMonoxidePeakLevel,0,0,100); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(UINT8_t,CarbonMonoxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||
CREATE_CHAR(FLOAT_t,CarbonDioxideLevel,0,0,100000); // measured on parts per million (ppm)
|
||||
CREATE_CHAR(FLOAT_t,CarbonDioxidePeakLevel,0,0,100000); // measured in parts per million (ppm)
|
||||
CREATE_CHAR(UINT8_t,CarbonDioxideDetected,0,0,1,NORMAL,ABNORMAL); // indicates if abnormal level is detected
|
||||
CREATE_CHAR(UINT8_t,ChargingState,0,0,2,NOT_CHARGING,CHARGING,NOT_CHARGEABLE); // indicates state of battery charging
|
||||
CREATE_CHAR(UINT8_t,ClosedCaptions,0,0,1); // unused by any Service
|
||||
CREATE_CHAR(FLOAT_t,CoolingThresholdTemperature,10,10,35); // cooling turns on when temperature (in Celsius) rises above this threshold
|
||||
CREATE_CHAR(UINT32_t,ColorTemperature,200,140,500); // measured in inverse megaKelvin (= 1,000,000 / Kelvin)
|
||||
CREATE_CHAR(UINT8_t,ContactSensorState,1,0,1,DETECTED,NOT_DETECTED); // indictates if contact is detected (i.e. closed)
|
||||
CREATE_CHAR(STRING_t,ConfiguredName,"unnamed",NULL,NULL); // default display name of this Service
|
||||
CREATE_CHAR(FLOAT_t,CurrentAmbientLightLevel,1,0.0001,100000); // measured in Lux (lumens/m<sup>2</sup>
|
||||
CREATE_CHAR(INT_t,CurrentHorizontalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||
CREATE_CHAR(UINT8_t,CurrentAirPurifierState,0,0,2,INACTIVE,IDLE,PURIFYING); // indicates current state of air purification
|
||||
CREATE_CHAR(UINT8_t,CurrentSlatState,0,0,2,FIXED,JAMMED,SWINGING); // indicates current state of slats
|
||||
CREATE_CHAR(UINT8_t,CurrentPosition,0,0,100); // current position (as a percentage) from fully closed (0) to full open (100)
|
||||
CREATE_CHAR(INT_t,CurrentVerticalTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||
CREATE_CHAR(UINT8_t,CurrentVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // current visibility of the Service, as selectable on the Settings Page of the Home App
|
||||
CREATE_CHAR(UINT8_t,CurrentHumidifierDehumidifierState,1,0,3,INACTIVE,IDLE,HUMIDIFYING,DEHUMIDIFYING); // indicates current state of humidifier/dehumidifer
|
||||
CREATE_CHAR(UINT8_t,CurrentDoorState,1,0,4,OPEN,CLOSED,OPENING,CLOSING,STOPPED); // indicates current state of a door
|
||||
CREATE_CHAR(UINT8_t,CurrentFanState,1,0,2,INACTIVE,IDLE,BLOWING); // indicates current state of a fan
|
||||
CREATE_CHAR(UINT8_t,CurrentHeatingCoolingState,0,0,2,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, or just idle
|
||||
CREATE_CHAR(UINT8_t,CurrentHeaterCoolerState,1,0,3,INACTIVE,IDLE,HEATING,COOLING); // indicates whether appliance is currently heating, cooling, idle, or off
|
||||
CREATE_CHAR(UINT8_t,CurrentMediaState,0,0,5); // not used
|
||||
CREATE_CHAR(FLOAT_t,CurrentRelativeHumidity,0,0,100); // current humidity measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,CurrentTemperature,0,0,100); // current temperature measured in Celsius
|
||||
CREATE_CHAR(INT_t,CurrentTiltAngle,0,-90,90); // current angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||
CREATE_CHAR(TLV_ENC_t,DisplayOrder,NULL_TLV,NULL_TLV,NULL_TLV); // specifies the order in which the TV inputs are displayed for selection in the Home App
|
||||
CREATE_CHAR(FLOAT_t,FilterLifeLevel,100,0,100); // measured as a percentage of remaining life
|
||||
CREATE_CHAR(UINT8_t,FilterChangeIndication,0,0,1,NO_CHANGE_NEEDED,CHANGE_NEEDED); // indicates state of filter
|
||||
CREATE_CHAR(STRING_t,FirmwareRevision,"1.0.0",NULL,NULL); // must be in form x[.y[.z]] - informational only
|
||||
CREATE_CHAR(STRING_t,HardwareRevision,"1.0.0",NULL,NULL); // must be in form x[.y[.z]] - informational only
|
||||
CREATE_CHAR(FLOAT_t,HeatingThresholdTemperature,16,0,25); // heating turns on when temperature (in Celsius) falls below this threshold
|
||||
CREATE_CHAR(BOOL_t,HoldPosition,false,0,1); // deprecated
|
||||
CREATE_CHAR(FLOAT_t,Hue,0,0,360); // color (in degrees) from red (0) to green (120) to blue (240) and back to red (360)
|
||||
CREATE_CHAR(BOOL_t,Identify,1,1,1,RUN_ID=1); // triggers an update when HomeKit wants HomeSpan to run its identification routine for an Accessory
|
||||
CREATE_CHAR(UINT32_t,Identifier,0,0,255); // numerical Identifer of the <b>InputSource</b>.
|
||||
CREATE_CHAR(UINT8_t,InputDeviceType,0,0,6); // not used
|
||||
CREATE_CHAR(UINT8_t,InputSourceType,0,0,10); // not used
|
||||
CREATE_CHAR(UINT8_t,InUse,0,0,1,NOT_IN_USE,IN_USE); // if Service is set to active, this indictes whether it is currently in use
|
||||
CREATE_CHAR(UINT8_t,IsConfigured,0,0,1,NOT_CONFIGURED,CONFIGURED); // indicates if a predefined Service has been configured
|
||||
CREATE_CHAR(UINT8_t,LeakDetected,0,0,1,NOT_DETECTED,DETECTED); // indictates if a leak is detected
|
||||
CREATE_CHAR(UINT8_t,LockCurrentState,0,0,3,UNLOCKED,LOCKED,JAMMED,UNKNOWN); // indicates state of a lock
|
||||
CREATE_CHAR(UINT8_t,LockPhysicalControls,0,0,1,CONTROL_LOCK_DISABLED,CONTROL_LOCK_ENABLED); // indicates if local control lock is enabled
|
||||
CREATE_CHAR(UINT8_t,LockTargetState,0,0,1,UNLOCK,LOCK); // indicates desired state of lock
|
||||
CREATE_CHAR(STRING_t,Manufacturer,"HomeSpan",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(STRING_t,Model,"HomeSpan-ESP32",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(BOOL_t,MotionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if motion is detected
|
||||
CREATE_CHAR(BOOL_t,Mute,0,0,1,OFF,ON); // not used
|
||||
CREATE_CHAR(STRING_t,Name,"unnamed",NULL,NULL); // default display name of the Accessory
|
||||
CREATE_CHAR(FLOAT_t,NitrogenDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(BOOL_t,ObstructionDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if obstruction is detected
|
||||
CREATE_CHAR(FLOAT_t,PM25Density,0,0,1000); // 2.5-micron particulate density, measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,OccupancyDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if occupanccy is detected
|
||||
CREATE_CHAR(BOOL_t,OutletInUse,0,0,1,NOT_IN_USE,IN_USE); // indicates if an appliance or light is plugged into the outlet, regardless of whether on or off
|
||||
CREATE_CHAR(BOOL_t,On,0,0,1,OFF,ON); // indicates if the Service is active/on
|
||||
CREATE_CHAR(FLOAT_t,OzoneDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,PictureMode,0,0,13); // not used
|
||||
CREATE_CHAR(FLOAT_t,PM10Density,0,0,1000); // 10-micron particulate density, measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,PositionState,2,0,2,GOING_TO_MINIMUM,GOING_TO_MAXIMUM,STOPPED); // deprecated
|
||||
CREATE_CHAR(UINT8_t,PowerModeSelection,0,0,0,VIEW_SETTINGS); // when defined, creates a "View TV Settings" button in the Home App that triggers an update to this Characteristic when pressed
|
||||
CREATE_CHAR(UINT8_t,ProgramMode,0,0,2,NONE,SCHEDULED,SCHEDULE_OVERRIDEN); // indicates if pre-scheduled program is running
|
||||
CREATE_CHAR(UINT8_t,ProgrammableSwitchEvent,0,0,2,SINGLE_PRESS,DOUBLE_PRESS,LONG_PRESS); // specifies type of button press
|
||||
CREATE_CHAR(FLOAT_t,RelativeHumidityDehumidifierThreshold,50,0,100); // dehumidfier turns on when humidity rises above this threshold
|
||||
CREATE_CHAR(FLOAT_t,RelativeHumidityHumidifierThreshold,50,0,100); // humidfier turns on when humidity falls below this threshold
|
||||
CREATE_CHAR(UINT32_t,RemainingDuration,60,0,3600); // duration (in seconds) remaining for Service to be active/on
|
||||
CREATE_CHAR(UINT8_t,RemoteKey,4,4,15,UP=4,DOWN,LEFT,RIGHT,CENTER,BACK,PLAY_PAUSE=11,INFO=15); // triggers an update when the corresponding key is pressed in the Remote Control widget on an iPhone
|
||||
CREATE_CHAR(UINT8_t,ResetFilterIndication,1,1,1,RESET_FILTER=1); // triggers an update when the user chooses to reset the <b>FilterChangeIndication</b> (only appears in Eve App, not Home App)
|
||||
CREATE_CHAR(INT_t,RotationDirection,0,0,1,CLOCKWISE,COUNTERCLOCKWISE); // indicates the rotation direction of a fan
|
||||
CREATE_CHAR(FLOAT_t,RotationSpeed,0,0,100); // measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,Saturation,0,0,100); // color saturation, measured as a percentage
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemAlarmType,0,0,1,KNOWN,UNKNOWN); // indicates whether alarm was triggered for known reason
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemCurrentState,3,0,4,ARMED_STAY,ARMED_AWAY,ARMED_NIGHT,DISARMED,ALARM_TRIGGERED); // indicates current state of the security system
|
||||
CREATE_CHAR(UINT8_t,SecuritySystemTargetState,3,0,3,ARM_STAY,ARM_AWAY,ARM_NIGHT,DISARM); // indicates desired state of the security system
|
||||
CREATE_CHAR(STRING_t,SerialNumber,"HS-12345",NULL,NULL); // any string - informational only
|
||||
CREATE_CHAR(UINT8_t,ServiceLabelIndex,1,1,255); // numerical index used to distinguish multiple copies of the same Service within an Accessory
|
||||
CREATE_CHAR(UINT8_t,ServiceLabelNamespace,1,0,1,DOTS,NUMERALS); // indicates how un-named Services linked together with a <b>ServiceLabel</b> Service should be displayed in the Home App
|
||||
CREATE_CHAR(UINT8_t,SlatType,0,0,1,HORIZONTAL,VERTICAL); // indicates the direction of a slat or group of slats
|
||||
CREATE_CHAR(UINT8_t,SleepDiscoveryMode,0,0,1); // not used
|
||||
CREATE_CHAR(UINT8_t,SmokeDetected,0,0,1,NOT_DETECTED,DETECTED); // indicates if smoke is detected
|
||||
CREATE_CHAR(BOOL_t,StatusActive,1,0,1,NOT_FUNCTIONING,FUNCTIONING); // indicates whether the Service is properly functioning
|
||||
CREATE_CHAR(UINT8_t,StatusFault,0,0,1,NO_FAULT,FAULT); // indicates whether the Service has a fault (only appears in Eve App, not Home App)
|
||||
CREATE_CHAR(UINT8_t,StatusJammed,0,0,1,NOT_JAMMED,JAMMED); // indicates whether the Service has been "jammed"
|
||||
CREATE_CHAR(UINT8_t,StatusLowBattery,0,0,1,NOT_LOW_BATTERY,LOW_BATTERY); // indicates state of battery
|
||||
CREATE_CHAR(UINT8_t,StatusTampered,0,0,1,NOT_TAMPERED,TAMPERED); // indicates whether the Service has been tampered with
|
||||
CREATE_CHAR(FLOAT_t,SulphurDioxideDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,SwingMode,0,0,1,SWING_DISABLED,SWING_ENABLED); // indicates whether swing-mode is enabled
|
||||
CREATE_CHAR(UINT8_t,TargetAirPurifierState,1,0,1,MANUAL,AUTO); // indicates desired state of air purifier
|
||||
CREATE_CHAR(UINT8_t,TargetFanState,1,0,1,MANUAL,AUTO); // indicates desired state of fan
|
||||
CREATE_CHAR(INT_t,TargetTiltAngle,0,-90,90); // indicated desired angle (in degrees) of slats from fully up or left (-90) to fully open (0) to fully down or right (90)
|
||||
CREATE_CHAR(UINT8_t,TargetHeaterCoolerState,0,0,2,AUTO,HEAT,COOL); // indicates desired state of heater/cooler
|
||||
CREATE_CHAR(UINT32_t,SetDuration,60,0,3600); // specifies the duration (in seconds) for a Service to remain on once activated
|
||||
CREATE_CHAR(INT_t,TargetHorizontalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully up (-90) to fully open (0) to fully down (90)
|
||||
CREATE_CHAR(UINT8_t,TargetHumidifierDehumidifierState,0,0,2,AUTO,HUMIDIFY,DEHUMIDIFY); // indicates desired state of humidifier/dehumidifier
|
||||
CREATE_CHAR(UINT8_t,TargetPosition,0,0,100); // indicates target position (as a percentage) from fully closed (0) to full open (100)
|
||||
CREATE_CHAR(UINT8_t,TargetDoorState,1,0,1,OPEN,CLOSED); // indicates desired state of door
|
||||
CREATE_CHAR(UINT8_t,TargetHeatingCoolingState,0,0,3,OFF,HEAT,COOL,AUTO); // indicates desired state of appliance
|
||||
CREATE_CHAR(UINT8_t,TargetMediaState,0,0,2); // unused
|
||||
CREATE_CHAR(FLOAT_t,TargetRelativeHumidity,0,0,100); // indicates desired humidity measured as a percentage
|
||||
CREATE_CHAR(FLOAT_t,TargetTemperature,16,10,38); // indicates desired temperature measures in Celsius
|
||||
CREATE_CHAR(UINT8_t,TargetVisibilityState,0,0,1,VISIBLE,NOT_VISIBLE); // indicates desired visibility of the Service, as selectable on the Settings Page of the Home App
|
||||
CREATE_CHAR(UINT8_t,TemperatureDisplayUnits,0,0,1,CELSIUS,FAHRENHEIT); // indicates the desired units to display the temperature on the device itself (has no effect on Home App)
|
||||
CREATE_CHAR(INT_t,TargetVerticalTiltAngle,0,-90,90); // indicates desired angle (in degrees) of slats from fully left (-90) to fully open (0) to fully right (90)
|
||||
CREATE_CHAR(UINT8_t,ValveType,0,0,3,GENERIC,IRRIGATION,SHOWER_HEAD,FAUCET); // indicates the type of valve
|
||||
CREATE_CHAR(STRING_t,Version,"1.0.0",NULL,NULL); // unused
|
||||
CREATE_CHAR(FLOAT_t,VOCDensity,0,0,1000); // measured in µg/m<sup>3</sup>
|
||||
CREATE_CHAR(UINT8_t,Volume,0,0,100); // unused
|
||||
CREATE_CHAR(UINT8_t,VolumeControlType,3,0,3,NONE,RELATIVE,RELATIVE_CURRENT,ABSOLUTE); // indicates the type of volume control
|
||||
CREATE_CHAR(UINT8_t,VolumeSelector,0,0,1,VOLUME_UP,VOLUME_DOWN); // triggered by presses to the iPhone's volume up/down buttons when TV is selected in the Remote Control widget
|
||||
CREATE_CHAR(FLOAT_t,WaterLevel,0,0,100); // measured as a percentage
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef CUSTOM_CHAR_HEADER
|
||||
|
||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init<FORMAT##_t>(val,nvsStore,MINVAL,MAXVAL); } }; }
|
||||
|
||||
#else
|
||||
|
||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||
extern HapChar _CUSTOM_##NAME; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init<FORMAT##_t>(val,nvsStore,MINVAL,MAXVAL); } }; }
|
||||
|
||||
#endif
|
||||
|
||||
#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) CUSTOM_CHAR(NAME,UUID,PERMISISONS,STRING,DEFVAL,NULL,NULL,true);
|
||||
#define CUSTOM_CHAR_TLV8(NAME,UUID,PERMISISONS) CUSTOM_CHAR(NAME,UUID,PERMISISONS,TLV_ENC,NULL_TLV,NULL_TLV,NULL_TLV,true);
|
||||
#define CUSTOM_CHAR_DATA(NAME,UUID,PERMISISONS) CUSTOM_CHAR(NAME,UUID,PERMISISONS,DATA,NULL_DATA,NULL_DATA,NULL_DATA,true);
|
||||
|
||||
#define CUSTOM_SERV(NAME,UUID) \
|
||||
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// MACROS TO ADD A NEW ACCESSORY WITH OPTIONAL NAME //
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
#define SPAN_ACCESSORY(...) new SpanAccessory(); new Service::AccessoryInformation(); new Characteristic::Identify(); __VA_OPT__(new Characteristic::Name(__VA_ARGS__));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
299
ESP32/HomeSpan-master/src/TLV8.cpp
Normal file
299
ESP32/HomeSpan-master/src/TLV8.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "TLV8.h"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
tlv8_t::tlv8_t(uint8_t tag, size_t len, const uint8_t* val) : tag{tag}, len{len} {
|
||||
if(len>0){
|
||||
this->val=std::unique_ptr<uint8_t>((uint8_t *)HS_MALLOC(len));
|
||||
if(val!=NULL)
|
||||
memcpy((this->val).get(),val,len);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void tlv8_t::update(size_t addLen, const uint8_t *addVal){
|
||||
if(addLen>0){
|
||||
uint8_t *p=val.release();
|
||||
p=(uint8_t *)HS_REALLOC(p,len+addLen);
|
||||
val=std::unique_ptr<uint8_t>(p);
|
||||
if(addVal!=NULL)
|
||||
memcpy(p+len,addVal,addLen);
|
||||
len+=addLen;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
void tlv8_t::osprint(std::ostream& os) const {
|
||||
|
||||
uint8_t *p=val.get(); // starting pointer
|
||||
uint8_t *pend=p+len; // ending pointer (may equal starting if len=0)
|
||||
|
||||
do{
|
||||
uint8_t nBytes=(pend-p)>255?255:(pend-p); // max is 255 bytes per TLV record
|
||||
os.write((char *)&tag,1);
|
||||
os.write((char *)&nBytes,1);
|
||||
os.write((char *)p,nBytes);
|
||||
p+=nBytes;
|
||||
} while(p<pend);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, size_t len, const uint8_t* val) {
|
||||
|
||||
if(!empty() && back().getTag()==tag)
|
||||
back().update(len,val);
|
||||
else
|
||||
emplace_back(tag,len,val);
|
||||
|
||||
return(--end());
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, TLV8 &subTLV){
|
||||
|
||||
auto it=add(tag,subTLV.pack_size(),NULL); // create space for inserting sub TLV and store iterator to new element
|
||||
subTLV.pack(*it); // pack subTLV into new element
|
||||
return(--end());
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::add(uint8_t tag, uint64_t val){
|
||||
|
||||
uint8_t *p=reinterpret_cast<uint8_t *>(&val);
|
||||
size_t nBytes=sizeof(uint64_t);
|
||||
while(nBytes>1 && p[nBytes-1]==0) // TLV requires little endian of size 1, 2, 4, or 8 bytes (include trailing zeros as needed)
|
||||
nBytes--;
|
||||
if(nBytes==3) // need to include a trailing zero so that total bytes=4
|
||||
nBytes=4;
|
||||
else if(nBytes>4) // need to include multiple trailing zeros so that total bytes=8
|
||||
nBytes=8;
|
||||
return(add(tag, nBytes, p));
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
TLV8_itc TLV8::find(uint8_t tag, TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
auto it=it1;
|
||||
while(it!=it2 && it->getTag()!=tag)
|
||||
it++;
|
||||
return(it);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
size_t TLV8::pack_size(TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
size_t nBytes=0;
|
||||
|
||||
while(it1!=it2){
|
||||
nBytes+=2+(*it1).getLen();
|
||||
if((*it1).getLen()>255)
|
||||
nBytes+=2*(((*it1).getLen()-1)/255);
|
||||
it1++;
|
||||
}
|
||||
|
||||
return(nBytes);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
size_t TLV8::pack(uint8_t *buf, size_t bufSize) const {
|
||||
|
||||
size_t nBytes=0;
|
||||
|
||||
while(nBytes<bufSize && currentPackIt!=endPackIt){
|
||||
switch(currentPackPhase){
|
||||
|
||||
case 0:
|
||||
currentPackBuf=*currentPackIt;
|
||||
endPackBuf=(*currentPackIt)+currentPackIt->getLen();
|
||||
currentPackPhase=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
*buf++=currentPackIt->getTag();
|
||||
nBytes++;
|
||||
currentPackPhase=2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
currentPackLen=endPackBuf-currentPackBuf;
|
||||
if(currentPackLen>255)
|
||||
currentPackLen=255;
|
||||
*buf++=currentPackLen;
|
||||
nBytes++;
|
||||
currentPackPhase=3;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(currentPackLen==0){
|
||||
if(endPackBuf==currentPackBuf){
|
||||
currentPackIt++;
|
||||
currentPackPhase=0;
|
||||
} else {
|
||||
currentPackPhase=1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
size_t copyBytes=(currentPackLen<(bufSize-nBytes)) ? currentPackLen : (bufSize-nBytes);
|
||||
memcpy(buf,currentPackBuf,copyBytes);
|
||||
buf+=copyBytes;
|
||||
currentPackBuf+=copyBytes;
|
||||
currentPackLen-=copyBytes;
|
||||
nBytes+=copyBytes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(nBytes);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
int TLV8::unpack(uint8_t *buf, size_t bufSize){
|
||||
|
||||
if(bufSize==0)
|
||||
return(-1);
|
||||
|
||||
if(empty())
|
||||
unpackPhase=0;
|
||||
|
||||
while(bufSize>0){
|
||||
switch(unpackPhase){
|
||||
|
||||
case 0:
|
||||
unpackTag=*buf++;
|
||||
bufSize--;
|
||||
add(unpackTag);
|
||||
unpackPhase=1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
unpackBytes=*buf++;
|
||||
bufSize--;
|
||||
if(unpackBytes==0)
|
||||
unpackPhase=0;
|
||||
else
|
||||
unpackPhase=2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
size_t copyBytes=unpackBytes<bufSize ? unpackBytes : bufSize;
|
||||
add(unpackTag,copyBytes,buf);
|
||||
buf+=copyBytes;
|
||||
unpackBytes-=copyBytes;
|
||||
bufSize-=copyBytes;
|
||||
if(unpackBytes==0)
|
||||
unpackPhase=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return(unpackPhase);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
int TLV8::unpack(TLV8_itc it){
|
||||
|
||||
if(it==end())
|
||||
return(0);
|
||||
|
||||
return(unpack(*it,it->getLen()));
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
const char *TLV8::getName(uint8_t tag) const {
|
||||
|
||||
if(names==NULL)
|
||||
return(NULL);
|
||||
|
||||
for(int i=0;i<nNames;i++){
|
||||
if(names[i].tag==tag)
|
||||
return(names[i].name);
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
void TLV8::print(TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
while(it1!=it2){
|
||||
const char *name=getName(it1->getTag());
|
||||
if(name)
|
||||
Serial.printf("%s",name);
|
||||
else
|
||||
Serial.printf("%d",it1->getTag());
|
||||
Serial.printf("(%d) ",it1->getLen());
|
||||
for(int i=0;i<it1->getLen();i++)
|
||||
Serial.printf("%02X",(*it1)[i]);
|
||||
if(it1->getLen()==0)
|
||||
Serial.printf(" [null]");
|
||||
else if(it1->getLen()<=4)
|
||||
Serial.printf(" [%u]",it1->getVal());
|
||||
else if(it1->getLen()<=8)
|
||||
Serial.printf(" [%llu]",it1->getVal<uint64_t>());
|
||||
Serial.printf("\n");
|
||||
it1++;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void TLV8::printAll_r(String label) const{
|
||||
|
||||
for(auto it=begin();it!=end();it++){
|
||||
Serial.printf("%s",label.c_str());
|
||||
print(it);
|
||||
TLV8 tlv;
|
||||
if(tlv.unpack(*it,(*it).getLen())==0)
|
||||
tlv.printAll_r(label+String((*it).getTag())+"-");
|
||||
}
|
||||
Serial.printf("%sDONE\n",label.c_str());
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void TLV8::osprint(std::ostream& os, TLV8_itc it1, TLV8_itc it2) const {
|
||||
|
||||
for(auto it=it1;it!=it2;it++)
|
||||
(*it).osprint(os);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
147
ESP32/HomeSpan-master/src/TLV8.h
Normal file
147
ESP32/HomeSpan-master/src/TLV8.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "PSRAM.h"
|
||||
|
||||
class tlv8_t {
|
||||
|
||||
private:
|
||||
|
||||
uint8_t tag;
|
||||
size_t len;
|
||||
std::unique_ptr<uint8_t> val;
|
||||
|
||||
public:
|
||||
|
||||
tlv8_t(uint8_t tag, size_t len, const uint8_t* val);
|
||||
void update(size_t addLen, const uint8_t *addVal);
|
||||
void osprint(std::ostream& os) const;
|
||||
|
||||
operator uint8_t*() const {
|
||||
return(val.get());
|
||||
}
|
||||
|
||||
uint8_t & operator[](int index) const {
|
||||
return(val.get()[index]);
|
||||
}
|
||||
|
||||
uint8_t *get() const {
|
||||
return(val.get());
|
||||
}
|
||||
|
||||
size_t getLen() const {
|
||||
return(len);
|
||||
}
|
||||
|
||||
uint8_t getTag() const {
|
||||
return(tag);
|
||||
}
|
||||
|
||||
template<class T=uint32_t> T getVal() const {
|
||||
T iVal=0;
|
||||
for(int i=0;i<len;i++)
|
||||
iVal|=static_cast<T>(val.get()[i])<<(i*8);
|
||||
return(iVal);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
typedef std::list<tlv8_t, Mallocator<tlv8_t>>::const_iterator TLV8_itc;
|
||||
typedef struct { const uint8_t tag; const char *name; } TLV8_names;
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class TLV8 : public std::list<tlv8_t, Mallocator<tlv8_t>> {
|
||||
|
||||
TLV8_itc mutable currentPackIt;
|
||||
TLV8_itc mutable endPackIt;
|
||||
uint8_t mutable *currentPackBuf;
|
||||
uint8_t mutable *endPackBuf;
|
||||
int mutable currentPackPhase;
|
||||
size_t mutable currentPackLen;
|
||||
|
||||
uint8_t unpackTag;
|
||||
size_t unpackBytes;
|
||||
int unpackPhase;
|
||||
|
||||
const TLV8_names *names=NULL;
|
||||
int nNames=0;
|
||||
|
||||
void printAll_r(String label) const;
|
||||
|
||||
public:
|
||||
|
||||
TLV8(){};
|
||||
TLV8(const TLV8_names *names, int nNames) : names{names}, nNames{nNames} {};
|
||||
|
||||
TLV8_itc add(uint8_t tag, size_t len, const uint8_t *val);
|
||||
TLV8_itc add(uint8_t tag, uint64_t val);
|
||||
TLV8_itc add(uint8_t tag, TLV8 &subTLV);
|
||||
TLV8_itc add(uint8_t tag){return(add(tag, 0, NULL));}
|
||||
TLV8_itc add(uint8_t tag, const char *val){return(add(tag, strlen(val), reinterpret_cast<const uint8_t*>(val)));}
|
||||
|
||||
TLV8_itc find(uint8_t tag, TLV8_itc it1, TLV8_itc it2) const;
|
||||
TLV8_itc find(uint8_t tag, TLV8_itc it1) const {return(find(tag, it1, end()));}
|
||||
TLV8_itc find(uint8_t tag) const {return(find(tag, begin(), end()));}
|
||||
|
||||
int len(TLV8_itc it) const {return(it==end()?-1:(*it).getLen());}
|
||||
|
||||
size_t pack_size(TLV8_itc it1, TLV8_itc it2) const;
|
||||
size_t pack_size() const {return(pack_size(begin(), end()));}
|
||||
|
||||
void pack_init(TLV8_itc it1, TLV8_itc it2) const {currentPackIt=it1; endPackIt=it2; currentPackPhase=0;}
|
||||
void pack_init(TLV8_itc it1) const {pack_init(it1, std::next(it1));}
|
||||
void pack_init() const {pack_init(begin(),end());}
|
||||
|
||||
size_t pack(uint8_t *buf, size_t bufSize) const;
|
||||
size_t pack(uint8_t *buf) const {pack_init(); return(pack(buf,pack_size()));}
|
||||
|
||||
const char *getName(uint8_t tag) const;
|
||||
|
||||
void print(TLV8_itc it1, TLV8_itc it2) const;
|
||||
void print(TLV8_itc it1) const {print(it1, std::next(it1));}
|
||||
void print() const {print(begin(), end());}
|
||||
void printAll() const {printAll_r("");}
|
||||
|
||||
void osprint(std::ostream& os, TLV8_itc it1, TLV8_itc it2) const;
|
||||
void osprint(std::ostream& os, TLV8_itc it1) const {osprint(os, it1, std::next(it1));}
|
||||
void osprint(std::ostream& os) const {osprint(os, begin(), end());}
|
||||
|
||||
int unpack(uint8_t *buf, size_t bufSize);
|
||||
int unpack(TLV8_itc it);
|
||||
|
||||
void wipe() {std::list<tlv8_t, Mallocator<tlv8_t>>().swap(*this);}
|
||||
};
|
||||
295
ESP32/HomeSpan-master/src/Utils.cpp
Normal file
295
ESP32/HomeSpan-master/src/Utils.cpp
Normal file
@@ -0,0 +1,295 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "Utils.h"
|
||||
#include "HomeSpan.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Contains various generic utility functions and classes:
|
||||
//
|
||||
// Utils::readSerial - reads all characters from Serial port and saves only up to max specified
|
||||
// Utils::mask - masks a string with asterisks (good for displaying passwords)
|
||||
//
|
||||
// class PushButton - tracks Single, Double, and Long Presses of a pushbutton that connects a specified pin to ground
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char *Utils::readSerial(char *c, int max){
|
||||
|
||||
if(homeSpan.getSerialInputDisable()){
|
||||
c[0]='\0';
|
||||
return(c);
|
||||
}
|
||||
|
||||
int i=0;
|
||||
char buf;
|
||||
|
||||
while(1){
|
||||
|
||||
while(!Serial.available()); // wait until there is a new character
|
||||
|
||||
buf=Serial.read();
|
||||
|
||||
if(buf=='\n'){ // exit upon newline
|
||||
if(i>0) // characters have been typed
|
||||
c[i]='\0'; // replace newline with string terminator
|
||||
return(c); // return updated string
|
||||
}
|
||||
|
||||
if(buf!='\r'){ // save any character except carriage return
|
||||
c[i]=buf; // store new character
|
||||
if(i<max) // do not store more than max characters (excluding string terminator)
|
||||
i++;
|
||||
}
|
||||
|
||||
} // while(1)
|
||||
|
||||
} // readSerial
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
char *Utils::stripBackslash(char *c){
|
||||
|
||||
size_t n=strlen(c);
|
||||
char *p=c;
|
||||
for(int i=0;i<=n;i++){
|
||||
*p=c[i];
|
||||
if(*p!='\\')
|
||||
p++;
|
||||
}
|
||||
return(c);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
String Utils::mask(char *c, int n){
|
||||
String s="";
|
||||
int len=strlen(c);
|
||||
|
||||
for(int i=0;i<len;i++){
|
||||
if(i<n || i>=len-n)
|
||||
s+=c[i];
|
||||
else
|
||||
s+='*';
|
||||
}
|
||||
|
||||
return(s);
|
||||
} // mask
|
||||
|
||||
////////////////////////////////
|
||||
// PushButton //
|
||||
////////////////////////////////
|
||||
|
||||
PushButton::PushButton(int pin, triggerType_t triggerType){
|
||||
|
||||
this->pin=pin;
|
||||
this->triggerType=triggerType;
|
||||
|
||||
status=0;
|
||||
doubleCheck=false;
|
||||
|
||||
if(triggerType==TRIGGER_ON_LOW)
|
||||
pinMode(pin, INPUT_PULLUP);
|
||||
else if(triggerType==TRIGGER_ON_HIGH)
|
||||
pinMode(pin, INPUT_PULLDOWN);
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
else if (triggerType==TRIGGER_ON_TOUCH && threshold==0){
|
||||
for(int i=0;i<calibCount;i++)
|
||||
threshold+=touchRead(pin);
|
||||
threshold/=calibCount;
|
||||
#if SOC_TOUCH_VERSION_1
|
||||
threshold/=2;
|
||||
LOG0("Touch Sensor at pin=%d used for calibration. Triggers when sensor reading < %d.\n",pin,threshold);
|
||||
#elif SOC_TOUCH_VERSION_2
|
||||
threshold*=2;
|
||||
LOG0("Touch Sensor at pin=%d used for calibration. Triggers when sensor reading > %d.\n",pin,threshold);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if(triggerType(pin)){
|
||||
pressType=CLOSED;
|
||||
toggleStatus=2;
|
||||
} else {
|
||||
pressType=OPEN;
|
||||
toggleStatus=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime){
|
||||
|
||||
unsigned long cTime=millis();
|
||||
|
||||
switch(status){
|
||||
|
||||
case 0:
|
||||
if(doubleCheck && cTime>doubleAlarm){
|
||||
doubleCheck=false;
|
||||
pressType=SINGLE;
|
||||
return(true);
|
||||
}
|
||||
|
||||
if(triggerType(pin)){ // button is "pressed"
|
||||
singleAlarm=cTime+singleTime;
|
||||
if(!doubleCheck){
|
||||
status=1;
|
||||
doubleAlarm=singleAlarm+doubleTime;
|
||||
longAlarm=cTime+longTime;
|
||||
} else {
|
||||
status=4;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
if(!triggerType(pin)){ // button is released
|
||||
status=0;
|
||||
if(cTime>singleAlarm){
|
||||
doubleCheck=true;
|
||||
}
|
||||
} else
|
||||
|
||||
if(cTime>longAlarm){ // button is long-pressed
|
||||
longAlarm=cTime+longTime;
|
||||
status=3;
|
||||
pressType=LONG;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(!triggerType(pin)) // button has been released after a long press
|
||||
status=0;
|
||||
else if(cTime>longAlarm){
|
||||
longAlarm=cTime+longTime;
|
||||
pressType=LONG;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(!triggerType(pin)){ // button is released
|
||||
status=0;
|
||||
} else
|
||||
|
||||
if(cTime>singleAlarm){ // button is still pressed
|
||||
status=5;
|
||||
pressType=DOUBLE;
|
||||
doubleCheck=false;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if(!triggerType(pin)) // button has been released after double-click
|
||||
status=0;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::toggled(uint16_t toggleTime){
|
||||
|
||||
unsigned long cTime=millis();
|
||||
|
||||
switch(toggleStatus){
|
||||
|
||||
case 0:
|
||||
if(triggerType(pin)){ // switch is toggled CLOSED
|
||||
singleAlarm=cTime+toggleTime;
|
||||
toggleStatus=1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if(!triggerType(pin)){ // switch is toggled back OPEN too soon
|
||||
toggleStatus=0;
|
||||
}
|
||||
|
||||
else if(cTime>singleAlarm){ // switch has been in CLOSED state for sufficient time
|
||||
toggleStatus=2;
|
||||
pressType=CLOSED;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(!triggerType(pin)){ // switch is toggled OPEN after being in CLOSED state
|
||||
toggleStatus=0;
|
||||
pressType=OPEN;
|
||||
return(true);
|
||||
}
|
||||
break;
|
||||
|
||||
} // switch
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
boolean PushButton::primed(){
|
||||
|
||||
if(millis()>singleAlarm && status==1){
|
||||
status=2;
|
||||
return(true);
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int PushButton::type(){
|
||||
return(pressType);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void PushButton::wait(){
|
||||
while(triggerType(pin));
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void PushButton::reset(){
|
||||
status=0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
PushButton::touch_value_t PushButton::threshold=0;
|
||||
237
ESP32/HomeSpan-master/src/Utils.h
Normal file
237
ESP32/HomeSpan-master/src/Utils.h
Normal file
@@ -0,0 +1,237 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "PSRAM.h"
|
||||
|
||||
namespace Utils {
|
||||
|
||||
char *readSerial(char *c, int max); // read serial port into 'c' until <newline>, but storing only first 'max' characters (the rest are discarded)
|
||||
String mask(char *c, int n); // simply utility that creates a String from 'c' with all except the first and last 'n' characters replaced by '*'
|
||||
char *stripBackslash(char *c); // strips backslashes out of c (Apple unecessesarily "escapes" forward slashes in JSON)
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// Creates a temporary buffer that is freed after
|
||||
// going out of scope
|
||||
|
||||
template <class bufType>
|
||||
class TempBuffer {
|
||||
|
||||
private:
|
||||
|
||||
bufType *buf=NULL;
|
||||
size_t nElements;
|
||||
|
||||
public:
|
||||
|
||||
TempBuffer(size_t _nElements=1) : nElements(_nElements) {
|
||||
buf=(bufType *)HS_MALLOC(nElements*sizeof(bufType));
|
||||
if(buf==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
|
||||
TempBuffer(bufType *addBuf...) : nElements(0) {
|
||||
va_list args;
|
||||
va_start(args,addBuf);
|
||||
while(addBuf!=NULL){
|
||||
size_t addElements=va_arg(args,size_t);
|
||||
buf=(bufType *)HS_REALLOC(buf,(nElements+addElements)*sizeof(bufType));
|
||||
if(buf==NULL){
|
||||
Serial.printf("\n\n*** FATAL ERROR: Requested allocation of %d bytes failed. Program Halting.\n\n",nElements*sizeof(bufType));
|
||||
while(1);
|
||||
}
|
||||
memcpy(buf+nElements,addBuf,addElements*sizeof(bufType));
|
||||
nElements+=addElements;
|
||||
addBuf=va_arg(args,bufType *);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
~TempBuffer(){
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int len(){
|
||||
return(nElements*sizeof(bufType));
|
||||
}
|
||||
|
||||
int size(){
|
||||
return(nElements);
|
||||
}
|
||||
|
||||
bufType *get(){
|
||||
return(buf);
|
||||
}
|
||||
|
||||
operator bufType*() const{
|
||||
return(buf);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// PushButton //
|
||||
////////////////////////////////
|
||||
|
||||
class PushButton{
|
||||
|
||||
int status;
|
||||
int toggleStatus;
|
||||
boolean doubleCheck;
|
||||
uint32_t singleAlarm;
|
||||
uint32_t doubleAlarm;
|
||||
uint32_t longAlarm;
|
||||
|
||||
#if SOC_TOUCH_VERSION_2
|
||||
typedef uint32_t touch_value_t;
|
||||
#else
|
||||
typedef uint16_t touch_value_t;
|
||||
#endif
|
||||
|
||||
static touch_value_t threshold;
|
||||
static const int calibCount=20;
|
||||
|
||||
public:
|
||||
|
||||
typedef boolean (*triggerType_t)(int pin);
|
||||
|
||||
protected:
|
||||
|
||||
int pressType;
|
||||
int pin;
|
||||
triggerType_t triggerType;
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
SINGLE=0, // applicable only for push button
|
||||
DOUBLE=1, // applicable only for push button
|
||||
LONG=2, // applicable only for push button
|
||||
CLOSED=3, // applicable only for toggle switch
|
||||
OPEN=4 // applicable only for toggle switch
|
||||
};
|
||||
|
||||
static boolean TRIGGER_ON_LOW(int pin){return(!digitalRead(pin));}
|
||||
static boolean TRIGGER_ON_HIGH(int pin){return(digitalRead(pin));}
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
#if SOC_TOUCH_VERSION_2
|
||||
static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin)>threshold);}
|
||||
#else
|
||||
static boolean TRIGGER_ON_TOUCH(int pin){return(touchRead(pin)<threshold);}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
PushButton(int pin, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||
|
||||
// Creates a push-button/toggle-switch of specified type on specified pin
|
||||
//
|
||||
// pin: pin number to which the button is connected
|
||||
// triggerType: a function of of the form 'boolean f(int)' that is passed
|
||||
// the parameter *pin* and returns TRUE if the button associated
|
||||
// with *pin* is pressed/on, or FALSE if not. Can choose from 3 pre-specifed
|
||||
// triggerType_t functions (TRIGGER_ON_LOW, TRIGGER_ON_HIGH, and TRIGGER_ON_TOUCH),
|
||||
// or write your own custom handler
|
||||
|
||||
void reset();
|
||||
|
||||
// Resets state of PushButton. Should be called once before any loops that will
|
||||
// repeatedly check the button for a trigger() event.
|
||||
|
||||
boolean triggered(uint16_t singleTime, uint16_t longTime, uint16_t doubleTime=0);
|
||||
|
||||
// Returns true if button has been triggered by an press event based on the following parameters:
|
||||
|
||||
// singleTime: the minimum time required for the button to be pressed to trigger a Single Press
|
||||
// doubleTime: the maximum time allowed between button presses to qualify as a Double Press
|
||||
// longTime: the minimum time required for the button to be pressed and held to trigger a Long Press
|
||||
|
||||
// All times are in milliseconds (ms). Trigger Rules:
|
||||
|
||||
// * If button is pressed and continuously held, a Long Press will be triggered every longTime ms until the
|
||||
// button is released.
|
||||
// * If button is pressed for more than singleTime ms but less than longTime ms and then released, a Single Press
|
||||
// will be triggered, UNLESS
|
||||
// * The button is pressed a second time within doubleTime ms AND held again for at least singleTime ms, in which case
|
||||
// a DoublePress will be triggered. No further events will occur until the button is released.
|
||||
// * If singleTime>longTime, only Long Press triggers can occur.
|
||||
// * If doubleTime=0, Double Presses cannot occur.
|
||||
// * Once triggered() returns true, if will subsequently return false until there is a new trigger event.
|
||||
|
||||
boolean primed();
|
||||
|
||||
// Returns true if button has been pressed and held for greater than singleTime, but has not yet been released.
|
||||
// After returning true, subsequent calls will always return false until the button has been released and reset.
|
||||
|
||||
int type();
|
||||
|
||||
// Returns the press type based on the whether triggered() or toggled() is called:
|
||||
|
||||
// * For a push button, returns the last trigger event: 0=Single Press, 1=Double Press, 2=Long Press
|
||||
// * For a toggle switch, returns the current state of the switch: 4=ON, 5=OFF
|
||||
|
||||
void wait();
|
||||
|
||||
// Waits for button to be released. Use after Long Press if button release confirmation is desired
|
||||
|
||||
boolean toggled(uint16_t toggleTime);
|
||||
|
||||
// Returns true if switch has been toggled, where
|
||||
|
||||
// toggleTime: the minimum time (in milliseconds) a switch needs to be ON to register a toggle event
|
||||
|
||||
// Once toggled() returns true, if will subsequently return false until the switch is toggled again.
|
||||
|
||||
int getPin(){return(pin);}
|
||||
|
||||
// Returns pin number
|
||||
|
||||
#if SOC_TOUCH_SENSOR_NUM > 0
|
||||
|
||||
static void setTouchCycles(uint16_t measureTime, uint16_t sleepTime){touchSetCycles(measureTime,sleepTime);}
|
||||
|
||||
// Sets the measure time and sleep time touch cycles , and lower threshold that triggers a touch - used only when triggerType=PushButton::TRIGGER_ON_TOUCH
|
||||
|
||||
// measureTime: duration of measurement time of all touch sensors in number of clock cycles
|
||||
// sleepTime: duration of sleep time (between measurements) of all touch sensors number of clock cycles
|
||||
|
||||
static void setTouchThreshold(touch_value_t thresh){threshold=thresh;}
|
||||
|
||||
// Sets the threshold that triggers a touch - used only when triggerType=TRIGGER_ON_TOUCH
|
||||
|
||||
// thresh: the read value of touch sensors, beyond which which sensors are considered touched (i.e. "pressed").
|
||||
// This is a class-level value applied to all touch sensor buttons.
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
30
ESP32/HomeSpan-master/src/extras/Blinker.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Blinker.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Blinker.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Pixel.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Pixel.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Pixel.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/PwmPin.h
Normal file
30
ESP32/HomeSpan-master/src/extras/PwmPin.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/PwmPin.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/RFControl.h
Normal file
30
ESP32/HomeSpan-master/src/extras/RFControl.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/RFControl.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/StepperControl.h
Normal file
30
ESP32/HomeSpan-master/src/extras/StepperControl.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/StepperControl.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Stepper_A3967.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Stepper_A3967.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Stepper_A3967.h"
|
||||
30
ESP32/HomeSpan-master/src/extras/Stepper_TB6612.h
Normal file
30
ESP32/HomeSpan-master/src/extras/Stepper_TB6612.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Stepper_TB6612.h"
|
||||
149
ESP32/HomeSpan-master/src/src/extras/Blinker.cpp
Normal file
149
ESP32/HomeSpan-master/src/src/extras/Blinker.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "Blinker.h"
|
||||
|
||||
////////////////////////////////
|
||||
// Blinker //
|
||||
////////////////////////////////
|
||||
|
||||
Blinker::Blinker(Blinkable *led, uint16_t autoOffDuration){
|
||||
this->led=led;
|
||||
pauseDuration=autoOffDuration*1000;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::blinkTask(void *arg){
|
||||
|
||||
Blinker *b=(Blinker *)arg;
|
||||
|
||||
for(;;){
|
||||
for(int i=0;i<b->nBlinks;i++){
|
||||
b->led->on();
|
||||
delay(b->onTime);
|
||||
b->led->off();
|
||||
delay(b->offTime);
|
||||
}
|
||||
delay(b->delayTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::start(int period, float dutyCycle){
|
||||
|
||||
start(period, dutyCycle, 1, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
onTime=dutyCycle*period;
|
||||
offTime=period-onTime;
|
||||
this->delayTime=delayTime+offTime;
|
||||
this->nBlinks=nBlinks;
|
||||
|
||||
stop();
|
||||
xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 2, &blinkHandle );
|
||||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::BLINKING;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::stop(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
if(blinkHandle!=NULL){
|
||||
vTaskDelete(blinkHandle);
|
||||
blinkHandle=NULL;
|
||||
}
|
||||
|
||||
isPaused=true;
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::on(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
stop();
|
||||
led->on();
|
||||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::ON;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::off(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
stop();
|
||||
led->off();
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void Blinker::check(){
|
||||
|
||||
if(!led)
|
||||
return;
|
||||
|
||||
if(pauseDuration==0 || isPaused || (millis()-pauseTime)<pauseDuration)
|
||||
return;
|
||||
|
||||
ESP_LOGI(BLINKER_TAG,"Pausing LED");
|
||||
off();
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int Blinker::getPin(){
|
||||
|
||||
if(!led)
|
||||
return(-1);
|
||||
|
||||
return(led->getPin());
|
||||
}
|
||||
137
ESP32/HomeSpan-master/src/src/extras/Blinker.h
Normal file
137
ESP32/HomeSpan-master/src/src/extras/Blinker.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/timer.h>
|
||||
|
||||
[[maybe_unused]] static const char* BLINKER_TAG = "Blinker";
|
||||
|
||||
////////////////////////////////
|
||||
// Blinkable Interface //
|
||||
////////////////////////////////
|
||||
|
||||
class Blinkable {
|
||||
public:
|
||||
|
||||
virtual void on()=0;
|
||||
virtual void off()=0;
|
||||
virtual int getPin()=0;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// Blinker //
|
||||
////////////////////////////////
|
||||
|
||||
class Blinker {
|
||||
|
||||
enum STATUS {OFF, BLINKING, ON};
|
||||
|
||||
TaskHandle_t blinkHandle = NULL;
|
||||
Blinkable *led;
|
||||
|
||||
int nBlinks;
|
||||
int onTime;
|
||||
int offTime;
|
||||
int delayTime;
|
||||
STATUS status=STATUS::OFF;
|
||||
|
||||
unsigned long pauseDuration;
|
||||
unsigned long pauseTime;
|
||||
boolean isPaused=false;
|
||||
|
||||
static void blinkTask(void *arg);
|
||||
|
||||
public:
|
||||
|
||||
Blinker(Blinkable *led, uint16_t autoOffDuration=0);
|
||||
|
||||
// Creates a generic blinking LED in a separate task thread
|
||||
//
|
||||
// led: An initialized LED device that implements the Blinkable Interface
|
||||
////
|
||||
// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed
|
||||
// Blinker will resume normal operation upon next call to start(), on(), or off()
|
||||
// Program must periodically call check() for auto-off functionality to work
|
||||
|
||||
void start(int period, float dutyCycle=0.5);
|
||||
|
||||
// Starts simple ON/OFF blinking.
|
||||
//
|
||||
// period: ON/OFF blinking period, in milliseconds
|
||||
// dutyCycle: Fraction of period that LED is ON (default=50%)
|
||||
|
||||
void start(int period, float dutyCycle, int nBlinks, int delayTime);
|
||||
|
||||
// Starts ON/OFF blinking pattern.
|
||||
//
|
||||
// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern
|
||||
// dutyCycle: Fraction of period that LED is ON (default=50%)
|
||||
// nBlinks: Number of blinks in blinking portion of pattern
|
||||
// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern
|
||||
|
||||
void stop();
|
||||
|
||||
// Stops current blinking pattern.
|
||||
|
||||
void on();
|
||||
|
||||
// Stops current blinking pattern and turns on LED
|
||||
|
||||
void off();
|
||||
|
||||
// Stops current blinking pattern and turns off LED
|
||||
|
||||
void refresh(){if(status==STATUS::ON)on();}
|
||||
|
||||
// Refreshes LED color by turning device ON if status=ON (if status=BLINKING, new color is automatically used at next blink)
|
||||
|
||||
void check();
|
||||
|
||||
// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0)
|
||||
|
||||
int getPin();
|
||||
|
||||
// Returns pin number of connected LED
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// GenericLED //
|
||||
////////////////////////////////
|
||||
|
||||
class GenericLED : public Blinkable {
|
||||
int pin;
|
||||
|
||||
public:
|
||||
|
||||
GenericLED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);}
|
||||
void on() {digitalWrite(pin,HIGH);}
|
||||
void off() {digitalWrite(pin,LOW);}
|
||||
int getPin() {return(pin);}
|
||||
};
|
||||
205
ESP32/HomeSpan-master/src/src/extras/Pixel.cpp
Normal file
205
ESP32/HomeSpan-master/src/src/extras/Pixel.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Addressable LEDs //
|
||||
////////////////////////////////////////////
|
||||
|
||||
#include "Pixel.h"
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Single-Wire RGB/RGBW NeoPixels //
|
||||
////////////////////////////////////////////
|
||||
|
||||
Pixel::Pixel(int pin, pixelType_t pixelType){
|
||||
|
||||
rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver
|
||||
if(!*rf)
|
||||
return;
|
||||
|
||||
map=pixelType;
|
||||
|
||||
if(map[3])
|
||||
bytesPerPixel=4;
|
||||
else
|
||||
bytesPerPixel=3;
|
||||
|
||||
setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels)
|
||||
|
||||
rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler
|
||||
|
||||
rmt_set_tx_thr_intr_en(rf->getChannel(),false,8); // disable threshold interrupt
|
||||
txThrMask=RMT.int_ena.val; // save interrupt enable vector
|
||||
rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt to trigger every 8 pulses
|
||||
txThrMask^=RMT.int_ena.val; // find bit that flipped and save as threshold mask for this channel
|
||||
|
||||
rmt_set_tx_intr_en(rf->getChannel(),false); // disable end-of-transmission interrupt
|
||||
txEndMask=RMT.int_ena.val; // save interrupt enable vector
|
||||
rmt_set_tx_intr_en(rf->getChannel(),true); // enable end-of-transmission interrupt
|
||||
txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel
|
||||
|
||||
onColor.HSV(0,100,100,0);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset){
|
||||
|
||||
pattern[0]=RF_PULSE(high0*80+0.5,low0*80+0.5);
|
||||
pattern[1]=RF_PULSE(high1*80+0.5,low1*80+0.5);
|
||||
resetTime=lowReset;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Pixel::set(Color *c, int nPixels, boolean multiColor){
|
||||
|
||||
if(!*rf || nPixels==0)
|
||||
return;
|
||||
|
||||
status.nPixels=nPixels;
|
||||
status.color=c;
|
||||
status.iMem=0;
|
||||
status.started=true;
|
||||
status.px=this;
|
||||
status.multiColor=multiColor;
|
||||
status.iByte=0;
|
||||
|
||||
loadData(this); // load first two bytes of data to get started
|
||||
loadData(this);
|
||||
|
||||
rmt_tx_start(rf->getChannel(),true);
|
||||
|
||||
while(status.started); // wait for transmission to be complete
|
||||
delayMicroseconds(resetTime); // end-of-marker delay
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void IRAM_ATTR Pixel::loadData(void *arg){
|
||||
|
||||
if(RMT.int_st.val & status.px->txEndMask){
|
||||
RMT.int_clr.val=status.px->txEndMask;
|
||||
status.started=false;
|
||||
return;
|
||||
}
|
||||
|
||||
RMT.int_clr.val=status.px->txThrMask; // if loadData() is called and it is NOT because of an END interrupt (above) then must either be a pre-load, or a threshold trigger
|
||||
|
||||
if(status.nPixels==0){
|
||||
RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem].val=0;
|
||||
return;
|
||||
}
|
||||
|
||||
int startBit=status.px->map[status.iByte];
|
||||
int endBit=startBit-8;
|
||||
|
||||
for(int iBit=startBit;iBit>endBit;iBit--)
|
||||
RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(status.color->val>>iBit)&1];
|
||||
|
||||
if(++status.iByte==status.px->bytesPerPixel){
|
||||
status.iByte=0;
|
||||
status.color+=status.multiColor;
|
||||
status.nPixels--;
|
||||
}
|
||||
|
||||
status.iMem%=status.px->memSize;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
volatile Pixel::pixel_status_t Pixel::status;
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Two-Wire RGB DotStars //
|
||||
////////////////////////////////////////////
|
||||
|
||||
Dot::Dot(uint8_t dataPin, uint8_t clockPin){
|
||||
|
||||
pinMode(dataPin,OUTPUT);
|
||||
pinMode(clockPin,OUTPUT);
|
||||
digitalWrite(dataPin,LOW);
|
||||
digitalWrite(clockPin,LOW);
|
||||
|
||||
dataMask=1<<(dataPin%32);
|
||||
clockMask=1<<(clockPin%32);
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32C3
|
||||
dataSetReg=&GPIO.out_w1ts.val;
|
||||
dataClearReg=&GPIO.out_w1tc.val;
|
||||
clockSetReg=&GPIO.out_w1ts.val;
|
||||
clockClearReg=&GPIO.out_w1tc.val;
|
||||
#else
|
||||
if(dataPin<32){
|
||||
dataSetReg=&GPIO.out_w1ts;
|
||||
dataClearReg=&GPIO.out_w1tc;
|
||||
} else {
|
||||
dataSetReg=&GPIO.out1_w1ts.val;
|
||||
dataClearReg=&GPIO.out1_w1tc.val;
|
||||
}
|
||||
|
||||
if(clockPin<32){
|
||||
clockSetReg=&GPIO.out_w1ts;
|
||||
clockClearReg=&GPIO.out_w1tc;
|
||||
} else {
|
||||
clockSetReg=&GPIO.out1_w1ts.val;
|
||||
clockClearReg=&GPIO.out1_w1tc.val;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void Dot::set(Color *c, int nPixels, boolean multiColor){
|
||||
|
||||
*dataClearReg=dataMask; // send all zeros
|
||||
for(int j=0;j<31;j++){
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
|
||||
for(int i=0;i<nPixels;i++){
|
||||
for(int b=31;b>=0;b--){
|
||||
if((c->val>>b)&1)
|
||||
*dataSetReg=dataMask;
|
||||
else
|
||||
*dataClearReg=dataMask;
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
c+=multiColor;
|
||||
}
|
||||
|
||||
*dataClearReg=dataMask; // send all zeros
|
||||
for(int j=0;j<31;j++){
|
||||
*clockSetReg=clockMask;
|
||||
*clockClearReg=clockMask;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
286
ESP32/HomeSpan-master/src/src/extras/Pixel.h
Normal file
286
ESP32/HomeSpan-master/src/src/extras/Pixel.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Addressable LEDs //
|
||||
////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RFControl.h"
|
||||
#include "PwmPin.h"
|
||||
#include "Blinker.h"
|
||||
|
||||
[[maybe_unused]] static const char* PIXEL_TAG = "Pixel";
|
||||
|
||||
typedef const uint8_t pixelType_t[];
|
||||
|
||||
namespace PixelType {
|
||||
|
||||
pixelType_t RGB={31,23,15,0};
|
||||
pixelType_t RBG={31,15,23,0};
|
||||
pixelType_t BRG={23,15,31,0};
|
||||
pixelType_t BGR={15,23,31,0};
|
||||
pixelType_t GBR={15,31,23,0};
|
||||
pixelType_t GRB={23,31,15,0};
|
||||
pixelType_t RGBW={31,23,15,7};
|
||||
pixelType_t RBGW={31,15,23,7};
|
||||
pixelType_t BRGW={23,15,31,7};
|
||||
pixelType_t BGRW={15,23,31,7};
|
||||
pixelType_t GBRW={15,31,23,7};
|
||||
pixelType_t GRBW={23,31,15,7};
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Single-Wire RGB/RGBW NeoPixels //
|
||||
////////////////////////////////////////////
|
||||
|
||||
class Pixel : public Blinkable {
|
||||
|
||||
public:
|
||||
struct Color {
|
||||
union{
|
||||
struct {
|
||||
uint8_t white:8;
|
||||
uint8_t blue:8;
|
||||
uint8_t green:8;
|
||||
uint8_t red:8;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){ // returns Color based on provided RGB(W) values where r/g/b/w=[0-255]
|
||||
this->red=r;
|
||||
this->green=g;
|
||||
this->blue=b;
|
||||
this->white=w;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color HSV(float h, float s, float v, double w=0){ // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100]
|
||||
float r,g,b;
|
||||
LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b);
|
||||
this->red=r*255;
|
||||
this->green=g*255;
|
||||
this->blue=b*255;
|
||||
this->white=w*2.555;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
bool operator==(const Color& color){
|
||||
return(val==color.val);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& color){
|
||||
return(val!=color.val);
|
||||
}
|
||||
|
||||
Color operator+(const Color& color){
|
||||
Color newColor;
|
||||
newColor.white=white+color.white;
|
||||
newColor.blue=blue+color.blue;
|
||||
newColor.red=red+color.red;
|
||||
newColor.green=green+color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator+=(const Color& color){
|
||||
white+=color.white;
|
||||
red+=color.red;
|
||||
blue+=color.blue;
|
||||
green+=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color operator-(const Color& color){
|
||||
Color newColor;
|
||||
newColor.white=white-color.white;
|
||||
newColor.blue=blue-color.blue;
|
||||
newColor.red=red-color.red;
|
||||
newColor.green=green-color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator-=(const Color& color){
|
||||
white-=color.white;
|
||||
red-=color.red;
|
||||
blue-=color.blue;
|
||||
green-=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
}; // Color
|
||||
|
||||
private:
|
||||
struct pixel_status_t {
|
||||
int nPixels;
|
||||
Color *color;
|
||||
int iMem;
|
||||
boolean started;
|
||||
Pixel *px;
|
||||
boolean multiColor;
|
||||
int iByte;
|
||||
};
|
||||
|
||||
RFControl *rf; // Pixel utilizes RFControl
|
||||
uint32_t pattern[2]; // storage for zero-bit and one-bit pulses
|
||||
uint32_t resetTime; // minimum time (in usec) between pulse trains
|
||||
uint32_t txEndMask; // mask for end-of-transmission interrupt
|
||||
uint32_t txThrMask; // mask for threshold interrupt
|
||||
uint8_t bytesPerPixel; // RGBW=4; RGB=3
|
||||
const uint8_t *map; // color map representing order in which color bytes are transmitted
|
||||
Color onColor; // color used for on() command
|
||||
|
||||
const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel
|
||||
|
||||
static void loadData(void *arg); // interrupt handler
|
||||
volatile static pixel_status_t status; // storage for volatile information modified in interupt handler
|
||||
|
||||
public:
|
||||
|
||||
Pixel(int pin, pixelType_t pixelType=PixelType::GRB); // creates addressable single-wire LED of pixelType connected to pin (such as the SK68 or WS28)
|
||||
void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels
|
||||
void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c
|
||||
|
||||
static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){return(Color().RGB(r,g,b,w));} // an alternative method for returning an RGB Color
|
||||
static Color HSV(float h, float s, float v, double w=0){return(Color().HSV(h,s,v,w));} // an alternative method for returning an HSV Color
|
||||
|
||||
int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1
|
||||
boolean isRGBW(){return(bytesPerPixel==4);} // returns true if RGBW LED, else false if RGB LED
|
||||
void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(*rf);
|
||||
}
|
||||
|
||||
void on() {set(onColor);}
|
||||
void off() {set(RGB(0,0,0,0));}
|
||||
Pixel *setOnColor(Color c){onColor=c;return(this);}
|
||||
|
||||
[[deprecated("Please use Pixel(int pin, pixelType_t pixelType) constructor instead.")]]
|
||||
Pixel(int pin, boolean isRGBW):Pixel(pin,isRGBW?PixelType::GRBW:PixelType::GRB){};
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
// Two-Wire RGB DotStars //
|
||||
////////////////////////////////////////////
|
||||
|
||||
class Dot {
|
||||
|
||||
public:
|
||||
struct Color {
|
||||
union{
|
||||
struct {
|
||||
uint8_t red:8;
|
||||
uint8_t green:8;
|
||||
uint8_t blue:8;
|
||||
uint8_t drive:5;
|
||||
uint8_t flags:3;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
|
||||
Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){ // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive level=[0,31]
|
||||
this->red=r;
|
||||
this->green=g;
|
||||
this->blue=b;
|
||||
this->drive=driveLevel;
|
||||
this->flags=7;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color HSV(float h, float s, float v, double drivePercent=100){ // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive percent=[0,100]
|
||||
float r,g,b;
|
||||
LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b);
|
||||
this->red=r*255;
|
||||
this->green=g*255;
|
||||
this->blue=b*255;
|
||||
this->drive=drivePercent*0.315;
|
||||
this->flags=7;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
bool operator==(const Color& color){
|
||||
return(val==color.val);
|
||||
}
|
||||
|
||||
bool operator!=(const Color& color){
|
||||
return(val!=color.val);
|
||||
}
|
||||
|
||||
Color operator+(const Color& color){
|
||||
Color newColor;
|
||||
newColor.blue=blue+color.blue;
|
||||
newColor.red=red+color.red;
|
||||
newColor.green=green+color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator+=(const Color& color){
|
||||
red+=color.red;
|
||||
blue+=color.blue;
|
||||
green+=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
Color operator-(const Color& color){
|
||||
Color newColor;
|
||||
newColor.blue=blue-color.blue;
|
||||
newColor.red=red-color.red;
|
||||
newColor.green=green-color.green;
|
||||
return(newColor);
|
||||
}
|
||||
|
||||
Color& operator-=(const Color& color){
|
||||
red-=color.red;
|
||||
blue-=color.blue;
|
||||
green-=color.green;
|
||||
return(*this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t dataMask;
|
||||
uint32_t clockMask;
|
||||
volatile uint32_t *dataSetReg;
|
||||
volatile uint32_t *dataClearReg;
|
||||
volatile uint32_t *clockSetReg;
|
||||
volatile uint32_t *clockClearReg;
|
||||
|
||||
public:
|
||||
Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102)
|
||||
void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels
|
||||
void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c
|
||||
|
||||
static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){return(Color().RGB(r,g,b,driveLevel));} // an alternative method for returning an RGB Color
|
||||
static Color HSV(float h, float s, float v, double drivePercent=100){return(Color().HSV(h,s,v,drivePercent));} // an alternative method for returning an HSV Color
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////
|
||||
275
ESP32/HomeSpan-master/src/src/extras/PwmPin.cpp
Normal file
275
ESP32/HomeSpan-master/src/src/extras/PwmPin.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "PwmPin.h"
|
||||
|
||||
///////////////////
|
||||
|
||||
LedC::LedC(uint8_t pin, uint16_t freq, boolean invert){
|
||||
|
||||
if(freq==0)
|
||||
freq=DEFAULT_PWM_FREQ;
|
||||
|
||||
for(int nMode=0;nMode<LEDC_SPEED_MODE_MAX;nMode++){
|
||||
for(int nChannel=0;nChannel<LEDC_CHANNEL_MAX;nChannel++){
|
||||
for(int nTimer=0;nTimer<LEDC_TIMER_MAX;nTimer++){
|
||||
if(!channelList[nChannel][nMode]){
|
||||
|
||||
if(!timerList[nTimer][nMode]){ // if this timer slot is free, use it
|
||||
timerList[nTimer][nMode]=new ledc_timer_config_t; // create new timer instance
|
||||
timerList[nTimer][nMode]->speed_mode=(ledc_mode_t)nMode;
|
||||
timerList[nTimer][nMode]->timer_num=(ledc_timer_t)nTimer;
|
||||
timerList[nTimer][nMode]->freq_hz=freq;
|
||||
timerList[nTimer][nMode]->clk_cfg=LEDC_USE_APB_CLK;
|
||||
|
||||
int res=LEDC_TIMER_BIT_MAX-1; // find the maximum possible resolution
|
||||
while(getApbFrequency()/(freq*pow(2,res))<1)
|
||||
res--;
|
||||
|
||||
timerList[nTimer][nMode]->duty_resolution=(ledc_timer_bit_t)res;
|
||||
if(ledc_timer_config(timerList[nTimer][nMode])!=0){
|
||||
ESP_LOGE(PWM_TAG,"Frequency=%d Hz is out of allowed range ---",freq);
|
||||
delete timerList[nTimer][nMode];
|
||||
timerList[nTimer][nMode]=NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(timerList[nTimer][nMode]->freq_hz==freq){ // if timer matches desired frequency (always true if newly-created above)
|
||||
channelList[nChannel][nMode]=new ledc_channel_config_t; // create new channel instance
|
||||
channelList[nChannel][nMode]->speed_mode=(ledc_mode_t)nMode;
|
||||
channelList[nChannel][nMode]->channel=(ledc_channel_t)nChannel;
|
||||
channelList[nChannel][nMode]->timer_sel=(ledc_timer_t)nTimer;
|
||||
channelList[nChannel][nMode]->intr_type=LEDC_INTR_DISABLE;
|
||||
channelList[nChannel][nMode]->flags.output_invert=invert;
|
||||
channelList[nChannel][nMode]->hpoint=0;
|
||||
channelList[nChannel][nMode]->gpio_num=pin;
|
||||
timer=timerList[nTimer][nMode];
|
||||
channel=channelList[nChannel][nMode];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(pin, freq, invert){
|
||||
|
||||
if(!channel){
|
||||
ESP_LOGE(PWM_TAG,"Can't create LedPin(%d) - no open PWM channels and/or Timers",pin);
|
||||
return;
|
||||
}
|
||||
else
|
||||
ESP_LOGI(PWM_TAG,"LedPin=%d: mode=%d, channel=%d, timer=%d, freq=%d Hz, resolution=%d bits %s",
|
||||
channel->gpio_num,
|
||||
channel->speed_mode,
|
||||
channel->channel,
|
||||
channel->timer_sel,
|
||||
timer->freq_hz,
|
||||
timer->duty_resolution,
|
||||
channel->flags.output_invert?"(inverted)":""
|
||||
);
|
||||
|
||||
ledc_fade_func_install(0);
|
||||
ledc_cbs_t fadeCallbackList = {.fade_cb = fadeCallback}; // for some reason, ledc_cb_register requires the function to be wrapped in a structure
|
||||
ledc_cb_register(channel->speed_mode,channel->channel,&fadeCallbackList,this);
|
||||
|
||||
set(level);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void LedPin::set(float level){
|
||||
|
||||
if(!channel)
|
||||
return;
|
||||
|
||||
if(level>100)
|
||||
level=100;
|
||||
|
||||
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||
|
||||
channel->duty=d;
|
||||
ledc_channel_config(channel);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
int LedPin::fade(float level, uint32_t fadeTime, int fadeType){
|
||||
|
||||
if(!channel)
|
||||
return(1);
|
||||
|
||||
if(fadeState==FADING) // fading already in progress
|
||||
return(1); // return error
|
||||
|
||||
if(level>100)
|
||||
level=100;
|
||||
|
||||
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||
|
||||
if(fadeType==PROPORTIONAL)
|
||||
fadeTime*=fabs((float)ledc_get_duty(channel->speed_mode,channel->channel)-d)/(float)(pow(2,(int)timer->duty_resolution)-1);
|
||||
|
||||
fadeState=FADING;
|
||||
ledc_set_fade_time_and_start(channel->speed_mode,channel->channel,d,fadeTime,LEDC_FADE_NO_WAIT);
|
||||
return(0);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
int LedPin::fadeStatus(){
|
||||
if(fadeState==COMPLETED){
|
||||
fadeState=NOT_FADING;
|
||||
return(COMPLETED);
|
||||
}
|
||||
|
||||
return(fadeState);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
bool IRAM_ATTR LedPin::fadeCallback(const ledc_cb_param_t *param, void *arg){
|
||||
((LedPin *)arg)->fadeState=COMPLETED;
|
||||
return(false);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
|
||||
void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
|
||||
|
||||
// The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||
// h = [0,360]
|
||||
// s = [0,1]
|
||||
// v = [0,1]
|
||||
|
||||
int i;
|
||||
float f, p, q, t;
|
||||
|
||||
if( s == 0 ){
|
||||
*r = *g = *b = v;
|
||||
return;
|
||||
}
|
||||
|
||||
h /= 60;
|
||||
i = floor( h ) ;
|
||||
f = h - i;
|
||||
p = v * ( 1 - s );
|
||||
q = v * ( 1 - s * f );
|
||||
t = v * ( 1 - s * ( 1 - f ) );
|
||||
switch( i % 6 ) {
|
||||
case 0:
|
||||
*r = v;
|
||||
*g = t;
|
||||
*b = p;
|
||||
break;
|
||||
case 1:
|
||||
*r = q;
|
||||
*g = v;
|
||||
*b = p;
|
||||
break;
|
||||
case 2:
|
||||
*r = p;
|
||||
*g = v;
|
||||
*b = t;
|
||||
break;
|
||||
case 3:
|
||||
*r = p;
|
||||
*g = q;
|
||||
*b = v;
|
||||
break;
|
||||
case 4:
|
||||
*r = t;
|
||||
*g = p;
|
||||
*b = v;
|
||||
break;
|
||||
case 5:
|
||||
*r = v;
|
||||
*g = p;
|
||||
*b = q;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
|
||||
ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees) : LedC(pin, 50){
|
||||
|
||||
if(!channel)
|
||||
ESP_LOGE(PWM_TAG,"Can't create ServoPin(%d) - no open PWM channels and/or Timers",pin);
|
||||
else
|
||||
ESP_LOGI(PWM_TAG,"ServoPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits",
|
||||
channel->gpio_num,
|
||||
channel->speed_mode,
|
||||
channel->channel,
|
||||
channel->timer_sel,
|
||||
timer->freq_hz,
|
||||
timer->duty_resolution
|
||||
);
|
||||
|
||||
this->minMicros=minMicros;
|
||||
this->maxMicros=maxMicros;
|
||||
this->minDegrees=minDegrees;
|
||||
microsPerDegree=(double)(maxMicros-minMicros)/(maxDegrees-minDegrees);
|
||||
|
||||
set(initDegrees);
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void ServoPin::set(double degrees){
|
||||
|
||||
if(!channel)
|
||||
return;
|
||||
|
||||
if(!isnan(degrees)){
|
||||
double usec=(degrees-minDegrees)*microsPerDegree+minMicros;
|
||||
|
||||
if(usec<minMicros)
|
||||
usec=minMicros;
|
||||
else if(usec>maxMicros)
|
||||
usec=maxMicros;
|
||||
|
||||
usec*=timer->freq_hz/1e6*(pow(2,(int)timer->duty_resolution)-1);
|
||||
|
||||
channel->duty=usec;
|
||||
} else {
|
||||
channel->duty=0;
|
||||
}
|
||||
|
||||
ledc_channel_config(channel);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
|
||||
ledc_channel_config_t *LedC::channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX]={};
|
||||
ledc_timer_config_t *LedC::timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX]={};
|
||||
121
ESP32/HomeSpan-master/src/src/extras/PwmPin.h
Normal file
121
ESP32/HomeSpan-master/src/src/extras/PwmPin.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ----- PWM Pin Control -----
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Wrappers around the ESP-IDF ledc library to control PWM-based devices:
|
||||
//
|
||||
// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz
|
||||
// - use set(level) to set brightness from 0-100%
|
||||
//
|
||||
// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz
|
||||
// - use set(degrees) to set position to degrees
|
||||
//
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <driver/ledc.h>
|
||||
#include "Blinker.h"
|
||||
|
||||
[[maybe_unused]] static const char* PWM_TAG = "PwmPin";
|
||||
|
||||
#define DEFAULT_PWM_FREQ 5000
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class LedC {
|
||||
|
||||
protected:
|
||||
static ledc_channel_config_t *channelList[LEDC_CHANNEL_MAX][LEDC_SPEED_MODE_MAX];
|
||||
static ledc_timer_config_t *timerList[LEDC_TIMER_MAX][LEDC_SPEED_MODE_MAX];
|
||||
|
||||
ledc_channel_config_t *channel=NULL;
|
||||
ledc_timer_config_t *timer;
|
||||
|
||||
LedC(uint8_t pin, uint16_t freq, boolean invert=false);
|
||||
|
||||
public:
|
||||
int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(channel);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
class LedPin : public LedC {
|
||||
|
||||
public:
|
||||
enum {
|
||||
NOT_FADING,
|
||||
COMPLETED,
|
||||
FADING
|
||||
};
|
||||
|
||||
enum {
|
||||
ABSOLUTE,
|
||||
PROPORTIONAL
|
||||
};
|
||||
|
||||
private:
|
||||
int fadeState=NOT_FADING;
|
||||
static bool fadeCallback(const ledc_cb_param_t *param, void *arg);
|
||||
|
||||
public:
|
||||
LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ, boolean invert=false); // assigns pin to be output of one of 16 PWM channels initial level and frequency
|
||||
void set(float level); // sets the PWM duty to level (0-100)
|
||||
int fade(float level, uint32_t fadeTime, int fadeType=ABSOLUTE); // sets the PWM duty to level (0-100) within fadeTime in milliseconds, returns success (0) or fail (1)
|
||||
int fadeStatus(); // returns fading state
|
||||
|
||||
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////
|
||||
// ServoPin //
|
||||
////////////////////////////////
|
||||
|
||||
class ServoPin : public LedC {
|
||||
uint16_t minMicros;
|
||||
uint16_t maxMicros;
|
||||
double minDegrees;
|
||||
double microsPerDegree;
|
||||
|
||||
public:
|
||||
ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees);
|
||||
ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {};
|
||||
|
||||
void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees]
|
||||
};
|
||||
164
ESP32/HomeSpan-master/src/src/extras/RFControl.cpp
Normal file
164
ESP32/HomeSpan-master/src/src/extras/RFControl.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "RFControl.h"
|
||||
|
||||
///////////////////
|
||||
|
||||
RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if(nChannels==RMT_CHANNEL_MAX/2){
|
||||
#else
|
||||
if(nChannels==RMT_CHANNEL_MAX){
|
||||
#endif
|
||||
ESP_LOGE(RFControl_TAG,"Can't create RFControl(%d) - no open channels",pin);
|
||||
return;
|
||||
}
|
||||
|
||||
config=new rmt_config_t;
|
||||
|
||||
config->rmt_mode=RMT_MODE_TX;
|
||||
config->tx_config.carrier_en=false;
|
||||
config->channel=(rmt_channel_t)nChannels;
|
||||
config->flags=0;
|
||||
config->clk_div = 1;
|
||||
config->mem_block_num=1;
|
||||
config->gpio_num=(gpio_num_t)pin;
|
||||
config->tx_config.idle_output_en=false;
|
||||
config->tx_config.idle_level=RMT_IDLE_LEVEL_LOW;
|
||||
config->tx_config.loop_en=false;
|
||||
|
||||
rmt_config(config);
|
||||
|
||||
if(installDriver)
|
||||
rmt_driver_install(config->channel,0,0);
|
||||
|
||||
// If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock
|
||||
|
||||
this->refClock=refClock;
|
||||
|
||||
if(refClock)
|
||||
#ifdef RMT_SYS_CONF_REG
|
||||
REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 and ESP32-S3 do not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider
|
||||
#else
|
||||
rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2
|
||||
#endif
|
||||
|
||||
nChannels++;
|
||||
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::start(uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from internal data structure, repeated for nCycles, where each tick in pulse is tickTime microseconds long
|
||||
start(data.data(), data.size(), nCycles, tickTime);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from specified data pointer, repeated for nCycles, where each tick in pulse is tickTime microseconds long
|
||||
|
||||
if(!config || nData==0)
|
||||
return;
|
||||
|
||||
rmt_set_clk_div(config->channel,tickTime); // set clock divider
|
||||
|
||||
for(int i=0;i<nCycles;i++) // loop over nCycles
|
||||
rmt_write_items(config->channel, (rmt_item32_t *) data, nData, true); // start transmission and wait until completed before returning
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::clear(){
|
||||
data.clear();
|
||||
lowWord=true;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::add(uint32_t onTime, uint32_t offTime){
|
||||
|
||||
phase(onTime,HIGH);
|
||||
phase(offTime,LOW);
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::phase(uint32_t nTicks, uint8_t phase){
|
||||
|
||||
while(nTicks>0){ // create as many repeated phases as needed to accomodate duration of nTicks
|
||||
uint32_t ticks=nTicks>0x7FFF?0x7FFF:nTicks;
|
||||
nTicks-=ticks;
|
||||
|
||||
if(lowWord)
|
||||
data.push_back(ticks | (phase?(1<<15):0));
|
||||
else
|
||||
data.back()|=ticks<<16 | (phase?(1<<31):0);
|
||||
|
||||
lowWord=!lowWord;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void RFControl::enableCarrier(uint32_t freq, float duty){
|
||||
|
||||
if(duty<0)
|
||||
duty=0;
|
||||
if(duty>1)
|
||||
duty=1;
|
||||
|
||||
if(freq>0){
|
||||
float period=1.0e6/freq*(refClock?1:80);
|
||||
uint32_t highTime=period*duty+0.5;
|
||||
uint32_t lowTime=period*(1.0-duty)+0.5;
|
||||
|
||||
if(highTime>0xFFFF || lowTime>0xFFFF){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Frequency is too low!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
if(highTime==0){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too low or frequency is too high!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
if(lowTime==0){
|
||||
ESP_LOGE(RFControl_TAG,"Can't enable carrier frequency=%d Hz for RF Control pin=%d, duty=%0.2f. Duty is too high or frequency is too high!",freq,config->gpio_num,duty);
|
||||
return;
|
||||
}
|
||||
|
||||
rmt_set_tx_carrier(config->channel,true,highTime,lowTime,RMT_CARRIER_LEVEL_HIGH);
|
||||
} else {
|
||||
rmt_set_tx_carrier(config->channel,false,0,0,RMT_CARRIER_LEVEL_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
uint8_t RFControl::nChannels=0;
|
||||
78
ESP32/HomeSpan-master/src/src/extras/RFControl.h
Normal file
78
ESP32/HomeSpan-master/src/src/extras/RFControl.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////
|
||||
// RF Control Module //
|
||||
////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <soc/rmt_reg.h>
|
||||
#include "driver/rmt.h"
|
||||
#include <vector>
|
||||
|
||||
[[maybe_unused]] static const char* RFControl_TAG = "RFControl";
|
||||
|
||||
using std::vector;
|
||||
|
||||
class RFControl {
|
||||
friend class Pixel;
|
||||
|
||||
private:
|
||||
rmt_config_t *config=NULL;
|
||||
vector<uint32_t> data;
|
||||
boolean lowWord=true;
|
||||
boolean refClock;
|
||||
static uint8_t nChannels;
|
||||
|
||||
RFControl(uint8_t pin, boolean refClock, boolean installDriver); // private constructor (only used by Pixel class)
|
||||
|
||||
public:
|
||||
RFControl(uint8_t pin, boolean refClock=true):RFControl(pin,refClock,true){}; // public constructor to create transmitter on pin, using 1-MHz Ref Tick clock or 80-MHz APB clock
|
||||
|
||||
void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long
|
||||
void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long
|
||||
|
||||
void clear(); // clears transmitter memory
|
||||
void add(uint32_t onTime, uint32_t offTime); // adds pulse of onTime ticks HIGH followed by offTime ticks LOW
|
||||
void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks
|
||||
void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1
|
||||
void disableCarrier(){enableCarrier(0);} // disables carrier wave
|
||||
|
||||
int getPin(){return(config?config->gpio_num:-1);} // returns the pin number, or -1 if no channel defined
|
||||
rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined
|
||||
|
||||
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
|
||||
return(config);
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above
|
||||
|
||||
#define RF_PULSE(highTicks,lowTicks) (1 << 15 | uint32_t(highTicks) | uint32_t(lowTicks) << 16)
|
||||
|
||||
205
ESP32/HomeSpan-master/src/src/extras/StepperControl.cpp
Normal file
205
ESP32/HomeSpan-master/src/src/extras/StepperControl.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "StepperControl.h"
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl::StepperControl(uint32_t priority, uint32_t cpu){
|
||||
|
||||
upLinkQueue = xQueueCreate(1,sizeof(upLink_t));
|
||||
downLinkQueue = xQueueCreate(1,sizeof(downLink_t));
|
||||
xTaskCreateUniversal(motorTask, "motorTaskHandle", 8096, this, priority, &motorTaskHandle, cpu);
|
||||
ESP_LOGI(STEPPER_TAG,"motor task started with priority %d on cpu %d",priority,cpu);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *StepperControl::setAccel(float accelSize, float accelSteps){
|
||||
if(accelSize<0.0){
|
||||
ESP_LOGE(STEPPER_TAG,"accelSize cannot be less than 0.0");
|
||||
return(this);
|
||||
}
|
||||
|
||||
if(accelSteps<1.0){
|
||||
ESP_LOGE(STEPPER_TAG,"accelSteps cannot be less than 1.0");
|
||||
return(this);
|
||||
}
|
||||
|
||||
this->accelSize=accelSize;
|
||||
this->accelSteps=accelSteps;
|
||||
return(this);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void StepperControl::move(int nSteps, uint32_t msDelay, endAction_t endAction){
|
||||
if(msDelay==0){
|
||||
ESP_LOGE(STEPPER_TAG,"msDelay must be greater than zero");
|
||||
return;
|
||||
}
|
||||
|
||||
upLink_t upLinkData = { .nSteps=nSteps, .msDelay=msDelay, .action=MOVE, .endAction=endAction };
|
||||
xQueueOverwrite(upLinkQueue,&upLinkData);
|
||||
waitForAck();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void StepperControl::moveTo(int nPosition, uint32_t msDelay, endAction_t endAction){
|
||||
if(msDelay==0){
|
||||
ESP_LOGE(STEPPER_TAG,"msDelay must be greater than zero");
|
||||
return;
|
||||
}
|
||||
|
||||
upLink_t upLinkData = { .nSteps=nPosition, .msDelay=msDelay, .action=MOVETO, .endAction=endAction };
|
||||
xQueueOverwrite(upLinkQueue,&upLinkData);
|
||||
waitForAck();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
int StepperControl::stepsRemaining(){
|
||||
xQueuePeek(downLinkQueue,&downLinkData,0);
|
||||
return(downLinkData.stepsRemaining);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
int StepperControl::position(){
|
||||
xQueuePeek(downLinkQueue,&downLinkData,0);
|
||||
return(downLinkData.position);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void StepperControl::setPosition(int pos){
|
||||
if(!stepsRemaining()){
|
||||
upLink_t upLinkData = { .nSteps=pos, .msDelay=10, .action=SET_POSITION, .endAction=NONE };
|
||||
xQueueOverwrite(upLinkQueue,&upLinkData);
|
||||
waitForAck();
|
||||
} else {
|
||||
ESP_LOGE(STEPPER_TAG,"can't set position while motor is running");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void StepperControl::waitForAck(){
|
||||
downLinkData.ack=false;
|
||||
while(downLinkData.ack==false)
|
||||
xQueueReceive(downLinkQueue,&downLinkData,0);
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *StepperControl::brake(){
|
||||
move(0,10,BRAKE);
|
||||
while(stepsRemaining());
|
||||
return(this);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *StepperControl::disable(){
|
||||
move(0,10,DISABLE);
|
||||
while(stepsRemaining());
|
||||
return(this);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *StepperControl::enable(){
|
||||
move(0,10);
|
||||
while(stepsRemaining());
|
||||
return(this);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void StepperControl::motorTask(void *args){
|
||||
StepperControl *motor = (StepperControl *)args;
|
||||
upLink_t upLinkData = { .nSteps=0, .msDelay=10, .action=MOVE, .endAction=NONE };
|
||||
downLink_t downLinkData;
|
||||
boolean running=false;
|
||||
|
||||
for(;;){
|
||||
|
||||
if(xQueueReceive(motor->upLinkQueue, &upLinkData, 0)){
|
||||
switch(upLinkData.action){
|
||||
case SET_POSITION:
|
||||
downLinkData.position=upLinkData.nSteps;
|
||||
break;
|
||||
case MOVETO:
|
||||
upLinkData.nSteps-=downLinkData.position;
|
||||
[[fallthrough]];
|
||||
case MOVE:
|
||||
downLinkData.stepsRemaining=upLinkData.nSteps;
|
||||
motor->onEnable();
|
||||
break;
|
||||
}
|
||||
running=true;
|
||||
downLinkData.ack=true;
|
||||
}
|
||||
|
||||
uint32_t msDelay=upLinkData.msDelay;
|
||||
|
||||
if(running==false){
|
||||
vTaskDelay(msDelay);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(downLinkData.stepsRemaining!=0)
|
||||
msDelay+=msDelay * motor->accelSize * (exp(-fabs(upLinkData.nSteps-downLinkData.stepsRemaining)/motor->accelSteps) + exp(-(fabs(downLinkData.stepsRemaining)-1.0)/motor->accelSteps));
|
||||
|
||||
ESP_LOGD(STEPPER_TAG,"Position: %d Steps Remaining: %d Delay=%d ms",downLinkData.position,downLinkData.stepsRemaining,(int)(msDelay));
|
||||
|
||||
int dStep=0;
|
||||
|
||||
if(downLinkData.stepsRemaining>0){
|
||||
dStep=-1;
|
||||
motor->onStep(1);
|
||||
} else if(downLinkData.stepsRemaining<0){
|
||||
dStep=1;
|
||||
motor->onStep(0);
|
||||
} else {
|
||||
if(upLinkData.endAction==DISABLE)
|
||||
motor->onDisable();
|
||||
else if(upLinkData.endAction==BRAKE)
|
||||
motor->onBrake();
|
||||
running=false;
|
||||
}
|
||||
|
||||
xQueueOverwrite(motor->downLinkQueue,&downLinkData);
|
||||
downLinkData.stepsRemaining+=dStep;
|
||||
downLinkData.position-=dStep;
|
||||
downLinkData.ack=false;
|
||||
vTaskDelay(msDelay);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
115
ESP32/HomeSpan-master/src/src/extras/StepperControl.h
Normal file
115
ESP32/HomeSpan-master/src/src/extras/StepperControl.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "PwmPin.h"
|
||||
|
||||
[[maybe_unused]] static const char* STEPPER_TAG = "StepperControl";
|
||||
|
||||
//////////////////////////
|
||||
|
||||
class StepperControl {
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
FULL_STEP_ONE_PHASE=0,
|
||||
FULL_STEP_TWO_PHASE=1,
|
||||
HALF_STEP=2,
|
||||
QUARTER_STEP=4,
|
||||
EIGHTH_STEP=8
|
||||
};
|
||||
|
||||
enum endAction_t {
|
||||
NONE,
|
||||
DISABLE,
|
||||
BRAKE
|
||||
};
|
||||
|
||||
enum action_t {
|
||||
MOVE,
|
||||
MOVETO,
|
||||
SET_POSITION
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct upLink_t {
|
||||
int nSteps;
|
||||
uint32_t msDelay;
|
||||
action_t action;
|
||||
endAction_t endAction;
|
||||
};
|
||||
|
||||
struct downLink_t {
|
||||
int stepsRemaining=0;
|
||||
int position=0;
|
||||
boolean ack=false;
|
||||
};
|
||||
|
||||
float accelSteps=1;
|
||||
float accelSize=0;
|
||||
downLink_t downLinkData;
|
||||
|
||||
TaskHandle_t motorTaskHandle;
|
||||
QueueHandle_t upLinkQueue;
|
||||
QueueHandle_t downLinkQueue;
|
||||
|
||||
void waitForAck();
|
||||
virtual void onStep(boolean direction)=0;
|
||||
virtual void onEnable(){};
|
||||
virtual void onDisable(){};
|
||||
virtual void onBrake(){};
|
||||
static void motorTask(void *args);
|
||||
|
||||
public:
|
||||
|
||||
StepperControl(uint32_t priority=1, uint32_t cpu=0);
|
||||
virtual StepperControl *setStepType(int mode){return(this);};
|
||||
StepperControl *setAccel(float accelSize, float accelSteps);
|
||||
void move(int nSteps, uint32_t msDelay, endAction_t endAction=NONE);
|
||||
void moveTo(int nPosition, uint32_t msDelay, endAction_t endAction=NONE);
|
||||
int position();
|
||||
void setPosition(int pos);
|
||||
int stepsRemaining();
|
||||
StepperControl *enable();
|
||||
StepperControl *disable();
|
||||
StepperControl *brake();
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
|
||||
#include "Stepper_UNIPOLAR.h"
|
||||
#include "Stepper_TB6612.h" // https://www.adafruit.com/product/2448
|
||||
#include "Stepper_A3967.h" // https://www.sparkfun.com/products/12779
|
||||
|
||||
|
||||
struct Stepper_ULN2003A : Stepper_UNIPOLAR {
|
||||
Stepper_ULN2003A(int IN1, int IN2, int IN3, int IN4, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : Stepper_UNIPOLAR(IN1,IN3,IN2,IN4,taskParams){}
|
||||
};
|
||||
118
ESP32/HomeSpan-master/src/src/extras/Stepper_A3967.h
Normal file
118
ESP32/HomeSpan-master/src/src/extras/Stepper_A3967.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Implementation of StepperControl for a Sparkfun A3967 EasyDriver Stepper Motor Driver
|
||||
// Breakout Board (https://www.sparkfun.com/products/12779)
|
||||
|
||||
// This implementation uses the driver board's MS1, MS2, STEP, DIR, and ENABLE pins.
|
||||
// Separate PWM pins on the ESP32 are NOT needed since the driver board contains its own
|
||||
// waveform generation. Supported modes include FULL_STEP (double-phase only), HALF_STEP,
|
||||
// QUARTER_STEP, and EIGHTH_STEP.
|
||||
|
||||
// The motor outputs can be enabled (current running through the coils) or
|
||||
// disabled (no current / high impedence). The EasyDriver board does NOT support
|
||||
// the short brake mode.
|
||||
|
||||
//////////////////////////
|
||||
|
||||
struct Stepper_A3967 : StepperControl {
|
||||
|
||||
int m1Pin;
|
||||
int m2Pin;
|
||||
int stepPin;
|
||||
int dirPin;
|
||||
int enablePin;
|
||||
|
||||
//////////////////////////
|
||||
|
||||
Stepper_A3967(int m1Pin, int m2Pin, int stepPin, int dirPin, int enablePin, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : StepperControl(taskParams.first,taskParams.second){
|
||||
this->m1Pin=m1Pin;
|
||||
this->m2Pin=m2Pin;
|
||||
this->stepPin=stepPin;
|
||||
this->dirPin=dirPin;
|
||||
this->enablePin=enablePin;
|
||||
|
||||
pinMode(m1Pin,OUTPUT);
|
||||
pinMode(m2Pin,OUTPUT);
|
||||
pinMode(stepPin,OUTPUT);
|
||||
pinMode(dirPin,OUTPUT);
|
||||
pinMode(enablePin,OUTPUT);
|
||||
|
||||
setStepType(FULL_STEP_TWO_PHASE);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onStep(boolean direction) override {
|
||||
digitalWrite(dirPin,direction);
|
||||
digitalWrite(stepPin,HIGH);
|
||||
digitalWrite(stepPin,LOW);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onEnable() override {
|
||||
digitalWrite(enablePin,0);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onDisable() override {
|
||||
digitalWrite(enablePin,1);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *setStepType(int mode) override {
|
||||
switch(mode){
|
||||
case FULL_STEP_TWO_PHASE:
|
||||
digitalWrite(m1Pin,LOW);
|
||||
digitalWrite(m2Pin,LOW);
|
||||
break;
|
||||
case HALF_STEP:
|
||||
digitalWrite(m1Pin,HIGH);
|
||||
digitalWrite(m2Pin,LOW);
|
||||
break;
|
||||
case QUARTER_STEP:
|
||||
digitalWrite(m1Pin,LOW);
|
||||
digitalWrite(m2Pin,HIGH);
|
||||
break;
|
||||
case EIGHTH_STEP:
|
||||
digitalWrite(m1Pin,HIGH);
|
||||
digitalWrite(m2Pin,HIGH);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
|
||||
}
|
||||
return(this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
179
ESP32/HomeSpan-master/src/src/extras/Stepper_TB6612.h
Normal file
179
ESP32/HomeSpan-master/src/src/extras/Stepper_TB6612.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Implementation of StepperControl for an Adafruit TB6612 1.2A DC/Stepper Motor Driver
|
||||
// Breakout Board (https://www.adafruit.com/product/2448)
|
||||
|
||||
// This implementation supports two constructors reflecting implementations both with and
|
||||
// without the use of PWM pins. The first operates the driver board using only its
|
||||
// four digital control pins: AIN1, AIN2, BIN1, BIN2. In this configuration the
|
||||
// PWMA, PWMB, and STBY pins on the driver board should be directly connected to Vcc.
|
||||
|
||||
// The second configuration uses the four digital control pins (AIN1, AIN2, BIN1, and BIN2)
|
||||
// as well as the PWMA and PWMB pins. In this configuration only the STBY pin on the
|
||||
// driver board should be directly connected to Vcc.
|
||||
|
||||
// The first configuration supports both single-phase and double-phase FULL_STEP modes,
|
||||
// as well as a HALF_STEP mode. The second configuration also includes QUARTER_STEP
|
||||
// and EIGHTH_STEP modes, made possible by the use of the PWM pins to micro-step the motor.
|
||||
|
||||
// In either configuration the motor outputs can be enabled (current running through the coils)
|
||||
// disabled (no current / high impedence) or set to a short brake.
|
||||
|
||||
//////////////////////////
|
||||
|
||||
struct Stepper_TB6612 : StepperControl {
|
||||
|
||||
int ain1, ain2, bin1, bin2;
|
||||
uint8_t phase, nPhases;
|
||||
double offset;
|
||||
LedPin *pwmA=NULL, *pwmB;
|
||||
|
||||
//////////////////////////
|
||||
|
||||
Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : StepperControl(taskParams.first,taskParams.second){
|
||||
|
||||
ain1=AIN1;
|
||||
ain2=AIN2;
|
||||
bin1=BIN1;
|
||||
bin2=BIN2;
|
||||
|
||||
pinMode(ain1,OUTPUT);
|
||||
pinMode(ain2,OUTPUT);
|
||||
pinMode(bin1,OUTPUT);
|
||||
pinMode(bin2,OUTPUT);
|
||||
|
||||
setStepType(FULL_STEP_TWO_PHASE);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
Stepper_TB6612(int AIN1, int AIN2, int BIN1, int BIN2, int PWMA, int PWMB, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : Stepper_TB6612(AIN1,AIN2,BIN1,BIN2,taskParams){
|
||||
|
||||
pwmA=new LedPin(PWMA,0,50000);
|
||||
pwmB=new LedPin(PWMB,0,50000);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onEnable() override {
|
||||
setPins();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onDisable() override {
|
||||
digitalWrite(ain1,0);
|
||||
digitalWrite(ain2,0);
|
||||
digitalWrite(bin1,0);
|
||||
digitalWrite(bin2,0);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onBrake() override {
|
||||
digitalWrite(ain1,1);
|
||||
digitalWrite(ain2,1);
|
||||
digitalWrite(bin1,1);
|
||||
digitalWrite(bin2,1);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onStep(boolean direction) override {
|
||||
if(direction)
|
||||
phase=(phase+1)%nPhases;
|
||||
else
|
||||
phase=(phase+nPhases-1)%nPhases;
|
||||
|
||||
setPins();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void setPins(){
|
||||
float levelA=cos(phase*TWO_PI/nPhases+offset)*100.0;
|
||||
float levelB=sin(phase*TWO_PI/nPhases+offset)*100.0;
|
||||
digitalWrite(ain1,levelA>0.01);
|
||||
digitalWrite(ain2,levelA<-0.01);
|
||||
digitalWrite(bin1,levelB>0.01);
|
||||
digitalWrite(bin2,levelB<-0.01);
|
||||
if(pwmA){
|
||||
pwmA->set(fabs(levelA));
|
||||
pwmB->set(fabs(levelB));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *setStepType(int mode) override {
|
||||
|
||||
switch(mode){
|
||||
case FULL_STEP_ONE_PHASE:
|
||||
phase=0;
|
||||
nPhases=4;
|
||||
offset=0;
|
||||
break;
|
||||
case FULL_STEP_TWO_PHASE:
|
||||
phase=0;
|
||||
nPhases=4;
|
||||
offset=TWO_PI/8.0;
|
||||
break;
|
||||
case HALF_STEP:
|
||||
phase=0;
|
||||
nPhases=8;
|
||||
offset=0;
|
||||
break;
|
||||
case QUARTER_STEP:
|
||||
if(!pwmA){
|
||||
ESP_LOGE(STEPPER_TAG,"QUARTER_STEP requires PWM pins");
|
||||
return(this);
|
||||
}
|
||||
phase=0;
|
||||
nPhases=16;
|
||||
offset=0;
|
||||
break;
|
||||
case EIGHTH_STEP:
|
||||
if(!pwmA){
|
||||
ESP_LOGE(STEPPER_TAG,"EIGHTH_STEP requires PWM pins");
|
||||
return(this);
|
||||
}
|
||||
phase=0;
|
||||
nPhases=32;
|
||||
offset=0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
|
||||
}
|
||||
return(this);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////
|
||||
169
ESP32/HomeSpan-master/src/src/extras/Stepper_UNIPOLAR.h
Normal file
169
ESP32/HomeSpan-master/src/src/extras/Stepper_UNIPOLAR.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*********************************************************************************
|
||||
* 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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Implementation of a basic 4-wire controller for a center-tapped Unipolar Stepper Motor
|
||||
// with two coils (Coil 1 and Coil 2) each having two driving inputs (A and B).
|
||||
//
|
||||
// Requires 4 pins on the ESP32 connected to an appropriate driver circuit, such as a set
|
||||
// of Darlington transitors, that are in turn connected to each of the 4 Stepper Motor wires:
|
||||
//
|
||||
// * Coil 1, Input A (1A)
|
||||
// * Coil 1, Input B (1B)
|
||||
// * Coil 2, Input A (2A)
|
||||
// * Coil 2, Input B (2B)
|
||||
//
|
||||
// When any of the pins are HIGH, the driver circuit should cause current to flow in the corresponding
|
||||
// half of the coil. When the pin is set LOW, the driver circuit should stop the flow of current through
|
||||
// that half of the coil. Supported modes and driving logic are as follows:
|
||||
//
|
||||
// FULL_STEP_ONE_PHASE (4 steps, where current flows only through ONE of the phases of ONE of the coils at each step):
|
||||
//
|
||||
// 1A 2A 1B 2B
|
||||
// Step 1: HI -- -- --
|
||||
// Step 2: -- HI -- --
|
||||
// Step 3: -- -- HI --
|
||||
// Step 4: -- -- -- HI
|
||||
//
|
||||
// FULL_STEP_TWO_PHASE (4 steps, where current flows through ONE of the phases of EACH of the coils at each step):
|
||||
//
|
||||
// 1A 2A 1B 2B
|
||||
// Step 1: HI HI -- --
|
||||
// Step 2: -- HI HI --
|
||||
// Step 3: -- -- HI HI
|
||||
// Step 4: HI -- -- HI
|
||||
//
|
||||
// HALF_STEP (8 steps that alternate between the 4 steps of the FULL_STEP modes above):
|
||||
//
|
||||
// 1A 2A 1B 2B
|
||||
// Step 1: HI -- -- --
|
||||
// Step 2: HI HI -- --
|
||||
// Step 3: -- HI -- --
|
||||
// Step 4: -- HI HI --
|
||||
// Step 5: -- -- HI --
|
||||
// Step 6: -- -- HI HI
|
||||
// Step 7: -- -- -- HI
|
||||
// Step 8: HI -- -- HI
|
||||
|
||||
// NOTE ORDER OF CONSTRUCTOR PARAMETERS: First the two pins that drive the A and B side of Coil 1,
|
||||
// followed by the two pints that drive the A and B side of Coil 2.
|
||||
|
||||
// It does not matter which coil is defined as 1 or 2, nor which side is called A or B, as long as
|
||||
// the first two parameters are for one of the coils and the second two are for the other coil.
|
||||
|
||||
// Note: This driver supports enabling and disabling all current flow, but does NOT support a short brake.
|
||||
|
||||
//////////////////////////
|
||||
|
||||
struct Stepper_UNIPOLAR : StepperControl {
|
||||
|
||||
int c1A, c1B, c2A, c2B;
|
||||
uint8_t phase, nPhases;
|
||||
double offset;
|
||||
|
||||
//////////////////////////
|
||||
|
||||
Stepper_UNIPOLAR(int coil1A, int coil1B, int coil2A, int coil2B, std::pair<uint32_t, uint32_t> taskParams = {1,0}) : StepperControl(taskParams.first,taskParams.second) {
|
||||
|
||||
c1A=coil1A;
|
||||
c1B=coil1B;
|
||||
c2A=coil2A;
|
||||
c2B=coil2B;
|
||||
|
||||
pinMode(c1A,OUTPUT);
|
||||
pinMode(c1B,OUTPUT);
|
||||
pinMode(c2A,OUTPUT);
|
||||
pinMode(c2B,OUTPUT);
|
||||
|
||||
setStepType(FULL_STEP_ONE_PHASE);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onEnable() override {
|
||||
setPins();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onDisable() override {
|
||||
digitalWrite(c1A,0);
|
||||
digitalWrite(c1B,0);
|
||||
digitalWrite(c2A,0);
|
||||
digitalWrite(c2B,0);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void onStep(boolean direction) override {
|
||||
if(direction)
|
||||
phase=(phase+1)%nPhases;
|
||||
else
|
||||
phase=(phase+nPhases-1)%nPhases;
|
||||
|
||||
setPins();
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
void setPins(){
|
||||
float levelA=cos(phase*TWO_PI/nPhases+offset)*100.0;
|
||||
float levelB=sin(phase*TWO_PI/nPhases+offset)*100.0;
|
||||
digitalWrite(c1A,levelA>0.01);
|
||||
digitalWrite(c1B,levelA<-0.01);
|
||||
digitalWrite(c2A,levelB>0.01);
|
||||
digitalWrite(c2B,levelB<-0.01);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
StepperControl *setStepType(int mode) override {
|
||||
|
||||
switch(mode){
|
||||
case FULL_STEP_ONE_PHASE:
|
||||
phase=0;
|
||||
nPhases=4;
|
||||
offset=0;
|
||||
break;
|
||||
case FULL_STEP_TWO_PHASE:
|
||||
phase=0;
|
||||
nPhases=4;
|
||||
offset=TWO_PI/8.0;
|
||||
break;
|
||||
case HALF_STEP:
|
||||
phase=0;
|
||||
nPhases=8;
|
||||
offset=0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(STEPPER_TAG,"Unknown StepType=%d",mode);
|
||||
}
|
||||
return(this);
|
||||
}
|
||||
|
||||
};
|
||||
64
ESP32/HomeSpan-master/src/src/extras/extras.ino
Normal file
64
ESP32/HomeSpan-master/src/src/extras/extras.ino
Normal file
@@ -0,0 +1,64 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "PwmPin.h"
|
||||
|
||||
ServoPin servo(21,0,500,2200,-60,60);
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
Serial.print("\n\nReady\n\n");
|
||||
|
||||
for(int count=0;count<3;count++){
|
||||
for(int i=-60;i<61;i++){
|
||||
servo.set(i);
|
||||
delay(10);
|
||||
}
|
||||
|
||||
for(int i=60;i>-61;i--){
|
||||
servo.set(i);
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
delay(5000);
|
||||
|
||||
servo.set(NAN);
|
||||
|
||||
delay(10000);
|
||||
|
||||
servo.set(0);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
}
|
||||
67
ESP32/HomeSpan-master/src/version.h
Normal file
67
ESP32/HomeSpan-master/src/version.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// HomeSpan Version //
|
||||
|
||||
#define HS_MAJOR 1
|
||||
#define HS_MINOR 9
|
||||
#define HS_PATCH 1
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP32
|
||||
#error ERROR: HOMESPAN IS ONLY AVAILABLE FOR ESP32 MICROCONTROLLERS!
|
||||
#include <FATAL_ERROR>
|
||||
#endif
|
||||
|
||||
#include <esp_arduino_version.h>
|
||||
|
||||
#if ESP_ARDUINO_VERSION_MAJOR!=2
|
||||
#error ERROR: HOMESPAN REQUIRES VERSION 2 OF THE ARDUINO ESP32 LIBRARY. HOMESPAN IS NOT COMPATIBLE WITH VERSION 1 OR VERSION 3
|
||||
#include <FATAL_ERROR>
|
||||
#endif
|
||||
|
||||
#define STRINGIFY(x) _STR(x)
|
||||
#define _STR(x) #x
|
||||
|
||||
#define HOMESPAN_VERSION STRINGIFY(HS_MAJOR) "." STRINGIFY(HS_MINOR) "." STRINGIFY(HS_PATCH)
|
||||
|
||||
#define VERSION(major,minor,patch) major*10000+minor*100+patch
|
||||
|
||||
#ifndef REQUIRED
|
||||
#define REQUIRED 0
|
||||
#endif
|
||||
|
||||
#if (REQUIRED>VERSION(HS_MAJOR,HS_MINOR,HS_PATCH))
|
||||
#error ERROR: THIS SKETCH REQUIRES A LATER VERSION OF THE HOMESPAN LIBRARY
|
||||
#include <FATAL_ERROR>
|
||||
#endif
|
||||
|
||||
#define ARDUINO_ESP_VERSION STRINGIFY(ESP_ARDUINO_VERSION_MAJOR) "." STRINGIFY(ESP_ARDUINO_VERSION_MINOR) "." STRINGIFY(ESP_ARDUINO_VERSION_PATCH)
|
||||
Reference in New Issue
Block a user