From 61655845a9d31051a3426f415017d52ee6d49c6d Mon Sep 17 00:00:00 2001 From: Gregg Date: Fri, 19 Mar 2021 06:38:41 -0500 Subject: [PATCH 01/55] Updated PWM Removed need to include channel number in set() method. Why was this ever there? Need to update Examples and PwmPin docs. But keep stub method that includes channel for backwards compatibility. --- src/extras/PwmPin.cpp | 3 ++- src/extras/PwmPin.h | 7 ++++--- src/extras/extras.ino | 29 ++++++++++++++++------------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 6fb67cf..58c7fa6 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -20,10 +20,11 @@ PwmPin::PwmPin(uint8_t channel, uint8_t pin){ ledChannel.timer_sel=LEDC_TIMER_0; ledChannel.duty=0; ledChannel.hpoint=0; + ledc_channel_config(&ledChannel); } -void PwmPin::set(uint8_t channel, uint8_t level){ +void PwmPin::set(uint8_t level){ ledChannel.duty=level*1023; ledChannel.duty/=100; ledChannel.duty&=0x03FF; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 791fd7f..bb30eb7 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -16,9 +16,10 @@ class PwmPin { ledc_channel_config_t ledChannel; public: - PwmPin(uint8_t channel, uint8_t pin); // assigns pin to be output of one of 16 PWM channels (0-15) - void set(uint8_t channel, uint8_t level); // sets the PWM duty of channel to level (0-100) - int getPin(){return pin;} // returns the pin number + PwmPin(uint8_t channel, uint8_t pin); // assigns pin to be output of one of 16 PWM channels (0-15) + void set(uint8_t level); // sets the PWM duty to level (0-100) + void set(uint8_t channel, uint8_t level){set(level);} // sets the PWM duty to level (0-100) - deprecated, but defined for backwards compatibility + int getPin(){return pin;} // returns the pin number static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B diff --git a/src/extras/extras.ino b/src/extras/extras.ino index d07e894..67c5120 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -2,7 +2,10 @@ // This is a placeholder .ino file that allows you to easily edit the contents of this library using the Arduino IDE, // as well as compile and test from this point. This file is ignored when the library is included in other sketches. -#include "RFControl.h" +#include "PwmPin.h" + +PwmPin yellow(0,16); +PwmPin red(1,17); void setup(){ @@ -11,20 +14,20 @@ void setup(){ Serial.print("\n\nTest sketch for HomeSpan Extras Library\n\n"); - RFControl rf(4); - Serial.println("Starting..."); - - rf.clear(); - for(int i=0;i<3;i++) - rf.add(2000,2000); - rf.phase(10000,0); - rf.start(5,100); - - Serial.println("Done!"); - + } void loop(){ - + + for(int i=0;i<100;i++){ + red.set(22,i); + delay(10); + } + + for(int i=100;i>=0;i--){ + yellow.set(i); + delay(10); + } + } From 65df97c563feab10a20bb88b0a97afa324c0a5a6 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 20 Mar 2021 12:20:43 -0500 Subject: [PATCH 02/55] Added ServoPin() class to PwmPin.h Controls a 50-Hz servo motor using ESP32 built-in LED PWM. Allows custom range for microseconds and degrees. --- src/extras/PwmPin.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/extras/PwmPin.h | 20 ++++++++++++++++++++ src/extras/extras.ino | 24 +++++++++++++++++++++--- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 58c7fa6..ecd7967 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -88,3 +88,44 @@ void PwmPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ break; } } + +//////////////////////////// + +ServoPin::ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ + this->channel=channel & 0x07; + this->pin=pin; + this->minMicros=minMicros; + this->maxMicros=maxMicros; + this->minDegrees=minDegrees; + microsPerDegree=(double)(maxMicros-minMicros)/(maxDegrees-minDegrees); + + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_1; + ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; + ledTimer.duty_resolution=LEDC_TIMER_16_BIT; + ledTimer.freq_hz=50; + ledc_timer_config(&ledTimer); + + ledChannel.gpio_num=pin; + ledChannel.speed_mode=LEDC_HIGH_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(this->channel); + ledChannel.intr_type=LEDC_INTR_DISABLE; + ledChannel.timer_sel=LEDC_TIMER_1; + ledChannel.hpoint=0; + ledChannel.duty*=micros2duty; + set(initDegrees); +} + +void ServoPin::set(double degrees){ + ledChannel.duty=(degrees-minDegrees)*microsPerDegree+minMicros; + + if(ledChannel.dutymaxMicros) + ledChannel.duty=maxMicros; + + ledChannel.duty*=micros2duty; + ledc_channel_config(&ledChannel); +} + +const double ServoPin::micros2duty=65535.0/20000.0; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index bb30eb7..34723ae 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -24,3 +24,23 @@ class PwmPin { static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B }; + +class ServoPin { + uint8_t channel; // channel must be in range [0,7] (only HighSpeed Channels will be used) + uint8_t pin; + uint16_t minMicros; + uint16_t maxMicros; + double minDegrees; + double microsPerDegree; + ledc_channel_config_t ledChannel; + + static const double micros2duty; + + public: + ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); + ServoPin(uint8_t channel, uint8_t pin, double initDegrees=0) : ServoPin(channel,pin,initDegrees,1000,2000,-90,90) {}; + + void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] + int getPin(){return pin;} // returns the pin number + +}; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 67c5120..e8d338a 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -5,21 +5,39 @@ #include "PwmPin.h" PwmPin yellow(0,16); -PwmPin red(1,17); +PwmPin red(0,17); + +//ServoPin servo(3,18,-90); +ServoPin servo(3,18,0,500,2200,-90,90); void setup(){ - + Serial.begin(115200); delay(1000); Serial.print("\n\nTest sketch for HomeSpan Extras Library\n\n"); Serial.println("Starting..."); - + } void loop(){ + + double STEP=1; + + for(int i=-100*STEP;i<=100*STEP;i++){ + servo.set((double)i/STEP); + delay(10); + } + + for(int i=100*STEP;i>=-100*STEP;i--){ + servo.set((double)i/STEP); + delay(10); + } + + return; + for(int i=0;i<100;i++){ red.set(22,i); delay(10); From bc498c32e43aebafb6307cbeb0421ecec34fc2c3 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 20 Mar 2021 17:14:19 -0500 Subject: [PATCH 03/55] Created LedPin() This is a replacement for PwmPin(). It keeps track of channel numbers internally, which greatly simplifies the user interface. Starts by using the 8 Low Speed Timer channels and then moves to the 8 High Speed Timer channels if more than 8 LedPins are instantiated. Throws a non-fatal error if more than 16 LedPins are instantiated, and ignores any attempts to set the duty cycle of those channels. --- src/extras/PwmPin.cpp | 55 ++++++++++++++++++++++++++++++++++- src/extras/PwmPin.h | 20 +++++++++++++ src/extras/extras.ino | 67 +++++++++++++++++++++++++++---------------- 3 files changed, 117 insertions(+), 25 deletions(-) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index ecd7967..1ad9556 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -1,6 +1,5 @@ #include "PwmPin.h" -#include PwmPin::PwmPin(uint8_t channel, uint8_t pin){ this->channel=channel & 0x0F; @@ -89,6 +88,59 @@ void PwmPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ } } +/////////////////// + +LedPin::LedPin(uint8_t pin, uint8_t level){ + if(numChannels>15){ + Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels ***\n\n",pin); + return; + } + + enabled=true; + this->pin=pin; + + if(numChannels==0){ // first instantiation of an LedPin + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_0; + ledTimer.duty_resolution=LEDC_TIMER_10_BIT; + ledTimer.freq_hz=5000; + + ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; // configure both the HIGH-Speed Timer 0 and Low-Speed Timer 0 + ledc_timer_config(&ledTimer); + + ledTimer.speed_mode=LEDC_LOW_SPEED_MODE; + ledc_timer_config(&ledTimer); + } + + ledChannel.gpio_num=pin; + if(numChannels<8){ + ledChannel.speed_mode=LEDC_LOW_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(7-numChannels); + } else { + ledChannel.speed_mode=LEDC_HIGH_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(15-numChannels); + } + + numChannels++; + + ledChannel.intr_type=LEDC_INTR_DISABLE; + ledChannel.timer_sel=LEDC_TIMER_0; + ledChannel.hpoint=0; + ledc_channel_config(&ledChannel); + set(level); +} + +void LedPin::set(uint8_t level){ + if(!enabled) + return; + + ledChannel.duty=level*1023; + ledChannel.duty/=100; + ledChannel.duty&=0x03FF; + ledc_channel_config(&ledChannel); + +} + //////////////////////////// ServoPin::ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ @@ -129,3 +181,4 @@ void ServoPin::set(double degrees){ } const double ServoPin::micros2duty=65535.0/20000.0; +uint8_t LedPin::numChannels=0; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 34723ae..4aadbd9 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -8,6 +8,7 @@ // is hardcoded to 5000 Hz and either High-Speed Timer-0 (for channels 0-7) or Low-Speed Timer-0 // for channels (8-15) is configured and selected automatically. +#include #include class PwmPin { @@ -25,6 +26,25 @@ class PwmPin { }; +///////////////////////////////////// + +class LedPin { + uint8_t pin; + boolean enabled=false; + ledc_channel_config_t ledChannel; + static uint8_t numChannels; + + public: + LedPin(uint8_t pin, uint8_t level=0); // assigns pin to be output of one of 16 PWM channels within initial level + void set(uint8_t level); // sets the PWM duty to level (0-100) + int getPin(){return pin;} // returns the pin number + + static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B + +}; + +///////////////////////////////////// + class ServoPin { uint8_t channel; // channel must be in range [0,7] (only HighSpeed Channels will be used) uint8_t pin; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e8d338a..e69cea3 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -4,9 +4,6 @@ #include "PwmPin.h" -PwmPin yellow(0,16); -PwmPin red(0,17); - //ServoPin servo(3,18,-90); ServoPin servo(3,18,0,500,2200,-90,90); @@ -19,33 +16,55 @@ void setup(){ Serial.println("Starting..."); + LedPin yellow(16); + LedPin d1(19); + LedPin d2(19); + LedPin d3(19); + LedPin d4(19); + LedPin d5(19); + LedPin d6(19); + LedPin d7(19); + LedPin d8(19); + LedPin d9(19); + LedPin d10(19); + LedPin d11(19); + LedPin d12(19); + LedPin d13(19); + LedPin d14(19); + LedPin d15(19); + LedPin d16(19); + LedPin red(17); + + while(1){ + for(int i=0;i<100;i++){ + yellow.set(i); + delay(10); + } + + for(int i=100;i>=0;i--){ + red.set(i); + delay(10); + } + } + } void loop(){ - double STEP=1; - - for(int i=-100*STEP;i<=100*STEP;i++){ - servo.set((double)i/STEP); - delay(10); - } - - for(int i=100*STEP;i>=-100*STEP;i--){ - servo.set((double)i/STEP); - delay(10); - } +// double STEP=1; +// +// for(int i=-100*STEP;i<=100*STEP;i++){ +// servo.set((double)i/STEP); +// delay(10); +// } +// +// for(int i=100*STEP;i>=-100*STEP;i--){ +// servo.set((double)i/STEP); +// delay(10); +// } - return; +// return; - for(int i=0;i<100;i++){ - red.set(22,i); - delay(10); - } - - for(int i=100;i>=0;i--){ - yellow.set(i); - delay(10); - } } From 59e34fde05e828e62bbdf9cc1893bf162edfd4cd Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 20 Mar 2021 17:19:47 -0500 Subject: [PATCH 04/55] Updated LedPin() --- src/extras/PwmPin.cpp | 1 - src/extras/PwmPin.h | 3 +-- src/extras/extras.ino | 5 +++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 1ad9556..5b58a9f 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -97,7 +97,6 @@ LedPin::LedPin(uint8_t pin, uint8_t level){ } enabled=true; - this->pin=pin; if(numChannels==0){ // first instantiation of an LedPin ledc_timer_config_t ledTimer; diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 4aadbd9..509f048 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -29,7 +29,6 @@ class PwmPin { ///////////////////////////////////// class LedPin { - uint8_t pin; boolean enabled=false; ledc_channel_config_t ledChannel; static uint8_t numChannels; @@ -37,7 +36,7 @@ class LedPin { public: LedPin(uint8_t pin, uint8_t level=0); // assigns pin to be output of one of 16 PWM channels within initial level void set(uint8_t level); // sets the PWM duty to level (0-100) - int getPin(){return pin;} // returns the pin number + int getPin(){return ledChannel.gpio_num;} // returns the pin number static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B diff --git a/src/extras/extras.ino b/src/extras/extras.ino index e69cea3..8ce5b29 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -6,7 +6,7 @@ //ServoPin servo(3,18,-90); ServoPin servo(3,18,0,500,2200,-90,90); - + void setup(){ Serial.begin(115200); @@ -16,6 +16,7 @@ void setup(){ Serial.println("Starting..."); + LedPin yellow(16); LedPin d1(19); LedPin d2(19); @@ -33,7 +34,7 @@ void setup(){ LedPin d14(19); LedPin d15(19); LedPin d16(19); - LedPin red(17); + LedPin red(17); while(1){ for(int i=0;i<100;i++){ From a8dff0a7de4c20395882bc178b28987c21bdeb06 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 20 Mar 2021 17:35:04 -0500 Subject: [PATCH 05/55] Update PwmPin.cpp --- src/extras/PwmPin.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 5b58a9f..957f3c2 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -129,6 +129,8 @@ LedPin::LedPin(uint8_t pin, uint8_t level){ set(level); } +/////////////////// + void LedPin::set(uint8_t level){ if(!enabled) return; @@ -140,6 +142,63 @@ void LedPin::set(uint8_t level){ } +/////////////////// + +void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ + + // The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html + // h = [0,360] + // s = [0,1] + // v = [0,1] + + int i; + float f, p, q, t; + + if( s == 0 ){ + *r = *g = *b = v; + return; + } + + h /= 60; + i = floor( h ) ; + f = h - i; + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + switch( i % 6 ) { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + case 5: + *r = v; + *g = p; + *b = q; + break; + } +} + //////////////////////////// ServoPin::ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ From d49bca9bbed731fc706398690786d86f5380b4af Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 20 Mar 2021 23:21:09 -0500 Subject: [PATCH 06/55] Completed LedPin() and ServoPin() Both classes check that total number of combined instantiations does not exceed 16. ServoPin() has additional limit of only 8 instantiations. DEPRECATED: PwmPin(). Keep for backwards compatibility only. TO DO: Update Tutorial Examples to replace PwmPin with LedPin, and update Extras.h documentations with LedPin and ServoPin. --- src/extras/PwmPin.cpp | 231 +++++++++++++++++++++++------------------- src/extras/PwmPin.h | 111 +++++++++++--------- src/extras/extras.ino | 48 +++++---- 3 files changed, 211 insertions(+), 179 deletions(-) diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index 957f3c2..e302ebb 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -1,97 +1,10 @@ #include "PwmPin.h" -PwmPin::PwmPin(uint8_t channel, uint8_t pin){ - this->channel=channel & 0x0F; - this->pin=pin; - - ledc_timer_config_t ledTimer; - ledTimer.timer_num=LEDC_TIMER_0; - ledTimer.speed_mode=(this->channel)<8?LEDC_HIGH_SPEED_MODE:LEDC_LOW_SPEED_MODE; - ledTimer.duty_resolution=LEDC_TIMER_10_BIT; - ledTimer.freq_hz=5000; - ledc_timer_config(&ledTimer); - - ledChannel.gpio_num=pin; - ledChannel.speed_mode=(this->channel)<8?LEDC_HIGH_SPEED_MODE:LEDC_LOW_SPEED_MODE; - ledChannel.channel=(ledc_channel_t)(this->channel&0x07); - ledChannel.intr_type=LEDC_INTR_DISABLE; - ledChannel.timer_sel=LEDC_TIMER_0; - ledChannel.duty=0; - ledChannel.hpoint=0; - ledc_channel_config(&ledChannel); - -} - -void PwmPin::set(uint8_t level){ - ledChannel.duty=level*1023; - ledChannel.duty/=100; - ledChannel.duty&=0x03FF; - ledc_channel_config(&ledChannel); - -} - -/////////////////// - -void PwmPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ - - // The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html - // h = [0,360] - // s = [0,1] - // v = [0,1] - - int i; - float f, p, q, t; - - if( s == 0 ){ - *r = *g = *b = v; - return; - } - - h /= 60; - i = floor( h ) ; - f = h - i; - p = v * ( 1 - s ); - q = v * ( 1 - s * f ); - t = v * ( 1 - s * ( 1 - f ) ); - switch( i % 6 ) { - case 0: - *r = v; - *g = t; - *b = p; - break; - case 1: - *r = q; - *g = v; - *b = p; - break; - case 2: - *r = p; - *g = v; - *b = t; - break; - case 3: - *r = p; - *g = q; - *b = v; - break; - case 4: - *r = t; - *g = p; - *b = v; - break; - case 5: - *r = v; - *g = p; - *b = q; - break; - } -} - /////////////////// LedPin::LedPin(uint8_t pin, uint8_t level){ - if(numChannels>15){ + if(numChannels+ServoPin::numChannels>15){ Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels ***\n\n",pin); return; } @@ -127,6 +40,7 @@ LedPin::LedPin(uint8_t pin, uint8_t level){ ledChannel.hpoint=0; ledc_channel_config(&ledChannel); set(level); + //Serial.printf("Configured LED on Pin %d using Channel %d in Speed Mode %d\n",ledChannel.gpio_num,ledChannel.channel,ledChannel.speed_mode); } /////////////////// @@ -201,9 +115,14 @@ void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ //////////////////////////// -ServoPin::ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ - this->channel=channel & 0x07; - this->pin=pin; +ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ + if(numChannels>7 || numChannels>(15-LedPin::numChannels)){ + Serial.printf("\n*** ERROR: Can't create ServoPin(%d) - no open PWM channels ***\n\n",pin); + return; + } + + enabled=true; + this->minMicros=minMicros; this->maxMicros=maxMicros; this->minDegrees=minDegrees; @@ -216,27 +135,129 @@ ServoPin::ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t mi ledTimer.freq_hz=50; ledc_timer_config(&ledTimer); - ledChannel.gpio_num=pin; - ledChannel.speed_mode=LEDC_HIGH_SPEED_MODE; - ledChannel.channel=(ledc_channel_t)(this->channel); - ledChannel.intr_type=LEDC_INTR_DISABLE; - ledChannel.timer_sel=LEDC_TIMER_1; - ledChannel.hpoint=0; - ledChannel.duty*=micros2duty; + servoChannel.gpio_num=pin; + servoChannel.speed_mode=LEDC_HIGH_SPEED_MODE; + servoChannel.channel=(ledc_channel_t)numChannels++; + servoChannel.intr_type=LEDC_INTR_DISABLE; + servoChannel.timer_sel=LEDC_TIMER_1; + servoChannel.hpoint=0; + servoChannel.duty*=micros2duty; set(initDegrees); + //Serial.printf("Configured Servo on Pin %d using Channel %d in Speed Mode %d\n",servoChannel.gpio_num,servoChannel.channel,servoChannel.speed_mode); } +/////////////////// + void ServoPin::set(double degrees){ - ledChannel.duty=(degrees-minDegrees)*microsPerDegree+minMicros; + if(!enabled) + return; - if(ledChannel.dutymaxMicros) - ledChannel.duty=maxMicros; + servoChannel.duty=(degrees-minDegrees)*microsPerDegree+minMicros; + + if(servoChannel.dutymaxMicros) + servoChannel.duty=maxMicros; - ledChannel.duty*=micros2duty; - ledc_channel_config(&ledChannel); + servoChannel.duty*=micros2duty; + ledc_channel_config(&servoChannel); } +//////////////////////////// + const double ServoPin::micros2duty=65535.0/20000.0; uint8_t LedPin::numChannels=0; +uint8_t ServoPin::numChannels=0; + +//******************************************************* +// DEPRECATED - INCLUDED FOR BACKWARDS COMPATIBILITY ONLY +//******************************************************* + +PwmPin::PwmPin(uint8_t channel, uint8_t pin){ + this->channel=channel & 0x0F; + this->pin=pin; + + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_0; + ledTimer.speed_mode=(this->channel)<8?LEDC_HIGH_SPEED_MODE:LEDC_LOW_SPEED_MODE; + ledTimer.duty_resolution=LEDC_TIMER_10_BIT; + ledTimer.freq_hz=5000; + ledc_timer_config(&ledTimer); + + ledChannel.gpio_num=pin; + ledChannel.speed_mode=(this->channel)<8?LEDC_HIGH_SPEED_MODE:LEDC_LOW_SPEED_MODE; + ledChannel.channel=(ledc_channel_t)(this->channel&0x07); + ledChannel.intr_type=LEDC_INTR_DISABLE; + ledChannel.timer_sel=LEDC_TIMER_0; + ledChannel.duty=0; + ledChannel.hpoint=0; + ledc_channel_config(&ledChannel); + +} + +/////////////////// + +void PwmPin::set(uint8_t channel, uint8_t level){ + ledChannel.duty=level*1023; + ledChannel.duty/=100; + ledChannel.duty&=0x03FF; + ledc_channel_config(&ledChannel); + +} + +/////////////////// + +void PwmPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ + + // The algorithm below was provided on the web at https://www.cs.rit.edu/~ncs/color/t_convert.html + // h = [0,360] + // s = [0,1] + // v = [0,1] + + int i; + float f, p, q, t; + + if( s == 0 ){ + *r = *g = *b = v; + return; + } + + h /= 60; + i = floor( h ) ; + f = h - i; + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + switch( i % 6 ) { + case 0: + *r = v; + *g = t; + *b = p; + break; + case 1: + *r = q; + *g = v; + *b = p; + break; + case 2: + *r = p; + *g = v; + *b = t; + break; + case 3: + *r = p; + *g = q; + *b = v; + break; + case 4: + *r = t; + *g = p; + *b = v; + break; + case 5: + *r = v; + *g = p; + *b = q; + break; + } +} diff --git a/src/extras/PwmPin.h b/src/extras/PwmPin.h index 509f048..76b79fb 100644 --- a/src/extras/PwmPin.h +++ b/src/extras/PwmPin.h @@ -1,16 +1,69 @@ -///////////////////////////////////// -// PWM Pin Control // -///////////////////////////////////// - -// A wrapper around the ESP-IDF ledc library to easily set the brightness of an LED from 0-100%. -// Can be used for any device requiring a PWM output (not just an LED). Frequency of PWM -// is hardcoded to 5000 Hz and either High-Speed Timer-0 (for channels 0-7) or Low-Speed Timer-0 -// for channels (8-15) is configured and selected automatically. +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ----- PWM Pin Control ----- +///////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Wrappers around the ESP-IDF ledc library to control PWM-based devices: +// +// LedPin(pin) - controls a Dimmable LED on specified pin with frequency=5000 Hz +// - use set(level) to set brightness from 0-100% +// +// ServoPin(pin) - controls a Servo Motor on specified pin with frequency=50 Hz +// - use set(degrees) to set position to degrees +// +// Max number of LedPin instantiations: 16 +// Max number of ServoPin instantiatons: 8 +// Max combined limit (LedPins+ServoPins): 16 +// +// Instantiation of an LedPin or ServoPin that causes any of the maximums above to be exceeded throws +// an error message. The object will still be created, but calls to set(level) or set(degrees) are ignored. +// +///////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #include +///////////////////////////////////// + +class LedPin { + boolean enabled=false; + ledc_channel_config_t ledChannel; + + public: + LedPin(uint8_t pin, uint8_t level=0); // assigns pin to be output of one of 16 PWM channels within initial level + void set(uint8_t level); // sets the PWM duty to level (0-100) + int getPin(){return ledChannel.gpio_num;} // returns the pin number + + static uint8_t numChannels; + static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B +}; + +///////////////////////////////////// + +class ServoPin { + boolean enabled=false; + uint16_t minMicros; + uint16_t maxMicros; + double minDegrees; + double microsPerDegree; + ledc_channel_config_t servoChannel; + + static const double micros2duty; + + public: + ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); + ServoPin(uint8_t pin, double initDegrees=0) : ServoPin(pin,initDegrees,1000,2000,-90,90) {}; + + void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] + int getPin(){return servoChannel.gpio_num;} // returns the pin number + + static uint8_t numChannels; +}; + +//******************************************************* +// DEPRECATED - INCLUDED FOR BACKWARDS COMPATIBILITY ONLY +//******************************************************* + class PwmPin { uint8_t channel; uint8_t pin; @@ -18,48 +71,8 @@ class PwmPin { public: PwmPin(uint8_t channel, uint8_t pin); // assigns pin to be output of one of 16 PWM channels (0-15) - void set(uint8_t level); // sets the PWM duty to level (0-100) - void set(uint8_t channel, uint8_t level){set(level);} // sets the PWM duty to level (0-100) - deprecated, but defined for backwards compatibility + void set(uint8_t channel, uint8_t level); // sets the PWM duty to level (0-100) int getPin(){return pin;} // returns the pin number static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B - -}; - -///////////////////////////////////// - -class LedPin { - boolean enabled=false; - ledc_channel_config_t ledChannel; - static uint8_t numChannels; - - public: - LedPin(uint8_t pin, uint8_t level=0); // assigns pin to be output of one of 16 PWM channels within initial level - void set(uint8_t level); // sets the PWM duty to level (0-100) - int getPin(){return ledChannel.gpio_num;} // returns the pin number - - static void HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ); // converts Hue/Saturation/Brightness to R/G/B - -}; - -///////////////////////////////////// - -class ServoPin { - uint8_t channel; // channel must be in range [0,7] (only HighSpeed Channels will be used) - uint8_t pin; - uint16_t minMicros; - uint16_t maxMicros; - double minDegrees; - double microsPerDegree; - ledc_channel_config_t ledChannel; - - static const double micros2duty; - - public: - ServoPin(uint8_t channel, uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees); - ServoPin(uint8_t channel, uint8_t pin, double initDegrees=0) : ServoPin(channel,pin,initDegrees,1000,2000,-90,90) {}; - - void set(double degrees); // sets the Servo to degrees, where degrees is bounded by [minDegrees,maxDegrees] - int getPin(){return pin;} // returns the pin number - }; diff --git a/src/extras/extras.ino b/src/extras/extras.ino index 8ce5b29..d68f7a0 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -5,7 +5,7 @@ #include "PwmPin.h" //ServoPin servo(3,18,-90); -ServoPin servo(3,18,0,500,2200,-90,90); +//ServoPin servo(18,0,500,2200,-90,90); void setup(){ @@ -17,7 +17,7 @@ void setup(){ Serial.println("Starting..."); - LedPin yellow(16); + LedPin yellow(16,10); LedPin d1(19); LedPin d2(19); LedPin d3(19); @@ -30,13 +30,14 @@ void setup(){ LedPin d10(19); LedPin d11(19); LedPin d12(19); - LedPin d13(19); - LedPin d14(19); - LedPin d15(19); - LedPin d16(19); - LedPin red(17); + LedPin red(17,100); - while(1){ +// ServoPin servo(18,0,500,2200,-90,90); + ServoPin s0(19); + ServoPin servo(18,45); + ServoPin s1(19); + + while(0){ for(int i=0;i<100;i++){ yellow.set(i); delay(10); @@ -48,24 +49,21 @@ void setup(){ } } + while(1){ + double STEP=1; + + for(int i=-100*STEP;i<=100*STEP;i++){ + servo.set((double)i/STEP); + delay(10); + } + + for(int i=100*STEP;i>=-100*STEP;i--){ + servo.set((double)i/STEP); + delay(10); + } + } + } void loop(){ - - -// double STEP=1; -// -// for(int i=-100*STEP;i<=100*STEP;i++){ -// servo.set((double)i/STEP); -// delay(10); -// } -// -// for(int i=100*STEP;i>=-100*STEP;i--){ -// servo.set((double)i/STEP); -// delay(10); -// } - -// return; - - } From 451a2885b1a5eee7fe3e408191c5634de4bfcbee Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 21 Mar 2021 09:54:57 -0500 Subject: [PATCH 07/55] Changed PwmPin to LedPin in all Tutorial Examples Greatly simplifies use interface. No need to specify or save channels. And no need to even save pin number since that can be found using LedPin->getPin() method whenever needed. --- examples/06-DimmableLED/06-DimmableLED.ino | 4 +- examples/06-DimmableLED/DEV_LED.h | 25 +++++---- .../07-IdentifyRoutines.ino | 2 +- examples/07-IdentifyRoutines/DEV_LED.h | 10 ++-- examples/08-Bridges/08-Bridges.ino | 2 +- examples/08-Bridges/DEV_LED.h | 10 ++-- .../09-MessageLogging/09-MessageLogging.ino | 4 +- examples/09-MessageLogging/DEV_LED.h | 26 +++------ examples/10-RGB_LED/10-RGB_LED.ino | 6 +-- examples/10-RGB_LED/DEV_LED.h | 53 ++++++++----------- .../11-ServiceOptions/11-ServiceOptions.ino | 12 ++--- examples/11-ServiceOptions/DEV_LED.h | 20 +++---- .../15-RealPushButtons/15-RealPushButtons.ino | 2 +- examples/15-RealPushButtons/DEV_LED.h | 25 ++++----- src/extras/PwmPin.cpp | 20 +++---- src/extras/extras.ino | 9 ++-- 16 files changed, 97 insertions(+), 133 deletions(-) diff --git a/examples/06-DimmableLED/06-DimmableLED.ino b/examples/06-DimmableLED/06-DimmableLED.ino index f5cc74d..b28fc82 100644 --- a/examples/06-DimmableLED/06-DimmableLED.ino +++ b/examples/06-DimmableLED/06-DimmableLED.ino @@ -54,7 +54,7 @@ void setup() { // and the ESP32 chip has built-in PWM functionality specifically for this purpose. There are numerous libraries // you can download that mimics or reproduces analogWrite() in some form or another. HomeSpan conveniently comes with // it own version of a wrapper around the ESP32 PWM classes that make it very easy to define PWM "channel," attach a pin, - // and set the PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the PwmPin class, as defined in + // and set the PWM level (or duty cycle) from 0-100%. These functions are encapsualted in the LedPin class, as defined in // extras/PwmPin.h. We will include this file in our updated DEV_LED.h for use with DEV_DimmableLED. Serial.begin(115200); @@ -89,7 +89,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new DEV_DimmableLED(0,17); // NEW! create a dimmable LED attached to pin 17 using PWM channel 0. See new code at end of DEV_LED.h + new DEV_DimmableLED(17); // NEW! create a dimmable (PWM-driven) LED attached to pin 17. See new code at end of DEV_LED.h } // end of setup() diff --git a/examples/06-DimmableLED/DEV_LED.h b/examples/06-DimmableLED/DEV_LED.h index ba6f9e2..d9445e9 100644 --- a/examples/06-DimmableLED/DEV_LED.h +++ b/examples/06-DimmableLED/DEV_LED.h @@ -33,34 +33,33 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // NEW! Create reference to PWM Pin instantiated below - int channel; // NEW! Store the PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // NEW! Create reference to LED Pin instantiated below SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // NEW! Create a reference to the Brightness Characteristic instantiated below - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // NEW! Instantiate the Brightness Characteristic with an initial value of 50% (same as we did in Example 4) level->setRange(5,100,1); // NEW! This sets the range of the Brightness to be from a min of 5%, to a max of 100%, in steps of 1% (different from Example 4 values) - this->channel=channel; // NEW! Save the channel number (from 0-15) - this->pwmPin=new PwmPin(channel, ledPin); // NEW! Configures the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // NEW! Configures a PWM LED for output to the specified pin. Note pinMode() does NOT need to be called in advance } // end constructor boolean update(){ // update() method - // Here we set the duty cycle (brightness) of the LED by callng pwmPin with the appropriate channel. - // The second argument should be a number from 0-100 (representing %brightness). HomeKit sets the on/off - // status of the LED ("power") separately from the brightness of the LED ("level"). This means HomeKit can - // request the LED be turned off, but retain the brightness level so that it does not need to be resent if - // the LED is turned back on. Multiplying the newValue of the power Characteristic (as a boolean) with the - // newValue of the Brightness Characteristic (as an int) is a short-hand way of creating the logic to - // set the PWM level when the LED is off (always zero) or on (whatever the brightness level is). + // Here we set the brightness of the LED by calling ledPin->set(brightness), where brightness=0-100. + // Note HomeKit sets the on/off status of a LightBulb separately from its brightness, which means HomeKit + // can request a LightBulb be turned off, but still retains the brightness level so that it does not need + // to be resent once the LightBulb is turned back on. - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + // Multiplying the newValue of the On Characteristic ("power", which is a boolean) with the newValue of the + // Brightness Characteristic ("level", which is an integer) is a short-hand way of creating the logic to + // set the LED level to zero when the LightBulb is off, or to the current brightness level when it is on. + + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino b/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino index ee71c3b..62d9f5c 100644 --- a/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino +++ b/examples/07-IdentifyRoutines/07-IdentifyRoutines.ino @@ -102,7 +102,7 @@ void setup() { new Service::HAPProtocolInformation(); new Characteristic::Version("1.1.0"); - new DEV_DimmableLED(0,17); // NEW! create a dimmable LED attached to pin 17 using PWM channel 0. See new code at end of DEV_LED.h + new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17 } // end of setup() diff --git a/examples/07-IdentifyRoutines/DEV_LED.h b/examples/07-IdentifyRoutines/DEV_LED.h index 0598e22..36c05de 100644 --- a/examples/07-IdentifyRoutines/DEV_LED.h +++ b/examples/07-IdentifyRoutines/DEV_LED.h @@ -31,26 +31,24 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->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% - this->channel=channel; // save the channel number (from 0-15) - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin } // end constructor boolean update(){ // update() method - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/08-Bridges/08-Bridges.ino b/examples/08-Bridges/08-Bridges.ino index 2f40e6b..b01a4dd 100644 --- a/examples/08-Bridges/08-Bridges.ino +++ b/examples/08-Bridges/08-Bridges.ino @@ -98,7 +98,7 @@ void setup() { // new Service::HAPProtocolInformation(); - DELETED - NO LONGER NEEDED // new Characteristic::Version("1.1.0"); - DELETED - NO LONGER NEEDED - new DEV_DimmableLED(0,17); // create a dimmable LED attached to pin 17 using PWM channel 0 + new DEV_DimmableLED(17); // create a dimmable (PWM-driven) LED attached to pin 17 } // end of setup() diff --git a/examples/08-Bridges/DEV_LED.h b/examples/08-Bridges/DEV_LED.h index 0598e22..36c05de 100644 --- a/examples/08-Bridges/DEV_LED.h +++ b/examples/08-Bridges/DEV_LED.h @@ -31,26 +31,24 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->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% - this->channel=channel; // save the channel number (from 0-15) - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin } // end constructor boolean update(){ // update() method - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/09-MessageLogging/09-MessageLogging.ino b/examples/09-MessageLogging/09-MessageLogging.ino index 857a837..26f5081 100644 --- a/examples/09-MessageLogging/09-MessageLogging.ino +++ b/examples/09-MessageLogging/09-MessageLogging.ino @@ -85,11 +85,11 @@ void setup() { new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0); new DEV_LED(16); - // Defines a Dimmable LED Accessory attached to pin 17 using PWM channel 0 + // Defines a Dimmable (PWM-driven) LED Accessory attached to pin 17 new SpanAccessory(); new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); } // end of setup() diff --git a/examples/09-MessageLogging/DEV_LED.h b/examples/09-MessageLogging/DEV_LED.h index dc8e0ce..50ad054 100644 --- a/examples/09-MessageLogging/DEV_LED.h +++ b/examples/09-MessageLogging/DEV_LED.h @@ -53,31 +53,26 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED <- NEW!! - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->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% - this->channel=channel; // save the channel number (from 0-15) - this->ledPin=ledPin; // LED pin number <- NEW!! - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin. pinMode() does NOT need to be called. + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin // Here we output log messages when the constructor is initially called. // We use Serial.print() since to ensure the message is always output - // regardless of the VERBOSITY setting. + // regardless of the VERBOSITY setting. Note that ledPin has a method getPin() + // that retrieves the pin number so you don't need to store it separately. Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -89,13 +84,8 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // is not functioning as expected. Since it's just for debugging, // we use LOG1() instead of Serial.print(). - // Note that in the prior example we did not save the ledPin number for - // DimmableLED since it was only needed by the constructor for initializing - // PwmPin(). For this example we add ledPin as a saved variable (see the two - // lines marketed NEW!! above) for the sole purpose of this log message. - LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); @@ -121,7 +111,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/10-RGB_LED/10-RGB_LED.ino b/examples/10-RGB_LED/10-RGB_LED.ino index 739ca4b..988dae0 100644 --- a/examples/10-RGB_LED/10-RGB_LED.ino +++ b/examples/10-RGB_LED/10-RGB_LED.ino @@ -59,15 +59,15 @@ void setup() { new SpanAccessory(); new DEV_Identify("On/Off LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_LED(16); // Create an On/Off LED attached to pin 16 + new DEV_LED(16); // Create an On/Off LED attached to pin 16 new SpanAccessory(); new DEV_Identify("Dimmable LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); // Create a Dimmable LED using PWM channel 0, attached to pin 17 + new DEV_DimmableLED(17); // Create a Dimmable (PWM-driven) LED using attached to pin 17 new SpanAccessory(); new DEV_Identify("RGB LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_RgbLED(1,2,3,32,22,23); // Create an RGB LED using PWM channels 1,2,3, attached to pins 32,22,23 (for R, G, and B LED anodes) + new DEV_RgbLED(32,22,23); // Create an RGB LED attached to pins 32,22,23 (for R, G, and B LED anodes) } // end of setup() diff --git a/examples/10-RGB_LED/DEV_LED.h b/examples/10-RGB_LED/DEV_LED.h index d571420..833b8ad 100644 --- a/examples/10-RGB_LED/DEV_LED.h +++ b/examples/10-RGB_LED/DEV_LED.h @@ -45,27 +45,21 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ // constructor() method + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->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% - this->channel=channel; // save the channel number (from 0-15) - this->ledPin=ledPin; // save LED pin number - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -73,12 +67,12 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); LOG1(level->getVal()); - + if(power->updated()){ LOG1(" New Power="); LOG1(power->getNewVal()?"true":"false"); @@ -91,7 +85,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true @@ -102,32 +96,27 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) - PwmPin *redPin, *greenPin, *bluePin; - int redChannel, greenChannel, blueChannel; + LedPin *redPin, *greenPin, *bluePin; SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *H; // reference to the Hue Characteristic SpanCharacteristic *S; // reference to the Saturation Characteristic SpanCharacteristic *V; // reference to the Brightness Characteristic - DEV_RgbLED(int redChannel, int greenChannel, int blueChannel, int redPin, int greenPin, int bluePin) : Service::LightBulb(){ // constructor() method + DEV_RgbLED(int red_pin, int green_pin, int blue_pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); H=new Characteristic::Hue(0); // instantiate the Hue Characteristic with an initial value of 0 out of 360 S=new Characteristic::Saturation(0); // instantiate the Saturation Characteristic with an initial value of 0% V=new Characteristic::Brightness(100); // instantiate the Brightness Characteristic with an initial value of 100% 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% - - this->redChannel=redChannel; // save the channel number (from 0-15) - this->greenChannel=greenChannel; - this->blueChannel=blueChannel; - this->redPin=new PwmPin(redChannel, redPin); // instantiate the PWM channel and attach the specified pin - this->greenPin=new PwmPin(greenChannel, greenPin); - this->bluePin=new PwmPin(blueChannel, bluePin); - + this->redPin=new LedPin(red_pin); // configures a PWM LED for output to the RED pin + this->greenPin=new LedPin(green_pin); // configures a PWM LED for output to the GREEN pin + this->bluePin=new LedPin(blue_pin); // configures a PWM LED for output to the BLUE pin + char cBuf[128]; - sprintf(cBuf,"Configuring RGB LED: Pins=(%d,%d,%d) Channels=(%d,%d,%d)\n",redPin,greenPin,bluePin,redChannel,greenChannel,blueChannel); + sprintf(cBuf,"Configuring RGB LED: Pins=(%d,%d,%d)\n",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); Serial.print(cBuf); } // end constructor @@ -143,7 +132,7 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) p=power->getVal(); char cBuf[128]; - sprintf(cBuf,"Updating RGB LED on pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); + sprintf(cBuf,"Updating RGB LED: Pins=(%d,%d,%d): ",redPin->getPin(),greenPin->getPin(),bluePin->getPin()); LOG1(cBuf); if(power->updated()){ @@ -178,24 +167,24 @@ struct DEV_RgbLED : Service::LightBulb { // RGB LED (Command Cathode) } LOG1(cBuf); - // Here we call a static function of PwmPin that converts HSV to RGB. + // Here we call a static function of LedPin that converts HSV to RGB. // Parameters must all be floats in range of H[0,360], S[0,1], and V[0,1] // R, G, B, returned [0,1] range as well - PwmPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); // since HomeKit provides S and V in percent, scale down by 100 + LedPin::HSVtoRGB(h,s/100.0,v/100.0,&r,&g,&b); // since HomeKit provides S and V in percent, scale down by 100 int R, G, B; - R=p*r*100; // since PwmPin uses percent, scale back up by 100, and multiple by status fo power (either 0 or 1) + R=p*r*100; // since LedPin uses percent, scale back up by 100, and multiple by status fo power (either 0 or 1) G=p*g*100; B=p*b*100; sprintf(cBuf,"RGB=(%d,%d,%d)\n",R,G,B); LOG1(cBuf); - redPin->set(redChannel,R); // update the PWM channels with new values - greenPin->set(greenChannel,G); - bluePin->set(blueChannel,B); + redPin->set(R); // update the ledPin channels with new values + greenPin->set(G); + bluePin->set(B); return(true); // return true diff --git a/examples/11-ServiceOptions/11-ServiceOptions.ino b/examples/11-ServiceOptions/11-ServiceOptions.ino index 72b49e9..29764af 100644 --- a/examples/11-ServiceOptions/11-ServiceOptions.ino +++ b/examples/11-ServiceOptions/11-ServiceOptions.ino @@ -83,7 +83,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #1","HomeSpan","123-ABC","20mA LED","0.9",0); - (new DEV_DimmableLED(0,17))->setPrimary(); // Here we specify DEV_DimmableLED as the Primary Service by "chaining" setPrimary() to the pointer return by new. Note parentheses! + (new DEV_DimmableLED(17))->setPrimary(); // Here we specify DEV_DimmableLED as the Primary Service by "chaining" setPrimary() to the pointer return by new. Note parentheses! new Service::Fan(); new Characteristic::Active(); new Characteristic::RotationDirection(); @@ -91,8 +91,8 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #2","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); - (new Service::Fan())->setPrimary(); // Here we specify the Fan as the Primary Service. Again, note how we encapsulated the "new" command in parentheses, then chained setPrimary() + new DEV_DimmableLED(17); + (new Service::Fan())->setPrimary(); // Here we specify the Fan as the Primary Service. Again, note how we encapsulated the "new" command in parentheses, then chained setPrimary() new Characteristic::Active(); new Characteristic::RotationDirection(); new Characteristic::RotationSpeed(0); @@ -121,7 +121,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("Ceiling Fan #3","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); // Here we create a name for the Dimmable LED new DEV_LED(16); new Characteristic::Name("Night Light"); // Here we create a name for the On/Off LED @@ -149,7 +149,7 @@ void setup() { new SpanAccessory(); (new DEV_Identify("Ceiling Fan #4","HomeSpan","123-ABC","20mA LED","0.9",0))->setPrimary(); // specify DEV_Identify as the Primary Service - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); new DEV_LED(16); new Characteristic::Name("Night Light"); @@ -172,7 +172,7 @@ void setup() { new Characteristic::RotationDirection(); new Characteristic::RotationSpeed(0); new Characteristic::Name("Fan"); - new DEV_DimmableLED(0,17); + new DEV_DimmableLED(17); new Characteristic::Name("Main Light"); new DEV_LED(16); new Characteristic::Name("Night Light"); diff --git a/examples/11-ServiceOptions/DEV_LED.h b/examples/11-ServiceOptions/DEV_LED.h index 1a9924c..66fe7ac 100644 --- a/examples/11-ServiceOptions/DEV_LED.h +++ b/examples/11-ServiceOptions/DEV_LED.h @@ -45,27 +45,21 @@ struct DEV_LED : Service::LightBulb { // ON/OFF LED struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED - int channel; // PWM channel used for this LED (should be unique for each LED) + LedPin *ledPin; // reference to Led Pin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic - DEV_DimmableLED(int channel, int ledPin) : Service::LightBulb(){ + DEV_DimmableLED(int pin) : Service::LightBulb(){ // constructor() method power=new Characteristic::On(); level=new Characteristic::Brightness(50); // Brightness Characteristic with an initial value of 50% level->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% - this->channel=channel; // save the channel number (from 0-15) - this->ledPin=ledPin; // save LED pin number - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); - Serial.print(" Channel="); - Serial.print(channel); + Serial.print(ledPin->getPin()); Serial.print("\n"); } // end constructor @@ -73,12 +67,12 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); LOG1(level->getVal()); - + if(power->updated()){ LOG1(" New Power="); LOG1(power->getNewVal()?"true":"false"); @@ -91,7 +85,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true diff --git a/examples/15-RealPushButtons/15-RealPushButtons.ino b/examples/15-RealPushButtons/15-RealPushButtons.ino index 0290bfb..1fd56af 100644 --- a/examples/15-RealPushButtons/15-RealPushButtons.ino +++ b/examples/15-RealPushButtons/15-RealPushButtons.ino @@ -139,7 +139,7 @@ void setup() { new SpanAccessory(); new DEV_Identify("PushButton LED","HomeSpan","123-ABC","20mA LED","0.9",0); - new DEV_DimmableLED(0,17,23,5,18); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h + new DEV_DimmableLED(17,23,5,18); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h } // end of setup() diff --git a/examples/15-RealPushButtons/DEV_LED.h b/examples/15-RealPushButtons/DEV_LED.h index 2b22f05..ee2ca6f 100644 --- a/examples/15-RealPushButtons/DEV_LED.h +++ b/examples/15-RealPushButtons/DEV_LED.h @@ -16,8 +16,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // raise button: SHORT press increases brightness by 1%; LONG press increases brightness by 10%; DOUBLE press increases brightness to maximum // lower button: SHORT press decreases brightness by 1%; LONG press decreases brightness by 10%; DOUBLE press decreases brightness to minimum - PwmPin *pwmPin; // reference to PWM Pin - int ledPin; // pin number defined for this LED + LedPin *ledPin; // reference to Led Pin int powerPin; // NEW! pin with pushbutton to turn on/off LED int raisePin; // NEW! pin with pushbutton to increase brightness int lowerPin; // NEW! pin with pushButton to decrease brightness @@ -26,9 +25,9 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED SpanCharacteristic *level; // reference to the Brightness Characteristic int favoriteLevel=50; // NEW! keep track of a 'favorite' level - // NEW! Consructor includes 3 additionl arguments to specify pin numbers for power, raise, and lower buttons + // NEW! Consructor includes 3 additional arguments to specify pin numbers for power, raise, and lower buttons - DEV_DimmableLED(int channel, int ledPin, int powerPin, int raisePin, int lowerPin) : Service::LightBulb(){ + DEV_DimmableLED(int pin, int powerPin, int raisePin, int lowerPin) : Service::LightBulb(){ power=new Characteristic::On(); @@ -46,15 +45,13 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED new SpanButton(raisePin,500); // NEW! create new SpanButton to increase brightness using pushbutton on pin number "raisePin" new SpanButton(lowerPin,500); // NEW! create new SpanButton to decrease brightness using pushbutton on pin number "lowerPin" - this->channel=channel; // save the channel number (from 0-15) - this->ledPin=ledPin; // save LED pin number this->powerPin=powerPin; // NEW! save power pushbutton pin number this->raisePin=raisePin; // NEW! save increase brightness pushbutton pin number this->lowerPin=lowerPin; // NEW! save decrease brightness pushbutton pin number - this->pwmPin=new PwmPin(channel, ledPin); // configure the PWM channel and attach the specified ledPin + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin); + Serial.print(ledPin->getPin()); Serial.print(" Channel="); Serial.print(channel); Serial.print("\n"); @@ -64,7 +61,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin); + LOG1(ledPin->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); @@ -82,7 +79,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - pwmPin->set(channel,power->getNewVal()*level->getNewVal()); + ledPin->set(power->getNewVal()*level->getNewVal()); return(true); // return true @@ -115,9 +112,9 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("Saved new brightness level="); // ...and output log message LOG1(favoriteLevel); LOG1("\n"); - pwmPin->set(channel,(1-power->getVal())*level->getVal()); // blink the LED to indicate new level has been saved + ledPin->set((1-power->getVal())*level->getVal()); // blink the LED to indicate new level has been saved delay(100); - pwmPin->set(channel,(1-power->getVal())*level->getVal()); + ledPin->set((1-power->getVal())*level->getVal()); } } else @@ -153,7 +150,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // Don't forget to set the new power and level for the actual LED - the above code by itself only changes the values of the Characteristics // within HomeKit! We still need to take an action on the actual LED itself. - // Note the line below is similar to, but not the same as, the pwmPin->set function used in the update() method above. Within the + // Note the line below is similar to, but not the same as, the ledPin->set function used in the update() method above. Within the // update() method we used getNewVal() because we wanted to change the LED to match the NEW VALUES requested by the user via the // HomeKit Controller. We did not need to (and must not) use setVal() to modify these values in the update() method since HomeSpan // automatically does this for us, provided we return StatusCode::OK at the end of the update() method. @@ -164,7 +161,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED // as shown below. As usual, HomeSpan will send Event Notifications to all registered HomeKit Controllers letting them know about any changes // we made using setVal(). - pwmPin->set(channel,power->getVal()*level->getVal()); // update the physical LED to reflect the new values + ledPin->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values } diff --git a/src/extras/PwmPin.cpp b/src/extras/PwmPin.cpp index e302ebb..1f85bbe 100644 --- a/src/extras/PwmPin.cpp +++ b/src/extras/PwmPin.cpp @@ -6,12 +6,13 @@ LedPin::LedPin(uint8_t pin, uint8_t level){ if(numChannels+ServoPin::numChannels>15){ Serial.printf("\n*** ERROR: Can't create LedPin(%d) - no open PWM channels ***\n\n",pin); + ledChannel.gpio_num=0; return; } enabled=true; - if(numChannels==0){ // first instantiation of an LedPin + if(numChannels==0){ // first instantiation of an LedPin ledc_timer_config_t ledTimer; ledTimer.timer_num=LEDC_TIMER_0; ledTimer.duty_resolution=LEDC_TIMER_10_BIT; @@ -40,7 +41,6 @@ LedPin::LedPin(uint8_t pin, uint8_t level){ ledChannel.hpoint=0; ledc_channel_config(&ledChannel); set(level); - //Serial.printf("Configured LED on Pin %d using Channel %d in Speed Mode %d\n",ledChannel.gpio_num,ledChannel.channel,ledChannel.speed_mode); } /////////////////// @@ -118,6 +118,7 @@ void LedPin::HSVtoRGB(float h, float s, float v, float *r, float *g, float *b ){ ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees){ if(numChannels>7 || numChannels>(15-LedPin::numChannels)){ Serial.printf("\n*** ERROR: Can't create ServoPin(%d) - no open PWM channels ***\n\n",pin); + servoChannel.gpio_num=0; return; } @@ -128,12 +129,14 @@ ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t this->minDegrees=minDegrees; microsPerDegree=(double)(maxMicros-minMicros)/(maxDegrees-minDegrees); - ledc_timer_config_t ledTimer; - ledTimer.timer_num=LEDC_TIMER_1; - ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; - ledTimer.duty_resolution=LEDC_TIMER_16_BIT; - ledTimer.freq_hz=50; - ledc_timer_config(&ledTimer); + if(numChannels==0){ // first instantiation of a ServoPin + ledc_timer_config_t ledTimer; + ledTimer.timer_num=LEDC_TIMER_1; + ledTimer.speed_mode=LEDC_HIGH_SPEED_MODE; + ledTimer.duty_resolution=LEDC_TIMER_16_BIT; + ledTimer.freq_hz=50; + ledc_timer_config(&ledTimer); + } servoChannel.gpio_num=pin; servoChannel.speed_mode=LEDC_HIGH_SPEED_MODE; @@ -143,7 +146,6 @@ ServoPin::ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t servoChannel.hpoint=0; servoChannel.duty*=micros2duty; set(initDegrees); - //Serial.printf("Configured Servo on Pin %d using Channel %d in Speed Mode %d\n",servoChannel.gpio_num,servoChannel.channel,servoChannel.speed_mode); } /////////////////// diff --git a/src/extras/extras.ino b/src/extras/extras.ino index d68f7a0..1bdb34a 100644 --- a/src/extras/extras.ino +++ b/src/extras/extras.ino @@ -3,10 +3,7 @@ // as well as compile and test from this point. This file is ignored when the library is included in other sketches. #include "PwmPin.h" - -//ServoPin servo(3,18,-90); -//ServoPin servo(18,0,500,2200,-90,90); - + void setup(){ Serial.begin(115200); @@ -30,14 +27,14 @@ void setup(){ LedPin d10(19); LedPin d11(19); LedPin d12(19); - LedPin red(17,100); + LedPin red(17); // ServoPin servo(18,0,500,2200,-90,90); ServoPin s0(19); ServoPin servo(18,45); ServoPin s1(19); - while(0){ + while(1){ for(int i=0;i<100;i++){ yellow.set(i); delay(10); From 54f7928204e8e6b0d700b1f2948a9637025b552e Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 21 Mar 2021 12:37:12 -0500 Subject: [PATCH 08/55] Fixed bug in Example 13 WindowShade was incorrectly sending repeated event notifications to HomeKit! --- examples/13-TargetStates/DEV_DoorsWindows.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/13-TargetStates/DEV_DoorsWindows.h b/examples/13-TargetStates/DEV_DoorsWindows.h index c4c7a54..a42fa6c 100644 --- a/examples/13-TargetStates/DEV_DoorsWindows.h +++ b/examples/13-TargetStates/DEV_DoorsWindows.h @@ -107,8 +107,8 @@ struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Sha // Here we simulate a window shade that takes 5 seconds to move to its new target posiiton - if(target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... - current->setVal(target->getVal()); // ...set the current position to equal the target position + if(current->getVal()!=target->getVal() && target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... + current->setVal(target->getVal()); // ...set the current position to equal the target position } // Note there is no reason to send continuous updates of the current position to the HomeKit. HomeKit does NOT display the From 2a4ebe952221a91218b892e8fc1cd1eea17ac80b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 18:26:12 -0500 Subject: [PATCH 09/55] Update Tutorials.md --- docs/Tutorials.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Tutorials.md b/docs/Tutorials.md index 1d7eaf3..08ca70b 100644 --- a/docs/Tutorials.md +++ b/docs/Tutorials.md @@ -35,8 +35,8 @@ Example 5 expands on Example 2 by adding in the code needed to actually control ### [Example 6 - DimmableLED](../examples/06-DimmableLED) Example 6 changes Example 5 so that LED #2 is now dimmable, instead of just on/off. New HomeSpan API topics covered in this example include: -* implementing pulse-width-modulation on any ESP32 pin by instantiating a `PwmPin()` object -* setting the PWM level to control the brightness of an LED using the PwmPin `set()` method +* implementing pulse-width-modulation (PWM) to control an LED attached to any ESP32 pin by instantiating an `LedPin()` object +* setting the brightness of an LED using the LedPin `set()` method * storing similar derived Service classes in the same \*.h file for ease of use ### [Example 7 - IdentifyRoutines](../examples/07-IdentifyRoutines) From bbf36504160e0c179a7387a9b37e184c3c375eea Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:34:05 -0500 Subject: [PATCH 10/55] Update Extras.md --- docs/Extras.md | 52 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/docs/Extras.md b/docs/Extras.md index 157edf9..940277a 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -4,28 +4,23 @@ HomeSpan includes integrated access to a number of ESP32 features you'll likely ## Pulse Width Modulation (PWM) -PWM on the ESP32 is more flexible, but slighly more complicated, than PWM on most Arduino devices (like the Uno or Mega). On the ESP32, you use one of 16 built-in timer-channels to create a PWM signal, and then link that channel to any ESP32 pin. HomeSpan includes a library that makes this very easy, and is accessed as by placing the following near the top of your sketch: +The ESP32 has 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 accessd by placing the following near the top of your sketch: `#include "extras/PwmPin.h"` -### *PwmPin(uint8_t channel, uint8_t pin)* +### *LedPin(uint8_t pin)* -Creating an instance of this **class** links one of 16 timer-channels to an ESP32 pin. +Creating an instance of this **class** configures the specified *pin* to output a 5000 Hz PWM signal, which is suitable for dimming LEDs. The following methods are supported: -* *channel* - the ESP32 timer-channel number (0-15) to generate the PWM signal -* *pin* - the ESP32 pin that will output the PWM signal produced by the channel +* `void set(uint8_t level)` -The following methods are supported: - -* `void set(uint8_t channel, uint8_t level)` - - * sets the PWM %duty-cycle of timer-channel *channel* (0-15) to *level*, where *level* ranges from 0 (off) to 100 (steady on) + * 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 -PwmPin 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). +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)` @@ -36,7 +31,38 @@ PwmPin also includes a static class function that converts Hue/Saturation/Bright * *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 PwmPin to control an RGB LED. +See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of using LedPin to control an RGB LED. + +### *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. Parameters, 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(uint8_t 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 value greater than *maxDegrees* are automatically reset to *maxDegrees*. + +* `int getPin()` + + * returns the pin number + +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*. + +Resource limitations: + +* A maximum of 16 LedPin objects can be instantiated +* A maximum of 8 ServoPin objects can be instantiated +* A maximum combined total of 16 LedPin and ServoPin objects can be instantiated (for example 10 LedPin and 6 ServoPin objects) + +HomeSpan will report a non-fatal error message to the Arduino Serial Monitor for each LedPin or ServoPin that instantiated beyond these limits. Calls to the `set()` method for objects that exceed these limits are ignored. ## Remote Control Radio Frequency / Infrared Signal Generation @@ -76,7 +102,7 @@ Since most RF/IR signals repeat the same train of pulses more than once, the dur * starts the transmission of the pulse train stored in the pulse train memory buffer. 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) + * *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) * *tickTime* - the duration, in **microseconds**, of a *tick*. This is an optional argument with a default of 1𝛍s if not specified. Valid range is 1-255𝛍s, or set to 0 for 256𝛍s From 8252c5390ed931eac139dc73969fda786af1fcb7 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 21 Mar 2021 22:37:35 -0500 Subject: [PATCH 11/55] Created ServoControl Example Created in "Other Examples" and is based on Example 13 - WindowShade --- .../ServoControl/DEV_DoorsWindows.h | 68 +++++++++++++++++++ Other Examples/ServoControl/DEV_Identify.h | 38 +++++++++++ Other Examples/ServoControl/ServoControl.ino | 60 ++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 Other Examples/ServoControl/DEV_DoorsWindows.h create mode 100644 Other Examples/ServoControl/DEV_Identify.h create mode 100644 Other Examples/ServoControl/ServoControl.ino diff --git a/Other Examples/ServoControl/DEV_DoorsWindows.h b/Other Examples/ServoControl/DEV_DoorsWindows.h new file mode 100644 index 0000000..bc82648 --- /dev/null +++ b/Other Examples/ServoControl/DEV_DoorsWindows.h @@ -0,0 +1,68 @@ + +//////////////////////////////////// +// DEVICE-SPECIFIC LED SERVICES // +//////////////////////////////////// + +#include + +//////////////////////////////////// + +struct DEV_WindowShade : Service::WindowCovering { // A motorized Window Shade with Hold Feature + + SpanCharacteristic *current; // reference to a "generic" Current Position Characteristic (used by a variety of different Service) + SpanCharacteristic *target; // reference to a "generic" Target Position Characteristic (used by a variety of different Service) + SpanCharacteristic *hTiltCurrent; // reference to horizontal tilt of window shade - current position + SpanCharacteristic *hTiltTarget; // reference to horizontal tilt of window shade - target position + + ServoPin *hTiltServo; // reference to Servo Pin to control Horiontal Tilt + + DEV_WindowShade(uint8_t hTiltServoPin) : Service::WindowCovering(){ // constructor() method + + current=new Characteristic::CurrentPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised) + target=new Characteristic::TargetPosition(0); // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised) + target->setRange(0,100,10); // set the allowable target-position range to 0-100 IN STEPS of 10 + + hTiltCurrent=new Characteristic::CurrentHorizontalTiltAngle(); // Tilt Angle is measured in degrees; HAP default is -90 to +90 + hTiltTarget=new Characteristic::TargetHorizontalTiltAngle(); + + // Here we define our Servo using HomeSpan's ServoPin Class. + // See the HomeSpan API Reference for full details and a list of all parameters. + + hTiltServo=new ServoPin(hTiltServoPin); + + Serial.print("Configuring Motorized Window Shade"); // initialization message + Serial.print("\n"); + + } // end constructor + + boolean update(){ // update() method + + if(target->updated()){ // check to see if shade target position was updated + if(target->getNewVal()>current->getVal()){ // if the target-position requested is greater than the current-position, simply log a "raise" message + LOG1("Raising Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows raising is required + } else + if(target->getNewVal()getVal()){ // if the target-position requested is less than the current-position, simply log a "raise" message + LOG1("Lowering Shade\n"); // ** there is nothing more to do - HomeKit keeps track of the current-position so knows lowering is required + } + } + + if(hTiltTarget->updated()){ // check to see if shade tilt angle was updated + hTiltCurrent->setVal(hTiltTarget->getNewVal()); // set current value of tilt to match target value + hTiltServo->set(hTiltTarget->getNewVal()); // <--- update actual servo position with ServoPin->set(degrees) method + } + + return(true); // return true + + } // update + + void loop(){ // loop() method + + // Here we simulate a window shade that takes 5 seconds to move to its new target position + + if(current->getVal()!=target->getVal() && target->timeVal()>5000){ // if 5 seconds have elapsed since the target-position was last modified... + current->setVal(target->getVal()); // ...set the current position to equal the target position + } + + } // loop + +}; diff --git a/Other Examples/ServoControl/DEV_Identify.h b/Other Examples/ServoControl/DEV_Identify.h new file mode 100644 index 0000000..b8d21d6 --- /dev/null +++ b/Other Examples/ServoControl/DEV_Identify.h @@ -0,0 +1,38 @@ + +////////////////////////////////// +// DEVICE-SPECIFIC SERVICES // +////////////////////////////////// + +struct DEV_Identify : Service::AccessoryInformation { + + int nBlinks; // number of times to blink built-in LED in identify routine + SpanCharacteristic *identify; // reference to the Identify Characteristic + + DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){ + + new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments + new Characteristic::Manufacturer(manu); + new Characteristic::SerialNumber(sn); + new Characteristic::Model(model); + new Characteristic::FirmwareRevision(version); + identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below + + this->nBlinks=nBlinks; // store the number of times to blink the LED + + pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output + } + + boolean update(){ + + for(int i=0;i Date: Sun, 21 Mar 2021 22:40:08 -0500 Subject: [PATCH 12/55] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 940277a..0072401 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -54,7 +54,7 @@ The *minMicros* parameter must be less than the *maxMicros* parameter, but setti * returns the pin number -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*. +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*](https://github.com/HomeSpan/HomeSpan/tree/dev/Other%20Examples/ServoControl). Resource limitations: From 61e0b12cf12215511e891269baa404a04fed04cb Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:43:53 -0500 Subject: [PATCH 13/55] Update Extras.md --- docs/Extras.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Extras.md b/docs/Extras.md index 0072401..74fb3d1 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -35,14 +35,14 @@ See tutorial sketch [#10 (RGB_LED)](../examples/10-RGB_LED) for an example of us ### *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. Parameters, along with their defaults if left unspecified, are as follows: +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°) + * *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: @@ -106,7 +106,7 @@ Since most RF/IR signals repeat the same train of pulses more than once, the dur * *tickTime* - the duration, in **microseconds**, of a *tick*. This is an optional argument with a default of 1𝛍s if not specified. Valid range is 1-255𝛍s, or set to 0 for 256𝛍s -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*. +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*](https://github.com/HomeSpan/HomeSpan/tree/dev/Other%20Examples/RemoteControl). ```C++ /* HomeSpan Remote Control Example */ From 8ac275559a218cd44c1f67d8db88ed324660eafc Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:47:27 -0500 Subject: [PATCH 14/55] Update Extras.md --- docs/Extras.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 74fb3d1..e135fed 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -33,7 +33,9 @@ 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)* +### *ServoPin(uint8_t pin, double initDegrees)* +### *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: From 39fbed5526289001346f2c69b9b3dac3f3e82561 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:48:06 -0500 Subject: [PATCH 15/55] Update Extras.md --- docs/Extras.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Extras.md b/docs/Extras.md index e135fed..6c8b86c 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -33,9 +33,9 @@ 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)* -### *ServoPin(uint8_t pin, double initDegrees)* -### *ServoPin(uint8_t pin, double initDegrees, uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees)* +##### *ServoPin(uint8_t pin)* +##### *ServoPin(uint8_t pin, double initDegrees)* +#### *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: From 76fcfc215c3206cecfdd289e69b3f97599a7981b Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:49:03 -0500 Subject: [PATCH 16/55] Update Extras.md --- docs/Extras.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Extras.md b/docs/Extras.md index 6c8b86c..cd2cddc 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -33,9 +33,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)* -##### *ServoPin(uint8_t pin, double initDegrees)* -#### *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: From 165dffaebffe7776835d3f91b775f87994fe91ba Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:49:28 -0500 Subject: [PATCH 17/55] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index cd2cddc..74fb3d1 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -33,7 +33,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: From 9274f918f28920c7ada612e96d87b0b3d758d412 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:50:22 -0500 Subject: [PATCH 18/55] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 74fb3d1..9b4c262 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -41,7 +41,7 @@ Creating an instance of this **class** configures the specified *pin* to output * *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°) + * *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: From 52541d6be161c12adad793e2cb5f7c523bd288c9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:51:43 -0500 Subject: [PATCH 19/55] Update Extras.md --- docs/Extras.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Extras.md b/docs/Extras.md index 9b4c262..306b13f 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -48,7 +48,7 @@ The *minMicros* parameter must be less than the *maxMicros* parameter, but setti * `void set(uint8_t 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 value greater than *maxDegrees* are automatically reset to *maxDegrees*. + * 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()` From da4665f44eaf916eced86f847cf804c1e43faef1 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 21 Mar 2021 22:52:42 -0500 Subject: [PATCH 20/55] 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 8a65e98..d8d8d8c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -56,7 +56,7 @@ 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 PWM and Remote Control peripherals! +* [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 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 4dfb881780742704e24ffa5748304b0598d16806 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 22 Mar 2021 07:45:08 -0500 Subject: [PATCH 21/55] Update README.md --- docs/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index d8d8d8c..c12ef6f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,7 +21,9 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A * Complete transparency to every underlying HomeKit action, data request, and data response * Command-line interface with a variety of info, debugging, and configuration commands * Built-in database validation to ensure your configuration meets all HAP requirements -* Integrated PWM functionality supporting pulse-wave-modulation on any ESP32 pin +* Dedicated classes that utilize the ESP32's 16-channel PWM peripheral for easy control of: + * LED Brightness + * Servo Motors * Integrated Push Button functionality supporting single, double, and long presses * Integrated access to the ESP32's on-chip Remote Control peripheral for easy generation of IR and RF signals * 17 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks From 5204eeaeefd1f08e63e6569eb11c6476bca31f4d Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 30 May 2021 07:08:36 -0500 Subject: [PATCH 22/55] Update Reference.md --- docs/Reference.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 9f73bcf..391747a 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -221,15 +221,13 @@ If REQUIRED is defined in the main sketch prior to including the HomeSpan librar ``` --- -## *SpanRange(int min, int max, int step)* +#### Deprecated functions (available for backwards compatibility with older sketches): -Creating an instance of this **class** overrides the default HAP range for a Characteristic with the *min*, *max*, and *step* values specified. +[*SpanRange(int min, int max, int step)*](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md#spanrangeint-min-int-max-int-step) -* instantiated Ranges are added to the HomeSpan HAP Database and associated with the last Characteristic instantiated -* instantiating a Range without first instantiating a Characteristic throws an error during initialization -* example: `new Characteristic::Brightness(50); new SpanRange(10,100,5);` -* this is a legacy function that is limited to integer-based parameters, and has been re-coded to simply call the more generic `setRange(min, max, step)` method -* **please use** `setRange(min, max, step)` **for all new sketches** + * 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 + * last supported version: v1.2.0 + * **please use** `setRange(min, max, step)` **for all new sketches** --- From 2309212d342e6429fbc0831132c1f0753b96f8ff Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 30 May 2021 07:21:19 -0500 Subject: [PATCH 23/55] Update Extras.md --- docs/Extras.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/Extras.md b/docs/Extras.md index 306b13f..e10dd55 100644 --- a/docs/Extras.md +++ b/docs/Extras.md @@ -161,4 +161,20 @@ void loop(){ --- +#### Deprecated functions (available for backwards compatibility with older sketches): + +*PwmPin(uint8_t channel, uint8_t pin)* + + * this legacy function was used to generically control the ESP32's built-in PWM generators to drive a dimmable LED and required the user to keep track of individual PWM channels. It has been replaced by two specific (and much easier-to-use) methods: + + * *LedPin(uint8_t pin)* - drives a dimmable LED + + * *ServoPin(uint8_t pin [,double initDegrees [,uint16_t minMicros, uint16_t maxMicros, double minDegrees, double maxDegrees]])* - drives a Servo Motor + + * last supported version: [v1.2.1](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.1/docs/Extras.md#pwmpinuint8_t-channel-uint8_t-pin) + + * **please use** `LedPin` and `ServoPin` **for all new sketches** + +--- + [↩️](README.md) Back to the Welcome page From 7ce04a70f4b76ea6349200f8e7d7c5c33da250c8 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 30 May 2021 07:23:16 -0500 Subject: [PATCH 24/55] Update Reference.md --- docs/Reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 391747a..e0315b9 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -223,10 +223,10 @@ If REQUIRED is defined in the main sketch prior to including the HomeSpan librar #### Deprecated functions (available for backwards compatibility with older sketches): -[*SpanRange(int min, int max, int step)*](https://github.com/HomeSpan/HomeSpan/blob/release-1.2.0/docs/Reference.md#spanrangeint-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 - * last supported version: v1.2.0 + * 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** --- From bda90c59cad1e2d5b484585def18f93b8d5ffa12 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 May 2021 13:45:52 -0500 Subject: [PATCH 25/55] Created SpanUserCommand() Allows the user to add a command function to the Command Line Interface. All User Commands are defined with a '@' prefix. To Do: Document this new feature. --- src/HomeSpan.cpp | 38 ++++++++++++++++++++++++++++++++------ src/HomeSpan.h | 13 ++++++++++++- src/src.ino | 25 ++++++++++++++++++------- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index c6faa3c..1c16941 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -892,17 +892,33 @@ void Span::processSerialCommand(const char *c){ Serial.print(" E - erase ALL stored data and restart\n"); Serial.print("\n"); Serial.print(" L - change the Log Level setting to \n"); - Serial.print("\n"); - Serial.print(" ? - print this list of commands\n"); - Serial.print("\n"); - Serial.print("\n*** End Commands ***\n\n"); + Serial.print("\n"); + + for(auto uCom=homeSpan.UserCommands.begin(); uCom!=homeSpan.UserCommands.end(); uCom++) // loop over all UserCommands using an iterator + Serial.printf(" @%c %s\n",uCom->first,uCom->second->s); + + if(!homeSpan.UserCommands.empty()) + Serial.print("\n"); + + Serial.print(" ? - print this list of commands\n\n"); + Serial.print("*** End Commands ***\n\n"); } break; + case '@':{ + + auto uCom=UserCommands.find(c[1]); + + if(uCom!=UserCommands.end()){ + uCom->second->userFunction(c+1); + break; + } + } + default: - Serial.print("** Unknown command: '"); + Serial.print("*** Unknown command: '"); Serial.print(c); - Serial.print("' - type '?' for list of commands.\n"); + Serial.print("'. Type '?' for list of commands.\n"); break; } // switch @@ -1695,4 +1711,14 @@ SpanButton::SpanButton(int pin, uint16_t longTime, uint16_t singleTime, uint16_t homeSpan.PushButtons.push_back(this); } + /////////////////////////////// +// SpanUserCommand // +/////////////////////////////// + +SpanUserCommand::SpanUserCommand(char c, const char *s, void (*f)(const char *v)){ + this->s=s; + userFunction=f; + + homeSpan.UserCommands[c]=this; +} diff --git a/src/HomeSpan.h b/src/HomeSpan.h index fbf2cde..149d7c4 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -66,6 +66,7 @@ struct SpanCharacteristic; struct SpanRange; struct SpanBuf; struct SpanButton; +struct SpanUserCommand; extern Span homeSpan; @@ -134,6 +135,8 @@ 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 UserCommands; // map of pointers to all UserCommands void begin(Category catID=DEFAULT_CATEGORY, const char *displayName=DEFAULT_DISPLAY_NAME, @@ -474,7 +477,15 @@ struct SpanButton{ SpanButton(int pin, uint16_t longTime=2000, uint16_t singleTime=5, uint16_t doubleTime=200); }; +/////////////////////////////// + +struct SpanUserCommand { + const char *s; // description of command + void (*userFunction)(const char *v); // user-defined function to call + + SpanUserCommand(char c, const char *s, void (*f)(const char *v)); +}; + ///////////////////////////////////////////////// - #include "Span.h" diff --git a/src/src.ino b/src/src.ino index 3becb91..ad9f749 100644 --- a/src/src.ino +++ b/src/src.ino @@ -18,6 +18,9 @@ void setup() { homeSpan.setSketchVersion("Test 1.3.1"); homeSpan.setWifiCallback(wifiEstablished); + new SpanUserCommand('d',"My Description",userCom1); + new SpanUserCommand('f',"My second Description",userCom2); + homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments @@ -30,21 +33,17 @@ void setup() { new Characteristic::FirmwareRevision(HOMESPAN_VERSION); // Firmware of the Accessory (arbitrary text string, and can be the same for every Accessory) new Characteristic::Identify(); // Create the required Identify -// new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service + new Service::HAPProtocolInformation(); // Create the HAP Protcol Information Service new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP new Service::LightBulb(); -// new Characteristic::On(); + new Characteristic::On(); new Characteristic::Brightness(); new Characteristic::Name("Light 1"); new Service::LightBulb(); new Characteristic::On(2); - (new Characteristic::Brightness(150))->setRange(0,140,5); + (new Characteristic::Brightness(50))->setRange(10,100,5); new Characteristic::Name("Light 2"); - (new Service::Switch())->setPrimary(); - new Characteristic::On(); - new Characteristic::Name("Switch 3"); - new SpanButton(17); } // end of setup() @@ -61,3 +60,15 @@ void loop(){ void wifiEstablished(){ Serial.print("IN CALLBACK FUNCTION\n\n"); } + +////////////////////////////////////// + +void userCom1(const char *v){ + Serial.printf("In User Command 1: '%s'\n\n",v); +} + +////////////////////////////////////// + +void userCom2(const char *v){ + Serial.printf("In User Command 2: '%s'\n\n",v); +} From 83507abb41c5a7f822c0abe3f519ae0bd3b61579 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 30 May 2021 13:48:28 -0500 Subject: [PATCH 26/55] Update src.ino --- src/src.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src.ino b/src/src.ino index ad9f749..404eca4 100644 --- a/src/src.ino +++ b/src/src.ino @@ -19,7 +19,7 @@ void setup() { homeSpan.setWifiCallback(wifiEstablished); new SpanUserCommand('d',"My Description",userCom1); - new SpanUserCommand('f',"My second Description",userCom2); + new SpanUserCommand('d',"My second Description",userCom2); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); From 571a350d5bed48b6384b08aa7d2e558698f703ab Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 30 May 2021 20:57:25 -0500 Subject: [PATCH 27/55] Update Reference.md --- docs/Reference.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index e0315b9..8b2ac1c 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -212,6 +212,18 @@ HomeSpan automatically calls the `button(int pin, int pressType)` method of a Se HomeSpan will report a warning, but not an error, during initialization if the user had not overridden the virtual button() method for a Service contaning one or more Buttons; triggers of those Buttons will simply ignored. +## *SpanUserCommand(char c, const char \*s, void (\*f)(const char \*v))* + +Creating an instance of this **class** adds a user-defined command to the HomeSpan Command-Line Interface (CLI), where: + + * *c* is the single-letter name of the user-defined command + * *s* is a description of the user-defined command that is displayed when the user types '?' into the CLI + * *f* is a pointer to a user-defined function that is called when the command is invoked. This function must be of the form `void f(const char *v)`, where *v* points to all characters typed into the CLI, beginning with the single-letter command name *c*. + +To invoke this command from the CLI, preface the single-letter name *c* with '@'. This allows HomeSpan to distinguish user-defined commands from its built-in commands. For example, `new SpanUserCommand('s', "save current configuration",saveConfig)` would add a new command '@s' to the CLI with description "save current configuration" that will call the user-defined function `void saveConfig(const char *v)` when invoked. The argument *v* points to an array of all characters typed into the CLI after the '@'. This allows the user to pass arguments from the CLI to the user-defined function. For example, typing '@s123' into the CLI sets *v* to "s123" when saveConfig is called. + +To create more than one user-defined command, simply create multiple instances of SpanUserCommand, each with its own single-letter name. Note that re-using the same single-letter name in an instance of SpanUserCommand over-rides any previous instances using that same letter. + ## *#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: From d46fd3949d87e1e94490f7bab4d2f96816e9abfd Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 30 May 2021 21:07:15 -0500 Subject: [PATCH 28/55] Update FAQ.md --- docs/FAQ.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/FAQ.md b/docs/FAQ.md index 83334a0..6b8eefb 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -32,6 +32,10 @@ * Though HomeKit now supports Television controls, at the time of this posting Apple has not publicly released any specifications, SDKs, or ADKs, related to HAP Television Services or any HAP Television Characteristics. It appears that for now these Services are meant only for commercial use. Support for HAP Television Services and Characteristics will be added to HomeSpan as soon as Apple makes the specifications publicly available in some form or another. +#### Can you use HomeSpan with an Ethernet connection instead of a WiFi connection? + +* Not as present. Though with a compatible Ethernet board the ESP32 can be configured to run as an Ethernet Server, using MDNS over Ethernet does not work on the ESP32 due to some apparent problems with the Ethernet UDP stack. Unfortunately, HomeSpan and HAP-R2 require MDNS to operate. If anyone has managed to get an Ethernet version of MDNS working on an ESP32 please let me know - it would be great to add Ethernet support to HomeSpan. + --- [↩️](README.md) Back to the Welcome page From 1dfd66bf2570c4e8f6605e7e2b035c5e530a9c4d Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 5 Jun 2021 10:30:21 -0500 Subject: [PATCH 29/55] Add homeSpan.enableAutoStartAP() Enables auto-start of AccessPoint if WiFi Credentials not found. --- src/HomeSpan.cpp | 10 ++++++++-- src/HomeSpan.h | 2 ++ src/src.ino | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 1c16941..b273ae2 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -142,8 +142,14 @@ void Span::poll() { HAPClient::init(); // read NVS and load HAP settings if(!strlen(network.wifiData.ssid)){ - Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - statusLED.start(LED_WIFI_NEEDED); + Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. "); + if(autoStartAPEnabled){ + Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); + processSerialCommand("A"); + } else { + Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); + statusLED.start(LED_WIFI_NEEDED); + } } else { homeSpan.statusLED.start(LED_WIFI_CONNECTING); } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 149d7c4..1d604a6 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -123,6 +123,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 + boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status @@ -177,6 +178,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 enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found }; /////////////////////////////// diff --git a/src/src.ino b/src/src.ino index 404eca4..1f4c5ab 100644 --- a/src/src.ino +++ b/src/src.ino @@ -21,6 +21,8 @@ void setup() { new SpanUserCommand('d',"My Description",userCom1); new SpanUserCommand('d',"My second Description",userCom2); +// homeSpan.enableAutoStartAP(); + homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); new SpanAccessory(); // Begin by creating a new Accessory using SpanAccessory(), which takes no arguments @@ -40,6 +42,7 @@ void setup() { new Characteristic::On(); new Characteristic::Brightness(); new Characteristic::Name("Light 1"); + new Characteristic::ColorTemperature(0); new Service::LightBulb(); new Characteristic::On(2); (new Characteristic::Brightness(50))->setRange(10,100,5); From 49e3786618d09b301811d1d44f7d4993d9f9d9b3 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 5 Jun 2021 10:40:08 -0500 Subject: [PATCH 30/55] Update Reference.md --- docs/Reference.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/Reference.md b/docs/Reference.md index 8b2ac1c..76589c6 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -77,6 +77,10 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * 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 + +* `void enableAutoStartAP()` + * enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time + * methods to alter the behavior the Access Point, such as `setApTimeout()` must be called prior to `enableAutoStartAP()` to have an effect * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string From 40798b15cfbb6431c0f4e31a0002b18f0459020d Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 08:45:11 -0500 Subject: [PATCH 31/55] Begin development of NVS Characteristic storage Used method restore() to restore value. To do: Change this to a flag during instantiation of a new Characteristic instead of a separate method. --- src/HAP.cpp | 3 --- src/HAP.h | 1 - src/HomeSpan.cpp | 36 ++++++++++++++++++++++++++++++------ src/HomeSpan.h | 4 ++++ src/src.ino | 10 +++++----- 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index c29658b..66512ea 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -26,7 +26,6 @@ ********************************************************************************/ #include -#include #include #include @@ -39,8 +38,6 @@ void HAPClient::init(){ size_t len; // not used but required to read blobs from NVS - nvs_flash_init(); // initialize non-volatile-storage partition in flash - nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS diff --git a/src/HAP.h b/src/HAP.h index 070a170..23c3f96 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -28,7 +28,6 @@ #pragma once #include -#include #include "HomeSpan.h" #include "TLV.h" diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index b273ae2..71b58ae 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -65,6 +65,9 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa hapServer=new WiFiServer(tcpPortNum); + nvs_flash_init(); // initialize non-volatile-storage partition in flash + nvs_open("CHAR",NVS_READWRITE,&charNVS); + delay(2000); Serial.print("\n************************************************************\n" @@ -1135,13 +1138,15 @@ int Span::updateCharacteristics(char *buf, SpanBuf *pObj){ LOG1(pObj[j].characteristic->aid); LOG1(" iid="); LOG1(pObj[j].characteristic->iid); - if(status==StatusCode::OK){ // if status is okay - pObj[j].characteristic->value - =pObj[j].characteristic->newValue; // update characteristic value with new value + if(status==StatusCode::OK){ // if status is okay + pObj[j].characteristic->value=pObj[j].characteristic->newValue; // update characteristic value with new value + if(pObj[j].characteristic->nvsKey){ // if storage key found + nvs_set_blob(charNVS,pObj[j].characteristic->nvsKey,&(pObj[j].characteristic->value),sizeof(pObj[j].characteristic->value)); // store data + nvs_commit(charNVS); + } LOG1(" (okay)\n"); - } else { // if status not okay - pObj[j].characteristic->newValue - =pObj[j].characteristic->value; // replace characteristic new value with original value + } else { // if status not okay + pObj[j].characteristic->newValue=pObj[j].characteristic->value; // replace characteristic new value with original value LOG1(" (failed)\n"); } pObj[j].characteristic->isUpdated=false; // reset isUpdated flag for characteristic @@ -1520,6 +1525,25 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar){ /////////////////////////////// +void SpanCharacteristic::restore(){ + + nvsKey=(char *)malloc(16); + uint16_t t; + sscanf(type,"%x",&t); + sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); + size_t len; + + if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ + nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); + } + else { + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); // commit to NVS + } +} + +/////////////////////////////// + int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){ int nBytes=0; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 1d604a6..1525ef8 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -33,6 +33,7 @@ #include #include +#include #include "Settings.h" #include "Utils.h" @@ -106,6 +107,7 @@ struct Span{ boolean isBridge=true; // flag indicating whether device is configured as a bridge (i.e. first Accessory contains nothing but AccessoryInformation and HAPProtocolInformation) HapQR qrCode; // optional QR Code to use for pairing const char *sketchVersion="n/a"; // version of the sketch + nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data boolean connected=false; // WiFi connection status unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -252,6 +254,7 @@ struct SpanCharacteristic{ boolean staticRange; // Flag that indiates whether Range is static and cannot be changed with setRange() boolean customRange=false; // Flag for custom ranges boolean *ev; // Characteristic Event Notify Enable (per-connection) + char *nvsKey=NULL; // key for NVS storage of Characteristic value uint32_t aid=0; // Accessory ID - passed through from Service containing this Characteristic boolean isUpdated=false; // set to true when new value has been requested by PUT /characteristic @@ -263,6 +266,7 @@ struct SpanCharacteristic{ int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intiial HAP status code (checks to see if characteristic is found, is writable, etc.) + void restore(); // loads previous value of Characteristic from NVS (if found) boolean updated(){return(isUpdated);} // returns isUpdated unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated diff --git a/src/src.ino b/src/src.ino index 1f4c5ab..e103ba0 100644 --- a/src/src.ino +++ b/src/src.ino @@ -39,13 +39,13 @@ void setup() { new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP new Service::LightBulb(); - new Characteristic::On(); - new Characteristic::Brightness(); + (new Characteristic::On())->restore(); + (new Characteristic::Brightness())->restore(); new Characteristic::Name("Light 1"); - new Characteristic::ColorTemperature(0); + new Characteristic::ColorTemperature(); new Service::LightBulb(); - new Characteristic::On(2); - (new Characteristic::Brightness(50))->setRange(10,100,5); + (new Characteristic::On())->restore(); + (new Characteristic::Brightness(50))->setRange(10,100,5)->restore(); new Characteristic::Name("Light 2"); } // end of setup() From f4c9c430ef40de2f76a15e822b8327861951c304 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 09:20:13 -0500 Subject: [PATCH 32/55] Replace restore() method with second optional parameter when instantiating Characteristic To enable save/restore for a Characteristic, set second parameter to TRUE when instantiating. Since first parameter was optional as well, this requires setting it as well. Next up: Must add logic to setVal() to store new value as well. --- src/HomeSpan.cpp | 19 ------------ src/HomeSpan.h | 78 +++++++++++++++++++++++++++++------------------- src/Span.h | 2 +- src/src.ino | 8 ++--- 4 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 71b58ae..7fc6a47 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -1525,25 +1525,6 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar){ /////////////////////////////// -void SpanCharacteristic::restore(){ - - nvsKey=(char *)malloc(16); - uint16_t t; - sscanf(type,"%x",&t); - sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); - size_t len; - - if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ - nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); - } - else { - nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data - nvs_commit(homeSpan.charNVS); // commit to NVS - } -} - -/////////////////////////////// - int SpanCharacteristic::sprintfAttributes(char *cBuf, int flags){ int nBytes=0; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 1525ef8..1ed6c64 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -266,7 +266,6 @@ struct SpanCharacteristic{ int sprintfAttributes(char *cBuf, int flags); // prints Characteristic JSON records into buf, according to flags mask; return number of characters printed, excluding null terminator StatusCode loadUpdate(char *val, char *ev); // load updated val/ev from PUT /characteristic JSON request. Return intiial HAP status code (checks to see if characteristic is found, is writable, etc.) - void restore(); // loads previous value of Characteristic from NVS (if found) boolean updated(){return(isUpdated);} // returns isUpdated unsigned long timeVal(); // returns time elapsed (in millis) since value was last updated @@ -379,7 +378,7 @@ struct SpanCharacteristic{ } // setRange() - template void init(T val, A min=0, B max=1){ + template void init(T val, boolean nvsStore, A min=0, B max=1){ uvSet(value,val); uvSet(newValue,val); @@ -387,36 +386,55 @@ struct SpanCharacteristic{ uvSet(maxValue,max); uvSet(stepValue,0); - homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); - if(format!=STRING && format!=BOOL) - homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; - - boolean valid=false; - - for(int i=0; !valid && iServices.back()->req.size(); i++) - valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); - - for(int i=0; !valid && iServices.back()->opt.size(); i++) - valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type); - - if(!valid){ - homeSpan.configLog+=" *** ERROR! Service does not support this Characteristic. ***"; - homeSpan.nFatalErrors++; - } - - boolean repeated=false; + if(nvsStore){ + nvsKey=(char *)malloc(16); + uint16_t t; + sscanf(type,"%x",&t); + sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF); + size_t len; + + if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ + nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); + } + else { + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); // commit to NVS + } + } - for(int i=0; !repeated && iServices.back()->Characteristics.size(); i++) - repeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type); + homeSpan.configLog+="(" + uvPrint(value) + ")" + ": IID=" + String(iid) + ", UUID=0x" + String(type); + if(format!=STRING && format!=BOOL) + homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; + + if(nvsStore) + homeSpan.configLog+=" (restored)"; - if(valid && repeated){ - homeSpan.configLog+=" *** ERROR! Characteristic already defined for this Service. ***"; - homeSpan.nFatalErrors++; - } - - homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); - - homeSpan.configLog+="\n"; + boolean valid=false; + + for(int i=0; !valid && iServices.back()->req.size(); i++) + valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->req[i]->type); + + for(int i=0; !valid && iServices.back()->opt.size(); i++) + valid=!strcmp(type,homeSpan.Accessories.back()->Services.back()->opt[i]->type); + + if(!valid){ + homeSpan.configLog+=" *** ERROR! Service does not support this Characteristic. ***"; + homeSpan.nFatalErrors++; + } + + boolean repeated=false; + + for(int i=0; !repeated && iServices.back()->Characteristics.size(); i++) + repeated=!strcmp(type,homeSpan.Accessories.back()->Services.back()->Characteristics[i]->type); + + if(valid && repeated){ + homeSpan.configLog+=" *** ERROR! Characteristic already defined for this Service. ***"; + homeSpan.nFatalErrors++; + } + + homeSpan.Accessories.back()->Services.back()->Characteristics.push_back(this); + + homeSpan.configLog+="\n"; } // init() diff --git a/src/Span.h b/src/Span.h index f8eb3ce..64695af 100644 --- a/src/Span.h +++ b/src/Span.h @@ -384,7 +384,7 @@ namespace Service { // Macro to define Span Characteristic structures based on name of HAP Characteristic, default value, and mix/max value (not applicable for STRING or BOOL which default to min=0, max=1) #define CREATE_CHAR(TYPE,HAPCHAR,DEFVAL,MINVAL,MAXVAL) \ - struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,(TYPE)MINVAL,(TYPE)MAXVAL); } }; + struct HAPCHAR : SpanCharacteristic { HAPCHAR(TYPE val=DEFVAL, boolean nvsStore=false) : SpanCharacteristic {&hapChars.HAPCHAR} { init(val,nvsStore,(TYPE)MINVAL,(TYPE)MAXVAL); } }; namespace Characteristic { diff --git a/src/src.ino b/src/src.ino index e103ba0..9e67e91 100644 --- a/src/src.ino +++ b/src/src.ino @@ -39,13 +39,13 @@ void setup() { new Characteristic::Version("1.1.0"); // Set the Version Characteristic to "1.1.0" as required by HAP new Service::LightBulb(); - (new Characteristic::On())->restore(); - (new Characteristic::Brightness())->restore(); + new Characteristic::On(0); + new Characteristic::Brightness(); new Characteristic::Name("Light 1"); new Characteristic::ColorTemperature(); new Service::LightBulb(); - (new Characteristic::On())->restore(); - (new Characteristic::Brightness(50))->setRange(10,100,5)->restore(); + new Characteristic::On(0,true); + (new Characteristic::Brightness(50,true))->setRange(10,100,5); new Characteristic::Name("Light 2"); } // end of setup() From c7d82f74c6daf2b6bbaa051a78f7814cca06fbe7 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 10:55:06 -0500 Subject: [PATCH 33/55] Logic added to save setVal() status changes in NVS Also created new Example 18 demonstrating NVS storage for an LED. To do: Create CLI command to erase stored characteristics. --- examples/18-SavingStatus/18-SavingStatus.ino | 152 +++++++++++++++++++ examples/18-SavingStatus/DEV_Identify.h | 38 +++++ examples/18-SavingStatus/DEV_LED.h | 97 ++++++++++++ src/HomeSpan.h | 6 + 4 files changed, 293 insertions(+) create mode 100644 examples/18-SavingStatus/18-SavingStatus.ino create mode 100644 examples/18-SavingStatus/DEV_Identify.h create mode 100644 examples/18-SavingStatus/DEV_LED.h diff --git a/examples/18-SavingStatus/18-SavingStatus.ino b/examples/18-SavingStatus/18-SavingStatus.ino new file mode 100644 index 0000000..f048b79 --- /dev/null +++ b/examples/18-SavingStatus/18-SavingStatus.ino @@ -0,0 +1,152 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +//////////////////////////////////////////////////////////// +// // +// HomeSpan: A HomeKit implementation for the ESP32 // +// ------------------------------------------------ // +// // +// Example 15: Real PushButtons // +// * manually controlling a Dimmable LED // +// // +// // +//////////////////////////////////////////////////////////// + +#include "HomeSpan.h" +#include "DEV_LED.h" +#include "DEV_Identify.h" + +void setup() { + + // In Example 14 we saw how to emulate a PushButton tile within HomeKit by automatically resetting a Characteristic so that + // it "turns off" after a short period of time. However, sometimes we want to be able to physically control a device with actual + // PushButtons (or momentary switches) that trigger an action, such as turning on a light or fan, or opening a garage door. + // Additionally, we want HomeKit to reflect any changes in the device as a result of such manual actions - HomeKit should know + // when the light has been turned on or off manually. + + // One way to accomplish would be via custom code added to the loop() method of your derived Service that monitors the button, + // checks when it is pressed, debounces button noise, performs some actions when pressed, and informs HomeKit of the actions with + // the setVal() method. Or you can use HomeSpan's built-in SpanButton() object. + + // SpanButton() is a Service-level object, meaning it attaches itself to the last Service you define. Typically you would instantiate + // one of more SpanButton() objects directly inside the constructor for your derived Service. + + // SpanButton() supports three types of a triggers: a SINGLE button press, a DOUBLE press, and a LONG (extended) press. + + // The length of the presses needed to trigger these different types can be specified by optional arguments to SpanButton(). + // Since most buttons create spurious noise when pressed (and then again when released), the default time to trigger a SINGLE press is 5ms. + // It's fine to change this to a longer value, but a shorter value is not recommended as this may allow spurious triggers unless + // you debounce your switch with hardware. + + // The SpanButton() constructor takes 4 arguments, in the following order: + // + // pin - the pin number to which the PushButton is attached (required) + // longTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a LONG press (optional; default=2000 ms) + // singleTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a SINGLE press (optional; default=5 ms) + // doubleTime - the maximum length of time (in milliseconds) between button presses to create a DOUBLE press (optional; default=200 ms) + + // When a SpanButton() is instantiated, it sets the specified pin on the ESP32 to be an INPUT with PULL-UP, meaning that the pin will + // normally return a value of HIGH when read. Your actual PushButton should be connected so that this pin is GROUNDED when the button + // is pressed. + + // HomeSpan automatically polls all pins with associated SpanButton() objects and checks for LOW values, which indicates the button was + // pressed, but not yet released. It then starts a timer. If the button is released after being pressed for less than singleTime milliseconds, + // nothing happens. If the button is released after being pressed for more than singleTime milliseconds, but for less than longTime milliseconds, + // a SINGLE press is triggered, unless you press once again within doubleTime milliseconds to trigger a DOUBLE press. If the button is held for more + // than longTime milliseconds without being released, a LONG press is triggered. Once a LONG press is triggered the timer resets so that if you keep + // holding the button, another LONG press will be triggered in another longTime milliseconds. This continues until you finally release the button. + + // Note if you set longTime > singleTime, SpanButton() will only trigger LONG presses. Also, if you set doubleTime to zero, SpanButton() will not be + // able to trigger a DOUBLE press. + + // To use SpanButton() within a derived Service you need to implement a button() method. Similar to the loop() method, your button() + // method will typically contain some combination of getVal() functions and setVal() functions, along with code that performs some set + // of actions on the physical device (seting pins high or low, turning on fans, etc). However, in contrast to the loop() method, which + // is called by HomeSpan every polling cycle, HomeSpan only calls the button() method when a button attached to the Service registers a + // SINGLE, DOUBLE, or LONG press. + + // Also in contrast with the loop method, the button() method takes two 'int' arguments, and should defined as follows: + // + // void button(int pin, int pressType) + // + // where "pin" is the pin number of the PushButton that was triggered, and pressType is set to 0 for a SINGLE press, 1 for a DOUBLE press, + // and 2 for a LONG press. You can also use the pre-defined constants SpanButton::SINGLE, SpanButton::DOUBLE, and SpanButton::LONG in place + // of the numbers 0, 1, and 2 (this is recommended, though you will see in Example 16 why these integers can't be replaced by an C++ enum class). + + // Of course you can replace the variables "pin" and "pressType" with your own names. The only requirement is the definition conform to + // the "void button(int, int)" signature. When HomeSpan first starts up it checks all Services containing one or more SpanButton() instances to + // ensure you've implemented your own button(int, int) method. If not, HomeSpan will print a warning message on the Serial Monitor. Nothing bad + // happens if you instantiate a SpanButton() but forget to create the button() method, or you create it with the wrong parameters. But nothing good + // happens either - button presses are just ignored. + // + // C++ Note: For an extra check, you can also place the the contextual keyword "override" after your method definition as such: + // + // void button(int buttonPin, int pressType) override {...your code...} + // + // Doing so allows the compiler to check that you are indeed over-riding the base class button() method and not inadvertently creating a new + // button() method with an incorrect signature that will never be called by SpanButton(). In fact, you could add "override" to the definition + // of your update() and loop() methods as well, since these are always supposed to over-ride the base-class method. + + // To demonstrate how SpanButtons works in practice, we will implement a Dimmable LED starting with the same LED code use in Example 11, + // but with 3 SpanButton() objects performing different functions that showcase the different types of presses. + // + // * A "power" SpanButton that will toggle the power in response a SINGLE press, turn on the power and set the brightness to a "favorite" level + // in response to the DOUBLE press, and set a new "favorite" level in response to a LONG press. + // + // * A "raise brightness" SpanButton that will increase the brightness by 1% in response to a SINGLE press, repeatedly increase the brightness + // by 10% in response to a LONG press, and jump to the maximum brightness in response to a DOUBLE press. + // + // * A "lower brightness" SpanButton that will decrease the brightness by 1% in response to a SINGLE press, repeatedly decrease the brightness + // by 10% in response to a LONG press, and jump to the minimum brightness in response to a DOUBLE press. + + // As usual, all the code is implemented in DEV_LED.h, with NEW! comments highlighting changes from Example 11. You'll also notice that we've + // extended the constructor for this version of our derived Dimmable LED Service to include the pin numbers for each of our buttons. + // See DEV_LED.h for details. + + Serial.begin(115200); + + homeSpan.begin(Category::Bridges,"HomeSpan Bridge"); + + new SpanAccessory(); + new DEV_Identify("Bridge #1","HomeSpan","123-ABC","HS Bridge","0.9",3); + new Service::HAPProtocolInformation(); + new Characteristic::Version("1.1.0"); + + new SpanAccessory(); + new DEV_Identify("PushButton LED","HomeSpan","123-ABC","20mA LED","0.9",0); + + new DEV_DimmableLED(17,19); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h + +} // end of setup() + +////////////////////////////////////// + +void loop(){ + + homeSpan.poll(); + +} // end of loop() diff --git a/examples/18-SavingStatus/DEV_Identify.h b/examples/18-SavingStatus/DEV_Identify.h new file mode 100644 index 0000000..b8d21d6 --- /dev/null +++ b/examples/18-SavingStatus/DEV_Identify.h @@ -0,0 +1,38 @@ + +////////////////////////////////// +// DEVICE-SPECIFIC SERVICES // +////////////////////////////////// + +struct DEV_Identify : Service::AccessoryInformation { + + int nBlinks; // number of times to blink built-in LED in identify routine + SpanCharacteristic *identify; // reference to the Identify Characteristic + + DEV_Identify(const char *name, const char *manu, const char *sn, const char *model, const char *version, int nBlinks) : Service::AccessoryInformation(){ + + new Characteristic::Name(name); // create all the required Characteristics with values set based on above arguments + new Characteristic::Manufacturer(manu); + new Characteristic::SerialNumber(sn); + new Characteristic::Model(model); + new Characteristic::FirmwareRevision(version); + identify=new Characteristic::Identify(); // store a reference to the Identify Characteristic for use below + + this->nBlinks=nBlinks; // store the number of times to blink the LED + + pinMode(homeSpan.getStatusPin(),OUTPUT); // make sure LED is set for output + } + + boolean update(){ + + for(int i=0;isetRange(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% + + // NEW! Below we create three SpanButton() objects. In the first we specify the pin number, as required, but allow SpanButton() to use + // its default values for a LONG press (2000 ms), a SINGLE press (5 ms), and a DOUBLE press (200 ms). In the second and third we change the + // default LONG press time to 500 ms, which works well for repeatedly increasing or decreasing the brightness. + + // All of the logic for increasing/decreasing brightness, turning on/off power, and setting/resetting a favorite brightness level is found + // in the button() method below. + + new SpanButton(powerPin); // NEW! create new SpanButton to control power using pushbutton on pin number "powerPin" + + this->powerPin=powerPin; // NEW! save power pushbutton pin number + this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin + + Serial.print("Configuring Dimmable LED: Pin="); // initialization message + Serial.print(ledPin->getPin()); + Serial.print("\n"); + + ledPin->set(power->getVal()*level->getVal()); + + } // end constructor + + boolean update(){ // update() method + + LOG1("Updating Dimmable LED on pin="); + LOG1(ledPin->getPin()); + LOG1(": Current Power="); + LOG1(power->getVal()?"true":"false"); + LOG1(" Current Brightness="); + LOG1(level->getVal()); + + if(power->updated()){ + LOG1(" New Power="); + LOG1(power->getNewVal()?"true":"false"); + } + + if(level->updated()){ + LOG1(" New Brightness="); + LOG1(level->getNewVal()); + } + + LOG1("\n"); + + ledPin->set(power->getNewVal()*level->getNewVal()); + + return(true); // return true + + } // update + + void button(int pin, int pressType) override { + + LOG1("Found button press on pin: "); // always a good idea to log messages + LOG1(pin); + LOG1(" type: "); + LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE"); + LOG1("\n"); + + if(pin==powerPin && pressType==SpanButton::SINGLE){ + power->setVal(1-power->getVal()); // toggle the value of the power Characteristic + ledPin->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values + } + + } // button + +}; + +////////////////////////////////// diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 1ed6c64..dc52ac4 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -395,6 +395,7 @@ struct SpanCharacteristic{ if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); + newValue=value; } else { nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data @@ -469,6 +470,11 @@ struct SpanCharacteristic{ char dummy[]=""; sb.val=dummy; // set dummy "val" so that sprintfNotify knows to consider this "update" homeSpan.Notifications.push_back(sb); // store SpanBuf in Notifications vector + + if(nvsKey){ + nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data + nvs_commit(homeSpan.charNVS); + } } // setVal() From 6356012fd68353dfba69f894f71a1447190e1425 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 14:49:36 -0500 Subject: [PATCH 34/55] Added 'V' command to CLI This command erases all values of saved Characteristics --- src/HomeSpan.cpp | 9 +++++++++ src/HomeSpan.h | 8 +++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 7fc6a47..381f1e3 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -784,6 +784,14 @@ void Span::processSerialCommand(const char *c){ } break; + case 'V': { + + nvs_erase_all(charNVS); + nvs_commit(charNVS); + Serial.print("\n*** Values for all saved Characteristics erased!\n\n"); + } + break; + case 'H': { statusLED.off(); @@ -893,6 +901,7 @@ void Span::processSerialCommand(const char *c){ Serial.print(" O - change the OTA password\n"); Serial.print(" A - start the HomeSpan Setup Access Point\n"); Serial.print("\n"); + Serial.print(" V - delete value settings for all saved Characteristics\n"); Serial.print(" U - unpair device by deleting all Controller data\n"); Serial.print(" H - delete HomeKit Device ID as well as all Controller data and restart\n"); Serial.print("\n"); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index dc52ac4..b723080 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -380,6 +380,8 @@ struct SpanCharacteristic{ template void init(T val, boolean nvsStore, A min=0, B max=1){ + int nvsFlag=0; + uvSet(value,val); uvSet(newValue,val); uvSet(minValue,min); @@ -396,10 +398,12 @@ struct SpanCharacteristic{ if(!nvs_get_blob(homeSpan.charNVS,nvsKey,NULL,&len)){ nvs_get_blob(homeSpan.charNVS,nvsKey,&value,&len); newValue=value; + nvsFlag=2; } else { nvs_set_blob(homeSpan.charNVS,nvsKey,&value,sizeof(UVal)); // store data nvs_commit(homeSpan.charNVS); // commit to NVS + nvsFlag=1; } } @@ -407,8 +411,10 @@ struct SpanCharacteristic{ if(format!=STRING && format!=BOOL) homeSpan.configLog+= " Range=[" + String(uvPrint(minValue)) + "," + String(uvPrint(maxValue)) + "]"; - if(nvsStore) + if(nvsFlag==2) homeSpan.configLog+=" (restored)"; + else if(nvsFlag==1) + homeSpan.configLog+=" (storing)"; boolean valid=false; From e94a9bba04bb990e4565180df00d354a70fa73d4 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 18:40:54 -0500 Subject: [PATCH 35/55] Updating Example 18; and set HomeSpan version to 1.3.0 --- examples/18-SavingStatus/18-SavingStatus.ino | 121 ++++++------------- src/Settings.h | 4 +- 2 files changed, 36 insertions(+), 89 deletions(-) diff --git a/examples/18-SavingStatus/18-SavingStatus.ino b/examples/18-SavingStatus/18-SavingStatus.ino index f048b79..d96bac1 100644 --- a/examples/18-SavingStatus/18-SavingStatus.ino +++ b/examples/18-SavingStatus/18-SavingStatus.ino @@ -1,7 +1,7 @@ /********************************************************************************* * MIT License * - * Copyright (c) 2020 Gregg E. Berman + * Copyright (c) 2021 Gregg E. Berman * * https://github.com/HomeSpan/HomeSpan * @@ -30,8 +30,8 @@ // HomeSpan: A HomeKit implementation for the ESP32 // // ------------------------------------------------ // // // -// Example 15: Real PushButtons // -// * manually controlling a Dimmable LED // +// Example 18: Saving Characteristic Status in NVS // +// * saving the state of two dimmable LEDs // // // // // //////////////////////////////////////////////////////////// @@ -42,90 +42,34 @@ void setup() { - // In Example 14 we saw how to emulate a PushButton tile within HomeKit by automatically resetting a Characteristic so that - // it "turns off" after a short period of time. However, sometimes we want to be able to physically control a device with actual - // PushButtons (or momentary switches) that trigger an action, such as turning on a light or fan, or opening a garage door. - // Additionally, we want HomeKit to reflect any changes in the device as a result of such manual actions - HomeKit should know - // when the light has been turned on or off manually. + // In many of the prior examples we saw how Characteristics are initialized when first instantiated. You can either include an argument: + // + // new Characteristic::Brightness(25); + // + // in which case the value of the Brightness Characterisrtic is set to 25 when HomeSpan is powered up, or you can leave the argument blank: + // + // new Characteristic::Brightness(); + // + // in which case HomeSpan will apply a default value. - // One way to accomplish would be via custom code added to the loop() method of your derived Service that monitors the button, - // checks when it is pressed, debounces button noise, performs some actions when pressed, and informs HomeKit of the actions with - // the setVal() method. Or you can use HomeSpan's built-in SpanButton() object. + // These methods work fine, with the exception that if the HomeSpan device loses power, it will boot up according to the parameters above rather + // than remembering the state of each Characteristic after you've made any changes via the Home App or with any PushButtons. - // SpanButton() is a Service-level object, meaning it attaches itself to the last Service you define. Typically you would instantiate - // one of more SpanButton() objects directly inside the constructor for your derived Service. + // In this Example 18 we will see how to instruct HomeSpan to automatically save the values of one or more Characteristics in non-volatile storage + // so that they can be restored if the power is cycled. + + // To do so, we call the constructor for a Characteristic with TWO arguments as such: + // + // new Characteristic::Brightness(25, true); + // + // This instructs HomeSpan to set the Brightness to 25 the very first time the device is powered on, but to SAVE any changes to this Characteristic + // in NVS, AND RESTORE the last-saved value whenever the power is cycled. Though HomeSpan takes care of all the saving and restoring automatically for + // any Characteristic in which you set the second argument of the constructor to be "true," HomeSpan can't automatically perform any needed initialization + // of the physical appliance by itself. In other words, HomeSpan can set the value of the Brightness Characteristic to 55 on start-up, if that's the + // last value used before the power was cycled, but you'll need to add some code to set the brightness of the actual LED at startup. - // SpanButton() supports three types of a triggers: a SINGLE button press, a DOUBLE press, and a LONG (extended) press. - - // The length of the presses needed to trigger these different types can be specified by optional arguments to SpanButton(). - // Since most buttons create spurious noise when pressed (and then again when released), the default time to trigger a SINGLE press is 5ms. - // It's fine to change this to a longer value, but a shorter value is not recommended as this may allow spurious triggers unless - // you debounce your switch with hardware. - - // The SpanButton() constructor takes 4 arguments, in the following order: - // - // pin - the pin number to which the PushButton is attached (required) - // longTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a LONG press (optional; default=2000 ms) - // singleTime - the minimum length of time (in milliseconds) the button needs to be pushed to be considered a SINGLE press (optional; default=5 ms) - // doubleTime - the maximum length of time (in milliseconds) between button presses to create a DOUBLE press (optional; default=200 ms) - - // When a SpanButton() is instantiated, it sets the specified pin on the ESP32 to be an INPUT with PULL-UP, meaning that the pin will - // normally return a value of HIGH when read. Your actual PushButton should be connected so that this pin is GROUNDED when the button - // is pressed. - - // HomeSpan automatically polls all pins with associated SpanButton() objects and checks for LOW values, which indicates the button was - // pressed, but not yet released. It then starts a timer. If the button is released after being pressed for less than singleTime milliseconds, - // nothing happens. If the button is released after being pressed for more than singleTime milliseconds, but for less than longTime milliseconds, - // a SINGLE press is triggered, unless you press once again within doubleTime milliseconds to trigger a DOUBLE press. If the button is held for more - // than longTime milliseconds without being released, a LONG press is triggered. Once a LONG press is triggered the timer resets so that if you keep - // holding the button, another LONG press will be triggered in another longTime milliseconds. This continues until you finally release the button. - - // Note if you set longTime > singleTime, SpanButton() will only trigger LONG presses. Also, if you set doubleTime to zero, SpanButton() will not be - // able to trigger a DOUBLE press. - - // To use SpanButton() within a derived Service you need to implement a button() method. Similar to the loop() method, your button() - // method will typically contain some combination of getVal() functions and setVal() functions, along with code that performs some set - // of actions on the physical device (seting pins high or low, turning on fans, etc). However, in contrast to the loop() method, which - // is called by HomeSpan every polling cycle, HomeSpan only calls the button() method when a button attached to the Service registers a - // SINGLE, DOUBLE, or LONG press. - - // Also in contrast with the loop method, the button() method takes two 'int' arguments, and should defined as follows: - // - // void button(int pin, int pressType) - // - // where "pin" is the pin number of the PushButton that was triggered, and pressType is set to 0 for a SINGLE press, 1 for a DOUBLE press, - // and 2 for a LONG press. You can also use the pre-defined constants SpanButton::SINGLE, SpanButton::DOUBLE, and SpanButton::LONG in place - // of the numbers 0, 1, and 2 (this is recommended, though you will see in Example 16 why these integers can't be replaced by an C++ enum class). - - // Of course you can replace the variables "pin" and "pressType" with your own names. The only requirement is the definition conform to - // the "void button(int, int)" signature. When HomeSpan first starts up it checks all Services containing one or more SpanButton() instances to - // ensure you've implemented your own button(int, int) method. If not, HomeSpan will print a warning message on the Serial Monitor. Nothing bad - // happens if you instantiate a SpanButton() but forget to create the button() method, or you create it with the wrong parameters. But nothing good - // happens either - button presses are just ignored. - // - // C++ Note: For an extra check, you can also place the the contextual keyword "override" after your method definition as such: - // - // void button(int buttonPin, int pressType) override {...your code...} - // - // Doing so allows the compiler to check that you are indeed over-riding the base class button() method and not inadvertently creating a new - // button() method with an incorrect signature that will never be called by SpanButton(). In fact, you could add "override" to the definition - // of your update() and loop() methods as well, since these are always supposed to over-ride the base-class method. - - // To demonstrate how SpanButtons works in practice, we will implement a Dimmable LED starting with the same LED code use in Example 11, - // but with 3 SpanButton() objects performing different functions that showcase the different types of presses. - // - // * A "power" SpanButton that will toggle the power in response a SINGLE press, turn on the power and set the brightness to a "favorite" level - // in response to the DOUBLE press, and set a new "favorite" level in response to a LONG press. - // - // * A "raise brightness" SpanButton that will increase the brightness by 1% in response to a SINGLE press, repeatedly increase the brightness - // by 10% in response to a LONG press, and jump to the maximum brightness in response to a DOUBLE press. - // - // * A "lower brightness" SpanButton that will decrease the brightness by 1% in response to a SINGLE press, repeatedly decrease the brightness - // by 10% in response to a LONG press, and jump to the minimum brightness in response to a DOUBLE press. - - // As usual, all the code is implemented in DEV_LED.h, with NEW! comments highlighting changes from Example 11. You'll also notice that we've - // extended the constructor for this version of our derived Dimmable LED Service to include the pin numbers for each of our buttons. - // See DEV_LED.h for details. + // To see how this works in practice, we'll configure HomeSpan to operate two Dimmable LEDs, each with its own on/off PushButton. As usual, all the code + // is implemented in DEV_LED.h, with comments highlighting all the new features. See DEV_LED.h for full details. Serial.begin(115200); @@ -137,10 +81,13 @@ void setup() { new Characteristic::Version("1.1.0"); new SpanAccessory(); - new DEV_Identify("PushButton LED","HomeSpan","123-ABC","20mA LED","0.9",0); - - new DEV_DimmableLED(17,19); // NEW! added three extra arguments to specify the pin numbers for three SpanButtons() - see DEV_LED.h + new DEV_Identify("LED 1","HomeSpan","123-ABC","20mA LED","0.9",0); + new DEV_DimmableLED(17,19); // The first argument specifies the LED pin; the second argument specifies the PushButton pin + new SpanAccessory(); + new DEV_Identify("LED 2","HomeSpan","123-ABC","20mA LED","0.9",0); + new DEV_DimmableLED(16,18); // The first argument specifies the LED pin; the second argument specifies the PushButton pin + } // end of setup() ////////////////////////////////////// diff --git a/src/Settings.h b/src/Settings.h index f171e7f..ccf0323 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -33,8 +33,8 @@ // HomeSpan Version // #define HS_MAJOR 1 -#define HS_MINOR 2 -#define HS_PATCH 1 +#define HS_MINOR 3 +#define HS_PATCH 0 #define STRINGIFY(x) _STR(x) #define _STR(x) #x From ad11016b28849bd81095c43cf38120663f329920 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 13 Jun 2021 21:56:39 -0500 Subject: [PATCH 36/55] Completed Example 18 --- examples/18-SavingStatus/18-SavingStatus.ino | 32 +++++++++--- examples/18-SavingStatus/DEV_LED.h | 52 ++++++-------------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/examples/18-SavingStatus/18-SavingStatus.ino b/examples/18-SavingStatus/18-SavingStatus.ino index d96bac1..f9f8031 100644 --- a/examples/18-SavingStatus/18-SavingStatus.ino +++ b/examples/18-SavingStatus/18-SavingStatus.ino @@ -55,18 +55,18 @@ void setup() { // These methods work fine, with the exception that if the HomeSpan device loses power, it will boot up according to the parameters above rather // than remembering the state of each Characteristic after you've made any changes via the Home App or with any PushButtons. - // In this Example 18 we will see how to instruct HomeSpan to automatically save the values of one or more Characteristics in non-volatile storage - // so that they can be restored if the power is cycled. - - // To do so, we call the constructor for a Characteristic with TWO arguments as such: + // In this Example 18 we will see how to instruct HomeSpan to automatically save the values of one or more Characteristics in non-volatile storage (NVS) + // so that they can be restored to their latest state if the power is cycled. To do so, we call the constructor for a Characteristic with TWO arguments as such: // // new Characteristic::Brightness(25, true); // // This instructs HomeSpan to set the Brightness to 25 the very first time the device is powered on, but to SAVE any changes to this Characteristic - // in NVS, AND RESTORE the last-saved value whenever the power is cycled. Though HomeSpan takes care of all the saving and restoring automatically for - // any Characteristic in which you set the second argument of the constructor to be "true," HomeSpan can't automatically perform any needed initialization - // of the physical appliance by itself. In other words, HomeSpan can set the value of the Brightness Characteristic to 55 on start-up, if that's the - // last value used before the power was cycled, but you'll need to add some code to set the brightness of the actual LED at startup. + // in NVS, AND RESTORE the last-saved value whenever the power is cycled! + + // Note that though HomeSpan takes care of all the saving and restoring automatically for any Characteristic in which you set the second argument of + // the constructor to be "true," HomeSpan can't automatically perform any needed initialization of the physical appliance by itself. In other words, + // if you change the Brightness to 55 from the Home App and then sometime later the device loses power, HomeSpan will restore the value of the + // Brightness Characteristic to 55 on start-up, but you'll need to add some code to set the brightness of the actual LED once the value is restored. // To see how this works in practice, we'll configure HomeSpan to operate two Dimmable LEDs, each with its own on/off PushButton. As usual, all the code // is implemented in DEV_LED.h, with comments highlighting all the new features. See DEV_LED.h for full details. @@ -97,3 +97,19 @@ void loop(){ homeSpan.poll(); } // end of loop() + +////////////////////////////////////// + +// OPERATING NOTES +// +// When the values of Characteristics are saved in NVS, they are stored based on a unique key that combines the UUID of the Characteristic with its AID and IID. +// If you are actively developing a configuration, adding or subtracting a new SpanAccessory or SpanService can alter the AID and IID of other Characteristics whose +// values were already stored in the NVS. If the new UUID/AID/IID combination is unused, the previously-stored value will not be restored upon the very next +// start-up and instead the value specified in the first argument of the constructor will be used and stored in the NVS as the initial value. +// +// If the new UUID/AID/IID happens to match a combination that was previously used, the value of the Characteristic will restored to whatever is found under that key +// in the NVS. +// +// *** To clear all values stored in the NVS, type 'V' in the HomeSpan CLI. This ensures that there are no stray key/value pairs in the NVS from prior iterations of your +// configuration. +// diff --git a/examples/18-SavingStatus/DEV_LED.h b/examples/18-SavingStatus/DEV_LED.h index 870e801..04839b6 100644 --- a/examples/18-SavingStatus/DEV_LED.h +++ b/examples/18-SavingStatus/DEV_LED.h @@ -9,51 +9,35 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED - // This version of the Dimmable LED Service is similar to the one last used in Example 11, but now includes support for 3 physical PushButtons - // performing the following actions: - // - // power button: SHORT press toggles power on/off; LONG press saves current brightness as favorite level; DOUBLE press sets brightness to favorite level - // raise button: SHORT press increases brightness by 1%; LONG press increases brightness by 10%; DOUBLE press increases brightness to maximum - // lower button: SHORT press decreases brightness by 1%; LONG press decreases brightness by 10%; DOUBLE press decreases brightness to minimum + // This version of the Dimmable LED Service includes a PushButton that can be used to turn on/off the LED. Status of both the + // power state and the brightness of the LED are stored in NVS for restoration if the device reboots. - LedPin *ledPin; // reference to Led Pin - int powerPin; // NEW! pin with pushbutton to turn on/off LED + LedPin *LED; // reference to an LedPin SpanCharacteristic *power; // reference to the On Characteristic SpanCharacteristic *level; // reference to the Brightness Characteristic + + DEV_DimmableLED(int ledPin, int buttonPin) : Service::LightBulb(){ - // NEW! Consructor includes 3 additional arguments to specify pin numbers for power, raise, and lower buttons - - DEV_DimmableLED(int pin, int powerPin) : Service::LightBulb(){ - - power=new Characteristic::On(0,true); - - level=new Characteristic::Brightness(5,true); // Brightness Characteristic with an initial value equal to the favorite level + power=new Characteristic::On(0,true); // NEW! Second argument is true, so the value of the On Characteristic (initially set to 0) will be saved in NVS + level=new Characteristic::Brightness(5,true); // NEW! Second argument is true, so the value of the Brightness Characteristic (initially set to 5) will be saved in NVS level->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% - // NEW! Below we create three SpanButton() objects. In the first we specify the pin number, as required, but allow SpanButton() to use - // its default values for a LONG press (2000 ms), a SINGLE press (5 ms), and a DOUBLE press (200 ms). In the second and third we change the - // default LONG press time to 500 ms, which works well for repeatedly increasing or decreasing the brightness. - - // All of the logic for increasing/decreasing brightness, turning on/off power, and setting/resetting a favorite brightness level is found - // in the button() method below. + new SpanButton(buttonPin); // create a new SpanButton to control power using PushButton on pin number "buttonPin" - new SpanButton(powerPin); // NEW! create new SpanButton to control power using pushbutton on pin number "powerPin" - - this->powerPin=powerPin; // NEW! save power pushbutton pin number - this->ledPin=new LedPin(pin); // configures a PWM LED for output to the specified pin + this->LED=new LedPin(ledPin); // configures a PWM LED for output to pin number "ledPin" Serial.print("Configuring Dimmable LED: Pin="); // initialization message - Serial.print(ledPin->getPin()); + Serial.print(LED->getPin()); Serial.print("\n"); - ledPin->set(power->getVal()*level->getVal()); + LED->set(power->getVal()*level->getVal()); // NEW! IMPORTANT: Set the LED to its initial state at startup. Note we use getVal() here, since it is set upon instantiation. } // end constructor boolean update(){ // update() method LOG1("Updating Dimmable LED on pin="); - LOG1(ledPin->getPin()); + LOG1(LED->getPin()); LOG1(": Current Power="); LOG1(power->getVal()?"true":"false"); LOG1(" Current Brightness="); @@ -71,7 +55,7 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED LOG1("\n"); - ledPin->set(power->getNewVal()*level->getNewVal()); + LED->set(power->getNewVal()*level->getNewVal()); // update the physical LED to reflect the new values return(true); // return true @@ -79,15 +63,9 @@ struct DEV_DimmableLED : Service::LightBulb { // Dimmable LED void button(int pin, int pressType) override { - LOG1("Found button press on pin: "); // always a good idea to log messages - LOG1(pin); - LOG1(" type: "); - LOG1(pressType==SpanButton::LONG?"LONG":(pressType==SpanButton::SINGLE)?"SINGLE":"DOUBLE"); - LOG1("\n"); - - if(pin==powerPin && pressType==SpanButton::SINGLE){ + if(pressType==SpanButton::SINGLE){ // only respond to SINGLE presses power->setVal(1-power->getVal()); // toggle the value of the power Characteristic - ledPin->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values + LED->set(power->getVal()*level->getVal()); // update the physical LED to reflect the new values } } // button From 0a3dbb99dcef142a1b7db0d06431919243bdf7cc Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 13 Jun 2021 22:04:08 -0500 Subject: [PATCH 37/55] Update CLI.md --- docs/CLI.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/CLI.md b/docs/CLI.md index 72181b5..9460cdb 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -59,6 +59,9 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p * **A** - start the HomeSpan Setup Access Point * This command starts HomeSpan's temporary Access Point, which provides users with an alternate methods for configuring a device's WiFi Credentials and HomeKit Setup Code. Starting the Access Point with this command is identical to starting it via the Control Button. See the [HomeSpan User Guide](UserGuide.md) for complete details. +* **V** - delete value settings for all saved Characteristics + * The state of Characteristics can be saved in the device's NVS for restoration if the power cycles. This command is used to delete all saved data and is useful to run in the event saved Characteristics become out-of-sync with their stored values during the development phase of your sketch when you are adding new Accessories, Services, and Characteristics. + * **U** - unpair device by deleting all Controller data * This deletes all data stored about Controllers that have been paired with the device, which forces HomeSpan to reset its internal state to unpaired. Normally, unpairing is done by HomeKit at the direction of an end-user via the Home App on an iPhone. However, HomeKit requests to unpair a device are not subject to any confirmation from that device. HomeKit simply assumes that once it requests a device to unpair, the device has received the message and has reset its pairing state accordingly. In the event that HomeKit unpairs a HomeSpan device, but the device does not receive or properly process the request, its pairing status will be out of sync with HomeKit. Forcing HomeKit to reset its internal state to unpaired using this command resolves the issue and allows HomeSpan to be re-paired with HomeKit. * Note that if you run this command when HomeKit thinks it is still paired to the device, pairing status will be out of sync in the opposite direction. HomeKit Controllers will continue to send HAP requests to the device, thinking it is paired, but HomeSpan will ignore all these requests since it no longer recognizes any of the Controllers as being paired. To resolve this issue, you must instruct HomeKit to unpair the device via the Home App, after which you can re-pair the device if needed. From 765767573fc402417d04817d80000e0f7e11711d Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 13 Jun 2021 22:13:46 -0500 Subject: [PATCH 38/55] Update Tutorials.md --- docs/Tutorials.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/Tutorials.md b/docs/Tutorials.md index 08ca70b..c8796c3 100644 --- a/docs/Tutorials.md +++ b/docs/Tutorials.md @@ -92,6 +92,11 @@ Example 17 introduces the HAP concept of Linked Services and demonstrates how th * creating Linked Services using the `addLink()` method +### [Example 18 - SavingStatus](../examples/18-SavingStatus) +Example 18 demonstrates, through the implementaton of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is power-cycled. New HomeSpan API topics covered in this example include: + +* instructing HomeSpan to store the value of a Characteristic in NVS by setting the second parameter of the constuctor to `true` + --- [↩️](README.md) Back to the Welcome page From e90744910612750e1d98d86e42e265dfaf06878d Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 14 Jun 2021 05:34:04 -0500 Subject: [PATCH 39/55] Update CLI.md --- docs/CLI.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index 9460cdb..5510d42 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -59,8 +59,8 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p * **A** - start the HomeSpan Setup Access Point * This command starts HomeSpan's temporary Access Point, which provides users with an alternate methods for configuring a device's WiFi Credentials and HomeKit Setup Code. Starting the Access Point with this command is identical to starting it via the Control Button. See the [HomeSpan User Guide](UserGuide.md) for complete details. -* **V** - delete value settings for all saved Characteristics - * The state of Characteristics can be saved in the device's NVS for restoration if the power cycles. This command is used to delete all saved data and is useful to run in the event saved Characteristics become out-of-sync with their stored values during the development phase of your sketch when you are adding new Accessories, Services, and Characteristics. +* **V** - erases the values of any saved Characteristics + * As Characteristics are updated via the Home App, their latest values can be (optionally) saved in the device's non-volatile storage (NVS). If the device should ever lose power this allows HomeSpan to restore the latest values of saved Characteristic upon the next start-up. Typing 'V' from the CLI deletes all previously-saved Characteristic values from the NVS, though it does not alter the current values of those Characteristics. This is useful in the event that saved Characteristics become out-of-sync with their stored values during the development phase of your sketch when adding, deleting, and changing the configuration of new Accessories, Services, and Characteristics. * **U** - unpair device by deleting all Controller data * This deletes all data stored about Controllers that have been paired with the device, which forces HomeSpan to reset its internal state to unpaired. Normally, unpairing is done by HomeKit at the direction of an end-user via the Home App on an iPhone. However, HomeKit requests to unpair a device are not subject to any confirmation from that device. HomeKit simply assumes that once it requests a device to unpair, the device has received the message and has reset its pairing state accordingly. In the event that HomeKit unpairs a HomeSpan device, but the device does not receive or properly process the request, its pairing status will be out of sync with HomeKit. Forcing HomeKit to reset its internal state to unpaired using this command resolves the issue and allows HomeSpan to be re-paired with HomeKit. From 61db84ece7e1f454f34cc06cb446dc95363487e5 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 14 Jun 2021 05:35:31 -0500 Subject: [PATCH 40/55] Update Tutorials.md --- docs/Tutorials.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Tutorials.md b/docs/Tutorials.md index c8796c3..76b25d0 100644 --- a/docs/Tutorials.md +++ b/docs/Tutorials.md @@ -93,7 +93,7 @@ Example 17 introduces the HAP concept of Linked Services and demonstrates how th * creating Linked Services using the `addLink()` method ### [Example 18 - SavingStatus](../examples/18-SavingStatus) -Example 18 demonstrates, through the implementaton of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is power-cycled. New HomeSpan API topics covered in this example include: +Example 18 demonstrates, through the implementaton of two Dimmable LEDs, how the values of Characteristics can be automatically saved in the device's non-volatile storage (NVS) for restoration upon start-up if the device is loses power. New HomeSpan API topics covered in this example include: * instructing HomeSpan to store the value of a Characteristic in NVS by setting the second parameter of the constuctor to `true` From 22050532a9c90cc5279101f1580c5c1912383acd Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 14 Jun 2021 06:08:44 -0500 Subject: [PATCH 41/55] Update Reference.md --- docs/Reference.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index 76589c6..d24d9b8 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -148,15 +148,19 @@ The following methods are supported: * 1=double press (SpanButton::DOUBLE) * 2=long press (SpanButton::LONG) -## *SpanCharacteristic(value)* +## *SpanCharacteristic(value [,boolean nvsStore])* This is a **base class** from which all HomeSpan Characteristics are derived, and should **not** be directly instantiated. Rather, to create a new Characteristic instantiate one of the HomeSpan Characteristics defined in the [Characteristic](ServiceList.md) namespace. * instantiated Characteristics are added to the HomeSpan HAP Database and associated with the last Service instantiated * instantiating a Characteristic without first instantiating a Service throws an error during initialization -* a single, optional argument is used to set the initial value of the Characteristic at startup -* throws a runtime warning if value is outside of the min/max range for the Characteristic, where min/max is either the HAP default, or any new values set via a call to `setRange()` -* example: `new Characteristic::Brightness(50);` +* the first argument optionally allows you to set the initial *value* of the Characteristic at startup. If *value* is not specified, HomeSpan will supply a reasonable default for the Characteristic +* throws a runtime warning if *value* is outside of the min/max range for the Characteristic, where min/max is either the HAP default, or any new values set via a call to `setRange()` +* the second optional argument, if set to `true`, instructs HomeSpan to save updates to this Characteristic's value in the device's non-volative storage (NVS) for restoration at startup if the device should lose power. If not specified, *nvsStore* will default to `false` (no storage) +* examples: + * `new Characteristic::Brightness();` Brightness initialized to default value + * `new Characteristic::Brightness(50);` Brightness initialized to 50 + * `new Characteristic::Brightness(50,true);` Brightness initialized to 50; updates saved in NVS The following methods are supported: From c91d5984ac8129bc67e1d629148211d9e1cd6d06 Mon Sep 17 00:00:00 2001 From: Gregg Date: Mon, 14 Jun 2021 21:35:26 -0500 Subject: [PATCH 42/55] Changed version in library.properties to 1.3.0 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 162b33d..2ac5483 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=HomeSpan -version=1.2.1 +version=1.3.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 f42e8740a2f8b02154c1e602a7227e1d08873fba Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 14 Jun 2021 21:42:17 -0500 Subject: [PATCH 43/55] Update README.md --- docs/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index c12ef6f..012ede7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A * 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 -* 17 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks +* 18 detailed tutorial-sketches with extensive comments, HomeSpan documentation and tips and tricks ### For the HomeSpan End-User @@ -48,7 +48,6 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A HomeSpan includes the following documentation: * [Getting Started with HomeSpan](https://github.com/HomeSpan/HomeSpan/blob/master/docs/GettingStarted.md) - setting up the software and the hardware needed to develop HomeSpan devices -* [HomeKit Primer](https://github.com/HomeSpan/HomeSpan/blob/master/docs/HomeKitPrimer.md) - a gentle introduction to Apple HomeKit and HAP terminology :construction: * [HomeSpan API Overview](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Overview.md) - an overview of the HomeSpan API, including a step-by-step guide to developing your first HomeSpan Sketch * [HomeSpan Tutorials](https://github.com/HomeSpan/HomeSpan/blob/master/docs/Tutorials.md) - a guide to HomeSpan's tutorial-sketches * [HomeSpan Services and Characteristics](https://github.com/HomeSpan/HomeSpan/blob/master/docs/ServiceList.md) - a list of all HAP Services and Characterstics supported by HomeSpan From 835c647a4f32ef827c58651172ad94d57f72e9f9 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Mon, 14 Jun 2021 21:44:17 -0500 Subject: [PATCH 44/55] 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 c637d00..0fc8294 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -56,7 +56,7 @@ The use of these two components to configure a standalone HomeSpan device, inclu ## What Next? -If you've not yet read through the [HomeSpan API Overview](Overview.md) page, you should do so now so you'll have a good understanding of the framework for developing HomeSpan sketches. If you are new to HomeKit development, you may also want to read through HomeSpan's [HomeKit Primer](HomeKitPrimer.md), which should provide you with some important foundational knowledge about HomeKit itself. +If you've not yet read through the [HomeSpan API Overview](Overview.md) page, you should do so now so you'll have a good understanding of the framework for developing HomeSpan sketches. Next, explore the tutorial sketches, upload a few, and see how they work. The examples start simple and grow in complexity, taking you through all the functions and features of HomeSpan. Along the way you'll also learn a lot of HomeKit tips and tricks. See [HomeSpan Tutorials](Tutorials.md) for a summary of all the included examples. Find something in a sketch you don't understand? Visit the [HomeSpan API Reference](Reference.md) for details on all HomeSpan objects, functions, and methods. Have a more general question? See if it's been answered on the [HomeSpan FAQ](FAQ.md) page or any of the [Disussion](https://github.com/HomeSpan/HomeSpan/discussions) or [Issues](https://github.com/HomeSpan/HomeSpan/issues) pages. If not, feel free to join the Discusion by adding a new question. From 67fd691edef9e2c5a15ef979f79aef6800759720 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Tue, 15 Jun 2021 22:00:08 -0500 Subject: [PATCH 45/55] Update README.md --- docs/README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 012ede7..23e3f4d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,9 +39,18 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A * Launch the WiFi Access Point * A standalone, detailed End-User Guide -## Latest Update (3/13/2021) +## Latest Update - HomeSpan 1.3.0 (6/16/2021) -* HomeSpan 1.2.1 - This update adds run-time range-checking for all Characteristics and will warn you if the initial value you set for a Characteristic, or any subsequent changes you make to that value, are outside the Characteristic's allowable min/max range. This helps diagnosis "No Response" errors in the Home App. This update also introduces `setRange(min,max,step)` as a new and more robust method for changing a Characteristic's range. See [Release](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update. +This update brings a number of new features and enhancements: + + * The PWM library has been— + * upgraded to allow for much easier control of up to 16 dimmable LEDs, ***and*** + * extended with a dedicated class to simultaneously operate up to 8 Servo Motors! + * Characteristic values can be automatically saved in non-volatile storage for retention in the event of a power loss. When power is restored your Accessories will automatically revert to their most recent state! + * The HomeSpan CLI can now be customized — extend the CLI with your own functions and commands! + * Enable the automatic launch of HomeSpan's WiFi Access Point upon start-up whenever WiFi Credentials are not found. + +See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update. # HomeSpan Resources From b53081dfc75644e3025eea38e283e03e54cc57f5 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sat, 19 Jun 2021 08:39:55 -0500 Subject: [PATCH 46/55] Update CLI.md --- docs/CLI.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/CLI.md b/docs/CLI.md index 5510d42..348f798 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -85,8 +85,14 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p * 0 (minimal diagnostics), * 1 (all diagnostics), and * 2 (all diagnostics plus a real-time stream of all HAP communication between HomeSpan and any connected HomeKit Controllers). + +* **?** - prints a menu of all CLI commands + +### User-Defined Commands - --- +You can extend the HomeSpan CLI with custom functions using `SpanUserCommand()`. This class allows you to assign a single-character name to any custom function that will be called when you type the '@' symbol following by the single-character name into the CLI. For example, if you assigned the character 'K' to a custom function, you would type '@K' into the CLI to invoke it. This allows you to use any single-character name, even if that character is already used by HomeSpan for its built-in commands. The `SpanUserCommand` class also allows you to include a short text description of your function that will be added to the menu of commands when you type '?' into the CLI. See the the [API Reference](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/Reference.md#spanusercommandchar-c-const-char-s-void-fconst-char-v) for full details. + +--- [↩️](README.md) Back to the Welcome page From e3d081bb35138e928bef0c77695ac80523d00f36 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 19 Jun 2021 21:36:18 -0500 Subject: [PATCH 47/55] updated enableAutoStartAP to accept user-define function as argument --- src/HomeSpan.cpp | 8 ++++++-- src/HomeSpan.h | 4 +++- src/src.ino | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 381f1e3..a2a5c40 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -147,8 +147,12 @@ void Span::poll() { if(!strlen(network.wifiData.ssid)){ Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. "); if(autoStartAPEnabled){ - Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); - processSerialCommand("A"); + if(apFunction){ + apFunction(); + } else { + Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); + processSerialCommand("A"); + } } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); statusLED.start(LED_WIFI_NEEDED); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index b723080..a951077 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -126,6 +126,7 @@ struct Span{ boolean otaAuth; // OTA requires password when set to true void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found + void (*apFunction)()=NULL; // optional function to invoke when using enableAutoStartAP() WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status @@ -180,7 +181,8 @@ 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 enableAutoStartAP(){autoStartAPEnabled=true;} // enables auto start-up of Access Point when WiFi Credentials not found + + void enableAutoStartAP(void (*f)()=NULL){autoStartAPEnabled=true;apFunction=f;} // enables auto start-up of Access Point when WiFi Credentials not found (will call optional f, if specified) }; /////////////////////////////// diff --git a/src/src.ino b/src/src.ino index 9e67e91..905bb71 100644 --- a/src/src.ino +++ b/src/src.ino @@ -21,7 +21,7 @@ void setup() { new SpanUserCommand('d',"My Description",userCom1); new SpanUserCommand('d',"My second Description",userCom2); -// homeSpan.enableAutoStartAP(); +// homeSpan.enableAutoStartAP(myWiFiAP); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); @@ -60,6 +60,12 @@ void loop(){ ////////////////////////////////////// +void myWiFiAP(){ + Serial.print("Calling My WIFI AP\n\n"); +} + +////////////////////////////////////// + void wifiEstablished(){ Serial.print("IN CALLBACK FUNCTION\n\n"); } From fc01e375904ebdd5a1a317bb8e524341f7359a9f Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 06:37:09 -0500 Subject: [PATCH 48/55] Created setWifiCredentials() method To be used with user-define Access Point, but CAN be used to programmatically hardcode SSID and PASSWORD (not a good idea to do this!) --- src/HAP.cpp | 5 ----- src/HAP.h | 1 - src/HomeSpan.cpp | 49 +++++++++++++++++++++++++++++++++++++++--------- src/HomeSpan.h | 2 ++ src/src.ino | 3 ++- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/HAP.cpp b/src/HAP.cpp index 66512ea..6f1f2c1 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -38,14 +38,10 @@ void HAPClient::init(){ size_t len; // not used but required to read blobs from NVS - nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS nvs_open("SRP",NVS_READWRITE,&srpNVS); // open SRP data namespace in NVS nvs_open("HAP",NVS_READWRITE,&hapNVS); // open HAP data namespace in NVS nvs_open("OTA",NVS_READWRITE,&otaNVS); // open OTA data namespace in NVS - if(!nvs_get_blob(wifiNVS,"WIFIDATA",NULL,&len)) // if found WiFi data in NVS - nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data - if(!nvs_get_str(otaNVS,"OTADATA",NULL,&len)){ // if found OTA data in NVS nvs_get_str(otaNVS,"OTADATA",homeSpan.otaPwd,&len); // retrieve data } else { @@ -1650,7 +1646,6 @@ void Nonce::inc(){ TLV HAPClient::tlv8; nvs_handle HAPClient::hapNVS; -nvs_handle HAPClient::wifiNVS; nvs_handle HAPClient::srpNVS; nvs_handle HAPClient::otaNVS; uint8_t HAPClient::httpBuf[MAX_HTTP+1]; diff --git a/src/HAP.h b/src/HAP.h index 23c3f96..1d48e89 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -79,7 +79,6 @@ struct HAPClient { static TLV tlv8; // TLV8 structure (HAP Section 14.1) with space for 10 TLV records of type kTLVType (HAP Table 5-6) static nvs_handle hapNVS; // handle for non-volatile-storage of HAP data - static nvs_handle wifiNVS; // handle for non-volatile-storage of WiFi data static nvs_handle srpNVS; // handle for non-volatile-storage of SRP data static nvs_handle otaNVS; // handle for non-volatile-storage of OTA data static uint8_t httpBuf[MAX_HTTP+1]; // buffer to store HTTP messages (+1 to leave room for storing an extra 'overflow' character) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index a2a5c40..2b86399 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -66,7 +66,18 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa hapServer=new WiFiServer(tcpPortNum); nvs_flash_init(); // initialize non-volatile-storage partition in flash - nvs_open("CHAR",NVS_READWRITE,&charNVS); + nvs_open("CHAR",NVS_READWRITE,&charNVS); // open Characteristic data namespace in NVS + nvs_open("WIFI",NVS_READWRITE,&wifiNVS); // open WIFI data namespace in NVS + + size_t len; + + if(strlen(network.wifiData.ssid)){ // if setWifiCredentials was already called + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS + } else + + if(!nvs_get_blob(wifiNVS,"WIFIDATA",NULL,&len)) // else if found WiFi data in NVS + nvs_get_blob(wifiNVS,"WIFIDATA",&homeSpan.network.wifiData,&len); // retrieve data delay(2000); @@ -739,9 +750,16 @@ void Span::processSerialCommand(const char *c){ case 'W': { + if(strlen(network.wifiData.ssid)>0){ + Serial.print("*** Stopping all current WiFi services...\n\n"); + hapServer->end(); + MDNS.end(); + WiFi.disconnect(); + } + network.serialConfigure(); - nvs_set_blob(HAPClient::wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data - nvs_commit(HAPClient::wifiNVS); // commit to NVS + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** WiFi Credentials SAVED! Re-starting ***\n\n"); statusLED.off(); delay(1000); @@ -759,8 +777,8 @@ void Span::processSerialCommand(const char *c){ } network.apConfigure(); - nvs_set_blob(HAPClient::wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data - nvs_commit(HAPClient::wifiNVS); // commit to NVS + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS Serial.print("\n*** Credentials saved!\n\n"); if(strlen(network.setupCode)){ char s[10]; @@ -780,8 +798,8 @@ void Span::processSerialCommand(const char *c){ case 'X': { statusLED.off(); - nvs_erase_all(HAPClient::wifiNVS); - nvs_commit(HAPClient::wifiNVS); + nvs_erase_all(wifiNVS); + nvs_commit(wifiNVS); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); delay(1000); ESP.restart(); // re-start device @@ -821,8 +839,10 @@ void Span::processSerialCommand(const char *c){ statusLED.off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); - nvs_erase_all(HAPClient::wifiNVS); - nvs_commit(HAPClient::wifiNVS); + nvs_erase_all(wifiNVS); + nvs_commit(wifiNVS); + nvs_erase_all(charNVS); + nvs_commit(charNVS); Serial.print("\n*** FACTORY RESET! Restarting...\n\n"); delay(1000); ESP.restart(); @@ -948,6 +968,17 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// +void Span::setWifiCredentials(char *ssid, char *pwd){ + sprintf(network.wifiData.ssid,"%.*s",MAX_SSID,ssid); + sprintf(network.wifiData.pwd,"%.*s",MAX_PWD,pwd); + if(wifiNVS){ // is begin() already called and wifiNVS is open + nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data + nvs_commit(wifiNVS); // commit to NVS + } +} + +/////////////////////////////// + int Span::sprintfAttributes(char *cBuf){ int nBytes=0; diff --git a/src/HomeSpan.h b/src/HomeSpan.h index a951077..382caf7 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -108,6 +108,7 @@ struct Span{ HapQR qrCode; // optional QR Code to use for pairing const char *sketchVersion="n/a"; // version of the sketch nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data + nvs_handle wifiNVS=NULL; // handle for non-volatile-storage of WiFi data boolean connected=false; // WiFi connection status unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -183,6 +184,7 @@ struct Span{ void setWifiCallback(void (*f)()){wifiCallback=f;} // sets an optional user-defined function to call once WiFi connectivity is established void enableAutoStartAP(void (*f)()=NULL){autoStartAPEnabled=true;apFunction=f;} // enables auto start-up of Access Point when WiFi Credentials not found (will call optional f, if specified) + void setWifiCredentials(char *ssid, char *pwd); // sets WiFi Credentials }; /////////////////////////////// diff --git a/src/src.ino b/src/src.ino index 905bb71..df77f94 100644 --- a/src/src.ino +++ b/src/src.ino @@ -21,7 +21,7 @@ void setup() { new SpanUserCommand('d',"My Description",userCom1); new SpanUserCommand('d',"My second Description",userCom2); -// homeSpan.enableAutoStartAP(myWiFiAP); + homeSpan.enableAutoStartAP(myWiFiAP); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); @@ -62,6 +62,7 @@ void loop(){ void myWiFiAP(){ Serial.print("Calling My WIFI AP\n\n"); + homeSpan.setWifiCredentials("MY_NETWORK","MY_PASSWORD"); } ////////////////////////////////////// From 7b66f61fbf468244c8045151e10698fab3c9357c Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 09:57:20 -0500 Subject: [PATCH 49/55] Update Reference.md --- docs/Reference.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index d24d9b8..c5acb36 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -72,15 +72,22 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * the HomeSpan default is "HSPN" unless permanently changed for the device via the [HomeSpan CLI](CLI.md) using the 'Q' command * *id* must be exactly 4 alphanumeric characters (0-9, A-Z, and a-z). If not, the request to change the Setup ID is silently ignored and the default is used instead +The following **optional** `homeSpan` methods enables additional features and provides for further customization of the HomeSpan environment: + * `void enableOTA(boolean auth=true)` * enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled * HomeSpan OTA requires an authorizing password unless *auth* is specified and set to *false* * 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 -* `void enableAutoStartAP()` +* `void enableAutoStartAP(void (*func)()=NULL)` * enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time - * methods to alter the behavior the Access Point, such as `setApTimeout()` must be called prior to `enableAutoStartAP()` to have an effect + * *func* is typically left unspecified (or set to NULL). This causes HomeSpan to call its built-in WiFi Access Point, exactly as if you had typed 'A' into the CLI + * methods to alter the behavior HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect + * advanced users can bypass HomeSpan's built-in Access Point and instead call their own Access Point code by specifying the function *func* + * *func* must be of type *void* and have no arguments + * after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values + * it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved * `void setSketchVersion(const char *sVer)` * sets the version of a HomeSpan sketch to *sVer*, which can be any arbitrary character string @@ -91,7 +98,7 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * `const char *getSketchVersion()` * returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set -* `void setWifiCallback(void (*func)(void))` +* `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 ## *SpanAccessory(uint32_t aid)* From 9273304fde665d4a4078fd734834d465399fccc3 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 10:30:44 -0500 Subject: [PATCH 50/55] Update Reference.md --- docs/Reference.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index c5acb36..cb6ba8d 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -89,6 +89,16 @@ The following **optional** `homeSpan` methods enables additional features and pr * after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values * it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved +* `void setWifiCredentials(const char *ssid, const char *pwd)` + * sets the SSID (*ssid*) and password (*pwd*) of the WiFi network to which HomeSpan will connect + * *ssid* and *pwd* are automatically saved in HomeSpan's non-volatile storage (NVS) for retrieval when the device restarts + * note that the saved values are truncated if they exceed the maximum allowable characters (ssid=32; pwd=64) + +> :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 via `enableAutoStartAP(func)` or with some other method. 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 by typing 'A' from the CLI, 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 + * `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 @@ -97,10 +107,7 @@ The following **optional** `homeSpan` methods enables additional features and pr * `const char *getSketchVersion()` * returns the version of a HomeSpan sketch, as set using `void setSketchVersion(const char *sVer)`, or "n/a" if not set - -* `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 - + ## *SpanAccessory(uint32_t aid)* Creating an instance of this **class** adds a new HAP Accessory to the HomeSpan HAP Database. From 6d77e2559a8ceb1be2c4eb456a6d0167b727bbb5 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 20 Jun 2021 15:20:56 -0500 Subject: [PATCH 51/55] Added setApFunction(); simplified enableAutoStartAP() Simplified enableAutoStartAP() so it no longer takes any arguments. It now simply enables the auto launch of the WiFi Access Point at start-up if WiFi Credentials are not found. New method setApFunction() will now be used to set an alternative method for the WiFi Access Point. This will be called anytime 'A' is typed into the CLI, which also covers the auto-launch of the AP at start-up a well as starting it via the Control Button, since both of these functions call processCommand('A'). --- src/HomeSpan.cpp | 15 ++++++++------- src/HomeSpan.h | 11 ++++++----- src/src.ino | 3 ++- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 2b86399..4c4480d 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -158,12 +158,8 @@ void Span::poll() { if(!strlen(network.wifiData.ssid)){ Serial.print("*** WIFI CREDENTIALS DATA NOT FOUND. "); if(autoStartAPEnabled){ - if(apFunction){ - apFunction(); - } else { - Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); - processSerialCommand("A"); - } + Serial.print("AUTO-START OF ACCESS POINT ENABLED...\n\n"); + processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); statusLED.start(LED_WIFI_NEEDED); @@ -775,6 +771,11 @@ void Span::processSerialCommand(const char *c){ MDNS.end(); WiFi.disconnect(); } + + if(apFunction){ + apFunction(); + return; + } network.apConfigure(); nvs_set_blob(wifiNVS,"WIFIDATA",&network.wifiData,sizeof(network.wifiData)); // update data @@ -968,7 +969,7 @@ void Span::processSerialCommand(const char *c){ /////////////////////////////// -void Span::setWifiCredentials(char *ssid, char *pwd){ +void Span::setWifiCredentials(const char *ssid, const char *pwd){ sprintf(network.wifiData.ssid,"%.*s",MAX_SSID,ssid); sprintf(network.wifiData.pwd,"%.*s",MAX_PWD,pwd); if(wifiNVS){ // is begin() already called and wifiNVS is open diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 382caf7..d1e5b14 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -108,7 +108,7 @@ struct Span{ HapQR qrCode; // optional QR Code to use for pairing const char *sketchVersion="n/a"; // version of the sketch nvs_handle charNVS; // handle for non-volatile-storage of Characteristics data - nvs_handle wifiNVS=NULL; // handle for non-volatile-storage of WiFi data + nvs_handle wifiNVS=0; // handle for non-volatile-storage of WiFi data boolean connected=false; // WiFi connection status unsigned long waitTime=60000; // time to wait (in milliseconds) between WiFi connection attempts @@ -127,8 +127,8 @@ struct Span{ boolean otaAuth; // OTA requires password when set to true void (*wifiCallback)()=NULL; // optional callback function to invoke once WiFi connectivity is established boolean autoStartAPEnabled=false; // enables auto start-up of Access Point when WiFi Credentials not found - void (*apFunction)()=NULL; // optional function to invoke when using enableAutoStartAP() - + void (*apFunction)()=NULL; // optional function to invoke when starting Access Point + WiFiServer *hapServer; // pointer to the HAP Server connection Blinker statusLED; // indicates HomeSpan status PushButton controlButton; // controls HomeSpan configuration and resets @@ -182,9 +182,10 @@ 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 setApFunction(void (*f)()){apFunction=f;} // sets an optional user-defined function to call when activating the WiFi Access Point - void enableAutoStartAP(void (*f)()=NULL){autoStartAPEnabled=true;apFunction=f;} // enables auto start-up of Access Point when WiFi Credentials not found (will call optional f, if specified) - void setWifiCredentials(char *ssid, char *pwd); // sets WiFi Credentials + 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 }; /////////////////////////////// diff --git a/src/src.ino b/src/src.ino index df77f94..2a2a0a8 100644 --- a/src/src.ino +++ b/src/src.ino @@ -21,7 +21,8 @@ void setup() { new SpanUserCommand('d',"My Description",userCom1); new SpanUserCommand('d',"My second Description",userCom2); - homeSpan.enableAutoStartAP(myWiFiAP); +// homeSpan.enableAutoStartAP(); +// homeSpan.setApFunction(myWiFiAP); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); From 2d36bb7a8151084d9ba6e3fd32822fa974f73ab8 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 20 Jun 2021 15:30:25 -0500 Subject: [PATCH 52/55] Update src.ino --- src/src.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/src.ino b/src/src.ino index 2a2a0a8..60385c0 100644 --- a/src/src.ino +++ b/src/src.ino @@ -18,11 +18,11 @@ void setup() { homeSpan.setSketchVersion("Test 1.3.1"); homeSpan.setWifiCallback(wifiEstablished); - new SpanUserCommand('d',"My Description",userCom1); - new SpanUserCommand('d',"My second Description",userCom2); + new SpanUserCommand('d',"- My Description",userCom1); + new SpanUserCommand('e',"- My second Description",userCom2); -// homeSpan.enableAutoStartAP(); -// homeSpan.setApFunction(myWiFiAP); + homeSpan.enableAutoStartAP(); + homeSpan.setApFunction(myWiFiAP); homeSpan.begin(Category::Lighting,"HomeSpan Lamp Server","homespan"); From 25fdfe82ba3040ecfc39773894eb1a82f3caad24 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 15:53:07 -0500 Subject: [PATCH 53/55] Update Reference.md --- docs/Reference.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/Reference.md b/docs/Reference.md index cb6ba8d..ee783a1 100644 --- a/docs/Reference.md +++ b/docs/Reference.md @@ -24,6 +24,8 @@ At runtime this HomeSpan will create a global **object** named `homeSpan` that s * checks for HAP requests, local commands, and device activity * **must** be called repeatedly in each sketch and is typically placed at the top of the Arduino `loop()` method +--- + The following **optional** `homeSpan` methods override various HomeSpan initialization parameters used in `begin()`, and therefore **should** be called before `begin()` to take effect. If a method is *not* called, HomeSpan uses the default parameter indicated below: * `void setControlPin(uint8_t pin)` @@ -72,7 +74,9 @@ The following **optional** `homeSpan` methods override various HomeSpan initiali * the HomeSpan default is "HSPN" unless permanently changed for the device via the [HomeSpan CLI](CLI.md) using the 'Q' command * *id* must be exactly 4 alphanumeric characters (0-9, A-Z, and a-z). If not, the request to change the Setup ID is silently ignored and the default is used instead -The following **optional** `homeSpan` methods enables additional features and provides for further customization of the HomeSpan environment: +--- + +The following **optional** `homeSpan` methods enable additional features and provide for further customization of the HomeSpan environment: * `void enableOTA(boolean auth=true)` * enables [Over-the-Air (OTA) Updating](OTA.md) of a HomeSpan device, which is otherwise disabled @@ -80,21 +84,26 @@ The following **optional** `homeSpan` methods enables additional features and pr * 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 -* `void enableAutoStartAP(void (*func)()=NULL)` +* `void enableAutoStartAP()` * enables automatic start-up of WiFi Access Point if WiFi Credentials are **not** found at boot time - * *func* is typically left unspecified (or set to NULL). This causes HomeSpan to call its built-in WiFi Access Point, exactly as if you had typed 'A' into the CLI - * methods to alter the behavior HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect - * advanced users can bypass HomeSpan's built-in Access Point and instead call their own Access Point code by specifying the function *func* - * *func* must be of type *void* and have no arguments - * after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values - * it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved + * methods to alter the behavior of HomeSpan's Access Point, such as `setApTimeout()`, must be called prior to `enableAutoStartAP()` to have an effect + +* `void setApFunction(void (*func)())` + * replaces HomeSpan's built-in WiFi Access Point with user-defined function *func* + * *func* must be of type *void* and have no arguments + * *func* will be called instead of HomeSpan's built-in WiFi Access Point whenever the Access Point is launched: + * via the CLI by typing 'A', or + * via the Control Button using option 3 of the Command Mode, or + * automatically upon start-up if `enableAutoStartAP()` is set and there are no stored WiFi Credentials + * after identifying the SSID and password of the desired network, *func* must call `setWifiCredentials()` to save and use these values + * it is recommended that *func* terminates by restarting the device using `ESP.restart()`. Upon restart HomeSpan will use the SSID and password just saved * `void setWifiCredentials(const char *ssid, const char *pwd)` * sets the SSID (*ssid*) and password (*pwd*) of the WiFi network to which HomeSpan will connect * *ssid* and *pwd* are automatically saved in HomeSpan's non-volatile storage (NVS) for retrieval when the device restarts * note that the saved values are truncated if they exceed the maximum allowable characters (ssid=32; pwd=64) -> :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 via `enableAutoStartAP(func)` or with some other method. 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 by typing 'A' from the CLI, to set your WiFi credentials without hardcoding them into your sketch. +> :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 by typing 'A' from the CLI, 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 From 910eedcecd4df996b4f6a1230167e40784244844 Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 16:22:07 -0500 Subject: [PATCH 54/55] Update README.md --- docs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 23e3f4d..09a3ed8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,7 +39,7 @@ HomeSpan provides a microcontroller-focused implementation of [Apple's HomeKit A * Launch the WiFi Access Point * A standalone, detailed End-User Guide -## Latest Update - HomeSpan 1.3.0 (6/16/2021) +## Latest Update - HomeSpan 1.3.0 (6/20/2021) This update brings a number of new features and enhancements: @@ -49,6 +49,7 @@ This update brings a number of new features and enhancements: * Characteristic values can be automatically saved in non-volatile storage for retention in the event of a power loss. When power is restored your Accessories will automatically revert to their most recent state! * The HomeSpan CLI can now be customized — extend the CLI with your own functions and commands! * Enable the automatic launch of HomeSpan's WiFi Access Point upon start-up whenever WiFi Credentials are not found. + * For advanced users: create your own custom WiFi Access Point and set your WiFi Credentials programmatically. See [Releases](https://github.com/HomeSpan/HomeSpan/releases) for details on all changes included in this update. From 1ed7089a02e7ca69dcad24394e9d0baeac765bff Mon Sep 17 00:00:00 2001 From: HomeSpan Date: Sun, 20 Jun 2021 18:04:59 -0500 Subject: [PATCH 55/55] Update CLI.md --- docs/CLI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CLI.md b/docs/CLI.md index 348f798..a670d2a 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -90,7 +90,7 @@ In addition to listening for incoming HAP requests, HomeSpan also continuously p ### User-Defined Commands -You can extend the HomeSpan CLI with custom functions using `SpanUserCommand()`. This class allows you to assign a single-character name to any custom function that will be called when you type the '@' symbol following by the single-character name into the CLI. For example, if you assigned the character 'K' to a custom function, you would type '@K' into the CLI to invoke it. This allows you to use any single-character name, even if that character is already used by HomeSpan for its built-in commands. The `SpanUserCommand` class also allows you to include a short text description of your function that will be added to the menu of commands when you type '?' into the CLI. See the the [API Reference](https://github.com/HomeSpan/HomeSpan/blob/dev/docs/Reference.md#spanusercommandchar-c-const-char-s-void-fconst-char-v) for full details. +You can extend the HomeSpan CLI with custom functions using `SpanUserCommand()`. This class allows you to assign a single-character name to any custom function that will be called when you type the '@' symbol following by the single-character name into the CLI. For example, if you assigned the character 'K' to a custom function, you would type '@K' into the CLI to invoke it. This allows you to use any single-character name, even if that character is already used by HomeSpan for its built-in commands. The `SpanUserCommand` class also allows you to include a short text description of your function that will be added to the menu of commands when you type '?' into the CLI. See the the [API Reference](Reference.md#spanusercommandchar-c-const-char-s-void-fconst-char-v) for full details. ---