commit
341ed0b452
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Welcome!
|
||||||
|
|
||||||
|
Welcome to HomeSpan - a robust and extremely easy-to-use Arduino library for creating your own [ESP32-based](https://www.espressif.com/en/products/modules/esp32) HomeKit devices entirely within the [Arduino IDE](http://www.arduino.cc).
|
||||||
|
|
||||||
|
HomeSpan provides a microcontroller-focused implementation of Apple's HomeKit Accessory Protocol Specification Release R2 (HAP-R2) designed specifically for the Espressif ESP32 microcontroller running within the Arduino IDE. HomeSpan pairs directly to HomeKit via your home WiFi network without the need for any external bridges or components. With HomeSpan you can use the full power of the ESP32's I/O functionality to create custom control software and/or hardware to automatically operate external devices from the Home App on your iPhone, iPad, or Mac, or with Siri.
|
||||||
|
|
||||||
|
HomeSpan requires version 2.0.0 or later of the [Arduino-ESP32 Board Manager](https://github.com/espressif/arduino-esp32), and has been tested up through version 2.0.7 (recommended). HomeSpan can be run on the original ESP32 as well as Espressif's ESP32-S2, ESP32-C3, and ESP32-S3 chips.
|
||||||
|
|
||||||
|
### HomeSpan Highlights
|
||||||
|
|
||||||
|
* Provides a natural, intuitive, and **very** easy-to-use framework
|
||||||
|
* Utilizes a unique *Service-Centric* approach to creating HomeKit devices
|
||||||
|
* Takes full advantage of the widely-popular Arduino IDE
|
||||||
|
* 100% HAP-R2 compliance
|
||||||
|
* 41 integrated HomeKit Services
|
||||||
|
* Operates in either Accessory or Bridge mode
|
||||||
|
* Supports pairing with Setup Codes or QR Codes
|
||||||
|
|
||||||
|
### For the HomeSpan Developer
|
||||||
|
|
||||||
|
* Extensive use of the Arduino Serial Monitor
|
||||||
|
* Real-time, easy-to-understand diagnostics
|
||||||
|
* Complete transparency to every underlying HomeKit action, data request, and data response
|
||||||
|
* Command-line interface with a variety of info, debugging, and configuration commands
|
||||||
|
* Built-in database validation to ensure your configuration meets all HAP requirements
|
||||||
|
* Dedicated classes that utilize the ESP32's 16-channel PWM peripheral for easy control of:
|
||||||
|
* LED Brightness (including auto-fading)
|
||||||
|
* Servo Motors
|
||||||
|
* Integrated Push Button and Toggle Switch functionality supporting single, double, and long presses of:
|
||||||
|
* Physical pushbuttons that connect an ESP32 pin to either ground or VCC
|
||||||
|
* Touch pads/sensors connected to an ESP32 pin (for ESP32 devices that support touch pads)
|
||||||
|
* Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals
|
||||||
|
* Dedicated classes to control one- and two-wire addressable RGB and RGBW LEDs and LED strips
|
||||||
|
* Dedicated class that faciliates seemless point-to-point communication between ESP32 devices using ESP-NOW
|
||||||
|
* Integrated Web Log for user-defined log messages
|
||||||
|
* Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics
|
||||||
|
* Additional examples and projects showcasing real-world implementations of HomeSpan
|
||||||
|
* A complete set of documentation explaining every aspect of the HomeSpan API
|
||||||
|
|
||||||
|
### For the HomeSpan End-User
|
||||||
|
|
||||||
|
* Embedded WiFi Access Point and Web Interface to allow end-users (non-developers) to:
|
||||||
|
* Set up Homespan with their own home WiFi Credentials
|
||||||
|
* Create their own HomeKit Pairing Setup Code
|
||||||
|
* Status LED and Control Button to allow end-users to:
|
||||||
|
* Force-unpair the device from HomeKit
|
||||||
|
* Perform a Factory Reset
|
||||||
|
* Launch the WiFi Access Point
|
||||||
|
* A standalone, detailed End-User Guide
|
||||||
|
|
||||||
|
## ❗Latest Update - HomeSpan 1.7.2 (4/5/2023)
|
||||||
|
|
||||||
|
* New ability to set OTA password from within sketch
|
||||||
|
* See the [OTA Page](docs/OTA.md) for details
|
||||||
|
|
||||||
|
* Added logic to allow duplicates of the same Custom Characteristic to be "defined" in more than one file in a sketch
|
||||||
|
* Allows the use of the same Custom Characteristic across multiple files in the same sketch without the compiler throwing a "redefinition error"
|
||||||
|
* See the [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
|
* Extended functionality of `setValidValues()` to work with more than just UINT8 Characteristics
|
||||||
|
* Now works with INT, UINT16, and UINT32 Characteristics, as well as UINT8 Characteristics
|
||||||
|
* See the [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
|
* New parameters added to `autoPoll()` that allow the user to set priority and chose CPU
|
||||||
|
* Provides for enhanced performance on dual-processor chips
|
||||||
|
* See the [API Reference](docs/Reference.md) for details
|
||||||
|
|
||||||
|
* Automatic LED Fading!
|
||||||
|
* Added new methods to LedPin class that enable use of the ESP32's built-in fade controls
|
||||||
|
* Allows user to specify speed of fade
|
||||||
|
* Runs in background without consuming any CPU resources
|
||||||
|
* See the [PWM Page](docs/PWM.md) for details
|
||||||
|
|
||||||
|
* Added ability to Clone the Pairing Data from one device to another
|
||||||
|
* Adds new 'P' and 'C' commands to the CLI
|
||||||
|
* Enables a broken device to be swapped for a new device (running the same sketch) without the need to unpair the old device or pair the new device
|
||||||
|
* Avoids loss of automations, scenes, and any other Home App customizations associated with device
|
||||||
|
* New and old device can be different chips (e.g. ESP32-S2 versus ESP32-C3)
|
||||||
|
* See the new [Cloning Page](docs/Cloning.md) for details
|
||||||
|
|
||||||
|
See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update.
|
||||||
|
|
||||||
|
# HomeSpan Resources
|
||||||
|
|
||||||
|
HomeSpan includes the following documentation:
|
||||||
|
|
||||||
|
* [Getting Started with HomeSpan](docs/GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices
|
||||||
|
* [HomeSpan API Overview](docs/Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch
|
||||||
|
* [HomeSpan Tutorials](docs/Tutorials.md) - a guide to HomeSpan's tutorial-sketches
|
||||||
|
* [HomeSpan Services and Characteristics](docs/ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan
|
||||||
|
* [HomeSpan Accessory Categories](docs/Categories.md) - a list of all HAP Accessory Categories defined by HomeSpan
|
||||||
|
* [HomeSpan Command-Line Interface (CLI)](docs/CLI.md) - configure a HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, monitor and update its status, and access detailed, real-time device diagnostics from the Arduino IDE Serial Monitor
|
||||||
|
* [HomeSpan User Guide](docs/UserGuide.md) - turnkey instructions on how to configure an already-programmed HomeSpan device's WiFi Credentials, modify its HomeKit Setup Code, and pair the device to HomeKit. No computer needed!
|
||||||
|
* [HomeSpan API Reference](docs/Reference.md) - a complete guide to the HomeSpan Library API
|
||||||
|
* [HomeSpan QR Codes](docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices
|
||||||
|
* [HomeSpan OTA](docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection
|
||||||
|
* [HomeSpan PWM](docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral
|
||||||
|
* [HomeSpan RFControl](docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral
|
||||||
|
* [HomeSpan Pixels](docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips
|
||||||
|
* [HomeSpan SpanPoint](docs/NOW.md) - facilitates point-to-point, bi-directional communication between ESP32 Devices using ESP-NOW
|
||||||
|
* [HomeSpan Television Services](docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics
|
||||||
|
* [HomeSpan Message Logging](docs/Logging.md) - how to generate log messages for display on the Arduino Serial Monitor as well as optionally posted to an integrated Web Log page
|
||||||
|
* [HomeSpan Device Cloning](docs/Cloning.md) - seamlessly swap a broken device for a new one without needing to re-pair and lose HomeKit automations
|
||||||
|
* [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library
|
||||||
|
* [HomeSpan FAQ](docs/FAQ.md) - answers to frequently-asked questions
|
||||||
|
|
||||||
|
Note that all documentation is version-controlled and tied to each branch. The *master* branch generally points to the latest release. The *dev* branch, when available, will contain code under active development.
|
||||||
|
|
||||||
|
# External Resources
|
||||||
|
|
||||||
|
In addition to HomeSpan resources, developers who are new to HomeKit programming should download Apple's HomeKit Accessory Protocol Specification, Non-Commercial Version, Release R2 (HAP-R2). This document is unfortunately no longer available from Apple (perhaps because it was last updated July, 2019, and is now somewhat out-of-date). However, you may be able find copies of this document elsewhere on the web. Note Apple has not replaced the HAP-R2 document with any other versions for non-commercial use, and Apple's open-source [HomeKit ADK](https://github.com/apple/HomeKitADK) only reflects the original HAP-R2 specs (rather than all the latest Services and Characteristics available in HomeKit for commercial devices).
|
||||||
|
|
||||||
|
You ***do not*** need to read the entire HAP-R2 document. The whole point of HomeSpan is that it implements all the required HAP operations under the hood so you can focus on just programming whatever logic is needed to control your real-world appliances (lights, fans, RF remote controls, etc.) with the device. However, you will find Chapters 8 and 9 of the HAP guide to be an invaluable reference as it lists and describes all of the Services and Characteristics implemented in HomeSpan, many of which you will routinely utilize in your own HomeSpan sketches.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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).
|
||||||
|
|
@ -73,6 +73,12 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p
|
||||||
* In addition to deleting all Controller data (as if the 'U' command was run), this command also deletes the device's HomeKit ID. This unique ID is broadcast to all HomeKit Controllers so the device can be uniquely recognized. When HomeSpan first runs on a new device, it creates this unique ID and stores it permanently in an NVS partition. Normally, this ID should not changed once set. However, if you are actively developing and testing a HomeSpan sketch, you may find that HomeKit is cacheing information about your device and the changes you have made to your HAP Accessory Database are not always reflected in the Home App. Sometimes simply unpairing and re-pairing the device solves this HomeKit issue. If not, deleting your device's HomeKit ID with this command forces HomeSpan to generate a new one after restarting, which means HomeKit will think this is a completely different device, thereby ignoring any prior data it had cached.
|
* In addition to deleting all Controller data (as if the 'U' command was run), this command also deletes the device's HomeKit ID. This unique ID is broadcast to all HomeKit Controllers so the device can be uniquely recognized. When HomeSpan first runs on a new device, it creates this unique ID and stores it permanently in an NVS partition. Normally, this ID should not changed once set. However, if you are actively developing and testing a HomeSpan sketch, you may find that HomeKit is cacheing information about your device and the changes you have made to your HAP Accessory Database are not always reflected in the Home App. Sometimes simply unpairing and re-pairing the device solves this HomeKit issue. If not, deleting your device's HomeKit ID with this command forces HomeSpan to generate a new one after restarting, which means HomeKit will think this is a completely different device, thereby ignoring any prior data it had cached.
|
||||||
* This command also restores the device's default Setup ID, which is used for optional pairing with QR codes, to "HSPN".
|
* This command also restores the device's default Setup ID, which is used for optional pairing with QR codes, to "HSPN".
|
||||||
|
|
||||||
|
* **P** - prints the device's Pairing Data in base-64 chunks
|
||||||
|
* Used for [Cloning](Cloning.md) the Pairing Data from one device to another
|
||||||
|
|
||||||
|
* **C** - prompts you to input the Pairing Data from another device in base-64 chunks
|
||||||
|
* Used for [Cloning](Cloning.md) the Pairing Data from one device to another
|
||||||
|
|
||||||
* **R** - restart the device
|
* **R** - restart the device
|
||||||
* This command simply reboots HomeSpan.
|
* This command simply reboots HomeSpan.
|
||||||
|
|
||||||
|
|
@ -97,7 +103,7 @@ You can extend the HomeSpan CLI with custom functions using `SpanUserCommand()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# HomeSpan Accessory Categories
|
# HomeSpan Accessory Categories
|
||||||
|
|
||||||
Every HomeSpan device must be assigned a HomeKit Accessory Category. HomeSpan implements these categories as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Section 13 of [HAP-R2](https://developer.apple.com/homekit/specification/), but without any spaces. HomeSpan Accessory Categories are defined in HomeSpan's `Category` namespace. For example, HomeSpan defines the *Garage Door Openers* Category (HAP Category 4) as `Category::GarageDoorOpeners`, which could be used when initializing HomeSpan as follows:
|
Every HomeSpan device must be assigned a HomeKit Accessory Category. HomeSpan implements these categories as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Section 13 of HAP-R2, but without any spaces. HomeSpan Accessory Categories are defined in HomeSpan's `Category` namespace. For example, HomeSpan defines the *Garage Door Openers* Category (HAP Category 4) as `Category::GarageDoorOpeners`, which could be used when initializing HomeSpan as follows:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
homeSpan.begin(Category::GarageDoorOpeners,"Acme Garage Door Lifts");
|
homeSpan.begin(Category::GarageDoorOpeners,"Acme Garage Door Lifts");
|
||||||
|
|
@ -41,5 +41,5 @@ Note that the HomeKit primarily uses the Accessory Category of a device for dete
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
# Cloning Pairing Data from one Device to another
|
||||||
|
|
||||||
|
### HomeSpan Pairing Data
|
||||||
|
|
||||||
|
Even though two different ESP32 devices may be running the exact same sketch, they are nevertheless distinct. This is because every HomeSpan Accessory has unique 17-character Device ID, a unique 32-byte long-term public key (LTPK), and a unique 64-byte long-term secret key (LTSK). When HomeSpan is run for the first time on a new device, it looks for these data in the device's non-volatile storage (NVS) memory. If it is found, the data is loaded for use. If not found, HomeSpan generates a new set of random keys, and saves this data in the NVS. The data is permanently stored, though can be erased by typing 'H' into the CLI, which causes HomeSpan to generate a new set of random keys upon the next reboot.
|
||||||
|
|
||||||
|
When HomeSpan is initially paired to HomeKit, the 36-character Device ID and 32-byte LTPK for one or more HomeKit Controllers is securely transmitted to the HomeSpan Accessory. These keys are then saved in the device's NVS for permanent retention (though can be erased by the 'H' command).
|
||||||
|
|
||||||
|
Collectively, the Accessory Device ID, LTPK and LTSK, along with the Device ID and LTPK for each paired Controller, is known as the device's *Pairing Data*. You can view the Pairing Data (except for the LTSK) for any HomeSpan Accessory by typing 'S' into the CLI. Here is an example:
|
||||||
|
|
||||||
|
```
|
||||||
|
*** HomeSpan Status ***
|
||||||
|
|
||||||
|
IP Address: 192.168.1.11
|
||||||
|
|
||||||
|
Accessory ID: 77:D2:F6:99:CE:65 LTPK: 346A544A876B124E50F9E3CC276A29D23E8B5DD0590138AA59C833A0D2096E37
|
||||||
|
Paired Controller: A487DE69-81C3-B5ED-8762-C3B9A987F967 (admin) LTPK: EE12A678DD56C4E9C0D935A341B8E6C6C098A6B3E6D4C5F5F914A54C9E85BA76
|
||||||
|
Paired Controller: 449AD09E-109D-3EB5-25B4-8A04E5C57D65 (admin) LTPK: 34A6B57DE881A75B647D2C9C68E76745A3B466577D19E4C78A67A68C4ED959B8
|
||||||
|
|
||||||
|
Connection #0 192.168.1.29 on Socket 3/16 ID=A487DE69-81C3-B5ED-8762-C3B9A987F967 (admin)
|
||||||
|
Connection #1 (unconnected)
|
||||||
|
Connection #2 (unconnected)
|
||||||
|
Connection #3 (unconnected)
|
||||||
|
Connection #4 (unconnected)
|
||||||
|
Connection #5 (unconnected)
|
||||||
|
Connection #6 (unconnected)
|
||||||
|
Connection #7 (unconnected)
|
||||||
|
Connection #8 (unconnected)
|
||||||
|
Connection #9 (unconnected)
|
||||||
|
Connection #10 (unconnected)
|
||||||
|
Connection #11 (unconnected)
|
||||||
|
|
||||||
|
*** End Status ***
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cloning a HomeSpan Device
|
||||||
|
|
||||||
|
Because every device has a unique set of Pairing Data, it is not possible to simply swap a device that is already paired to HomeKit with another one that is not yet paired - the Device IDs will be different and the second device will need to be separately paired.
|
||||||
|
|
||||||
|
This can present a problem if you need to swap out a device (perhaps because it has malfunctioned or was damaged) and you have created a lot of custom automations and scenes for the device from within the Home App. The new device you use to replace the old device will be seen as a completely new Accessory by HomeKit, and will not be connected with any automations or scenes associated with the old device. In fact, if your unpair the old device, automations and scenes specific to that device will be lost.
|
||||||
|
|
||||||
|
To solve this problem you need to be able to replace the broken device with a new device, but *without* unpairing the old device or re-pairing the new device. This requires the new device to be initialized not with a new set of randomly-generated Device IDs, LTPKs and LTSKs, but rather with the *same* Pairing Data as the old device.
|
||||||
|
|
||||||
|
Fortunately, HomeSpan provides a methods for "cloning" the Pairing Data from one device to another. This means you can swap out a broken device for a new device without HomeKit knowing the difference (provided it is running the same sketch of course). In fact, you can even swap out an ESP32 for an ESP32-S2, or ESP32-C3. As long as the sketch is the same, once you clone the Pairing Data the devices are effectively hot-swappable.
|
||||||
|
|
||||||
|
Cloning HomeSpan's Pairing Data is a two-step process. First you output the Pairing Data from one device to the Serial Monitor, then you copy and paste this data into the Serial Monitor of the second device. Of course if the first device is completely broken you will not be able to output its Pairing Data. If you create a lot of automations in HomeKit you may want to output the Pairing Data from each of your devices and save it in a plain text file for later use should any device need to be replaced in the future.
|
||||||
|
|
||||||
|
#### Step 1: Type 'P' into the Serial Monitor CLI of the first device to output its Pairing Data
|
||||||
|
|
||||||
|
Unlike the 'S' command, the 'P' command compresses all the Pairing Data into *base-64* chunks to make it easier to copy and paste as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
*** Pairing Data used for Cloning another Device
|
||||||
|
|
||||||
|
Accessory data: ZzbH11I8uNx47Y3Bapq3axQfY5uPOrDfC8D2Q6ke2NwWqat/IGa/6ll8xyY8AShMYO2q6h8gZr/qWXzHJjwBKExg7arqFnNsfXUjy43HgNzc6RDI6RjY6OTk6Q0U6NjUb7mHwbmWzrEWca+5frayfmp=
|
||||||
|
Controller data: YaNJH5JYDAQE4NjI0NTAwNy02Mi1FRUY4ODNENTA2NjdDvTRGLTRBRDEtQjkwRXFM1On32PKvumS+0YgVMaEo53X/TYNzg==
|
||||||
|
Controller data: MEUwLTREMEUtODk3Ni0yMjBDREQ2RDUxMjjmah3s+Je0GkmAQE0NDQ1NUE2Ni1ExIUkujzeyWfCCRWol/xecsVkjAIYDRQ==
|
||||||
|
|
||||||
|
*** End Pairing Data
|
||||||
|
```
|
||||||
|
|
||||||
|
The first line completely encodes the Pairing Data for the HomeSpan Accessory. The second two lines encode the Pairing Data for two Controllers that HomeKit is using to control the HomeSpan device. Note your system may only have one Controller, or it may have more than two. The number of Controllers depends on your HomeKit network, how it is configured, what devices you have (Apple TVs, HomePods, etc.) and what version of iOS you are running.
|
||||||
|
|
||||||
|
Copy this data, exactly as is, from the CLI and save it in a text file. Make sure not to lose any trailing equal signs as they are part of the base‑64 data!
|
||||||
|
|
||||||
|
Next, power down the first device, or at least remove it from the WiFi network to avoid potential duplications of two devices running on the same network with identical Pairing Data (HomeKit will likely not behave if this occurs). If the second device is not plugged in, do so now and open its Serial Monitor.
|
||||||
|
|
||||||
|
#### Step 2: Type 'C' into the Serial Monitor CLI of the second device to input the Pairing Data you just saved from the first device
|
||||||
|
|
||||||
|
HomeSpan will begin by asking you for the Accessory Pairing Data. Copy and paste this data (it's the first set of base-64 data output in Step 1 above) directly into the Serial Monitor input window and hit return. If you copied the data correctly it will be accepted and HomeSpan will display the Device ID that was encoded in the data (it does not bother to display the LTPK and LTSK data). The Device ID should match that of the orignal device.
|
||||||
|
|
||||||
|
If you copied or pasted the data incorrectly, HomeSpan will let you know there is a problem, cancel the process, and reboot without making any changes. You can also cancel the process by simply hitting return after typing 'P' *without* entering any data (this does not cause a reboot, since no data was changed).
|
||||||
|
|
||||||
|
After the Accessory data is accepted, HomeSpan will then ask for Controller data. Copy and paste this base-64 data from one of the Controllers in the saved text file directly into the Serial Monitor input window and hit return. As before, if you copied and pasted correctly, HomeSpan will accept the data and display the Device ID of the Controller. If you copied and pasted incorrectly, HomeSpan will inform you of the error, cancel the process, and reboot without making any changes.
|
||||||
|
|
||||||
|
Assuming the data for the first Controller has been accepted, HomeSpan will ask you to repeat the process for any other Controllers you may have. Keep repeating the process for copying and pasting the Pairing Data for each Controller. When you have input the Pairing Data for all Controllers, simply hit return without entering any data when asked for the next Controller. An empty response tells HomeSpan you are done adding Controller data.
|
||||||
|
|
||||||
|
Finally, HomeSpan will ask you to confirm saving the new data. Type either 'y' to confirm (yes) or 'n' to cancel (no). If you type 'n', HomeSpan will reboot without saving any of the changes.
|
||||||
|
|
||||||
|
If you type 'y', HomeSpan will save all of the new Pairing Data in the device's NVS and reboot. Upon restarting, this second device will be a perfect clone of the first device and HomeKit should recognize it as if it were the original. You will not need to re-pair the device or make any other changes to the Home App.
|
||||||
|
|
||||||
|
❗Caution: Do NOT run two devices on the same HomeKit network with the same Pairing Data. If you want to experiment by Cloning a working device onto a second device, make sure to unplug the first device before cloning the data onto the second device. When you are finished experimenting, type 'H' into the CLI of one of the devices so the cloned Pairing Data will be erased and re-generated into something once again unique, allowing you to plug both devices in at the same time without conflict.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
|
|
||||||
#### Can you add a Web Server to HomeSpan?
|
#### Can you add a Web Server to HomeSpan?
|
||||||
|
|
||||||
* Yes, provided you implement your Web Server using standard ESP32-Arduino libraries, such as `WebServer.h`. See [ProgrammableHub](../Other%20Examples/ProgrammableHub) for an illustrative example of how to easily integrate a Web Server into HomeSpan. This project also covers various other advanced topics, including TCP slot management, dynamic creation of Accessories, and saving arbitrary data in the ESP32's NVS.
|
* Yes, provided you implement your Web Server using standard ESP32-Arduino libraries, such as `WebServer.h`. See [ProgrammableHub](../examples/Other%20Examples/ProgrammableHub) for an illustrative example of how to easily integrate a Web Server into HomeSpan. This project also covers various other advanced topics, including TCP slot management, dynamic creation of Accessories, and saving arbitrary data in the ESP32's NVS.
|
||||||
|
|
||||||
#### Can you add *custom* Services and Characteristics to HomeSpan?
|
#### Can you add *custom* Services and Characteristics to HomeSpan?
|
||||||
|
|
||||||
|
|
@ -75,5 +75,5 @@
|
||||||
* Though not documented in HAP-R2, it appears that the Doorbell Service is designed to be used in conjunction with another service, such as the Lock Mechanism. If you add in a second service, the Home App will show the appropriate Tile (such as a Lock) with the Doorbell being the second service. Howeveer, you can still use the Doorbell Service on a stanadlone basis --- even though the Home App says it is unsupported, a button press on the device will properly trigger a chime on your Home Pods as expected.
|
* Though not documented in HAP-R2, it appears that the Doorbell Service is designed to be used in conjunction with another service, such as the Lock Mechanism. If you add in a second service, the Home App will show the appropriate Tile (such as a Lock) with the Doorbell being the second service. Howeveer, you can still use the Doorbell Service on a stanadlone basis --- even though the Home App says it is unsupported, a button press on the device will properly trigger a chime on your Home Pods as expected.
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ If you've not yet read through the [HomeSpan API Overview](Overview.md) page, yo
|
||||||
|
|
||||||
Next, explore the tutorial sketches, upload a few, and see how they work. The examples start simple and grow in complexity, taking you through all the functions and features of HomeSpan. Along the way you'll also learn a lot of HomeKit tips and tricks. See [HomeSpan Tutorials](Tutorials.md) for a summary of all the included examples. Find something in a sketch you don't understand? Visit the [HomeSpan API Reference](Reference.md) for details on all HomeSpan objects, functions, and methods. Have a more general question? See if it's been answered on the [HomeSpan FAQ](FAQ.md) page or any of the [Disussion](https://github.com/HomeSpan/HomeSpan/discussions) or [Issues](https://github.com/HomeSpan/HomeSpan/issues) pages. If not, feel free to join the Discusion by adding a new question.
|
Next, explore the tutorial sketches, upload a few, and see how they work. The examples start simple and grow in complexity, taking you through all the functions and features of HomeSpan. Along the way you'll also learn a lot of HomeKit tips and tricks. See [HomeSpan Tutorials](Tutorials.md) for a summary of all the included examples. Find something in a sketch you don't understand? Visit the [HomeSpan API Reference](Reference.md) for details on all HomeSpan objects, functions, and methods. Have a more general question? See if it's been answered on the [HomeSpan FAQ](FAQ.md) page or any of the [Disussion](https://github.com/HomeSpan/HomeSpan/discussions) or [Issues](https://github.com/HomeSpan/HomeSpan/issues) pages. If not, feel free to join the Discusion by adding a new question.
|
||||||
|
|
||||||
Ready to start creating your own HomeSpan sketches? Check out the [HomeSpan Services and Characteristics](ServiceList.md) page for a full list of all the HomeKit Services and Characteristics supported by HomeSpan, as well as the [HomeSpan Categories](Categories.md) page for a list of all supported HomeKit Categories. And don't forget to use Apple's [HomeKit Accessory Protocol Specification, Release R2 (HAP-R2)](https://developer.apple.com/homekit/specification/) as your go-to reference for details on every Service and Characteristic.
|
Ready to start creating your own HomeSpan sketches? Check out the [HomeSpan Services and Characteristics](ServiceList.md) page for a full list of all the HomeKit Services and Characteristics supported by HomeSpan, as well as the [HomeSpan Categories](Categories.md) page for a list of all supported HomeKit Categories. And don't forget to use Apple's HomeKit Accessory Protocol Specification, Release R2 (HAP-R2) as your go-to reference for details on every Service and Characteristic.
|
||||||
|
|
||||||
While developing your sketch remember to utilize the Arduino Serial Monitor. HomeSpan produces extensive diagnostics that will help you debug your sketches as well as monitor all aspects of the HomeSpan device. You'll also be able to control various aspects of HomeSpan from the Serial Monitor using the [HomeSpan Command-Line Interface (CLI)](CLI.md), including configuring the device's WiFi Credentials and HomeKit Setup Code.
|
While developing your sketch remember to utilize the Arduino Serial Monitor. HomeSpan produces extensive diagnostics that will help you debug your sketches as well as monitor all aspects of the HomeSpan device. You'll also be able to control various aspects of HomeSpan from the Serial Monitor using the [HomeSpan Command-Line Interface (CLI)](CLI.md), including configuring the device's WiFi Credentials and HomeKit Setup Code.
|
||||||
|
|
||||||
|
|
@ -72,4 +72,4 @@ Finally, disconnect your HomeSpan device from the computer and power it directly
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,6 @@ See [Example 19 - WebLog](Tutorials.md#example-19---weblog) for a tutorial sketc
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ 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.
|
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 the following sketches:
|
Examples showing such a configuration can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteSensors*](../examples/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
|
* *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
|
* *RemoteDevice.ino* - a lightweight sketch that simulates taking periodic temperature measurements, which are then transmitted to the Main Device via SpanPoint
|
||||||
|
|
@ -83,4 +83,4 @@ Examples showing such a configuration can be found in the Arduino IDE under [*Fi
|
||||||
* *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
|
* *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
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ By default, HomeSpan requires the use of a password whenever you begin an OTA up
|
||||||
|
|
||||||
You can change the password for a HomeSpan device from the [HomeSpan CLI](CLI.md) with the 'O' command. Similar to a device's Setup Code, HomeSpan saves a non-recoverable hashed version of the OTA password you specify in non-volatile storage (NVS). If you forget the password you specified, you'll need to create a new one using the 'O' command, or you can restore the default OTA password by fully erasing the NVS with the 'E' command.
|
You can change the password for a HomeSpan device from the [HomeSpan CLI](CLI.md) with the 'O' command. Similar to a device's Setup Code, HomeSpan saves a non-recoverable hashed version of the OTA password you specify in non-volatile storage (NVS). If you forget the password you specified, you'll need to create a new one using the 'O' command, or you can restore the default OTA password by fully erasing the NVS with the 'E' command.
|
||||||
|
|
||||||
|
You can also change the password programmatically from within a sketch by calling `homeSpan.enableOTA(const char *pwd)`. This is not as secure as setting the password using the method above since your sketch will contain a plaintext-version, instead of a hashed-version, or your password. Note that setting your password this way causes HomeSpan to ignore, but does not alter, any password you have saved in NVS using the 'O' command.
|
||||||
|
|
||||||
> :exclamation: Though not recommended, you can override the requirement for a password when enabling OTA for your sketch by including *false* as a parameter to the enabling method as such: `homeSpan.enableOTA(false)`. Use with caution! Anyone who can access the device over your network will now be able to upload a new sketch.
|
> :exclamation: Though not recommended, you can override the requirement for a password when enabling OTA for your sketch by including *false* as a parameter to the enabling method as such: `homeSpan.enableOTA(false)`. Use with caution! Anyone who can access the device over your network will now be able to upload a new sketch.
|
||||||
|
|
||||||
Note that in in order for OTA to properly operate, your sketch must be compiled with a partition scheme that includes OTA partitions. Partition schemes are found under the *Tools → Partition Scheme* menu of the Arduino IDE. Select a scheme that indicates it supports OTA. Note that schemes labeled "default" usually include OTA partitions. If unsure, try it out. HomeSpan will let you know if it does or does not.
|
Note that in in order for OTA to properly operate, your sketch must be compiled with a partition scheme that includes OTA partitions. Partition schemes are found under the *Tools → Partition Scheme* menu of the Arduino IDE. Select a scheme that indicates it supports OTA. Note that schemes labeled "default" usually include OTA partitions. If unsure, try it out. HomeSpan will let you know if it does or does not.
|
||||||
|
|
@ -36,6 +38,6 @@ Note that these check are *only* applicable when uploading sketches via OTA. Th
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -314,5 +314,5 @@ Finally, don't forget to visit the [HomeSpan Command-Line Interface (CLI)](CLI.m
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
|
||||||
28
docs/PWM.md
28
docs/PWM.md
|
|
@ -19,6 +19,28 @@ Creating an instance of this **class** configures the specified *pin* to output
|
||||||
|
|
||||||
* sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on)
|
* sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on)
|
||||||
|
|
||||||
|
* `int fade(float level, uint32_t fadeTime, int fadeType=LedPin::ABSOLUTE)`
|
||||||
|
|
||||||
|
* uses the ESP32's PWM hardware to smoothly fade the LED to *level* (from 0-100) over a maximum of *fadeTime* milliseconds
|
||||||
|
* if *fadeType* is set to **LedPin::ABSOLUTE** (the default), fading will take the full amount of time specified by *fadeTime*
|
||||||
|
* if *fadeType* is set to **LedPin::PROPORTIONAL**, the fading time will be scaled down proportionally according to the difference between the current level and the level specified. For example, if the current level is set to 30, then
|
||||||
|
* `fade(20, 1000, LedPin::ABSOLUTE)` sets the level to 20 over the course of 1 second, whereas
|
||||||
|
* `fade(20, 1000, LedPin::PROPORTIONAL)` sets the level to 20 over the course of 100 milliseconds (since the level only needs to change by 10 out of 100 units)
|
||||||
|
* this is a **NON-BLOCKING** method and will return immediately. Fading occurs in the background controlled by the ESP32 hardware
|
||||||
|
* note: once fading begins it CANNOT be stopped or changed until completed (this is a limitation of the ESP32 hardware)
|
||||||
|
* this method returns 0 if the fading has successfully started, or 1 if fading is already in progress and cannot yet be changed (new requests for fading while fading is already in progress for a specific LedPin are simply ignored)
|
||||||
|
* use the *fadeStatus* method (below) to determine the current fading status of any given LedPin
|
||||||
|
|
||||||
|
* `int fadeStatus()`
|
||||||
|
|
||||||
|
* returns the fading status of an LedPin. Return values are as follows:
|
||||||
|
|
||||||
|
* **LedPin::NOT_FADING** - the LedPin is not currently fading
|
||||||
|
* **LedPin::FADING** - fading on LedPin is currently in progress and cannot be changed/stopped
|
||||||
|
* **LedPin::COMPLETED** - fading has just completed
|
||||||
|
* once this value is returned, subsequent calls to `fadeStatus()` will return **LedPin::NOT_FADING** (unless you called `fade()` again)
|
||||||
|
* by checking for `fadeStatus()==LedPin::COMPLETED` in a `loop()` method, you can thus trigger a new action (if desired) once fading is completed
|
||||||
|
|
||||||
* `int getPin()`
|
* `int getPin()`
|
||||||
|
|
||||||
* returns the pin number (or -1 if LedPin was not successfully initialized)
|
* returns the pin number (or -1 if LedPin was not successfully initialized)
|
||||||
|
|
@ -34,7 +56,7 @@ LedPin also includes a static class function that converts Hue/Saturation/Bright
|
||||||
* *g* - output Green value, range 0-1
|
* *g* - output Green value, range 0-1
|
||||||
* *b* - output Blue value, range 0-1
|
* *b* - output Blue value, range 0-1
|
||||||
|
|
||||||
See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED.
|
See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. Also see [*File → Examples → HomeSpan → Other Examples → FadingLED*](../examples/Other%20Examples/FadingLED) for an example of to use the ESP32's built-in fading controls.
|
||||||
|
|
||||||
## *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])*
|
## *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])*
|
||||||
|
|
||||||
|
|
@ -57,7 +79,7 @@ The *minMicros* parameter must be less than the *maxMicros* parameter, but setti
|
||||||
|
|
||||||
* returns the pin number (or -1 if ServoPin was not successfully initialized)
|
* returns the pin number (or -1 if ServoPin was not successfully initialized)
|
||||||
|
|
||||||
A worked example showing how ServoPin can be used to control the Horizontal Tilt of a motorized Window Shade can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → ServoControl*](../Other%20Examples/ServoControl).
|
A worked example showing how ServoPin can be used to control the Horizontal Tilt of a motorized Window Shade can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → ServoControl*](../examples/Other%20Examples/ServoControl).
|
||||||
|
|
||||||
### PWM Resource Allocation and Limitations
|
### PWM Resource Allocation and Limitations
|
||||||
|
|
||||||
|
|
@ -74,4 +96,4 @@ HomeSpan will report a non-fatal error message to the Arduino Serial Monitor whe
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -118,10 +118,10 @@ Unlike the **Pixel** class, the **Dot** class does *not* utilize the ESP32's RMT
|
||||||
|
|
||||||
### Example Sketches
|
### Example Sketches
|
||||||
|
|
||||||
A fully worked example showing how to use the Pixel library within a HomeSpan sketch to control an RGB Pixel Device, an RGBW Pixel Device, and an RGB DotStar Device, all from the Home App on your iPhone, can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → Pixel*](../Other%20Examples/Pixel).
|
A fully worked example showing how to use the Pixel library within a HomeSpan sketch to control an RGB Pixel Device, an RGBW Pixel Device, and an RGB DotStar Device, all from the Home App on your iPhone, can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → Pixel*](../examples/Other%20Examples/Pixel).
|
||||||
|
|
||||||
For a more complete showcase of the Pixel library , check out [Holiday Lights](https://github.com/HomeSpan/HolidayLights) on the [HomeSpan Projects page](https://github.com/topics/homespan). This sketch demonstrates how the Pixel library can be used to generate a variety of special effects with a 60-pixel RGBW strip. The sketch also showcases the use of HomeSpan's [Custom Characteristic macro](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md#define-custom_charnameuuidpermsformatdefaultvalueminvaluemaxvaluestaticrange) to implement a special-effects "selector" button for use in the Eve for HomeKit App.
|
For a more complete showcase of the Pixel library , check out [Holiday Lights](https://github.com/HomeSpan/HolidayLights) on the [HomeSpan Projects page](https://github.com/topics/homespan). This sketch demonstrates how the Pixel library can be used to generate a variety of special effects with a 60-pixel RGBW strip. The sketch also showcases the use of HomeSpan's [Custom Characteristic macro](Reference.md#custom-characteristics-and-custom-services-macros) to implement a special-effects "selector" button for use in the Eve for HomeKit App.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,6 @@ The result must be 9 digits. If less, pad with leading zeros.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters
|
||||||
|
|
||||||
## Example RFControl Sketch
|
## Example RFControl Sketch
|
||||||
|
|
||||||
Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteControl*](../Other%20Examples/RemoteControl).
|
Below is a complete sketch that produces two different pulse trains with the signal output linked to the ESP32 device's built-in LED (rather than an RF or IR transmitter). For illustrative purposes the tick duration has been set to a very long 100𝛍s, and pulse times range from of 1000-10,000 ticks, so that the individual pulses are easily discernable on the LED. Note this example sketch is also available in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → RemoteControl*](../examples/Other%20Examples/RemoteControl).
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
/* HomeSpan Remote Control Example */
|
/* HomeSpan Remote Control Example */
|
||||||
|
|
@ -139,4 +139,4 @@ void loop(){
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -109,13 +109,20 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali
|
||||||
|
|
||||||
The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment. Unless otherwise noted, calls **should** be made before `begin()` to take effect:
|
The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment. Unless otherwise noted, calls **should** be made before `begin()` to take effect:
|
||||||
|
|
||||||
* `void enableOTA(boolean auth=true, boolean safeLoad=true)`
|
* `int enableOTA(boolean auth=true, boolean safeLoad=true)`
|
||||||
* enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled
|
* enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled
|
||||||
* HomeSpan OTA requires an authorizing password unless *auth* is specified and set to *false*
|
* HomeSpan OTA requires an authorizing password unless *auth* is specified and set to *false*
|
||||||
* the default OTA password for new HomeSpan devices is "homespan-ota"
|
* the default OTA password for new HomeSpan devices is "homespan-ota"
|
||||||
* this can be changed via the [HomeSpan CLI](CLI.md) using the 'O' command
|
* this can be changed via the [HomeSpan CLI](CLI.md) using the 'O' command
|
||||||
* note enabling OTA reduces the number of HAP Controller Connections by 1
|
* note enabling OTA reduces the number of HAP Controller Connections by 1
|
||||||
* OTA Safe Load will be enabled by default unless the second argument is specified and set to *false*. HomeSpan OTA Safe Load checks to ensure that sketches uploaded to an existing HomeSpan device are themselves HomeSpan sketches, and that they also have OTA enabled. See [HomeSpan OTA Safe Load](OTA.md#ota-safe-load) for details
|
* OTA Safe Load will be enabled by default unless the second argument is specified and set to *false*. HomeSpan OTA Safe Load checks to ensure that sketches uploaded to an existing HomeSpan device are themselves HomeSpan sketches, and that they also have OTA enabled. See [HomeSpan OTA Safe Load](OTA.md#ota-safe-load) for details
|
||||||
|
* returns 0 if enabling OTA was successful, or -1 and reports an error to the Serial Monitor if not
|
||||||
|
|
||||||
|
* `int enableOTA(const char *pwd, boolean safeLoad=true)`
|
||||||
|
* an alternative form of `enableOTA()` that allows you to programmatically change the OTA password to the specified *pwd*
|
||||||
|
* *pwd* must contain between 1 and 32 characters
|
||||||
|
* this command causes HomeSpan to ignore, but does not otherwise alter, any password stored using the 'O' command
|
||||||
|
* returns 0 if enabling OTA was successful, or -1 and reports an error to the Serial Monitor if not
|
||||||
|
|
||||||
* `void enableAutoStartAP()`
|
* `void enableAutoStartAP()`
|
||||||
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
|
* enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time
|
||||||
|
|
@ -221,12 +228,15 @@ The following **optional** `homeSpan` methods provide additional run-time functi
|
||||||
|
|
||||||
The following `homeSpan` methods are considered experimental, since not all use cases have been explored or debugged. Use with caution:
|
The following `homeSpan` methods are considered experimental, since not all use cases have been explored or debugged. Use with caution:
|
||||||
|
|
||||||
* `void autoPoll(uint32_t stackSize)`
|
* `void autoPoll(uint32_t stackSize, uint32_t priority, uint32_t cpu)`
|
||||||
* an *optional* method to create a task with *stackSize* bytes of stack memory that repeatedly calls `poll()` in the background. This frees up the Ardino `loop()` method for any user-defined code to run in parallel that would otherwise block, or be blocked by, calling `poll()` in the `loop()` method
|
|
||||||
|
* an *optional* method to create a separate task that repeatedly calls `poll()` in the background. This frees up the Ardino `loop()` method for any user-defined code to run in parallel that would otherwise block, or be blocked by, calling `poll()` in the `loop()` method. Parameters, and their default values if unspecified, are as follows:
|
||||||
|
|
||||||
|
* *stackSize* - size of stack, in bytes, used by the polling task. Default=8192 if unspecified
|
||||||
|
* *priority* - priority at which task runs. Minimum is 1. Maximum is typically 24, but it depends on how the ESP32 operating system is configured. If you set it to an arbitrarily high value (e.g. 999), it will be set to the maximum priority allowed. Default=1 if unspecified
|
||||||
|
* *cpu* - specifies the CPU on which the polling task will run. Valid values are 0 and 1. This paramater is ignored on single-cpu boards. Default=0 if unspecified
|
||||||
* if used, **must** be placed in a sketch as the last line in the Arduino `setup()` method
|
* if used, **must** be placed in a sketch as the last line in the Arduino `setup()` method
|
||||||
* HomeSpan will throw and error and halt if both `poll()`and `autoPoll()` are used in the same sketch - either place `poll()` in the Arduino `loop()` method **or** place `autoPoll()` at the the end of the Arduino `setup()` method
|
* HomeSpan will throw and error and halt if both `poll()`and `autoPoll()` are used in the same sketch - either place `poll()` in the Arduino `loop()` method **or** place `autoPoll()` at the the end of the Arduino `setup()` method
|
||||||
* can be used with both single-core and dual-core ESP32 boards. If used with a dual-core board, the polling task is created on the free processor that is typically not running other Arduino functions
|
|
||||||
* if *stackSize* is not specified, defaults to the size used by the system for the normal Arduino `loop()` task (typically 8192 bytes)
|
|
||||||
* if this method is used, and you have no need to add your own code to the main Arduino `loop()`, you can safely skip defining a blank `void loop(){}` function in your sketch
|
* if this method is used, and you have no need to add your own code to the main Arduino `loop()`, you can safely skip defining a blank `void loop(){}` function in your sketch
|
||||||
* warning: if any code you add to the Arduino `loop()` method tries to alter any HomeSpan settings or functions running in the background `poll()` task, race conditions may yield undefined results
|
* warning: if any code you add to the Arduino `loop()` method tries to alter any HomeSpan settings or functions running in the background `poll()` task, race conditions may yield undefined results
|
||||||
|
|
||||||
|
|
@ -268,8 +278,8 @@ The following methods are supported:
|
||||||
* note though this functionality is defined by Apple in HAP-R2, it seems to have been deprecated and no longer serves any purpose or has any affect on the Home App
|
* note though this functionality is defined by Apple in HAP-R2, it seems to have been deprecated and no longer serves any purpose or has any affect on the Home App
|
||||||
|
|
||||||
* `SpanService *addLink(SpanService *svc)`
|
* `SpanService *addLink(SpanService *svc)`
|
||||||
* adds *svc* as a Linked Service. Returns a pointer to the calling Service itself so that the method can be chained during instantiation.
|
* adds *svc* as a Linked Service. Returns a pointer to the calling Service itself so that the method can be chained during instantiation
|
||||||
* note that Linked Services are only applicable for select HAP Services. See Apple's [HAP-R2](https://developer.apple.com/support/homekit-accessory-protocol/) documentation for full details.
|
* note that Linked Services are only applicable for select HAP Services. See Apple's HAP-R2 documentation for full details
|
||||||
* example: `(new Service::Faucet)->addLink(new Service::Valve)->addLink(new Service::Valve);` (links two Valves to a Faucet)
|
* example: `(new Service::Faucet)->addLink(new Service::Valve)->addLink(new Service::Valve);` (links two Valves to a Faucet)
|
||||||
|
|
||||||
* `vector<SpanService *> getLinks()`
|
* `vector<SpanService *> getLinks()`
|
||||||
|
|
@ -334,9 +344,8 @@ This is a **base class** from which all HomeSpan Characteristics are derived, an
|
||||||
|
|
||||||
* `SpanCharacteristic *setValidValues(int n, [int v1, int v2 ...])`
|
* `SpanCharacteristic *setValidValues(int n, [int v1, int v2 ...])`
|
||||||
* overrides the default HAP Valid Values for Characteristics that have specific enumerated Valid Values with a variable-length list of *n* values *v1*, *v2*, etc.
|
* overrides the default HAP Valid Values for Characteristics that have specific enumerated Valid Values with a variable-length list of *n* values *v1*, *v2*, etc.
|
||||||
* an error is thrown if:
|
* works on Characteristics with UINT8, UINT16, UINT32, and INT formats only
|
||||||
* called on a Characteristic that does not have specific enumerated Valid Values, or
|
* a warning message is thrown, and the request is ignored, if this method is called on a Characteristic with any other format
|
||||||
* called more than once on the same Characteristic
|
|
||||||
* returns a pointer to the Characteristic itself so that the method can be chained during instantiation
|
* returns a pointer to the Characteristic itself so that the method can be chained during instantiation
|
||||||
* example: `(new Characteristic::SecuritySystemTargetState())->setValidValues(3,0,1,3);` creates a new Valid Value list of length=3 containing the values 0, 1, and 3. This has the effect of informing HomeKit that a SecuritySystemTargetState value of 2 (Night Arm) is not valid and should not be shown as a choice in the Home App
|
* example: `(new Characteristic::SecuritySystemTargetState())->setValidValues(3,0,1,3);` creates a new Valid Value list of length=3 containing the values 0, 1, and 3. This has the effect of informing HomeKit that a SecuritySystemTargetState value of 2 (Night Arm) is not valid and should not be shown as a choice in the Home App
|
||||||
|
|
||||||
|
|
@ -544,6 +553,8 @@ Note that Custom Characteristics must be created at the global level (i.e. not i
|
||||||
|
|
||||||
> 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.
|
> 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.
|
||||||
|
|
||||||
|
> Advanced Tip 3: When using multi-file sketches, the compiler will throw a "redefinition error" if you define the same Custom Characteristic in more than one file. To avoid this error and allow the same Custom Characteristic to be used across more than one file, add the line `#define CUSTOM_CHAR_HEADER` *before* `#include "HomeSpan.h"` in each file containing a *duplicate* definition of a previously-defined Custom Characteristic.
|
||||||
|
|
||||||
### *CUSTOM_SERV(name,uuid)*
|
### *CUSTOM_SERV(name,uuid)*
|
||||||
|
|
||||||
Creates a custom Service for use with third-party applications (such as *Eve for HomeKit*). Custom Services will be displayed in the native Apple Home App with a Tile labeled "Not Supported", but otherwise the Service will be safely ignored by the Home App. Parameters are as follows (note that quotes should NOT be used in either of the macro parameters):
|
Creates a custom Service for use with third-party applications (such as *Eve for HomeKit*). Custom Services will be displayed in the native Apple Home App with a Tile labeled "Not Supported", but otherwise the Service will be safely ignored by the Home App. Parameters are as follows (note that quotes should NOT be used in either of the macro parameters):
|
||||||
|
|
@ -553,7 +564,7 @@ Creates a custom Service for use with third-party applications (such as *Eve for
|
||||||
|
|
||||||
Custom Services may contain a mix of both Custom Characteristics and standard HAP Characteristics, though since the Service itself is custom, the Home App will ignore the entire Service even if it contains some standard HAP Characterstics. Note that Custom Services must be created prior to calling `homeSpan.begin()`
|
Custom Services may contain a mix of both Custom Characteristics and standard HAP Characteristics, though since the Service itself is custom, the Home App will ignore the entire Service even if it contains some standard HAP Characterstics. Note that Custom Services must be created prior to calling `homeSpan.begin()`
|
||||||
|
|
||||||
A fully worked example showing how to use both the ***CUSTOM_SERV()*** and ***CUSTOM_CHAR()*** macros to create a Pressure Sensor Accessory that is recognized by *Eve for HomeKit* can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → CustomService*](../Other%20Examples/CustomService).
|
A fully worked example showing how to use both the ***CUSTOM_SERV()*** and ***CUSTOM_CHAR()*** macros to create a Pressure Sensor Accessory that is recognized by *Eve for HomeKit* can be found in the Arduino IDE under [*File → Examples → HomeSpan → Other Examples → CustomService*](../examples/Other%20Examples/CustomService).
|
||||||
|
|
||||||
## Other Macros
|
## Other Macros
|
||||||
|
|
||||||
|
|
@ -599,4 +610,4 @@ If REQUIRED is defined in the main sketch *prior* to including the HomeSpan libr
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# HomeSpan Services and Characteristics
|
# HomeSpan Services and Characteristics
|
||||||
|
|
||||||
HomeSpan implements all [HAP-R2](https://developer.apple.com/homekit/specification/) Services and Characteristics except for those that involve video or audio streaming, Apple TV, or advanced lock management (i.e. all HAP Services except those that require Characteristics with a TLV8 data type).
|
HomeSpan implements all HAP-R2 Services and Characteristics except for those that involve video or audio streaming, Apple TV, or advanced lock management (i.e. all HAP Services except those that require Characteristics with a TLV8 data type).
|
||||||
|
|
||||||
HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/), but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`.
|
HomeSpan Services and Characteristics are implemented as C++ Classes with names that exactly match the spelling and capitalization specified by Apple in Sections 8 and 9 of HAP-R2, but without any spaces. HomeSpan Services are defined in HomeSpan's `Service` namespace. HomeSpan Characteristics are defined in HomeSpan's `Characteristic` namespace. For example, HomeSpan defines the *Carbon Dioxide Sensor* Service (HAP Service 8.7) as `Service::CarbonDioxideSensor`, and the *Carbon Dioxide Detected* Characteristic (HAP Characteristic 9.16) as `Characteristic::CarbonDioxideDetected`.
|
||||||
|
|
||||||
HomeSpan Services and Characteristics are instantiated with a C++ `new` command. Services do not take any arguments, whereas Characteristics take a single, optional argument that is used to initialize the value of the Characteristic at startup. If this argument is not specified, HomeSpan will apply a reasonable [default value](#characteristic-types-and-defaults) based on the Characteristic's type and allowed range.
|
HomeSpan Services and Characteristics are instantiated with a C++ `new` command. Services do not take any arguments, whereas Characteristics take a single, optional argument that is used to initialize the value of the Characteristic at startup. If this argument is not specified, HomeSpan will apply a reasonable [default value](#characteristic-types-and-defaults) based on the Characteristic's type and allowed range.
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ new Service::LightBulb(); // instantiate a Light Bulb Se
|
||||||
new Characteristic::Name("Living Room Lamp"); // instantiate an optional Name Characteristic for this Service, and set to "Living Room Lamp"
|
new Characteristic::Name("Living Room Lamp"); // instantiate an optional Name Characteristic for this Service, and set to "Living Room Lamp"
|
||||||
```
|
```
|
||||||
|
|
||||||
Please see Sections 8 and 9 of [HAP-R2](https://developer.apple.com/homekit/specification/) for a complete description of all HAP Services and Characteristics. Note that HomeSpan's Service and Characteristic Classes already contain all the required HAP fields, such as the UUID, Format, and Permissions, so you don't need to specify any of these parameters.
|
Please see Sections 8 and 9 of HAP-R2 for a complete description of all HAP Services and Characteristics. Note that HomeSpan's Service and Characteristic Classes already contain all the required HAP fields, such as the UUID, Format, and Permissions, so you don't need to specify any of these parameters.
|
||||||
|
|
||||||
Additionally, when first starting up, HomeSpan begins by validating the device's configuration to ensure each Service you instantiate includes all required Characteristics, but does not include any Characteristics that are neither required nor optional. If any errors are found, HomeSpan reports them to the Arduino Serial Monitor.
|
Additionally, when first starting up, HomeSpan begins by validating the device's configuration to ensure each Service you instantiate includes all required Characteristics, but does not include any Characteristics that are neither required nor optional. If any errors are found, HomeSpan reports them to the Arduino Serial Monitor.
|
||||||
|
|
||||||
|
|
@ -200,4 +200,4 @@ WaterLevel|double|0|[0,100]|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -79,4 +79,4 @@ Much thanks to @unreality for the PR to include Television codes and associated
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -111,30 +111,33 @@ Example 20 illustrates a number of advanced techniques through the implementatio
|
||||||
|
|
||||||
The following examples showcase a variety of HomeSpan and HomeKit functionality as referenced in different sections of the HomeSpan documentation. The sketches can be found in the Arduino IDE under *File → Examples → HomeSpan → Other Examples*
|
The following examples showcase a variety of HomeSpan and HomeKit functionality as referenced in different sections of the HomeSpan documentation. The sketches can be found in the Arduino IDE under *File → Examples → HomeSpan → Other Examples*
|
||||||
|
|
||||||
### [TableLamp](../Other%20Examples/TableLamp)
|
### [TableLamp](../examples/Other%20Examples/TableLamp)
|
||||||
A basic implementation of a Table Lamp Accessory. Used as the tutorial in [HomeSpan API Overview](Overview.md)
|
A basic implementation of a Table Lamp Accessory. Used as the tutorial in [HomeSpan API Overview](Overview.md)
|
||||||
|
|
||||||
### [RemoteControl](../Other%20Examples/RemoteControl)
|
### [RemoteControl](../examples/Other%20Examples/RemoteControl)
|
||||||
A standalone example that shows how to use HomeSpan's *RFControl* class to produce a custom pulse train. For illustrative purposes the pulse widths are very long and suitable for output to an LED so you can "see" the pulse train. See the [RF/IR Generation](RMT.md) page for full details
|
A standalone example that shows how to use HomeSpan's *RFControl* class to produce a custom pulse train. For illustrative purposes the pulse widths are very long and suitable for output to an LED so you can "see" the pulse train. See the [RF/IR Generation](RMT.md) page for full details
|
||||||
|
|
||||||
### [ServoControl](../Other%20Examples/ServoControl)
|
### [ServoControl](../examples/Other%20Examples/ServoControl)
|
||||||
An implementation of a Window Shade that uses HomeSpan's *ServoPin* class to control the horizontal tilt of the slats. See [ServoPin](PWM.md#servopinuint8_t-pin-double-initdegrees-uint16_t-minmicros-uint16_t-maxmicros-double-mindegrees-double-maxdegrees) for full details
|
An implementation of a Window Shade that uses HomeSpan's *ServoPin* class to control the horizontal tilt of the slats. See [ServoPin](PWM.md#servopinuint8_t-pin-double-initdegrees-uint16_t-minmicros-uint16_t-maxmicros-double-mindegrees-double-maxdegrees) for full details
|
||||||
|
|
||||||
### [Television](../Other%20Examples/Television)
|
### [Television](../examples/Other%20Examples/Television)
|
||||||
An example of HomeKit's *undocumented* Television Service showing how different Characteristics can be used to control a TV's power, input sources, and a few other functions. See the [Television Services and Characteristics](TVServices.md) page for full details
|
An example of HomeKit's *undocumented* Television Service showing how different Characteristics can be used to control a TV's power, input sources, and a few other functions. See the [Television Services and Characteristics](TVServices.md) page for full details
|
||||||
|
|
||||||
### [Pixel](../Other%20Examples/Pixel)
|
### [Pixel](../examples/Other%20Examples/Pixel)
|
||||||
Demonstrates how to use HomeSpan's *Pixel* and *Dot* classes to control one- and two-wire Addressable RGB and RGBW LEDs. See the [Addressable RGB LEDs](Pixels.md) page for full details
|
Demonstrates how to use HomeSpan's *Pixel* and *Dot* classes to control one- and two-wire Addressable RGB and RGBW LEDs. See the [Addressable RGB LEDs](Pixels.md) page for full details
|
||||||
|
|
||||||
### [CustomService](../Other%20Examples/CustomService)
|
### [CustomService](../examples/Other%20Examples/CustomService)
|
||||||
Demonstrates how to create Custom Services and Custom Characteristics in HomeSpan to implement an Atmospheric Pressure Sensor recognized by the *Eve for HomeKit* app. See [Custom Characteristics and Custom Services Macros](Reference.md#custom-characteristics-and-custom-services-macros) for full details
|
Demonstrates how to create Custom Services and Custom Characteristics in HomeSpan to implement an Atmospheric Pressure Sensor recognized by the *Eve for HomeKit* app. See [Custom Characteristics and Custom Services Macros](Reference.md#custom-characteristics-and-custom-services-macros) for full details
|
||||||
|
|
||||||
### [ProgrammableHub](../Other%20Examples/ProgrammableHub)
|
### [ProgrammableHub](../examples/Other%20Examples/ProgrammableHub)
|
||||||
Demonstrates how to implement a fully programmable Light Accessory Hub that allows the user to *dynamically* add/delete up to 12 Light Accessories directly through a device-hosted *web interface* or via HomeSpan's *command-line inteface*. Each light can be configured as dimmable/non-dimmable with either no color control, full RGB color control, or color-temperature control. Builds upon many of the techniques used in [Example 20](../examples/20-AdvancedTechniques)
|
Demonstrates how to implement a fully programmable Light Accessory Hub that allows the user to *dynamically* add/delete up to 12 Light Accessories directly through a device-hosted *web interface* or via HomeSpan's *command-line inteface*. Each light can be configured as dimmable/non-dimmable with either no color control, full RGB color control, or color-temperature control. Builds upon many of the techniques used in [Example 20](../examples/20-AdvancedTechniques)
|
||||||
|
|
||||||
### [RemoteSensors](../Other%20Examples/RemoteSensors)
|
### [RemoteSensors](../examples/Other%20Examples/RemoteSensors)
|
||||||
Demonstrates how SpanPoint can be used to transmit messages from battery-powered Remote Devices running light-weight sketches that measure the local temperature, to a wall-powered Main Device running a full HomeSpan sketch implementing Temperature Sensor Accessories. See [SpanPoint: Point-to-Point Communication between ESP32 Devices](NOW.md) for full details regarding the SpanPoint class and all of its methods.
|
Demonstrates how *SpanPoint* can be used to transmit messages from battery-powered Remote Devices running light-weight sketches that measure the local temperature, to a wall-powered Main Device running a full HomeSpan sketch implementing Temperature Sensor Accessories. See [SpanPoint: Point-to-Point Communication between ESP32 Devices](NOW.md) for full details regarding the *SpanPoint* class and all of its methods.
|
||||||
|
|
||||||
|
### [FadingLED](../examples/Other%20Examples/FadingLED)
|
||||||
|
Demonstrates how the *LedPin* class can use the ESP32's built-in fading control to automatically fade an LED from from one level of brightness to another over a specified period of time. See the [LedPin](PWM.md#pulse-width-modulation-pwm) page for full details
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,6 @@ Note that if you can’t find *Scancardium* listed as a font choice in either th
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[↩️](README.md) Back to the Welcome page
|
[↩️](../README.md) Back to the Welcome page
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ void setup() {
|
||||||
// output to drive the brightness of an LED. The ESP32 code base itself includes a set of functions to create PWM output
|
// output to drive the brightness of an LED. The ESP32 code base itself includes a set of functions to create PWM output
|
||||||
// and the ESP32 chip has built-in PWM functionality specifically for this purpose. There are numerous libraries
|
// and the ESP32 chip has built-in PWM functionality specifically for this purpose. There are numerous libraries
|
||||||
// you can download that mimics or reproduces analogWrite() in some form or another. HomeSpan conveniently comes with
|
// you can download that mimics or reproduces analogWrite() in some form or another. HomeSpan conveniently comes with
|
||||||
// it own version of a wrapper around the ESP32 PWM classes that make it very easy to define PWM "channel," attach a pin,
|
// its own version of a wrapper around the ESP32 PWM classes that make it very easy to define LED pins, and set the
|
||||||
// and set the PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the LedPin class, as defined in
|
// PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the LedPin class, as defined in
|
||||||
// extras/PwmPin.h. We will include this file in our updated DEV_LED.h for use with DEV_DimmableLED.
|
// extras/PwmPin.h. We will include this file in our updated DEV_LED.h for use with DEV_DimmableLED.
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
// If the only Service defined in the FIRST Accessory of a mult-Accessory device is the required Accessory Information Service,
|
// If the only Service defined in the FIRST Accessory of a multi-Accessory device is the required Accessory Information Service,
|
||||||
// the device is said to be configured as a "Bridge". Historically there may have been a number of functional differences between bridge
|
// the device is said to be configured as a "Bridge". Historically there may have been a number of functional differences between bridge
|
||||||
// devices and non-bridge devices, but since iOS 15, it's not obvious there are any differences in functionality, with two exceptions:
|
// devices and non-bridge devices, but since iOS 15, it's not obvious there are any differences in functionality, with two exceptions:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode)
|
||||||
sprintf(cBuf,"RGB=(%d,%d,%d)\n",R,G,B);
|
sprintf(cBuf,"RGB=(%d,%d,%d)\n",R,G,B);
|
||||||
LOG1(cBuf);
|
LOG1(cBuf);
|
||||||
|
|
||||||
redPin->set(R); // update the ledPin channels with new values
|
redPin->set(R); // update each ledPin with new values
|
||||||
greenPin->set(G);
|
greenPin->set(G);
|
||||||
bluePin->set(B);
|
bluePin->set(B);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||||
int powerPin; // NEW! pin with pushbutton to turn on/off LED
|
int powerPin; // NEW! pin with pushbutton to turn on/off LED
|
||||||
int raisePin; // NEW! pin with pushbutton to increase brightness
|
int raisePin; // NEW! pin with pushbutton to increase brightness
|
||||||
int lowerPin; // NEW! pin with pushButton to decrease brightness
|
int lowerPin; // NEW! pin with pushButton to decrease brightness
|
||||||
int channel; // PWM channel used for this LED (should be unique for each LED)
|
|
||||||
SpanCharacteristic *power; // reference to the On Characteristic
|
SpanCharacteristic *power; // reference to the On Characteristic
|
||||||
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||||
int favoriteLevel=50; // NEW! keep track of a 'favorite' level
|
int favoriteLevel=50; // NEW! keep track of a 'favorite' level
|
||||||
|
|
@ -53,8 +52,6 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED
|
||||||
|
|
||||||
Serial.print("Configuring Dimmable LED: Pin="); // initialization message
|
Serial.print("Configuring Dimmable LED: Pin="); // initialization message
|
||||||
Serial.print(ledPin->getPin());
|
Serial.print(ledPin->getPin());
|
||||||
Serial.print(" Channel=");
|
|
||||||
Serial.print(channel);
|
|
||||||
Serial.print("\n");
|
Serial.print("\n");
|
||||||
|
|
||||||
} // end constructor
|
} // end constructor
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-2023 Gregg E. Berman
|
||||||
|
*
|
||||||
|
* https://github.com/HomeSpan/HomeSpan
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
// HomeSpan Fading-LED Example. Demonstrates use of:
|
||||||
|
//
|
||||||
|
// LedPin::fade() and LedPin::fadeStatus() methods
|
||||||
|
//
|
||||||
|
// In this sketch we control a single dimmable LED using the Home App as well as a SpanButton.
|
||||||
|
// You can control the brightness of the LED from the Home App, but the SpanButton only turns the
|
||||||
|
// LED either fully on (if it's off) or fully off (if it's already on).
|
||||||
|
//
|
||||||
|
// Rather than set the LED to a specific brightness, this sketch uses the ESP32's hardware-based fading
|
||||||
|
// functionality to fade the LED from one level to the next. We set the timing for each fade to be 2000 ms,
|
||||||
|
// proportional to the difference between the current brightness and the desired brightness. This means it
|
||||||
|
// will take a full 2 seconds to fade the LED from 0-100, but only 1 second to fade from half-brightness to
|
||||||
|
// off.
|
||||||
|
|
||||||
|
#include "HomeSpan.h"
|
||||||
|
#include "extras/PwmPin.h" // library of various PWM functions
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
|
||||||
|
struct FadingLED : Service::LightBulb {
|
||||||
|
|
||||||
|
LedPin *ledPin; // reference to Led Pin
|
||||||
|
SpanCharacteristic *power; // reference to the On Characteristic
|
||||||
|
SpanCharacteristic *level; // reference to the Brightness Characteristic
|
||||||
|
|
||||||
|
FadingLED(int _ledPin, int _buttonPin) : Service::LightBulb(){
|
||||||
|
|
||||||
|
power=new Characteristic::On();
|
||||||
|
level=new Characteristic::Brightness(0);
|
||||||
|
ledPin=new LedPin(_ledPin);
|
||||||
|
new SpanButton(_buttonPin);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean update(){
|
||||||
|
|
||||||
|
ledPin->fade(power->getNewVal()*level->getNewVal(),2000,LedPin::PROPORTIONAL); // use fade() to set new level; timing=2 seconds, proportional scale
|
||||||
|
while(ledPin->fadeStatus()==LedPin::FADING); // wait until fading is completed
|
||||||
|
|
||||||
|
return(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void button(int pin, int pressType) override {
|
||||||
|
|
||||||
|
// Below we turn LED fully on or off depending on whether power is on
|
||||||
|
// Unlike above, we will NOT wait for the fading to complete, but will return immediately
|
||||||
|
|
||||||
|
if(ledPin->fade(100-(power->getVal())*100,2000,LedPin::PROPORTIONAL)!=0) // use fade to either turn fully on or fully off; check return status to see if call was successful
|
||||||
|
Serial.printf("Button Press Ignored\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() override {
|
||||||
|
|
||||||
|
// Below we set power and level once fading from a button press is completed
|
||||||
|
|
||||||
|
if(ledPin->fadeStatus()==LedPin::COMPLETED){
|
||||||
|
power->setVal(1-power->getVal());
|
||||||
|
level->setVal(power->getVal()?100:0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
homeSpan.begin(Category::Lighting,"Fading LED");
|
||||||
|
|
||||||
|
new SpanAccessory();
|
||||||
|
new Service::AccessoryInformation();
|
||||||
|
new Characteristic::Identify();
|
||||||
|
|
||||||
|
new FadingLED(26,4); // first argument is LED Pin, second argument is PushButton Pin
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
|
||||||
|
homeSpan.poll();
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
name=HomeSpan
|
name=HomeSpan
|
||||||
version=1.7.1
|
version=1.7.2
|
||||||
author=Gregg <homespan@icloud.com>
|
author=Gregg <homespan@icloud.com>
|
||||||
maintainer=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.
|
sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE.
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@
|
||||||
|
|
||||||
#elif defined(ARDUINO_ESP32C3_DEV)
|
#elif defined(ARDUINO_ESP32C3_DEV)
|
||||||
enum {
|
enum {
|
||||||
F27=2,F32=3,F14=10,F16=20,F17=21,F21=19, // Digital Only (6 pins)
|
F27=19,F32=2,F14=10,F16=20,F17=21,F21=18, // Digital Only (6 pins)
|
||||||
F26=0,F25=1,F4=18, // A0/A1/A5
|
F26=0,F25=1,F4=3, // A0/A1/A5
|
||||||
F22=9,F23=8, // I2C SCL/SDA
|
F22=9,F23=8, // I2C SCL/SDA
|
||||||
F5=4,F18=6,F19=5,F33=7, // SPI SCK/SDO/SDI/CS
|
F5=4,F18=6,F19=5,F33=7, // SPI SCK/SDO/SDI/CS
|
||||||
BUILTIN_PIXEL=8 // Built-in Neo-Pixel
|
BUILTIN_PIXEL=8 // Built-in Neo-Pixel
|
||||||
|
|
|
||||||
14
src/HAP.cpp
14
src/HAP.cpp
|
|
@ -40,14 +40,12 @@ void HAPClient::init(){
|
||||||
nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS
|
nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS
|
||||||
nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS
|
nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS
|
||||||
|
|
||||||
if(!nvs_get_str(homeSpan.otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS
|
if(strlen(homeSpan.spanOTA.otaPwd)==0){ // OTA password has not been specified in sketch
|
||||||
nvs_get_str(homeSpan.otaNVS,"OTADATA",homeSpan.spanOTA.otaPwd,&len); // retrieve data
|
if(!nvs_get_str(homeSpan.otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS...
|
||||||
} else {
|
nvs_get_str(homeSpan.otaNVS,"OTADATA",homeSpan.spanOTA.otaPwd,&len); // ...retrieve data.
|
||||||
MD5Builder otaPwdHash;
|
} else { // otherwise...
|
||||||
otaPwdHash.begin();
|
homeSpan.spanOTA.setPassword(DEFAULT_OTA_PASSWORD); // ...use default password
|
||||||
otaPwdHash.add(DEFAULT_OTA_PASSWORD);
|
}
|
||||||
otaPwdHash.calculate();
|
|
||||||
otaPwdHash.getChars(homeSpan.spanOTA.otaPwd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strlen(homeSpan.pairingCodeCommand)){ // load verification setup code if provided
|
if(strlen(homeSpan.pairingCodeCommand)){ // load verification setup code if provided
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ struct HAPClient {
|
||||||
|
|
||||||
// individual structures and data defined for each Hap Client connection
|
// individual structures and data defined for each Hap Client connection
|
||||||
|
|
||||||
WiFiClient client=0; // handle to client
|
WiFiClient client; // handle to client
|
||||||
Controller *cPair; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
Controller *cPair; // pointer to info on current, session-verified Paired Controller (NULL=un-verified, and therefore un-encrypted, connection)
|
||||||
|
|
||||||
// These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
// These keys are generated in the first call to pair-verify and used in the second call to pair-verify so must persist for a short period
|
||||||
|
|
|
||||||
168
src/HomeSpan.cpp
168
src/HomeSpan.cpp
|
|
@ -310,6 +310,8 @@ void Span::pollTask() {
|
||||||
|
|
||||||
statusLED->check();
|
statusLED->check();
|
||||||
|
|
||||||
|
vTaskDelay(5);
|
||||||
|
|
||||||
} // poll
|
} // poll
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
@ -519,25 +521,20 @@ void Span::checkConnect(){
|
||||||
mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash
|
mdns_service_txt_item_set("_hap","_tcp","sh",setupHash); // Step 4: broadcast the resulting Setup Hash
|
||||||
|
|
||||||
if(spanOTA.enabled){
|
if(spanOTA.enabled){
|
||||||
if(esp_ota_get_running_partition()!=esp_ota_get_next_update_partition(NULL)){
|
ArduinoOTA.setHostname(hostName);
|
||||||
ArduinoOTA.setHostname(hostName);
|
|
||||||
|
|
||||||
if(spanOTA.auth)
|
if(spanOTA.auth)
|
||||||
ArduinoOTA.setPasswordHash(spanOTA.otaPwd);
|
ArduinoOTA.setPasswordHash(spanOTA.otaPwd);
|
||||||
|
|
||||||
ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error);
|
ArduinoOTA.onStart(spanOTA.start).onEnd(spanOTA.end).onProgress(spanOTA.progress).onError(spanOTA.error);
|
||||||
|
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
Serial.print("Starting OTA Server: ");
|
Serial.print("Starting OTA Server: ");
|
||||||
Serial.print(displayName);
|
Serial.print(displayName);
|
||||||
Serial.print(" at ");
|
Serial.print(" at ");
|
||||||
Serial.print(WiFi.localIP());
|
Serial.print(WiFi.localIP());
|
||||||
Serial.print("\nAuthorization Password: ");
|
Serial.print("\nAuthorization Password: ");
|
||||||
Serial.print(spanOTA.auth?"Enabled\n\n":"DISABLED!\n\n");
|
Serial.print(spanOTA.auth?"Enabled\n\n":"DISABLED!\n\n");
|
||||||
} else {
|
|
||||||
Serial.print("\n*** WARNING: Can't start OTA Server - Partition table used to compile this sketch is not configured for OTA.\n\n");
|
|
||||||
spanOTA.enabled=false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mdns_service_txt_item_set("_hap","_tcp","ota",spanOTA.enabled?"yes":"no"); // OTA status (info only - NOT used by HAP)
|
mdns_service_txt_item_set("_hap","_tcp","ota",spanOTA.enabled?"yes":"no"); // OTA status (info only - NOT used by HAP)
|
||||||
|
|
@ -683,12 +680,7 @@ void Span::processSerialCommand(const char *c){
|
||||||
|
|
||||||
Serial.print(mask(textPwd,2));
|
Serial.print(mask(textPwd,2));
|
||||||
Serial.print("\n");
|
Serial.print("\n");
|
||||||
|
spanOTA.setPassword(textPwd);
|
||||||
MD5Builder otaPwdHash;
|
|
||||||
otaPwdHash.begin();
|
|
||||||
otaPwdHash.add(textPwd);
|
|
||||||
otaPwdHash.calculate();
|
|
||||||
otaPwdHash.getChars(spanOTA.otaPwd);
|
|
||||||
nvs_set_str(otaNVS,"OTADATA",spanOTA.otaPwd); // update data
|
nvs_set_str(otaNVS,"OTADATA",spanOTA.otaPwd); // update data
|
||||||
nvs_commit(otaNVS);
|
nvs_commit(otaNVS);
|
||||||
|
|
||||||
|
|
@ -1051,6 +1043,86 @@ void Span::processSerialCommand(const char *c){
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'P': {
|
||||||
|
|
||||||
|
Serial.printf("\n*** Pairing Data used for Cloning another Device\n\n");
|
||||||
|
size_t olen;
|
||||||
|
TempBuffer<char> tBuf(256);
|
||||||
|
mbedtls_base64_encode((uint8_t *)tBuf.buf,256,&olen,(uint8_t *)&HAPClient::accessory,sizeof(struct Accessory));
|
||||||
|
Serial.printf("Accessory data: %s\n",tBuf.buf);
|
||||||
|
for(int i=0;i<HAPClient::MAX_CONTROLLERS;i++){
|
||||||
|
if(HAPClient::controllers[i].allocated){
|
||||||
|
mbedtls_base64_encode((uint8_t *)tBuf.buf,256,&olen,(uint8_t *)(HAPClient::controllers+i),sizeof(struct Controller));
|
||||||
|
Serial.printf("Controller data: %s\n",tBuf.buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.printf("\n*** End Pairing Data\n\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'C': {
|
||||||
|
|
||||||
|
Serial.printf("\n*** Clone Pairing Data from another Device\n\n");
|
||||||
|
TempBuffer<char> tBuf(200);
|
||||||
|
size_t olen;
|
||||||
|
|
||||||
|
tBuf.buf[0]='\0';
|
||||||
|
Serial.print(">>> Accessory data: ");
|
||||||
|
readSerial(tBuf.buf,199);
|
||||||
|
if(strlen(tBuf.buf)==0){
|
||||||
|
Serial.printf("(cancelled)\n\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mbedtls_base64_decode((uint8_t *)&HAPClient::accessory,sizeof(struct Accessory),&olen,(uint8_t *)tBuf.buf,strlen(tBuf.buf));
|
||||||
|
if(olen!=sizeof(struct Accessory)){
|
||||||
|
Serial.printf("\n*** Error in size of Accessory data - cloning cancelled. Restarting...\n\n");
|
||||||
|
reboot();
|
||||||
|
} else {
|
||||||
|
HAPClient::charPrintRow(HAPClient::accessory.ID,17);
|
||||||
|
Serial.printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0;i<HAPClient::MAX_CONTROLLERS;i++){
|
||||||
|
tBuf.buf[0]='\0';
|
||||||
|
Serial.print(">>> Controller data: ");
|
||||||
|
readSerial(tBuf.buf,199);
|
||||||
|
if(strlen(tBuf.buf)==0){
|
||||||
|
Serial.printf("(done)\n");
|
||||||
|
while(i<HAPClient::MAX_CONTROLLERS) // clear data from remaining controller slots
|
||||||
|
HAPClient::controllers[i++].allocated=false;
|
||||||
|
} else {
|
||||||
|
mbedtls_base64_decode((uint8_t *)(HAPClient::controllers+i),sizeof(struct Controller),&olen,(uint8_t *)tBuf.buf,strlen(tBuf.buf));
|
||||||
|
if(olen!=sizeof(struct Controller)){
|
||||||
|
Serial.printf("\n*** Error in size of Controller data - cloning cancelled. Restarting...\n\n");
|
||||||
|
reboot();
|
||||||
|
} else {
|
||||||
|
HAPClient::charPrintRow(HAPClient::controllers[i].ID,36);
|
||||||
|
Serial.printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char qSave[2];
|
||||||
|
while(1){
|
||||||
|
qSave[0]='-';
|
||||||
|
Serial.printf("Save Cloned Pairing Data (y/n): ");
|
||||||
|
readSerial(qSave,1);
|
||||||
|
if(qSave[0]=='y'){
|
||||||
|
Serial.printf("(yes)\nData saved! Rebooting...");
|
||||||
|
nvs_set_blob(HAPClient::hapNVS,"ACCESSORY",&HAPClient::accessory,sizeof(HAPClient::accessory)); // update data
|
||||||
|
nvs_set_blob(HAPClient::hapNVS,"CONTROLLERS",HAPClient::controllers,sizeof(HAPClient::controllers));
|
||||||
|
nvs_commit(HAPClient::hapNVS); // commit to NVS
|
||||||
|
reboot();
|
||||||
|
} else
|
||||||
|
if(qSave[0]=='n'){
|
||||||
|
Serial.printf("(no)\nProcess Cancelled! Rebooting...");
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
Serial.printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case '?': {
|
case '?': {
|
||||||
|
|
||||||
Serial.print("\n*** HomeSpan Commands ***\n\n");
|
Serial.print("\n*** HomeSpan Commands ***\n\n");
|
||||||
|
|
@ -1070,6 +1142,9 @@ void Span::processSerialCommand(const char *c){
|
||||||
Serial.print(" U - unpair device by deleting all Controller data\n");
|
Serial.print(" U - unpair device by deleting all Controller data\n");
|
||||||
Serial.print(" H - delete HomeKit Device ID as well as all Controller data and restart\n");
|
Serial.print(" H - delete HomeKit Device ID as well as all Controller data and restart\n");
|
||||||
Serial.print("\n");
|
Serial.print("\n");
|
||||||
|
Serial.print(" P - output Pairing Data that can be saved offline to clone a new device\n");
|
||||||
|
Serial.print(" C - clone Pairing Data previously saved offline from another device\n");
|
||||||
|
Serial.print("\n");
|
||||||
Serial.print(" R - restart device\n");
|
Serial.print(" R - restart device\n");
|
||||||
Serial.print(" F - factory reset and restart\n");
|
Serial.print(" F - factory reset and restart\n");
|
||||||
Serial.print(" E - erase ALL stored data and restart\n");
|
Serial.print(" E - erase ALL stored data and restart\n");
|
||||||
|
|
@ -1988,16 +2063,27 @@ unsigned long SpanCharacteristic::timeVal(){
|
||||||
|
|
||||||
SpanCharacteristic *SpanCharacteristic::setValidValues(int n, ...){
|
SpanCharacteristic *SpanCharacteristic::setValidValues(int n, ...){
|
||||||
|
|
||||||
if(format!=UINT8){
|
|
||||||
setValidValuesError=true;
|
|
||||||
return(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
String s="[";
|
String s="[";
|
||||||
va_list vl;
|
va_list vl;
|
||||||
va_start(vl,n);
|
va_start(vl,n);
|
||||||
for(int i=0;i<n;i++){
|
for(int i=0;i<n;i++){
|
||||||
s+=(uint8_t)va_arg(vl,int);
|
switch(format){
|
||||||
|
case FORMAT::UINT8:
|
||||||
|
s+=(uint8_t)va_arg(vl,uint32_t);
|
||||||
|
break;
|
||||||
|
case FORMAT::UINT16:
|
||||||
|
s+=(uint16_t)va_arg(vl,uint32_t);
|
||||||
|
break;
|
||||||
|
case FORMAT::UINT32:
|
||||||
|
s+=(uint32_t)va_arg(vl,uint32_t);
|
||||||
|
break;
|
||||||
|
case FORMAT::INT:
|
||||||
|
s+=(int)va_arg(vl,uint32_t);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setValidValuesError=true;
|
||||||
|
return(this);
|
||||||
|
}
|
||||||
if(i!=n-1)
|
if(i!=n-1)
|
||||||
s+=",";
|
s+=",";
|
||||||
}
|
}
|
||||||
|
|
@ -2147,11 +2233,35 @@ void SpanWebLog::vLog(boolean sysMsg, const char *fmt, va_list ap){
|
||||||
// SpanOTA //
|
// SpanOTA //
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
||||||
void SpanOTA::init(boolean _auth, boolean _safeLoad){
|
int SpanOTA::init(boolean _auth, boolean _safeLoad, const char *pwd){
|
||||||
|
if(esp_ota_get_running_partition()==esp_ota_get_next_update_partition(NULL)){
|
||||||
|
Serial.print("\n*** WARNING: Can't start OTA Server - Partition table used to compile this sketch is not configured for OTA.\n\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
enabled=true;
|
enabled=true;
|
||||||
safeLoad=_safeLoad;
|
safeLoad=_safeLoad;
|
||||||
auth=_auth;
|
auth=_auth;
|
||||||
homeSpan.reserveSocketConnections(1);
|
homeSpan.reserveSocketConnections(1);
|
||||||
|
if(pwd==NULL)
|
||||||
|
return(0);
|
||||||
|
return(setPassword(pwd));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
int SpanOTA::setPassword(const char *pwd){
|
||||||
|
if(strlen(pwd)<1 || strlen(pwd)>32){
|
||||||
|
Serial.printf("\n*** WARNING: Cannot change OTA password to '%s'. Password length must be between 1 and 32 characters.\n\n",pwd);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
MD5Builder otaPwdHash;
|
||||||
|
otaPwdHash.begin();
|
||||||
|
otaPwdHash.add(pwd);
|
||||||
|
otaPwdHash.calculate();
|
||||||
|
otaPwdHash.getChars(homeSpan.spanOTA.otaPwd);
|
||||||
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
|
|
|
||||||
|
|
@ -168,14 +168,15 @@ struct SpanWebLog{ // optional web status/log data
|
||||||
|
|
||||||
struct SpanOTA{ // manages OTA process
|
struct SpanOTA{ // manages OTA process
|
||||||
|
|
||||||
char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
char otaPwd[33]=""; // MD5 Hash of OTA password, represented as a string of hexidecimal characters
|
||||||
|
|
||||||
static boolean enabled; // enables OTA - default if not enabled
|
static boolean enabled; // enables OTA - default if not enabled
|
||||||
static boolean auth; // indicates whether OTA password is required
|
static boolean auth; // indicates whether OTA password is required
|
||||||
static int otaPercent;
|
static int otaPercent;
|
||||||
static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch
|
static boolean safeLoad; // indicates whether OTA update should reject any application update that is not another HomeSpan sketch
|
||||||
|
|
||||||
void init(boolean auth, boolean safeLoad);
|
int init(boolean auth, boolean safeLoad, const char *pwd);
|
||||||
|
int setPassword(const char *pwd);
|
||||||
static void start();
|
static void start();
|
||||||
static void end();
|
static void end();
|
||||||
static void progress(uint32_t progress, uint32_t total);
|
static void progress(uint32_t progress, uint32_t total);
|
||||||
|
|
@ -326,7 +327,8 @@ class Span{
|
||||||
void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead
|
||||||
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
void deleteStoredValues(){processSerialCommand("V");} // deletes stored Characteristic values from NVS
|
||||||
|
|
||||||
void enableOTA(boolean auth=true, boolean safeLoad=true){spanOTA.init(auth, safeLoad);} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
int enableOTA(boolean auth=true, boolean safeLoad=true){return(spanOTA.init(auth, safeLoad, NULL));} // enables Over-the-Air updates, with (auth=true) or without (auth=false) authorization password
|
||||||
|
int enableOTA(const char *pwd, boolean safeLoad=true){return(spanOTA.init(true, safeLoad, pwd));} // enables Over-the-Air updates, with custom authorization password (overrides any password stored with the 'O' command)
|
||||||
|
|
||||||
void enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
void enableWebLog(uint16_t maxEntries=0, const char *serv=NULL, const char *tz="UTC", const char *url=DEFAULT_WEBLOG_URL){ // enable Web Logging
|
||||||
webLog.init(maxEntries, serv, tz, url);
|
webLog.init(maxEntries, serv, tz, url);
|
||||||
|
|
@ -339,7 +341,10 @@ class Span{
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void autoPoll(uint32_t stackSize=CONFIG_ARDUINO_LOOP_STACK_SIZE){xTaskCreateUniversal([](void *parms){for(;;)homeSpan.pollTask();}, "pollTask", stackSize, NULL, 1, &pollTaskHandle, 0);} // start pollTask()
|
void autoPoll(uint32_t stackSize=8192, uint32_t priority=1, uint32_t cpu=0){ // start pollTask()
|
||||||
|
xTaskCreateUniversal([](void *parms){for(;;)homeSpan.pollTask();}, "pollTask", stackSize, NULL, priority, &pollTaskHandle, cpu);
|
||||||
|
Serial.printf("\n*** AutoPolling Task started with priority=%d\n\n",uxTaskPriorityGet(pollTaskHandle));
|
||||||
|
}
|
||||||
|
|
||||||
void setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;} // sets wait time (in seconds) for optional web log time server to connect
|
void setTimeServerTimeout(uint32_t tSec){webLog.waitTime=tSec*1000;} // sets wait time (in seconds) for optional web log time server to connect
|
||||||
|
|
||||||
|
|
@ -741,7 +746,7 @@ class SpanCharacteristic{
|
||||||
boolean updated(){return(isUpdated);} // returns isUpdated
|
boolean updated(){return(isUpdated);} // returns isUpdated
|
||||||
unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated
|
unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated
|
||||||
|
|
||||||
SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic and returns pointer to self. Only applicable if format=uint8
|
SpanCharacteristic *setValidValues(int n, ...); // sets a list of 'n' valid values allowed for a Characteristic and returns pointer to self. Only applicable if format=INT, UINT8, UINT16, or UINT32
|
||||||
|
|
||||||
template <typename A, typename B, typename S=int> SpanCharacteristic *setRange(A min, B max, S step=0){
|
template <typename A, typename B, typename S=int> SpanCharacteristic *setRange(A min, B max, S step=0){
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
|
|
||||||
#define HS_MAJOR 1
|
#define HS_MAJOR 1
|
||||||
#define HS_MINOR 7
|
#define HS_MINOR 7
|
||||||
#define HS_PATCH 1
|
#define HS_PATCH 2
|
||||||
|
|
||||||
#define STRINGIFY(x) _STR(x)
|
#define STRINGIFY(x) _STR(x)
|
||||||
#define _STR(x) #x
|
#define _STR(x) #x
|
||||||
|
|
|
||||||
189
src/Span.h
189
src/Span.h
|
|
@ -29,14 +29,18 @@
|
||||||
// SPAN SERVICES (HAP Chapter 8) //
|
// SPAN SERVICES (HAP Chapter 8) //
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
|
|
||||||
// Macros to define vectors of required and optional characteristics for each Span Service structure
|
// Macros to define services, along with vectors of required and optional characteristics for each Span Service structure
|
||||||
|
// The names of the macros are picked up by external scripts to help generate documentation
|
||||||
|
|
||||||
|
#define CREATE_SERV(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{
|
||||||
|
#define END_SERV }};
|
||||||
|
|
||||||
#define REQ(HAPCHAR) req.insert(&hapChars.HAPCHAR)
|
#define REQ(HAPCHAR) req.insert(&hapChars.HAPCHAR)
|
||||||
#define OPT(HAPCHAR) opt.insert(&hapChars.HAPCHAR)
|
#define OPT(HAPCHAR) opt.insert(&hapChars.HAPCHAR)
|
||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
struct AccessoryInformation : SpanService { AccessoryInformation() : SpanService{"3E","AccessoryInformation"}{
|
CREATE_SERV(AccessoryInformation,3E)
|
||||||
REQ(Identify);
|
REQ(Identify);
|
||||||
OPT(FirmwareRevision);
|
OPT(FirmwareRevision);
|
||||||
OPT(Manufacturer);
|
OPT(Manufacturer);
|
||||||
|
|
@ -45,9 +49,9 @@ namespace Service {
|
||||||
OPT(SerialNumber);
|
OPT(SerialNumber);
|
||||||
OPT(HardwareRevision);
|
OPT(HardwareRevision);
|
||||||
OPT(AccessoryFlags);
|
OPT(AccessoryFlags);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct AirPurifier : SpanService { AirPurifier() : SpanService{"BB","AirPurifier"}{
|
CREATE_SERV(AirPurifier,BB)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(CurrentAirPurifierState);
|
REQ(CurrentAirPurifierState);
|
||||||
REQ(TargetAirPurifierState);
|
REQ(TargetAirPurifierState);
|
||||||
|
|
@ -55,9 +59,9 @@ namespace Service {
|
||||||
OPT(RotationSpeed);
|
OPT(RotationSpeed);
|
||||||
OPT(SwingMode);
|
OPT(SwingMode);
|
||||||
OPT(LockPhysicalControls);
|
OPT(LockPhysicalControls);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct AirQualitySensor : SpanService { AirQualitySensor() : SpanService{"8D","AirQualitySensor"}{
|
CREATE_SERV(AirQualitySensor,8D)
|
||||||
REQ(AirQuality);
|
REQ(AirQuality);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(OzoneDensity);
|
OPT(OzoneDensity);
|
||||||
|
|
@ -70,16 +74,16 @@ namespace Service {
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct BatteryService : SpanService { BatteryService() : SpanService{"96","BatteryService"}{
|
CREATE_SERV(BatteryService,96)
|
||||||
REQ(BatteryLevel);
|
REQ(BatteryLevel);
|
||||||
REQ(ChargingState);
|
REQ(ChargingState);
|
||||||
REQ(StatusLowBattery);
|
REQ(StatusLowBattery);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct CarbonDioxideSensor : SpanService { CarbonDioxideSensor() : SpanService{"97","CarbonDioxideSensor"}{
|
CREATE_SERV(CarbonDioxideSensor,97)
|
||||||
REQ(CarbonDioxideDetected);
|
REQ(CarbonDioxideDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
|
|
@ -88,9 +92,9 @@ namespace Service {
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
OPT(CarbonDioxideLevel);
|
OPT(CarbonDioxideLevel);
|
||||||
OPT(CarbonDioxidePeakLevel);
|
OPT(CarbonDioxidePeakLevel);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct CarbonMonoxideSensor : SpanService { CarbonMonoxideSensor() : SpanService{"7F","CarbonMonoxideSensor"}{
|
CREATE_SERV(CarbonMonoxideSensor,7F)
|
||||||
REQ(CarbonMonoxideDetected);
|
REQ(CarbonMonoxideDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
|
|
@ -99,34 +103,34 @@ namespace Service {
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
OPT(CarbonMonoxideLevel);
|
OPT(CarbonMonoxideLevel);
|
||||||
OPT(CarbonMonoxidePeakLevel);
|
OPT(CarbonMonoxidePeakLevel);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct ContactSensor : SpanService { ContactSensor() : SpanService{"80","ContactSensor"}{
|
CREATE_SERV(ContactSensor,80)
|
||||||
REQ(ContactSensorState);
|
REQ(ContactSensorState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Door : SpanService { Door() : SpanService{"81","Door"}{
|
CREATE_SERV(Door,81)
|
||||||
REQ(CurrentPosition);
|
REQ(CurrentPosition);
|
||||||
REQ(TargetPosition);
|
REQ(TargetPosition);
|
||||||
REQ(PositionState);
|
REQ(PositionState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(HoldPosition);
|
OPT(HoldPosition);
|
||||||
OPT(ObstructionDetected);
|
OPT(ObstructionDetected);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Doorbell : SpanService { Doorbell() : SpanService{"121","Doorbell"}{
|
CREATE_SERV(Doorbell,121)
|
||||||
REQ(ProgrammableSwitchEvent);
|
REQ(ProgrammableSwitchEvent);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(Volume);
|
OPT(Volume);
|
||||||
OPT(Brightness);
|
OPT(Brightness);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Fan : SpanService { Fan() : SpanService{"B7","Fan"}{
|
CREATE_SERV(Fan,B7)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(CurrentFanState);
|
OPT(CurrentFanState);
|
||||||
|
|
@ -135,35 +139,35 @@ namespace Service {
|
||||||
OPT(RotationSpeed);
|
OPT(RotationSpeed);
|
||||||
OPT(SwingMode);
|
OPT(SwingMode);
|
||||||
OPT(LockPhysicalControls);
|
OPT(LockPhysicalControls);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Faucet : SpanService { Faucet() : SpanService{"D7","Faucet"}{
|
CREATE_SERV(Faucet,D7)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct FilterMaintenance : SpanService { FilterMaintenance() : SpanService{"BA","FilterMaintenance"}{
|
CREATE_SERV(FilterMaintenance,BA)
|
||||||
REQ(FilterChangeIndication);
|
REQ(FilterChangeIndication);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(FilterLifeLevel);
|
OPT(FilterLifeLevel);
|
||||||
OPT(ResetFilterIndication);
|
OPT(ResetFilterIndication);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct GarageDoorOpener : SpanService { GarageDoorOpener() : SpanService{"41","GarageDoorOpener"}{
|
CREATE_SERV(GarageDoorOpener,41)
|
||||||
REQ(CurrentDoorState);
|
REQ(CurrentDoorState);
|
||||||
REQ(TargetDoorState);
|
REQ(TargetDoorState);
|
||||||
REQ(ObstructionDetected);
|
REQ(ObstructionDetected);
|
||||||
OPT(LockCurrentState);
|
OPT(LockCurrentState);
|
||||||
OPT(LockTargetState);
|
OPT(LockTargetState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct HAPProtocolInformation : SpanService { HAPProtocolInformation() : SpanService{"A2","HAPProtocolInformation"}{
|
CREATE_SERV(HAPProtocolInformation,A2)
|
||||||
REQ(Version);
|
REQ(Version);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct HeaterCooler : SpanService { HeaterCooler() : SpanService{"BC","HeaterCooler"}{
|
CREATE_SERV(HeaterCooler,BC)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(CurrentTemperature);
|
REQ(CurrentTemperature);
|
||||||
REQ(CurrentHeaterCoolerState);
|
REQ(CurrentHeaterCoolerState);
|
||||||
|
|
@ -175,9 +179,9 @@ namespace Service {
|
||||||
OPT(CoolingThresholdTemperature);
|
OPT(CoolingThresholdTemperature);
|
||||||
OPT(HeatingThresholdTemperature);
|
OPT(HeatingThresholdTemperature);
|
||||||
OPT(LockPhysicalControls);
|
OPT(LockPhysicalControls);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct HumidifierDehumidifier : SpanService { HumidifierDehumidifier() : SpanService{"BD","HumidifierDehumidifier"}{
|
CREATE_SERV(HumidifierDehumidifier,BD)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(CurrentRelativeHumidity);
|
REQ(CurrentRelativeHumidity);
|
||||||
REQ(CurrentHumidifierDehumidifierState);
|
REQ(CurrentHumidifierDehumidifierState);
|
||||||
|
|
@ -189,167 +193,167 @@ namespace Service {
|
||||||
OPT(SwingMode);
|
OPT(SwingMode);
|
||||||
OPT(WaterLevel);
|
OPT(WaterLevel);
|
||||||
OPT(LockPhysicalControls);
|
OPT(LockPhysicalControls);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct HumiditySensor : SpanService { HumiditySensor() : SpanService{"82","HumiditySensor"}{
|
CREATE_SERV(HumiditySensor,82)
|
||||||
REQ(CurrentRelativeHumidity);
|
REQ(CurrentRelativeHumidity);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct InputSource : SpanService { InputSource() : SpanService{"D9","InputSource"}{
|
CREATE_SERV(InputSource,D9)
|
||||||
OPT(ConfiguredName);
|
OPT(ConfiguredName);
|
||||||
OPT(IsConfigured);
|
OPT(IsConfigured);
|
||||||
REQ(Identifier);
|
REQ(Identifier);
|
||||||
OPT(CurrentVisibilityState);
|
OPT(CurrentVisibilityState);
|
||||||
OPT(TargetVisibilityState);
|
OPT(TargetVisibilityState);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct IrrigationSystem : SpanService { IrrigationSystem() : SpanService{"CF","IrrigationSystem"}{
|
CREATE_SERV(IrrigationSystem,CF)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(ProgramMode);
|
REQ(ProgramMode);
|
||||||
REQ(InUse);
|
REQ(InUse);
|
||||||
OPT(RemainingDuration);
|
OPT(RemainingDuration);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct LeakSensor : SpanService { LeakSensor() : SpanService{"83","LeakSensor"}{
|
CREATE_SERV(LeakSensor,83)
|
||||||
REQ(LeakDetected);
|
REQ(LeakDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct LightBulb : SpanService { LightBulb() : SpanService{"43","LightBulb"}{
|
CREATE_SERV(LightBulb,43)
|
||||||
REQ(On);
|
REQ(On);
|
||||||
OPT(Brightness);
|
OPT(Brightness);
|
||||||
OPT(Hue);
|
OPT(Hue);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(Saturation);
|
OPT(Saturation);
|
||||||
OPT(ColorTemperature);
|
OPT(ColorTemperature);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct LightSensor : SpanService { LightSensor() : SpanService{"84","LightSensor"}{
|
CREATE_SERV(LightSensor,84)
|
||||||
REQ(CurrentAmbientLightLevel);
|
REQ(CurrentAmbientLightLevel);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct LockMechanism : SpanService { LockMechanism() : SpanService{"45","LockMechanism"}{
|
CREATE_SERV(LockMechanism,45)
|
||||||
REQ(LockCurrentState);
|
REQ(LockCurrentState);
|
||||||
REQ(LockTargetState);
|
REQ(LockTargetState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Microphone : SpanService { Microphone() : SpanService{"112","Microphone"}{
|
CREATE_SERV(Microphone,112)
|
||||||
REQ(Mute);
|
REQ(Mute);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(Volume);
|
OPT(Volume);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct MotionSensor : SpanService { MotionSensor() : SpanService{"85","MotionSensor"}{
|
CREATE_SERV(MotionSensor,85)
|
||||||
REQ(MotionDetected);
|
REQ(MotionDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct OccupancySensor : SpanService { OccupancySensor() : SpanService{"86","OccupancySensor"}{
|
CREATE_SERV(OccupancySensor,86)
|
||||||
REQ(OccupancyDetected);
|
REQ(OccupancyDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Outlet : SpanService { Outlet() : SpanService{"47","Outlet"}{
|
CREATE_SERV(Outlet,47)
|
||||||
REQ(On);
|
REQ(On);
|
||||||
REQ(OutletInUse);
|
REQ(OutletInUse);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct SecuritySystem : SpanService { SecuritySystem() : SpanService{"7E","SecuritySystem"}{
|
CREATE_SERV(SecuritySystem,7E)
|
||||||
REQ(SecuritySystemCurrentState);
|
REQ(SecuritySystemCurrentState);
|
||||||
REQ(SecuritySystemTargetState);
|
REQ(SecuritySystemTargetState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(SecuritySystemAlarmType);
|
OPT(SecuritySystemAlarmType);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct ServiceLabel : SpanService { ServiceLabel() : SpanService{"CC","ServiceLabel"}{
|
CREATE_SERV(ServiceLabel,CC)
|
||||||
REQ(ServiceLabelNamespace);
|
REQ(ServiceLabelNamespace);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Slat : SpanService { Slat() : SpanService{"B9","Slat"}{
|
CREATE_SERV(Slat,B9)
|
||||||
REQ(CurrentSlatState);
|
REQ(CurrentSlatState);
|
||||||
REQ(SlatType);
|
REQ(SlatType);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(SwingMode);
|
OPT(SwingMode);
|
||||||
OPT(CurrentTiltAngle);
|
OPT(CurrentTiltAngle);
|
||||||
OPT(TargetTiltAngle);
|
OPT(TargetTiltAngle);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct SmokeSensor : SpanService { SmokeSensor() : SpanService{"87","SmokeSensor"}{
|
CREATE_SERV(SmokeSensor,87)
|
||||||
REQ(SmokeDetected);
|
REQ(SmokeDetected);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Speaker : SpanService { Speaker() : SpanService{"113","Speaker"}{
|
CREATE_SERV(Speaker,113)
|
||||||
REQ(Mute);
|
REQ(Mute);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(Volume);
|
OPT(Volume);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct StatelessProgrammableSwitch : SpanService { StatelessProgrammableSwitch() : SpanService{"89","StatelessProgrammableSwitch"}{
|
CREATE_SERV(StatelessProgrammableSwitch,89)
|
||||||
REQ(ProgrammableSwitchEvent);
|
REQ(ProgrammableSwitchEvent);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(ServiceLabelIndex);
|
OPT(ServiceLabelIndex);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Switch : SpanService { Switch() : SpanService{"49","Switch"}{
|
CREATE_SERV(Switch,49)
|
||||||
REQ(On);
|
REQ(On);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Television : SpanService { Television() : SpanService{"D8","Television"}{
|
CREATE_SERV(Television,D8)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
OPT(ConfiguredName);
|
OPT(ConfiguredName);
|
||||||
OPT(ActiveIdentifier);
|
OPT(ActiveIdentifier);
|
||||||
OPT(RemoteKey);
|
OPT(RemoteKey);
|
||||||
OPT(PowerModeSelection);
|
OPT(PowerModeSelection);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct TelevisionSpeaker : SpanService { TelevisionSpeaker() : SpanService{"113","TelevisionSpeaker"}{
|
CREATE_SERV(TelevisionSpeaker,113)
|
||||||
REQ(VolumeControlType);
|
REQ(VolumeControlType);
|
||||||
REQ(VolumeSelector);
|
REQ(VolumeSelector);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct TemperatureSensor : SpanService { TemperatureSensor() : SpanService{"8A","TemperatureSensor"}{
|
CREATE_SERV(TemperatureSensor,8A)
|
||||||
REQ(CurrentTemperature);
|
REQ(CurrentTemperature);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(StatusActive);
|
OPT(StatusActive);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(StatusTampered);
|
OPT(StatusTampered);
|
||||||
OPT(StatusLowBattery);
|
OPT(StatusLowBattery);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Thermostat : SpanService { Thermostat() : SpanService{"4A","Thermostat"}{
|
CREATE_SERV(Thermostat,4A)
|
||||||
REQ(CurrentHeatingCoolingState);
|
REQ(CurrentHeatingCoolingState);
|
||||||
REQ(TargetHeatingCoolingState);
|
REQ(TargetHeatingCoolingState);
|
||||||
REQ(CurrentTemperature);
|
REQ(CurrentTemperature);
|
||||||
|
|
@ -360,9 +364,9 @@ namespace Service {
|
||||||
OPT(HeatingThresholdTemperature);
|
OPT(HeatingThresholdTemperature);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(TargetRelativeHumidity);
|
OPT(TargetRelativeHumidity);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Valve : SpanService { Valve() : SpanService{"D0","Valve"}{
|
CREATE_SERV(Valve,D0)
|
||||||
REQ(Active);
|
REQ(Active);
|
||||||
REQ(InUse);
|
REQ(InUse);
|
||||||
REQ(ValveType);
|
REQ(ValveType);
|
||||||
|
|
@ -372,18 +376,18 @@ namespace Service {
|
||||||
OPT(ServiceLabelIndex);
|
OPT(ServiceLabelIndex);
|
||||||
OPT(StatusFault);
|
OPT(StatusFault);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct Window : SpanService { Window() : SpanService{"8B","Window"}{
|
CREATE_SERV(Window,8B)
|
||||||
REQ(CurrentPosition);
|
REQ(CurrentPosition);
|
||||||
REQ(TargetPosition);
|
REQ(TargetPosition);
|
||||||
REQ(PositionState);
|
REQ(PositionState);
|
||||||
OPT(Name);
|
OPT(Name);
|
||||||
OPT(HoldPosition);
|
OPT(HoldPosition);
|
||||||
OPT(ObstructionDetected);
|
OPT(ObstructionDetected);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
struct WindowCovering : SpanService { WindowCovering() : SpanService{"8C","WindowCovering"}{
|
CREATE_SERV(WindowCovering,8C)
|
||||||
REQ(TargetPosition);
|
REQ(TargetPosition);
|
||||||
REQ(CurrentPosition);
|
REQ(CurrentPosition);
|
||||||
REQ(PositionState);
|
REQ(PositionState);
|
||||||
|
|
@ -394,7 +398,7 @@ namespace Service {
|
||||||
OPT(CurrentVerticalTiltAngle);
|
OPT(CurrentVerticalTiltAngle);
|
||||||
OPT(TargetVerticalTiltAngle);
|
OPT(TargetVerticalTiltAngle);
|
||||||
OPT(ObstructionDetected);
|
OPT(ObstructionDetected);
|
||||||
}};
|
END_SERV
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,6 +536,8 @@ namespace Characteristic {
|
||||||
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS //
|
// MACROS TO ADD CUSTOM SERVICES AND CHARACTERISTICS //
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef CUSTOM_CHAR_HEADER
|
||||||
|
|
||||||
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \
|
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),FORMAT,STATIC_RANGE}; \
|
||||||
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }
|
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }
|
||||||
|
|
@ -544,9 +550,26 @@ namespace Characteristic {
|
||||||
HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),DATA,true}; \
|
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); } }; }
|
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val="AA==", boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CUSTOM_CHAR(NAME,UUID,PERMISISONS,FORMAT,DEFVAL,MINVAL,MAXVAL,STATIC_RANGE) \
|
||||||
|
extern HapChar _CUSTOM_##NAME; \
|
||||||
|
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(FORMAT##_t val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; }
|
||||||
|
|
||||||
|
#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \
|
||||||
|
extern HapChar _CUSTOM_##NAME; \
|
||||||
|
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) \
|
||||||
|
extern HapChar _CUSTOM_##NAME; \
|
||||||
|
namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val="AA==", boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME,true} { init(val,nvsStore); } }; }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CUSTOM_SERV(NAME,UUID) \
|
#define CUSTOM_SERV(NAME,UUID) \
|
||||||
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
namespace Service { struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME,true}{} }; }
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
// MACROS TO ADD A NEW ACCESSORT WITH OPTIONAL NAME //
|
// MACROS TO ADD A NEW ACCESSORT WITH OPTIONAL NAME //
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
|
|
|
||||||
63
src/src.ino
63
src/src.ino
|
|
@ -30,43 +30,11 @@
|
||||||
|
|
||||||
#include "HomeSpan.h"
|
#include "HomeSpan.h"
|
||||||
|
|
||||||
struct RemoteTempSensor : Service::TemperatureSensor {
|
CUSTOM_CHAR(CharFloat, 00000001-0001-0001-0001-46637266EA00, PR+PW+EV, FLOAT, 0, 0, 100, false);
|
||||||
|
CUSTOM_CHAR(CharUInt8, 00000009-0001-0001-0001-46637266EA00, PR+PW+EV, UINT8, 0, 0, 100, false);
|
||||||
SpanCharacteristic *temp;
|
CUSTOM_CHAR(CharUInt16, 00000016-0001-0001-0001-46637266EA00, PR+PW+EV, UINT16, 0, 0, 100, false);
|
||||||
SpanCharacteristic *fault;
|
CUSTOM_CHAR(CharUInt32, 00000032-0001-0001-0001-46637266EA00, PR+PW+EV, UINT32, 0, 0, 100, false);
|
||||||
SpanPoint *remoteTemp;
|
CUSTOM_CHAR(CharInt, 00000002-0001-0001-0001-46637266EA00, PR+PW+EV, INT, 0, 0, 100, false);
|
||||||
const char *name;
|
|
||||||
float temperature;
|
|
||||||
|
|
||||||
RemoteTempSensor(const char *name, const char*macAddress, boolean is8266=false) : Service::TemperatureSensor(){
|
|
||||||
|
|
||||||
this->name=name;
|
|
||||||
|
|
||||||
temp=new Characteristic::CurrentTemperature(-10.0); // set initial temperature
|
|
||||||
temp->setRange(-50,100); // expand temperature range to allow negative values
|
|
||||||
|
|
||||||
fault=new Characteristic::StatusFault(1); // set initial state = fault
|
|
||||||
|
|
||||||
remoteTemp=new SpanPoint(macAddress,0,sizeof(float),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
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
|
@ -76,24 +44,19 @@ void setup() {
|
||||||
|
|
||||||
homeSpan.setLogLevel(1);
|
homeSpan.setLogLevel(1);
|
||||||
|
|
||||||
homeSpan.begin(Category::Bridges,"Sensor Hub");
|
homeSpan.begin(Category::Other,"HomeSpan Test");
|
||||||
|
|
||||||
new SpanAccessory();
|
new SpanAccessory();
|
||||||
new Service::AccessoryInformation();
|
new Service::AccessoryInformation();
|
||||||
new Characteristic::Identify();
|
new Characteristic::Identify();
|
||||||
|
new Service::LightBulb();
|
||||||
|
new Characteristic::On();
|
||||||
|
|
||||||
new SpanAccessory();
|
(new Characteristic::CharFloat())->setValidValues(5,0,1,2,6,7,8);
|
||||||
new Service::AccessoryInformation();
|
(new Characteristic::CharUInt8())->setValidValues(5,0,1,2,6,7,8);
|
||||||
new Characteristic::Identify();
|
(new Characteristic::CharUInt16())->setValidValues(5,0,1<<8,1<<16,0xFFFFFFFF,-1);
|
||||||
new Characteristic::Name("Indoor Temp");
|
(new Characteristic::CharUInt32())->setValidValues(5,0,1<<8,1<<16,0xFFFFFFFF,-1);
|
||||||
new RemoteTempSensor("Device 1","AC:67:B2:77:42:20"); // pass MAC Address of Remote Device
|
(new Characteristic::CharInt())->setValidValues(5,0,255,2000000000,-2000000000,-1)->setValidValues(1,2);
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
} // end of setup()
|
} // end of setup()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(p
|
||||||
if(!channel)
|
if(!channel)
|
||||||
Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels and/or Timers ***\n\n",pin);
|
Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels and/or Timers ***\n\n",pin);
|
||||||
else
|
else
|
||||||
Serial.printf("LedPin=%d: mode=%d channel=%d, timer=%d, freq=%d Hz, resolution=%d bits %s\n",
|
Serial.printf("LedPin=%d: mode=%d, channel=%d, timer=%d, freq=%d Hz, resolution=%d bits %s\n",
|
||||||
channel->gpio_num,
|
channel->gpio_num,
|
||||||
channel->speed_mode,
|
channel->speed_mode,
|
||||||
channel->channel,
|
channel->channel,
|
||||||
|
|
@ -95,8 +95,11 @@ LedPin::LedPin(uint8_t pin, float level, uint16_t freq, boolean invert) : LedC(p
|
||||||
channel->flags.output_invert?"(inverted)":""
|
channel->flags.output_invert?"(inverted)":""
|
||||||
);
|
);
|
||||||
|
|
||||||
set(level);
|
ledc_fade_func_install(0);
|
||||||
|
ledc_cbs_t fadeCallbackList = {.fade_cb = fadeCallback}; // for some reason, ledc_cb_register requires the function to be wrapped in a structure
|
||||||
|
ledc_cb_register(channel->speed_mode,channel->channel,&fadeCallbackList,this);
|
||||||
|
|
||||||
|
set(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
|
|
@ -110,13 +113,55 @@ void LedPin::set(float level){
|
||||||
level=100;
|
level=100;
|
||||||
|
|
||||||
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||||
|
|
||||||
channel->duty=d;
|
channel->duty=d;
|
||||||
ledc_channel_config(channel);
|
ledc_channel_config(channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
|
|
||||||
|
int LedPin::fade(float level, uint32_t fadeTime, int fadeType){
|
||||||
|
|
||||||
|
if(!channel)
|
||||||
|
return(1);
|
||||||
|
|
||||||
|
if(fadeState==FADING) // fading already in progress
|
||||||
|
return(1); // return error
|
||||||
|
|
||||||
|
if(level>100)
|
||||||
|
level=100;
|
||||||
|
|
||||||
|
float d=level*(pow(2,(int)timer->duty_resolution)-1)/100.0;
|
||||||
|
|
||||||
|
if(fadeType==PROPORTIONAL)
|
||||||
|
fadeTime*=fabs((float)ledc_get_duty(channel->speed_mode,channel->channel)-d)/(float)(pow(2,(int)timer->duty_resolution)-1);
|
||||||
|
|
||||||
|
fadeState=FADING;
|
||||||
|
ledc_set_fade_time_and_start(channel->speed_mode,channel->channel,d,fadeTime,LEDC_FADE_NO_WAIT);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
int LedPin::fadeStatus(){
|
||||||
|
if(fadeState==COMPLETED){
|
||||||
|
fadeState=NOT_FADING;
|
||||||
|
return(COMPLETED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(fadeState);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
bool IRAM_ATTR LedPin::fadeCallback(const ledc_cb_param_t *param, void *arg){
|
||||||
|
((LedPin *)arg)->fadeState=COMPLETED;
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////
|
||||||
|
|
||||||
|
|
||||||
void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
|
void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){
|
||||||
|
|
||||||
// The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html
|
// The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,27 @@ class LedC {
|
||||||
|
|
||||||
class LedPin : public LedC {
|
class LedPin : public LedC {
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
NOT_FADING,
|
||||||
|
COMPLETED,
|
||||||
|
FADING
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ABSOLUTE,
|
||||||
|
PROPORTIONAL
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int fadeState=NOT_FADING;
|
||||||
|
static bool fadeCallback(const ledc_cb_param_t *param, void *arg);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LedPin(uint8_t pin, float level=0, uint16_t freq=DEFAULT_PWM_FREQ, boolean invert=false); // assigns pin to be output of one of 16 PWM channels initial level and frequency
|
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)
|
void set(float level); // sets the PWM duty to level (0-100)
|
||||||
|
int fade(float level, uint32_t fadeTime, int fadeType=ABSOLUTE); // sets the PWM duty to level (0-100) within fadeTime in milliseconds, returns success (0) or fail (1)
|
||||||
|
int fadeStatus(); // returns fading state
|
||||||
|
|
||||||
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B
|
static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,7 @@
|
||||||
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
|
// 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.
|
// as well as compile and test from this point. This file is ignored when the library is included in other sketches.
|
||||||
|
|
||||||
#include "Blinker.h"
|
#include "PwmPin.h"
|
||||||
#include "Pixel.h"
|
|
||||||
#include <Wire.h>
|
|
||||||
|
|
||||||
Blinker p(new Pixel(2),10);
|
|
||||||
//Blinker p(NULL,10);
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
|
||||||
|
|
@ -41,19 +36,34 @@ void setup() {
|
||||||
Serial.flush();
|
Serial.flush();
|
||||||
delay(1000); // wait for interface to flush
|
delay(1000); // wait for interface to flush
|
||||||
|
|
||||||
Serial.println("\n\nHomeSpan Blinker Example\n");
|
Serial.println("\n\nHomeSpan LED Fade Test\n");
|
||||||
Serial.printf("Pins = %d\n",p.getPin());
|
|
||||||
|
LedPin red(33,0);
|
||||||
|
LedPin green(32,0);
|
||||||
|
LedPin blue(14,0);
|
||||||
|
|
||||||
|
int redLevel=0;
|
||||||
|
|
||||||
|
for(int i=100;i<=100;i+=10){
|
||||||
|
while(red.fadeStatus()==LedPin::FADING);
|
||||||
|
red.fade(i,1000,LedPin::PROPORTIONAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
|
||||||
|
|
||||||
|
while(1){
|
||||||
|
delay(1000);
|
||||||
|
if(red.fade(redLevel,5000))
|
||||||
|
Serial.printf("Failed\n");
|
||||||
|
else{
|
||||||
|
Serial.printf("Success\n");
|
||||||
|
redLevel=100-redLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
p.on();
|
|
||||||
delay(2000);
|
|
||||||
p.off();
|
|
||||||
delay(2000);
|
|
||||||
p.start(300,0.25,4,1000);
|
|
||||||
delay(5000);
|
|
||||||
Serial.printf("New Pattern\n");
|
|
||||||
p.start(200,0.2,2,200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop(){
|
void loop(){
|
||||||
p.check();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue