From a87211174354af23647cda8462d4b90d67f2c2bf Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 3 Dec 2021 07:02:25 -0600 Subject: [PATCH 001/106] Fixed bug in CUSTOM_CHAR() when applied to STRING Characteristic Created a new CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) macro specifically for STRING Characteristics --- src/Span.h | 4 +++- src/src.ino | 9 +++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Span.h b/src/Span.h index e10c585..57eda29 100644 --- a/src/Span.h +++ b/src/Span.h @@ -529,4 +529,6 @@ namespace Characteristic { 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} { init(val,nvsStore,(FORMAT##_t)MINVAL,(FORMAT##_t)MAXVAL); } }; } - +#define CUSTOM_CHAR_STRING(NAME,UUID,PERMISISONS,DEFVAL) \ + HapChar _CUSTOM_##NAME {#UUID,#NAME,(PERMS)(PERMISISONS),STRING,true}; \ + namespace Characteristic { struct NAME : SpanCharacteristic { NAME(const char * val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&_CUSTOM_##NAME} { init(val,nvsStore); } }; } diff --git a/src/src.ino b/src/src.ino index 671ca89..1aa3888 100644 --- a/src/src.ino +++ b/src/src.ino @@ -4,7 +4,10 @@ #include "HomeSpan.h" -CUSTOM_CHAR(CustomActive, E863F10A-079E-48FF-8F27-9C2605A29F52, PR+EV, UINT16, 0, 0, 4800, false); +#define STRING_t const char * // WORK-AROUND + +CUSTOM_CHAR(LightMode, AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA, PR, STRING, "ANY_VALUE", NULL, NULL, true); +CUSTOM_CHAR_STRING(DarkMode, AAAAAAAA-BBBB-AAAA-AAAA-AAAAAAAAAAAA, PR, "MY_VALUE"); void setup() { @@ -45,7 +48,9 @@ void setup() { new Service::LightBulb(); new Characteristic::On(0); - new Characteristic::CustomActive(1200); + new Characteristic::LightMode("HELLO"); + new Characteristic::DarkMode(); + new Characteristic::DarkMode("OVERRIDE"); new Characteristic::Brightness(50); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); From 0aba248888e799c00dd12211ad1fada68a7bd75a Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 3 Dec 2021 07:18:39 -0600 Subject: [PATCH 002/106] Update Reference.md --- docs/Reference.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index cb7e88d..d10cbaf 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -305,28 +305,31 @@ If REQUIRED is defined in the main sketch prior to including the HomeSpan librar #define REQUIRED VERISON(2,1,3) // throws a compile-time error unless HomeSpan library used is version 2.1.3 or later ``` ### *#define CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)* +### *#define CUSTOM_CHAR_STRING(name,uuid,perms,defaultValue)* -Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as Eve for HomeKit). Parameters are as follows (note that quotes should NOT be used in any of the string parameters): +Creates a custom Characteristic that can be added to any Service. Custom Characteristics are generally ignored by the Home App but may be used by other third-party applications (such as Eve for HomeKit). The first form should be used create numerical Characterstics (e.g., UINT8, BOOL...). The second form is used to String-based Characteristics. Parameters are as follows (note that quotes should NOT be used in any of the macro parameters, except for defaultValue): * *name* - the name of the custom Characteristic. This will be added to the Characteristic namespace so that it is accessed the same as any HomeSpan Characteristic * *uuid* - the UUID of the Characteristic as defined by the manufacturer. Must be *exactly* 36 characters in the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, where *X* represent a valid hexidecimal digit. Leading zeros are required if needed as described more fully in HAP-R2 Section 6.6.1 * *perms* - additive list of permissions as described in HAP-R2 Table 6-4. Valid values are PR, PW, EV, AA, TW, HD, and WR -* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, FLOAT, and STRING. Note that the HomeSpan does not presently support the TLV8 or DATA formats +* *format* - specifies the format of the Characteristic value, as described in HAP-R2 Table 6-5. Valid value are BOOL, UINT8, UINT16, UNIT32, UINT64, INT, and FLOAT. Note that the HomeSpan does not presently support the TLV8 or DATA formats. Not applicable for Strings-based Characteristics * *defaultValue* - specifies the default value of the Characteristic if not defined during instantiation -* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()` -* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()` -* *staticRange* - set to *true* if *minValue* and *maxValue* are static and cannot be overridden with a call to `setRange()`. Set to *false* if calls to `setRange()` are allowed +* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics +* *minValue* - specifies the default minimum range for a valid value, which may be able to be overriden by a call to `setRange()`. Not applicable for Strings-based Characteristics +* *staticRange* - set to *true* if *minValue* and *maxValue* are static and cannot be overridden with a call to `setRange()`. Set to *false* if calls to `setRange()` are allowed. Not applicable for Strings-based Characteristics -As an example, the following creates a custom Characteristic named "Voltage" with a UUID code that is recognized by Eve for HomeKit. The parameters show that the Characteristic is read-only (PR) and notifications are enabled (EV). The default range of allowed values is 0-240, with a default of 120. The range *can* be overridden by subsequent calls to `setRange()`: +As an example, the first line below creates a custom Characteristic named "Voltage" with a UUID code that is recognized by Eve for HomeKit. The parameters show that the Characteristic is read-only (PR) and notifications are enabled (EV). The default range of allowed values is 0-240, with a default of 120. The range *can* be overridden by subsequent calls to `setRange()`. The second line below creates a custom read-only String-based Characteristic: ```C++ CUSTOM_CHAR(Voltage, E863F10A-079E-48FF-8F27-9C2605A29F52, PR+EV, UINT16, 120, 0, 240, false); +CUSTOM_CHAR_STRING(UserTag, AAAAAAAA-BBBB-AAAA-AAAA-AAAAAAAAAAAA, PR, "Tag 123"); ... new Service::LightBulb(); new Characteristic::Name("Low-Voltage Lamp"); new Characteristic::On(0); new Characteristic::Brightness(50); - new Characteristic::Voltage(12); // adds Voltage Characteristics and sets initial value to 12 volts + new Characteristic::Voltage(12); // adds Voltage Characteristic and sets initial value to 12 volts + new Characteristic::UserTag(); // adds UserTag Characteristic and retains default initial value of "Tag 123" ``` Note that Custom Characteristics must be created prior to calling `homeSpan.begin()` From 715adf44f1b1ab0b58e6688d49fee6c206208389 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 17 Dec 2021 06:44:27 -0600 Subject: [PATCH 003/106] Added setDescription(const char *) method to SpanCharacteristic This allows you to add an optional "description" to a Characteristic. This is not the same as the name of a Service and is generally not used by the Home App. However, it appears to be consumed by the Eve app, which can be helpful in some circumstances when developing custom characteristics. --- src/HomeSpan.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 664d4b9..40f872a 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -601,6 +601,12 @@ struct SpanCharacteristic{ return(setPerms(perms&(~dPerms))); } + SpanCharacteristic *setDescription(const char *c){ + desc = (char *)realloc(desc, strlen(c) + 1); + strcpy(desc, c); + return(this); + } + }; /////////////////////////////// From 8dd9a40f779b353e64a5da3753f4136bc627b575 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 18 Dec 2021 15:26:33 -0600 Subject: [PATCH 004/106] Update Reference.md --- docs/Reference.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index d10cbaf..c4c1736 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -256,6 +256,11 @@ The following methods are supported: * returns a pointer to the Characteristic itself so that the method can be chained during instantiation * example: `(new Characteristic::ConfiguredName("HDMI 1"))->removePerms(PW);` +* `SpanCharacteristic *setDescription(const char *desc)` + * adds an optional description, *desc*, to a Characteristic, as described in HAP-R2 Table 6-3 + * this field is generally used to provide information about custom Characteristics, but does not appear to be used in any way by the Home App + * returns a pointer to the Characteristic itself so that the method can be chained during instantiation + * example: `(new Characteristic::MyCustomChar())->setDescription("Tuner Frequency");` ## *SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t doubleTime)* From 3cc14c3a8d6de2924d2cee371b8ee1a5d23b3dd3 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 23 Dec 2021 15:37:26 -0600 Subject: [PATCH 005/106] Addressed automation issue by extending true/false parsing Added true/false parsing to all integer-based Characteristics. Previously true/false was only parsed for BOOL Characteristics. For integer-based on/off Characteristics, the Home App would send a 0 for off, and a 1 for on, consistent with HAP-R2. BUT...when using Home App AUTOMATIONS, the Home App would send true/false for integer-based Characteristics, which is inconsistent with HAP-R2. This meant automations worked with lights (that use the boolean ON Characteristic) but not with fans (that use the uint8 ACTIVE Characteristic). With this "fix", true/false will be recognized all the time (except for float- and string-based Characteristics. Confirmed that fans now work with Home App Automations. --- src/HomeSpan.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index f3af167..9a0991f 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1722,27 +1722,47 @@ StatusCode SpanCharacteristic::loadUpdate(char *val, char *ev){ break; case INT: - if(!sscanf(val,"%d",&newValue.INT)) + if(!strcmp(val,"false")) + newValue.INT=0; + else if(!strcmp(val,"true")) + newValue.INT=1; + else if(!sscanf(val,"%d",&newValue.INT)) return(StatusCode::InvalidValue); break; case UINT8: - if(!sscanf(val,"%hhu",&newValue.UINT8)) + if(!strcmp(val,"false")) + newValue.UINT8=0; + else if(!strcmp(val,"true")) + newValue.UINT8=1; + else if(!sscanf(val,"%hhu",&newValue.UINT8)) return(StatusCode::InvalidValue); break; case UINT16: - if(!sscanf(val,"%hu",&newValue.UINT16)) + if(!strcmp(val,"false")) + newValue.UINT16=0; + else if(!strcmp(val,"true")) + newValue.UINT16=1; + else if(!sscanf(val,"%hu",&newValue.UINT16)) return(StatusCode::InvalidValue); break; case UINT32: - if(!sscanf(val,"%u",&newValue.UINT32)) + if(!strcmp(val,"false")) + newValue.UINT32=0; + else if(!strcmp(val,"true")) + newValue.UINT32=1; + else if(!sscanf(val,"%u",&newValue.UINT32)) return(StatusCode::InvalidValue); break; case UINT64: - if(!sscanf(val,"%llu",&newValue.UINT64)) + if(!strcmp(val,"false")) + newValue.UINT64=0; + else if(!strcmp(val,"true")) + newValue.UINT64=1; + else if(!sscanf(val,"%llu",&newValue.UINT64)) return(StatusCode::InvalidValue); break; From 8737efea14d78c591341c966b390058370bec430 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 23 Dec 2021 22:28:26 -0600 Subject: [PATCH 006/106] Added setUnit(char *) as method to SpanCharacteristic Optional method to add or override the units for a Characteristic (i.e., "percentage", or "celsius") as per HAP-R2 Table 6-6. --- src/HomeSpan.cpp | 7 +++++++ src/HomeSpan.h | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 9a0991f..d0612f2 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1645,6 +1645,13 @@ int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){ nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"minStep\":%s",uvPrint(stepValue).c_str()); } + if(unit){ + if(strlen(unit)>0) + nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"unit\":\"%s\"",unit); + else + nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"unit\":null"); + } + if(validValues){ nBytes+=snprintf(cBuf?(cBuf+nBytes):NULL,cBuf?128:0,",\"valid-values\":%s",validValues); } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 40f872a..3a3f39c 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -260,6 +260,7 @@ struct SpanCharacteristic{ uint8_t perms; // Characteristic Permissions FORMAT format; // Characteristic Format char *desc=NULL; // Characteristic Description (optional) + char *unit=NULL; // Characteristic Unit (optional) UVal minValue; // Characteristic minimum (not applicable for STRING) UVal maxValue; // Characteristic maximum (not applicable for STRING) UVal stepValue; // Characteristic step size (not applicable for STRING) @@ -607,6 +608,12 @@ struct SpanCharacteristic{ return(this); } + SpanCharacteristic *setUnit(const char *c){ + unit = (char *)realloc(unit, strlen(c) + 1); + strcpy(unit, c); + return(this); + } + }; /////////////////////////////// From 81776f336660650b7a78ad6beb123fdb163aad76 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 23 Dec 2021 22:58:52 -0600 Subject: [PATCH 007/106] Update Reference.md --- docs/Reference.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index c4c1736..178885a 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -262,6 +262,11 @@ The following methods are supported: * returns a pointer to the Characteristic itself so that the method can be chained during instantiation * example: `(new Characteristic::MyCustomChar())->setDescription("Tuner Frequency");` +* `SpanCharacteristic *setUnit(const char *unit)` + * adds or overrides the *unit* for a Characteristic, as described in HAP-R2 Table 6-6 + * returns a pointer to the Characteristic itself so that the method can be chained during instantiation + * example: `(new Characteristic::RotationSpeed())->setUnit("percentage");` + ## *SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t doubleTime)* Creating an instance of this **class** attaches a pushbutton handler to the ESP32 *pin* specified. From d667f5e81dc0e3ab7ddc07ffc3d536b08a8c89f2 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 26 Dec 2021 23:07:37 -0600 Subject: [PATCH 008/106] Added homeSpan.setStatusAutoOff(uint16_t duration) Optional method to automatically turn off Status LED after a *duration* seconds. LED will resume normal operation any time it is re-triggered with a new pattern. This also resets the elapsed time used to check for autoOff. --- src/HomeSpan.cpp | 11 ++++++++--- src/HomeSpan.h | 4 +++- src/Utils.cpp | 35 +++++++++++++++++++++++++++++++---- src/Utils.h | 39 +++++++++++++++++++++++++++++---------- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index d0612f2..543f770 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -58,7 +58,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 controlButton.init(controlPin); - statusLED.init(statusPin); + statusLED.init(statusPin,0,autoOffLED); int maxLimit=CONFIG_LWIP_MAX_SOCKETS-2-otaEnabled; if(maxConnections>maxLimit) @@ -85,7 +85,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data delay(2000); - + Serial.print("\n************************************************************\n" "Welcome to HomeSpan!\n" "Apple HomeKit for the Espressif ESP-32 WROOM and Arduino IDE\n" @@ -95,8 +95,11 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa Serial.print("Message Logs: Level "); Serial.print(logLevel); Serial.print("\nStatus LED: Pin "); - if(statusPin>=0) + if(statusPin>=0){ Serial.print(statusPin); + if(autoOffLED>0) + Serial.printf(" (Auto Off=%d sec)",autoOffLED); + } else Serial.print("- *** WARNING: Status LED Pin is UNDEFINED"); Serial.print("\nDevice Control: Pin "); @@ -291,6 +294,8 @@ void Span::poll() { commandMode(); // COMMAND MODE } } + + statusLED.check(); } // poll diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 3a3f39c..9acd360 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -119,7 +119,8 @@ struct Span{ unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing - int statusPin=DEFAULT_STATUS_PIN; // pin for status LED + int statusPin=DEFAULT_STATUS_PIN; // pin for Status LED + uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED int controlPin=DEFAULT_CONTROL_PIN; // pin for Control Pushbutton uint8_t logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor uint8_t maxConnections=DEFAULT_MAX_CONNECTIONS; // number of simultaneous HAP connections @@ -173,6 +174,7 @@ struct Span{ void setControlPin(uint8_t pin){controlPin=pin;} // sets Control Pin void setStatusPin(uint8_t pin){statusPin=pin;} // sets Status Pin + void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) int getStatusPin(){return(statusPin);} // get Status Pin void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password diff --git a/src/Utils.cpp b/src/Utils.cpp index e6f5294..953b9bb 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -232,13 +232,13 @@ Blinker::Blinker(){ ////////////////////////////////////// -Blinker::Blinker(int pin, int timerNum){ - init(pin, timerNum); +Blinker::Blinker(int pin, int timerNum, uint16_t autoOffDuration){ + init(pin, timerNum, autoOffDuration); } ////////////////////////////////////// -void Blinker::init(int pin, int timerNum){ +void Blinker::init(int pin, int timerNum, uint16_t autoOffDuration){ this->pin=pin; if(pin<0) @@ -247,6 +247,8 @@ void Blinker::init(int pin, int timerNum){ pinMode(pin,OUTPUT); digitalWrite(pin,0); + pauseDuration=autoOffDuration*1000; + #if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group @@ -327,7 +329,9 @@ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ if(pin<0) return; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); // needed to ensure digitalRead() functions correctly on ESP32-C3 + pauseTime=millis(); + isPaused=false; + gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); // needed to ensure digitalRead() functions correctly on ESP32-C3; also needed to re-enable after pause() period*=10; onTime=dutyCycle*period; @@ -357,6 +361,10 @@ void Blinker::on(){ if(pin<0) return; + pauseTime=millis(); + isPaused=false; + gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); + stop(); digitalWrite(pin,1); } @@ -368,6 +376,25 @@ void Blinker::off(){ if(pin<0) return; + pauseTime=millis(); + isPaused=false; + gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); + stop(); digitalWrite(pin,0); } + +////////////////////////////////////// + +void Blinker::check(){ + + if(pin<0) + return; + + if(pauseDuration==0 || isPaused || (millis()-pauseTime) Date: Mon, 27 Dec 2021 15:41:41 -0600 Subject: [PATCH 009/106] Update Reference.md --- docs/Reference.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index 178885a..397d95d 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -33,6 +33,11 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * `void setStatusPin(uint8_t pin)` * sets the ESP32 pin to use for the HomeSpan Status LED. If not specified, HomeSpan will assume there is no Status LED + +* `void setStatusAutoOff(uint16_t duration)` + * sets Status LED to automatically turn off after *duration* seconds + * Status LED will automatically turn on, and duration timer will be reset, whenever HomeSpan activates a new blinking pattern + * if *duration* is set to zero, auto-off is disabled (Status LED will remain on indefinitely) * `int getStatusPin()` * returns the pin number of the Status LED as set by `setStatusPin(pin)`, or -1 if no pin has been set From facba4c02a03dc1391d05a00723615d023c64492 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 31 Dec 2021 14:51:42 -0600 Subject: [PATCH 010/106] Added Pixel.h and Pixel.cpp and implemented SK68XX Addressable LED Class Also add getPin() to RFControl (which is used by SK68XX Class) as well as boolean operator overrides for both RFControl and PwmPin/ServoPin so that instances can be checked for validity. --- src/extras/Pixel.cpp | 36 +++++++++++++++++++++++++++++++ src/extras/Pixel.h | 24 +++++++++++++++++++++ src/extras/PwmPin.h | 6 ++++++ src/extras/RFControl.cpp | 3 --- src/extras/RFControl.h | 10 +++++++++ src/extras/extras.ino | 46 ++++++++++++++++------------------------ 6 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 src/extras/Pixel.cpp create mode 100644 src/extras/Pixel.h diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp new file mode 100644 index 0000000..7c40a3d --- /dev/null +++ b/src/extras/Pixel.cpp @@ -0,0 +1,36 @@ + +#include "Pixel.h" + +/////////////////// + +sk68xx::sk68xx(int pin){ + rf=new RFControl(pin,false); + setRGB(0,0,0); +} + +/////////////////// + +void sk68xx::setRGB(uint8_t r, uint8_t g, uint8_t b){ + + if(!*rf) + return; + + rf->clear(); + loadColor(g); + loadColor(r); + loadColor(b); + rf->phase(6400,0); // add 80 usec end-marker delay + rf->start(); +} + +/////////////////// + +void sk68xx::loadColor(uint8_t c){ + + for(int i=7;i>=0;i--){ + if((c>>i)&1) + rf->add(51,45); + else + rf->add(26,70); + } +} diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h new file mode 100644 index 0000000..b8ec4a3 --- /dev/null +++ b/src/extras/Pixel.h @@ -0,0 +1,24 @@ + +//////////////////////////////////// +// Addressable LED Pixel // +//////////////////////////////////// + +#pragma once + +#include "RFControl.h" + +class sk68xx { + private: + int pin=-1; + RFControl *rf; + void loadColor(uint8_t c); + + public: + sk68xx(int pin); // creates addressable SK68XX RGB LED on pin + void setRGB(uint8_t r, uint8_t g, uint8_t b); // sets color to rgb values (0-255) + int getPin(){return(rf->getPin());} // returns pin number + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(*rf); + } +}; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 2057e07..50eebb8 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -14,6 +14,8 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma once + #include #include @@ -34,6 +36,10 @@ class LedC { public: int getPin(){return(channel?channel->gpio_num:-1);} // returns the pin number + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(channel); + } }; diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index ce2ce88..b87bb0b 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -1,7 +1,4 @@ -#include -#include - #include "RFControl.h" /////////////////// diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index c099935..8c17212 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -3,6 +3,10 @@ // RF Control Module // //////////////////////////////////// +#pragma once + +#include +#include #include "driver/rmt.h" #include @@ -27,6 +31,12 @@ class RFControl { void phase(uint32_t nTicks, uint8_t phase); // adds either a HIGH phase or LOW phase lasting numTicks ticks void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 void disableCarrier(){enableCarrier(0);} // disables carrier wave + + int getPin(){return(config?config->gpio_num:-1);} // returns the pin number + + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed + return(config); + } }; // Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 6fcf935..7b1f674 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -1,14 +1,6 @@ -/* HomeSpan Remote Control Example */ +/* HomeSpan Pixel Example */ -#include "RFControl.h" // include RF Control Library - -#define PRONTO_N 0.241246 - -uint16_t pronto[]={0000,0x006D,0x0000,0x0022,0x00AC,0x00AC,0x0015,0x0040,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0015,0x0015,0x0040,0x0015,0x0040,0x0015,0x0015,0x0015,0x0689}; -//uint16_t pronto[]={0000,0x006D,0x0000,0x0022,0x00AC,0x00AC,0x0017,0x003E,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0018,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0013,0x0017,0x003E,0x0017,0x003E,0x0017,0x0013,0x0017,0x0746}; -//uint16_t pronto[]={0000,0x0067,0x0000,0x000d,0x0060,0x0018,0x0018,0x0018,0x0030,0x0018,0x0030,0x0018,0x0030,0x0018,0x0018,0x0018,0x0030,0x0018,0x0018,0x0018,0x0030,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x03f6}; - -uint32_t data[100]; +#include "Pixel.h" void setup() { @@ -16,29 +8,27 @@ void setup() { Serial.flush(); delay(1000); // wait for interface to flush - Serial.println("\n\nHomeSpan RF Transmitter Example\n\n"); + Serial.println("\n\nHomeSpan Pixel Example\n\n"); - RFControl rf(17); - rf.enableCarrier(38000,0.5); + sk68xx px(8); + sk68xx test7(7); + sk68xx test6(6); - uint32_t code = 0xE0E019E6; // OFF -// uint32_t code = 0xE0E09966; // ON + Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); + Serial.printf("Test 7 on Pin=%d check: %s\n",test7.getPin(),test7?"OKAY":"BAD"); + Serial.printf("Test 6 on Pin=%d check: %s\n",test6.getPin(),test6?"OKAY":"BAD"); - int unit=563; - - rf.add(4500,4500); - - for(int i=31;i>=0;i--){ - rf.add(unit,unit*((code&(1<=0;i--){ + px.setRGB(i,0,0); + delay(2); + } } - rf.add(unit,45000); - - rf.start(2); - Serial.println("Done!"); } // end of setup() From 3b6bc13b1c0b22903a77de18764f329a911a927f Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 31 Dec 2021 15:34:20 -0600 Subject: [PATCH 011/106] Added setHSV() to sk68xx class --- src/extras/Pixel.cpp | 10 ++++++++++ src/extras/Pixel.h | 2 ++ src/extras/extras.ino | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 7c40a3d..a2cca3c 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -14,6 +14,8 @@ void sk68xx::setRGB(uint8_t r, uint8_t g, uint8_t b){ if(!*rf) return; + + Serial.printf("%d %d %d\n",r,g,b); rf->clear(); loadColor(g); @@ -25,6 +27,14 @@ void sk68xx::setRGB(uint8_t r, uint8_t g, uint8_t b){ /////////////////// +void sk68xx::setHSV(float h, float s, float v){ + float r,g,b; + LedPin::HSVtoRGB(h,s,v,&r,&g,&b); + setRGB(r*255,g*255,b*255); +} + +/////////////////// + void sk68xx::loadColor(uint8_t c){ for(int i=7;i>=0;i--){ diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index b8ec4a3..35002d9 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -6,6 +6,7 @@ #pragma once #include "RFControl.h" +#include "PwmPin.h" class sk68xx { private: @@ -16,6 +17,7 @@ class sk68xx { public: sk68xx(int pin); // creates addressable SK68XX RGB LED on pin void setRGB(uint8_t r, uint8_t g, uint8_t b); // sets color to rgb values (0-255) + void setHSV(float h, float s, float v); // sets color to hsv values where h=[0,360], s=[0,1], v=[0,1] int getPin(){return(rf->getPin());} // returns pin number operator bool(){ // override boolean operator to return true/false if creation succeeded/failed diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 7b1f674..d62d1b2 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -18,6 +18,17 @@ void setup() { Serial.printf("Test 7 on Pin=%d check: %s\n",test7.getPin(),test7?"OKAY":"BAD"); Serial.printf("Test 6 on Pin=%d check: %s\n",test6.getPin(),test6?"OKAY":"BAD"); +while(1){ + px.setHSV(60,0.9,0.5); + delay(1000); + px.setHSV(120,0.9,0.5); + delay(1000); + px.setHSV(240,0.9,0.5); + delay(1000); +} + + while(1); + while(1){ for(int i=0;i<50;i++){ px.setRGB(i,0,0); From 368a06301a7dc9c9de7c9d975f896625cad3d20f Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 31 Dec 2021 18:10:51 -0600 Subject: [PATCH 012/106] renamed sk68xx class to Pixel and made it generic Constructor now allows you to specify high/low timings for 1-bit and 0-bit, as well as timing for reset delay. Default parameters are included if none are specified. --- src/extras/Pixel.cpp | 27 ++++++++++++++++----------- src/extras/Pixel.h | 13 +++++++++---- src/extras/extras.ino | 30 +++++++++++++++--------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index a2cca3c..1d4c634 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -3,31 +3,36 @@ /////////////////// -sk68xx::sk68xx(int pin){ - rf=new RFControl(pin,false); - setRGB(0,0,0); +Pixel::Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset){ + + H0=high0*80; // high0, low0, etc. are all in microseconds and must be multiplied by 80 to match 80MHz RFControl clock + L0=low0*80; + H1=high1*80; + L1=low1*80; + LR=lowReset*80; + + rf=new RFControl(pin,false); // set clock to 1/80 usec + setRGB(0,255,0); } /////////////////// -void sk68xx::setRGB(uint8_t r, uint8_t g, uint8_t b){ +void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b){ if(!*rf) return; - - Serial.printf("%d %d %d\n",r,g,b); rf->clear(); loadColor(g); loadColor(r); loadColor(b); - rf->phase(6400,0); // add 80 usec end-marker delay + rf->phase(LR,0); // end-marker delay/reset rf->start(); } /////////////////// -void sk68xx::setHSV(float h, float s, float v){ +void Pixel::setHSV(float h, float s, float v){ float r,g,b; LedPin::HSVtoRGB(h,s,v,&r,&g,&b); setRGB(r*255,g*255,b*255); @@ -35,12 +40,12 @@ void sk68xx::setHSV(float h, float s, float v){ /////////////////// -void sk68xx::loadColor(uint8_t c){ +void Pixel::loadColor(uint8_t c){ for(int i=7;i>=0;i--){ if((c>>i)&1) - rf->add(51,45); + rf->add(H1,L1); // 1-bit else - rf->add(26,70); + rf->add(H0,L0); // 0-bit } } diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 35002d9..d109b02 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -8,17 +8,22 @@ #include "RFControl.h" #include "PwmPin.h" -class sk68xx { +class Pixel { private: - int pin=-1; + uint32_t H0, L0; // High and Low times for a zero-pulse (in units of 1/80 microseconds) + uint32_t H1, L1; // High and Low times for a one-pulse (in units of 1/80 microseconds) + uint32_t LR; // Low time for a reset/end-of-data (in units of 1/80 microseconds) + RFControl *rf; void loadColor(uint8_t c); public: - sk68xx(int pin); // creates addressable SK68XX RGB LED on pin + Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! + Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for some SK68XX chips + void setRGB(uint8_t r, uint8_t g, uint8_t b); // sets color to rgb values (0-255) void setHSV(float h, float s, float v); // sets color to hsv values where h=[0,360], s=[0,1], v=[0,1] - int getPin(){return(rf->getPin());} // returns pin number + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index d62d1b2..a7b1d1a 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -10,15 +10,15 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n\n"); - sk68xx px(8); - sk68xx test7(7); - sk68xx test6(6); + Pixel px(8); + Pixel test7(7); + Pixel test6(6); Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); Serial.printf("Test 7 on Pin=%d check: %s\n",test7.getPin(),test7?"OKAY":"BAD"); Serial.printf("Test 6 on Pin=%d check: %s\n",test6.getPin(),test6?"OKAY":"BAD"); -while(1){ +for(int i=0;i<5;i++){ px.setHSV(60,0.9,0.5); delay(1000); px.setHSV(120,0.9,0.5); @@ -27,18 +27,18 @@ while(1){ delay(1000); } - while(1); - while(1){ - for(int i=0;i<50;i++){ - px.setRGB(i,0,0); - delay(2); - } - for(int i=50;i>=0;i--){ - px.setRGB(i,0,0); - delay(2); - } - } + +// while(1){ +// for(int i=0;i<50;i++){ +// px.setRGB(i,0,0); +// delay(2); +// } +// for(int i=50;i>=0;i--){ +// px.setRGB(i,0,0); +// delay(2); +// } +// } Serial.println("Done!"); From 12c283e8c343065d5e2aa369169728a630d7e6f5 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 1 Jan 2022 07:14:30 -0600 Subject: [PATCH 013/106] Update Pixel.h --- src/extras/Pixel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index d109b02..bfcc5b8 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -19,7 +19,7 @@ class Pixel { public: Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! - Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for some SK68XX chips + Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for SK68XXMINI-HS LEDs, though will likely work with many other variations as well void setRGB(uint8_t r, uint8_t g, uint8_t b); // sets color to rgb values (0-255) void setHSV(float h, float s, float v); // sets color to hsv values where h=[0,360], s=[0,1], v=[0,1] From 5d3a4bfc38c487091b90a4556141a33079e9a949 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 1 Jan 2022 08:14:32 -0600 Subject: [PATCH 014/106] Added simple Pixel example to Other Examples --- Other Examples/Pixel/Pixel.ino | 97 ++++++++++++++++++++++++++++++++++ src/extras/Pixel.cpp | 2 +- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 Other Examples/Pixel/Pixel.ino diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino new file mode 100644 index 0000000..bdc9a1e --- /dev/null +++ b/Other Examples/Pixel/Pixel.ino @@ -0,0 +1,97 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + + // HomeSpan Addressable RGB LED Example + +// Demonstrates use of HomeSpan Pixel Class that provides for control of single-wire +// addressable RGB LEDs, such as the SK68xx or WS28xx models found in many devices, +// including the Espressif ESP32, ESP32-S2, and ESP32-C3 development boards. + +#include "HomeSpan.h" +#include "extras/Pixel.h" // include the HomeSpan Pixel class + +/////////////////////////////// + +struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel + + Characteristic::On power{0,true}; + Characteristic::Hue H{0,true}; + Characteristic::Saturation S{0,true}; + Characteristic::Brightness V{100,true}; + Pixel *pixel; + + Pixel_Light(int pin) : Service::LightBulb(){ + + V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% + pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs + update(); // manually call update() to set pixel with restored initial values + } + + boolean update() override { + + int p=power.getNewVal(); + + float h=H.getNewVal(); // range = [0,360] + float s=S.getNewVal(); // range = [0,100] + float v=V.getNewVal(); // range = [0,100] + + pixel->setHSV(h*p, s*p/100.0, v*p/100.0); // must scale down S and V from [0,100] to [0.0,1.0] + + return(true); + } +}; + +/////////////////////////////// + +void setup() { + + Serial.begin(115200); + + homeSpan.begin(Category::Lighting,"RGB Pixel"); + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Name("RGB Pixel"); + new Characteristic::Manufacturer("HomeSpan"); + new Characteristic::SerialNumber("123-ABC"); + new Characteristic::Model("SK68 LED"); + new Characteristic::FirmwareRevision("1.0"); + new Characteristic::Identify(); + + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); + + new Pixel_Light(8); // create Pixel LED attached to pin 8 + + +} + +/////////////////////////////// + +void loop() { + homeSpan.poll(); +} diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 1d4c634..8feb825 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -12,7 +12,7 @@ Pixel::Pixel(int pin, float high0, float low0, float high1, float low1, float lo LR=lowReset*80; rf=new RFControl(pin,false); // set clock to 1/80 usec - setRGB(0,255,0); + setRGB(0,0,0); } /////////////////// From e8c7afae0012f329ebcdea8d3cad1d66d4187222 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 2 Jan 2022 15:36:54 -0600 Subject: [PATCH 015/106] Increased default max number of connections to 14! Reflects increase in max number of LWIP sockets from 10 (Arduino-ESP32 v2.0.0) to 16 (Arduino-ESP32 v2.0.1). Since HomeSpan needs at least 2 (one for Server and 1 free for new connections), this leaves 14 connections for controllers. A big improvement over only having 8! --- src/Settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Settings.h b/src/Settings.h index fa694be..f63c4b1 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -79,7 +79,7 @@ #define DEFAULT_LOG_LEVEL 0 // change with homeSpan.setLogLevel(level) -#define DEFAULT_MAX_CONNECTIONS 8 // change with homeSpan.setMaxConnections(num); +#define DEFAULT_MAX_CONNECTIONS 14 // change with homeSpan.setMaxConnections(num); #define DEFAULT_TCP_PORT 80 // change with homeSpan.setPort(port); ///////////////////////////////////////////////////// From 22de894f9207ad75059f3fca031acf2c973d0932 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 2 Jan 2022 17:06:15 -0600 Subject: [PATCH 016/106] Update Reference.md --- docs/Reference.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 397d95d..078ecd4 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -62,10 +62,10 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * this parameter can also be changed at runtime via the [HomeSpan CLI](CLI.md) * `void setMaxConnections(uint8_t nCon)` - * sets the desired maximum number of HAP Controllers that can be simultaneously connected to HomeSpan (default=8) + * sets the desired maximum number of HAP Controllers that can be simultaneously connected to HomeSpan (default=14) * due to limitations of the ESP32 Arduino library, HomeSpan will override *nCon* if it exceed the following internal limits: - * if OTA is not enabled, *nCon* will be reduced to 8 if it has been set to a value greater than 8 - * if OTA is enabled, *nCon* will be reduced to 7 if it has been set to a value greater than 7 + * if OTA is not enabled, *nCon* will be reduced to 14 if it has been set to a value greater than 14 + * if OTA is enabled, *nCon* will be reduced to 13 if it has been set to a value greater than 13 * if you add code to a sketch that uses it own network resources, you will need to determine how many TCP sockets your code may need to use, and use this method to reduce the maximum number of connections available to HomeSpan accordingly * `void setPortNum(uint16_t port)` From 738dce3ad3ab272303cd163652ccd236577cd858 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Wed, 5 Jan 2022 17:25:28 -0600 Subject: [PATCH 017/106] Update Reference.md --- docs/Reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index 078ecd4..d97270b 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -349,6 +349,8 @@ new Service::LightBulb(); Note that Custom Characteristics must be created prior to calling `homeSpan.begin()` +> Advanced Tip: When presented with an unrecognized Custom Characteristic, Eve for HomeKit helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnits()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic. + --- #### Deprecated functions (available for backwards compatibility with older sketches): From d5b27d6e1462700650a28772c4044aabd486146b Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 6 Jan 2022 18:09:22 -0600 Subject: [PATCH 018/106] Added ability to set same color on multiple pixels Added 4th, optional argument, nPixels, to setRGB() and setHSV(). --- src/extras/Pixel.cpp | 15 ++++++++------- src/extras/Pixel.h | 6 +++--- src/extras/extras.ino | 41 +++++++++++------------------------------ 3 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 8feb825..40639e1 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -12,30 +12,31 @@ Pixel::Pixel(int pin, float high0, float low0, float high1, float low1, float lo LR=lowReset*80; rf=new RFControl(pin,false); // set clock to 1/80 usec - setRGB(0,0,0); } /////////////////// -void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b){ +void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ if(!*rf) return; rf->clear(); - loadColor(g); - loadColor(r); - loadColor(b); + for(int i=0;iphase(LR,0); // end-marker delay/reset rf->start(); } /////////////////// -void Pixel::setHSV(float h, float s, float v){ +void Pixel::setHSV(float h, float s, float v, int nPixels){ float r,g,b; LedPin::HSVtoRGB(h,s,v,&r,&g,&b); - setRGB(r*255,g*255,b*255); + setRGB(r*255,g*255,b*255,nPixels); } /////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index bfcc5b8..af08dd1 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -21,9 +21,9 @@ class Pixel { Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for SK68XXMINI-HS LEDs, though will likely work with many other variations as well - void setRGB(uint8_t r, uint8_t g, uint8_t b); // sets color to rgb values (0-255) - void setHSV(float h, float s, float v); // sets color to hsv values where h=[0,360], s=[0,1], v=[0,1] - int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + void setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels=1); // sets color of nPixels to rgb values (0-255) + void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to hsv values where h=[0,360], s=[0,1], v=[0,1] + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index a7b1d1a..e134f05 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,6 +2,8 @@ #include "Pixel.h" +Pixel px(23); + void setup() { Serial.begin(115200); // start the Serial interface @@ -10,40 +12,19 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n\n"); - Pixel px(8); - Pixel test7(7); - Pixel test6(6); - Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); - Serial.printf("Test 7 on Pin=%d check: %s\n",test7.getPin(),test7?"OKAY":"BAD"); - Serial.printf("Test 6 on Pin=%d check: %s\n",test6.getPin(),test6?"OKAY":"BAD"); - -for(int i=0;i<5;i++){ - px.setHSV(60,0.9,0.5); - delay(1000); - px.setHSV(120,0.9,0.5); - delay(1000); - px.setHSV(240,0.9,0.5); - delay(1000); -} - - - -// while(1){ -// for(int i=0;i<50;i++){ -// px.setRGB(i,0,0); -// delay(2); -// } -// for(int i=50;i>=0;i--){ -// px.setRGB(i,0,0); -// delay(2); -// } -// } - - Serial.println("Done!"); } // end of setup() void loop(){ + for(int i=0;i<5;i++){ + px.setHSV(0,1.0,1.0,3); + delay(1000); + px.setHSV(120,1.0,1.0,3); + delay(1000); + px.setHSV(240,1.0,1.0,3); + delay(1000); + } + } // end of loop() From f10f5cffcd98242f3c01325f0a1807fcac727e23 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 6 Jan 2022 21:15:03 -0600 Subject: [PATCH 019/106] Normalized and optimized Pixel methods created color_t typedef and use as basis for all RGB and HSV methods --- src/extras/Pixel.cpp | 41 ++++++++++++++++++++++++++++++++++------- src/extras/Pixel.h | 14 ++++++++++---- src/extras/extras.ino | 29 ++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 40639e1..cc3f37a 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -22,11 +22,22 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ return; rf->clear(); - for(int i=0;iphase(LR,0); // end-marker delay/reset + rf->start(); +} + +/////////////////// + +void Pixel::setColor(color_t *color, int nPixels){ + + if(!*rf) + return; + + rf->clear(); + for(int i=0;iphase(LR,0); // end-marker delay/reset rf->start(); } @@ -41,12 +52,28 @@ void Pixel::setHSV(float h, float s, float v, int nPixels){ /////////////////// -void Pixel::loadColor(uint8_t c){ +void Pixel::loadColor(color_t c){ - for(int i=7;i>=0;i--){ + for(int i=23;i>=0;i--){ if((c>>i)&1) rf->add(H1,L1); // 1-bit else rf->add(H0,L0); // 0-bit } } + +/////////////////// + +color_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b){ + return(g<<16 | r<<8 | b); +} + +/////////////////// + +color_t Pixel::getColorHSV(float h, float s, float v){ + float r,g,b; + LedPin::HSVtoRGB(h,s,v,&r,&g,&b); + return(getColorRGB(r*255,g*255,b*255)); +} + +/////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index af08dd1..ae9d9ad 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -8,6 +8,8 @@ #include "RFControl.h" #include "PwmPin.h" +typedef uint32_t color_t; + class Pixel { private: uint32_t H0, L0; // High and Low times for a zero-pulse (in units of 1/80 microseconds) @@ -15,15 +17,19 @@ class Pixel { uint32_t LR; // Low time for a reset/end-of-data (in units of 1/80 microseconds) RFControl *rf; - void loadColor(uint8_t c); + void loadColor(color_t c); // creates bit pattern for RGB color (encoded in low 24-bits) public: Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for SK68XXMINI-HS LEDs, though will likely work with many other variations as well - void setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels=1); // sets color of nPixels to rgb values (0-255) - void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to hsv values where h=[0,360], s=[0,1], v=[0,1] - int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + void setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels=1); // sets color of nPixels to RGB values (0-255) + void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,1], v=[0,1] + void setColor(color_t *color, int nPixels); // sets color of nPixels from array of Colors + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + + static color_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values + static color_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e134f05..49d5ce5 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,7 +2,7 @@ #include "Pixel.h" -Pixel px(23); +Pixel px(21); void setup() { @@ -13,18 +13,29 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n\n"); Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); + + px.setRGB(0,0,0,8); } // end of setup() void loop(){ - for(int i=0;i<5;i++){ - px.setHSV(0,1.0,1.0,3); - delay(1000); - px.setHSV(120,1.0,1.0,3); - delay(1000); - px.setHSV(240,1.0,1.0,3); - delay(1000); - } +// px.setHSV(0,1.0,0.2,8); +// delay(1000); +// px.setHSV(120,1.0,0.2,4); +// delay(1000); +// px.setHSV(240,1.0,0.2,2); +// delay(1000); + + color_t x[2]; + + x[0]=Pixel::getColorHSV(0,1,0.2); + x[1]=px.getColorHSV(0,0.7,0.2); + px.setColor(x,2); + delay(1000); + x[0]=px.getColorHSV(0,0.7,0.2); + x[1]=px.getColorHSV(0,1,0.2); + px.setColor(x,2); + delay(1000); } // end of loop() From 3020b800a6d0388083ffb2d43782980fcbb1ad31 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 6 Jan 2022 21:43:54 -0600 Subject: [PATCH 020/106] testing --- src/extras/Pixel.h | 2 +- src/extras/extras.ino | 74 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index ae9d9ad..d4c600e 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -17,7 +17,7 @@ class Pixel { uint32_t LR; // Low time for a reset/end-of-data (in units of 1/80 microseconds) RFControl *rf; - void loadColor(color_t c); // creates bit pattern for RGB color (encoded in low 24-bits) + void loadColor(color_t c); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits) public: Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 49d5ce5..ca18592 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -27,15 +27,71 @@ void loop(){ // px.setHSV(240,1.0,0.2,2); // delay(1000); - color_t x[2]; + color_t RED=px.getColorRGB(50,0,0); + + color_t x0[8]={0,0,0,0,0,0,0,0}; - x[0]=Pixel::getColorHSV(0,1,0.2); - x[1]=px.getColorHSV(0,0.7,0.2); - px.setColor(x,2); - delay(1000); - x[0]=px.getColorHSV(0,0.7,0.2); - x[1]=px.getColorHSV(0,1,0.2); - px.setColor(x,2); - delay(1000); + color_t x1[8]={RED,RED,RED,RED,RED,RED,RED,RED}; + color_t x2[8]={0,0,0,0,0,0,RED,RED}; + color_t x3[8]={0,0,0,0,0,RED,0,0}; + color_t x4[8]={0,0,0,0,RED,0,0,0}; + color_t x5[8]={0,0,0,0,0,RED,0,0}; + color_t x6[8]={0,0,0,0,0,0,RED,RED}; + color_t x7[8]={RED,RED,RED,RED,RED,RED,RED,RED}; + + color_t x8[8]={RED,RED,RED,RED,RED,RED,RED,RED}; + color_t x9[8]={RED,0,0,0,RED,0,0,RED}; + color_t x10[8]={RED,0,0,0,RED,0,0,RED}; + color_t x11[8]={RED,0,0,0,RED,0,0,RED}; + color_t x12[8]={RED,0,0,0,0,0,0,RED}; + color_t x13[8]={RED,0,0,0,0,0,0,RED}; + color_t x14[8]={RED,0,0,0,0,0,0,RED}; + + + uint32_t d=2; + + while(1){ + px.setColor(x1,8); + delay(d); + px.setColor(x2,8); + delay(d); + px.setColor(x3,8); + delay(d); + px.setColor(x4,8); + delay(d); + px.setColor(x5,8); + delay(d); + px.setColor(x6,8); + delay(d); + px.setColor(x7,8); + delay(d); + px.setColor(x0,8); + delay(d); + px.setColor(x8,8); + delay(d); + px.setColor(x9,8); + delay(d); + px.setColor(x10,8); + delay(d); + px.setColor(x11,8); + delay(d); + px.setColor(x12,8); + delay(d); + px.setColor(x13,8); + delay(d); + px.setColor(x14,8); + delay(d); + px.setColor(x0,8); + delay(d); + } + +// x[0]=Pixel::getColorHSV(0,1,0.2); +// x[1]=px.getColorHSV(0,0.7,0.2); +// px.setColor(x,2); +// delay(1000); +// x[0]=px.getColorHSV(0,0.7,0.2); +// x[1]=px.getColorHSV(0,1,0.2); +// px.setColor(x,2); +// delay(1000); } // end of loop() From 1b402b5ebc6b0f3c578695ca45008910b31f11ac Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 7 Jan 2022 06:25:11 -0600 Subject: [PATCH 021/106] changed range of S and V to be 0-100 instead of 0-1 But still use float so that values of 50.3, 99.5, etc. are still distinguishable. --- src/extras/Pixel.cpp | 6 ++--- src/extras/Pixel.h | 8 +++---- src/extras/extras.ino | 56 ++++++++++++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index cc3f37a..f1c190e 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -30,7 +30,7 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ /////////////////// -void Pixel::setColor(color_t *color, int nPixels){ +void Pixel::setColors(color_t *color, int nPixels){ if(!*rf) return; @@ -46,7 +46,7 @@ void Pixel::setColor(color_t *color, int nPixels){ void Pixel::setHSV(float h, float s, float v, int nPixels){ float r,g,b; - LedPin::HSVtoRGB(h,s,v,&r,&g,&b); + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); setRGB(r*255,g*255,b*255,nPixels); } @@ -72,7 +72,7 @@ color_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b){ color_t Pixel::getColorHSV(float h, float s, float v){ float r,g,b; - LedPin::HSVtoRGB(h,s,v,&r,&g,&b); + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); return(getColorRGB(r*255,g*255,b*255)); } diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index d4c600e..8de4220 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -24,12 +24,12 @@ class Pixel { Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for SK68XXMINI-HS LEDs, though will likely work with many other variations as well void setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels=1); // sets color of nPixels to RGB values (0-255) - void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,1], v=[0,1] - void setColor(color_t *color, int nPixels); // sets color of nPixels from array of Colors + void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,100], v=[0,100] + void setColors(color_t *color, int nPixels); // sets colors of nPixels from array of Colors int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 - static color_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values - static color_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values + static color_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values + static color_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index ca18592..476691d 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -14,7 +14,25 @@ void setup() { Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); - px.setRGB(0,0,0,8); + int H=0; + color_t x[8]; + int s; + + while(1){ + + for(int i=0;i<8;i++) + x[i]=px.getColorHSV(H,i*3+79,i*2+5); + + px.setColors(x,8); + delay(20); + H=H+1; + if(H>=360) + H=0; + + s++; + if(s>8) + s=0; + } } // end of setup() @@ -51,47 +69,47 @@ void loop(){ uint32_t d=2; while(1){ - px.setColor(x1,8); + px.setColors(x1,8); delay(d); - px.setColor(x2,8); + px.setColors(x2,8); delay(d); - px.setColor(x3,8); + px.setColors(x3,8); delay(d); - px.setColor(x4,8); + px.setColors(x4,8); delay(d); - px.setColor(x5,8); + px.setColors(x5,8); delay(d); - px.setColor(x6,8); + px.setColors(x6,8); delay(d); - px.setColor(x7,8); + px.setColors(x7,8); delay(d); - px.setColor(x0,8); + px.setColors(x0,8); delay(d); - px.setColor(x8,8); + px.setColors(x8,8); delay(d); - px.setColor(x9,8); + px.setColors(x9,8); delay(d); - px.setColor(x10,8); + px.setColors(x10,8); delay(d); - px.setColor(x11,8); + px.setColors(x11,8); delay(d); - px.setColor(x12,8); + px.setColors(x12,8); delay(d); - px.setColor(x13,8); + px.setColors(x13,8); delay(d); - px.setColor(x14,8); + px.setColors(x14,8); delay(d); - px.setColor(x0,8); + px.setColors(x0,8); delay(d); } // x[0]=Pixel::getColorHSV(0,1,0.2); // x[1]=px.getColorHSV(0,0.7,0.2); -// px.setColor(x,2); +// px.setColors(x,2); // delay(1000); // x[0]=px.getColorHSV(0,0.7,0.2); // x[1]=px.getColorHSV(0,1,0.2); -// px.setColor(x,2); +// px.setColors(x,2); // delay(1000); } // end of loop() From 9d0c56799c197a2a51f846782cc87e3b30d05eb1 Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 7 Jan 2022 06:58:40 -0600 Subject: [PATCH 022/106] Update extras.ino --- src/extras/extras.ino | 173 +++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 97 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 476691d..832396e 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,7 +2,80 @@ #include "Pixel.h" +struct Effect1 { + + Pixel *px; + int H=0; + color_t x[8]; + uint32_t alarmTime=0; + uint32_t speed; + + Effect1(Pixel *px, uint32_t speed=20){ + this->px=px; + this->speed=speed; + } + + void update(){ + if(millis()getColorHSV(H,i*3+79,i*2+5); + + px->setColors(x,8); + H=(H+1)%360; + + alarmTime=millis()+speed; + } +}; + +struct Effect2 { + + Pixel *px; + int phase=0; + int dir=1; + int H=0; + color_t x[8]; + uint32_t alarmTime=0; + uint32_t speed; + + Effect2(Pixel *px, uint32_t speed=20){ + this->px=px; + this->speed=speed; + } + + void update(){ + if(millis()getColorHSV(H,100,10); + else if(i==7-phase) + x[i]=px->getColorHSV(H+180,100,10); + else + x[i]=0; + } + + px->setColors(x,8); + phase=(phase+dir)%8; + + if(phase==0){ + dir=1; + H=(H+10)%360; + } + else if(phase==7){ + dir=-1; + H=(H+10)%360; + } + + alarmTime=millis()+speed; + } +}; + Pixel px(21); +Effect1 effect1(&px,5); +Effect2 effect2(&px,100); void setup() { @@ -10,106 +83,12 @@ void setup() { Serial.flush(); delay(1000); // wait for interface to flush - Serial.println("\n\nHomeSpan Pixel Example\n\n"); - + Serial.println("\n\nHomeSpan Pixel Example\n"); Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); - - int H=0; - color_t x[8]; - int s; - - while(1){ - - for(int i=0;i<8;i++) - x[i]=px.getColorHSV(H,i*3+79,i*2+5); - - px.setColors(x,8); - delay(20); - H=H+1; - if(H>=360) - H=0; - - s++; - if(s>8) - s=0; - } } // end of setup() void loop(){ -// px.setHSV(0,1.0,0.2,8); -// delay(1000); -// px.setHSV(120,1.0,0.2,4); -// delay(1000); -// px.setHSV(240,1.0,0.2,2); -// delay(1000); - - color_t RED=px.getColorRGB(50,0,0); - - color_t x0[8]={0,0,0,0,0,0,0,0}; - - color_t x1[8]={RED,RED,RED,RED,RED,RED,RED,RED}; - color_t x2[8]={0,0,0,0,0,0,RED,RED}; - color_t x3[8]={0,0,0,0,0,RED,0,0}; - color_t x4[8]={0,0,0,0,RED,0,0,0}; - color_t x5[8]={0,0,0,0,0,RED,0,0}; - color_t x6[8]={0,0,0,0,0,0,RED,RED}; - color_t x7[8]={RED,RED,RED,RED,RED,RED,RED,RED}; - - color_t x8[8]={RED,RED,RED,RED,RED,RED,RED,RED}; - color_t x9[8]={RED,0,0,0,RED,0,0,RED}; - color_t x10[8]={RED,0,0,0,RED,0,0,RED}; - color_t x11[8]={RED,0,0,0,RED,0,0,RED}; - color_t x12[8]={RED,0,0,0,0,0,0,RED}; - color_t x13[8]={RED,0,0,0,0,0,0,RED}; - color_t x14[8]={RED,0,0,0,0,0,0,RED}; - - - uint32_t d=2; - - while(1){ - px.setColors(x1,8); - delay(d); - px.setColors(x2,8); - delay(d); - px.setColors(x3,8); - delay(d); - px.setColors(x4,8); - delay(d); - px.setColors(x5,8); - delay(d); - px.setColors(x6,8); - delay(d); - px.setColors(x7,8); - delay(d); - px.setColors(x0,8); - delay(d); - px.setColors(x8,8); - delay(d); - px.setColors(x9,8); - delay(d); - px.setColors(x10,8); - delay(d); - px.setColors(x11,8); - delay(d); - px.setColors(x12,8); - delay(d); - px.setColors(x13,8); - delay(d); - px.setColors(x14,8); - delay(d); - px.setColors(x0,8); - delay(d); - } - -// x[0]=Pixel::getColorHSV(0,1,0.2); -// x[1]=px.getColorHSV(0,0.7,0.2); -// px.setColors(x,2); -// delay(1000); -// x[0]=px.getColorHSV(0,0.7,0.2); -// x[1]=px.getColorHSV(0,1,0.2); -// px.setColors(x,2); -// delay(1000); - -} // end of loop() + effect2.update(); +} From ee6c270de3cecc9767557ccc27bf023d48e85e10 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 8 Jan 2022 16:18:37 -0600 Subject: [PATCH 023/106] optimizing to allow for pulse train batching --- src/extras/Pixel.cpp | 74 ++++++++++++++++++++++++++++-------------- src/extras/Pixel.h | 13 ++++---- src/extras/RFControl.h | 2 +- src/extras/extras.ino | 16 +++++++++ 4 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index f1c190e..4c40f24 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -3,15 +3,24 @@ /////////////////// -Pixel::Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset){ +Pixel::Pixel(int pin, uint32_t nPixels){ + + rf=new RFControl(pin,false); // set clock to 1/80 usec + setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) + + if(nPixels==0) // must reserve at least enough memory for one pixel per transmission batch + nPixels=1; - H0=high0*80; // high0, low0, etc. are all in microseconds and must be multiplied by 80 to match 80MHz RFControl clock - L0=low0*80; - H1=high1*80; - L1=low1*80; - LR=lowReset*80; + nTrain=nPixels; +} - rf=new RFControl(pin,false); // set clock to 1/80 usec +/////////////////// + +void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset){ + + pattern[0]=RF_PULSE(high0*80+0.5,low0*80+0.5); + pattern[1]=RF_PULSE(high1*80+0.5,low1*80+0.5); + resetTime=lowReset; } /////////////////// @@ -20,12 +29,14 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ if(!*rf) return; - - rf->clear(); - for(int i=0;iphase(LR,0); // end-marker delay/reset - rf->start(); + + uint32_t *pulses = (uint32_t *) malloc(96); + + loadColor(getColorRGB(r,g,b),pulses); + rf->start(pulses,24,nPixels); // start pulse train and repeat for nPixels + delayMicroseconds(resetTime); + + free(pulses); } /////////////////// @@ -34,12 +45,24 @@ void Pixel::setColors(color_t *color, int nPixels){ if(!*rf) return; + + uint32_t x0,x1,x2; - rf->clear(); - for(int i=0;iphase(LR,0); // end-marker delay/reset - rf->start(); + x0=micros(); + + uint32_t *pulses = (uint32_t *) malloc(nTrain*96); + + for(int i=0;istart(pulses,24); // start pulse train + } + + x1=micros(); + Serial.printf("%d\n",x1-x0); + + while(1); + delayMicroseconds(resetTime); + free(pulses); } /////////////////// @@ -52,13 +75,14 @@ void Pixel::setHSV(float h, float s, float v, int nPixels){ /////////////////// -void Pixel::loadColor(color_t c){ - - for(int i=23;i>=0;i--){ - if((c>>i)&1) - rf->add(H1,L1); // 1-bit - else - rf->add(H0,L0); // 0-bit +void Pixel::loadColor(color_t c, uint32_t *p){ + + uint32_t count=24; + p+=23; + + while(count--){ + *p--=pattern[c&1]; + c=c>>1; } } diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 8de4220..01053f3 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -12,17 +12,18 @@ typedef uint32_t color_t; class Pixel { private: - uint32_t H0, L0; // High and Low times for a zero-pulse (in units of 1/80 microseconds) - uint32_t H1, L1; // High and Low times for a one-pulse (in units of 1/80 microseconds) - uint32_t LR; // Low time for a reset/end-of-data (in units of 1/80 microseconds) + uint32_t pattern[2]; // storage for zero-bit and one-bit pulses + uint32_t resetTime; // minimum time (in usec) between pulse trains + uint32_t nTrain; // number of Pixels to transmit per pulse train batch RFControl *rf; - void loadColor(color_t c); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits) + void loadColor(color_t c, uint32_t *p); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits of *p) public: - Pixel(int pin, float high0, float low0, float high1, float low1, float lowReset); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28); parameters are in MICROSECONDS! - Pixel(int pin) : Pixel(pin, 0.32, 0.88, 0.64, 0.56, 80.0) {}; // default parameters for SK68XXMINI-HS LEDs, though will likely work with many other variations as well + Pixel(int pin, uint32_t nPixels=1); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28), with OPTIONAL reserve of memory for nPixels + void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS + void setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels=1); // sets color of nPixels to RGB values (0-255) void setHSV(float h, float s, float v, int nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,100], v=[0,100] void setColors(color_t *color, int nPixels); // sets colors of nPixels from array of Colors diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index 8c17212..e07851d 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -41,5 +41,5 @@ class RFControl { // Helper macro for creating your own storage of uint32_t data array elements - used with first variation of start() above -#define RF_PULSE(highTicks,lowTicks) (1 << 15 | highTicks | lowTicks << 16) +#define RF_PULSE(highTicks,lowTicks) (1 << 15 | uint32_t(highTicks) | uint32_t(lowTicks) << 16) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 832396e..5045f1b 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -85,7 +85,23 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); + + px.setRGB(0,0,0,8); + px.setRGB(255,0,0,1); + delay(500); + px.setRGB(0,255,0,2); + delay(500); + px.setRGB(0,255,0,4); + delay(500); + px.setRGB(0,255,255,6); + delay(500); + px.setRGB(0,0,255,8); + delay(500); + +// while(1); + + } // end of setup() void loop(){ From aefa737675ab27df0fb767433fd84513ba6faa54 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 8 Jan 2022 17:28:43 -0600 Subject: [PATCH 024/106] Completed "optimization" Pixels now lets you reserve memory for pixels so that the call to start the RF transmission can be done for multiple pixels at once. However, gain is not as much as expected. May need to revisit if driving a large matrix of pixels is needed. --- src/extras/Pixel.cpp | 30 +++++++++++++++--------------- src/extras/extras.ino | 28 +++++++++++++--------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 4c40f24..ad990c5 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -30,7 +30,7 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ if(!*rf) return; - uint32_t *pulses = (uint32_t *) malloc(96); + uint32_t *pulses = (uint32_t *) malloc(24*sizeof(uint32_t)); loadColor(getColorRGB(r,g,b),pulses); rf->start(pulses,24,nPixels); // start pulse train and repeat for nPixels @@ -44,25 +44,25 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ void Pixel::setColors(color_t *color, int nPixels){ if(!*rf) + return; + + uint32_t *pulses = (uint32_t *) malloc(nTrain*24*sizeof(uint32_t)); + + if(!pulses){ + Serial.printf("*** ERROR: Not enough memory to reserve for %d Pixels per batch transmission\n",nTrain); return; - - uint32_t x0,x1,x2; - - x0=micros(); - - uint32_t *pulses = (uint32_t *) malloc(nTrain*96); - - for(int i=0;istart(pulses,24); // start pulse train } - x1=micros(); - Serial.printf("%d\n",x1-x0); + int i,j; + + for(i=0;istart(pulses,j*24); + } - while(1); - delayMicroseconds(resetTime); free(pulses); + delayMicroseconds(resetTime); } /////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 5045f1b..5343ba5 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -73,7 +73,7 @@ struct Effect2 { } }; -Pixel px(21); +Pixel px(21,8); Effect1 effect1(&px,5); Effect2 effect2(&px,100); @@ -87,21 +87,19 @@ void setup() { Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); px.setRGB(0,0,0,8); - px.setRGB(255,0,0,1); - delay(500); - px.setRGB(0,255,0,2); - delay(500); - px.setRGB(0,255,0,4); - delay(500); - px.setRGB(0,255,255,6); - delay(500); - px.setRGB(0,0,255,8); - delay(500); + for(int i=1;i<5;i++){ + px.setHSV(0,100,20,i); + delay(500); + } + + for(int i=5;i<8;i++){ + px.setHSV(60,100,30,i); + delay(500); + } -// while(1); - - - + px.setHSV(120,100,100,8); + delay(500); + } // end of setup() void loop(){ From 6f4cea1b4dfb334a2133ee945d55a438960263fd Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 10 Jan 2022 22:52:08 -0600 Subject: [PATCH 025/106] Updated version number to 1.4.3 Also updated Pixel example. --- Other Examples/Pixel/Pixel.ino | 15 ++++++++------- src/Settings.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index bdc9a1e..e289e45 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -36,19 +36,21 @@ /////////////////////////////// -struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel +struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - assume SAME color for every Pixel Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; Characteristic::Saturation S{0,true}; Characteristic::Brightness V{100,true}; - Pixel *pixel; + Pixel *pixel; + int nPixels; // number of Pixels in Strand (default=1) - Pixel_Light(int pin) : Service::LightBulb(){ + Pixel_Light(int pin, int nPixels=1) : Service::LightBulb(){ V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs - update(); // manually call update() to set pixel with restored initial values + this->nPixels=nPixels; // store number of Pixels in Strand + update(); // manually call update() to set pixel with restored initial values } boolean update() override { @@ -59,7 +61,7 @@ struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel float s=S.getNewVal(); // range = [0,100] float v=V.getNewVal(); // range = [0,100] - pixel->setHSV(h*p, s*p/100.0, v*p/100.0); // must scale down S and V from [0,100] to [0.0,1.0] + pixel->setHSV(h*p, s*p, v*p, nPixels); // sets all nPixels to HSV colors return(true); } @@ -85,8 +87,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_Light(8); // create Pixel LED attached to pin 8 - + new Pixel_Light(8); // create single Pixel attached to pin 8 } diff --git a/src/Settings.h b/src/Settings.h index f63c4b1..dfce918 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -36,7 +36,7 @@ #define HS_MAJOR 1 #define HS_MINOR 4 -#define HS_PATCH 2 +#define HS_PATCH 3 #define STRINGIFY(x) _STR(x) #define _STR(x) #x From 184cd3d82f216d07c4e0b6686ed7dd433e2cc948 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Tue, 11 Jan 2022 06:01:19 -0600 Subject: [PATCH 026/106] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index d97270b..4e359ab 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -349,7 +349,7 @@ new Service::LightBulb(); Note that Custom Characteristics must be created prior to calling `homeSpan.begin()` -> Advanced Tip: When presented with an unrecognized Custom Characteristic, Eve for HomeKit helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnits()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic. +> Advanced Tip: When presented with an unrecognized Custom Characteristic, Eve for HomeKit helpfully displays a *generic control* allowing you to interact with any Custom Characteristic you create in HomeSpan. However, since Eve does not recognize the Characteristic, it will only render the generic control if the Characteristic includes a **description** field, which you can add to any Characteristic using the `setDescription()` method described above. You may also want to use `setUnit()` and `setRange()` so that the Eve App displays a control with appropriate ranges for your Custom Characteristic. --- From 3e1c676f90031b805a8dc74cb2a07ae417bf90ec Mon Sep 17 00:00:00 2001 From: Gregg Date: Tue, 11 Jan 2022 06:41:17 -0600 Subject: [PATCH 027/106] Update Pixel.ino --- Other Examples/Pixel/Pixel.ino | 58 ++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index e289e45..fbfd5a1 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -34,9 +34,42 @@ #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class +CUSTOM_CHAR(Selector, 00000001-0001-0001-0001-46637266EA00, PR+PW+EV, UINT8, 1, 1, 5, false); // create Custom Characteristic to "select" special effects via Eve App + /////////////////////////////// -struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - assume SAME color for every Pixel +struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel + + Characteristic::On power{0,true}; + Characteristic::Hue H{0,true}; + Characteristic::Saturation S{0,true}; + Characteristic::Brightness V{100,true}; + Pixel *pixel; + + Pixel_Light(int pin) : Service::LightBulb(){ + + V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% + pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs + update(); // manually call update() to set pixel with restored initial values + } + + boolean update() override { + + int p=power.getNewVal(); + + float h=H.getNewVal(); // range = [0,360] + float s=S.getNewVal(); // range = [0,100] + float v=V.getNewVal(); // range = [0,100] + + pixel->setHSV(h*p, s*p, v*p); // sets pixel to HSV colors + + return(true); + } +}; + +/////////////////////////////// + +struct Pixel_Strand : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - allows for special effects controlled by custom characreristic Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; @@ -45,7 +78,7 @@ struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel Strand o Pixel *pixel; int nPixels; // number of Pixels in Strand (default=1) - Pixel_Light(int pin, int nPixels=1) : Service::LightBulb(){ + Pixel_Strand(int pin, int nPixels=1) : Service::LightBulb(){ V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs @@ -73,7 +106,7 @@ void setup() { Serial.begin(115200); - homeSpan.begin(Category::Lighting,"RGB Pixel"); + homeSpan.begin(Category::Lighting,"RGB Pixels"); new SpanAccessory(); new Service::AccessoryInformation(); @@ -89,6 +122,23 @@ void setup() { new Pixel_Light(8); // create single Pixel attached to pin 8 + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Name("Pixel Strand"); + new Characteristic::Manufacturer("HomeSpan"); + new Characteristic::SerialNumber("123-ABC"); + new Characteristic::Model("8-LED NeoPixel"); + new Characteristic::FirmwareRevision("1.0"); + new Characteristic::Identify(); + + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); + + new Pixel_Strand(7,8); // create single Pixel attached to pin 8 + + (new Characteristic::Selector())->setUnit("")->setDescription("Color Effect")->setRange(1,5,1); + + } /////////////////////////////// @@ -96,3 +146,5 @@ void setup() { void loop() { homeSpan.poll(); } + +/////////////////////////////// From 46d7ade046bcfb6c5d8717307f64530a03f01d44 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 16 Jan 2022 14:16:52 -0600 Subject: [PATCH 028/106] Re-doing Pixel class once again This time using customized interrupts to fill RMT memory on-the-fly. * Added getChannel() to RFControl * Add 3rd, optional, boolean argument to RFControl(int pin, bool refTick, bool defaultDrive) to that RMT can be initialized but without the default driver (allows for use of custom interrupt code instead) --- src/extras/RFControl.cpp | 6 ++- src/extras/RFControl.h | 5 ++- src/extras/extras.ino | 79 +++++++++++++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index b87bb0b..7d1a78f 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -3,7 +3,7 @@ /////////////////// -RFControl::RFControl(uint8_t pin, boolean refClock){ +RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){ #ifdef CONFIG_IDF_TARGET_ESP32C3 if(nChannels==RMT_CHANNEL_MAX/2){ @@ -29,7 +29,9 @@ RFControl::RFControl(uint8_t pin, boolean refClock){ config->tx_config.loop_en=false; rmt_config(config); - rmt_driver_install(config->channel,0,0); + + if(installDriver) + rmt_driver_install(config->channel,0,0); // If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index e07851d..91c86d4 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -21,7 +21,7 @@ class RFControl { static uint8_t nChannels; public: - RFControl(uint8_t pin, boolean refClock=true); // creates transmitter on pin, using 1-MHz Ref Tick clock + RFControl(uint8_t pin, boolean refClock=true, boolean installDriver=true); // creates transmitter on pin, using 1-MHz Ref Tick clock void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long @@ -32,7 +32,8 @@ class RFControl { void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 void disableCarrier(){enableCarrier(0);} // disables carrier wave - int getPin(){return(config?config->gpio_num:-1);} // returns the pin number + int getPin(){return(config?config->gpio_num:-1);} // returns the pin number + rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(config); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 5343ba5..226fad2 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -73,9 +73,19 @@ struct Effect2 { } }; -Pixel px(21,8); -Effect1 effect1(&px,5); -Effect2 effect2(&px,100); +//Pixel px(21,8); +//Effect1 effect1(&px,5); +//Effect2 effect2(&px,100); + +#ifdef CONFIG_IDF_TARGET_ESP32C3 + #define PIN 7 + #define RMT_MEM_SIZE 48 +#else + #define PIN 13 + #define RMT_MEM_SIZE 64 +#endif + +volatile uint32_t data[10]; void setup() { @@ -84,25 +94,60 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Pixel Example\n"); - Serial.printf("PX on Pin=%d check: %s\n",px.getPin(),px?"OKAY":"BAD"); - px.setRGB(0,0,0,8); - for(int i=1;i<5;i++){ - px.setHSV(0,100,20,i); - delay(500); - } + RFControl rf(PIN,true,false); + rmt_set_clk_div(rf.getChannel(),100); // set clock divider + + rmt_isr_register(eot,(void *)data,0,NULL); + rmt_set_tx_intr_en(rf.getChannel(),true); + rmt_set_tx_thr_intr_en(rf.getChannel(),true,32); + + RMTMEM.chan[0].data32[0].val=5000<<16 | 5000 | 1<<15; + RMTMEM.chan[0].data32[1].val=5000<<16 | 5000 | 1<<15; + + for(int i=2;i Date: Sun, 16 Jan 2022 23:14:12 -0600 Subject: [PATCH 029/106] check in progress --- src/extras/Pixel.cpp | 101 ++++++++++++++++++++++++++++++--------- src/extras/Pixel.h | 32 ++++++++++++- src/extras/RFControl.cpp | 1 - src/extras/extras.ino | 17 +++++++ 4 files changed, 126 insertions(+), 25 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index ad990c5..6b32b5d 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -5,13 +5,13 @@ Pixel::Pixel(int pin, uint32_t nPixels){ - rf=new RFControl(pin,false); // set clock to 1/80 usec + rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) - - if(nPixels==0) // must reserve at least enough memory for one pixel per transmission batch - nPixels=1; - nTrain=nPixels; + rmt_isr_register(isrHandler,(void *)this,0,NULL); // end-transmission interrupt automatically enabled by rmt_tx_start + rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // need to also enable threshold interrupt + channelNum=rf->getChannel(); + } /////////////////// @@ -41,28 +41,45 @@ void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ /////////////////// -void Pixel::setColors(color_t *color, int nPixels){ +void Pixel::setColors(const uint32_t *data, uint32_t nPixels){ - if(!*rf) - return; - - uint32_t *pulses = (uint32_t *) malloc(nTrain*24*sizeof(uint32_t)); - - if(!pulses){ - Serial.printf("*** ERROR: Not enough memory to reserve for %d Pixels per batch transmission\n",nTrain); + if(!*rf || nPixels==0) return; - } - int i,j; - - for(i=0;istart(pulses,j*24); - } + status.nPixels=nPixels; + status.data=data; + status.iMem=0; + status.iBit=24; + status.started=true; + status.txEndMask=TxEndMask(channelNum); - free(pulses); - delayMicroseconds(resetTime); + loadData(); // load first 2 bytes + loadData(); + + rmt_tx_start(rf->getChannel(),true); + + while(status.started); + + + return; + +// uint32_t *pulses = (uint32_t *) malloc(nTrain*24*sizeof(uint32_t)); +// +// if(!pulses){ +// Serial.printf("*** ERROR: Not enough memory to reserve for %d Pixels per batch transmission\n",nTrain); +// return; +// } +// +// int i,j; +// +// for(i=0;istart(pulses,j*24); +// } +// +// free(pulses); +// delayMicroseconds(resetTime); } /////////////////// @@ -101,3 +118,41 @@ color_t Pixel::getColorHSV(float h, float s, float v){ } /////////////////// + +void Pixel::loadData(){ + + if(status.nPixels==0){ + RMTMEM.chan[channelNum].data32[status.iMem].val=0; + return; + } + + for(int i=0;i<8;i++) + RMTMEM.chan[channelNum].data32[status.iMem++].val=pattern[(*status.data>>(--status.iBit))&1]; + + if(status.iBit==0){ + status.iBit=24; + status.data++; + status.nPixels--; + } + status.iMem%=memSize; +} + +/////////////////// + +void Pixel::isrHandler(void *arg){ + + Pixel *pix=(Pixel *)arg; + + if(RMT.int_st.val & status.txEndMask){ + RMT.int_clr.val=~0; + status.started=false; + return; + } + + RMT.int_clr.val=~0; + pix->loadData(); +} + +/////////////////// + +volatile pixel_status_t Pixel::status; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 01053f3..9b07509 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -10,13 +10,41 @@ typedef uint32_t color_t; +struct pixel_status_t { + int nPixels; + const uint32_t *data; + int iBit; + int iMem; + boolean started; + uint32_t txEndMask; // mask for end-of-transmission interrupt +}; + class Pixel { private: uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains + int channelNum; // channel number + uint32_t nTrain; // number of Pixels to transmit per pulse train batch + + #if defined(CONFIG_IDF_TARGET_ESP32) + const int memSize=64; + #define TxEndMask(chNum) (1<<(chNum*3)) + #elif defined(CONFIG_IDF_TARGET_ESP32S2) + const int memSize=48; + #define TxEndMask(chNum) (1<<(chNum*3)) + #elif defined(CONFIG_IDF_TARGET_ESP32C3) + const int memSize=48; + #define TxEndMask(chNum) (1<getPin());} // returns pixel pin if valid, else returns -1 static color_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values static color_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values + + void loadData(); operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index 7d1a78f..182cefb 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -128,7 +128,6 @@ void RFControl::enableCarrier(uint32_t freq, float duty){ return; } -// Serial.printf("%d %g %d %d\n",freq,period,highTime,lowTime); rmt_set_tx_carrier(config->channel,true,highTime,lowTime,RMT_CARRIER_LEVEL_HIGH); } else { rmt_set_tx_carrier(config->channel,false,0,0,RMT_CARRIER_LEVEL_HIGH); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 226fad2..a944271 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -95,6 +95,23 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); + Pixel px(1); + + uint32_t colors[20]; + + colors[0]=px.getColorRGB(40,40,0); + colors[1]=px.getColorRGB(40,40,0); + colors[2]=px.getColorRGB(40,0,0); + colors[3]=px.getColorRGB(0,0,40); + colors[4]=px.getColorRGB(40,0,0); + colors[5]=px.getColorRGB(40,0,0); + colors[6]=px.getColorRGB(40,0,0); + colors[7]=px.getColorRGB(0,0,40); + + px.setColors(colors,8); + Serial.println("\n\nDone\n\n"); + while(1); + RFControl rf(PIN,true,false); rmt_set_clk_div(rf.getChannel(),100); // set clock divider From 40c05bd53fee449e543052fe979c0121ae0a9f8d Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 16 Jan 2022 23:24:54 -0600 Subject: [PATCH 030/106] some more cleanup Next up: Move loadData into isrHandler, if possible. To Do: redo single-color methods --- src/extras/Pixel.cpp | 11 ++++++----- src/extras/Pixel.h | 10 ++++------ src/extras/extras.ino | 6 +++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 6b32b5d..dbf5489 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -10,7 +10,9 @@ Pixel::Pixel(int pin, uint32_t nPixels){ rmt_isr_register(isrHandler,(void *)this,0,NULL); // end-transmission interrupt automatically enabled by rmt_tx_start rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // need to also enable threshold interrupt + channelNum=rf->getChannel(); + txEndMask=TxEndMask(channelNum); } @@ -51,7 +53,6 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels){ status.iMem=0; status.iBit=24; status.started=true; - status.txEndMask=TxEndMask(channelNum); loadData(); // load first 2 bytes loadData(); @@ -92,7 +93,7 @@ void Pixel::setHSV(float h, float s, float v, int nPixels){ /////////////////// -void Pixel::loadColor(color_t c, uint32_t *p){ +void Pixel::loadColor(uint32_t c, uint32_t *p){ uint32_t count=24; p+=23; @@ -105,13 +106,13 @@ void Pixel::loadColor(color_t c, uint32_t *p){ /////////////////// -color_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b){ +uint32_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b){ return(g<<16 | r<<8 | b); } /////////////////// -color_t Pixel::getColorHSV(float h, float s, float v){ +uint32_t Pixel::getColorHSV(float h, float s, float v){ float r,g,b; LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); return(getColorRGB(r*255,g*255,b*255)); @@ -143,7 +144,7 @@ void Pixel::isrHandler(void *arg){ Pixel *pix=(Pixel *)arg; - if(RMT.int_st.val & status.txEndMask){ + if(RMT.int_st.val & pix->txEndMask){ RMT.int_clr.val=~0; status.started=false; return; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 9b07509..30c4498 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -8,15 +8,12 @@ #include "RFControl.h" #include "PwmPin.h" -typedef uint32_t color_t; - struct pixel_status_t { int nPixels; const uint32_t *data; int iBit; int iMem; boolean started; - uint32_t txEndMask; // mask for end-of-transmission interrupt }; class Pixel { @@ -24,6 +21,7 @@ class Pixel { uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains int channelNum; // channel number + uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t nTrain; // number of Pixels to transmit per pulse train batch @@ -45,7 +43,7 @@ class Pixel { volatile static pixel_status_t status; static void isrHandler(void *arg); // interrupt handler - void loadColor(color_t c, uint32_t *p); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits of *p) + void loadColor(uint32_t c, uint32_t *p); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits of *p) public: Pixel(int pin, uint32_t nPixels=1); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28), with OPTIONAL reserve of memory for nPixels @@ -57,8 +55,8 @@ class Pixel { void setColors(const uint32_t *data, uint32_t nPixels); // sets colors of nPixels from array of Colors int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 - static color_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values - static color_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values + static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values + static uint32_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values void loadData(); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index a944271..1a01f68 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -6,7 +6,7 @@ struct Effect1 { Pixel *px; int H=0; - color_t x[8]; + uint32_t x[8]; uint32_t alarmTime=0; uint32_t speed; @@ -35,7 +35,7 @@ struct Effect2 { int phase=0; int dir=1; int H=0; - color_t x[8]; + uint32_t x[8]; uint32_t alarmTime=0; uint32_t speed; @@ -106,7 +106,7 @@ void setup() { colors[4]=px.getColorRGB(40,0,0); colors[5]=px.getColorRGB(40,0,0); colors[6]=px.getColorRGB(40,0,0); - colors[7]=px.getColorRGB(0,0,40); + colors[7]=px.getColorRGB(0,40,0); px.setColors(colors,8); Serial.println("\n\nDone\n\n"); From 79c028c0570c2bcd0b8772e48011fe30ee3d07c1 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 17 Jan 2022 07:16:31 -0600 Subject: [PATCH 031/106] moved loadData into interrupt --- src/extras/Pixel.cpp | 54 +++++++++++++++++++++---------------------- src/extras/Pixel.h | 4 +--- src/extras/extras.ino | 12 +++++----- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index dbf5489..5c77b61 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -8,11 +8,11 @@ Pixel::Pixel(int pin, uint32_t nPixels){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) - rmt_isr_register(isrHandler,(void *)this,0,NULL); // end-transmission interrupt automatically enabled by rmt_tx_start - rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // need to also enable threshold interrupt + rmt_isr_register(loadData,(void *)this,0,NULL); // set custom interrupt handler + rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt (note end-transmission interrupt automatically enabled by rmt_tx_start) - channelNum=rf->getChannel(); - txEndMask=TxEndMask(channelNum); + channelNum=rf->getChannel(); // save integer form of channel number + txEndMask=TxEndMask(channelNum); // create bit mask for end-of-transmission interrupt specific to this channel } @@ -54,8 +54,11 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels){ status.iBit=24; status.started=true; - loadData(); // load first 2 bytes - loadData(); +// loadData(); // load first 2 bytes +// loadData(); + + loadData(this); + loadData(this); rmt_tx_start(rf->getChannel(),true); @@ -120,27 +123,7 @@ uint32_t Pixel::getColorHSV(float h, float s, float v){ /////////////////// -void Pixel::loadData(){ - - if(status.nPixels==0){ - RMTMEM.chan[channelNum].data32[status.iMem].val=0; - return; - } - - for(int i=0;i<8;i++) - RMTMEM.chan[channelNum].data32[status.iMem++].val=pattern[(*status.data>>(--status.iBit))&1]; - - if(status.iBit==0){ - status.iBit=24; - status.data++; - status.nPixels--; - } - status.iMem%=memSize; -} - -/////////////////// - -void Pixel::isrHandler(void *arg){ +void Pixel::loadData(void *arg){ Pixel *pix=(Pixel *)arg; @@ -151,7 +134,22 @@ void Pixel::isrHandler(void *arg){ } RMT.int_clr.val=~0; - pix->loadData(); + + if(status.nPixels==0){ + RMTMEM.chan[pix->channelNum].data32[status.iMem].val=0; + return; + } + + for(int i=0;i<8;i++) + RMTMEM.chan[pix->channelNum].data32[status.iMem++].val=pix->pattern[(*status.data>>(--status.iBit))&1]; + + if(status.iBit==0){ + status.iBit=24; + status.data++; + status.nPixels--; + } + + status.iMem%=pix->memSize; } /////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 30c4498..df19221 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -42,7 +42,7 @@ class Pixel { volatile static pixel_status_t status; - static void isrHandler(void *arg); // interrupt handler + static void loadData(void *arg); // interrupt handler void loadColor(uint32_t c, uint32_t *p); // creates pulse pattern for pixel color (encoded as RGB in low 24-bits of *p) public: @@ -58,8 +58,6 @@ class Pixel { static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values static uint32_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values - void loadData(); - operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); } diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 1a01f68..66ccd09 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -99,13 +99,13 @@ void setup() { uint32_t colors[20]; - colors[0]=px.getColorRGB(40,40,0); - colors[1]=px.getColorRGB(40,40,0); + colors[0]=px.getColorRGB(40,0,0); + colors[1]=px.getColorRGB(40,0,0); colors[2]=px.getColorRGB(40,0,0); - colors[3]=px.getColorRGB(0,0,40); - colors[4]=px.getColorRGB(40,0,0); - colors[5]=px.getColorRGB(40,0,0); - colors[6]=px.getColorRGB(40,0,0); + colors[3]=px.getColorRGB(40,40,0); + colors[4]=px.getColorRGB(40,40,0); + colors[5]=px.getColorRGB(40,40,0); + colors[6]=px.getColorRGB(0,40,0); colors[7]=px.getColorRGB(0,40,0); px.setColors(colors,8); From 5f513c4c3471ab509420faf4dbbd55141c19cb71 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 17 Jan 2022 08:25:26 -0600 Subject: [PATCH 032/106] updated --- src/extras/Pixel.cpp | 87 ++++++++++++------------------------------- src/extras/Pixel.h | 61 +++++++++++++++--------------- src/extras/extras.ino | 4 +- 3 files changed, 55 insertions(+), 97 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 5c77b61..d64bde0 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -3,7 +3,7 @@ /////////////////// -Pixel::Pixel(int pin, uint32_t nPixels){ +Pixel::Pixel(int pin){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) @@ -11,8 +11,7 @@ Pixel::Pixel(int pin, uint32_t nPixels){ rmt_isr_register(loadData,(void *)this,0,NULL); // set custom interrupt handler rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt (note end-transmission interrupt automatically enabled by rmt_tx_start) - channelNum=rf->getChannel(); // save integer form of channel number - txEndMask=TxEndMask(channelNum); // create bit mask for end-of-transmission interrupt specific to this channel + txEndMask=TxEndMask(rf->getChannel()); // create bit mask for end-of-transmission interrupt specific to this channel } @@ -27,23 +26,26 @@ void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t /////////////////// -void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, int nPixels){ +void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, uint32_t nPixels){ - if(!*rf) + if(!*rf || nPixels==0) return; - uint32_t *pulses = (uint32_t *) malloc(24*sizeof(uint32_t)); - - loadColor(getColorRGB(r,g,b),pulses); - rf->start(pulses,24,nPixels); // start pulse train and repeat for nPixels - delayMicroseconds(resetTime); - - free(pulses); + uint32_t data=getColorRGB(r,g,b); + setColors(&data,nPixels,false); } /////////////////// -void Pixel::setColors(const uint32_t *data, uint32_t nPixels){ +void Pixel::setHSV(float h, float s, float v, uint32_t nPixels){ + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + setRGB(r*255,g*255,b*255,nPixels); +} + +/////////////////// + +void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor){ if(!*rf || nPixels==0) return; @@ -53,58 +55,15 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels){ status.iMem=0; status.iBit=24; status.started=true; + this->multiColor=multiColor; -// loadData(); // load first 2 bytes -// loadData(); - - loadData(this); + loadData(this); // load first two bytes of data to get started loadData(this); rmt_tx_start(rf->getChannel(),true); - while(status.started); - - - return; - -// uint32_t *pulses = (uint32_t *) malloc(nTrain*24*sizeof(uint32_t)); -// -// if(!pulses){ -// Serial.printf("*** ERROR: Not enough memory to reserve for %d Pixels per batch transmission\n",nTrain); -// return; -// } -// -// int i,j; -// -// for(i=0;istart(pulses,j*24); -// } -// -// free(pulses); -// delayMicroseconds(resetTime); -} - -/////////////////// - -void Pixel::setHSV(float h, float s, float v, int nPixels){ - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - setRGB(r*255,g*255,b*255,nPixels); -} - -/////////////////// - -void Pixel::loadColor(uint32_t c, uint32_t *p){ - - uint32_t count=24; - p+=23; - - while(count--){ - *p--=pattern[c&1]; - c=c>>1; - } + while(status.started); // wait for transmission to be complete + delayMicroseconds(resetTime); // end-of-marker delay } /////////////////// @@ -136,16 +95,16 @@ void Pixel::loadData(void *arg){ RMT.int_clr.val=~0; if(status.nPixels==0){ - RMTMEM.chan[pix->channelNum].data32[status.iMem].val=0; + RMTMEM.chan[pix->rf->getChannel()].data32[status.iMem].val=0; return; } for(int i=0;i<8;i++) - RMTMEM.chan[pix->channelNum].data32[status.iMem++].val=pix->pattern[(*status.data>>(--status.iBit))&1]; + RMTMEM.chan[pix->rf->getChannel()].data32[status.iMem++].val=pix->pattern[(*status.data>>(--status.iBit))&1]; if(status.iBit==0){ status.iBit=24; - status.data++; + status.data+=pix->multiColor; status.nPixels--; } @@ -154,4 +113,4 @@ void Pixel::loadData(void *arg){ /////////////////// -volatile pixel_status_t Pixel::status; +volatile Pixel::pixel_status_t Pixel::status; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index df19221..8dbb222 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -8,51 +8,48 @@ #include "RFControl.h" #include "PwmPin.h" -struct pixel_status_t { - int nPixels; - const uint32_t *data; - int iBit; - int iMem; - boolean started; -}; - class Pixel { + + struct pixel_status_t { + int nPixels; + const uint32_t *data; + int iBit; + int iMem; + boolean started; + }; + private: + RFControl *rf; // Pixel utilizes RFControl uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains - int channelNum; // channel number uint32_t txEndMask; // mask for end-of-transmission interrupt + boolean multiColor; // flag to indicate array contains multiple colors (don't just repeat first color for nPixels) - uint32_t nTrain; // number of Pixels to transmit per pulse train batch - - #if defined(CONFIG_IDF_TARGET_ESP32) - const int memSize=64; - #define TxEndMask(chNum) (1<<(chNum*3)) - #elif defined(CONFIG_IDF_TARGET_ESP32S2) - const int memSize=48; - #define TxEndMask(chNum) (1<<(chNum*3)) - #elif defined(CONFIG_IDF_TARGET_ESP32C3) - const int memSize=48; - #define TxEndMask(chNum) (1<getPin());} // returns pixel pin if valid, else returns -1 static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 66ccd09..d447e49 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -95,11 +95,12 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); +// Pixel px0(10); Pixel px(1); uint32_t colors[20]; - colors[0]=px.getColorRGB(40,0,0); + colors[0]=px.getColorRGB(0,40,0); colors[1]=px.getColorRGB(40,0,0); colors[2]=px.getColorRGB(40,0,0); colors[3]=px.getColorRGB(40,40,0); @@ -109,6 +110,7 @@ void setup() { colors[7]=px.getColorRGB(0,40,0); px.setColors(colors,8); +// px.setHSV(240,80,40,3); Serial.println("\n\nDone\n\n"); while(1); From 89f0d0dd944e7edbb722a021bd66cb136e4c3359 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 17 Jan 2022 11:30:55 -0600 Subject: [PATCH 033/106] verified works on C3 when either channel 0 or 1 is used --- src/extras/Pixel.cpp | 22 +++++++++++++--------- src/extras/Pixel.h | 3 ++- src/extras/extras.ino | 9 +++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index d64bde0..6699b4d 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -8,11 +8,13 @@ Pixel::Pixel(int pin){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) - rmt_isr_register(loadData,(void *)this,0,NULL); // set custom interrupt handler + rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt (note end-transmission interrupt automatically enabled by rmt_tx_start) txEndMask=TxEndMask(rf->getChannel()); // create bit mask for end-of-transmission interrupt specific to this channel + Serial.printf("%d %d %08X\n",rf->getChannel(),txEndMask,RMT.int_ena.val); + } /////////////////// @@ -55,11 +57,15 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor status.iMem=0; status.iBit=24; status.started=true; - this->multiColor=multiColor; + status.px=this; + status.multiColor=multiColor; loadData(this); // load first two bytes of data to get started loadData(this); + for(int i=0;imemSize;i++) + Serial.printf("%d %08X\n",i,RMTMEM.chan[rf->getChannel()].data32[i].val); + rmt_tx_start(rf->getChannel(),true); while(status.started); // wait for transmission to be complete @@ -84,9 +90,7 @@ uint32_t Pixel::getColorHSV(float h, float s, float v){ void Pixel::loadData(void *arg){ - Pixel *pix=(Pixel *)arg; - - if(RMT.int_st.val & pix->txEndMask){ + if((RMT.int_st.val & status.px->txEndMask) >0){ RMT.int_clr.val=~0; status.started=false; return; @@ -95,20 +99,20 @@ void Pixel::loadData(void *arg){ RMT.int_clr.val=~0; if(status.nPixels==0){ - RMTMEM.chan[pix->rf->getChannel()].data32[status.iMem].val=0; + RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem].val=0; return; } for(int i=0;i<8;i++) - RMTMEM.chan[pix->rf->getChannel()].data32[status.iMem++].val=pix->pattern[(*status.data>>(--status.iBit))&1]; + RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(*status.data>>(--status.iBit))&1]; if(status.iBit==0){ status.iBit=24; - status.data+=pix->multiColor; + status.data+=status.multiColor; status.nPixels--; } - status.iMem%=pix->memSize; + status.iMem%=status.px->memSize; } /////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 8dbb222..cdb6d21 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -16,6 +16,8 @@ class Pixel { int iBit; int iMem; boolean started; + Pixel *px; + boolean multiColor; }; private: @@ -23,7 +25,6 @@ class Pixel { uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt - boolean multiColor; // flag to indicate array contains multiple colors (don't just repeat first color for nPixels) #if defined(CONFIG_IDF_TARGET_ESP32) const int memSize=64; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index d447e49..f5b0f56 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -95,22 +95,23 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); -// Pixel px0(10); + Pixel px0(10); Pixel px(1); + Pixel px1(0); uint32_t colors[20]; - colors[0]=px.getColorRGB(0,40,0); + colors[0]=px.getColorRGB(40,0,0); colors[1]=px.getColorRGB(40,0,0); colors[2]=px.getColorRGB(40,0,0); colors[3]=px.getColorRGB(40,40,0); colors[4]=px.getColorRGB(40,40,0); colors[5]=px.getColorRGB(40,40,0); colors[6]=px.getColorRGB(0,40,0); - colors[7]=px.getColorRGB(0,40,0); + colors[7]=px.getColorRGB(40,0,0); px.setColors(colors,8); -// px.setHSV(240,80,40,3); + px.setHSV(0,100,40,3); Serial.println("\n\nDone\n\n"); while(1); From b6d21fd2c0691f1b3871d70087171ea32c7580a2 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 17 Jan 2022 12:36:02 -0600 Subject: [PATCH 034/106] streamlined identification of interrupt masks and memory size --- src/extras/Pixel.cpp | 9 ++++++--- src/extras/Pixel.h | 15 ++------------- src/extras/extras.ino | 4 ++-- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 6699b4d..22998b6 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -8,10 +8,13 @@ Pixel::Pixel(int pin){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) - rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler - rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt (note end-transmission interrupt automatically enabled by rmt_tx_start) + rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler + rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt to trigger every 8 pulses - txEndMask=TxEndMask(rf->getChannel()); // create bit mask for end-of-transmission interrupt specific to this channel + rmt_set_tx_intr_en(rf->getChannel(),false); // disable end-of-transmission interrupt + txEndMask=RMT.int_ena.val; // save interrupt enable vector + rmt_set_tx_intr_en(rf->getChannel(),true); // enable end-of-transmission interrupt + txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel Serial.printf("%d %d %08X\n",rf->getChannel(),txEndMask,RMT.int_ena.val); diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index cdb6d21..414554b 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -25,20 +25,9 @@ class Pixel { uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt - - #if defined(CONFIG_IDF_TARGET_ESP32) - const int memSize=64; - #define TxEndMask(chNum) (1<<(chNum*3)) - #elif defined(CONFIG_IDF_TARGET_ESP32S2) - const int memSize=48; - #define TxEndMask(chNum) (1<<(chNum*3)) - #elif defined(CONFIG_IDF_TARGET_ESP32C3) - const int memSize=48; - #define TxEndMask(chNum) (1< Date: Mon, 17 Jan 2022 12:52:09 -0600 Subject: [PATCH 035/106] verified functionality on ESP32 original --- src/extras/Pixel.cpp | 8 +------- src/extras/extras.ino | 10 +++++----- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 22998b6..64febd5 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -15,9 +15,6 @@ Pixel::Pixel(int pin){ txEndMask=RMT.int_ena.val; // save interrupt enable vector rmt_set_tx_intr_en(rf->getChannel(),true); // enable end-of-transmission interrupt txEndMask^=RMT.int_ena.val; // find bit that flipped and save as end-of-transmission mask for this channel - - Serial.printf("%d %d %08X\n",rf->getChannel(),txEndMask,RMT.int_ena.val); - } /////////////////// @@ -66,9 +63,6 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor loadData(this); // load first two bytes of data to get started loadData(this); - for(int i=0;imemSize;i++) - Serial.printf("%d %08X\n",i,RMTMEM.chan[rf->getChannel()].data32[i].val); - rmt_tx_start(rf->getChannel(),true); while(status.started); // wait for transmission to be complete @@ -93,7 +87,7 @@ uint32_t Pixel::getColorHSV(float h, float s, float v){ void Pixel::loadData(void *arg){ - if((RMT.int_st.val & status.px->txEndMask) >0){ + if(RMT.int_st.val & status.px->txEndMask){ RMT.int_clr.val=~0; status.started=false; return; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 4cde163..49f07fc 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -95,9 +95,9 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); -// Pixel px0(10); - Pixel px(1); -// Pixel px1(0); + Pixel px0(22); + Pixel px1(3); + Pixel px(21); uint32_t colors[20]; @@ -108,10 +108,10 @@ void setup() { colors[4]=px.getColorRGB(40,40,0); colors[5]=px.getColorRGB(40,40,0); colors[6]=px.getColorRGB(0,40,0); - colors[7]=px.getColorRGB(40,0,0); + colors[7]=px.getColorRGB(40,0,40); px.setColors(colors,8); - px.setHSV(0,100,40,3); + px.setHSV(240,100,40,2); Serial.println("\n\nDone\n\n"); while(1); From 4419a91bef7fbe5472d4170d8b2c1dd97fda274f Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 17 Jan 2022 13:15:24 -0600 Subject: [PATCH 036/106] verified functionality on ESP32-S2 --- src/extras/Pixel.cpp | 3 ++ src/extras/extras.ino | 100 +++++++----------------------------------- 2 files changed, 20 insertions(+), 83 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 64febd5..917b7c2 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -6,6 +6,9 @@ Pixel::Pixel(int pin){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver + if(!rf) + return; + setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 49f07fc..4c7cecd 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -73,19 +73,24 @@ struct Effect2 { } }; -//Pixel px(21,8); -//Effect1 effect1(&px,5); -//Effect2 effect2(&px,100); - -#ifdef CONFIG_IDF_TARGET_ESP32C3 - #define PIN 7 - #define RMT_MEM_SIZE 48 -#else - #define PIN 13 - #define RMT_MEM_SIZE 64 +#if defined(CONFIG_IDF_TARGET_ESP32C3) + #define PIN 1 +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + #define PIN 1 +#else + #define PIN 21 #endif -volatile uint32_t data[10]; +Pixel px2(2); +Pixel px3(3); +Pixel px4(4); +Pixel px(PIN); +Pixel px5(5); +Pixel px6(6); +Pixel px7(7); + +Effect1 effect1(&px,5); +Effect2 effect2(&px,100); void setup() { @@ -94,80 +99,9 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Pixel Example\n"); - - Pixel px0(22); - Pixel px1(3); - Pixel px(21); - - uint32_t colors[20]; - - colors[0]=px.getColorRGB(40,0,0); - colors[1]=px.getColorRGB(40,0,0); - colors[2]=px.getColorRGB(40,0,0); - colors[3]=px.getColorRGB(40,40,0); - colors[4]=px.getColorRGB(40,40,0); - colors[5]=px.getColorRGB(40,40,0); - colors[6]=px.getColorRGB(0,40,0); - colors[7]=px.getColorRGB(40,0,40); - - px.setColors(colors,8); - px.setHSV(240,100,40,2); - Serial.println("\n\nDone\n\n"); - while(1); - - RFControl rf(PIN,true,false); - rmt_set_clk_div(rf.getChannel(),100); // set clock divider - - rmt_isr_register(eot,(void *)data,0,NULL); - rmt_set_tx_intr_en(rf.getChannel(),true); - rmt_set_tx_thr_intr_en(rf.getChannel(),true,32); - - RMTMEM.chan[0].data32[0].val=5000<<16 | 5000 | 1<<15; - RMTMEM.chan[0].data32[1].val=5000<<16 | 5000 | 1<<15; - - for(int i=2;i Date: Thu, 20 Jan 2022 21:01:31 -0600 Subject: [PATCH 037/106] corrected an issue in loadData ISR that would cause a hang Also updated Pixel example with Knight Rider Effect. Testing on C3 seems to be working. Must test on S2 and ESP32 next. --- Other Examples/Pixel/Pixel.ino | 56 ++++++++++++++++++++++------------ src/extras/Pixel.cpp | 12 +++++--- src/extras/Pixel.h | 1 + src/extras/extras.ino | 2 +- 4 files changed, 46 insertions(+), 25 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index fbfd5a1..d263fa9 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -34,8 +34,6 @@ #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class -CUSTOM_CHAR(Selector, 00000001-0001-0001-0001-46637266EA00, PR+PW+EV, UINT8, 1, 1, 5, false); // create Custom Characteristic to "select" special effects via Eve App - /////////////////////////////// struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel @@ -69,35 +67,56 @@ struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel /////////////////////////////// -struct Pixel_Strand : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - allows for special effects controlled by custom characreristic +struct Pixel_KnightRider : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - Knight Rider Effect Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; - Characteristic::Saturation S{0,true}; - Characteristic::Brightness V{100,true}; + Characteristic::Saturation S{100,true}; Pixel *pixel; - int nPixels; // number of Pixels in Strand (default=1) + int nPixels; + uint32_t *colors; + int phase=0; + int dir=1; + uint32_t alarmTime=0; - Pixel_Strand(int pin, int nPixels=1) : Service::LightBulb(){ + Pixel_KnightRider(int pin, int nPixels) : Service::LightBulb(){ - V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs this->nPixels=nPixels; // store number of Pixels in Strand - update(); // manually call update() to set pixel with restored initial values + + colors=(uint32_t *)calloc(2*nPixels-1,sizeof(uint32_t)); // storage for dynamic pixel pattern + update(); // manually call update() to set pixelk pattern with restored initial values } boolean update() override { - int p=power.getNewVal(); + if(!power.getNewVal()){ + pixel->setRGB(0,0,0,8); + } else { + float level=100; + for(int i=0;igetColorHSV(H.getNewVal(),S.getNewVal(),level); + colors[nPixels-i-1]=colors[nPixels+i-1]; + } + } - float h=H.getNewVal(); // range = [0,360] - float s=S.getNewVal(); // range = [0,100] - float v=V.getNewVal(); // range = [0,100] - - pixel->setHSV(h*p, s*p, v*p, nPixels); // sets all nPixels to HSV colors - return(true); } + + void loop() override { + + if(millis()>alarmTime && power.getVal()){ + alarmTime=millis()+80; + pixel->setColors(colors+phase,nPixels); + if(phase==7) + dir=-1; + else if(phase==0) + dir=1; + phase+=dir; + } + + } + }; /////////////////////////////// @@ -134,10 +153,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_Strand(7,8); // create single Pixel attached to pin 8 - - (new Characteristic::Selector())->setUnit("")->setDescription("Color Effect")->setRange(1,5,1); - + new Pixel_KnightRider(1,8); // create 8-Pixel Strand with Knight-Rider Effect attached to pin 1 } diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 917b7c2..7ece3ab 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -12,7 +12,11 @@ Pixel::Pixel(int pin){ setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler - rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt to trigger every 8 pulses + + rmt_set_tx_thr_intr_en(rf->getChannel(),false,8); // disable threshold interrupt + txThrMask=RMT.int_ena.val; // save interrupt enable vector + rmt_set_tx_thr_intr_en(rf->getChannel(),true,8); // enable threshold interrupt to trigger every 8 pulses + txThrMask^=RMT.int_ena.val; // find bit that flipped and save as threshold mask for this channel rmt_set_tx_intr_en(rf->getChannel(),false); // disable end-of-transmission interrupt txEndMask=RMT.int_ena.val; // save interrupt enable vector @@ -88,15 +92,15 @@ uint32_t Pixel::getColorHSV(float h, float s, float v){ /////////////////// -void Pixel::loadData(void *arg){ +void IRAM_ATTR Pixel::loadData(void *arg){ if(RMT.int_st.val & status.px->txEndMask){ - RMT.int_clr.val=~0; + RMT.int_clr.val=status.px->txEndMask; status.started=false; return; } - RMT.int_clr.val=~0; + RMT.int_clr.val=status.px->txThrMask; // if loadData() is called and it is NOT because of an END interrupt (above) then must either be a pre-load, or a threshold trigger if(status.nPixels==0){ RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem].val=0; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 414554b..b2a05b4 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -25,6 +25,7 @@ class Pixel { uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt + uint32_t txThrMask; // mask for threshold interrupt const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 4c7cecd..e248795 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -82,9 +82,9 @@ struct Effect2 { #endif Pixel px2(2); +Pixel px(PIN); Pixel px3(3); Pixel px4(4); -Pixel px(PIN); Pixel px5(5); Pixel px6(6); Pixel px7(7); From 4af3a22764f6cbbfeeccd1dec43b504040315c7d Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 21 Jan 2022 21:56:51 -0600 Subject: [PATCH 038/106] Update logic so Pixel uses only ONE channel and shares across all instances --- src/extras/Pixel.cpp | 11 +++++++++-- src/extras/Pixel.h | 5 +++-- src/extras/RFControl.cpp | 23 ++++++++++------------- src/extras/extras.ino | 12 +++++++++--- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 7ece3ab..e26e4d0 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -4,10 +4,14 @@ /////////////////// Pixel::Pixel(int pin){ + + if(!rf) // RMT Channel not yet assigned + rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver - rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver - if(!rf) + if(!*rf) // if RMT Channel could NOT be assigned (no more channels) return; + + this->pin=pin; setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) @@ -67,6 +71,8 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor status.px=this; status.multiColor=multiColor; + rmt_set_gpio(rf->getChannel(),RMT_MODE_TX,(gpio_num_t)(pin),false); + loadData(this); // load first two bytes of data to get started loadData(this); @@ -122,3 +128,4 @@ void IRAM_ATTR Pixel::loadData(void *arg){ /////////////////// volatile Pixel::pixel_status_t Pixel::status; +RFControl *Pixel::rf=NULL; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index b2a05b4..419f5ed 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -21,11 +21,12 @@ class Pixel { }; private: - RFControl *rf; // Pixel utilizes RFControl + static RFControl *rf; // Pixel utilizes RFControl (shared across all Pixel instances) uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t txThrMask; // mask for threshold interrupt + int pin=-1; // pin number to which pixel is connected const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel @@ -41,7 +42,7 @@ class Pixel { void setHSV(float h, float s, float v, uint32_t nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,100], v=[0,100] void setColors(const uint32_t *data, uint32_t nPixels, bool multiColor=true); // sets colors of nPixels from array of colors stored in data - int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + int getPin(){return(pin);} // returns pixel pin (or -1 if initialization failed) static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values static uint32_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index 182cefb..b556a24 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -33,21 +33,18 @@ RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){ if(installDriver) rmt_driver_install(config->channel,0,0); - // If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock - this->refClock=refClock; - - if(refClock) -#ifdef CONFIG_IDF_TARGET_ESP32C3 - REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider -#else - rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2 -#endif - nChannels++; + if(refClock) +#if SOC_RMT_SUPPORT_REF_TICK + rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2 instead of 80 MHz APB clock +#elif SOC_RMT_SUPPORT_XTAL + REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider +#endif + } - + /////////////////// void RFControl::start(uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from internal data structure, repeated for nCycles, where each tick in pulse is tickTime microseconds long @@ -60,8 +57,8 @@ void RFControl::start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTi if(!config || nData==0) return; - - rmt_set_clk_div(config->channel,tickTime); // set clock divider + + rmt_set_clk_div(config->channel,tickTime); // set clock divider for(int i=0;ichannel, (rmt_item32_t *) data, nData, true); // start transmission and wait until completed before returning diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e248795..407ed81 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -81,13 +81,12 @@ struct Effect2 { #define PIN 21 #endif -Pixel px2(2); Pixel px(PIN); +Pixel px2(2); +Pixel px5(5); Pixel px3(3); Pixel px4(4); -Pixel px5(5); Pixel px6(6); -Pixel px7(7); Effect1 effect1(&px,5); Effect2 effect2(&px,100); @@ -99,6 +98,13 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Pixel Example\n"); + + Serial.println(px.getPin()); + Serial.println(px2.getPin()); + Serial.println(px3.getPin()); + Serial.println(px4.getPin()); + Serial.println(px5.getPin()); + Serial.println(px6.getPin()); } // end of setup() From 95b41fd929f9f6871f807228ed002a9313eaf046 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 22 Jan 2022 07:09:48 -0600 Subject: [PATCH 039/106] Revert "Update logic so Pixel uses only ONE channel and shares across all instances" This reverts commit 4af3a22764f6cbbfeeccd1dec43b504040315c7d. --- src/extras/Pixel.cpp | 11 ++--------- src/extras/Pixel.h | 5 ++--- src/extras/RFControl.cpp | 21 ++++++++++++--------- src/extras/extras.ino | 12 +++--------- 4 files changed, 19 insertions(+), 30 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index e26e4d0..7ece3ab 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -4,14 +4,10 @@ /////////////////// Pixel::Pixel(int pin){ - - if(!rf) // RMT Channel not yet assigned - rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver - if(!*rf) // if RMT Channel could NOT be assigned (no more channels) + rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver + if(!rf) return; - - this->pin=pin; setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) @@ -71,8 +67,6 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor status.px=this; status.multiColor=multiColor; - rmt_set_gpio(rf->getChannel(),RMT_MODE_TX,(gpio_num_t)(pin),false); - loadData(this); // load first two bytes of data to get started loadData(this); @@ -128,4 +122,3 @@ void IRAM_ATTR Pixel::loadData(void *arg){ /////////////////// volatile Pixel::pixel_status_t Pixel::status; -RFControl *Pixel::rf=NULL; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 419f5ed..b2a05b4 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -21,12 +21,11 @@ class Pixel { }; private: - static RFControl *rf; // Pixel utilizes RFControl (shared across all Pixel instances) + RFControl *rf; // Pixel utilizes RFControl uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t txThrMask; // mask for threshold interrupt - int pin=-1; // pin number to which pixel is connected const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel @@ -42,7 +41,7 @@ class Pixel { void setHSV(float h, float s, float v, uint32_t nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,100], v=[0,100] void setColors(const uint32_t *data, uint32_t nPixels, bool multiColor=true); // sets colors of nPixels from array of colors stored in data - int getPin(){return(pin);} // returns pixel pin (or -1 if initialization failed) + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values static uint32_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index b556a24..182cefb 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -33,18 +33,21 @@ RFControl::RFControl(uint8_t pin, boolean refClock, boolean installDriver){ if(installDriver) rmt_driver_install(config->channel,0,0); - this->refClock=refClock; - nChannels++; + // If specified, set the base clock to 1 MHz so tick-units are in microseconds (before any CLK_DIV is applied), otherwise default will be 80 MHz APB clock + this->refClock=refClock; + if(refClock) -#if SOC_RMT_SUPPORT_REF_TICK - rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2 instead of 80 MHz APB clock -#elif SOC_RMT_SUPPORT_XTAL - REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider +#ifdef CONFIG_IDF_TARGET_ESP32C3 + REG_SET_FIELD(RMT_SYS_CONF_REG,RMT_SCLK_DIV_NUM,79); // ESP32-C3 does not have a 1 MHz REF Tick Clock, but allows the 80 MHz APB clock to be scaled by an additional RMT-specific divider +#else + rmt_set_source_clk(config->channel,RMT_BASECLK_REF); // use 1 MHz REF Tick Clock for ESP32 and ESP32-S2 #endif -} + nChannels++; +} + /////////////////// void RFControl::start(uint8_t nCycles, uint8_t tickTime){ // starts transmission of pulses from internal data structure, repeated for nCycles, where each tick in pulse is tickTime microseconds long @@ -57,8 +60,8 @@ void RFControl::start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTi if(!config || nData==0) return; - - rmt_set_clk_div(config->channel,tickTime); // set clock divider + + rmt_set_clk_div(config->channel,tickTime); // set clock divider for(int i=0;ichannel, (rmt_item32_t *) data, nData, true); // start transmission and wait until completed before returning diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 407ed81..e248795 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -81,12 +81,13 @@ struct Effect2 { #define PIN 21 #endif -Pixel px(PIN); Pixel px2(2); -Pixel px5(5); +Pixel px(PIN); Pixel px3(3); Pixel px4(4); +Pixel px5(5); Pixel px6(6); +Pixel px7(7); Effect1 effect1(&px,5); Effect2 effect2(&px,100); @@ -98,13 +99,6 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Pixel Example\n"); - - Serial.println(px.getPin()); - Serial.println(px2.getPin()); - Serial.println(px3.getPin()); - Serial.println(px4.getPin()); - Serial.println(px5.getPin()); - Serial.println(px6.getPin()); } // end of setup() From a09b8d0b850a0af58b0b3e978f5f7217714b3aed Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 22 Jan 2022 07:18:19 -0600 Subject: [PATCH 040/106] Update Pixel.cpp --- src/extras/Pixel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 7ece3ab..8264c20 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -6,7 +6,7 @@ Pixel::Pixel(int pin){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver - if(!rf) + if(!*rf) return; setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) From b7f2495e7c6deefeaa1c79473dc3c5afa4920a44 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 22 Jan 2022 17:52:33 -0600 Subject: [PATCH 041/106] Update Pixel.ino Added example pin definitions for ESP32/S2/C3 --- Other Examples/Pixel/Pixel.ino | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index d263fa9..dfad8b7 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -30,6 +30,25 @@ // Demonstrates use of HomeSpan Pixel Class that provides for control of single-wire // addressable RGB LEDs, such as the SK68xx or WS28xx models found in many devices, // including the Espressif ESP32, ESP32-S2, and ESP32-C3 development boards. +// +// IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD + +#if defined(CONFIG_IDF_TARGET_ESP32C3) + + #define PIXEL_PIN 8 + #define PIXEL_STRAND_PIN 1 + +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + + #define PIXEL_PIN 18 + #define PIXEL_STRAND_PIN 7 + +#elif defined(CONFIG_IDF_TARGET_ESP32) + + #define PIXEL_PIN 23 + #define PIXEL_STRAND_PIN 21 + +#endif #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class @@ -139,7 +158,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_Light(8); // create single Pixel attached to pin 8 + new Pixel_Light(PIXEL_PIN); // create single Pixel Light new SpanAccessory(); new Service::AccessoryInformation(); @@ -153,7 +172,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_KnightRider(1,8); // create 8-Pixel Strand with Knight-Rider Effect attached to pin 1 + new Pixel_KnightRider(PIXEL_STRAND_PIN,8); // create 8-Pixel Strand with Knight-Rider Effect } From 46820c66a2df15451b6a49b1f89a8d60bea6a144 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 23 Jan 2022 15:00:12 -0600 Subject: [PATCH 042/106] Add Arduino-ESP32 version number to MDNS output --- src/HomeSpan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 543f770..a9457d0 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -499,6 +499,7 @@ void Span::checkConnect(){ mdns_service_txt_item_set("_hap","_tcp","sf","0"); // set Status Flag = 0 mdns_service_txt_item_set("_hap","_tcp","hspn",HOMESPAN_VERSION); // HomeSpan Version Number (info only - NOT used by HAP) + mdns_service_txt_item_set("_hap","_tcp","ard-esp32",ARDUINO_ESP_VERSION); // Arduino-ESP32 Version Number (info only - NOT used by HAP) mdns_service_txt_item_set("_hap","_tcp","sketch",sketchVersion); // Sketch Version (info only - NOT used by HAP) mdns_service_txt_item_set("_hap","_tcp","ota",otaEnabled?"yes":"no"); // OTA Enabled (info only - NOT used by HAP) From 4588669cd20fcf02f88a950f93c9c8408d3dded8 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 23 Jan 2022 16:40:46 -0600 Subject: [PATCH 043/106] Added ARDUINO_VARIANT (type of board) to MDNS --- src/HomeSpan.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index a9457d0..0435c93 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -500,6 +500,7 @@ void Span::checkConnect(){ mdns_service_txt_item_set("_hap","_tcp","hspn",HOMESPAN_VERSION); // HomeSpan Version Number (info only - NOT used by HAP) mdns_service_txt_item_set("_hap","_tcp","ard-esp32",ARDUINO_ESP_VERSION); // Arduino-ESP32 Version Number (info only - NOT used by HAP) + mdns_service_txt_item_set("_hap","_tcp","board",ARDUINO_VARIANT); // Board Name (info only - NOT used by HAP) mdns_service_txt_item_set("_hap","_tcp","sketch",sketchVersion); // Sketch Version (info only - NOT used by HAP) mdns_service_txt_item_set("_hap","_tcp","ota",otaEnabled?"yes":"no"); // OTA Enabled (info only - NOT used by HAP) From 877f47a64d70e28c054a60eb74ef9d0f3cb83f92 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 27 Jan 2022 21:41:05 -0600 Subject: [PATCH 044/106] Split RFControl constructor into public and private versions Private constructor only used for Pixel, which is friend class of RFControl --- src/extras/RFControl.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index 91c86d4..ea8f731 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -13,15 +13,19 @@ using std::vector; class RFControl { + friend class Pixel; + private: rmt_config_t *config=NULL; vector data; boolean lowWord=true; boolean refClock; static uint8_t nChannels; - + + RFControl(uint8_t pin, boolean refClock, boolean installDriver); // private constructor (only used by Pixel class) + public: - RFControl(uint8_t pin, boolean refClock=true, boolean installDriver=true); // creates transmitter on pin, using 1-MHz Ref Tick clock + RFControl(uint8_t pin, boolean refClock=true):RFControl(pin,refClock,true){}; // public constructor to create transmitter on pin, using 1-MHz Ref Tick clock or 80-MHz APB clock void start(uint32_t *data, int nData, uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from specified data pointer, repeated for numCycles, where each tick in pulse is tickTime microseconds long void start(uint8_t nCycles=1, uint8_t tickTime=1); // starts transmission of pulses from internal data structure, repeated for numCycles, where each tick in pulse is tickTime microseconds long @@ -32,7 +36,7 @@ class RFControl { void enableCarrier(uint32_t freq, float duty=0.5); // enables carrier wave if freq>0, else disables carrier wave; duty is a fraction from 0-1 void disableCarrier(){enableCarrier(0);} // disables carrier wave - int getPin(){return(config?config->gpio_num:-1);} // returns the pin number + int getPin(){return(config?config->gpio_num:-1);} // returns the pin number, or -1 if no channel defined rmt_channel_t getChannel(){return(config?config->channel:RMT_CHANNEL_0);} // returns channel, or channel_0 is no channel defined operator bool(){ // override boolean operator to return true/false if creation succeeded/failed From d2bbd4f56cd2d7955a234b3a0eafb83f6dc3c5f1 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 29 Jan 2022 18:26:17 -0600 Subject: [PATCH 045/106] Added logic for RGB vs. RGBW Tested with original 8-pixel RGB and 60-pixel RGBW. Works as expected. --- src/extras/Pixel.cpp | 29 ++++++++++++----------- src/extras/Pixel.h | 43 ++++++++++++++++++++--------------- src/extras/extras.ino | 53 ++++++++++++++++++++++--------------------- 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 8264c20..882ba72 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -3,12 +3,14 @@ /////////////////// -Pixel::Pixel(int pin){ +Pixel::Pixel(int pin, pixel_type_t pType){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver if(!*rf) return; - + + this->pType=pType; + setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) rmt_isr_register(loadData,NULL,0,NULL); // set custom interrupt handler @@ -35,21 +37,21 @@ void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t /////////////////// -void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, uint32_t nPixels){ +void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint32_t nPixels){ if(!*rf || nPixels==0) return; - uint32_t data=getColorRGB(r,g,b); + uint32_t data=getColorRGB(r,g,b,w); setColors(&data,nPixels,false); } /////////////////// -void Pixel::setHSV(float h, float s, float v, uint32_t nPixels){ +void Pixel::setHSV(float h, float s, float v, double w, uint32_t nPixels){ float r,g,b; LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - setRGB(r*255,g*255,b*255,nPixels); + setRGB(r*255,g*255,b*255,w*2.555,nPixels); } /////////////////// @@ -62,7 +64,7 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor status.nPixels=nPixels; status.data=data; status.iMem=0; - status.iBit=24; + status.iBit=32; status.started=true; status.px=this; status.multiColor=multiColor; @@ -78,16 +80,17 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor /////////////////// -uint32_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b){ - return(g<<16 | r<<8 | b); +uint32_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w){ +// return(g<<16 | r<<8 | b); + return(g<<24 | r<<16 | b<<8 | w); } /////////////////// -uint32_t Pixel::getColorHSV(float h, float s, float v){ +uint32_t Pixel::getColorHSV(float h, float s, float v, double w){ float r,g,b; LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - return(getColorRGB(r*255,g*255,b*255)); + return(getColorRGB(r*255,g*255,b*255,w*2.555)); } /////////////////// @@ -110,8 +113,8 @@ void IRAM_ATTR Pixel::loadData(void *arg){ for(int i=0;i<8;i++) RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(*status.data>>(--status.iBit))&1]; - if(status.iBit==0){ - status.iBit=24; + if(status.iBit==status.px->pType){ + status.iBit=32; status.data+=status.multiColor; status.nPixels--; } diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index b2a05b4..bd46bf0 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -10,41 +10,48 @@ class Pixel { - struct pixel_status_t { - int nPixels; - const uint32_t *data; - int iBit; - int iMem; - boolean started; - Pixel *px; - boolean multiColor; - }; - + public: + enum pixel_type_t { + RGB=8, + RGBW=0 + }; + private: + struct pixel_status_t { + int nPixels; + const uint32_t *data; + int iBit; + int iMem; + boolean started; + Pixel *px; + boolean multiColor; + }; + RFControl *rf; // Pixel utilizes RFControl uint32_t pattern[2]; // storage for zero-bit and one-bit pulses uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t txThrMask; // mask for threshold interrupt + pixel_type_t pType; // type of Pixel (RGB or RGBW) const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel static void loadData(void *arg); // interrupt handler volatile static pixel_status_t status; // storage for volatile information modified in interupt handler - public: - Pixel(int pin); // creates addressable single-wire RGB LED on pin (such as the SK68 or WS28), with OPTIONAL reserve of memory for nPixels + public: + Pixel(int pin, pixel_type_t pType=RGB); // creates addressable single-wire RGB or RGBW LED on pin (such as the SK68 or WS28) void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS - void setRGB(uint8_t r, uint8_t g, uint8_t b, uint32_t nPixels=1); // sets color of nPixels to RGB values (0-255) - void setHSV(float h, float s, float v, uint32_t nPixels=1); // sets color of nPixels to HSV values where h=[0,360], s=[0,100], v=[0,100] - void setColors(const uint32_t *data, uint32_t nPixels, bool multiColor=true); // sets colors of nPixels from array of colors stored in data + void setRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0, uint32_t nPixels=1); // sets color of nPixels to RGB(W) values (0-255) + void setHSV(float h, float s, float v, double w=0, uint32_t nPixels=1); // sets color of nPixels to HSV(W) values where h=[0,360], s/v/w=[0,100] + void setColors(const uint32_t *data, uint32_t nPixels, bool multiColor=true); // sets colors of nPixels from array of colors stored in data - int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 - static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b); // return pixel Color from RGB values - static uint32_t getColorHSV(float h, float s, float v); // return pixel Color from HSV values + static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0); // return pixel Color from RGB(W) values + static uint32_t getColorHSV(float h, float s, float v, double w=0); // return pixel Color from HSV(W) values operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e248795..1f94862 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -6,7 +6,6 @@ struct Effect1 { Pixel *px; int H=0; - uint32_t x[8]; uint32_t alarmTime=0; uint32_t speed; @@ -19,10 +18,7 @@ struct Effect1 { if(millis()getColorHSV(H,i*3+79,i*2+5); - - px->setColors(x,8); + px->setHSV(H,100,100,0,60); H=(H+1)%360; alarmTime=millis()+speed; @@ -35,7 +31,7 @@ struct Effect2 { int phase=0; int dir=1; int H=0; - uint32_t x[8]; + uint32_t x[60]; uint32_t alarmTime=0; uint32_t speed; @@ -48,23 +44,23 @@ struct Effect2 { if(millis()getColorHSV(H,100,10); - else if(i==7-phase) - x[i]=px->getColorHSV(H+180,100,10); + x[i]=px->getColorHSV(H,100,100); + else if(i==59-phase) + x[i]=px->getColorHSV(H+180,100,100); else x[i]=0; } - px->setColors(x,8); - phase=(phase+dir)%8; + px->setColors(x,60); + phase=(phase+dir)%60; if(phase==0){ dir=1; H=(H+10)%360; } - else if(phase==7){ + else if(phase==59){ dir=-1; H=(H+10)%360; } @@ -74,23 +70,27 @@ struct Effect2 { }; #if defined(CONFIG_IDF_TARGET_ESP32C3) - #define PIN 1 + + #define PIXEL_PIN_1 8 + #define PIXEL_PIN_2 1 + #elif defined(CONFIG_IDF_TARGET_ESP32S2) - #define PIN 1 -#else - #define PIN 21 + + #define PIXEL_PIN_1 18 + #define PIXEL_PIN_2 7 + +#elif defined(CONFIG_IDF_TARGET_ESP32) + + #define PIXEL_PIN_1 23 + #define PIXEL_PIN_2 21 + #endif -Pixel px2(2); -Pixel px(PIN); -Pixel px3(3); -Pixel px4(4); -Pixel px5(5); -Pixel px6(6); -Pixel px7(7); +Pixel px1(PIXEL_PIN_1,Pixel::RGBW); +Pixel px2(PIXEL_PIN_2,Pixel::RGBW); -Effect1 effect1(&px,5); -Effect2 effect2(&px,100); +Effect1 effect1(&px1,20); +Effect2 effect2(&px2,20); void setup() { @@ -103,5 +103,6 @@ void setup() { } // end of setup() void loop(){ + effect1.update(); effect2.update(); } From 852a916d614193fc9277bc8f66049cbfc5015622 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 Jan 2022 22:33:07 -0600 Subject: [PATCH 046/106] Initial coding of protectPinISR(pin) method Works for all NVS calls in homeSpan.h. To Do: Add logic to all other NVS calls. --- src/HomeSpan.cpp | 18 ++++++++++++++++++ src/HomeSpan.h | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 0435c93..5ecda27 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1387,6 +1387,24 @@ void Span::checkRanges(){ homeSpan.configLog+="\n\n"; } +/////////////////////////////// + +void Span::protectGPIOs(boolean suspend){ + + auto gp=ProtectedGPIOs.begin(); + while(gp!=ProtectedGPIOs.end()){ + if(suspend){ + Serial.printf("PIN: %d Status: %d : ",gp->first,GPIO.pin[gp->first].int_type); + gp->second=GPIO.pin[gp->first].int_type; + GPIO.pin[gp->first].int_type=0; + Serial.printf("PIN: %d Status: %d\n",gp->first,GPIO.pin[gp->first].int_type); + } else { + GPIO.pin[gp->first].int_type=gp->second; + } + gp++; + } +} + /////////////////////////////// // SpanAccessory // /////////////////////////////// diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 9acd360..8822342 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -145,6 +145,7 @@ struct Span{ vector Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event vector PushButtons; // vector of pointer to all PushButtons unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs) + unordered_map ProtectedGPIOs; // map of pins to protect when running an ISR during NVS operations unordered_map UserCommands; // map of pointers to all UserCommands @@ -159,6 +160,7 @@ struct Span{ void commandMode(); // allows user to control and reset HomeSpan settings with the control button void processSerialCommand(const char *c); // process command 'c' (typically from readSerial, though can be called with any 'c') void checkRanges(); // checks values of all Characteristics to ensure they are each within range + void protectGPIOs(boolean suspend); // true - disable ISRs on protectedGPIOs; false - re-enable ISRs on protectedGPIOs int sprintfAttributes(char *cBuf); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL void prettyPrint(char *buf, int nsp=2); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces @@ -190,6 +192,7 @@ struct Span{ const char *getSketchVersion(){return sketchVersion;} // get sketch version number void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point + void protectPinISR(uint8_t pin){ProtectedGPIOs[pin]=0;} // protects ISR on pin from NVS operations void setPairingCode(const char *s){sprintf(pairingCodeCommand,"S %9s",s);} // sets the Pairing Code - use is NOT recommended. Use 'S' from CLI instead @@ -409,6 +412,7 @@ struct SpanCharacteristic{ uvSet(value,val); if(nvsStore){ + homeSpan.protectGPIOs(true); nvsKey=(char *)malloc(16); uint16_t t; sscanf(type,"%x",&t); @@ -438,6 +442,7 @@ struct SpanCharacteristic{ nvsFlag=1; } } + homeSpan.protectGPIOs(false); } uvSet(newValue,value); @@ -534,8 +539,10 @@ struct SpanCharacteristic{ homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector if(nvsKey){ + homeSpan.protectGPIOs(true); nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data nvs_commit(homeSpan.charNVS); + homeSpan.protectGPIOs(false); } } // setString() @@ -566,8 +573,10 @@ struct SpanCharacteristic{ homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector if(nvsKey){ + homeSpan.protectGPIOs(true); nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data nvs_commit(homeSpan.charNVS); + homeSpan.protectGPIOs(false); } } From a21cc0679d6a42cae0b443524c6b3a4f17fa2789 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Feb 2022 09:17:56 -0600 Subject: [PATCH 047/106] Add two-wire addressable DotStar RGB LEDs to Pixel.h Pixel.h now contains Pixel() and Dot() classes. Dot() class uses more streamlined methods; must next update Pixel() to use similar methods, which will allow Pixel and Dot to be more "interchangeable". --- src/extras/Pixel.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++- src/extras/Pixel.h | 48 +++++++++++++++++++-- 2 files changed, 144 insertions(+), 4 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 882ba72..b79c8c6 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -1,7 +1,13 @@ +//////////////////////////////////////////// +// Addressable LEDs // +//////////////////////////////////////////// + #include "Pixel.h" -/////////////////// +//////////////////////////////////////////// +// Single-Wire RGB/RGBW NeoPixels // +//////////////////////////////////////////// Pixel::Pixel(int pin, pixel_type_t pType){ @@ -125,3 +131,95 @@ void IRAM_ATTR Pixel::loadData(void *arg){ /////////////////// volatile Pixel::pixel_status_t Pixel::status; + +//////////////////////////////////////////// +// Two-Wire RGB DotStars // +//////////////////////////////////////////// + +Dot::Dot(uint8_t dataPin, uint8_t clockPin){ + + pinMode(dataPin,OUTPUT); + pinMode(clockPin,OUTPUT); + digitalWrite(dataPin,LOW); + digitalWrite(clockPin,LOW); + + dataMask=1<<(dataPin%32); + clockMask=1<<(clockPin%32); + +#ifdef CONFIG_IDF_TARGET_ESP32C3 + dataSetReg=&GPIO.out_w1ts.val; + dataClearReg=&GPIO.out_w1tc.val; + clockSetReg=&GPIO.out_w1ts.val; + clockClearReg=&GPIO.out_w1tc.val; +#else + if(dataPin<32){ + dataSetReg=&GPIO.out_w1ts; + dataClearReg=&GPIO.out_w1tc; + } else { + dataSetReg=&GPIO.out1_w1ts.val; + dataClearReg=&GPIO.out1_w1tc.val; + } + + if(clockPin<32){ + clockSetReg=&GPIO.out_w1ts; + clockClearReg=&GPIO.out_w1tc; + } else { + clockSetReg=&GPIO.out1_w1ts.val; + clockClearReg=&GPIO.out1_w1tc.val; + } +#endif + +} + +/////////////////// + +void Dot::set(Color *c, int nPixels, boolean multiColor){ + + *dataClearReg=dataMask; // send all zeros + for(int j=0;j<31;j++){ + *clockSetReg=clockMask; + *clockClearReg=clockMask; + } + + for(int i=0;i=0;b--){ + if((c->val>>b)&1) + *dataSetReg=dataMask; + else + *dataClearReg=dataMask; + *clockSetReg=clockMask; + *clockClearReg=clockMask; + } + if(multiColor) + c++; + } + + *dataClearReg=dataMask; // send all zeros + for(int j=0;j<31;j++){ + *clockSetReg=clockMask; + *clockClearReg=clockMask; + } +} + +/////////////////// + +Dot::Color Dot::RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive){ + Color x; + x.red=red; + x.green=green; + x.blue=blue; + x.drive=drive; + x.flags=7; + return(x); +} + + +/////////////////// + +Dot::Color Dot::HSV(float h, float s, float v, float level){ + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + return(RGB(r*255,g*255,b*255,level/100*31.5)); +} + +//////////////////////////////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index bd46bf0..bb45bf0 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -1,13 +1,17 @@ -//////////////////////////////////// -// Addressable LED Pixel // -//////////////////////////////////// +//////////////////////////////////////////// +// Addressable LEDs // +//////////////////////////////////////////// #pragma once #include "RFControl.h" #include "PwmPin.h" +//////////////////////////////////////////// +// Single-Wire RGB/RGBW NeoPixels // +//////////////////////////////////////////// + class Pixel { public: @@ -57,3 +61,41 @@ class Pixel { return(*rf); } }; + +//////////////////////////////////////////// +// Two-Wire RGB DotStars // +//////////////////////////////////////////// + +class Dot { + + public: + struct Color { + union{ + struct { + uint8_t red:8; + uint8_t green:8; + uint8_t blue:8; + uint8_t drive:5; + uint8_t flags:3; + }; + uint32_t val; + }; + }; + + private: + uint32_t dataMask; + uint32_t clockMask; + volatile uint32_t *dataSetReg; + volatile uint32_t *dataClearReg; + volatile uint32_t *clockSetReg; + volatile uint32_t *clockClearReg; + + public: + Dot(uint8_t dataPin, uint8_t clockPin); + void set(Color c, int nPixels=1){set(&c,nPixels,false);} + void set (Color *c, int nPixels, boolean multiColor=true); + static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive=31); + static Color HSV(float h, float s, float v, float level=100); +}; + +//////////////////////////////////////////// From d0a13e54170436945499f301c636237af900bb2c Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Feb 2022 12:35:01 -0600 Subject: [PATCH 048/106] Updated Pixel() class to use more streamlined methods implemented in Dot() class Pixel() and Dot() classes are now complete. Tested DotStar RGB, NeoPixel RGB, and NeoPixel RGBW, all running at same time on a single ESP32. Next up: Must update Pixel Example as well as Holiday Lights Project To Do: Add documentation page for Pixel() and Dot() --- src/extras/Pixel.cpp | 58 ++++++++++++++----------------------- src/extras/Pixel.h | 53 ++++++++++++++++++---------------- src/extras/extras.ino | 67 ++++++++++++++++++++++++++++++++----------- 3 files changed, 100 insertions(+), 78 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index b79c8c6..3d16e29 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -9,13 +9,16 @@ // Single-Wire RGB/RGBW NeoPixels // //////////////////////////////////////////// -Pixel::Pixel(int pin, pixel_type_t pType){ +Pixel::Pixel(int pin, boolean isRGBW){ rf=new RFControl(pin,false,false); // set clock to 1/80 usec, no default driver if(!*rf) return; - this->pType=pType; + if(isRGBW) + this->lastBit=0; + else + this->lastBit=8; setTiming(0.32, 0.88, 0.64, 0.56, 80.0); // set default timing parameters (suitable for most SK68 and WS28 RGB pixels) @@ -43,32 +46,13 @@ void Pixel::setTiming(float high0, float low0, float high1, float low1, uint32_t /////////////////// -void Pixel::setRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w, uint32_t nPixels){ - - if(!*rf || nPixels==0) - return; - - uint32_t data=getColorRGB(r,g,b,w); - setColors(&data,nPixels,false); -} - -/////////////////// - -void Pixel::setHSV(float h, float s, float v, double w, uint32_t nPixels){ - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - setRGB(r*255,g*255,b*255,w*2.555,nPixels); -} - -/////////////////// - -void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor){ +void Pixel::set(Color *c, int nPixels, boolean multiColor){ if(!*rf || nPixels==0) return; status.nPixels=nPixels; - status.data=data; + status.color=c; status.iMem=0; status.iBit=32; status.started=true; @@ -86,17 +70,21 @@ void Pixel::setColors(const uint32_t *data, uint32_t nPixels, boolean multiColor /////////////////// -uint32_t Pixel::getColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w){ -// return(g<<16 | r<<8 | b); - return(g<<24 | r<<16 | b<<8 | w); +Pixel::Color Pixel::RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t white){ + Color x; + x.red=red; + x.green=green; + x.blue=blue; + x.white=white; + return(x); } /////////////////// -uint32_t Pixel::getColorHSV(float h, float s, float v, double w){ +Pixel::Color Pixel::HSV(float h, float s, float v, double w){ float r,g,b; LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - return(getColorRGB(r*255,g*255,b*255,w*2.555)); + return(RGB(r*255,g*255,b*255,w*2.555)); } /////////////////// @@ -117,11 +105,11 @@ void IRAM_ATTR Pixel::loadData(void *arg){ } for(int i=0;i<8;i++) - RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(*status.data>>(--status.iBit))&1]; + RMTMEM.chan[status.px->rf->getChannel()].data32[status.iMem++].val=status.px->pattern[(status.color->val>>(--status.iBit))&1]; - if(status.iBit==status.px->pType){ + if(status.iBit==status.px->lastBit){ status.iBit=32; - status.data+=status.multiColor; + status.color+=status.multiColor; status.nPixels--; } @@ -190,8 +178,7 @@ void Dot::set(Color *c, int nPixels, boolean multiColor){ *clockSetReg=clockMask; *clockClearReg=clockMask; } - if(multiColor) - c++; + c+=multiColor; } *dataClearReg=dataMask; // send all zeros @@ -213,13 +200,12 @@ Dot::Color Dot::RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive){ return(x); } - /////////////////// -Dot::Color Dot::HSV(float h, float s, float v, float level){ +Dot::Color Dot::HSV(float h, float s, float v, double level){ float r,g,b; LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - return(RGB(r*255,g*255,b*255,level/100*31.5)); + return(RGB(r*255,g*255,b*255,level*0.315)); } //////////////////////////////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index bb45bf0..37708be 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -14,16 +14,23 @@ class Pixel { - public: - enum pixel_type_t { - RGB=8, - RGBW=0 - }; - + public: + struct Color { + union{ + struct { + uint8_t white:8; + uint8_t blue:8; + uint8_t red:8; + uint8_t green:8; + }; + uint32_t val; + }; + }; + private: struct pixel_status_t { int nPixels; - const uint32_t *data; + Color *color; int iBit; int iMem; boolean started; @@ -36,7 +43,7 @@ class Pixel { uint32_t resetTime; // minimum time (in usec) between pulse trains uint32_t txEndMask; // mask for end-of-transmission interrupt uint32_t txThrMask; // mask for threshold interrupt - pixel_type_t pType; // type of Pixel (RGB or RGBW) + uint32_t lastBit; // 0=RGBW; 8=RGB const int memSize=sizeof(RMTMEM.chan[0].data32)/4; // determine size (in pulses) of one channel @@ -44,19 +51,15 @@ class Pixel { volatile static pixel_status_t status; // storage for volatile information modified in interupt handler public: - Pixel(int pin, pixel_type_t pType=RGB); // creates addressable single-wire RGB or RGBW LED on pin (such as the SK68 or WS28) - - void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS - - void setRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0, uint32_t nPixels=1); // sets color of nPixels to RGB(W) values (0-255) - void setHSV(float h, float s, float v, double w=0, uint32_t nPixels=1); // sets color of nPixels to HSV(W) values where h=[0,360], s/v/w=[0,100] - void setColors(const uint32_t *data, uint32_t nPixels, bool multiColor=true); // sets colors of nPixels from array of colors stored in data - + Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) + static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t white=0); // returns Color based on provided RGB(W) values where r/g/b/w=[0-255] + static Color HSV(float h, float s, float v, double w=0); // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100] + void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels + void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c + int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 - - static uint32_t getColorRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0); // return pixel Color from RGB(W) values - static uint32_t getColorHSV(float h, float s, float v, double w=0); // return pixel Color from HSV(W) values - + void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS + operator bool(){ // override boolean operator to return true/false if creation succeeded/failed return(*rf); } @@ -91,11 +94,11 @@ class Dot { volatile uint32_t *clockClearReg; public: - Dot(uint8_t dataPin, uint8_t clockPin); - void set(Color c, int nPixels=1){set(&c,nPixels,false);} - void set (Color *c, int nPixels, boolean multiColor=true); - static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive=31); - static Color HSV(float h, float s, float v, float level=100); + Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102) + static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive=31); // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive=[0,31] + static Color HSV(float h, float s, float v, double level=100); // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive=[0,100] + void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels + void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c }; //////////////////////////////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 1f94862..991a457 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -8,9 +8,11 @@ struct Effect1 { int H=0; uint32_t alarmTime=0; uint32_t speed; + uint8_t nPixels; - Effect1(Pixel *px, uint32_t speed=20){ + Effect1(Pixel *px, uint32_t speed, uint8_t nPixels){ this->px=px; + this->nPixels=nPixels; this->speed=speed; } @@ -18,9 +20,9 @@ struct Effect1 { if(millis()setHSV(H,100,100,0,60); + px->set(px->HSV(H,100,10),nPixels); H=(H+1)%360; - + alarmTime=millis()+speed; } }; @@ -31,12 +33,14 @@ struct Effect2 { int phase=0; int dir=1; int H=0; - uint32_t x[60]; + Pixel::Color x[60]; uint32_t alarmTime=0; uint32_t speed; + uint8_t nPixels; - Effect2(Pixel *px, uint32_t speed=20){ + Effect2(Pixel *px, uint32_t speed, uint8_t nPixels){ this->px=px; + this->nPixels=nPixels; this->speed=speed; } @@ -44,23 +48,23 @@ struct Effect2 { if(millis()getColorHSV(H,100,100); - else if(i==59-phase) - x[i]=px->getColorHSV(H+180,100,100); + x[i]=px->HSV(H,100,100); + else if(i==nPixels-1-phase) + x[i]=px->HSV(H+180,100,100); else - x[i]=0; + x[i]=Pixel::HSV(0,0,0); } - px->setColors(x,60); - phase=(phase+dir)%60; + px->set(x,nPixels); + phase=(phase+dir)%nPixels; if(phase==0){ dir=1; H=(H+10)%360; } - else if(phase==59){ + else if(phase==nPixels-1){ dir=-1; H=(H+10)%360; } @@ -69,6 +73,31 @@ struct Effect2 { } }; +struct Effect3 { + + Dot *dot; + int H=0; + uint32_t alarmTime=0; + uint32_t speed; + uint8_t nPixels; + + Effect3(Dot *dot, uint32_t speed, uint8_t nPixels){ + this->dot=dot; + this->nPixels=nPixels; + this->speed=speed; + } + + void update(){ + if(millis()set(Dot::HSV(H,100,100),nPixels); + H=(H+1)%360; + + alarmTime=millis()+speed; + } +}; + #if defined(CONFIG_IDF_TARGET_ESP32C3) #define PIXEL_PIN_1 8 @@ -83,14 +112,17 @@ struct Effect2 { #define PIXEL_PIN_1 23 #define PIXEL_PIN_2 21 + + Dot dot(32,5); #endif -Pixel px1(PIXEL_PIN_1,Pixel::RGBW); -Pixel px2(PIXEL_PIN_2,Pixel::RGBW); +Pixel px1(PIXEL_PIN_1); +Pixel px2(PIXEL_PIN_2,true); -Effect1 effect1(&px1,20); -Effect2 effect2(&px2,20); +Effect1 effect1(&px1,20,8); +Effect2 effect2(&px2,20,60); +Effect3 effect3(&dot,20,30); void setup() { @@ -105,4 +137,5 @@ void setup() { void loop(){ effect1.update(); effect2.update(); + effect3.update(); } From e27d31073370bade50db51bda365bac0c246a855 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Feb 2022 16:08:51 -0600 Subject: [PATCH 049/106] Updated Pixel Example to use Pixel() and Dot() Classes --- Other Examples/Pixel/Pixel.ino | 100 +++++++++++++++------------------ 1 file changed, 46 insertions(+), 54 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index dfad8b7..a950d41 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -25,48 +25,38 @@ * ********************************************************************************/ - // HomeSpan Addressable RGB LED Example - -// Demonstrates use of HomeSpan Pixel Class that provides for control of single-wire -// addressable RGB LEDs, such as the SK68xx or WS28xx models found in many devices, -// including the Espressif ESP32, ESP32-S2, and ESP32-C3 development boards. +// HomeSpan Addressable RGB LED Examples. Demonstrates use of: +// +// * HomeSpan Pixel Class that provides for control of single-wire addressable RGB and RGBW LEDs, such as the WS2812 and SK6812 +// * HomeSpan Dot Class that provides for control of two-wire addressable RGB LEDs, such as the APA102 and SK9822 // // IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD +// -#if defined(CONFIG_IDF_TARGET_ESP32C3) +#define NEOPIXEL_PIN 23 // only one pin needed for NeoPixels - #define PIXEL_PIN 8 - #define PIXEL_STRAND_PIN 1 - -#elif defined(CONFIG_IDF_TARGET_ESP32S2) - - #define PIXEL_PIN 18 - #define PIXEL_STRAND_PIN 7 - -#elif defined(CONFIG_IDF_TARGET_ESP32) - - #define PIXEL_PIN 23 - #define PIXEL_STRAND_PIN 21 +#define DOTSTAR_DATA_PIN 32 // two pins are needed to DotStars +#define DOTSTAR_CLOCK_PIN 5 -#endif - #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class /////////////////////////////// -struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel +struct Pixel_Light : Service::LightBulb { // Addressable single-wire RGB LED Strand (e.g. NeoPixel) Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; Characteristic::Saturation S{0,true}; Characteristic::Brightness V{100,true}; - Pixel *pixel; + Pixel *pixel; + uint8_t nPixels; - Pixel_Light(int pin) : Service::LightBulb(){ + Pixel_Light(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){ V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% - pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs + pixel=new Pixel(pin); // creates Pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs + this->nPixels=nPixels; // save number of Pixels in this LED Strand update(); // manually call update() to set pixel with restored initial values } @@ -78,7 +68,7 @@ struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel float s=S.getNewVal(); // range = [0,100] float v=V.getNewVal(); // range = [0,100] - pixel->setHSV(h*p, s*p, v*p); // sets pixel to HSV colors + pixel->set(Pixel::HSV(h*p, s*p, v*p),nPixels); // sets all nPixels to the same HSV color return(true); } @@ -86,38 +76,32 @@ struct Pixel_Light : Service::LightBulb { // Addressable RGB Pixel /////////////////////////////// -struct Pixel_KnightRider : Service::LightBulb { // Addressable RGB Pixel Strand of nPixel Pixels - Knight Rider Effect +struct Dot_Light : Service::LightBulb { // Addressable two-wire RGB LED Strand (e.g. DotStar) - Chasing Light Effect Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; - Characteristic::Saturation S{100,true}; - Pixel *pixel; - int nPixels; - uint32_t *colors; + Characteristic::Saturation S{0,true}; + Characteristic::Brightness V{100,true}; + Dot *dot; + uint8_t nPixels; + Dot::Color *colors; int phase=0; int dir=1; uint32_t alarmTime=0; - Pixel_KnightRider(int pin, int nPixels) : Service::LightBulb(){ + Dot_Light(uint8_t dataPin, uint8_t clockPin, uint8_t nPixels) : Service::LightBulb(){ - pixel=new Pixel(pin); // creates pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs - this->nPixels=nPixels; // store number of Pixels in Strand + dot=new Dot(dataPin,clockPin); // creates Dot LED on specified pins - suitable for APA102 or SK9822 + this->nPixels=nPixels; // save number of Pixels in this LED Strand - colors=(uint32_t *)calloc(2*nPixels-1,sizeof(uint32_t)); // storage for dynamic pixel pattern - update(); // manually call update() to set pixelk pattern with restored initial values + colors=(Dot::Color *)calloc(2*nPixels-1,sizeof(Dot::Color)); // storage for dynamic pixel pattern + update(); // manually call update() to set pixel with restored initial values } boolean update() override { - if(!power.getNewVal()){ - pixel->setRGB(0,0,0,8); - } else { - float level=100; - for(int i=0;igetColorHSV(H.getNewVal(),S.getNewVal(),level); - colors[nPixels-i-1]=colors[nPixels+i-1]; - } - } + if(!power.getNewVal()) + dot->set(Dot::RGB(0,0,0),nPixels); // turn off all LEDs in Strand return(true); } @@ -125,15 +109,23 @@ struct Pixel_KnightRider : Service::LightBulb { // Addressable RGB Pixel St void loop() override { if(millis()>alarmTime && power.getVal()){ - alarmTime=millis()+80; - pixel->setColors(colors+phase,nPixels); - if(phase==7) + alarmTime=millis()+40; + colors[phase]=Dot::HSV(0,0,0); + if(phase==nPixels-1) dir=-1; else if(phase==0) dir=1; phase+=dir; + + float h=H.getNewVal(); // range = [0,360] + float s=S.getNewVal(); // range = [0,100] + float v=V.getNewVal(); // range = [0,100] + + colors[phase]=Dot::HSV(h,s,100,v); + + dot->set(colors,nPixels); // update Strand with new colors } - + } }; @@ -148,31 +140,31 @@ void setup() { new SpanAccessory(); new Service::AccessoryInformation(); - new Characteristic::Name("RGB Pixel"); + new Characteristic::Name("NeoPixel"); new Characteristic::Manufacturer("HomeSpan"); new Characteristic::SerialNumber("123-ABC"); - new Characteristic::Model("SK68 LED"); + new Characteristic::Model("8-LED Strand"); new Characteristic::FirmwareRevision("1.0"); new Characteristic::Identify(); new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_Light(PIXEL_PIN); // create single Pixel Light + new Pixel_Light(NEOPIXEL_PIN,8); // create 8-LED NeoPixel Strand new SpanAccessory(); new Service::AccessoryInformation(); - new Characteristic::Name("Pixel Strand"); + new Characteristic::Name("DotStar"); new Characteristic::Manufacturer("HomeSpan"); new Characteristic::SerialNumber("123-ABC"); - new Characteristic::Model("8-LED NeoPixel"); + new Characteristic::Model("30-LED Strand"); new Characteristic::FirmwareRevision("1.0"); new Characteristic::Identify(); new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new Pixel_KnightRider(PIXEL_STRAND_PIN,8); // create 8-Pixel Strand with Knight-Rider Effect + new Dot_Light(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar Strand with Chasing-Light effect } From acdcab113222dae0f7c21e1d842df2633e0416ea Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Feb 2022 16:24:01 -0600 Subject: [PATCH 050/106] Verified Pixel() and Dot() classes work on ESP32-C3 --- src/extras/extras.ino | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 991a457..e222b7f 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -100,26 +100,18 @@ struct Effect3 { #if defined(CONFIG_IDF_TARGET_ESP32C3) - #define PIXEL_PIN_1 8 - #define PIXEL_PIN_2 1 - -#elif defined(CONFIG_IDF_TARGET_ESP32S2) - - #define PIXEL_PIN_1 18 - #define PIXEL_PIN_2 7 + Pixel px1(8); // NeoPixel RGB + Pixel px2(9,true); // NeoPixel RGBW + Dot dot(2,3); // DotStar #elif defined(CONFIG_IDF_TARGET_ESP32) - #define PIXEL_PIN_1 23 - #define PIXEL_PIN_2 21 - - Dot dot(32,5); + Pixel px1(23); // NeoPixel RGB + Pixel px2(21,true); // NeoPixel RGBW + Dot dot(32,5); // DotStar #endif -Pixel px1(PIXEL_PIN_1); -Pixel px2(PIXEL_PIN_2,true); - Effect1 effect1(&px1,20,8); Effect2 effect2(&px2,20,60); Effect3 effect3(&dot,20,30); From 9e4066623164cdb6215b9f16b6b6310a57a1a14d Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Feb 2022 23:30:28 -0600 Subject: [PATCH 051/106] Added Pixel() and Dot() operator overloads to Color struct Color now supports a==b, a!=b, a+b, a-b, a+=b, and a-=b operators --- src/extras/Pixel.h | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 37708be..f2bb087 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -25,6 +25,49 @@ class Pixel { }; uint32_t val; }; + + bool operator==(const Color& color){ + return(val==color.val); + } + + bool operator!=(const Color& color){ + return(val!=color.val); + } + + Color operator+(const Color& color){ + Color newColor; + newColor.white=white+color.white; + newColor.blue=blue+color.blue; + newColor.red=red+color.red; + newColor.green=green+color.green; + return(newColor); + } + + Color& operator+=(const Color& color){ + white+=color.white; + red+=color.red; + blue+=color.blue; + green+=color.green; + return(*this); + } + + Color operator-(const Color& color){ + Color newColor; + newColor.white=white-color.white; + newColor.blue=blue-color.blue; + newColor.red=red-color.red; + newColor.green=green-color.green; + return(newColor); + } + + Color& operator-=(const Color& color){ + white-=color.white; + red-=color.red; + blue-=color.blue; + green-=color.green; + return(*this); + } + }; private: @@ -83,6 +126,45 @@ class Dot { }; uint32_t val; }; + + bool operator==(const Color& color){ + return(val==color.val); + } + + bool operator!=(const Color& color){ + return(val!=color.val); + } + + Color operator+(const Color& color){ + Color newColor; + newColor.blue=blue+color.blue; + newColor.red=red+color.red; + newColor.green=green+color.green; + return(newColor); + } + + Color& operator+=(const Color& color){ + red+=color.red; + blue+=color.blue; + green+=color.green; + return(*this); + } + + Color operator-(const Color& color){ + Color newColor; + newColor.blue=blue-color.blue; + newColor.red=red-color.red; + newColor.green=green-color.green; + return(newColor); + } + + Color& operator-=(const Color& color){ + red-=color.red; + blue-=color.blue; + green-=color.green; + return(*this); + } + }; private: From f955ff689bdd72adc3f9bd40a2ee66cf07a02848 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 6 Feb 2022 08:39:47 -0600 Subject: [PATCH 052/106] Added homeSpan.setPairCallback() method Callback function must be of type void()(boolean isPaired). Will be called when device is paired and when device is unpaired. Allows for user-defineable actions upon device pairing/unpairing. --- src/HAP.cpp | 5 +++++ src/HomeSpan.h | 2 ++ src/src.ino | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 2120511..31f4435 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -661,6 +661,9 @@ int HAPClient::postPairSetupURL(){ LOG1("\n*** ACCESSORY PAIRED! ***\n"); homeSpan.statusLED.on(); + if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired + homeSpan.pairCallback(true); + return(1); break; @@ -1592,6 +1595,8 @@ void HAPClient::removeController(uint8_t *id){ LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) homeSpan.statusLED.start(LED_PAIRING_NEEDED); + if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired + homeSpan.pairCallback(false); } LOG2("\n"); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 8822342..3f1f215 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -131,6 +131,7 @@ struct Span{ char otaPwd[33]; // MD5 Hash of OTA password, represented as a string of hexidecimal characters boolean otaAuth; // OTA requires password when set to true void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established + void (*pairCallback)(boolean isPaired)=NULL; // optional callback function to invoke when pairing is established (true) or lost (false) boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found void (*apFunction)()=NULL; // optional function to invoke when starting Access Point @@ -191,6 +192,7 @@ struct Span{ void setSketchVersion(const char *sVer){sketchVersion=sVer;} // set optional sketch version number const char *getSketchVersion(){return sketchVersion;} // get sketch version number void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established + void setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;} // sets an optional user-defined function to call when Pairing is established (true) or lost (false) void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point void protectPinISR(uint8_t pin){ProtectedGPIOs[pin]=0;} // protects ISR on pin from NVS operations diff --git a/src/src.ino b/src/src.ino index 1aa3888..bb37e6d 100644 --- a/src/src.ino +++ b/src/src.ino @@ -50,7 +50,6 @@ void setup() { new Characteristic::On(0); new Characteristic::LightMode("HELLO"); new Characteristic::DarkMode(); - new Characteristic::DarkMode("OVERRIDE"); new Characteristic::Brightness(50); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); From 9e229cda9a88e7a7d13d437739ad0b73b6e550ad Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 7 Feb 2022 21:57:58 -0600 Subject: [PATCH 053/106] Update QRCodes.md --- docs/QRCodes.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/QRCodes.md b/docs/QRCodes.md index 009180d..463e7dc 100644 --- a/docs/QRCodes.md +++ b/docs/QRCodes.md @@ -11,7 +11,14 @@ In contrast, pairing a HomeKit device with a **QR Code** requires you to only sc This is possible because the QR Code includes a 4-character *Setup ID* that is unique to the device associated with the QR Code. The QR Code also embeds other paring information about the device, such as its Category (e.g. Light, Fan, Door) and its 8-digit *Setup Code*. -HomeSpan supports pairing with QR Codes and uses "HSPN" as its default *Setup ID*. However, if you have more than one device that you intend on pairing with a QR Code, you'll need to change each device's *Setup ID* from "HSPN" to something unique using the method `homeSpan.setQRID(const char *ID)` (see the [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details). If you do not intend on pairing your devices with QR Codes, it is fine for them to all have the same *Setup ID*, as this ID is only used to initiate the pairing process via a QR code and serves no other purpose. +HomeSpan supports pairing with QR Codes and uses "HSPN" as its default *Setup ID*. However, if you have more than one device that you intend on pairing with a QR Code, you'll need to enure each has a unique *Setup ID*. You can change the *Setup ID* on your device in one of two ways: + +* Store a new code in the device NVS by typing 'O \' into the HomeSpan [Command Line Interface](https://github.com/HomeSpan/HomeSpan/blob/master/docs/CLI.md); or +* Specify the QR *Setup ID* directly in your sketch using the method `homeSpan.setQRID(const char *ID)`. + +The order of preference is as follows: If your sketch contains a call to `homeSpan.setQRID(const char *ID)`, the specified ID is used. If not, HomeSpan will instead search the NVS for a stored *Setup ID*. If not found, HomeSpan defaults to using "HSPN" as the *Setup ID*. + +Note that if you do not intend on pairing your devices with QR Codes, it is fine for them to all retain the default *Setup ID* of "HSPN" since this ID is only used to initiate the pairing process via a QR code and serves no other purpose. ### Creating Scannable QR Codes From 4270760c402103c7b507e562d9aa79cdec97597f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 10 Feb 2022 06:21:53 -0600 Subject: [PATCH 054/106] Update Reference.md --- docs/Reference.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index 4e359ab..4cc900b 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -114,7 +114,13 @@ The following **optional** `homeSpan` methods enable additional features and pro > :warning: SECURITY WARNING: The purpose of this function is to allow advanced users to *dynamically* set the device's WiFi Credentials using a customized Access Point function specified by `setApFunction(func)`. It it NOT recommended to use this function to hardcode your WiFi SSID and password directly into your sketch. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'W' from the CLI, or launching HomeSpan's Access Point, to set your WiFi credentials without hardcoding them into your sketch * `void setWifiCallback(void (*func)())` - * Sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments + * sets an optional user-defined callback function, *func*, to be called by HomeSpan upon start-up just after WiFi connectivity has been established. This one-time call to *func* is provided for users that are implementing other network-related services as part of their sketch, but that cannot be started until WiFi connectivity is established. The function *func* must be of type *void* and have no arguments + +* `void setPairCallback(void (*func)(boolean status))` + * sets an optional user-defined callback function, *func*, to be called by HomeSpan upon completion of pairing to a controller (*status=true*) or unpairing from a controller (*status=false*) + * this one-time call to *func* is provided for users that would like to trigger additional actions when the device is first paired, or the device is later unpaired + * note this *func* is **not** called upon start-up and should not be used to simply check whether a device is paired or unpaired. It is only called when pairing status changes + * the function *func* must be of type *void* and have one *boolean* argument * `void setPairingCode(const char *s)` * sets the Setup Pairing Code to *s*, which **must** be exactly eight numerical digits (no dashes) @@ -123,6 +129,12 @@ The following **optional** `homeSpan` methods enable additional features and pro * if *s* contains an invalid code, an error will be reported and the code will *not* be saved. Instead, the currently-stored Pairing Code (or the HomeSpan default Pairing Code if no code has been stored) will be used * :exclamation: SECURTY WARNING: Hardcoding a device's Pairing Code into your sketch is considered a security risk and is **not** recommended. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'S \' from the CLI, or launching HomeSpan's Access Point, to set your Pairing Code without hardcoding it into your sketch +* void `processSerialCommand(char *c)` + * processes the character string *c* as if it were typed into the Arduino Serial Monitor + * allows for programmatic calls to [HomeSpan CLI commands](.../CLI.md) + * example: `homeSpan.processSerialCommand("V");` deletes the values of all stored Characteristics from the NVS + * example: `homeSpan.processSerialCommand("Q id");` changes the QR Code Setup ID to *id* (this is effectively the same as using the `setQRID()` method above) + * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string * if unspecified, HomeSpan uses "n/a" as the default version text From 65f000d65ef6f0febb2416bedeff5e0a3d951798 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 10 Feb 2022 20:48:16 -0600 Subject: [PATCH 055/106] Update Reference.md --- docs/Reference.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 4cc900b..e36bd44 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -129,11 +129,8 @@ The following **optional** `homeSpan` methods enable additional features and pro * if *s* contains an invalid code, an error will be reported and the code will *not* be saved. Instead, the currently-stored Pairing Code (or the HomeSpan default Pairing Code if no code has been stored) will be used * :exclamation: SECURTY WARNING: Hardcoding a device's Pairing Code into your sketch is considered a security risk and is **not** recommended. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'S \' from the CLI, or launching HomeSpan's Access Point, to set your Pairing Code without hardcoding it into your sketch -* void `processSerialCommand(char *c)` - * processes the character string *c* as if it were typed into the Arduino Serial Monitor - * allows for programmatic calls to [HomeSpan CLI commands](.../CLI.md) - * example: `homeSpan.processSerialCommand("V");` deletes the values of all stored Characteristics from the NVS - * example: `homeSpan.processSerialCommand("Q id");` changes the QR Code Setup ID to *id* (this is effectively the same as using the `setQRID()` method above) +* `deleteStoredValues()` + * deletes the value settings of all stored Characteristics from the NVS * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string From 32b417a1dab373b10043dfd21954ccf865e0983a Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 10 Feb 2022 21:02:10 -0600 Subject: [PATCH 056/106] Added homeSpan method deleteStoredValues() Provides a programmatic way of deleting all stored Characteristic values from NVS. Identical to the 'V' CLI Command. --- src/HomeSpan.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 3f1f215..da1ffac 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -197,6 +197,7 @@ struct Span{ void protectPinISR(uint8_t pin){ProtectedGPIOs[pin]=0;} // protects ISR on pin from NVS operations 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 enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found void setWifiCredentials(const char *ssid, const char *pwd); // sets WiFi Credentials From 8525495720f56f16761be74c93eb4dd4b27f234b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 10 Feb 2022 21:04:52 -0600 Subject: [PATCH 057/106] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index e36bd44..51a43b2 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -129,7 +129,7 @@ The following **optional** `homeSpan` methods enable additional features and pro * if *s* contains an invalid code, an error will be reported and the code will *not* be saved. Instead, the currently-stored Pairing Code (or the HomeSpan default Pairing Code if no code has been stored) will be used * :exclamation: SECURTY WARNING: Hardcoding a device's Pairing Code into your sketch is considered a security risk and is **not** recommended. Instead, use one of the more secure methods provided by HomeSpan, such as typing 'S \' from the CLI, or launching HomeSpan's Access Point, to set your Pairing Code without hardcoding it into your sketch -* `deleteStoredValues()` +* `void deleteStoredValues()` * deletes the value settings of all stored Characteristics from the NVS * `void setSketchVersion(const char *sVer)` From 1efe7bba0ac0925c69c8157c0058d7171f6d8831 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Thu, 10 Feb 2022 21:08:17 -0600 Subject: [PATCH 058/106] Update Reference.md --- docs/Reference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Reference.md b/docs/Reference.md index 51a43b2..cd9b88b 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -131,6 +131,7 @@ The following **optional** `homeSpan` methods enable additional features and pro * `void deleteStoredValues()` * deletes the value settings of all stored Characteristics from the NVS + * performs the same function as typing 'V' into the CLI * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string From 4973d1aaa1f55240294288db03c7ad3cc8dfb21e Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 10 Feb 2022 21:09:42 -0600 Subject: [PATCH 059/106] Revert "Initial coding of protectPinISR(pin) method" This reverts commit 852a916d614193fc9277bc8f66049cbfc5015622. --- src/HomeSpan.cpp | 18 ------------------ src/HomeSpan.h | 9 --------- 2 files changed, 27 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 5ecda27..0435c93 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1387,24 +1387,6 @@ void Span::checkRanges(){ homeSpan.configLog+="\n\n"; } -/////////////////////////////// - -void Span::protectGPIOs(boolean suspend){ - - auto gp=ProtectedGPIOs.begin(); - while(gp!=ProtectedGPIOs.end()){ - if(suspend){ - Serial.printf("PIN: %d Status: %d : ",gp->first,GPIO.pin[gp->first].int_type); - gp->second=GPIO.pin[gp->first].int_type; - GPIO.pin[gp->first].int_type=0; - Serial.printf("PIN: %d Status: %d\n",gp->first,GPIO.pin[gp->first].int_type); - } else { - GPIO.pin[gp->first].int_type=gp->second; - } - gp++; - } -} - /////////////////////////////// // SpanAccessory // /////////////////////////////// diff --git a/src/HomeSpan.h b/src/HomeSpan.h index da1ffac..607f0e0 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -146,7 +146,6 @@ struct Span{ vector Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event vector PushButtons; // vector of pointer to all PushButtons unordered_map TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs) - unordered_map ProtectedGPIOs; // map of pins to protect when running an ISR during NVS operations unordered_map UserCommands; // map of pointers to all UserCommands @@ -161,7 +160,6 @@ struct Span{ void commandMode(); // allows user to control and reset HomeSpan settings with the control button void processSerialCommand(const char *c); // process command 'c' (typically from readSerial, though can be called with any 'c') void checkRanges(); // checks values of all Characteristics to ensure they are each within range - void protectGPIOs(boolean suspend); // true - disable ISRs on protectedGPIOs; false - re-enable ISRs on protectedGPIOs int sprintfAttributes(char *cBuf); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL void prettyPrint(char *buf, int nsp=2); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces @@ -194,7 +192,6 @@ struct Span{ void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established void setPairCallback(void (*f)(boolean isPaired)){pairCallback=f;} // sets an optional user-defined function to call when Pairing is established (true) or lost (false) void setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point - void protectPinISR(uint8_t pin){ProtectedGPIOs[pin]=0;} // protects ISR on pin from NVS operations 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 @@ -415,7 +412,6 @@ struct SpanCharacteristic{ uvSet(value,val); if(nvsStore){ - homeSpan.protectGPIOs(true); nvsKey=(char *)malloc(16); uint16_t t; sscanf(type,"%x",&t); @@ -445,7 +441,6 @@ struct SpanCharacteristic{ nvsFlag=1; } } - homeSpan.protectGPIOs(false); } uvSet(newValue,value); @@ -542,10 +537,8 @@ struct SpanCharacteristic{ homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector if(nvsKey){ - homeSpan.protectGPIOs(true); nvs_set_str(homeSpan.charNVS,nvsKey,value.STRING); // store data nvs_commit(homeSpan.charNVS); - homeSpan.protectGPIOs(false); } } // setString() @@ -576,10 +569,8 @@ struct SpanCharacteristic{ homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector if(nvsKey){ - homeSpan.protectGPIOs(true); nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data nvs_commit(homeSpan.charNVS); - homeSpan.protectGPIOs(false); } } From ac88329d34a742be3fa830eb3d616cb6a5c5a690 Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 10 Feb 2022 21:55:03 -0600 Subject: [PATCH 060/106] Deprecated setMaxConnections(); Added reserveSocketConnections() Use homeSpan.reserveSocketConnections(n) to reserve n sockets *not* to be used for HAP. Multiple calls can be used to cumulate a total number of reserved sockets. This makes is easy to add reserveSocketConnections(n) at multiple point in the code whenever a certain number of sockets need to be reserved for use with that portion of the code. For example enableOTA() calls reserveSocketConnections(1). If both setMaxConnections(), and one or more reserveSocketConnections(), methods are called HomeSpan will use the more restrictive net value. --- src/HomeSpan.cpp | 7 +++---- src/HomeSpan.h | 20 +++++++++++++------- src/Settings.h | 1 - 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 0435c93..f36956e 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -60,10 +60,9 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa controlButton.init(controlPin); statusLED.init(statusPin,0,autoOffLED); - int maxLimit=CONFIG_LWIP_MAX_SOCKETS-2-otaEnabled; - if(maxConnections>maxLimit) - maxConnections=maxLimit; - + if(requestedMaxCon Date: Fri, 11 Feb 2022 07:51:55 -0600 Subject: [PATCH 061/106] Update Reference.md --- docs/Reference.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index cd9b88b..75c95ea 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -61,12 +61,14 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * 2 = all status messages plus all HAP communication packets to and from the HomeSpan device * this parameter can also be changed at runtime via the [HomeSpan CLI](CLI.md) -* `void setMaxConnections(uint8_t nCon)` - * sets the desired maximum number of HAP Controllers that can be simultaneously connected to HomeSpan (default=14) - * due to limitations of the ESP32 Arduino library, HomeSpan will override *nCon* if it exceed the following internal limits: - * if OTA is not enabled, *nCon* will be reduced to 14 if it has been set to a value greater than 14 - * if OTA is enabled, *nCon* will be reduced to 13 if it has been set to a value greater than 13 - * if you add code to a sketch that uses it own network resources, you will need to determine how many TCP sockets your code may need to use, and use this method to reduce the maximum number of connections available to HomeSpan accordingly +* `void reserveSocketConnections(uint8_t nSockets)` + * reserves *nSockets* network sockets for uses **other than** by the HomeSpan HAP Server for HomeKit Controller Connections + * for sketches compiled under Arduino-ESP32 v2.0.2, HomeSpan reserves 14 sockets for HAP Controller Connections + * each call to `reserveSocketConnections(nSockets)` reduces this number by *nSockets* + * use this method if you add code to a sketch that requires its own socket connections (e.g. a separate web service, an MQTT server, etc.) + * multiple calls to this method are allowed - the number of sockets reserved will be the sum of *nSockets* across all calls + * note you do not need to separately reserve sockets for built-in HomeSpan functionality + * for example, `enableOTA()` already contains an embedded call to `reserveSocketConnections(1)` since HomeSpan knows one socket must be reserved to support OTA * `void setPortNum(uint16_t port)` * sets the TCP port number used for communication between HomeKit and HomeSpan (default=80) @@ -91,6 +93,7 @@ The following **optional** `homeSpan` methods enable additional features and pro * 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" * 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 * `void enableAutoStartAP()` * enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time @@ -371,6 +374,11 @@ Note that Custom Characteristics must be created prior to calling `homeSpan.begi * last supported version: [v1.2.0](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md#spanrangeint-min-int-max-int-step) * **please use** `setRange(min, max, step)` **for all new sketches** +`void homeSpan.setMaxConnections(uint8_t nCon)` + * this legacy function was used to reduce the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources + * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md) + * **please use** `homeSpan.reserveSocketsConnections(uint8_t nSockets)` **for all new sketches** + --- [↩️](README.md) Back to the Welcome page From e0855e9b52be533dbade66cbe30d6a4a4374086c Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 09:05:01 -0600 Subject: [PATCH 062/106] Update Reference.md --- docs/Reference.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 75c95ea..cfa25ca 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -368,16 +368,17 @@ Note that Custom Characteristics must be created prior to calling `homeSpan.begi #### Deprecated functions (available for backwards compatibility with older sketches): -*SpanRange(int min, int max, int step)* +* `SpanRange(int min, int max, int step)` - * this legacy function is limited to integer-based parameters and has been re-coded to simply call the more generic `setRange(min, max, step)` method + * this legacy class was limited to integer-based parameters and has been re-coded to simply call the more generic `setRange(min, max, step)` method * last supported version: [v1.2.0](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md#spanrangeint-min-int-max-int-step) * **please use** `setRange(min, max, step)` **for all new sketches** -`void homeSpan.setMaxConnections(uint8_t nCon)` - * this legacy function was used to reduce the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources +* `void homeSpan.setMaxConnections(uint8_t nCon)` + * this legacy method was used to set the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources + * this has been replaces by the more flexible method, `reserveSocketConnections(uint8_t nSockets)`, that allows you to simply reserve network sockets for other functions as needed - HomeSpan will then automarically determine how many sockts are left that it can use for HAP Controller Connections * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md) - * **please use** `homeSpan.reserveSocketsConnections(uint8_t nSockets)` **for all new sketches** + * **please use** `homeSpan.reserveSocketConnections(uint8_t nSockets)` **for all new sketches** --- From a198de8de918ccd279fc8c9a6efe92e8658193ba Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 09:08:29 -0600 Subject: [PATCH 063/106] Update Reference.md --- docs/Reference.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index cfa25ca..ecf23ba 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -86,7 +86,7 @@ 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: +The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment and **should** be called before `begin()` to take effect: * `void enableOTA(boolean auth=true)` * enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled @@ -376,7 +376,9 @@ Note that Custom Characteristics must be created prior to calling `homeSpan.begi * `void homeSpan.setMaxConnections(uint8_t nCon)` * this legacy method was used to set the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources - * this has been replaces by the more flexible method, `reserveSocketConnections(uint8_t nSockets)`, that allows you to simply reserve network sockets for other functions as needed - HomeSpan will then automarically determine how many sockts are left that it can use for HAP Controller Connections + * this has been replaces by the more flexible method, `reserveSocketConnections(uint8_t nSockets)` + * allows you to simply reserve network sockets for other custom code as needed + * HomeSpan will then automatically determine how many sockets are left that it can use for HAP Controller Connections * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md) * **please use** `homeSpan.reserveSocketConnections(uint8_t nSockets)` **for all new sketches** From e54f4fbc3f9263e06583537c6de07ac20c5650f7 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 09:09:40 -0600 Subject: [PATCH 064/106] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index ecf23ba..5ae0837 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -86,7 +86,7 @@ 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 and **should** be called before `begin()` to take effect: +The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment. Calls **should** be made before `begin()` to take effect: * `void enableOTA(boolean auth=true)` * enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled From f8d69316fafd804d54b05e411c500175c9032d66 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 09:15:31 -0600 Subject: [PATCH 065/106] Update Reference.md --- docs/Reference.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 5ae0837..dc4290e 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -86,7 +86,7 @@ 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. 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)` * enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled @@ -135,6 +135,7 @@ The following **optional** `homeSpan` methods enable additional features and pro * `void deleteStoredValues()` * deletes the value settings of all stored Characteristics from the NVS * performs the same function as typing 'V' into the CLI + * can by called from anywhere in a sketch * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string @@ -144,6 +145,7 @@ The following **optional** `homeSpan` methods enable additional features and pro * `const char *getSketchVersion()` * returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set + * can by called from anywhere in a sketch ## *SpanAccessory(uint32_t aid)* @@ -376,10 +378,10 @@ Note that Custom Characteristics must be created prior to calling `homeSpan.begi * `void homeSpan.setMaxConnections(uint8_t nCon)` * this legacy method was used to set the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources - * this has been replaces by the more flexible method, `reserveSocketConnections(uint8_t nSockets)` - * allows you to simply reserve network sockets for other custom code as needed - * HomeSpan will then automatically determine how many sockets are left that it can use for HAP Controller Connections * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md) + * this method has been replaced by the more flexible method, `reserveSocketConnections(uint8_t nSockets)` + * allows you to simply reserve network sockets for other custom code as needed + * upon calling `homespan.begin()`, HomeSpan automatically determines how many sockets are left that it can use for HAP Controller Connections * **please use** `homeSpan.reserveSocketConnections(uint8_t nSockets)` **for all new sketches** --- From 9f497f7f616343c43d1ede0961a4b0ec18d4a602 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 09:16:12 -0600 Subject: [PATCH 066/106] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index dc4290e..77e8b89 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -378,7 +378,7 @@ Note that Custom Characteristics must be created prior to calling `homeSpan.begi * `void homeSpan.setMaxConnections(uint8_t nCon)` * this legacy method was used to set the total number of HAP Controller Connections HomeSpan implements upon start-up to ensure there are still free sockets available for user-defined code requiring separate network resources - * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md) + * last supported version: [v1.4.2](https://github.com/HomeSpan/HomeSpan/blob/release-1.4.2/docs/Reference.md) * this method has been replaced by the more flexible method, `reserveSocketConnections(uint8_t nSockets)` * allows you to simply reserve network sockets for other custom code as needed * upon calling `homespan.begin()`, HomeSpan automatically determines how many sockets are left that it can use for HAP Controller Connections From c40c744702de0a56dd8b39bb51b8bf85b5eb3d6c Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 11 Feb 2022 09:24:51 -0600 Subject: [PATCH 067/106] Small modification to status message --- src/HomeSpan.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index f36956e..835e510 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -559,9 +559,8 @@ void Span::checkConnect(){ } } - Serial.print("Starting Web (HTTP) Server supporting up to "); - Serial.print(maxConnections); - Serial.print(" simultaneous connections...\n"); + Serial.printf("Starting HAP Server on port %d supporting %d simultaneous HomeKit Controller Connections...\n",tcpPortNum,maxConnections); + hapServer->begin(); Serial.print("\n"); From effe8e596514f3cc2d6a76c9f8d83580aecae56f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 11 Feb 2022 15:49:37 -0600 Subject: [PATCH 068/106] Update Reference.md --- docs/Reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Reference.md b/docs/Reference.md index 77e8b89..73e0f57 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -63,7 +63,7 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * `void reserveSocketConnections(uint8_t nSockets)` * reserves *nSockets* network sockets for uses **other than** by the HomeSpan HAP Server for HomeKit Controller Connections - * for sketches compiled under Arduino-ESP32 v2.0.2, HomeSpan reserves 14 sockets for HAP Controller Connections + * for sketches compiled under Arduino-ESP32 v2.0.1 or later, HomeSpan reserves 14 sockets for HAP Controller Connections * each call to `reserveSocketConnections(nSockets)` reduces this number by *nSockets* * use this method if you add code to a sketch that requires its own socket connections (e.g. a separate web service, an MQTT server, etc.) * multiple calls to this method are allowed - the number of sockets reserved will be the sum of *nSockets* across all calls From eee0eb69545d377ab19478a803dad7fce0572d7b Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 12 Feb 2022 15:31:55 -0600 Subject: [PATCH 069/106] Embedded HSV and RGB methods inside Color object Makes coding with Pixel() and Dot() easier --- src/extras/Pixel.cpp | 39 -------------------------------------- src/extras/Pixel.h | 44 ++++++++++++++++++++++++++++++++++++++----- src/extras/extras.ino | 23 ++++++++++++---------- 3 files changed, 52 insertions(+), 54 deletions(-) diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 3d16e29..7b8d925 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -70,25 +70,6 @@ void Pixel::set(Color *c, int nPixels, boolean multiColor){ /////////////////// -Pixel::Color Pixel::RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t white){ - Color x; - x.red=red; - x.green=green; - x.blue=blue; - x.white=white; - return(x); -} - -/////////////////// - -Pixel::Color Pixel::HSV(float h, float s, float v, double w){ - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - return(RGB(r*255,g*255,b*255,w*2.555)); -} - -/////////////////// - void IRAM_ATTR Pixel::loadData(void *arg){ if(RMT.int_st.val & status.px->txEndMask){ @@ -188,24 +169,4 @@ void Dot::set(Color *c, int nPixels, boolean multiColor){ } } -/////////////////// - -Dot::Color Dot::RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive){ - Color x; - x.red=red; - x.green=green; - x.blue=blue; - x.drive=drive; - x.flags=7; - return(x); -} - -/////////////////// - -Dot::Color Dot::HSV(float h, float s, float v, double level){ - float r,g,b; - LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); - return(RGB(r*255,g*255,b*255,level*0.315)); -} - //////////////////////////////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index f2bb087..dae3290 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -26,6 +26,24 @@ class Pixel { uint32_t val; }; + Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){ // returns Color based on provided RGB(W) values where r/g/b/w=[0-255] + this->red=r; + this->green=g; + this->blue=b; + this->white=w; + return(*this); + } + + Color HSV(float h, float s, float v, double w=0){ // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100] + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + this->red=r*255; + this->green=g*255; + this->blue=b*255; + this->white=w*2.555; + return(*this); + } + bool operator==(const Color& color){ return(val==color.val); } @@ -68,7 +86,7 @@ class Pixel { return(*this); } - }; + }; // Color private: struct pixel_status_t { @@ -95,8 +113,6 @@ class Pixel { public: Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) - static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t white=0); // returns Color based on provided RGB(W) values where r/g/b/w=[0-255] - static Color HSV(float h, float s, float v, double w=0); // returns Color based on provided HSV(W) values where h=[0,360] and s/v/w=[0,100] void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c @@ -127,6 +143,26 @@ class Dot { uint32_t val; }; + Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){ // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive level=[0,31] + this->red=r; + this->green=g; + this->blue=b; + this->drive=driveLevel; + this->flags=7; + return(*this); + } + + Color HSV(float h, float s, float v, double drivePercent=100){ // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive percent=[0,100] + float r,g,b; + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); + this->red=r*255; + this->green=g*255; + this->blue=b*255; + this->drive=drivePercent*0.315; + this->flags=7; + return(*this); + } + bool operator==(const Color& color){ return(val==color.val); } @@ -177,8 +213,6 @@ class Dot { public: Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102) - static Color RGB(uint8_t red, uint8_t green, uint8_t blue, uint8_t drive=31); // returns Color based on provided RGB values where r/g/b=[0-255] and current-limiting drive=[0,31] - static Color HSV(float h, float s, float v, double level=100); // returns Color based on provided HSV values where h=[0,360], s/v=[0,100], and current-limiting drive=[0,100] void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c }; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e222b7f..0c53562 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -9,6 +9,7 @@ struct Effect1 { uint32_t alarmTime=0; uint32_t speed; uint8_t nPixels; + Pixel::Color c; Effect1(Pixel *px, uint32_t speed, uint8_t nPixels){ this->px=px; @@ -19,8 +20,8 @@ struct Effect1 { void update(){ if(millis()set(px->HSV(H,100,10),nPixels); + + px->set(c.HSV(H,100,10),nPixels); H=(H+1)%360; alarmTime=millis()+speed; @@ -50,11 +51,11 @@ struct Effect2 { for(int i=0;iHSV(H,100,100); + x[i].HSV(H,100,100); else if(i==nPixels-1-phase) - x[i]=px->HSV(H+180,100,100); + x[i].HSV(H+180,100,100); else - x[i]=Pixel::HSV(0,0,0); + x[i].RGB(0,0,0); } px->set(x,nPixels); @@ -80,6 +81,7 @@ struct Effect3 { uint32_t alarmTime=0; uint32_t speed; uint8_t nPixels; + Dot::Color c; Effect3(Dot *dot, uint32_t speed, uint8_t nPixels){ this->dot=dot; @@ -91,7 +93,7 @@ struct Effect3 { if(millis()set(Dot::HSV(H,100,100),nPixels); + dot->set(c.HSV(H,100,100),nPixels); H=(H+1)%360; alarmTime=millis()+speed; @@ -106,14 +108,15 @@ struct Effect3 { #elif defined(CONFIG_IDF_TARGET_ESP32) - Pixel px1(23); // NeoPixel RGB + Pixel px1(23,true); // NeoPixel RGB Pixel px2(21,true); // NeoPixel RGBW Dot dot(32,5); // DotStar #endif -Effect1 effect1(&px1,20,8); -Effect2 effect2(&px2,20,60); +//Effect1 effect1(&px1,20,60); +//Effect2 effect2(&px2,20,60); +Effect2 effect2(&px1,20,60); Effect3 effect3(&dot,20,30); void setup() { @@ -127,7 +130,7 @@ void setup() { } // end of setup() void loop(){ - effect1.update(); +// effect1.update(); effect2.update(); effect3.update(); } From a4fb99a684fdf57ffca308b72c4bf2825095975c Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Feb 2022 11:57:34 -0600 Subject: [PATCH 070/106] Updated Pixel Example --- Other Examples/Pixel/Pixel.ino | 150 +++++++++++++++++++++------------ src/extras/extras.ino | 26 ++++-- 2 files changed, 115 insertions(+), 61 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index a950d41..e7bf8d1 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -33,17 +33,20 @@ // IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD // +#define NEOPIXEL_RGB_PIN 26 +#define NEOPIXEL_RGBW_PIN 32 +#define DOTSTAR_DATA_PIN 33 +#define DOTSTAR_CLOCK_PIN 27 + #define NEOPIXEL_PIN 23 // only one pin needed for NeoPixels -#define DOTSTAR_DATA_PIN 32 // two pins are needed to DotStars -#define DOTSTAR_CLOCK_PIN 5 #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class /////////////////////////////// -struct Pixel_Light : Service::LightBulb { // Addressable single-wire RGB LED Strand (e.g. NeoPixel) +struct NeoPixel_RGB : Service::LightBulb { // Addressable single-wire RGB LED Strand (e.g. NeoPixel) Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; @@ -52,10 +55,10 @@ struct Pixel_Light : Service::LightBulb { // Addressable single-wire RGB LE Pixel *pixel; uint8_t nPixels; - Pixel_Light(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){ + NeoPixel_RGB(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){ V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% - pixel=new Pixel(pin); // creates Pixel LED on specified pin using default timing parameters suitable for most SK68xx LEDs + pixel=new Pixel(pin); // creates Pixel LED on specified pin this->nPixels=nPixels; // save number of Pixels in this LED Strand update(); // manually call update() to set pixel with restored initial values } @@ -68,7 +71,9 @@ struct Pixel_Light : Service::LightBulb { // Addressable single-wire RGB LE float s=S.getNewVal(); // range = [0,100] float v=V.getNewVal(); // range = [0,100] - pixel->set(Pixel::HSV(h*p, s*p, v*p),nPixels); // sets all nPixels to the same HSV color + Pixel::Color color; + + pixel->set(color.HSV(h*p, s*p, v*p),nPixels); // sets all nPixels to the same HSV color return(true); } @@ -76,58 +81,72 @@ struct Pixel_Light : Service::LightBulb { // Addressable single-wire RGB LE /////////////////////////////// -struct Dot_Light : Service::LightBulb { // Addressable two-wire RGB LED Strand (e.g. DotStar) - Chasing Light Effect +struct NeoPixel_RGBW : Service::LightBulb { // Addressable single-wire RGBW LED Strand (e.g. NeoPixel) + + Characteristic::On power{0,true}; + Characteristic::Brightness V{100,true}; + Characteristic::ColorTemperature T{140,true}; + Pixel *pixel; + uint8_t nPixels; + + NeoPixel_RGBW(uint8_t pin, uint8_t nPixels) : Service::LightBulb(){ + + V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% + pixel=new Pixel(pin,true); // creates Pixel RGBW LED (second parameter set to true for RGBW) on specified pin + this->nPixels=nPixels; // save number of Pixels in this LED Strand + update(); // manually call update() to set pixel with restored initial values + } + + boolean update() override { + + int p=power.getNewVal(); + + float v=V.getNewVal(); // range = [0,100] + float t=T.getNewVal(); // range = [140,500] (140=coldest, 500=warmest) + + float hue=240-(t-140)/3; // add in a splash of color between blue and green to simulated change of color temperature + + Pixel::Color color; + + pixel->set(color.HSV(hue, 100, v*p, v*p),nPixels); // sets all nPixels to the same HSV color + + return(true); + } +}; + +/////////////////////////////// + +struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED Strand (e.g. DotStar) Characteristic::On power{0,true}; Characteristic::Hue H{0,true}; Characteristic::Saturation S{0,true}; Characteristic::Brightness V{100,true}; - Dot *dot; - uint8_t nPixels; - Dot::Color *colors; - int phase=0; - int dir=1; - uint32_t alarmTime=0; + Dot *pixel; + uint8_t nPixels; - Dot_Light(uint8_t dataPin, uint8_t clockPin, uint8_t nPixels) : Service::LightBulb(){ + DotStar_RGB(uint8_t dataPin, uint8_t clockPin, uint8_t nPixels) : Service::LightBulb(){ - dot=new Dot(dataPin,clockPin); // creates Dot LED on specified pins - suitable for APA102 or SK9822 + V.setRange(5,100,1); // sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% + pixel=new Dot(dataPin,clockPin); // creates Dot LED on specified pins this->nPixels=nPixels; // save number of Pixels in this LED Strand - - colors=(Dot::Color *)calloc(2*nPixels-1,sizeof(Dot::Color)); // storage for dynamic pixel pattern - update(); // manually call update() to set pixel with restored initial values + update(); // manually call update() to set pixel with restored initial values } boolean update() override { - if(!power.getNewVal()) - dot->set(Dot::RGB(0,0,0),nPixels); // turn off all LEDs in Strand + int p=power.getNewVal(); + float h=H.getNewVal(); // range = [0,360] + float s=S.getNewVal(); // range = [0,100] + float v=V.getNewVal(); // range = [0,100] + + Dot::Color color; + + pixel->set(color.HSV(h*p, s*p, 100, v*p),nPixels); // sets all nPixels to the same HSV color, but instead of PWM, using current-limiting parameter to control overall brightness + return(true); } - - void loop() override { - - if(millis()>alarmTime && power.getVal()){ - alarmTime=millis()+40; - colors[phase]=Dot::HSV(0,0,0); - if(phase==nPixels-1) - dir=-1; - else if(phase==0) - dir=1; - phase+=dir; - - float h=H.getNewVal(); // range = [0,360] - float s=S.getNewVal(); // range = [0,100] - float v=V.getNewVal(); // range = [0,100] - - colors[phase]=Dot::HSV(h,s,100,v); - - dot->set(colors,nPixels); // update Strand with new colors - } - - } - }; /////////////////////////////// @@ -136,35 +155,58 @@ void setup() { Serial.begin(115200); - homeSpan.begin(Category::Lighting,"RGB Pixels"); + homeSpan.begin(Category::Lighting,"Addressable LEDs"); + + new SpanAccessory(); // create Bridge + new Service::AccessoryInformation(); + new Characteristic::Name("Addressable LEDs"); + new Characteristic::Manufacturer("HomeSpan"); + new Characteristic::SerialNumber("123-ABC"); + new Characteristic::Model("Neo/Dot Pixels"); + new Characteristic::FirmwareRevision("1.0"); + new Characteristic::Identify(); + + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); + +///////// new SpanAccessory(); new Service::AccessoryInformation(); - new Characteristic::Name("NeoPixel"); + new Characteristic::Name("Neo RGB"); new Characteristic::Manufacturer("HomeSpan"); new Characteristic::SerialNumber("123-ABC"); new Characteristic::Model("8-LED Strand"); new Characteristic::FirmwareRevision("1.0"); new Characteristic::Identify(); + + new NeoPixel_RGB(NEOPIXEL_RGB_PIN,8); // create 8-LED NeoPixel RGB Strand with full color control - new Service::HAPProtocolInformation(); - new Characteristic::Version("1.1.0"); +///////// - new Pixel_Light(NEOPIXEL_PIN,8); // create 8-LED NeoPixel Strand - new SpanAccessory(); new Service::AccessoryInformation(); - new Characteristic::Name("DotStar"); + new Characteristic::Name("Neo RGBW"); + new Characteristic::Manufacturer("HomeSpan"); + new Characteristic::SerialNumber("123-ABC"); + new Characteristic::Model("60-LED Strand"); + new Characteristic::FirmwareRevision("1.0"); + new Characteristic::Identify(); + + new NeoPixel_RGBW(NEOPIXEL_RGBW_PIN,60); // create 60-LED NeoPixel RGBW Strand with simulated color temperature control + +///////// + + new SpanAccessory(); + new Service::AccessoryInformation(); + new Characteristic::Name("Dot RGB"); new Characteristic::Manufacturer("HomeSpan"); new Characteristic::SerialNumber("123-ABC"); new Characteristic::Model("30-LED Strand"); new Characteristic::FirmwareRevision("1.0"); new Characteristic::Identify(); - new Service::HAPProtocolInformation(); - new Characteristic::Version("1.1.0"); - - new Dot_Light(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar Strand with Chasing-Light effect + new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand with full color control, but use current-limiting feature to create flicker-free dimming } diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 0c53562..9c9fa83 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -108,16 +108,17 @@ struct Effect3 { #elif defined(CONFIG_IDF_TARGET_ESP32) - Pixel px1(23,true); // NeoPixel RGB - Pixel px2(21,true); // NeoPixel RGBW - Dot dot(32,5); // DotStar +// Pixel px1(23,true); // NeoPixel RGB +// Dot dot(32,5); // DotStar + Pixel px2(21); // NeoPixel RGBW + Pixel neo(26); #endif //Effect1 effect1(&px1,20,60); //Effect2 effect2(&px2,20,60); -Effect2 effect2(&px1,20,60); -Effect3 effect3(&dot,20,30); +//Effect2 effect2(&px1,20,60); +//Effect3 effect3(&dot,20,30); void setup() { @@ -126,11 +127,22 @@ void setup() { delay(1000); // wait for interface to flush Serial.println("\n\nHomeSpan Pixel Example\n"); + + Pixel::Color c; + int hue=0; + +// Pixel px2(21); // NeoPixel RGBW + + while(1){ + neo.set(c.HSV(hue,100,10),8); + hue=(hue+10)%360; + delay(100); + } } // end of setup() void loop(){ // effect1.update(); - effect2.update(); - effect3.update(); +// effect2.update(); +// effect3.update(); } From 5e72dcbbea7d1a8643f1da9458d974c3c19974b1 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Feb 2022 14:39:34 -0600 Subject: [PATCH 071/106] Completed Pixel Example Example contains 1 NeoPixel RGB, 1 NeoPixel RGBW, and 1 DotStar RGB. Tested on ESP32, ESP32-S2, and ESP32-C3. --- Other Examples/Pixel/Pixel.ino | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index e7bf8d1..3d94653 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -33,13 +33,31 @@ // IMPORTANT: YOU LIKELY WILL NEED TO CHANGE THE PIN NUMBERS BELOW TO MATCH YOUR SPECIFIC ESP32/S2/C3 BOARD // -#define NEOPIXEL_RGB_PIN 26 -#define NEOPIXEL_RGBW_PIN 32 -#define DOTSTAR_DATA_PIN 33 -#define DOTSTAR_CLOCK_PIN 27 +#if defined(CONFIG_IDF_TARGET_ESP32) -#define NEOPIXEL_PIN 23 // only one pin needed for NeoPixels + #define NEOPIXEL_RGB_PIN 26 + #define NEOPIXEL_RGBW_PIN 32 + #define DOTSTAR_DATA_PIN 33 + #define DOTSTAR_CLOCK_PIN 27 + #define DEVICE_SUFFIX "" +#elif defined(CONFIG_IDF_TARGET_ESP32S2) + + #define NEOPIXEL_RGB_PIN 17 + #define NEOPIXEL_RGBW_PIN 38 + #define DOTSTAR_DATA_PIN 3 + #define DOTSTAR_CLOCK_PIN 7 + #define DEVICE_SUFFIX "-S2" + +#elif defined(CONFIG_IDF_TARGET_ESP32C3) + + #define NEOPIXEL_RGB_PIN 8 + #define NEOPIXEL_RGBW_PIN 2 + #define DOTSTAR_DATA_PIN 0 + #define DOTSTAR_CLOCK_PIN 1 + #define DEVICE_SUFFIX "-C3" + +#endif #include "HomeSpan.h" #include "extras/Pixel.h" // include the HomeSpan Pixel class @@ -155,11 +173,11 @@ void setup() { Serial.begin(115200); - homeSpan.begin(Category::Lighting,"Addressable LEDs"); + homeSpan.begin(Category::Lighting,"Pixel LEDS" DEVICE_SUFFIX); new SpanAccessory(); // create Bridge new Service::AccessoryInformation(); - new Characteristic::Name("Addressable LEDs"); + new Characteristic::Name("Pixel LEDS" DEVICE_SUFFIX); new Characteristic::Manufacturer("HomeSpan"); new Characteristic::SerialNumber("123-ABC"); new Characteristic::Model("Neo/Dot Pixels"); From a83d3a7fdab09fafbc3dad48dac240eb50d9b5bc Mon Sep 17 00:00:00 2001 From: Gregg Date: Thu, 17 Feb 2022 18:27:31 -0600 Subject: [PATCH 072/106] Added static HSV and RGB color methods to Pixel() and Dot() Updated Pixel example to demonstrate use of the static HSV method --- Other Examples/Pixel/Pixel.ino | 15 ++++++++++----- src/extras/Pixel.h | 7 +++++++ src/extras/extras.ino | 1 + 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Other Examples/Pixel/Pixel.ino b/Other Examples/Pixel/Pixel.ino index 3d94653..f32643a 100644 --- a/Other Examples/Pixel/Pixel.ino +++ b/Other Examples/Pixel/Pixel.ino @@ -124,9 +124,9 @@ struct NeoPixel_RGBW : Service::LightBulb { // Addressable single-wire RGBW float hue=240-(t-140)/3; // add in a splash of color between blue and green to simulated change of color temperature - Pixel::Color color; + // Pixel::Color color; // if static HSV method is used (below), there is no need to first create a Color object - pixel->set(color.HSV(hue, 100, v*p, v*p),nPixels); // sets all nPixels to the same HSV color + pixel->set(pixel->HSV(hue, 100, v*p, v*p),nPixels); // sets all nPixels to the same HSV color (note use of static method pixel->HSV, instead of defining and setting Pixel::Color) return(true); } @@ -159,9 +159,14 @@ struct DotStar_RGB : Service::LightBulb { // Addressable two-wire RGB LED S float s=S.getNewVal(); // range = [0,100] float v=V.getNewVal(); // range = [0,100] - Dot::Color color; + Dot::Color color[nPixels]; // create an arrary of Colors - pixel->set(color.HSV(h*p, s*p, 100, v*p),nPixels); // sets all nPixels to the same HSV color, but instead of PWM, using current-limiting parameter to control overall brightness + float hueStep=360.0/nPixels; // step size for change in hue from one pixel to the next + + for(int i=0;iset(color,nPixels); // set the colors according to the array return(true); } @@ -224,7 +229,7 @@ void setup() { new Characteristic::FirmwareRevision("1.0"); new Characteristic::Identify(); - new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand with full color control, but use current-limiting feature to create flicker-free dimming + new DotStar_RGB(DOTSTAR_DATA_PIN,DOTSTAR_CLOCK_PIN,30); // create 30-LED DotStar RGB Strand displaying a spectrum of colors and using the current-limiting feature of DotStars to create flicker-free dimming } diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index dae3290..de320aa 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -115,6 +115,9 @@ class Pixel { Pixel(int pin, boolean isRGBW=false); // creates addressable single-wire RGB (false) or RGBW (true) LED connected to pin (such as the SK68 or WS28) void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c + + static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0){return(Color().RGB(r,g,b,w));} // an alternative method for returning an RGB Color + static Color HSV(float h, float s, float v, double w=0){return(Color().HSV(h,s,v,w));} // an alternative method for returning an HSV Color int getPin(){return(rf->getPin());} // returns pixel pin if valid, else returns -1 void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset); // changes default timings for bit pulse - note parameters are in MICROSECONDS @@ -215,6 +218,10 @@ class Dot { Dot(uint8_t dataPin, uint8_t clockPin); // creates addressable two-wire RGB LED connected to dataPin and clockPin (such as the DotStar SK9822 or APA102) void set(Color *c, int nPixels, boolean multiColor=true); // sets colors of nPixels based on array of Colors c; setting multiColor to false repeats Color in c[0] for all nPixels void set(Color c, int nPixels=1){set(&c,nPixels,false);} // sets color of nPixels to be equal to specific Color c + + static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31){return(Color().RGB(r,g,b,driveLevel));} // an alternative method for returning an RGB Color + static Color HSV(float h, float s, float v, double drivePercent=100){return(Color().HSV(h,s,v,drivePercent));} // an alternative method for returning an HSV Color + }; //////////////////////////////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 9c9fa83..748deba 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -105,6 +105,7 @@ struct Effect3 { Pixel px1(8); // NeoPixel RGB Pixel px2(9,true); // NeoPixel RGBW Dot dot(2,3); // DotStar + Pixel neo(0); #elif defined(CONFIG_IDF_TARGET_ESP32) From 2b49a8cba123ea2493e756bd6ab2378baaed6462 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 18 Feb 2022 17:57:44 -0600 Subject: [PATCH 073/106] Create Pixels.md --- docs/Pixels.md | 218 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 docs/Pixels.md diff --git a/docs/Pixels.md b/docs/Pixels.md new file mode 100644 index 0000000..9b0cb7d --- /dev/null +++ b/docs/Pixels.md @@ -0,0 +1,218 @@ +# HomeSpan Extras + +HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. + +## Pulse Width Modulation (PWM) + +The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: + +`#include "extras/PwmPin.h"` + +### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* + +Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output + * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) + * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + + The following methods are supported: + +* `void set(float level)` + + * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) + +* `int getPin()` + + * returns the pin number (or -1 if LedPin was not successfully initialized) + +LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). + +* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` + + * *h* - input Hue value, range 0-360 + * *s* - input Saturation value, range 0-1 + * *v* - input Brightness value, range 0-1 + * *r* - output Red value, range 0-1 + * *g* - output Green 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. + +### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* + +Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin + * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) + * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) + * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) + * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) + * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) + +The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: + +* `void set(double position)` + + * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. + +* `int getPin()` + + * 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). + +### PWM Resource Allocation and Limitations + +The following PWM resources are available: + +* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) +* ESP32-S2: 8 Channels / 4 Timers +* ESP32-C3: 6 Channels / 4 Timers + +HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* + +HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. + +## Remote Control Radio Frequency / Infrared Signal Generation + +The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: + +`#include "extras/RFControl.h"` + +### *RFControl(int pin, boolean refClock=true)* + +Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. + +Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: + +![Pulse Train](images/pulseTrain.png) + +Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: + + * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted + * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted + * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + +HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. + +In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. + +Details of each function are as follows: + +* `void clear()` + + * clears the pulse train memory structure of a specific instance of RFControl + +* `void phase(uint32_t numTicks, uint8_t phase)` + + * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl + + * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) + + * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase + + * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) + +* `void add(uint32_t onTime, uint32_t offTime)` + + * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above + +* `void enableCarrier(uint32_t freq, float duty=0.5)` + + * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) + + * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled + + * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified + + * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations + +* `void disableCarrier()` + + * disables the carrier wave. Equivalent to `enableCarrier(0);` + +* `void start(uint8_t _numCycles, uint8_t tickTime)` +* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` + + * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long + + * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. + + * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) + +* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: + +```C++ + +RFControl rf(11); // create an instance of RFControl + +rf.clear(); // clear the internal memory structure +rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW +rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s + +uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array +rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters +``` + +### 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). + +```C++ +/* HomeSpan Remote Control Example */ + +#include "HomeSpan.h" // include the HomeSpan library +#include "extras/RFControl.h" // include RF Control Library + +void setup() { + + Serial.begin(115200); // start the Serial interface + Serial.flush(); + delay(1000); // wait for interface to flush + + Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); + + RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 + + rf.clear(); // clear the pulse train memory buffer + + rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses + rf.add(5000,5000); + rf.add(5000,10000); // double duration of final low period + + Serial.print("Starting 4 cycles of three 500 ms on pulses..."); + + rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + delay(2000); + + rf.clear(); + + for(int i=1000;i<10000;i+=1000) + rf.add(i,10000-i); + rf.add(10000,10000); + + Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); + + rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + Serial.print("\nEnd Example"); + +} // end of setup() + +void loop(){ + +} // end of loop() +``` + +--- + +[↩️](README.md) Back to the Welcome page From 84234f3265d1e100d5130ffab86ee04da28cedcc Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Fri, 18 Feb 2022 18:21:19 -0600 Subject: [PATCH 074/106] Update Pixels.md --- docs/Pixels.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 9b0cb7d..325c29f 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -1,8 +1,6 @@ -# HomeSpan Extras +## Addressable RGB LEDs -HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. - -## Pulse Width Modulation (PWM) +HomeSpan includes two dedicated classes that provide for precise control of Addressable RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as the [NeoPixel 8-element RGB Stick](https://www.adafruit.com/product/1426) or this [NeoPixel Single RGBW LED](https://www.adafruit.com/product/2759). These LEDs typically contain embedded driver chips such as the SK6812 or WS2812. The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this [DotStar 144-element RGB Strip](https://www.adafruit.com/product/2241). These LEDs typically contain embedded driver chips such as the SK9822 or WS2801. The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: From c8a613ccac790549206316e2a7f850e8efee191e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 07:01:32 -0600 Subject: [PATCH 075/106] Update Pixels.md --- docs/Pixels.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 325c29f..c8dbaaf 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -1,6 +1,10 @@ ## Addressable RGB LEDs -HomeSpan includes two dedicated classes that provide for precise control of Addressable RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as the [NeoPixel 8-element RGB Stick](https://www.adafruit.com/product/1426) or this [NeoPixel Single RGBW LED](https://www.adafruit.com/product/2759). These LEDs typically contain embedded driver chips such as the SK6812 or WS2812. The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this [DotStar 144-element RGB Strip](https://www.adafruit.com/product/2241). These LEDs typically contain embedded driver chips such as the SK9822 or WS2801. +HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241). + +Both classes allow you to individually set each of the "pixels" in a multi-pixel LED strip to a different 24-bit RGB color (or 32-bit color, if using RGBW LEDs). Alternatively, the classes allow you to simply specify a single 24-bit (or 32-bit) color to duplicate across all pixels. + +The methods for both classes are nearly identical, which allows you to interchange code written for single-wire devices to use with two-wire devices (and vice-versa) with only minor modifications. The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: From aed0a4b1db3c250e284980f933ffd66a136d57c6 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 07:41:27 -0600 Subject: [PATCH 076/106] Update Pixels.md --- docs/Pixels.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index c8dbaaf..1c9448e 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -1,29 +1,36 @@ ## Addressable RGB LEDs -HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241). +HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241) or this 60-pixel [RGB LED Strip](https://www.sparkfun.com/products/14015). Both classes allow you to individually set each of the "pixels" in a multi-pixel LED strip to a different 24-bit RGB color (or 32-bit color, if using RGBW LEDs). Alternatively, the classes allow you to simply specify a single 24-bit (or 32-bit) color to duplicate across all pixels. -The methods for both classes are nearly identical, which allows you to interchange code written for single-wire devices to use with two-wire devices (and vice-versa) with only minor modifications. +The methods for both classes are nearly identical, which allows you to readily interchange code written for single-wire devices to use with two-wire devices (and vice-versa) with only minor modifications. -The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: +Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: -`#include "extras/PwmPin.h"` +`#include "extras/Pixel.h"` -### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* +### *Pixel(uint8_t pin, [boolean isRGBW])* -Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for a controlling single-wire, addressable RGB or RGBW LEDs. Arguments, along with their defaults if left unspecified, are as follows: - * *pin* - the pin on which the PWM control signal will be output - * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) - * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device + * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified The following methods are supported: -* `void set(float level)` +* `void set(Color color, int nPixels=1)` - * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) + * sets the color of all pixels in an *nPixel* device to *color*, which is a structure of type **Color** defined below. *nPixels* defaults to 1 if left unspecified +* `void set(Color *color, int nPixels)` + + * individually sets the color of each pixel in an *nPixel* device according to the values specified in the **Color** array *\*color*, where the first pixel is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]*, etc. The number of pixels in array, *nPixels*, must be specified + +* `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` + + * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values [0-255], and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was set to *true* in the constructor + * `int getPin()` * returns the pin number (or -1 if LedPin was not successfully initialized) From 95c767a1b071deb266b25748c3b3faebf102d636 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 10:19:04 -0600 Subject: [PATCH 077/106] Update Pixels.md --- docs/Pixels.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 1c9448e..be96063 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -15,25 +15,35 @@ Both classes are provided in a standalone header file that is accessed by placin Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for a controlling single-wire, addressable RGB or RGBW LEDs. Arguments, along with their defaults if left unspecified, are as follows: * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device - * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified + * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified. Note you must set the *isRGBW* flag to *true* if you are using an RGBW device, even if you do not intend on utilizing the white LED The following methods are supported: * `void set(Color color, int nPixels=1)` - * sets the color of all pixels in an *nPixel* device to *color*, which is a structure of type **Color** defined below. *nPixels* defaults to 1 if left unspecified + * sets the color of a pixel in a single-pixel device, or equivalently, the color of the first *nPixels* in a multi-pixel device, to *color*, where *color* is a structure of type **Color** defined below. If unspecified, *nPixels* defaults to 1 (i.e. a single pixel). It is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to *color*; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input * `void set(Color *color, int nPixels)` - * individually sets the color of each pixel in an *nPixel* device according to the values specified in the **Color** array *\*color*, where the first pixel is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]*, etc. The number of pixels in array, *nPixels*, must be specified + * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. The number of pixels in *\*color* array, *nPixels*, must be specified. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to the values in the *\*color* array; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values [0-255], and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was set to *true* in the constructor + * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + +* `static Color HSV(float h, float s, float v, double w=0)` + + * returns a **Color** object after converting the values of *h* (a Hue from 0-360), *s* (a Saturation percentage from 0-100), and *v* (a Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + + * `int getPin()` - * returns the pin number (or -1 if LedPin was not successfully initialized) + * returns the pin number (or -1 if the instantiation failed due to lack of resources - see below) + +* `void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset)` + + * the waveform that the *set()* methods generate to set the color(s) of an RGB or RGBW device are calibrated to work with most commercial devices. However, if you have a device that utilizes non-standard pulses, you may use *setTiming()* to specify a custom pulse width, where LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). From 72a042b7cc6a259ba8d37979ae2a9e4903c32563 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 10:37:11 -0600 Subject: [PATCH 078/106] Update Pixels.md --- docs/Pixels.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index be96063..0f0f737 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -21,19 +21,19 @@ Creating an instance of this **class** configures the specified *pin* to output * `void set(Color color, int nPixels=1)` - * sets the color of a pixel in a single-pixel device, or equivalently, the color of the first *nPixels* in a multi-pixel device, to *color*, where *color* is a structure of type **Color** defined below. If unspecified, *nPixels* defaults to 1 (i.e. a single pixel). It is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to *color*; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input + * sets the color of a pixel in a single-pixel device, or equivalently, the color of the first *nPixels* in a multi-pixel device, to *color*, where *color* is an object of type **Color** defined below. If unspecified, *nPixels* defaults to 1 (i.e. a single pixel). It is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to *color*; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input * `void set(Color *color, int nPixels)` - * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. The number of pixels in *\*color* array, *nPixels*, must be specified. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to the values in the *\*color* array; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input + * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to the values in the *\*color* array; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor * `static Color HSV(float h, float s, float v, double w=0)` - * returns a **Color** object after converting the values of *h* (a Hue from 0-360), *s* (a Saturation percentage from 0-100), and *v* (a Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * returns a **Color** object after converting the values of *h*, *s*, and *v* (where *h*=Hue from 0-360; *s*=Saturation percentage from 0-100; *v*=Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor From c06daedfbae6cac378c7d4b0e30d57ed11b386a2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 12:26:43 -0600 Subject: [PATCH 079/106] Update Pixels.md --- docs/Pixels.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 0f0f737..a1f82e9 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -25,15 +25,30 @@ Creating an instance of this **class** configures the specified *pin* to output * `void set(Color *color, int nPixels)` - * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to the values in the *\*color* array; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input + * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device +* `struct Color` + + * instantiates a "blank" 32-bit Color object capable of holding four 8-bit RGBW values + * examples: `Pixel::Color myColor;` or `Pixel::Color myColors[8];` + * values for a Color object are set using the following methods: + + * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` + * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor; and + * `Color HSV(float h, float s, float v, double w=0)` + + * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the Color object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the Color object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 + * both methods above return the completed Color object itself and can thus be used wherever a Color object is required + * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * returns a **Color** object, where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * a convenience, class-level function that returns a **Color** object. Equivalent to `return(Color().RGB(r,g,b,w));` * `static Color HSV(float h, float s, float v, double w=0)` - * returns a **Color** object after converting the values of *h*, *s*, and *v* (where *h*=Hue from 0-360; *s*=Saturation percentage from 0-100; *v*=Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * a convenience, class-level function that returns a **Color** object. Equivalent to `return(Color().HSV(h,s,v,w));` + +* returns a **Color** object after converting the values of *h*, *s*, and *v* (where *h*=Hue from 0-360; *s*=Saturation percentage from 0-100; *v*=Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor From 95ca6637f7d6086e51d782764cfe06355c2e9d27 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 13:12:27 -0600 Subject: [PATCH 080/106] Update Pixels.md --- docs/Pixels.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index a1f82e9..f1cd96a 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -34,11 +34,16 @@ Creating an instance of this **class** configures the specified *pin* to output * values for a Color object are set using the following methods: * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor; and + * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * example: `myColor.RGB(255,255,0)` sets myColor to bright yellow + * `Color HSV(float h, float s, float v, double w=0)` - * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the Color object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the Color object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 - * both methods above return the completed Color object itself and can thus be used wherever a Color object is required + * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the *Color* object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the **Color** object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 + * example: `myColor.HSV(120,100,50)` sets myColor to fully-saturated green with 50% brightness + + * both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required + * example: `Pixel p(5); Pixel::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pin 5 to bright gold * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` @@ -48,28 +53,24 @@ Creating an instance of this **class** configures the specified *pin* to output * a convenience, class-level function that returns a **Color** object. Equivalent to `return(Color().HSV(h,s,v,w));` -* returns a **Color** object after converting the values of *h*, *s*, and *v* (where *h*=Hue from 0-360; *s*=Saturation percentage from 0-100; *v*=Brightness percentage from 0-100) to equivalent 8-bit RGB values, each from 0-255. The *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is subsequently converted into an 8-bit value from 0-255. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor - - - * `int getPin()` * returns the pin number (or -1 if the instantiation failed due to lack of resources - see below) * `void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset)` - * the waveform that the *set()* methods generate to set the color(s) of an RGB or RGBW device are calibrated to work with most commercial devices. However, if you have a device that utilizes non-standard pulses, you may use *setTiming()* to specify a custom pulse width, where - -LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). + * the default timing parameters used by the **Pixel** class to generate the "data" signal needed to set the colors of an RGB LED device should work with most commercial products based on SK6812 or WS2812 driver chips. Use this method if you need to override the class defaults and replace them with your own timing parameters, where + * *high0* and *low0* specify the duration (in microseconds) of the high phase and low phase for a pulse encoding a zero-bit; + * *high1* and *low1* specify the duration (in microseconds) of the high phase and low phase for a pulse encoding a one-bit; and + * *lowReset* specifies the delay (in microseconds) representing the end of a pulse stream + * for reference, the **Pixel** class uses the following defaults: *high0=0.32𝛍s, low0=0.88𝛍s, high1=0.64𝛍s, low1=0.56𝛍s, lowReset=80.0𝛍s* + +Resources usage. The **Pixel** class relies on the ESP32's RMT peripheral to create the precise pulse trains required to control single-wire addressable RGB LEDs. Since each instantiation of **Pixel** consumes an RMT channel, the number of **Pixel** objects you can instantiate (each controlling a separate multi-pixel RGB LED device attached to a specific pin) is limited to the number of RMT available as follows: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. + +Conflict Alert: The **Pixel** class is optimized to handle aribtrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's RFControl library. Unfortunately this means you cannot use both the *Pixel* class library and *RFControl* class library in the same HomeSpan sketch. + -* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` - * *h* - input Hue value, range 0-360 - * *s* - input Saturation value, range 0-1 - * *v* - input Brightness value, range 0-1 - * *r* - output Red value, range 0-1 - * *g* - output Green 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. From 993d8393769ddf846b68b1ecd92ed3d58528ed70 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 14:02:27 -0600 Subject: [PATCH 081/106] Update Pixels.md --- docs/Pixels.md | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index f1cd96a..d2c49f4 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -16,8 +16,8 @@ Creating an instance of this **class** configures the specified *pin* to output * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified. Note you must set the *isRGBW* flag to *true* if you are using an RGBW device, even if you do not intend on utilizing the white LED - - The following methods are supported: + +The two main methods to set pixel colors are: * `void set(Color color, int nPixels=1)` @@ -27,31 +27,31 @@ Creating an instance of this **class** configures the specified *pin* to output * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device -* `struct Color` - - * instantiates a "blank" 32-bit Color object capable of holding four 8-bit RGBW values - * examples: `Pixel::Color myColor;` or `Pixel::Color myColors[8];` - * values for a Color object are set using the following methods: +In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold four 8-bit RGBW value. **Color** objects can be instantiated as single variables (e.g. `Pixel::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Pixel** class is scoped to the **Pixel** class itself, so you need to use the fully-scoped class name "Pixel::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: - * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor - * example: `myColor.RGB(255,255,0)` sets myColor to bright yellow + * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` + + * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * example: `myColor.RGB(255,255,0)` sets myColor to bright yellow - * `Color HSV(float h, float s, float v, double w=0)` + * `Color HSV(float h, float s, float v, double w=0)` - * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the *Color* object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the **Color** object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 - * example: `myColor.HSV(120,100,50)` sets myColor to fully-saturated green with 50% brightness + * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the *Color* object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the **Color** object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 + * example: `myColor.HSV(120,100,50)` sets myColor to fully-saturated green with 50% brightness - * both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required - * example: `Pixel p(5); Pixel::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pin 5 to bright gold +Note both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required: For example: `Pixel p(5); Pixel::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pin 5 to bright gold. + +The **Pixel** class also supports the following class-level methods as a convenient alterntive to creating colors: * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - - * a convenience, class-level function that returns a **Color** object. Equivalent to `return(Color().RGB(r,g,b,w));` + * equivalent to `return(Color().RGB(r,g,b,w));` * `static Color HSV(float h, float s, float v, double w=0)` + * equivalent to `return(Color().HSV(h,s,v,w));` - * a convenience, class-level function that returns a **Color** object. Equivalent to `return(Color().HSV(h,s,v,w));` +For example: `` + +Finally, the **Pixel** class supports these two lesser-use methods: * `int getPin()` @@ -65,9 +65,11 @@ Creating an instance of this **class** configures the specified *pin* to output * *lowReset* specifies the delay (in microseconds) representing the end of a pulse stream * for reference, the **Pixel** class uses the following defaults: *high0=0.32𝛍s, low0=0.88𝛍s, high1=0.64𝛍s, low1=0.56𝛍s, lowReset=80.0𝛍s* -Resources usage. The **Pixel** class relies on the ESP32's RMT peripheral to create the precise pulse trains required to control single-wire addressable RGB LEDs. Since each instantiation of **Pixel** consumes an RMT channel, the number of **Pixel** objects you can instantiate (each controlling a separate multi-pixel RGB LED device attached to a specific pin) is limited to the number of RMT available as follows: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. +Notes on Resource Usage and Resource Conflicts -Conflict Alert: The **Pixel** class is optimized to handle aribtrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's RFControl library. Unfortunately this means you cannot use both the *Pixel* class library and *RFControl* class library in the same HomeSpan sketch. + * the **Pixel** class relies on the ESP32's RMT peripheral to create the precise pulse trains required to control single-wire addressable RGB LEDs. Since each instantiation of **Pixel** consumes an RMT channel, the number of **Pixel** objects you can instantiate (each controlling a separate multi-pixel RGB LED device attached to a specific pin) is limited to the number of RMT available as follows: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. + + * the **Pixel** class is optimized to handle aribtrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's RFControl library. Unfortunately this means you cannot use both the *Pixel* class library and *RFControl* class library in the same HomeSpan sketch. From 4ca2b212607f817276768c2e298ac9dab55a843b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 16:48:15 -0600 Subject: [PATCH 082/106] Update Pixels.md --- docs/Pixels.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index d2c49f4..4410f94 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -1,4 +1,4 @@ -## Addressable RGB LEDs +# Addressable RGB LEDs HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241) or this 60-pixel [RGB LED Strip](https://www.sparkfun.com/products/14015). @@ -10,7 +10,7 @@ Both classes are provided in a standalone header file that is accessed by placin `#include "extras/Pixel.h"` -### *Pixel(uint8_t pin, [boolean isRGBW])* +## *Pixel(uint8_t pin, [boolean isRGBW])* Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for a controlling single-wire, addressable RGB or RGBW LEDs. Arguments, along with their defaults if left unspecified, are as follows: @@ -31,8 +31,8 @@ In both of the methods above, colors are stored in a 32-bit **Color** object con * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` - * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor - * example: `myColor.RGB(255,255,0)` sets myColor to bright yellow + * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *w* represents an 8-bit value [0-255] for the white LED. The white value may be left unspecified, in which case it defaults to 0. Also, the white value will be ignored by *set()* unless the *isRGBW* flag was specified as *true* in the constructor + * example: `myColor.RGB(255,255,0)` sets myColor to bright yellow * `Color HSV(float h, float s, float v, double w=0)` @@ -41,17 +41,17 @@ In both of the methods above, colors are stored in a 32-bit **Color** object con Note both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required: For example: `Pixel p(5); Pixel::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pin 5 to bright gold. -The **Pixel** class also supports the following class-level methods as a convenient alterntive to creating colors: +The **Pixel** class also supports the following class-level methods as a convenient alternative to creating colors: * `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` * equivalent to `return(Color().RGB(r,g,b,w));` + * example: `Pixel p(8); p.set(Pixel::RGB(0,0,255),8);` sets the color of each pixel in an 8-pixel device to blue * `static Color HSV(float h, float s, float v, double w=0)` * equivalent to `return(Color().HSV(h,s,v,w));` + * example: `Pixel::Color c[]={Pixel::HSV(120,100,100),Pixel::HSV(60,100,100),Pixel::HSV(0,100,100)};` to create a red-yellow-green traffic light pattern -For example: `` - -Finally, the **Pixel** class supports these two lesser-use methods: +Finally, the **Pixel** class supports these two lesser-used methods: * `int getPin()` @@ -65,11 +65,11 @@ Finally, the **Pixel** class supports these two lesser-use methods: * *lowReset* specifies the delay (in microseconds) representing the end of a pulse stream * for reference, the **Pixel** class uses the following defaults: *high0=0.32𝛍s, low0=0.88𝛍s, high1=0.64𝛍s, low1=0.56𝛍s, lowReset=80.0𝛍s* -Notes on Resource Usage and Resource Conflicts +### Resource Usage and Resource Conflicts - * the **Pixel** class relies on the ESP32's RMT peripheral to create the precise pulse trains required to control single-wire addressable RGB LEDs. Since each instantiation of **Pixel** consumes an RMT channel, the number of **Pixel** objects you can instantiate (each controlling a separate multi-pixel RGB LED device attached to a specific pin) is limited to the number of RMT available as follows: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. +The **Pixel** class relies on the ESP32's RMT peripheral to create the precise pulse trains required to control single-wire addressable RGB LEDs. Since each instantiation of **Pixel** consumes an RMT channel, the number of **Pixel** objects you can instantiate (each controlling a separate multi-pixel RGB LED device attached to a specific pin) is limited to the number of RMT available as follows: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. - * the **Pixel** class is optimized to handle aribtrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's RFControl library. Unfortunately this means you cannot use both the *Pixel* class library and *RFControl* class library in the same HomeSpan sketch. +Also, the **Pixel** class is optimized to handle arbitrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's **RFControl** library. Unfortunately this means you cannot use both the **Pixel** class library and **RFControl** class library in the same HomeSpan sketch. From dc0969a10b52c1fc82417578d2621acb2d3fda56 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 17:35:13 -0600 Subject: [PATCH 083/106] Update Pixels.md --- docs/Pixels.md | 200 +++++++++---------------------------------------- 1 file changed, 35 insertions(+), 165 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 4410f94..6d35d24 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -12,7 +12,7 @@ Both classes are provided in a standalone header file that is accessed by placin ## *Pixel(uint8_t pin, [boolean isRGBW])* -Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for a controlling single-wire, addressable RGB or RGBW LEDs. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Arguments, along with their defaults if left unspecified, are as follows: * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified. Note you must set the *isRGBW* flag to *true* if you are using an RGBW device, even if you do not intend on utilizing the white LED @@ -27,7 +27,7 @@ The two main methods to set pixel colors are: * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device -In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold four 8-bit RGBW value. **Color** objects can be instantiated as single variables (e.g. `Pixel::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Pixel** class is scoped to the **Pixel** class itself, so you need to use the fully-scoped class name "Pixel::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: +In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold four 8-bit RGBW values. **Color** objects can be instantiated as single variables (e.g. `Pixel::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Pixel** class is scoped to the **Pixel** class itself, so you need to use the fully-scoped class name "Pixel::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` @@ -36,7 +36,7 @@ In both of the methods above, colors are stored in a 32-bit **Color** object con * `Color HSV(float h, float s, float v, double w=0)` - * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values, each from 0-255 for storage in the *Color* object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the **Color** object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 + * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values (0-255) for storage in the *Color* object. Note the *w* value is treated separately and represents a percentage of brightness for the white LED (from 0-100) that is also converted into an 8-bit value from 0-255 for storage in the **Color** object. Similar to above, the white value may be left unspecified, in which case it defaults to 0 * example: `myColor.HSV(120,100,50)` sets myColor to fully-saturated green with 50% brightness Note both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required: For example: `Pixel p(5); Pixel::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pin 5 to bright gold. @@ -71,184 +71,54 @@ The **Pixel** class relies on the ESP32's RMT peripheral to create the precise p Also, the **Pixel** class is optimized to handle arbitrarily-long LED strips containing hundreds of RGB or RGBW pixels. To accomplish this efficiently, the **Pixel** class implements its own RMT driver, which conflicts with the default RMT driver used by HomeSpan's **RFControl** library. Unfortunately this means you cannot use both the **Pixel** class library and **RFControl** class library in the same HomeSpan sketch. +## *Dot(uint8_t dataPin, uint8_t clockPin)* +Creating an instance of this **class** configures the specified pins to output waveform signals suitable for controlling a two-wire, addressable RGB LED device with an arbitrary number of pixels. Arguments, along with their defaults if left unspecified, are as follows: + * *dataPin* - the pin on which the RGB data signal will be output; normally connected to the "data" input of the addressable LED device + * *clockPin* - the pin on which the RGB clock signal will be output; normally connected to the "clock" input of the addressable LED device -See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. +The two main methods to set pixel colors are: -### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* +* `void set(Color color, int nPixels=1)` -Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin - * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) - * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) - * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) - * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) - * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) - -The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: - -* `void set(double position)` - - * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. + * sets the color of a pixel in a single-pixel device, or equivalently, the color of the first *nPixels* in a multi-pixel device, to *color*, where *color* is an object of type **Color** defined below. If unspecified, *nPixels* defaults to 1 (i.e. a single pixel). It is not a problem if the value specified for *nPixels* does not match the total number of actual RGB pixels in your device; if *nPixels* is less than the total number of device pixels, only the first *nPixels* will be set to *color*; if *nPixels* is greater than the total number of device pixels, the device will simply ignore the additional input -* `int getPin()` +* `void set(Color *color, int nPixels)` - * returns the pin number (or -1 if ServoPin was not successfully initialized) + * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB pixels in your device -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). +In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold three 8-bit RGB values plus a 5-bit value that can be used to limit the LED curent. **Color** objects can be instantiated as single variables (e.g. `Dot::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Dot** class is scoped to the **Dot** class itself, so you need to use the fully-scoped class name "Dot::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: + + * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31)` -### PWM Resource Allocation and Limitations - -The following PWM resources are available: - -* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) -* ESP32-S2: 8 Channels / 4 Timers -* ESP32-C3: 6 Channels / 4 Timers - -HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* - -HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. - -## Remote Control Radio Frequency / Infrared Signal Generation - -The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: - -`#include "extras/RFControl.h"` - -### *RFControl(int pin, boolean refClock=true)* - -Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. - -Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: - -![Pulse Train](images/pulseTrain.png) - -Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: - - * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted - * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted - * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - -HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. - -In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. - -Details of each function are as follows: - -* `void clear()` - - * clears the pulse train memory structure of a specific instance of RFControl - -* `void phase(uint32_t numTicks, uint8_t phase)` - - * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl - - * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) + * where *r*, *g*, and *b*, represent 8-bit red, green, and blue values over the range 0-255, and *driveLevel* represents an 5-bit value [0-31] used to limit the LED current from 0 (no current) to 31 (max current, which is the default). Limiting the LED current by setting the *driveLevel* to a value of less than 31 provides a flicker-free way of controlling the brightness of the RGB LEDs for each pixel. + * example: `myColor.RGB(128,128,0)` sets myColor to yellow at half-brightness using a 50% duty cycle for the red and green LEDs (i.e. 128/256) + * example: `myColor.RGB(255,255,0,16)` sets myColor to yellow at half-brightness by limiting the LED current for the pixel to 50% of its max value (i.e. 16/32) + + * `Color HSV(float h, float s, float v, double drivePercent=100)` - * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase - - * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) + * where *h*=Hue, over the range 0-360; *s*=Saturation percentage from 0-100; and *v*=Brightness percentage from 0-100. These values are converted to equivalent 8-bit RGB values (0-255) for storage in the *Color* object. The *drivePercent* parameter controls the current in the same fashion as *driveLevel* above, except that instead of being specified as an absolute value from 0-31, it is specified as a percentage from 0 to 100 (the default) + * example: `myColor.HSV(120,100,50)` sets myColor to fully-saturated green at half-brightness using a 50% duty cycle + * example: `myColor.HSV(120,100,100,50)` sets myColor to fully-saturated green at half-brightness by limiting the LED current for the pixel to 50% of its max value + +Note both methods above return the completed **Color** object itself and can thus be used wherever a **Color** object is required: For example: `Dot p(5,6); Dot::Color myColor; p.set(myColor.RGB(255,215,0))` sets the color of a single pixel device attached to pins 5 and 6 to bright gold. -* `void add(uint32_t onTime, uint32_t offTime)` - - * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above - -* `void enableCarrier(uint32_t freq, float duty=0.5)` - - * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) +The **Pixel** class also supports the following class-level methods as a convenient alternative to creating colors: - * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled - - * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified +* `static Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31)` + * equivalent to `return(Color().RGB(r,g,b,driveLevel));` + * example: `Dot p(8,11); p.set(Dot::RGB(0,0,255),8);` sets the color of each pixel in an 8-pixel device to blue - * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations +* `static Color HSV(float h, float s, float v, double drivePercent=100)` + * equivalent to `return(Color().HSV(h,s,v,drivePercent));` + * example: `Dot::Color c[]={Dot::HSV(120,100,100),Dot::HSV(60,100,100),Dot::HSV(0,100,100)};` to create a red-yellow-green traffic light pattern -* `void disableCarrier()` +Unlike the **Pixel** class, the **Dot** class does not utilize the ESP32'2 RMT peripheral and thus there are no limits to the number of **Dot** objects you can instantiate, not any conflicts with using the **RFControl** library at the same time in the same sketch. There are also no timing parameters to set since the clock signal is generated by the **Dot** class itself. - * disables the carrier wave. Equivalent to `enableCarrier(0);` +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). -* `void start(uint8_t _numCycles, uint8_t tickTime)` -* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` - - * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long - - * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. - - * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) - -* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: - -```C++ - -RFControl rf(11); // create an instance of RFControl - -rf.clear(); // clear the internal memory structure -rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW -rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s - -uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array -rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters -``` - -### 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). - -```C++ -/* HomeSpan Remote Control Example */ - -#include "HomeSpan.h" // include the HomeSpan library -#include "extras/RFControl.h" // include RF Control Library - -void setup() { - - Serial.begin(115200); // start the Serial interface - Serial.flush(); - delay(1000); // wait for interface to flush - - Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); - - RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 - - rf.clear(); // clear the pulse train memory buffer - - rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses - rf.add(5000,5000); - rf.add(5000,10000); // double duration of final low period - - Serial.print("Starting 4 cycles of three 500 ms on pulses..."); - - rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - delay(2000); - - rf.clear(); - - for(int i=1000;i<10000;i+=1000) - rf.add(i,10000-i); - rf.add(10000,10000); - - Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); - - rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - Serial.print("\nEnd Example"); - -} // end of setup() - -void loop(){ - -} // end of loop() -``` +For a more complete showcase of the Pixel library , check out [Holiday Lights]() on the HomeSpan projects page. 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 to implement a special-effects selector button for use in the Eve App for HomeKit. --- From c1ea67fe7b9cf43833e743423a6bbfdbb8bd7a5e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 17:44:16 -0600 Subject: [PATCH 084/106] Update Pixels.md --- docs/Pixels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 6d35d24..8470f01 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -118,7 +118,7 @@ Unlike the **Pixel** class, the **Dot** class does not utilize the ESP32'2 RMT p 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). -For a more complete showcase of the Pixel library , check out [Holiday Lights]() on the HomeSpan projects page. 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 to implement a special-effects selector button for use in the Eve App for HomeKit. +For a more complete showcase of the Pixel library , check out [Holiday Lights]() 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. --- From 7bae81270a842dfa3f8976d2d0d3a4ef259de68e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 17:45:09 -0600 Subject: [PATCH 085/106] Update Pixels.md --- docs/Pixels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 8470f01..6915cac 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -1,6 +1,6 @@ # Addressable RGB LEDs -HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The *Pixel()* class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The *Dot()* class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241) or this 60-pixel [RGB LED Strip](https://www.sparkfun.com/products/14015). +HomeSpan includes two dedicated classes that provide for easy control of "addressable" RGB LEDs. The **Pixel()** class is used for RGB and RGBW LEDs that require only a single "data" control wire, such as this 8-pixel [NeoPixel RGB Stick](https://www.adafruit.com/product/1426) or this single-pixel [NeoPixel RGBW LED](https://www.adafruit.com/product/2759). The **Dot()** class is used for RGB LEDs that require two control wires ("data" and "clock"), such as this 144-pixel [DotStar RGB Strip](https://www.adafruit.com/product/2241) or this 60-pixel [RGB LED Strip](https://www.sparkfun.com/products/14015). Both classes allow you to individually set each of the "pixels" in a multi-pixel LED strip to a different 24-bit RGB color (or 32-bit color, if using RGBW LEDs). Alternatively, the classes allow you to simply specify a single 24-bit (or 32-bit) color to duplicate across all pixels. From 846dc5edd49e49943940555d25b521f6a4355934 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Feb 2022 17:55:54 -0600 Subject: [PATCH 086/106] Update Pixels.md --- docs/Pixels.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 6915cac..ced5c0f 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -118,7 +118,7 @@ Unlike the **Pixel** class, the **Dot** class does not utilize the ESP32'2 RMT p 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). -For a more complete showcase of the Pixel library , check out [Holiday Lights]() 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](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. --- From b141a8363043d78f35fa99faaba387bc2984cb13 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 19 Feb 2022 18:29:34 -0600 Subject: [PATCH 087/106] Update extras.ino --- src/extras/extras.ino | 153 +++++++----------------------------------- 1 file changed, 23 insertions(+), 130 deletions(-) diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 748deba..703acf9 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,124 +2,11 @@ #include "Pixel.h" -struct Effect1 { +//#define PixelType Pixel +#define PixelType Dot - Pixel *px; - int H=0; - uint32_t alarmTime=0; - uint32_t speed; - uint8_t nPixels; - Pixel::Color c; - - Effect1(Pixel *px, uint32_t speed, uint8_t nPixels){ - this->px=px; - this->nPixels=nPixels; - this->speed=speed; - } - - void update(){ - if(millis()set(c.HSV(H,100,10),nPixels); - H=(H+1)%360; - - alarmTime=millis()+speed; - } -}; - -struct Effect2 { - - Pixel *px; - int phase=0; - int dir=1; - int H=0; - Pixel::Color x[60]; - uint32_t alarmTime=0; - uint32_t speed; - uint8_t nPixels; - - Effect2(Pixel *px, uint32_t speed, uint8_t nPixels){ - this->px=px; - this->nPixels=nPixels; - this->speed=speed; - } - - void update(){ - if(millis()set(x,nPixels); - phase=(phase+dir)%nPixels; - - if(phase==0){ - dir=1; - H=(H+10)%360; - } - else if(phase==nPixels-1){ - dir=-1; - H=(H+10)%360; - } - - alarmTime=millis()+speed; - } -}; - -struct Effect3 { - - Dot *dot; - int H=0; - uint32_t alarmTime=0; - uint32_t speed; - uint8_t nPixels; - Dot::Color c; - - Effect3(Dot *dot, uint32_t speed, uint8_t nPixels){ - this->dot=dot; - this->nPixels=nPixels; - this->speed=speed; - } - - void update(){ - if(millis()set(c.HSV(H,100,100),nPixels); - H=(H+1)%360; - - alarmTime=millis()+speed; - } -}; - -#if defined(CONFIG_IDF_TARGET_ESP32C3) - - Pixel px1(8); // NeoPixel RGB - Pixel px2(9,true); // NeoPixel RGBW - Dot dot(2,3); // DotStar - Pixel neo(0); - -#elif defined(CONFIG_IDF_TARGET_ESP32) - -// Pixel px1(23,true); // NeoPixel RGB -// Dot dot(32,5); // DotStar - Pixel px2(21); // NeoPixel RGBW - Pixel neo(26); - -#endif - -//Effect1 effect1(&px1,20,60); -//Effect2 effect2(&px2,20,60); -//Effect2 effect2(&px1,20,60); -//Effect3 effect3(&dot,20,30); +//Pixel p(8); +Dot p(0,1); void setup() { @@ -129,21 +16,27 @@ void setup() { Serial.println("\n\nHomeSpan Pixel Example\n"); - Pixel::Color c; - int hue=0; + PixelType::Color off=PixelType::RGB(0,0,0); -// Pixel px2(21); // NeoPixel RGBW + p.set(PixelType::RGB(0,0,255),3); + delay(1000); - while(1){ - neo.set(c.HSV(hue,100,10),8); - hue=(hue+10)%360; - delay(100); - } - -} // end of setup() + p.set(off,3); + delay(1000); + + PixelType::Color c[]={p.HSV(120,100,30),p.HSV(0,0,0),p.HSV(0,0,0)}; + p.set(c,3); + delay(1000); + + c[0].HSV(0,0,0); + c[1].HSV(60,100,30); + p.set(c,3); + delay(1000); + + c[1].HSV(0,0,0); + c[2].HSV(0,100,30); + p.set(c,3); +} void loop(){ -// effect1.update(); -// effect2.update(); -// effect3.update(); } From 3382dcda0fd3ac3f6196e18d87c578bc57069129 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 06:51:15 -0600 Subject: [PATCH 088/106] Update Pixels.md --- docs/Pixels.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index ced5c0f..1302867 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -12,7 +12,7 @@ Both classes are provided in a standalone header file that is accessed by placin ## *Pixel(uint8_t pin, [boolean isRGBW])* -Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Such devices typically contain embedded SK6812 or WS2812 driver chips. Arguments, along with their defaults if left unspecified, are as follows: * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified. Note you must set the *isRGBW* flag to *true* if you are using an RGBW device, even if you do not intend on utilizing the white LED @@ -27,7 +27,7 @@ The two main methods to set pixel colors are: * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB (or RGBW) pixels in your device -In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold four 8-bit RGBW values. **Color** objects can be instantiated as single variables (e.g. `Pixel::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Pixel** class is scoped to the **Pixel** class itself, so you need to use the fully-scoped class name "Pixel::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: +In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold four 8-bit RGBW values. **Color** objects can be instantiated as single variables (e.g. `Pixel::Color myColor;`) or as arrays (e.g. `Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Pixel** class is scoped to the **Pixel** class itself, so you need to use the fully-qualified class name "Pixel::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t w=0)` @@ -51,15 +51,15 @@ The **Pixel** class also supports the following class-level methods as a conveni * equivalent to `return(Color().HSV(h,s,v,w));` * example: `Pixel::Color c[]={Pixel::HSV(120,100,100),Pixel::HSV(60,100,100),Pixel::HSV(0,100,100)};` to create a red-yellow-green traffic light pattern -Finally, the **Pixel** class supports these two lesser-used methods: +Finally, the **Pixel** class supports these two additional, but rarely-needed, methods: * `int getPin()` - * returns the pin number (or -1 if the instantiation failed due to lack of resources - see below) + * returns the pin number, or -1 if the instantiation failed due to lack of resources * `void setTiming(float high0, float low0, float high1, float low1, uint32_t lowReset)` - * the default timing parameters used by the **Pixel** class to generate the "data" signal needed to set the colors of an RGB LED device should work with most commercial products based on SK6812 or WS2812 driver chips. Use this method if you need to override the class defaults and replace them with your own timing parameters, where + * the default timing parameters used by the **Pixel** class to generate the "data" signal needed to set the colors of an RGB LED device should work with most commercial products based on SK6812 or WS2812 driver chips. Use this method **ONLY** if you need to override the class defaults and replace them with your own timing parameters, where * *high0* and *low0* specify the duration (in microseconds) of the high phase and low phase for a pulse encoding a zero-bit; * *high1* and *low1* specify the duration (in microseconds) of the high phase and low phase for a pulse encoding a one-bit; and * *lowReset* specifies the delay (in microseconds) representing the end of a pulse stream @@ -88,7 +88,7 @@ The two main methods to set pixel colors are: * individually sets the color of each pixel in a multi-pixel device to the color values specified in the **Color** array *\*color*, of *nPixels* size, where the first pixel of the device is set to the value in *color\[0\]*, the second pixel is set to the value in *color\[1\]* ... and the last pixel is set to the value in *color\[nPixels-1\]*. Similar to above, it is not a problem if the value specified for *nPixels* does not match the total number of actual RGB pixels in your device -In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold three 8-bit RGB values plus a 5-bit value that can be used to limit the LED curent. **Color** objects can be instantiated as single variables (e.g. `Dot::Color myColor;`) or as arrays (e.g. Pixel::Color myColors\[8\];`). Note that the **Color** object used by the **Dot** class is scoped to the **Dot** class itself, so you need to use the fully-scoped class name "Dot::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: +In both of the methods above, colors are stored in a 32-bit **Color** object configured to hold three 8-bit RGB values plus a 5-bit value that can be used to limit the LED current. **Color** objects can be instantiated as single variables (e.g. `Dot::Color myColor;`) or as arrays (e.g. `Dot::Color myColors\[8\];`). Note that the **Color** object used by the **Dot** class is scoped to the **Dot** class itself, so you need to use the fully-qualified class name "Dot::Color". Once a **Color** object is created, the color it stores can be set using one of the two following methods: * `Color RGB(uint8_t r, uint8_t g, uint8_t b, uint8_t driveLevel=31)` @@ -114,7 +114,9 @@ The **Pixel** class also supports the following class-level methods as a conveni * equivalent to `return(Color().HSV(h,s,v,drivePercent));` * example: `Dot::Color c[]={Dot::HSV(120,100,100),Dot::HSV(60,100,100),Dot::HSV(0,100,100)};` to create a red-yellow-green traffic light pattern -Unlike the **Pixel** class, the **Dot** class does not utilize the ESP32'2 RMT peripheral and thus there are no limits to the number of **Dot** objects you can instantiate, not any conflicts with using the **RFControl** library at the same time in the same sketch. There are also no timing parameters to set since the clock signal is generated by the **Dot** class itself. +Unlike the **Pixel** class, the **Dot** class does *not* utilize the ESP32's RMT peripheral and thus there are no limits to the number of **Dot** objects you can instantiate, nor are there any conflicts with using the **Dot** class and the **RFControl** library at the same time in the same sketch. Also, since the clock signal is generated by the **Dot** class itself, there are no timing parameters to set and no need for a *setTiming()* method. + +### 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). From e16a05074169bdbca884873b7798b4ca804dfc61 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:14:01 -0600 Subject: [PATCH 089/106] Update Pixels.md --- docs/Pixels.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Pixels.md b/docs/Pixels.md index 1302867..77367ae 100644 --- a/docs/Pixels.md +++ b/docs/Pixels.md @@ -12,7 +12,7 @@ Both classes are provided in a standalone header file that is accessed by placin ## *Pixel(uint8_t pin, [boolean isRGBW])* -Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Such devices typically contain embedded SK6812 or WS2812 driver chips. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** configures the specified *pin* to output a waveform signal suitable for controlling a single-wire, addressable RGB or RGBW LED device with an arbitrary number of pixels. Such devices typically contain SK6812 or WS2812 LEDs. Arguments, along with their defaults if left unspecified, are as follows: * *pin* - the pin on which the RGB control signal will be output; normally connected to the "data" input of the addressable LED device * *isRGBW* - set to *true* for RGBW devices that contain 4-color (red/green/blue/white) LEDs; set to *false* for the more typical 3-color RGB devices. Defaults to *false* if unspecified. Note you must set the *isRGBW* flag to *true* if you are using an RGBW device, even if you do not intend on utilizing the white LED @@ -73,7 +73,7 @@ Also, the **Pixel** class is optimized to handle arbitrarily-long LED strips con ## *Dot(uint8_t dataPin, uint8_t clockPin)* -Creating an instance of this **class** configures the specified pins to output waveform signals suitable for controlling a two-wire, addressable RGB LED device with an arbitrary number of pixels. Arguments, along with their defaults if left unspecified, are as follows: +Creating an instance of this **class** configures the specified pins to output waveform signals suitable for controlling a two-wire, addressable RGB LED device with an arbitrary number of pixels. Such devices typically contain SK9822 or APA102 LEDs, or an embedded WS2801 driver chip. Arguments, along with their defaults if left unspecified, are as follows: * *dataPin* - the pin on which the RGB data signal will be output; normally connected to the "data" input of the addressable LED device * *clockPin* - the pin on which the RGB clock signal will be output; normally connected to the "clock" input of the addressable LED device From cc50e72b0af7244d00e2d91e3c19d624c76e91c4 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 20 Feb 2022 07:15:47 -0600 Subject: [PATCH 090/106] Splitting out HomeSpan Extras documentation into separate pages for RFControl and PWM --- docs/PWM.md | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ docs/RMT.md | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 436 insertions(+) create mode 100644 docs/PWM.md create mode 100644 docs/RMT.md diff --git a/docs/PWM.md b/docs/PWM.md new file mode 100644 index 0000000..9b0cb7d --- /dev/null +++ b/docs/PWM.md @@ -0,0 +1,218 @@ +# HomeSpan Extras + +HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. + +## Pulse Width Modulation (PWM) + +The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: + +`#include "extras/PwmPin.h"` + +### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* + +Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output + * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) + * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + + The following methods are supported: + +* `void set(float level)` + + * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) + +* `int getPin()` + + * returns the pin number (or -1 if LedPin was not successfully initialized) + +LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). + +* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` + + * *h* - input Hue value, range 0-360 + * *s* - input Saturation value, range 0-1 + * *v* - input Brightness value, range 0-1 + * *r* - output Red value, range 0-1 + * *g* - output Green 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. + +### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* + +Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin + * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) + * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) + * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) + * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) + * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) + +The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: + +* `void set(double position)` + + * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. + +* `int getPin()` + + * 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). + +### PWM Resource Allocation and Limitations + +The following PWM resources are available: + +* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) +* ESP32-S2: 8 Channels / 4 Timers +* ESP32-C3: 6 Channels / 4 Timers + +HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* + +HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. + +## Remote Control Radio Frequency / Infrared Signal Generation + +The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: + +`#include "extras/RFControl.h"` + +### *RFControl(int pin, boolean refClock=true)* + +Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. + +Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: + +![Pulse Train](images/pulseTrain.png) + +Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: + + * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted + * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted + * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + +HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. + +In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. + +Details of each function are as follows: + +* `void clear()` + + * clears the pulse train memory structure of a specific instance of RFControl + +* `void phase(uint32_t numTicks, uint8_t phase)` + + * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl + + * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) + + * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase + + * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) + +* `void add(uint32_t onTime, uint32_t offTime)` + + * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above + +* `void enableCarrier(uint32_t freq, float duty=0.5)` + + * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) + + * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled + + * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified + + * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations + +* `void disableCarrier()` + + * disables the carrier wave. Equivalent to `enableCarrier(0);` + +* `void start(uint8_t _numCycles, uint8_t tickTime)` +* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` + + * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long + + * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. + + * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) + +* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: + +```C++ + +RFControl rf(11); // create an instance of RFControl + +rf.clear(); // clear the internal memory structure +rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW +rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s + +uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array +rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters +``` + +### 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). + +```C++ +/* HomeSpan Remote Control Example */ + +#include "HomeSpan.h" // include the HomeSpan library +#include "extras/RFControl.h" // include RF Control Library + +void setup() { + + Serial.begin(115200); // start the Serial interface + Serial.flush(); + delay(1000); // wait for interface to flush + + Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); + + RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 + + rf.clear(); // clear the pulse train memory buffer + + rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses + rf.add(5000,5000); + rf.add(5000,10000); // double duration of final low period + + Serial.print("Starting 4 cycles of three 500 ms on pulses..."); + + rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + delay(2000); + + rf.clear(); + + for(int i=1000;i<10000;i+=1000) + rf.add(i,10000-i); + rf.add(10000,10000); + + Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); + + rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + Serial.print("\nEnd Example"); + +} // end of setup() + +void loop(){ + +} // end of loop() +``` + +--- + +[↩️](README.md) Back to the Welcome page diff --git a/docs/RMT.md b/docs/RMT.md new file mode 100644 index 0000000..9b0cb7d --- /dev/null +++ b/docs/RMT.md @@ -0,0 +1,218 @@ +# HomeSpan Extras + +HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. + +## Pulse Width Modulation (PWM) + +The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: + +`#include "extras/PwmPin.h"` + +### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* + +Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output + * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) + * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 + + The following methods are supported: + +* `void set(float level)` + + * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) + +* `int getPin()` + + * returns the pin number (or -1 if LedPin was not successfully initialized) + +LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). + +* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` + + * *h* - input Hue value, range 0-360 + * *s* - input Saturation value, range 0-1 + * *v* - input Brightness value, range 0-1 + * *r* - output Red value, range 0-1 + * *g* - output Green 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. + +### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* + +Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: + + * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin + * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) + * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) + * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) + * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) + * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) + +The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: + +* `void set(double position)` + + * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. + +* `int getPin()` + + * 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). + +### PWM Resource Allocation and Limitations + +The following PWM resources are available: + +* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) +* ESP32-S2: 8 Channels / 4 Timers +* ESP32-C3: 6 Channels / 4 Timers + +HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* + +HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. + +## Remote Control Radio Frequency / Infrared Signal Generation + +The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: + +`#include "extras/RFControl.h"` + +### *RFControl(int pin, boolean refClock=true)* + +Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. + +Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: + +![Pulse Train](images/pulseTrain.png) + +Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: + + * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted + * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted + * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) + +HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. + +In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. + +Details of each function are as follows: + +* `void clear()` + + * clears the pulse train memory structure of a specific instance of RFControl + +* `void phase(uint32_t numTicks, uint8_t phase)` + + * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl + + * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) + + * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase + + * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) + +* `void add(uint32_t onTime, uint32_t offTime)` + + * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above + +* `void enableCarrier(uint32_t freq, float duty=0.5)` + + * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) + + * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled + + * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified + + * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations + +* `void disableCarrier()` + + * disables the carrier wave. Equivalent to `enableCarrier(0);` + +* `void start(uint8_t _numCycles, uint8_t tickTime)` +* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` + + * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long + + * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. + + * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) + +* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: + +```C++ + +RFControl rf(11); // create an instance of RFControl + +rf.clear(); // clear the internal memory structure +rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW +rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW +rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s + +uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array +rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters +``` + +### 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). + +```C++ +/* HomeSpan Remote Control Example */ + +#include "HomeSpan.h" // include the HomeSpan library +#include "extras/RFControl.h" // include RF Control Library + +void setup() { + + Serial.begin(115200); // start the Serial interface + Serial.flush(); + delay(1000); // wait for interface to flush + + Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); + + RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 + + rf.clear(); // clear the pulse train memory buffer + + rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses + rf.add(5000,5000); + rf.add(5000,10000); // double duration of final low period + + Serial.print("Starting 4 cycles of three 500 ms on pulses..."); + + rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + delay(2000); + + rf.clear(); + + for(int i=1000;i<10000;i+=1000) + rf.add(i,10000-i); + rf.add(10000,10000); + + Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); + + rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds + + Serial.print("Done!\n"); + + Serial.print("\nEnd Example"); + +} // end of setup() + +void loop(){ + +} // end of loop() +``` + +--- + +[↩️](README.md) Back to the Welcome page From 278e2032964620eac4cca6a609cd58c4ceef2864 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:28:44 -0600 Subject: [PATCH 091/106] Update PWM.md --- docs/PWM.md | 149 ++-------------------------------------------------- 1 file changed, 3 insertions(+), 146 deletions(-) diff --git a/docs/PWM.md b/docs/PWM.md index 9b0cb7d..7743080 100644 --- a/docs/PWM.md +++ b/docs/PWM.md @@ -1,14 +1,10 @@ -# HomeSpan Extras - -HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. - -## Pulse Width Modulation (PWM) +# Pulse Width Modulation (PWM) The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: `#include "extras/PwmPin.h"` -### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* +## *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: @@ -39,7 +35,7 @@ LedPin also includes a static class function that converts Hue/Saturation/Bright See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. -### *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]])* Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: @@ -74,145 +70,6 @@ HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin ob HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. -## Remote Control Radio Frequency / Infrared Signal Generation - -The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: - -`#include "extras/RFControl.h"` - -### *RFControl(int pin, boolean refClock=true)* - -Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. - -Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: - -![Pulse Train](images/pulseTrain.png) - -Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: - - * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted - * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted - * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - -HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. - -In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. - -Details of each function are as follows: - -* `void clear()` - - * clears the pulse train memory structure of a specific instance of RFControl - -* `void phase(uint32_t numTicks, uint8_t phase)` - - * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl - - * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) - - * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase - - * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) - -* `void add(uint32_t onTime, uint32_t offTime)` - - * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above - -* `void enableCarrier(uint32_t freq, float duty=0.5)` - - * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) - - * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled - - * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified - - * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations - -* `void disableCarrier()` - - * disables the carrier wave. Equivalent to `enableCarrier(0);` - -* `void start(uint8_t _numCycles, uint8_t tickTime)` -* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` - - * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long - - * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. - - * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) - -* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: - -```C++ - -RFControl rf(11); // create an instance of RFControl - -rf.clear(); // clear the internal memory structure -rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW -rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s - -uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array -rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters -``` - -### 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). - -```C++ -/* HomeSpan Remote Control Example */ - -#include "HomeSpan.h" // include the HomeSpan library -#include "extras/RFControl.h" // include RF Control Library - -void setup() { - - Serial.begin(115200); // start the Serial interface - Serial.flush(); - delay(1000); // wait for interface to flush - - Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); - - RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 - - rf.clear(); // clear the pulse train memory buffer - - rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses - rf.add(5000,5000); - rf.add(5000,10000); // double duration of final low period - - Serial.print("Starting 4 cycles of three 500 ms on pulses..."); - - rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - delay(2000); - - rf.clear(); - - for(int i=1000;i<10000;i+=1000) - rf.add(i,10000-i); - rf.add(10000,10000); - - Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); - - rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - Serial.print("\nEnd Example"); - -} // end of setup() - -void loop(){ - -} // end of loop() -``` - --- [↩️](README.md) Back to the Welcome page From 70cc546df510c83beeb0d59ee082119ef25f6f7f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:31:55 -0600 Subject: [PATCH 092/106] Update RMT.md --- docs/RMT.md | 84 +++-------------------------------------------------- 1 file changed, 4 insertions(+), 80 deletions(-) diff --git a/docs/RMT.md b/docs/RMT.md index 9b0cb7d..112585b 100644 --- a/docs/RMT.md +++ b/docs/RMT.md @@ -1,86 +1,10 @@ -# HomeSpan Extras +# Remote Control Radio Frequency / Infrared Signal Generation -HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. - -## Pulse Width Modulation (PWM) - -The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: - -`#include "extras/PwmPin.h"` - -### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* - -Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output - * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) - * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 - - The following methods are supported: - -* `void set(float level)` - - * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) - -* `int getPin()` - - * returns the pin number (or -1 if LedPin was not successfully initialized) - -LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). - -* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` - - * *h* - input Hue value, range 0-360 - * *s* - input Saturation value, range 0-1 - * *v* - input Brightness value, range 0-1 - * *r* - output Red value, range 0-1 - * *g* - output Green 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. - -### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* - -Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin - * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) - * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) - * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) - * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) - * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) - -The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: - -* `void set(double position)` - - * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. - -* `int getPin()` - - * 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). - -### PWM Resource Allocation and Limitations - -The following PWM resources are available: - -* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) -* ESP32-S2: 8 Channels / 4 Timers -* ESP32-C3: 6 Channels / 4 Timers - -HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* - -HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. - -## Remote Control Radio Frequency / Infrared Signal Generation - -The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: +The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed by placing the following near the top of your sketch: `#include "extras/RFControl.h"` -### *RFControl(int pin, boolean refClock=true)* +## *RFControl(int pin, boolean refClock=true)* Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. @@ -158,7 +82,7 @@ uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; 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). From 3b1a76b878a6f7dcb260218c4acccc9cedf5f27e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:35:34 -0600 Subject: [PATCH 093/106] Update GettingStarted.md --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 8a945e6..990dbad 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -14,7 +14,7 @@ The Arduino IDE comes with built-in compilers and support for a variety of Ardui HomeSpan is packaged as a standard Arduino library and can be installed either automatically from the Arduino IDE, or manually via GitHub. -* To install automatically, open the Arduino Library Manager from wihtin the Arduino IDE by selecting *Sketch β†’ Include Library β†’ Manage Libraries...* from the Arduino menu bar. Then, type *HomeSpan* into the search box, choose the latest version from the drop-down box, and click the `Install` button. The Arduino Library Manager will automatically download and install the version you selected. HomeSpan and all of the tutorial example sketches are now ready for use. +* To install automatically, open the Arduino Library Manager from within the Arduino IDE by selecting *Sketch β†’ Include Library β†’ Manage Libraries...* from the Arduino menu bar. Then, type *HomeSpan* into the search box, choose the latest version from the drop-down box, and click the `Install` button. The Arduino Library Manager will automatically download and install the version you selected. HomeSpan and all of the tutorial example sketches are now ready for use. * To install manually, first download the [latest version](https://github.com/HomeSpan/HomeSpan/releases/latest) of *Source Code (zip)* to your desktop. It does not matter if your operating system keeps the package in zip form, or automatically unzips it when saving to you desktop. Next, from the top menu bar within the Arduino IDE, select *Sketch β†’ Include Library β†’ Add .ZIP Library...* and navigtate to the Desktop folder where you should see the HomeSpan package you just downloaded (either as a zip file, or a folder). Select the package (don't open it) and click `Choose`. This directs the Arduino IDE to copy the HomeSpan package into its library sub-folder. HomeSpan is now ready for use as a standard Arduino library (you may delete the HomeSpan package from your desktop as it is no longer needed). From b341f43d42b299a70a5a4bfc57b8ddd4e98e4f80 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:49:16 -0600 Subject: [PATCH 094/106] Update README.md --- docs/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index e0a895c..60363e9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,7 +28,10 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo * Servo Motors * Integrated Push Button functionality supporting single, double, and long presses * Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals -* 18 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks +* Dedicated classes to control one- and two-wire addressable RGBW LEDs and LED strips +* Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics +* Additional examples and projects showcasing advanced real-world implementations of HomeSpan +* A complete set of documentation explaining every aspect of the HomeSpan API ### For the HomeSpan End-User From 9e6989782d9a22f22277eae67a040b421d228ff8 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 07:50:43 -0600 Subject: [PATCH 095/106] Delete Extras.md --- docs/Extras.md | 218 ------------------------------------------------- 1 file changed, 218 deletions(-) delete mode 100644 docs/Extras.md diff --git a/docs/Extras.md b/docs/Extras.md deleted file mode 100644 index 9b0cb7d..0000000 --- a/docs/Extras.md +++ /dev/null @@ -1,218 +0,0 @@ -# HomeSpan Extras - -HomeSpan includes integrated access to a number of ESP32 features you'll likely find particularly useful when constructing your HomeSpan devices. - -## Pulse Width Modulation (PWM) - -The ESP32 has up to 16 PWM channels that can be used to drive a variety of devices. HomeSpan includes an integrated PWM library with dedicated classes designed for controlling **Dimmable LEDs** as well as **Servo Motors**. Both classes are provided in a standalone header file that is accessed by placing the following near the top of your sketch: - -`#include "extras/PwmPin.h"` - -### *LedPin(uint8_t pin [,float level [,uint16_t frequency]])* - -Creating an instance of this **class** configures the specified *pin* to output a PWM signal suitable for a controlling dimmable LED. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output - * *level* - sets the initial %duty-cycle of the PWM from from 0 (LED completely off) to 100 (LED fully on). Default=0 (LED initially off) - * *frequency* - sets the PWM frequency, in Hz, from 1-65535 (ESP32 only) or 5-65535 (ESP32-S2 and ESP32-C3). Defaults to 5000 Hz if unspecified, or if set to 0 - - The following methods are supported: - -* `void set(float level)` - - * sets the PWM %duty-cycle to *level*, where *level* ranges from 0 (LED completely off) to 100 (LED fully on) - -* `int getPin()` - - * returns the pin number (or -1 if LedPin was not successfully initialized) - -LedPin also includes a static class function that converts Hue/Saturation/Brightness values (typically used by HomeKit) to Red/Green/Blue values (typically used to control multi-color LEDS). - -* `static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b)` - - * *h* - input Hue value, range 0-360 - * *s* - input Saturation value, range 0-1 - * *v* - input Brightness value, range 0-1 - * *r* - output Red value, range 0-1 - * *g* - output Green 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. - -### *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* - -Creating an instance of this **class** configures the specified *pin* to output a 50 Hz PWM signal, which is suitable for controlling most Servo Motors. There are three forms of the constructor: one with just a single argument; one with two arguments; and one with all six arguments. Arguments, along with their defaults if left unspecified, are as follows: - - * *pin* - the pin on which the PWM control signal will be output. The control wire of a Servo Motor should be connected this pin - * *initDegrees* - the initial position (in degrees) to which the Servo Motor should be set (default=0Β°) - * *minMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "minimium" position of *minDegrees* (default=1000𝛍s) - * *maxMicros* - the pulse width (in microseconds) that moves the Servo Motor to its "maximum" position of *maxDegrees* (default=2000𝛍s) - * *minDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *minMicros* (default=-90Β°) - * *maxDegrees* - the position (in degrees) to which the Servo Motor moves when receiving a pulse width of *maxMicros* (default=90Β°) - -The *minMicros* parameter must be less than the *maxMicros* parameter, but setting *minDegrees* to a value greater than *maxDegrees* is allowed and can be used to reverse the minimum and maximum positions of the Servo Motor. The following methods are supported: - -* `void set(double position)` - - * sets the position of the Servo Motor to *position* (in degrees). In order to protect the Servo Motor, values of *position* less than *minDegrees* are automatically reset to *minDegrees*, and values greater than *maxDegrees* are automatically reset to *maxDegrees*. - -* `int getPin()` - - * 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). - -### PWM Resource Allocation and Limitations - -The following PWM resources are available: - -* ESP32: 16 Channels / 8 Timers (arranged in two distinct sets of 8 Channels and 4 Timers) -* ESP32-S2: 8 Channels / 4 Timers -* ESP32-C3: 6 Channels / 4 Timers - -HomeSpan *automatically* allocates Channels and Timers to LedPin and ServoPin objects as they are instantiated. Every pin assigned consumes a single Channel; every *unique* frequency specified among all channels (within the same set, for the ESP32) consumes a single Timer. HomeSpan will conserve resources by re-using the same Timer for all Channels operating at the same frequency. *HomeSpan also automatically configures each Timer to support the maximum duty-resolution possible for the frequency specified.* - -HomeSpan will report a non-fatal error message to the Arduino Serial Monitor when insufficient Channel or Timer resources prevent the creation of a new LedPin or ServoPin object. Calls to the `set()` method for objects that failed to be properly created are silently ignored. - -## Remote Control Radio Frequency / Infrared Signal Generation - -The ESP32 has an on-chip signal-generator peripheral designed to drive an RF or IR transmitter. HomeSpan includes an easy-to-use library that interfaces with this peripheral so that with a few additional electronic components you can create a HomeSpan device that controls an RF or IR appliance directly from the Home App on your iPhone, or via Siri. The library is accessed the following near the top of your sketch: - -`#include "extras/RFControl.h"` - -### *RFControl(int pin, boolean refClock=true)* - -Creating an instance of this **class** initializes the RF/IR signal generator and specifies the ESP32 *pin* to output the signal. You may create more than one instance of this class if driving more than one RF/IR transmitter (each connected to different *pin*), subject to the following limitations: ESP32 - 8 instances; ESP32-S2 - 4 instances; ESP32-C3 - 2 instances. The optional parameter *refClock* is more fully described further below under the `start()` method. - -Signals are defined as a sequence of HIGH and LOW phases that together form a pulse train where you specify the duration, in *ticks*, of each HIGH and LOW phase, shown respectively as H1-H4 and L1-L4 in the following diagram: - -![Pulse Train](images/pulseTrain.png) - -Since most RF/IR signals repeat the same train of pulses more than once, the duration of the last LOW phase should be extended to account for the delay between repeats of the pulse train. Pulse trains are encoded as sequential arrays of 32-bit words, where each 32-bit word represents an individual pulse using the following protocol: - - * bits 0-14: the duration, in *ticks* from 0-32767, of the first part of the pulse to be transmitted - * bit 15: indicates whether the first part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - * bits 16-30: the duration, in *ticks* from 0-32767, of the second part of the pulse to be transmitted - * bit 31: indicates whether the second part of the pulse to be trasnmitted is HIGH (1) or LOW (0) - -HomeSpan provides two easy methods to create, store, and transmit a pulse train. The first method relies on the fact that each instance of RFControl maintains its own internal memory structure to store a pulse train of arbitrary length. The functions `clear()`, `add()`, and `pulse()`, described below, allow you to create a pulse train using this internal memory structure. The `start()` function is then used to begin transmission of the full pulse train. This method is generally used when pulse trains are to be created on-the-fly as needed, since each RFControl instance can only store a single pulse train at one time. - -In the second method, you create one or more pulse trains in external arrays of 32-bit words using the protocol above. To begin transmission of a specific pulse train, call the `start()` function with a pointer reference to the external array containing that pulse train. This method is generally used when you want to pre-compute many different pulse trains and have them ready-to-transmit as needed. Note that this method requires the array to be stored in RAM, not PSRAM. - -Details of each function are as follows: - -* `void clear()` - - * clears the pulse train memory structure of a specific instance of RFControl - -* `void phase(uint32_t numTicks, uint8_t phase)` - - * appends either a HIGH or LOW phase to the pulse train memory buffer for a specific instance of RFControl - - * *numTicks* - the duration, in *ticks* of the pulse phase. Durations of greater than 32767 ticks allowed (the system automatically creates repeated pulses of a maximum of 32767 ticks each until the specified duration of *numTicks* is reached) - - * *phase* - set to 0 to create a LOW phase; set to 1 (or any non-zero number) to create a HIGH phase - - * repeated phases of the same type (e.g. HIGH followed by another HIGH) are permitted and result in a single HIGH or LOW phase with a duration equal to the sum of the *numTicks* specified for each repeated phase (this is helpful when generating Manchester-encoded signals) - -* `void add(uint32_t onTime, uint32_t offTime)` - - * appends a single HIGH/LOW pulse with duration *onTime* followed by *offTime* to the pulse train of a specific instance of RFControl. This is functionally equivalent to calling `phase(onTime,HIGH);` followed by `phase(offTime,LOW);` as defined above - -* `void enableCarrier(uint32_t freq, float duty=0.5)` - - * enables modulation of the pulse train with a "square" carrier wave. In practice this is only used for IR signals (not RF) - - * *freq* - the frequency, in Hz, of the carrier wave. If freq=0, carrier wave is disabled - - * *duty* - the duty cycle of the carrier wave, from 0-1. Default is 0.5 if not specified - - * RFControl will report an error if the combination of the specified frequency and duty cycle is outside the range of supported configurations - -* `void disableCarrier()` - - * disables the carrier wave. Equivalent to `enableCarrier(0);` - -* `void start(uint8_t _numCycles, uint8_t tickTime)` -* `void start(uint32_t *data, int nData, uint8_t nCycles, uint8_t tickTime)` - - * in the first variation, this starts the transmission of the pulse train stored in the internal memory structure of a given instance of RFControl that was created using the `clear()`, `add()`, and `phase()` functions above. In the second variation, this starts the transmission of the pulse train stored in an external array *data* containing *nData* 32-bit words. The signal will be output on the pin specified when RFControl was instantiated. Note this is a blocking callβ€”the method waits until transmission is completed before returning. This should not produce a noticeable delay in program operations since most RF/IR pulse trains are only a few tens-of-milliseconds long - - * *numCycles* - the total number of times to transmit the pulse train (i.e. a value of 3 means the pulse train will be transmitted once, followed by 2 additional re-transmissions). This is an optional argument with a default of 1 if not specified. - - * *tickTime* - the duration, in ***clock units***, of a *tick*. This is an optional argument with a default of 1 *clock unit* if not specified. Valid range is 1-255 *clock units*, or set to 0 for 256 *clock units*. The duration of a *clock unit* is determined by the *refClock* parameter (the second, optional argument, in the RFControl constructor described above). If *refClock* is set to true (the default), RFControl uses the ESP32's 1 MHz Reference Clock for timing so that each *clock unit* equals 1𝛍s. If *refClock* is set to false, RFControl uses the ESP32's faster 80 MHz APB Clock so that each *clock unit* equals 0.0125𝛍s (1/80 of microsecond) - -* To aid in the creation of a pulse train stored in an external array of 32-bit words, RFControl includes the macro *RF_PULSE(highTicks,lowTicks)* that returns a properly-formatted 32-bit value representing a single HIGH/LOW pulse of duration *highTicks* followed by *lowTicks*. This is basically an analog to the `add()` function. For example, the following code snippet shows two ways of creating and transmitting the same 3-pulse pulse-train --- the only difference being that one uses the internal memory structure of RFControl, and the second uses an external array: - -```C++ - -RFControl rf(11); // create an instance of RFControl - -rf.clear(); // clear the internal memory structure -rf.add(100,50); // create pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(100,50); // create a second pulse of 100 ticks HIGH followed by 50 ticks LOW -rf.add(25,500); // create a third pulse of 25 ticks HIGH followed by 500 ticks LOW -rf.start(4,1000); // start transmission of the pulse train; repeat for 4 cycles; one tick = 1000𝛍s - -uint32_t pulseTrain[] = {RF_PULSE(100,50), RF_PULSE(100,50), RF_PULSE(25,500)}; // create the same pulse train in an external array -rf.start(pulseTrain,3,4,1000); // start transmission using the same parameters -``` - -### 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). - -```C++ -/* HomeSpan Remote Control Example */ - -#include "HomeSpan.h" // include the HomeSpan library -#include "extras/RFControl.h" // include RF Control Library - -void setup() { - - Serial.begin(115200); // start the Serial interface - Serial.flush(); - delay(1000); // wait for interface to flush - - Serial.print("\n\nHomeSpan RF Transmitter Example\n\n"); - - RFControl rf(13); // create an instance of RFControl with signal output to pin 13 on the ESP32 - - rf.clear(); // clear the pulse train memory buffer - - rf.add(5000,5000); // create a pulse train with three 5000-tick high/low pulses - rf.add(5000,5000); - rf.add(5000,10000); // double duration of final low period - - Serial.print("Starting 4 cycles of three 500 ms on pulses..."); - - rf.start(4,100); // start transmission of 4 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - delay(2000); - - rf.clear(); - - for(int i=1000;i<10000;i+=1000) - rf.add(i,10000-i); - rf.add(10000,10000); - - Serial.print("Starting 3 cycles of 100-1000 ms pulses..."); - - rf.start(3,100); // start transmission of 3 cycles of the pulse train with 1 tick=100 microseconds - - Serial.print("Done!\n"); - - Serial.print("\nEnd Example"); - -} // end of setup() - -void loop(){ - -} // end of loop() -``` - ---- - -[↩️](README.md) Back to the Welcome page From 7bb028b17cdace63a1fd61b4fa2d053c3770ee4e Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 08:58:40 -0600 Subject: [PATCH 096/106] Update README.md --- docs/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 60363e9..e108239 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,7 +30,7 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo * 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 RGBW LEDs and LED strips * Extensively-commented Tutorial Sketches taking you from the very basics of HomeSpan through advanced HomeKit topics -* Additional examples and projects showcasing advanced real-world implementations of HomeSpan +* 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 @@ -75,7 +75,9 @@ HomeSpan includes the following documentation: * [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) - a complete guide to the HomeSpan Library API * [HomeSpan QR Codes](https://github.com/HomeSpan/HomeSpan/blob/master/docs/QRCodes.md) - create and use QR Codes for pairing HomeSpan devices * [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection -* [HomeSpan Extras](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Extras.md) - integrated access to the ESP32's on-chip LED, Servo Motor, and Remote Control peripherals! +* [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral +* [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral +* [HomeSpan Pixel](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library * [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions From f7014ff154895e4d4839e9bf3999d49874f0c8c0 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 11:57:36 -0600 Subject: [PATCH 097/106] Update Reference.md --- docs/Reference.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 73e0f57..7762734 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -329,10 +329,11 @@ To create more than one user-defined command, simply create multiple instances o ### *#define REQUIRED VERSION(major,minor,patch)* -If REQUIRED is defined in the main sketch prior to including the HomeSpan library with `#include "HomeSpan.h"`, HomeSpan will throw a compile-time error unless the version of the library included is equal to, or later than, the version specified using the VERSION macro. Example: +If REQUIRED is defined in the main sketch *prior* to including the HomeSpan library with `#include "HomeSpan.h"`, HomeSpan will throw a compile-time error unless the version of the library included is equal to, or later than, the version specified using the VERSION macro. Example: ```C++ -#define REQUIRED VERISON(2,1,3) // throws a compile-time error unless HomeSpan library used is version 2.1.3 or later +#define REQUIRED VERSION(1,3,0) // throws a compile-time error unless HomeSpan library used is version 1.3.0 or later +#include "HomeSpan.h" ``` ### *#define CUSTOM_CHAR(name,uuid,perms,format,defaultValue,minValue,maxValue,staticRange)* ### *#define CUSTOM_CHAR_STRING(name,uuid,perms,defaultValue)* From 7bfc1d48fb66c7fc07aa2515e951b062fab05edc Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 20 Feb 2022 12:09:03 -0600 Subject: [PATCH 098/106] Updated version to 1.5.0 --- src/Settings.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Settings.h b/src/Settings.h index 4d71ed3..ef72ef7 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -35,8 +35,8 @@ // HomeSpan Version // #define HS_MAJOR 1 -#define HS_MINOR 4 -#define HS_PATCH 3 +#define HS_MINOR 5 +#define HS_PATCH 0 #define STRINGIFY(x) _STR(x) #define _STR(x) #x @@ -50,7 +50,7 @@ #endif #if (REQUIRED>VERSION(HS_MAJOR,HS_MINOR,HS_PATCH)) - #error THIS SKETCH REQUIRES A LATER VERISON OF THE HOMESPAN LIBRARY + #error THIS SKETCH REQUIRES A LATER VERSION OF THE HOMESPAN LIBRARY #endif #define ARDUINO_ESP_VERSION STRINGIFY(ARDUINO_ESP32_GIT_DESC) From 76d86b65007ceb7a0a95c166d4b40e9cb2554fdf Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 12:10:30 -0600 Subject: [PATCH 099/106] Updated to version 1.5.0 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 42657a9..8a9eb30 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.4.2 +version=1.5.0 author=Gregg maintainer=Gregg sentence=A robust and extremely easy-to-use HomeKit implementation for the Espressif ESP32 running on the Arduino IDE. From fd0936d91cfa5767f73aa6ffc1ebb8f9d76541f6 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 12:29:14 -0600 Subject: [PATCH 100/106] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index e108239..dce69b0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -77,7 +77,7 @@ HomeSpan includes the following documentation: * [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection * [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral * [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral -* [HomeSpan Pixel](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips +* [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library * [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions From 7408379b9a6b0cd7e375765e616a9218122d34c9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 12:29:42 -0600 Subject: [PATCH 101/106] Update README.md --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index dce69b0..17d9cf6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -77,7 +77,7 @@ HomeSpan includes the following documentation: * [HomeSpan OTA](https://github.com/HomeSpan/HomeSpan/blob/master/docs/OTA.md) - update your sketches Over-the-Air directly from the Arduino IDE without a serial connection * [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md) - integrated control of standard LEDs and Servo Motors using the ESP32's on-chip PWM peripheral * [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md) - easy generation of RF and IR Remote Control signals using the ESP32's on-chip RMT peripheral -* [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips +* [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) - integrated control of addressable one- and two-wire RGB and RGBW LEDs and LED strips * [HomeSpan Television Services](https://github.com/HomeSpan/HomeSpan/blob/master/docs/TVServices.md) - how to use HomeKit's undocumented Television Services and Characteristics * [HomeSpan Projects](https://github.com/topics/homespan) - real-world applications of the HomeSpan Library * [HomeSpan FAQ](https://github.com/HomeSpan/HomeSpan/blob/master/docs/FAQ.md) - answers to frequently-asked questions From 3efe5e60779387c4e6af5245559ed7fe99304e4f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 13:00:25 -0600 Subject: [PATCH 102/106] Update README.md --- docs/README.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/README.md b/docs/README.md index 17d9cf6..f568117 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,7 +28,7 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo * Servo Motors * Integrated Push Button functionality supporting single, double, and long presses * 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 RGBW LEDs and LED strips +* Dedicated classes to control one- and two-wire addressable RGB and RGBW LEDs and LED strips * 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 @@ -44,22 +44,26 @@ HomeSpan is fully compatible with both Versions 1 and 2 of the [Arduino-ESP32 Bo * Launch the WiFi Access Point * A standalone, detailed End-User Guide -## ❗Latest Update - HomeSpan 1.4.2 (11/27/2021) +## ❗Latest Update - HomeSpan 1.5.0 (2/20/2022) -* **Updated for compatability with Arduino-ESP32 Board Manager 2.0.1** - * Maintains backward compatability with all previous versions +* **New integrated library to control one- and two-wire Addressable RGB and RGBW LEDs and LED strips!** + * Adds two new class: + * **Pixel()** for control of one-wire RGB and RGBW LEDs and LED strips, such as this [NeoPixel RGBW LED](https://www.adafruit.com/product/2759) + * **Dot()** for control of two-wire RGB LEDs and LED strips, such as this [DotStar RGB Strip](https://www.adafruit.com/product/2241) + + * See [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixel.md) for full details, including a detailed tutorial sketch demonstrating different ways of using the **Pixel()** and **Dot()** classes, as well an advanced HomeSpan "HolidayLights" Project that shows how to develop custom special effects! -* **Some new methods and options for advance-use circumstances:** +* **Increased the maximum number of simultaneous Controller connections from 8 to 14 (for Arduino-ESP32 version 2.0.1 and later)** + * Added new method `reserveSocketConnection(uint8_t nSockets)` to the global homeSpan object that allows for better management of custom connections + * Deprecated older `setMaxConnections(uint8_t nCon)` method - * Added *optional* second argument to the `setVal()` method that allows the value of a Characteristic to be updated *without* sending notification messages to HomeKit. Useful for keeping track of duration time when implementing a Sprinkler System - see [HomeSpan Reference Sprinkler](https://github.com/HomeSpan/HomeSpanReferenceSketches/tree/main/ReferenceSprinklers) for an example +* **Additional updates include:** + * Added new methods `setDescription(const char *desc)` and `setUnit(const char *unit)` to SpanCharacteristic. Useful when creating and working with Custom Characteristics + * Added new method `setStatusAutoOff(uint16_t duration)` to the global homeSpan object. Causes the Status LED (if used) to automatically turn off after *duration* seconds. Very handy for devices located in bedrooms or TV rooms! + * Added new method `setPairCallback(func)` to the global homeSpan object. Allows you to create custom actions whenever HomeSpan pairs or subsequently unpairs the device to the Home App + * Added new method `deleteStoredValues()` to the global homeSpan object. Provides a programmatic way of deleting the value settings of all Characteristics stored in the NVS - * Added `getLinks()` as a new method to SpanService. Returns a vector of pointers to SpanServices that have been linked to another Service with the addLink() method. Useful for looping over all linked services, such as checking all valves in a Shower System - see [HomeSpan Reference Shower](https://github.com/HomeSpan/HomeSpanReferenceSketches/tree/main/ReferenceShower) for an example - - * Added `setPerms()`, `addPerms()`, and `removePerms()` as new methods to SpanCharacteristic. Allows the user to modify (set/add/remove) the default permissions for any Characteristic. Useful for adding/deleting write-permissions for certain Characteristics - see [Television Example](https://github.com/HomeSpan/HomeSpan/tree/master/Other%20Examples/Television) for an example - - * Added `setPairingCode()` method to the global homeSpan object that allows for programmatically configuring the Pairing Setup Code inside your sketch. See the HomeSpan API for important security considerations when using this function! - -See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update. +See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes and bug fixes included in this update. # HomeSpan Resources From 02d841390cb5bba22d6ea53705f5e4c593835a76 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 20 Feb 2022 13:05:03 -0600 Subject: [PATCH 103/106] Updated License Dates --- src/Characteristics.h | 2 +- src/HAP.cpp | 2 +- src/HAP.h | 2 +- src/HAPConstants.h | 2 +- src/HKDF.cpp | 2 +- src/HKDF.h | 2 +- src/HapQR.h | 2 +- src/HomeSpan.cpp | 2 +- src/HomeSpan.h | 2 +- src/Network.cpp | 2 +- src/Network.h | 2 +- src/SRP.cpp | 2 +- src/SRP.h | 2 +- src/Settings.h | 2 +- src/Span.h | 2 +- src/TLV.h | 2 +- src/Utils.cpp | 2 +- src/Utils.h | 2 +- src/extras/Pixel.cpp | 28 +++++++++++++++++++++++++++- src/extras/Pixel.h | 28 +++++++++++++++++++++++++++- src/extras/PwmPin.cpp | 28 +++++++++++++++++++++++++++- src/extras/PwmPin.h | 28 +++++++++++++++++++++++++++- src/extras/RFControl.cpp | 28 +++++++++++++++++++++++++++- src/extras/RFControl.h | 28 +++++++++++++++++++++++++++- src/extras/extras.ino | 3 ++- 25 files changed, 182 insertions(+), 25 deletions(-) diff --git a/src/Characteristics.h b/src/Characteristics.h index dbb3424..7caa9d9 100644 --- a/src/Characteristics.h +++ b/src/Characteristics.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HAP.cpp b/src/HAP.cpp index 31f4435..89bdc9d 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HAP.h b/src/HAP.h index 1d48e89..7818d7d 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HAPConstants.h b/src/HAPConstants.h index 77bd183..0b42451 100644 --- a/src/HAPConstants.h +++ b/src/HAPConstants.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HKDF.cpp b/src/HKDF.cpp index 9c75be5..bf5f529 100644 --- a/src/HKDF.cpp +++ b/src/HKDF.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HKDF.h b/src/HKDF.h index c2057ba..787ef05 100644 --- a/src/HKDF.h +++ b/src/HKDF.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HapQR.h b/src/HapQR.h index 9b47938..737d04c 100644 --- a/src/HapQR.h +++ b/src/HapQR.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 835e510..c4d2596 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 7a86512..f8df8cf 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Network.cpp b/src/Network.cpp index 7de6045..8b2ad91 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Network.h b/src/Network.h index 035c3eb..663f744 100644 --- a/src/Network.h +++ b/src/Network.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/SRP.cpp b/src/SRP.cpp index 8af05eb..66a3611 100644 --- a/src/SRP.cpp +++ b/src/SRP.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/SRP.h b/src/SRP.h index 01c5f47..d8bca6e 100644 --- a/src/SRP.h +++ b/src/SRP.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Settings.h b/src/Settings.h index ef72ef7..1c73c56 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Span.h b/src/Span.h index 57eda29..03fd31a 100644 --- a/src/Span.h +++ b/src/Span.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/TLV.h b/src/TLV.h index 30ddb67..40d9eff 100644 --- a/src/TLV.h +++ b/src/TLV.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Utils.cpp b/src/Utils.cpp index 953b9bb..282d0e0 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/Utils.h b/src/Utils.h index 38d11b6..ff8784f 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020-2021 Gregg E. Berman + * Copyright (c) 2020-2022 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * diff --git a/src/extras/Pixel.cpp b/src/extras/Pixel.cpp index 7b8d925..479d115 100644 --- a/src/extras/Pixel.cpp +++ b/src/extras/Pixel.cpp @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + //////////////////////////////////////////// // Addressable LEDs // //////////////////////////////////////////// diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index de320aa..79a7b5a 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + //////////////////////////////////////////// // Addressable LEDs // //////////////////////////////////////////// diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 31609de..5359c4c 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + #include "PwmPin.h" /////////////////// diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 50eebb8..c3d3f4c 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // ----- PWM Pin Control ----- ///////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/extras/RFControl.cpp b/src/extras/RFControl.cpp index 182cefb..6b6f1b6 100644 --- a/src/extras/RFControl.cpp +++ b/src/extras/RFControl.cpp @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + #include "RFControl.h" /////////////////// diff --git a/src/extras/RFControl.h b/src/extras/RFControl.h index ea8f731..34cceb2 100644 --- a/src/extras/RFControl.h +++ b/src/extras/RFControl.h @@ -1,4 +1,30 @@ - +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + //////////////////////////////////// // RF Control Module // //////////////////////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 703acf9..8d47d19 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -1,4 +1,5 @@ -/* HomeSpan Pixel Example */ +// 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. #include "Pixel.h" From f247f66071c9407f814b8d4bddb1a417fe8051f6 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 13:15:30 -0600 Subject: [PATCH 104/106] Update GettingStarted.md --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 990dbad..99a1c9b 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -66,7 +66,7 @@ Ready to start creating your own HomeSpan sketches? Check out the [HomeSpan Ser 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. -For access to even more advanced features, check out the [HomeSpan Extras](Extras.md) page for details on how to use HomeSpan's integrated PWM and Remote Control libraries. +For access to even more advanced features, check out the [HomeSpan PWM](https://github.com/HomeSpan/HomeSpan/blob/master/docs/PWM.md), [HomeSpan RFControl](https://github.com/HomeSpan/HomeSpan/blob/master/docs/RMT.md), and [HomeSpan Pixels](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Pixels.md) pages for tutorials and details on how HomeSpan can be used to control LEDs, lights, and Servo Motors with PWM signals; generate RF and IR Remote Control codes to operate appliances and TVs; and even control multicolor RGB LED strips. Finally, disconnect your HomeSpan device from the computer and power it directly from a wall adapter. After all, HomeSpan devices are designed to run on a standalone basis connected to real-world applicances like lights, fans, door locks, and window shades. See the [HomeSpan User Guide](UserGuide.md) for end-user instructions on how to operate and configure a standlone HomeSpan device's WiFi Credentials and HomeKit Setup Code *without the need to connect the device to a computer*. From 9c53e7e025bbc4bb26af8bee3acf2800473501a2 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 13:32:47 -0600 Subject: [PATCH 105/106] Update FAQ.md --- docs/FAQ.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 996e84a..d75533a 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -8,6 +8,10 @@ * Instead, HomeSpan stores your WiFi Credentials in a dedicated non-volatile storage (NVS) partition of the ESP32. There are two ways of entering this information. If you are developing a sketch within the Arduino IDE, simply use the HomeSpan Command Line Interface and type 'W' into the Serial Monitor. HomeSpan will prompt you to input your WiFi SSID and Password (see [CLI](CLI.md) for details). As an alternative, if your HomeSpan device is not connected to a computer, you can launch HomeSpan's Temporary WiFi Setup Network and input your WiFi Credentials directly into the web forms served by your device (see the [User Guide](UserGuide.md#setting-homespans-wifi-credentials-and-setup-code) for details). +#### What is the Setup Code used for pairing a HomeSpan device to HomeKit? + +* The HomeSpan default Setup Code is 466-37-726. You can (and should) use the HomeSpan Command Line Interface to change this default to a secret code unique for each of your devices. Simply type 'S \' into the Serial Monitor, or specify your desired Setup Code at the outset when configuring your WiFi Credentials using HomeSpan's Temporary WiFi Setup Network. + #### Does HomeSpan support video and audio streams? * No, HomeSpan does not support streaming services, such as Audio Stream Management, Data Stream Transport Management, or Camera RTP Management. See [HomeSpan Services and Characteristics](ServiceList.md) for a complete list of all Services supported by HomeSpan. From 49ba6a2e3e46e576d8ea4f67b32a08d2d9160481 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Feb 2022 13:46:04 -0600 Subject: [PATCH 106/106] Update FAQ.md --- docs/FAQ.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index d75533a..fd53287 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -12,6 +12,10 @@ * The HomeSpan default Setup Code is 466-37-726. You can (and should) use the HomeSpan Command Line Interface to change this default to a secret code unique for each of your devices. Simply type 'S \' into the Serial Monitor, or specify your desired Setup Code at the outset when configuring your WiFi Credentials using HomeSpan's Temporary WiFi Setup Network. +#### Can you use more than one HomeSpan device on the same HomeKit network? + +* Yes, multiple ESP32's, each running a separate copy of HomeSpan, can be used on the same HomeKit network, provided that each device has a unique *Device ID*, a unique *Host Name*, and a unique *Display Name*. Normally, the *Device ID* is randomly-generated by HomeSpan at start-up and will therefore automatically be unique across multiple devices. Also, unless you override the suffix of the default *Host Name*, it will also be unique across multiple devices since HomeSpan uses the *Device ID* as the suffix of the *Host Name*. The only thing you need to ensure is that you assign a different *Display Name* to each of your devices. See the [HomeSpan API Reference](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Reference.md) for details on how to do this. + #### Does HomeSpan support video and audio streams? * No, HomeSpan does not support streaming services, such as Audio Stream Management, Data Stream Transport Management, or Camera RTP Management. See [HomeSpan Services and Characteristics](ServiceList.md) for a complete list of all Services supported by HomeSpan.