commit
cccbceeab7
13
docs/NOW.md
13
docs/NOW.md
|
|
@ -8,16 +8,19 @@ SpanPoint creates all the internal data queues needed to manage message flow, co
|
|||
|
||||
SpanPoint is part of the main HomeSpan library and is accessible by adding `#include "HomeSpan.h"` near the top of your sketch. Detailed descriptions of the SpanPoint class and all of its methods are provided below.
|
||||
|
||||
## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize, int queueDepth=1)*
|
||||
## *SpanPoint(const char \*macAddress, int sendSize, int receiveSize [, int queueDepth=1 [, boolean useAPaddress=false]])*
|
||||
|
||||
Creating an instance of this **class** enables the device to send messages to, and/or receive messages from, a "complementary" instance of *SpanPoint* on another ESP32 device. Arguments, along with their defaults if left unspecified, are as follows:
|
||||
|
||||
* *macAddress* - the MAC Address of the *other* device to which you want to send data to, and/or receive data from, in the standard 6-byte format "XX:XX:XX:XX:XX:XX", where each XX represents a single 2-digit hexidecimal byte from 00 to FF
|
||||
* *sendSize* - the size, in bytes, of any messages that will be sent from this device to the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `send()` to transmit any messages from this device to the *other* device
|
||||
* *receiveSize* - the size, in bytes, of any messages that will be received by this device from the *other* device. Allowed range is 0 to 200, where a value of 0 is used to indicate to SpanPoint that you will **not** be using `get()` to retreive any messages transmitted by the *other* device to this device
|
||||
* *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. Default=1 if left unspecified, which should be sufficient for most applications. See `get()` below for further details.
|
||||
* *queueDepth* - the depth of the queue reserved to hold messages of *receiveSize* bytes that were received by this device from the *other* device, but not yet retreived using `get()`. Default=1 if left unspecified, which should be sufficient for most applications. See `get()` below for further details
|
||||
* *useAPaddress* - SpanPoint normally communicates via the ESP32's WiFi Station (STA) Interface using the STA MAC Address. Setting *useAPaddress* to *true* causes SpanPoint to instead communicate via the ESP32's WiFi Access Point (AP) Interface using the AP MAC Address. This is needed when using an ESP-8266 as a Remote Device (see below). Default=*false* if left unspecified
|
||||
|
||||
> SpanPoint objects created on two separate devices are considered "complementary" if the MAC Addresses specified in each SpanPoint object references each other's devices, and the *sendSize* and *receiveSize* of the SpanPoint object on one device matches, respectively, the *receiveSize* and *sendSize* of the SpanPoint object on the *other* device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the *other* device.
|
||||
A list of all SpanPoint objects instantiated in a sketch, their parameters as specified above, and the specific MAC Address each Remote Device should use to connect back to the Main HomeSpan Device, is displayed in the Serial Monitor by typing 'i' into the CLI
|
||||
|
||||
> SpanPoint objects created on two separate devices are considered "complementary" if the MAC Addresses specified in each SpanPoint object references each other's devices, and the *sendSize* and *receiveSize* of the SpanPoint object on one device matches, respectively, the *receiveSize* and *sendSize* of the SpanPoint object on the *other* device, with the exception that it is always okay to set either the *sendSize* or *receiveSize* to zero regardless of the value set on the *other* device
|
||||
|
||||
SpanPoint will throw a fatal error during instantiation and halt the sketch if:
|
||||
* the *macAddress* specified is ill-formed, or
|
||||
|
|
@ -72,12 +75,12 @@ Also note that regardless of whether or not the queue if full, if the size of a
|
|||
|
||||
One of the primary reasons for using SpanPoint is to enable the deployement of battery-powered devices. Since HomeKit requires an always-on WiFi connection, wall-power is a must. But ESP-NOW does not require always-on connectivity to a central WiFi network, which makes it possible to power things like remote-sensor devices with just a battery. Such battery-powered "Remote Devices" can take periodic local measurements and transmit them via SpanPoint messages to a wall-powered "Main Device" that is running a full HomeSpan sketch connected to HomeKit via a central WiFi network.
|
||||
|
||||
Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains three sketches:
|
||||
Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../Other%20Examples/RemoteSensors). This folder contains the following sketches:
|
||||
|
||||
* *MainDevice.ino* - a full HomeSpan sketch that implements two Temperature Sensor Accessories, but instead of taking its own temperature measurements, it uses SpanPoint to read messages containing temperature updates from other Remote Devices
|
||||
* *RemoteDevice.ino* - a lightweight sketch that simulates taking periodic temperature measurements, which are then transmitted to the Main Device via SpanPoint
|
||||
* *RemoteTempSensor.ino* - a lightweight sketch that is similar to *RemoteDevice.ino*, except that instead of simulating a temperature sensor, it implements an actual Adafruit ADT7410 I2C-based temperature sensor. This sketch also uses some power-management techniques to extend battery life, such as lowering the CPU frequency and entering into deep-sleep after each measurement is taken
|
||||
|
||||
* *RemoteDevice8266.ino* - similar in function to *RemoteDevice.ino*, but implemented to run on an ESP8266 device using native ESP-NOW commands (since neither HomeSpan nor SpanPoint support the ESP8266). Note that the "complementary" SpanPoint object on the ESP32 that receives data from the ESP8266 must be configured to use the ESP32's *AP MAC Address* (instead of the *STA MAC Address*) by setting *useAPaddress* to *true* in the SpanPoint constructor
|
||||
---
|
||||
|
||||
[↩️](README.md) Back to the Welcome page
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ The ESP32 has up to 16 PWM channels that can be used to drive a variety of devic
|
|||
|
||||
`#include "extras/PwmPin.h"`
|
||||
|
||||
## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])*
|
||||
## *LedPin(uint8_t pin [,float level [,uint16_t frequency [,boolean invert]]])*
|
||||
|
||||
Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows:
|
||||
|
||||
* *pin* - the pin on which the PWM control signal will be output
|
||||
* *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off)
|
||||
* *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0
|
||||
* *boolean* - if true, the output of the PWM signal will be inverted. Default=false
|
||||
|
||||
The following methods are supported:
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
|
|||
* Utilizes a unique *Service-Centric* approach to creating HomeKit devices
|
||||
* Takes full advantage of the widely-popular Arduino IDE
|
||||
* 100% HAP-R2 compliance
|
||||
* 38 integrated HomeKit Services
|
||||
* 41 integrated HomeKit Services
|
||||
* Operates in either Accessory or Bridge mode
|
||||
* Supports pairing with Setup Codes or QR Codes
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
|
|||
* Dedicated classes that utilize the ESP32's 16-channel PWM peripheral for easy control of:
|
||||
* LED Brightness
|
||||
* Servo Motors
|
||||
* Integrated Push Button functionality supporting single, double, and long presses of:
|
||||
* Integrated Push Button and Toggle Switch functionality supporting single, double, and long presses of:
|
||||
* Physical pushbuttons that connect an ESP32 pin to either ground or VCC
|
||||
* Touch pads/sensors connected to an ESP32 pin (for ESP32 devices that support touch pads)
|
||||
* Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals
|
||||
|
|
@ -48,29 +48,40 @@ HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](ht
|
|||
* Launch the WiFi Access Point
|
||||
* A standalone, detailed End-User Guide
|
||||
|
||||
## ❗Latest Update - HomeSpan 1.7.0 (11/11/2022)
|
||||
## ❗Latest Update - HomeSpan 1.7.1 (2/4/2023)
|
||||
|
||||
* **ESP-NOW is now fully integrated into HomeSpan!**
|
||||
* New dedicated class, **SpanPoint**, that facilitates bi-directional device-to-device communication between multiple ESP32 devices
|
||||
* Provides automatic calibration of WiFi channels to ensure compatibility with HomeSpan's normal WiFi connectivity
|
||||
* Includes detailed [Example Sketches](../Other%20Examples/RemoteSensors) demonstrating how SpanPoint can be used to implement a battery-powered Remote Sensor
|
||||
* See the dedicated [SpanPoint Tutorial Page](NOW.md) for full details
|
||||
* **SpanPoint support for the ESP8266!**
|
||||
* Use ESP-NOW on an ESP8266 to connect to HomeSpan running on an ESP32
|
||||
* See the [SpanPoint Tutorial Page](NOW.md) for more info as well as a detailed ESP8266 example
|
||||
|
||||
* **NeoPixels can now be used as a Status LED**
|
||||
* Adds`homeSpan.setStatusPixel()` method
|
||||
* Works well with ESP32 boards that have a built-in NeoPixel LED
|
||||
* **SpanPoint WiFi channel scanning upgrade**
|
||||
* SpanPoint now saves the last WiFi channel successfully used for transmission in non-volatile storage
|
||||
* Avoids the need for SpanPoint to restart a full scan of all channels upon reboot
|
||||
* Dramatically extends battery life when used in conjunction with deep-sleep functionality
|
||||
|
||||
* **New SpanToggle class**
|
||||
* Similar to SpanButton, but designed for toggle switches
|
||||
* Integrated de-bounce logic prevents false triggers
|
||||
* Ideal for use with Contact Sensors
|
||||
* See the [API Reference](Reference.md) for details
|
||||
|
||||
* **New functionality to track HomeSpan run-time status**
|
||||
* Adds `homeSpan.setStatusCallback()` method providing users with a callback function whenever the internal state of HomeSpan changes, such as from *WiFi Needed* to *WiFi Connecting...*
|
||||
* Tracks changes to the run-time state of HomeSpan that would normally trigger a change in the blinking pattern of the (optional) Status LED
|
||||
* **Added Television Speaker Service**
|
||||
* Control the volume of a Television from Apple's Remote App
|
||||
* See the [Television Services Page](TVServices.md) for details and examples
|
||||
|
||||
* **Added support for byte-array ("DATA") Characteristics**
|
||||
* Useful for experimentation with other HomeKit applications, such as *Eve for HomeKit*
|
||||
* Includes three new Characteristic methods: `setData()`, `getData()`, and `getNewData()`
|
||||
* Includes new macro `CUSTOM_CHAR_DATA()` to easily create custom byte-array Characteristics
|
||||
* See the [API Reference](Reference.md) for details
|
||||
|
||||
* **Important Bug Fixes**
|
||||
* **LedPin upgrade**
|
||||
* New option to invert the PWM signal
|
||||
* Useful for generating two, separate out-of-phase PWM signals (one inverted, one not) to drive certain two-pin devices in a push/pull manner, such as a piezo-electric buzzer
|
||||
* See the [PWM Page](PWM.md) for details
|
||||
|
||||
* Fixed bug in controller-update logic associated with changes to the way Apple now handles HomeKit Hubs that was producing *ERROR: Device not yet paired!* messages
|
||||
|
||||
* Fixed bug in touch sensor logic that would cause compile failure when using Arduino-ESP32 versions 2.0.0-2.0.2
|
||||
* **Bug Fixes**
|
||||
* Added logic to prevent the extraneous broadcasting of an Access Point SSID when SpanPoint is being used
|
||||
|
||||
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
||||
|
||||
|
|
@ -112,3 +123,9 @@ You ***do not*** need to read the entire document. The whole point of HomeSpan
|
|||
### Feedback or Questions?
|
||||
|
||||
Please consider adding to the [HomeSpan Discussion Board](https://github.com/HomeSpan/HomeSpan/discussions), or email me directly at [homespan@icloud.com](mailto:homespan@icloud.com).
|
||||
|
||||
### About the Author
|
||||
|
||||
HomeSpan was developed and continues to be maintained and supported by Gregg Berman. It was originally conceived to solve the pesky problem of not being able to operate an RF-controlled kitchen vent hood with Siri. I hope you find it useful as well as fun to use.
|
||||
|
||||
This is my second large-scale open-source project --- my first was the design of an open-source sytem for operating model railroads using nothing more than an Arduino Uno and Arduino Motor Shield to generate digital command and control (DCC) signals. Though I have not been involved with the model railroading hobby for many years, videos showcasing my original system (dubbed DCC++), along with detailed tutorials of how it works, are still available on the [DCC++ YouTube Channel](https://www.youtube.com/@dcc2840/videos).
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ This is a **base class** from which all HomeSpan Characteristics are derived, an
|
|||
* example with template excluded : `int tilt = Characteristic::CurrentTiltAngle->getVal();`
|
||||
|
||||
* `type T getNewVal<T>()`
|
||||
* a template method that returns the desired **new** value to which a HomeKit Controller has requested the Characteristic be updated. Same casting rules as for `getVal<>()`. Only applicable for numerical-based Characteristics
|
||||
* a template method that returns the desired **new** value to which a HomeKit Controller has requested the Characteristic be updated. Same casting rules as for `getVal<>()`
|
||||
|
||||
* `void setVal(value [,boolean notify])`
|
||||
* sets the value of a numerical-based Characteristic to *value*, and, if *notify* is set to true, notifies all HomeKit Controllers of the change. The *notify* flag is optional and will be set to true if not specified. Setting the *notify* flag to false allows you to update a Characateristic without notifying any HomeKit Controllers, which is useful for Characteristics that HomeKit automatically adjusts (such as a countdown timer) but will be requested from the Accessory if the Home App closes and is then re-opened
|
||||
|
|
@ -351,6 +351,23 @@ This is a **base class** from which all HomeSpan Characteristics are derived, an
|
|||
* `void setString(const char *value)`
|
||||
* equivalent to `setVal(value)`, but used exclusively for string-characteristics (i.e. a null-terminated array of characters)
|
||||
|
||||
#### The following methods are supported for DATA (i.e. byte-array) Characteristics:
|
||||
|
||||
* `size_t getData(uint8_t *data, size_t len)`
|
||||
* similar to `getVal()`, but exclusively used for byte-array Characteristics
|
||||
* fills byte array *data*, of specified size *len*, with all bytes "encoded" as the current value of the Characteristic
|
||||
* returns the total number of bytes encoded in the Characteristic
|
||||
* if *len* is less than the total number of bytes encoded, no data is extracted (i.e. *data* is unmodified) and a warning message is thrown indicating that the size of the *data* array is insufficient to extract all the bytes encoded in the Characteristic
|
||||
* setting *data* to NULL returns the total number of bytes encoded without extracting any data. This can be used to help create a *data* array of sufficient size in advance of extracting the data
|
||||
|
||||
* `size_t getNewData(uint8_t *data, size_t len)`
|
||||
* similar to `getData()`, but fills byte array *data*, of specified size *len*, with bytes based on the desired **new** value to which a HomeKit Controller has requested the Characteristic be updated
|
||||
|
||||
* `void setData(uint8_t *data, size_t len)`
|
||||
* similar to `setVal()`, but exclusively used for byte-array Characteristics
|
||||
* updates the Characteristic by "filling" it with *len* bytes from bytes array *data*
|
||||
* note: byte-array Characteristics are encoded and transmitted as base-64 strings. HomeSpan automatically peforms all encoding and decoding between this format and the specified byte arrays. But when output to the Serial Monitor, the value of byte-array Characteristics are displayed in their base-64 format (as opposed to being shown as a byte array), since base-64 is the representation that is actually transmitted to and from HomeKit
|
||||
|
||||
#### The following methods are supported for all Characteristics:
|
||||
|
||||
* `boolean updated()`
|
||||
|
|
@ -440,6 +457,22 @@ In addition, you can also override the ESP32's touch sensor timing parameters us
|
|||
* changes the measurement time and sleep time clock cycles to *measureTime* and *sleepTime*, respectively. This is simply a pass-though call to the Arduino-ESP32 library `touchSetCycles()` function
|
||||
* unless a specific threshold value has been set with `setTouchThreshold()`, `setTouchCycles()` must be called *before* instantiating the first SpanButton() of type `SpanButton::TRIGGER_ON_TOUCH` so that HomeSpan will calibrate the touch threshold based on the new timing parameters specified
|
||||
|
||||
### *SpanToggle(int pin, boolean (\*triggerType)(int)=PushButton::TRIGGER_ON_LOW, uint16_32 toggleTime=5)*
|
||||
|
||||
Creating an instance of this **class** attaches a toggle-switch handler to the ESP32 *pin* specified. This is a child class of *SpanButton* and thus derives all of the same functionality. For example, you can set *triggerType* to PushButton::TRIGGER_ON_HIGH, create your own trigger function, etc. However, instead of HomeSpan calling `button(int pin, int pressType)` when a pushbutton is "pressed," HomeSpan calls the same `button()` method when the switch is "toggled" from one position to another. In this case the parameter *pressType* that is passed into `button()` has a different set of enumerations:
|
||||
* 3=switch is closed (`SpanToggle::CLOSED`)
|
||||
* 4=switch is open (`SpanToggle::OPEN`)
|
||||
|
||||
Note there are no *singleTime*, *longTime*, or *doubleTime* paramaters in the constructor since you can't single-press, double-press, or long-press a toggle switch. Instead, the constructor supports the single parameter *toggleTime* (default=5ms if left unspecified) that sets the minimum time at which the switch needs to be moved to the closed position in order to trigger a call to the `button()` method. This effectively "debounces" the toggle switch.
|
||||
|
||||
SpanToggle also supports the following additional method:
|
||||
|
||||
* `int position()`
|
||||
* returns the current position of the toggle switch (i.e. SpanToggle::CLOSED or SpanToggle::OPEN)
|
||||
* is equivalent to the *pressType* parameter passed to the `button()` method, but can be called from anywhere in a sketch
|
||||
* useful for reading the initial state of a contact switch upon start-up so that the initial value of Characteristic::ContactSensorState can be set accordingly
|
||||
* example `sensorState=new Characteristic::ContactSensorState(toggleSwitch->position()==SpanToggle::OPEN);`
|
||||
|
||||
### *SpanUserCommand(char c, const char \*desc, void (\*f)(const char \*buf [,void \*obj]) [,void \*userObject])*
|
||||
|
||||
Creating an instance of this **class** adds a user-defined command to the HomeSpan Command-Line Interface (CLI), where:
|
||||
|
|
@ -478,17 +511,18 @@ To create more than one user-defined command, simply create multiple instances o
|
|||
|
||||
### *CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)*
|
||||
### *CUSTOM_CHAR_STRING(name,uuid,perms,defaultValue)*
|
||||
### *CUSTOM_CHAR_DATA(name,uuid,perms)*
|
||||
|
||||
Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as *Eve for HomeKit*). The first form should be used create numerical Characterstics (e.g., UINT8, BOOL...). The second form is used to String-based Characteristics. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for *defaultValue* when applied to a STRING-based Characteristic):
|
||||
Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as *Eve for HomeKit*). The first form should be used create numerical Characterstics (e.g., UINT8, BOOL...). The second form is used to STRING-based Characteristics. The third form is used for DATA-based (i.e. byte-array) Characteristics. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for *defaultValue* when applied to a STRING-based Characteristic):
|
||||
|
||||
* *name* - the name of the custom Characteristic. This will be added to the Characteristic namespace so that it is accessed the same as any HomeSpan Characteristic
|
||||
* *uuid* - the UUID of the Characteristic as defined by the manufacturer. Must be *exactly* 36 characters in the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where *X* represent a valid hexidecimal digit. Leading zeros are required if needed as described more fully in HAP-R2 Section 6.6.1
|
||||
* *perms* - additive list of permissions as described in HAP-R2 Table 6-4. Valid values are PR, PW, EV, AA, TW, HD, and WR
|
||||
* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, and FLOAT (note that the HomeSpan does not presently support the TLV8 or DATA formats). Not applicable for Strings-based Characteristics
|
||||
* *defaultValue* - specifies the default value of the Characteristic if not defined during instantiation
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics
|
||||
* *staticRange* - set to *true* if *minValue* and *maxValue* are static and cannot be overridden with a call to `setRange()`. Set to *false* if calls to `setRange()` are allowed. Not applicable for Strings-based Characteristics
|
||||
* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, and FLOAT (note that the HomeSpan does not presently support the TLV8 formats). Not applicable for the STRING or DATA Characteristic macros
|
||||
* *defaultValue* - specifies the default value of the Characteristic if not defined during instantiation. Not applicable for the DATA Characteristic macro.
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for the STRING or DATA Characteristic macros
|
||||
* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for the STRING or DATA Characteristic macros
|
||||
* *staticRange* - set to *true* if *minValue* and *maxValue* are static and cannot be overridden with a call to `setRange()`. Set to *false* if calls to `setRange()` are allowed. Not applicable for the STRING or DATA Characteristic macros
|
||||
|
||||
As an example, the first line below creates a custom Characteristic named "Voltage" with a UUID code that is recognized by the *Eve for HomeKit* app. The parameters show that the Characteristic is read-only (PR) and notifications are enabled (EV). The default range of allowed values is 0-240, with a default of 120. The range *can* be overridden by subsequent calls to `setRange()`. The second line below creates a custom read-only String-based Characteristic:
|
||||
|
||||
|
|
@ -504,9 +538,11 @@ new Service::LightBulb();
|
|||
new Characteristic::UserTag(); // adds UserTag Characteristic and retains default initial value of "Tag 123"
|
||||
```
|
||||
|
||||
Note that Custom Characteristics must be created prior to calling `homeSpan.begin()`
|
||||
Note that Custom Characteristics must be created at the global level (i.e. not inside `setup()`) and prior to calling `homeSpan.begin()`
|
||||
|
||||
> Advanced Tip: When presented with an unrecognized Custom Characteristic, *Eve for HomeKit* helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnit()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic.
|
||||
> Advanced Tip 1: When presented with an unrecognized Custom Characteristic, *Eve for HomeKit* helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnit()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic.
|
||||
|
||||
> Advanced Tip 2: The DATA format is not currently used by any native Home App Characteristic, though it is part of the HAP-R2 specifications. This format is included in HomeSpan because other applications, such as *Eve for HomeKit* do use these types of Characteristics to create functionality beyond that of the Home App, and are thus provided for advanced users to experiment.
|
||||
|
||||
### *CUSTOM_SERV(name,uuid)*
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,14 @@ All of this is accomplished by using a combination of some, or all, of the follo
|
|||
|
||||
* `Characteristic::CurrentVisibilityState()` and `Characteristic::TargetVisibilityState()` - these two Characteristics work in tandem much like any current-state/target-state pair. When these are defined for an Input Source, a checkbox toggle appears next to the name of the Input Source on the Settings page, provided `Characteristic::IsConfigured()` has also been defined. Clicking the checkbox causes the Home App to toggle the TargetVisibilityState between 0 to 1, where 0 ironically means the checkbox is *checked*, and 1 means it is *unchecked* (the reverse of what you might expect!). If you read this update in HomeSpan you can then use `setVal()` to change the CurrentVisibiltyState() to match the TargetVisibilityState(). Setting CurrentVisibilityState() to 0 means the Input Source appears as a choice in the Input Source Selector. Setting CurrentVisibilityState() to 1 means it does not appear as a selection. Note these features only operate if an ID has been set for the Input Source with `Characteristic::Identifier()`, and IsConfigured() has been defined and set to 1
|
||||
|
||||
### `Service::TelevisionSpeaker()`
|
||||
|
||||
This Service allows you to change the volume of a television using the iPhone's physical volume control buttons when operating the TV via the iPhone's Remote Control widget (found in the iPhone Control Center). Similar the Input Source Service above, the Television Speaker Service ***must*** be linked to a Television Service using `addLink()`. The Television Speaker Service requires the following two Characteristics:
|
||||
|
||||
* `Characteristic::VolumeControlType()` - this read-only Characteristic seems to be required but there is uncertainty as to its purpose. In the example HomeSpan sketches I initialized this Characteristic with a value of 3 based on what others have done in scripts based on [HomeBridge](https://developers.homebridge.io/#/service/TelevisionSpeaker)
|
||||
|
||||
* `Characteristic::VolumeSelector()` - this write-only Characterstic is updated whenever the user is operating the TV via the iPhone's Remote Control widget and the physical volume control buttons are pressed. The Home App sends a value of 0 when the up-volume button is pressed, and value of 1 when the down-volume button is pressed. These values can be read the usual way by creating an `update()` method for a class that is derived from the Television Speaker Service
|
||||
|
||||
### Examples
|
||||
|
||||
Please see [*File → Examples → HomeSpan → Other Examples → Television*](../Other%20Examples/Television) for a complete worked example demonstrating the effects of using different combinations of the above Characteristics. Also, don't forget to check out the [HomeSpan Projects](https://github.com/topics/homespan) page for some real-world examples of TV sketches and controllers.
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void setup() {
|
|||
|
||||
// In the line below, replace the MAC Address with that of your MAIN HOMESPAN DEVICE
|
||||
|
||||
mainDevice=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
mainDevice=new SpanPoint("84:CC:A8:11:B4:84",sizeof(float),0); // create a SpanPoint with send size=sizeof(float) and receive size=0
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef ARDUINO_ARCH_ESP8266
|
||||
#error ERROR: THIS SKETCH IS DESIGNED FOR ESP8266 MICROCONTROLLERS!
|
||||
#endif
|
||||
|
||||
// *** THIS SKETCH IS FOR AN ESP8266, NOT AN ESP32 *** //
|
||||
|
||||
// This sketch is similar to HomeSpan's RemoteDevice.ino example (designed for an ESP32 running HomeSpan) in which we simulate
|
||||
// a Remote Temperature Sensor using HomeSpan's SpanPoint class. However, since neither HomeSpan nor SpanPoint is designed to
|
||||
// run on an ESP8266, we will implement the BASIC communication functionality of SpanPoint by directly calling the equivalent
|
||||
// ESP-NOW commands that are supported by the ESP8266. This sketch does NOT seek to replicate all of SpanPoint's features, and
|
||||
// does not include automatic channel calibration or queue management.
|
||||
|
||||
// Start by including the following ESP8266 libraries
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <espnow.h>
|
||||
#include <Crypto.h> // this library is needed to implement the hash-code process SpanPoint uses to generate ESP-NOW encryption keys
|
||||
|
||||
float temp=-10.0; // this global variable represents our "simulated" temperature (in degrees C)
|
||||
|
||||
// Below we encode the MAC Address of the Main ESP32 Device running HomeSpan to which this ESP8266 device will connect
|
||||
|
||||
// IMPORTANT: ESP32 devices have TWO MAC Addresses. One is used when the ESP32 is operating in Station (STA) mode. It is the address returned
|
||||
// by the WiFi.macAddress() function. The other is used when the ESP32 is operating in Access Point (AP) mode. This address is returned by the
|
||||
// WiFi.softAPmacAddress() function. HomeSpan normally operates the ESP32 with both modes (STA+AP), so both MAC Addresses are active.
|
||||
|
||||
// On ESP32 devices, ESP-NOW seems to work fine when each device sends data to other devices via their STA MAC Address. The same is true for ESP8266
|
||||
// devices sending data to an ESP32 device via ESP-NOW with one critical exception: Once the ESP32 connects (via STA mode) to a WiFi network, which it must
|
||||
// do to run HomeSpan, for some reason ESP8266 devices can no longer send data via ESP-NOW to the ESP32 using its STA MAC Address.
|
||||
|
||||
// The solution is to instead have the ESP8266 send data via ESP-NOW to the ESP32's AP MAC Address. This seems to work regardless of whether or not
|
||||
// the ESP32 is connected to a central WiFi newtork. To support such use on the ESP32, the SpanPoint constructor includes a fifth, optional parameter
|
||||
// called "useAPaddress". When creating SpanPoint links of the ESP32 using HomeSpan, set useAPaddress to TRUE if the Remote Device SpanPoint is connecting
|
||||
// to is an ESP8266. Set "useAPaddress" to FALSE (or leave unspecified, since FALSE is the default) if the Remote Device is an ESP32.
|
||||
|
||||
// When HomeSpan first starts (and whenever you type 'i' into the CLI), the Serial Monitor will display the details of each SpanPoint object you instantiated
|
||||
// in your ESP32 sketch. This output includes the MAC Address at which SpanPoint will be listening for incoming data from Remote Devices. The MAC Address
|
||||
// shown for the instance of SpanPoint corresponding to this Remote Deivce (i.e. this sketch) is the MAC Address you should use below.
|
||||
|
||||
uint8_t main_mac[6]={0x84,0xCC,0xA8,0x11,0xB4,0x85}; // this is the **AP MAC Address** of the Main Device running HomeSpan on an ESP32 as reported in the HomeSpan Serial Monitor
|
||||
|
||||
// Next we create a simple, standard ESP-NOW callback function to report on the status of each data transmission
|
||||
|
||||
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
|
||||
Serial.printf("Last Packet Send Status: %s\n",sendStatus==0?"Success":"Fail");
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.printf("\nMAC Address: %s\n",WiFi.macAddress().c_str()); // enter this MAC address as the first argument of the matching SpanPoint object on the ESP32 running HomeSpan
|
||||
|
||||
WiFi.mode(WIFI_STA); // set the mode to Station
|
||||
wifi_set_channel(6); // you also need to manually set the channel to match whatever channel is used by the ESP32 after it connects to your WiFi network
|
||||
|
||||
// Hint: As an alterntive, you can add code to this sketch to connect to the same WiFi network that HomeSpan uses. Though this sketch won't make any use of that WiFi network,
|
||||
// by establishing the connection the ESP8266 automatically configures the channel, which will now match the ESP32.
|
||||
|
||||
// Next, initialize ESP-NOW
|
||||
|
||||
if (esp_now_init() != 0) {
|
||||
Serial.println("Error initializing ESP-NOW");
|
||||
return;
|
||||
}
|
||||
|
||||
// SpanPoint uses ESP-NOW encryption for all communication. This encrpytion is based on two 16-byte keys: a local master key (LMK) and a primary master key (PMK). To generate
|
||||
// these keys, SpanPoint takes a text-based password (the default is the word "HomeSpan"), creates a 32 byte (256 bit) hash of the text (using the SHA256 method), and uses
|
||||
// the first 16 bytes as the LMK and the last 16 bytes as the PMK. This is easily replicated as follows:
|
||||
|
||||
uint8_t hash[32]; // create space to store as 32-byte hash code
|
||||
char password[]="HomeSpan"; // specify the password
|
||||
|
||||
experimental::crypto::SHA256::hash(password,strlen(password),hash); // create the hash code to be used further below
|
||||
|
||||
esp_now_register_send_cb(OnDataSent); // register the callback function we defined above
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER); // set the role of this device to be a controller (i.e. it sends data to the ESP32)
|
||||
|
||||
esp_now_set_kok(hash+16,16); // next we set the PMK. For some reason this is called KOK on the ESP8266. Note you must set the PMK BEFORE adding any peers
|
||||
|
||||
esp_now_add_peer(main_mac, ESP_NOW_ROLE_COMBO, 0, hash, 16); // now we add in the peer, set its role, and specify the LMK
|
||||
|
||||
// Hint: The third argument above is the WiFi Channel. However, this is only a reference number stored by ESP-NOW. ESP-NOW does NOT actually set the channel for you.
|
||||
// We already set the WiFi channel above. To make things easier, ESP-NOW allows you to set the channel as zero, which means ESP-NOW should expect the channel to be whatever was
|
||||
// already set for the WiFi controller. Recommend always setting this to zero to avoid having any mismatches if you instead specified a real channel.
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
|
||||
void loop() {
|
||||
|
||||
Serial.printf("Sending Temperature: %f\n",temp);
|
||||
esp_now_send(main_mac, (uint8_t *)&temp, sizeof(temp)); // Send the Data to the Main Device!
|
||||
|
||||
temp+=0.5; // increment the "temperature" by 0.5 C
|
||||
if(temp>35.0)
|
||||
temp=-10.0;
|
||||
|
||||
delay(5000); // wait 5 seconds before sending another update
|
||||
}
|
||||
|
|
@ -189,6 +189,10 @@ void setup() {
|
|||
new Characteristic::CurrentVisibilityState(0); // ...and included in the Selection List...
|
||||
new Characteristic::TargetVisibilityState(0); // ...and a "checked" checkbox is provided on the Settings Screen that can be used to toggle CurrentVisibilityState()
|
||||
|
||||
SpanService *speaker = new Service::TelevisionSpeaker();
|
||||
new Characteristic::VolumeSelector();
|
||||
new Characteristic::VolumeControlType(3);
|
||||
|
||||
(new HomeSpanTV("Test TV")) // Define a Television Service. Must link in InputSources!
|
||||
->addLink(hdmi1)
|
||||
->addLink(hdmi2)
|
||||
|
|
@ -200,6 +204,7 @@ void setup() {
|
|||
->addLink(hdmi8)
|
||||
->addLink(hdmi9)
|
||||
->addLink(hdmi10)
|
||||
->addLink(speaker)
|
||||
;
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
name=HomeSpan
|
||||
version=1.7.0
|
||||
version=1.7.1
|
||||
author=Gregg <homespan@icloud.com>
|
||||
maintainer=Gregg <homespan@icloud.com>
|
||||
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -48,7 +48,8 @@ enum FORMAT { // HAP Table 6-5
|
|||
UINT64=4,
|
||||
INT=5,
|
||||
FLOAT=6,
|
||||
STRING=7
|
||||
STRING=7,
|
||||
DATA=8
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
|
@ -192,7 +193,7 @@ struct HapCharacteristics {
|
|||
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, PR+EV, UINT8, true );
|
||||
HAPCHAR( VolumeSelector, EA, PW, UINT8, true );
|
||||
HAPCHAR( WaterLevel, B5, PR+EV, FLOAT, false );
|
||||
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -705,7 +705,7 @@ int HAPClient::postPairVerifyURL(){
|
|||
|
||||
memcpy(iosCurveKey,tlv8.buf(kTLVType_PublicKey),32); // save iosCurveKey (will persist until end of verification process)
|
||||
|
||||
int _x = crypto_scalarmult_curve25519(sharedCurveKey,secretCurveKey,iosCurveKey); // generate (and persist) Pair Verify SharedSecret CurveKey from Accessory's Curve25519 secret key and Controller's Curve25519 public key (32 bytes)
|
||||
crypto_scalarmult_curve25519(sharedCurveKey,secretCurveKey,iosCurveKey); // generate (and persist) Pair Verify SharedSecret CurveKey from Accessory's Curve25519 secret key and Controller's Curve25519 public key (32 bytes)
|
||||
|
||||
uint8_t *accessoryPairingID = accessory.ID; // set accessoryPairingID
|
||||
size_t accessoryPairingIDLen = 17;
|
||||
|
|
@ -1235,7 +1235,7 @@ int HAPClient::getStatusURL(){
|
|||
sprintf(clocktime,"Unknown");
|
||||
}
|
||||
|
||||
char uptime[16];
|
||||
char uptime[32];
|
||||
int seconds=esp_timer_get_time()/1e6;
|
||||
int secs=seconds%60;
|
||||
int mins=(seconds/=60)%60;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
118
src/HomeSpan.cpp
118
src/HomeSpan.cpp
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
#include "HomeSpan.h"
|
||||
#include "HAP.h"
|
||||
|
||||
const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE};
|
||||
const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE,0};
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
|
|
@ -903,21 +903,21 @@ void Span::processSerialCommand(const char *c){
|
|||
boolean foundInfo=false;
|
||||
|
||||
if(acc==Accessories.begin() && (*acc)->aid!=1)
|
||||
Serial.printf(" *** ERROR! AID of first Accessory must always be 1 ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! AID of first Accessory must always be 1 ***\n",++nErrors);
|
||||
|
||||
if(aidValues.find((*acc)->aid)!=aidValues.end())
|
||||
Serial.printf(" *** ERROR! AID already in use for another Accessory ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! AID already in use for another Accessory ***\n",++nErrors);
|
||||
|
||||
aidValues.insert((*acc)->aid);
|
||||
|
||||
for(auto svc=(*acc)->Services.begin(); svc!=(*acc)->Services.end(); svc++){
|
||||
Serial.printf(" \u279f Service %s: IID=%d, %sUUIS=\"%s\"",(*svc)->hapName,(*svc)->iid,(*svc)->isCustom?"Custom-":"",(*svc)->type);
|
||||
Serial.printf(" \u279f Service %s: IID=%d, %sUUID=\"%s\"",(*svc)->hapName,(*svc)->iid,(*svc)->isCustom?"Custom-":"",(*svc)->type);
|
||||
Serial.printf("\n");
|
||||
|
||||
if(!strcmp((*svc)->type,"3E")){
|
||||
foundInfo=true;
|
||||
if((*svc)->iid!=1)
|
||||
Serial.printf(" *** ERROR! The Accessory Information Service must be defined before any other Services in an Accessory ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! The Accessory Information Service must be defined before any other Services in an Accessory ***\n",++nErrors);
|
||||
}
|
||||
else if((*acc)->aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo. So...
|
||||
isBridge=false; // ...this is not a bridge device
|
||||
|
|
@ -934,7 +934,7 @@ void Span::processSerialCommand(const char *c){
|
|||
Serial.printf("%s%s",(foundPerms++)?"+":"",pNames[i]);
|
||||
}
|
||||
|
||||
if((*chr)->format!=FORMAT::STRING && (*chr)->format!=FORMAT::BOOL){
|
||||
if((*chr)->format!=FORMAT::STRING && (*chr)->format!=FORMAT::BOOL && (*chr)->format!=FORMAT::DATA){
|
||||
if((*chr)->validValues)
|
||||
Serial.printf(", Valid Values=%s",(*chr)->validValues);
|
||||
else if((*chr)->uvGet<double>((*chr)->stepValue)>0)
|
||||
|
|
@ -948,22 +948,22 @@ void Span::processSerialCommand(const char *c){
|
|||
Serial.printf("\n");
|
||||
|
||||
if(!(*chr)->isCustom && !(*svc)->isCustom && (*svc)->req.find((*chr)->hapChar)==(*svc)->req.end() && (*svc)->opt.find((*chr)->hapChar)==(*svc)->opt.end())
|
||||
Serial.printf(" *** WARNING! Service does not support this Characteristic ***\n",nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! Service does not support this Characteristic ***\n",++nWarnings);
|
||||
else
|
||||
if(invalidUUID((*chr)->type,(*chr)->isCustom))
|
||||
Serial.printf(" *** ERROR! Format of UUID is invalid ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! Format of UUID is invalid ***\n",++nErrors);
|
||||
else
|
||||
if(hapChar.find((*chr)->hapChar)!=hapChar.end())
|
||||
Serial.printf(" *** ERROR! Characteristic already defined for this Service ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! Characteristic already defined for this Service ***\n",++nErrors);
|
||||
|
||||
if((*chr)->setRangeError)
|
||||
Serial.printf(" *** WARNING! Attempt to set Custom Range for this Characteristic ignored ***\n",nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! Attempt to set Custom Range for this Characteristic ignored ***\n",++nWarnings);
|
||||
|
||||
if((*chr)->setValidValuesError)
|
||||
Serial.printf(" *** WARNING! Attempt to set Custom Valid Values for this Characteristic ignored ***\n",nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! Attempt to set Custom Valid Values for this Characteristic ignored ***\n",++nWarnings);
|
||||
|
||||
if((*chr)->format!=STRING && ((*chr)->uvGet<double>((*chr)->value) < (*chr)->uvGet<double>((*chr)->minValue) || (*chr)->uvGet<double>((*chr)->value) > (*chr)->uvGet<double>((*chr)->maxValue)))
|
||||
Serial.printf(" *** WARNING! Value of %llg is out of range [%llg,%llg] ***\n",(*chr)->uvGet<double>((*chr)->value),(*chr)->uvGet<double>((*chr)->minValue),(*chr)->uvGet<double>((*chr)->maxValue),nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! Value of %g is out of range [%g,%g] ***\n",++nWarnings,(*chr)->uvGet<double>((*chr)->value),(*chr)->uvGet<double>((*chr)->minValue),(*chr)->uvGet<double>((*chr)->maxValue));
|
||||
|
||||
hapChar.insert((*chr)->hapChar);
|
||||
|
||||
|
|
@ -971,12 +971,17 @@ void Span::processSerialCommand(const char *c){
|
|||
|
||||
for(auto req=(*svc)->req.begin(); req!=(*svc)->req.end(); req++){
|
||||
if(hapChar.find(*req)==hapChar.end())
|
||||
Serial.printf(" *** WARNING! Required '%s' Characteristic for this Service not found ***\n",(*req)->hapName,nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! Required '%s' Characteristic for this Service not found ***\n",++nWarnings,(*req)->hapName);
|
||||
}
|
||||
|
||||
for(auto button=PushButtons.begin(); button!=PushButtons.end(); button++){
|
||||
if((*button)->service==(*svc)){
|
||||
Serial.printf(" \u25bc SpanButton: Pin=%d, Single=%ums, Double=%ums, Long=%ums, Type=",(*button)->pin,(*button)->singleTime,(*button)->doubleTime,(*button)->longTime);
|
||||
|
||||
if((*button)->buttonType==SpanButton::BUTTON)
|
||||
Serial.printf(" \u25bc SpanButton: Pin=%d, Single=%ums, Double=%ums, Long=%ums, Type=",(*button)->pin,(*button)->singleTime,(*button)->doubleTime,(*button)->longTime);
|
||||
else
|
||||
Serial.printf(" \u25bc SpanToggle: Pin=%d, Toggle=%ums, Type=",(*button)->pin,(*button)->longTime);
|
||||
|
||||
if((*button)->triggerType==PushButton::TRIGGER_ON_LOW)
|
||||
Serial.printf("TRIGGER_ON_LOW\n");
|
||||
else if((*button)->triggerType==PushButton::TRIGGER_ON_HIGH)
|
||||
|
|
@ -990,14 +995,14 @@ void Span::processSerialCommand(const char *c){
|
|||
Serial.printf("USER-DEFINED\n");
|
||||
|
||||
if((void(*)(int,int))((*svc)->*(&SpanService::button))==(void(*)(int,int))(&SpanService::button))
|
||||
Serial.printf(" *** WARNING! No button() method defined in this Service ***\n",nWarnings++);
|
||||
Serial.printf(" *** WARNING #%d! No button() method defined in this Service ***\n",++nWarnings);
|
||||
}
|
||||
}
|
||||
|
||||
} // Services
|
||||
|
||||
if(!foundInfo)
|
||||
Serial.printf(" *** ERROR! Required 'AccessoryInformation' Service not found ***\n",nErrors++);
|
||||
Serial.printf(" *** ERROR #%d! Required 'AccessoryInformation' Service not found ***\n",++nErrors);
|
||||
|
||||
} // Accessories
|
||||
|
||||
|
|
@ -1027,6 +1032,21 @@ void Span::processSerialCommand(const char *c){
|
|||
Serial.print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(SpanPoint::SpanPoints.size()>0){
|
||||
uint8_t channel;
|
||||
wifi_second_chan_t channel2;
|
||||
esp_wifi_get_channel(&channel,&channel2);
|
||||
Serial.printf("\nFound %d SpanPoint Links:\n\n",SpanPoint::SpanPoints.size());
|
||||
Serial.printf("%-17s %18s %7s %7s %7s\n","Local MAC Address","Remote MAC Address","Send","Receive","Depth");
|
||||
Serial.printf("%.17s %.18s %.7s %.7s %.7s\n",d,d,d,d,d);
|
||||
for(auto it=SpanPoint::SpanPoints.begin();it!=SpanPoint::SpanPoints.end();it++)
|
||||
Serial.printf("%-18s %02X:%02X:%02X:%02X:%02X:%02X %7d %7d %7d\n",(*it)->peerInfo.ifidx==WIFI_IF_AP?WiFi.softAPmacAddress().c_str():WiFi.macAddress().c_str(),
|
||||
(*it)->peerInfo.peer_addr[0],(*it)->peerInfo.peer_addr[1],(*it)->peerInfo.peer_addr[2],(*it)->peerInfo.peer_addr[3],(*it)->peerInfo.peer_addr[4],(*it)->peerInfo.peer_addr[5],
|
||||
(*it)->sendSize,(*it)->receiveSize,uxQueueSpacesAvailable((*it)->receiveQueue));
|
||||
Serial.printf("\nSpanPoint using WiFi Channel %d%s\n",channel,WiFi.status()!=WL_CONNECTED?" (subject to change once WiFi connection established)":"");
|
||||
}
|
||||
|
||||
Serial.print("\n*** End Info ***\n\n");
|
||||
}
|
||||
break;
|
||||
|
|
@ -1077,9 +1097,13 @@ void Span::processSerialCommand(const char *c){
|
|||
uCom->second->userFunction1(c+1);
|
||||
else
|
||||
uCom->second->userFunction2(c+1,uCom->second->userArg);
|
||||
break;
|
||||
} else {
|
||||
Serial.print("*** Undefined user command: '");
|
||||
Serial.print(c);
|
||||
Serial.print("'. Type '?' for list of commands.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Serial.print("*** Unknown command: '");
|
||||
|
|
@ -1374,7 +1398,7 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){
|
|||
if(status==StatusCode::OK){ // if status is okay
|
||||
pObj[j].characteristic->uvSet(pObj[j].characteristic->value,pObj[j].characteristic->newValue); // update characteristic value with new value
|
||||
if(pObj[j].characteristic->nvsKey){ // if storage key found
|
||||
if(pObj[j].characteristic->format != FORMAT::STRING)
|
||||
if(pObj[j].characteristic->format!=FORMAT::STRING && pObj[j].characteristic->format!=FORMAT::DATA)
|
||||
nvs_set_blob(charNVS,pObj[j].characteristic->nvsKey,&(pObj[j].characteristic->value),sizeof(pObj[j].characteristic->value)); // store data
|
||||
else
|
||||
nvs_set_str(charNVS,pObj[j].characteristic->nvsKey,pObj[j].characteristic->value.STRING); // store data
|
||||
|
|
@ -1730,12 +1754,6 @@ int SpanService::sprintfAttributes(char *cBuf, int flags){
|
|||
|
||||
SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
|
||||
|
||||
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
|
||||
Serial.printf("\nFATAL ERROR! Can't create new Characteristic '%s' without a defined Service ***\n",hapName);
|
||||
Serial.printf("\n=== PROGRAM HALTED ===");
|
||||
while(1);
|
||||
}
|
||||
|
||||
type=hapChar->type;
|
||||
perms=hapChar->perms;
|
||||
hapName=hapChar->hapName;
|
||||
|
|
@ -1744,6 +1762,12 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
|
|||
this->isCustom=isCustom;
|
||||
this->hapChar=hapChar;
|
||||
|
||||
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
|
||||
Serial.printf("\nFATAL ERROR! Can't create new Characteristic '%s' without a defined Service ***\n",hapName);
|
||||
Serial.printf("\n=== PROGRAM HALTED ===");
|
||||
while(1);
|
||||
}
|
||||
|
||||
homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this);
|
||||
iid=++(homeSpan.Accessories.back()->iidCount);
|
||||
service=homeSpan.Accessories.back()->Services.back();
|
||||
|
|
@ -1767,7 +1791,7 @@ SpanCharacteristic::~SpanCharacteristic(){
|
|||
free(validValues);
|
||||
free(nvsKey);
|
||||
|
||||
if(format==FORMAT::STRING){
|
||||
if(format==FORMAT::STRING || format==FORMAT::DATA){
|
||||
free(value.STRING);
|
||||
free(newValue.STRING);
|
||||
}
|
||||
|
|
@ -1782,7 +1806,7 @@ int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){
|
|||
|
||||
const char permCodes[][7]={"pr","pw","ev","aa","tw","hd","wr"};
|
||||
|
||||
const char formatCodes[][8]={"bool","uint8","uint16","uint32","uint64","int","float","string"};
|
||||
const char formatCodes[][9]={"bool","uint8","uint16","uint32","uint64","int","float","string","data"};
|
||||
|
||||
nBytes+=snprintf(cBuf,cBuf?64:0,"{\"iid\":%d",iid);
|
||||
|
||||
|
|
@ -2008,7 +2032,11 @@ SpanRange::SpanRange(int min, int max, int step){
|
|||
SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t doubleTime, triggerType_t triggerType) : PushButton(pin, triggerType){
|
||||
|
||||
if(homeSpan.Accessories.empty() || homeSpan.Accessories.back()->Services.empty()){
|
||||
Serial.printf("\nFATAL ERROR! Can't create new SpanButton(%d,%u,%u,%u) without a defined Service ***\n",pin,longTime,singleTime,doubleTime);
|
||||
if(buttonType==BUTTON)
|
||||
Serial.printf("\nFATAL ERROR! Can't create new SpanButton(%d,%u,%u,%u) without a defined Service ***\n",pin,longTime,singleTime,doubleTime);
|
||||
else
|
||||
Serial.printf("\nFATAL ERROR! Can't create new SpanToggle(%d,%u) without a defined Service ***\n",pin,longTime);
|
||||
|
||||
Serial.printf("\n=== PROGRAM HALTED ===");
|
||||
while(1);
|
||||
}
|
||||
|
|
@ -2025,8 +2053,9 @@ SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t
|
|||
|
||||
void SpanButton::check(){
|
||||
|
||||
if(triggered(singleTime,longTime,doubleTime)) // if the underlying PushButton is triggered
|
||||
service->button(pin,type()); // call the Service's button() routine with pin and type as parameters
|
||||
if( (buttonType==BUTTON && triggered(singleTime,longTime,doubleTime)) ||
|
||||
(buttonType==TOGGLE && toggled(longTime)) ) // if the underlying PushButton is triggered/toggled
|
||||
service->button(pin,type()); // call the Service's button() routine with pin and type as parameters
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
|
@ -2183,7 +2212,7 @@ boolean SpanOTA::auth;
|
|||
// SpanPoint //
|
||||
///////////////////////////////
|
||||
|
||||
SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth){
|
||||
SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth, boolean useAPaddress){
|
||||
|
||||
if(sscanf(macAddress,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",peerInfo.peer_addr,peerInfo.peer_addr+1,peerInfo.peer_addr+2,peerInfo.peer_addr+3,peerInfo.peer_addr+4,peerInfo.peer_addr+5)!=6){
|
||||
Serial.printf("\nFATAL ERROR! Can't create new SpanPoint(\"%s\") - Invalid MAC Address ***\n",macAddress);
|
||||
|
|
@ -2200,9 +2229,6 @@ SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int
|
|||
this->sendSize=sendSize;
|
||||
this->receiveSize=receiveSize;
|
||||
|
||||
Serial.printf("SpanPoint: Created link to device with MAC Address %02X:%02X:%02X:%02X:%02X:%02X. Send size=%d bytes, Receive size=%d bytes with queue depth=%d.\n",
|
||||
peerInfo.peer_addr[0],peerInfo.peer_addr[1],peerInfo.peer_addr[2],peerInfo.peer_addr[3],peerInfo.peer_addr[4],peerInfo.peer_addr[5],sendSize,receiveSize,queueDepth);
|
||||
|
||||
if(receiveSize>0)
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
else if(WiFi.getMode()==WIFI_OFF)
|
||||
|
|
@ -2210,7 +2236,9 @@ SpanPoint::SpanPoint(const char *macAddress, int sendSize, int receiveSize, int
|
|||
|
||||
init(); // initialize SpanPoint
|
||||
peerInfo.channel=0; // 0 = matches current WiFi channel
|
||||
peerInfo.ifidx=WIFI_IF_STA; // must specify interface
|
||||
|
||||
peerInfo.ifidx=useAPaddress?WIFI_IF_AP:WIFI_IF_STA; // specify interface as either STA or AP
|
||||
|
||||
peerInfo.encrypt=true; // turn on encryption for this peer
|
||||
memcpy(peerInfo.lmk, lmk, 16); // set local key
|
||||
esp_now_add_peer(&peerInfo); // add peer to ESP-NOW
|
||||
|
|
@ -2231,6 +2259,11 @@ void SpanPoint::init(const char *password){
|
|||
if(WiFi.getMode()==WIFI_OFF)
|
||||
WiFi.mode(WIFI_STA);
|
||||
|
||||
wifi_config_t conf; // make sure AP is hidden (if WIFI_AP_STA is used), since it is just a "dummy" AP to keep WiFi alive for ESP-NOW
|
||||
esp_wifi_get_config(WIFI_IF_AP,&conf);
|
||||
conf.ap.ssid_hidden=1;
|
||||
esp_wifi_set_config(WIFI_IF_AP,&conf);
|
||||
|
||||
uint8_t hash[32];
|
||||
mbedtls_sha256_ret((const unsigned char *)password,strlen(password),hash,0); // produce 256-bit bit hash from password
|
||||
|
||||
|
|
@ -2243,6 +2276,16 @@ void SpanPoint::init(const char *password){
|
|||
statusQueue = xQueueCreate(1,sizeof(esp_now_send_status_t)); // create statusQueue even if not needed
|
||||
setChannelMask(channelMask); // default channel mask at start-up uses channels 1-13
|
||||
|
||||
uint8_t channel;
|
||||
if(!isHub){ // this is not a hub
|
||||
nvs_flash_init(); // initialize NVS
|
||||
nvs_open("POINT",NVS_READWRITE,&pointNVS); // open SpanPoint data namespace in NVS
|
||||
if(!nvs_get_u8(pointNVS,"CHANNEL",&channel)){ // if channel found in NVS...
|
||||
if(channelMask & (1<<channel)) // ... and if channel is allowed by channel mask
|
||||
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); // set the WiFi channel
|
||||
}
|
||||
}
|
||||
|
||||
initialized=true;
|
||||
}
|
||||
|
||||
|
|
@ -2285,6 +2328,9 @@ uint8_t SpanPoint::nextChannel(){
|
|||
|
||||
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); // set the WiFi channel
|
||||
|
||||
nvs_set_u8(pointNVS,"CHANNEL",channel);
|
||||
nvs_commit(pointNVS);
|
||||
|
||||
return(channel);
|
||||
}
|
||||
|
||||
|
|
@ -2360,6 +2406,8 @@ boolean SpanPoint::isHub=false;
|
|||
vector<SpanPoint *> SpanPoint::SpanPoints;
|
||||
uint16_t SpanPoint::channelMask=0x3FFE;
|
||||
QueueHandle_t SpanPoint::statusQueue;
|
||||
nvs_handle SpanPoint::pointNVS;
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
// MISC //
|
||||
|
|
|
|||
122
src/HomeSpan.h
122
src/HomeSpan.h
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
#endif
|
||||
|
||||
#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>
|
||||
|
|
@ -40,6 +41,7 @@
|
|||
#include <nvs.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <esp_now.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include "extras/Blinker.h"
|
||||
#include "extras/Pixel.h"
|
||||
|
|
@ -298,6 +300,9 @@ class Span{
|
|||
statusDevice=((new Pixel(pin))->setOnColor(Pixel::HSV(h,s,v)));
|
||||
}
|
||||
|
||||
void setStatusDevice(Blinkable *sDev){statusDevice=sDev;}
|
||||
void refreshStatusDevice(){if(statusLED)statusLED->refresh();}
|
||||
|
||||
void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID
|
||||
void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password
|
||||
void setApTimeout(uint16_t nSec){network.lifetime=nSec*1000;} // sets Access Point Timeout (seconds)
|
||||
|
|
@ -390,7 +395,7 @@ class SpanService{
|
|||
|
||||
protected:
|
||||
|
||||
~SpanService(); // destructor
|
||||
virtual ~SpanService(); // destructor
|
||||
unordered_set<HapChar *> req; // unordered set of pointers to all required HAP Characteristic Types for this Service
|
||||
unordered_set<HapChar *> opt; // unordered set of pointers to all optional HAP Characteristic Types for this Service
|
||||
|
||||
|
|
@ -472,9 +477,10 @@ class SpanCharacteristic{
|
|||
sprintf(c,"%llu",u.UINT64);
|
||||
return(String(c));
|
||||
case FORMAT::FLOAT:
|
||||
sprintf(c,"%llg",u.FLOAT);
|
||||
sprintf(c,"%g",u.FLOAT);
|
||||
return(String(c));
|
||||
case FORMAT::STRING:
|
||||
case FORMAT::DATA:
|
||||
sprintf(c,"\"%s\"",u.STRING);
|
||||
return(String(c));
|
||||
} // switch
|
||||
|
|
@ -482,7 +488,7 @@ class SpanCharacteristic{
|
|||
}
|
||||
|
||||
void uvSet(UVal &dest, UVal &src){
|
||||
if(format==FORMAT::STRING)
|
||||
if(format==FORMAT::STRING || format==FORMAT::DATA)
|
||||
uvSet(dest,(const char *)src.STRING);
|
||||
else
|
||||
dest=src;
|
||||
|
|
@ -516,6 +522,9 @@ class SpanCharacteristic{
|
|||
case FORMAT::FLOAT:
|
||||
u.FLOAT=(double)val;
|
||||
break;
|
||||
case FORMAT::STRING:
|
||||
case FORMAT::DATA:
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
|
|
@ -536,6 +545,9 @@ class SpanCharacteristic{
|
|||
return((T) u.UINT64);
|
||||
case FORMAT::FLOAT:
|
||||
return((T) u.FLOAT);
|
||||
case FORMAT::STRING:
|
||||
case FORMAT::DATA:
|
||||
break;
|
||||
}
|
||||
return(0); // included to prevent compiler warnings
|
||||
}
|
||||
|
|
@ -546,44 +558,39 @@ class SpanCharacteristic{
|
|||
|
||||
template <typename T, typename A=boolean, typename B=boolean> void init(T val, boolean nvsStore, A min=0, B max=1){
|
||||
|
||||
int nvsFlag=0;
|
||||
uvSet(value,val);
|
||||
|
||||
if(nvsStore){
|
||||
nvsKey=(char *)malloc(16);
|
||||
uint16_t t;
|
||||
sscanf(type,"%x",&t);
|
||||
sscanf(type,"%hx",&t);
|
||||
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
|
||||
size_t len;
|
||||
|
||||
if(format != FORMAT::STRING){
|
||||
if(format!=FORMAT::STRING && format!=FORMAT::DATA){
|
||||
if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){
|
||||
nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len);
|
||||
nvsFlag=2;
|
||||
}
|
||||
else {
|
||||
nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
nvsFlag=1;
|
||||
}
|
||||
} else {
|
||||
if(!nvs_get_str(homeSpan.charNVS,nvsKey,NULL,&len)){
|
||||
char c[len];
|
||||
nvs_get_str(homeSpan.charNVS,nvsKey,c,&len);
|
||||
uvSet(value,(const char *)c);
|
||||
nvsFlag=2;
|
||||
}
|
||||
else {
|
||||
nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store string data
|
||||
nvs_commit(homeSpan.charNVS); // commit to NVS
|
||||
nvsFlag=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uvSet(newValue,value);
|
||||
|
||||
if(format != FORMAT::STRING) {
|
||||
if(format!=FORMAT::STRING && format!=FORMAT::DATA) {
|
||||
uvSet(minValue,min);
|
||||
uvSet(maxValue,max);
|
||||
uvSet(stepValue,0);
|
||||
|
|
@ -643,6 +650,61 @@ class SpanCharacteristic{
|
|||
|
||||
} // setString()
|
||||
|
||||
size_t getData(uint8_t *data, size_t len){
|
||||
if(format!=FORMAT::DATA)
|
||||
return(0);
|
||||
|
||||
size_t olen;
|
||||
int ret=mbedtls_base64_decode(data,len,&olen,(uint8_t *)value.STRING,strlen(value.STRING));
|
||||
|
||||
if(data==NULL)
|
||||
return(olen);
|
||||
|
||||
if(ret==MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
|
||||
Serial.printf("\n*** WARNING: Can't decode Characteristic::%s with getData(). Destination buffer is too small (%d out of %d bytes needed)\n\n",hapName,len,olen);
|
||||
else if(ret==MBEDTLS_ERR_BASE64_INVALID_CHARACTER)
|
||||
Serial.printf("\n*** WARNING: Can't decode Characteristic::%s with getData(). Data is not in base-64 format\n\n",hapName);
|
||||
|
||||
return(olen);
|
||||
}
|
||||
|
||||
size_t getNewData(uint8_t *data, size_t len){
|
||||
if(format!=FORMAT::DATA)
|
||||
return(0);
|
||||
|
||||
size_t olen;
|
||||
int ret=mbedtls_base64_decode(data,len,&olen,(uint8_t *)newValue.STRING,strlen(newValue.STRING));
|
||||
|
||||
if(data==NULL)
|
||||
return(olen);
|
||||
|
||||
if(ret==MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
|
||||
Serial.printf("\n*** WARNING: Can't decode Characteristic::%s with getData(). Destination buffer is too small (%d out of %d bytes needed)\n\n",hapName,len,olen);
|
||||
else if(ret==MBEDTLS_ERR_BASE64_INVALID_CHARACTER)
|
||||
Serial.printf("\n*** WARNING: Can't decode Characteristic::%s with getData(). Data is not in base-64 format\n\n",hapName);
|
||||
|
||||
return(olen);
|
||||
}
|
||||
|
||||
void setData(uint8_t *data, size_t len){
|
||||
|
||||
if((perms & EV) == 0){
|
||||
Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. No NOTIFICATION permission on this characteristic\n\n",hapName);
|
||||
return;
|
||||
}
|
||||
|
||||
if(len<1){
|
||||
Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setData() ignored. Size of data buffer must be greater than zero\n\n",hapName);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t olen;
|
||||
mbedtls_base64_encode(NULL,0,&olen,data,len); // get length of string buffer needed (mbedtls includes the trailing null in this size)
|
||||
TempBuffer<char> tBuf(olen); // create temporary string buffer, with room for trailing null
|
||||
mbedtls_base64_encode((uint8_t*)tBuf.buf,olen,&olen,data,len ); // encode data into string buf
|
||||
setString(tBuf.buf); // call setString to continue processing as if characteristic was a string
|
||||
}
|
||||
|
||||
template <typename T> void setVal(T val, boolean notify=true){
|
||||
|
||||
if((perms & EV) == 0){
|
||||
|
|
@ -651,7 +713,7 @@ class SpanCharacteristic{
|
|||
}
|
||||
|
||||
if(val < uvGet<T>(minValue) || val > uvGet<T>(maxValue)){
|
||||
Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%llg) is out of range [%llg,%llg]. This may cause device to become non-reponsive!\n\n",
|
||||
Serial.printf("\n*** WARNING: Attempt to update Characteristic::%s with setVal(%g) is out of range [%g,%g]. This may cause device to become non-reponsive!\n\n",
|
||||
hapName,(double)val,uvGet<double>(minValue),uvGet<double>(maxValue));
|
||||
}
|
||||
|
||||
|
|
@ -732,7 +794,7 @@ struct [[deprecated("Please use Characteristic::setRange() method instead.")]] S
|
|||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanButton : PushButton {
|
||||
class SpanButton : public PushButton {
|
||||
|
||||
friend class Span;
|
||||
friend class SpanService;
|
||||
|
|
@ -742,16 +804,19 @@ class SpanButton : PushButton {
|
|||
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
|
||||
void check(); // check PushButton and call button() if "pressed"
|
||||
|
||||
protected:
|
||||
|
||||
enum buttonType_t {
|
||||
BUTTON,
|
||||
TOGGLE
|
||||
};
|
||||
|
||||
buttonType_t buttonType=BUTTON; // type of SpanButton
|
||||
|
||||
public:
|
||||
|
||||
enum {
|
||||
SINGLE=0,
|
||||
DOUBLE=1,
|
||||
LONG=2
|
||||
};
|
||||
|
||||
static constexpr triggerType_t TRIGGER_ON_LOW=PushButton::TRIGGER_ON_LOW;
|
||||
static constexpr triggerType_t TRIGGER_ON_HIGH=PushButton::TRIGGER_ON_HIGH;
|
||||
|
||||
|
|
@ -768,6 +833,16 @@ class SpanButton : PushButton {
|
|||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanToggle : public SpanButton {
|
||||
|
||||
public:
|
||||
|
||||
SpanToggle(int pin, triggerType_t triggerType=TRIGGER_ON_LOW, uint16_t toggleTime=5) : SpanButton(pin,triggerType,toggleTime){buttonType=TOGGLE;};
|
||||
int position(){return(pressType);}
|
||||
};
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
class SpanUserCommand {
|
||||
|
||||
friend class Span;
|
||||
|
|
@ -799,8 +874,9 @@ class SpanPoint {
|
|||
static boolean initialized;
|
||||
static boolean isHub;
|
||||
static vector<SpanPoint *> SpanPoints;
|
||||
static uint16_t channelMask; // channel mask
|
||||
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");
|
||||
|
|
@ -813,7 +889,7 @@ class SpanPoint {
|
|||
|
||||
public:
|
||||
|
||||
SpanPoint(const char *macAddress, int sendSize, int receiveSize, int queueDepth=1);
|
||||
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);
|
||||
boolean get(void *dataBuf);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
#define HS_MAJOR 1
|
||||
#define HS_MINOR 7
|
||||
#define HS_PATCH 0
|
||||
#define HS_PATCH 1
|
||||
|
||||
#define STRINGIFY(x) _STR(x)
|
||||
#define _STR(x) #x
|
||||
|
|
|
|||
11
src/Span.h
11
src/Span.h
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -335,6 +335,11 @@ namespace Service {
|
|||
OPT(PowerModeSelection);
|
||||
}};
|
||||
|
||||
struct TelevisionSpeaker : SpanService { TelevisionSpeaker() : SpanService{"113","TelevisionSpeaker"}{
|
||||
REQ(VolumeControlType);
|
||||
REQ(VolumeSelector);
|
||||
}};
|
||||
|
||||
struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{
|
||||
REQ(CurrentTemperature);
|
||||
OPT(Name);
|
||||
|
|
@ -535,6 +540,10 @@ namespace Characteristic {
|
|||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),STRING,true}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }
|
||||
|
||||
#define CUSTOM_CHAR_DATA(NAME,UUID,PERMISISONS) \
|
||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),DATA,true}; \
|
||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val="AA==", boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }
|
||||
|
||||
#define CUSTOM_SERV(NAME,UUID) \
|
||||
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -88,6 +88,7 @@ PushButton::PushButton(int pin, triggerType_t triggerType){
|
|||
|
||||
this->pin=pin;
|
||||
this->triggerType=triggerType;
|
||||
|
||||
status=0;
|
||||
doubleCheck=false;
|
||||
|
||||
|
|
@ -111,6 +112,14 @@ PushButton::PushButton(int pin, triggerType_t triggerType){
|
|||
}
|
||||
#endif
|
||||
|
||||
if(triggerType(pin)){
|
||||
pressType=CLOSED;
|
||||
toggleStatus=2;
|
||||
} else {
|
||||
pressType=OPEN;
|
||||
toggleStatus=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
|
@ -192,6 +201,46 @@ boolean PushButton::triggered(uint16_t singleTime, uint16_t longTime, uint16_t d
|
|||
|
||||
//////////////////////////////////////
|
||||
|
||||
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){
|
||||
|
|
|
|||
50
src/Utils.h
50
src/Utils.h
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -80,17 +80,18 @@ typedef uint16_t touch_value_t;
|
|||
class PushButton{
|
||||
|
||||
int status;
|
||||
int toggleStatus;
|
||||
boolean doubleCheck;
|
||||
uint32_t singleAlarm;
|
||||
uint32_t doubleAlarm;
|
||||
uint32_t longAlarm;
|
||||
int pressType;
|
||||
|
||||
static touch_value_t threshold;
|
||||
static const int calibCount=20;
|
||||
|
||||
protected:
|
||||
|
||||
int pressType;
|
||||
typedef boolean (*triggerType_t)(int pin);
|
||||
|
||||
int pin;
|
||||
|
|
@ -99,9 +100,11 @@ class PushButton{
|
|||
public:
|
||||
|
||||
enum {
|
||||
SINGLE=0,
|
||||
DOUBLE=1,
|
||||
LONG=2
|
||||
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));}
|
||||
|
|
@ -117,27 +120,27 @@ class PushButton{
|
|||
|
||||
PushButton(int pin, triggerType_t triggerType=TRIGGER_ON_LOW);
|
||||
|
||||
// Creates pushbutton of specified type on specified pin
|
||||
// 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, 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
|
||||
// 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.
|
||||
// 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
|
||||
// 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:
|
||||
|
||||
|
|
@ -158,12 +161,23 @@ class PushButton{
|
|||
|
||||
int type();
|
||||
|
||||
// Returns 0=Single Press, 1=Double Press, or 2=Long Press
|
||||
// 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
|
||||
|
|
@ -174,15 +188,15 @@ class PushButton{
|
|||
|
||||
// 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
|
||||
// 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.
|
||||
// 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
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,6 @@
|
|||
*
|
||||
********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Blinker.h"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@
|
|||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "../src/extras/Pixel.h"
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/Pixel.h"
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@
|
|||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "../src/extras/PwmPin.h"
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/PwmPin.h"
|
||||
|
|
|
|||
|
|
@ -25,5 +25,6 @@
|
|||
*
|
||||
********************************************************************************/
|
||||
|
||||
#include "../src/extras/RFControl.h"
|
||||
#pragma once
|
||||
|
||||
#include "../src/extras/RFControl.h"
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
// This is a placeholder .ino file that allows for editing of top-level "extras" header files.
|
||||
214
src/src.ino
214
src/src.ino
|
|
@ -1,172 +1,108 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// This is a placeholder .ino file that allows you to easily edit the contents of this library using the Arduino IDE,
|
||||
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "FeatherPins.h"
|
||||
#include "extras/Pixel.h"
|
||||
#include "extras/RFControl.h"
|
||||
#include "extras/Blinker.h"
|
||||
#include "extras/PwmPin.h"
|
||||
|
||||
struct RemoteTempSensor : Service::TemperatureSensor {
|
||||
|
||||
#define STRING_t const char * // WORK-AROUND
|
||||
SpanCharacteristic *temp;
|
||||
SpanCharacteristic *fault;
|
||||
SpanPoint *remoteTemp;
|
||||
const char *name;
|
||||
float temperature;
|
||||
|
||||
CUSTOM_CHAR(LightMode, AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA, PR, STRING, "ANY_VALUE", NULL, NULL, true);
|
||||
CUSTOM_CHAR_STRING(DarkMode, AAAAAAAA-BBBB-AAAA-AAAA-AAAAAAAAAAAA, PR, "MY_VALUE");
|
||||
RemoteTempSensor(const char *name, const char*macAddress, boolean is8266=false) : Service::TemperatureSensor(){
|
||||
|
||||
SpanPoint *dev1;
|
||||
SpanPoint *dev2;
|
||||
this->name=name;
|
||||
|
||||
struct message_t {
|
||||
char a[32];
|
||||
int b;
|
||||
float c;
|
||||
bool d;
|
||||
} message;
|
||||
temp=new Characteristic::CurrentTemperature(-10.0); // set initial temperature
|
||||
temp->setRange(-50,100); // expand temperature range to allow negative values
|
||||
|
||||
fault=new Characteristic::StatusFault(1); // set initial state = fault
|
||||
|
||||
remoteTemp=new SpanPoint(macAddress,0,sizeof(float),1,is8266); // create a SpanPoint with send size=0 and receive size=sizeof(float)
|
||||
|
||||
} // end constructor
|
||||
|
||||
void loop(){
|
||||
|
||||
if(remoteTemp->get(&temperature)){ // if there is data from the remote sensor
|
||||
temp->setVal(temperature); // update temperature
|
||||
fault->setVal(0); // clear fault
|
||||
|
||||
LOG1("Sensor %s update: Temperature=%0.2f\n",name,temperature*9/5+32);
|
||||
|
||||
} else if(remoteTemp->time()>60000 && !fault->getVal()){ // else if it has been a while since last update (60 seconds), and there is no current fault
|
||||
fault->setVal(1); // set fault state
|
||||
LOG1("Sensor %s update: FAULT\n",name);
|
||||
}
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
// homeSpan.setLogLevel(2);
|
||||
homeSpan.setControlPin(F25);
|
||||
homeSpan.setStatusPin(F26);
|
||||
// homeSpan.setStatusPin(new LED(F26));
|
||||
|
||||
// new Pixel(F27);
|
||||
|
||||
homeSpan.setHostNameSuffix("-lamp1");
|
||||
homeSpan.setPortNum(1201);
|
||||
// homeSpan.setMaxConnections(6);
|
||||
// homeSpan.setQRID("One1");
|
||||
homeSpan.enableOTA();
|
||||
homeSpan.setSketchVersion("OTA Test 8");
|
||||
homeSpan.setWifiCallback(wifiEstablished);
|
||||
|
||||
homeSpan.setStatusCallback(statusUpdate);
|
||||
|
||||
new SpanUserCommand('d',"- My Description",userCom1);
|
||||
new SpanUserCommand('e',"- My second Description",userCom2);
|
||||
|
||||
// homeSpan.enableAutoStartAP();
|
||||
// homeSpan.setApFunction(myWiFiAP);
|
||||
|
||||
homeSpan.enableWebLog(10,"pool.ntp.org","UTC","myLog"); // creates a web log on the URL /HomeSpan-[DEVICE-ID].local:[TCP-PORT]/myLog
|
||||
|
||||
SpanPoint::setChannelMask(1<<13);
|
||||
|
||||
SpanPoint::setPassword("Hello Thert");
|
||||
|
||||
homeSpan.setLogLevel(1);
|
||||
|
||||
dev2=new SpanPoint("7C:DF:A1:61:E4:A8",sizeof(int),sizeof(message_t));
|
||||
dev1=new SpanPoint("AC:67:B2:77:42:20",sizeof(int),0);
|
||||
homeSpan.begin(Category::Bridges,"Sensor Hub");
|
||||
|
||||
homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan");
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Indoor Temp");
|
||||
new RemoteTempSensor("Device 1","AC:67:B2:77:42:20"); // pass MAC Address of Remote Device
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Outdoor Temp");
|
||||
new RemoteTempSensor("Device 2","BC:FF:4D:40:8E:71",true); // pass MAC Address of Remote Device with 8266 flag set (will use AP MAC Address)
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("HomeSpan Test"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("HSL-123"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("HSL Test"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision(HOMESPAN_VERSION); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0);
|
||||
new Characteristic::LightMode("HELLO");
|
||||
new Characteristic::DarkMode();
|
||||
new Characteristic::Brightness(50);
|
||||
new Characteristic::Name("Light 1");
|
||||
new Characteristic::ColorTemperature();
|
||||
new Characteristic::Active();
|
||||
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
(new Characteristic::Brightness(50,false))->setRange(10,100,5);
|
||||
new Characteristic::Name("Light 2");
|
||||
|
||||
new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments
|
||||
|
||||
new Service::AccessoryInformation(); // HAP requires every Accessory to implement an AccessoryInformation Service, which has 6 required Characteristics
|
||||
new Characteristic::Name("HomeSpan Test"); // Name of the Accessory, which shows up on the HomeKit "tiles", and should be unique across Accessories
|
||||
new Characteristic::Manufacturer("HomeSpan"); // Manufacturer of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::SerialNumber("HSL-123"); // Serial Number of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Model("HSL Test"); // Model of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::FirmwareRevision(HOMESPAN_VERSION); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory)
|
||||
new Characteristic::Identify(); // Create the required Identify
|
||||
|
||||
new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service
|
||||
new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP
|
||||
|
||||
new Service::LightBulb();
|
||||
new Characteristic::On(0,true);
|
||||
(new Characteristic::Brightness(50,true))->setRange(10,100,5);
|
||||
new Characteristic::Name("Light 3");
|
||||
new Characteristic::TargetPosition();
|
||||
new Characteristic::OzoneDensity();
|
||||
(new Characteristic::OzoneDensity())->addPerms(PW|AA)->removePerms(EV|PR);
|
||||
|
||||
} // end of setup()
|
||||
|
||||
unsigned long alarmTime=0;
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
// if(dev1->get(&message))
|
||||
// Serial.printf("DEV1: '%s' %d %f %d\n",message.a,message.b,message.c,message.d);
|
||||
// if(dev2->get(&message))
|
||||
// Serial.printf("DEV2: '%s' %d %f %d\n",message.a,message.b,message.c,message.d);
|
||||
//
|
||||
// if(millis()-alarmTime>5000){
|
||||
// alarmTime=millis();
|
||||
// boolean success = dev2->send(&alarmTime);
|
||||
// Serial.printf("Success = %d\n",success);
|
||||
// }
|
||||
|
||||
} // end of loop()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void myWiFiAP(){
|
||||
Serial.print("Calling My WIFI AP\n\n");
|
||||
homeSpan.setWifiCredentials("MY_NETWORK","MY_PASSWORD");
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void wifiEstablished(){
|
||||
Serial.print("IN CALLBACK FUNCTION\n\n");
|
||||
Serial.printf("MODE = %d\n",WiFi.getMode());
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void userCom1(const char *v){
|
||||
Serial.printf("In User Command 1: '%s'\n\n",v);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void userCom2(const char *v){
|
||||
Serial.printf("In User Command 2: '%s'\n\n",v);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void statusUpdate(HS_STATUS status){
|
||||
Serial.printf("\n*** HOMESPAN STATUS CHANGE: %s\n",homeSpan.statusString(status));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -78,6 +78,7 @@ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){
|
|||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::BLINKING;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
|
@ -93,6 +94,7 @@ void Blinker::stop(){
|
|||
}
|
||||
|
||||
isPaused=true;
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
|
@ -107,6 +109,7 @@ void Blinker::on(){
|
|||
|
||||
pauseTime=millis();
|
||||
isPaused=false;
|
||||
status=STATUS::ON;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
|
@ -118,6 +121,7 @@ void Blinker::off(){
|
|||
|
||||
stop();
|
||||
led->off();
|
||||
status=STATUS::OFF;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -48,6 +48,8 @@ class Blinkable {
|
|||
|
||||
class Blinker {
|
||||
|
||||
enum STATUS {OFF, BLINKING, ON};
|
||||
|
||||
TaskHandle_t blinkHandle = NULL;
|
||||
Blinkable *led;
|
||||
|
||||
|
|
@ -55,6 +57,7 @@ class Blinker {
|
|||
int onTime;
|
||||
int offTime;
|
||||
int delayTime;
|
||||
STATUS status=STATUS::OFF;
|
||||
|
||||
unsigned long pauseDuration;
|
||||
unsigned long pauseTime;
|
||||
|
|
@ -102,6 +105,10 @@ class Blinker {
|
|||
|
||||
// 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)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
///////////////////
|
||||
|
||||
LedC::LedC(uint8_t pin, uint16_t freq){
|
||||
LedC::LedC(uint8_t pin, uint16_t freq, boolean invert){
|
||||
|
||||
if(freq==0)
|
||||
freq=DEFAULT_PWM_FREQ;
|
||||
|
|
@ -44,10 +44,7 @@ LedC::LedC(uint8_t pin, uint16_t freq){
|
|||
timerList[nTimer][nMode]->speed_mode=(ledc_mode_t)nMode;
|
||||
timerList[nTimer][nMode]->timer_num=(ledc_timer_t)nTimer;
|
||||
timerList[nTimer][nMode]->freq_hz=freq;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0)
|
||||
timerList[nTimer][nMode]->clk_cfg=LEDC_USE_APB_CLK;
|
||||
#endif
|
||||
|
||||
int res=LEDC_TIMER_BIT_MAX-1; // find the maximum possible resolution
|
||||
while(getApbFrequency()/(freq*pow(2,res))<1)
|
||||
|
|
@ -68,9 +65,7 @@ LedC::LedC(uint8_t pin, uint16_t freq){
|
|||
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;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
channelList[nChannel][nMode]->flags.output_invert=0;
|
||||
#endif
|
||||
channelList[nChannel][nMode]->flags.output_invert=invert;
|
||||
channelList[nChannel][nMode]->hpoint=0;
|
||||
channelList[nChannel][nMode]->gpio_num=pin;
|
||||
timer=timerList[nTimer][nMode];
|
||||
|
|
@ -85,18 +80,19 @@ LedC::LedC(uint8_t pin, uint16_t freq){
|
|||
|
||||
///////////////////
|
||||
|
||||
LedPin::LedPin(uint8_t pin, float level, uint16_t freq) : LedC(pin, freq){
|
||||
LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(pin, freq, invert){
|
||||
|
||||
if(!channel)
|
||||
Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels and/or Timers ***\n\n",pin);
|
||||
else
|
||||
Serial.printf("LedPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits\n",
|
||||
Serial.printf("LedPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits %s\n",
|
||||
channel->gpio_num,
|
||||
channel->speed_mode,
|
||||
channel->channel,
|
||||
channel->timer_sel,
|
||||
timer->freq_hz,
|
||||
timer->duty_resolution
|
||||
timer->duty_resolution,
|
||||
channel->flags.output_invert?"(inverted)":""
|
||||
);
|
||||
|
||||
set(level);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -59,7 +59,7 @@ class LedC {
|
|||
ledc_channel_config_t *channel=NULL;
|
||||
ledc_timer_config_t *timer;
|
||||
|
||||
LedC(uint8_t pin, uint16_t freq);
|
||||
LedC(uint8_t pin, uint16_t freq, boolean invert=false);
|
||||
|
||||
public:
|
||||
int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number
|
||||
|
|
@ -75,8 +75,8 @@ class LedC {
|
|||
class LedPin : public LedC {
|
||||
|
||||
public:
|
||||
LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ); // 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)
|
||||
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)
|
||||
|
||||
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
@ -45,9 +45,7 @@ RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){
|
|||
config->rmt_mode=RMT_MODE_TX;
|
||||
config->tx_config.carrier_en=false;
|
||||
config->channel=(rmt_channel_t)nChannels;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
|
||||
config->flags=0;
|
||||
#endif
|
||||
config->clk_div = 1;
|
||||
config->mem_block_num=1;
|
||||
config->gpio_num=(gpio_num_t)pin;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2022 Gregg E. Berman
|
||||
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,3 +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.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
|
||||
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue