From fc76db092d97dd72c471acf7366b9dd445c4ca0c Mon Sep 17 00:00:00 2001 From: Gregg Date: Sat, 27 Aug 2022 17:33:08 -0500 Subject: [PATCH] started development of Blinkable Interface to allow generic LED to be used with Blinker --- src/HAP.cpp | 7 +- src/HomeSpan.cpp | 95 +++++++----- src/HomeSpan.h | 12 +- src/Network.cpp | 26 +++- src/Settings.h | 2 - src/Utils.cpp | 322 +++++++++++++++++++---------------------- src/Utils.h | 184 ++++++++++++----------- src/extras/Blinker.cpp | 176 ++++++++++++++++++++++ src/extras/Blinker.h | 146 +++++++++++++++++++ src/extras/Pixel.h | 3 +- src/src.ino | 10 +- 11 files changed, 669 insertions(+), 314 deletions(-) create mode 100644 src/extras/Blinker.cpp create mode 100644 src/extras/Blinker.h diff --git a/src/HAP.cpp b/src/HAP.cpp index c4b2844..a44b9ee 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -637,7 +637,9 @@ int HAPClient::postPairSetupURL(){ mdns_service_txt_item_set("_hap","_tcp","sf","0"); // broadcast new status LOG1("\n*** ACCESSORY PAIRED! ***\n"); - homeSpan.statusLED.on(); + + if(homeSpan.statusLED) + homeSpan.statusLED->on(); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(true); @@ -1636,7 +1638,8 @@ void HAPClient::removeController(uint8_t *id){ removeControllers(); LOG1("That was last Admin Controller! Removing any remaining Regular Controllers and unpairing Accessory\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - homeSpan.statusLED.start(LED_PAIRING_NEEDED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_PAIRING_NEEDED); if(homeSpan.pairCallback) // if set, invoke user-defined Pairing Callback to indicate device has been paired homeSpan.pairCallback(false); } diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index bba3091..b59b237 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -59,8 +59,6 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(0)); // required to avoid watchdog timeout messages from ESP32-C3 - statusLED.init(statusPin,0,autoOffLED); - if(requestedMaxCon=0){ - Serial.print(statusPin); + if(getStatusPin()>=0){ + Serial.print(getStatusPin()); if(autoOffLED>0) Serial.printf(" (Auto Off=%d sec)",autoOffLED); } @@ -189,10 +187,11 @@ void Span::pollTask() { processSerialCommand("A"); } else { Serial.print("YOU MAY CONFIGURE BY TYPING 'W '.\n\n"); - statusLED.start(LED_WIFI_NEEDED); + if(statusLED) + statusLED->start(LED_WIFI_NEEDED); } - } else { - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + } else if(statusLED) { + statusLED->start(LED_WIFI_CONNECTING); } if(controlButton) @@ -290,12 +289,12 @@ void Span::pollTask() { if(spanOTA.enabled) ArduinoOTA.handle(); - if(controlButton && controlButton->primed()){ - statusLED.start(LED_ALERT); - } + if(controlButton && controlButton->primed() && statusLED) + statusLED->start(LED_ALERT); if(controlButton && controlButton->triggered(3000,10000)){ - statusLED.off(); + if(statusLED) + statusLED->off(); if(controlButton->type()==PushButton::LONG){ controlButton->wait(); processSerialCommand("F"); // FACTORY RESET @@ -304,7 +303,8 @@ void Span::pollTask() { } } - statusLED.check(); + if(statusLED) + statusLED->check(); } // poll @@ -323,11 +323,16 @@ int Span::getFreeSlot(){ ////////////////////////////////////// void Span::commandMode(){ + + if(!statusLED){ + Serial.print("*** ERROR: CAN'T ENTER COMMAND MODE WITHOUT A DEFINED STATUS LED***\n\n"); + return; + } Serial.print("*** ENTERING COMMAND MODE ***\n\n"); int mode=1; boolean done=false; - statusLED.start(500,0.3,mode,1000); + statusLED->start(500,0.3,mode,1000); unsigned long alarmTime=millis()+comModeLife; @@ -338,7 +343,7 @@ void Span::commandMode(){ Serial.print(" seconds).\n\n"); mode=1; done=true; - statusLED.start(LED_ALERT); + statusLED->start(LED_ALERT); delay(2000); } else if(controlButton->triggered(10,3000)){ @@ -346,14 +351,14 @@ void Span::commandMode(){ mode++; if(mode==6) mode=1; - statusLED.start(500,0.3,mode,1000); + statusLED->start(500,0.3,mode,1000); } else { done=true; } } // button press } // while - statusLED.start(LED_ALERT); + statusLED->start(LED_ALERT); controlButton->wait(); switch(mode){ @@ -361,12 +366,12 @@ void Span::commandMode(){ case 1: Serial.print("*** NO ACTION\n\n"); if(strlen(network.wifiData.ssid)==0) - statusLED.start(LED_WIFI_NEEDED); + statusLED->start(LED_WIFI_NEEDED); else if(!HAPClient::nAdminControllers()) - statusLED.start(LED_PAIRING_NEEDED); + statusLED->start(LED_PAIRING_NEEDED); else - statusLED.on(); + statusLED->on(); break; case 2: @@ -402,7 +407,8 @@ void Span::checkConnect(){ connected++; waitTime=60000; alarmConnect=0; - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + if(statusLED) + statusLED->start(LED_WIFI_CONNECTING); } if(WiFi.status()!=WL_CONNECTED){ @@ -429,10 +435,12 @@ void Span::checkConnect(){ return; } - if(!HAPClient::nAdminControllers()) - statusLED.start(LED_PAIRING_NEEDED); - else - statusLED.on(); + if(statusLED){ + if(!HAPClient::nAdminControllers()) + statusLED->start(LED_PAIRING_NEEDED); + else + statusLED->on(); + } connected++; @@ -756,11 +764,13 @@ void Span::processSerialCommand(const char *c){ Serial.print("\nDEVICE NOT YET PAIRED -- PLEASE PAIR WITH HOMEKIT APP\n\n"); mdns_service_txt_item_set("_hap","_tcp","sf","1"); // set Status Flag = 1 (Table 6-8) - - if(strlen(network.wifiData.ssid)==0) - statusLED.start(LED_WIFI_NEEDED); - else - statusLED.start(LED_PAIRING_NEEDED); + + if(statusLED){ + if(strlen(network.wifiData.ssid)==0) + statusLED->start(LED_WIFI_NEEDED); + else + statusLED->start(LED_PAIRING_NEEDED); + } } break; @@ -777,7 +787,8 @@ void Span::processSerialCommand(const char *c){ 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(); + if(statusLED) + statusLED->off(); delay(1000); ESP.restart(); } @@ -810,7 +821,8 @@ void Span::processSerialCommand(const char *c){ } Serial.print("\n*** Re-starting ***\n\n"); - statusLED.off(); + if(statusLED) + statusLED->off(); delay(1000); ESP.restart(); // re-start device } @@ -818,7 +830,8 @@ void Span::processSerialCommand(const char *c){ case 'X': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(wifiNVS); nvs_commit(wifiNVS); Serial.print("\n*** WiFi Credentials ERASED! Re-starting...\n\n"); @@ -837,7 +850,8 @@ void Span::processSerialCommand(const char *c){ case 'H': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); Serial.print("\n*** HomeSpan Device ID and Pairing Data DELETED! Restarting...\n\n"); @@ -848,7 +862,8 @@ void Span::processSerialCommand(const char *c){ case 'R': { - statusLED.off(); + if(statusLED) + statusLED->off(); Serial.print("\n*** Restarting...\n\n"); delay(1000); ESP.restart(); @@ -857,7 +872,8 @@ void Span::processSerialCommand(const char *c){ case 'F': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_erase_all(HAPClient::hapNVS); nvs_commit(HAPClient::hapNVS); nvs_erase_all(wifiNVS); @@ -874,7 +890,8 @@ void Span::processSerialCommand(const char *c){ case 'E': { - statusLED.off(); + if(statusLED) + statusLED->off(); nvs_flash_erase(); Serial.print("\n*** ALL DATA ERASED! Restarting...\n\n"); delay(1000); @@ -2098,7 +2115,8 @@ void SpanOTA::start(){ Serial.printf("\n*** Current Partition: %s\n*** New Partition: %s\n*** OTA Starting..", esp_ota_get_running_partition()->label,esp_ota_get_next_update_partition(NULL)->label); otaPercent=0; - homeSpan.statusLED.start(LED_OTA_STARTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_OTA_STARTED); } /////////////////////////////// @@ -2107,7 +2125,8 @@ void SpanOTA::end(){ nvs_set_u8(homeSpan.otaNVS,"OTA_REQUIRED",safeLoad); nvs_commit(homeSpan.otaNVS); Serial.printf(" DONE! Rebooting...\n"); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); delay(100); // make sure commit it finished before reboot } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 3bac4d5..dced380 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -40,6 +40,7 @@ #include #include +#include "extras/Blinker.h" #include "Settings.h" #include "Utils.h" #include "Network.h" @@ -189,7 +190,6 @@ class Span{ unsigned long alarmConnect=0; // time after which WiFi connection attempt should be tried again const char *defaultSetupCode=DEFAULT_SETUP_CODE; // Setup Code used for pairing - int statusPin=DEFAULT_STATUS_PIN; // pin for Status LED uint16_t autoOffLED=0; // automatic turn-off duration (in seconds) for Status LED uint8_t logLevel=DEFAULT_LOG_LEVEL; // level for writing out log messages to serial monitor uint8_t maxConnections=CONFIG_LWIP_MAX_SOCKETS-2; // maximum number of allowed simultaneous HAP connections @@ -203,7 +203,7 @@ class Span{ void (*apFunction)()=NULL; // optional function to invoke when starting Access Point WiFiServer *hapServer; // pointer to the HAP Server connection - Blinker statusLED; // indicates HomeSpan status + Blinker *statusLED = NULL; // indicates HomeSpan status PushButton *controlButton = NULL; // controls HomeSpan configuration and resets Network network; // configures WiFi and Setup Code via either serial monitor or temporary Access Point SpanWebLog webLog; // optional web status/log @@ -254,10 +254,12 @@ class Span{ boolean updateDatabase(boolean updateMDNS=true); // updates HAP Configuration Number and Loop vector; if updateMDNS=true and config number has changed, re-broadcasts MDNS 'c#' record; returns true if config number changed boolean deleteAccessory(uint32_t aid); // deletes Accessory with matching aid; returns true if found, else returns false - void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin - void setStatusPin(uint8_t pin){statusPin=pin;} // sets Status Pin + void setControlPin(uint8_t pin){controlButton=new PushButton(pin);} // sets Control Pin + void setStatusPin(uint8_t pin){statusLED=new Blinker(new LED(pin),0,autoOffLED);} // sets Status Pin +// void setStatusPin(Blinkable *led){statusLED=new Blinker(led,0,autoOffLED);} // sets Status Blinkable LED + void setStatusAutoOff(uint16_t duration){autoOffLED=duration;} // sets Status LED auto off (seconds) - int getStatusPin(){return(statusPin);} // get Status Pin + int getStatusPin(){return(statusLED?statusLED->getPin():-1);} // get Status Pin (returns -1 if undefined) int getControlPin(){return(controlButton?controlButton->getPin():-1);} // get Control Pin (returns -1 if undefined) void setApSSID(const char *ssid){network.apSSID=ssid;} // sets Access Point SSID void setApPassword(const char *pwd){network.apPassword=pwd;} // sets Access Point Password diff --git a/src/Network.cpp b/src/Network.cpp index cc1c102..47172cc 100644 --- a/src/Network.cpp +++ b/src/Network.cpp @@ -30,6 +30,8 @@ #include "Network.h" #include "HomeSpan.h" #include "Utils.h" +#include "extras/Blinker.h" + using namespace Utils; @@ -116,7 +118,8 @@ void Network::apConfigure(){ Serial.print(apPassword); Serial.print("\n"); - homeSpan.statusLED.start(LED_AP_STARTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_STARTED); Serial.print("\nScanning for Networks...\n\n"); @@ -154,10 +157,12 @@ void Network::apConfigure(){ if(homeSpan.controlButton && homeSpan.controlButton->triggered(9999,3000)){ Serial.print("\n*** Access Point Terminated."); - homeSpan.statusLED.start(LED_ALERT); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_ALERT); homeSpan.controlButton->wait(); Serial.print(" Restarting... \n\n"); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); ESP.restart(); } @@ -176,9 +181,11 @@ void Network::apConfigure(){ Serial.print("\n*** Access Point: Configuration Canceled."); } Serial.print(" Restarting...\n\n"); - homeSpan.statusLED.start(LED_ALERT); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_ALERT); delay(1000); - homeSpan.statusLED.off(); + if(homeSpan.statusLED) + homeSpan.statusLED->off(); ESP.restart(); } } @@ -273,7 +280,8 @@ void Network::processRequest(char *body, char *formData){ getFormValue(formData,"network",wifiData.ssid,MAX_SSID); getFormValue(formData,"pwd",wifiData.pwd,MAX_PWD); - homeSpan.statusLED.start(LED_WIFI_CONNECTING); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_WIFI_CONNECTING); responseBody+="" "

Initiating WiFi connection to:

" + String(wifiData.ssid) + "

"; @@ -320,7 +328,8 @@ void Network::processRequest(char *body, char *formData){ } else { - homeSpan.statusLED.start(LED_AP_CONNECTED); // slow double-blink + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_CONNECTED); // slow double-blink responseBody+="

SUCCESS! Connected to:

" + String(wifiData.ssid) + "

"; responseBody+="

You may enter new 8-digit Setup Code below, or leave blank to retain existing code.

"; @@ -340,7 +349,8 @@ void Network::processRequest(char *body, char *formData){ LOG1("In Landing Page...\n"); - homeSpan.statusLED.start(LED_AP_CONNECTED); + if(homeSpan.statusLED) + homeSpan.statusLED->start(LED_AP_CONNECTED); waitTime=2; responseBody+="

Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.

" diff --git a/src/Settings.h b/src/Settings.h index 73225ad..7ef9de3 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -71,8 +71,6 @@ #define DEFAULT_QR_ID "HSPN" // change with homeSpan.setQRID(qrID); -#define DEFAULT_STATUS_PIN -1 // change with homeSpan.setStatusPin(pin) - #define DEFAULT_AP_SSID "HomeSpan-Setup" // change with homeSpan.setApSSID(ssid) #define DEFAULT_AP_PASSWORD "homespan" // change with homeSpan.setApPassword(pwd) #define DEFAULT_OTA_PASSWORD "homespan-ota" // change with 'O' command diff --git a/src/Utils.cpp b/src/Utils.cpp index 5997e28..31b326d 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -227,178 +227,150 @@ void PushButton::reset(){ touch_value_t PushButton::threshold=0; #endif -//////////////////////////////// -// Blinker // -//////////////////////////////// - -Blinker::Blinker(){ -} - -////////////////////////////////////// - -Blinker::Blinker(int pin, int timerNum, uint16_t autoOffDuration){ - init(pin, timerNum, autoOffDuration); -} - -////////////////////////////////////// - -void Blinker::init(int pin, int timerNum, uint16_t autoOffDuration){ - - this->pin=pin; - if(pin<0) - return; - - pinMode(pin,OUTPUT); - digitalWrite(pin,0); - - pauseDuration=autoOffDuration*1000; - -#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group - group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group -#else - group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; - idx=TIMER_0; -#endif - - timer_config_t conf; - conf.alarm_en=TIMER_ALARM_EN; - conf.counter_en=TIMER_PAUSE; - conf.intr_type=TIMER_INTR_LEVEL; - conf.counter_dir=TIMER_COUNT_UP; - conf.auto_reload=TIMER_AUTORELOAD_EN; - conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) - -#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure - conf.clk_src=TIMER_SRC_CLK_APB; -#endif - - timer_init(group,idx,&conf); - timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); - timer_enable_intr(group,idx); - -} - -////////////////////////////////////// - -void Blinker::isrTimer(void *arg){ - - Blinker *b=(Blinker *)arg; - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 - timer_group_clr_intr_status_in_isr(b->group,b->idx); -#else // use older method that is only for ESP32 - if(b->group){ - if(b->idx) - TIMERG1.int_clr_timers.t1=1; - else - TIMERG1.int_clr_timers.t0=1; - } else { - if(b->idx) - TIMERG0.int_clr_timers.t1=1; - else - TIMERG0.int_clr_timers.t0=1; - } -#endif - - if(!digitalRead(b->pin)){ - digitalWrite(b->pin,1); - timer_set_alarm_value(b->group,b->idx,b->onTime); - b->count--; - } else { - digitalWrite(b->pin,0); - if(b->count){ - timer_set_alarm_value(b->group,b->idx,b->offTime); - } else { - timer_set_alarm_value(b->group,b->idx,b->delayTime); - b->count=b->nBlinks; - } - } - - timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); - -} - -////////////////////////////////////// - -void Blinker::start(int period, float dutyCycle){ - - start(period, dutyCycle, 1, 0); -} - -////////////////////////////////////// - -void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); // needed to ensure digitalRead() functions correctly on ESP32-C3; also needed to re-enable after pause() - - period*=10; - onTime=dutyCycle*period; - offTime=period-onTime; - this->delayTime=delayTime*10+offTime; - this->nBlinks=nBlinks; - count=nBlinks; - timer_set_counter_value(group,idx,0); - timer_set_alarm_value(group,idx,0); - timer_start(group,idx); -} - -////////////////////////////////////// - -void Blinker::stop(){ - - if(pin<0) - return; - - timer_pause(group,idx); -} - -////////////////////////////////////// - -void Blinker::on(){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); - - stop(); - digitalWrite(pin,1); -} - -////////////////////////////////////// - -void Blinker::off(){ - - if(pin<0) - return; - - pauseTime=millis(); - isPaused=false; - gpio_set_direction((gpio_num_t)pin, GPIO_MODE_INPUT_OUTPUT); - - stop(); - digitalWrite(pin,0); -} - -////////////////////////////////////// - -void Blinker::check(){ - - if(pin<0) - return; - - if(pauseDuration==0 || isPaused || (millis()-pauseTime)led=led; +// +// pauseDuration=autoOffDuration*1000; +// +//#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group +// group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; +// idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group +//#else +// group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; +// idx=TIMER_0; +//#endif +// +// timer_config_t conf; +// conf.alarm_en=TIMER_ALARM_EN; +// conf.counter_en=TIMER_PAUSE; +// conf.intr_type=TIMER_INTR_LEVEL; +// conf.counter_dir=TIMER_COUNT_UP; +// conf.auto_reload=TIMER_AUTORELOAD_EN; +// conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) +// +//#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure +// conf.clk_src=TIMER_SRC_CLK_APB; +//#endif +// +// timer_init(group,idx,&conf); +// timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); +// timer_enable_intr(group,idx); +// +//} +// +//////////////////////////////////////// +// +//void Blinker::isrTimer(void *arg){ +// +// Blinker *b=(Blinker *)arg; +// +//#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 +// timer_group_clr_intr_status_in_isr(b->group,b->idx); +//#else // use older method that is only for ESP32 +// if(b->group){ +// if(b->idx) +// TIMERG1.int_clr_timers.t1=1; +// else +// TIMERG1.int_clr_timers.t0=1; +// } else { +// if(b->idx) +// TIMERG0.int_clr_timers.t1=1; +// else +// TIMERG0.int_clr_timers.t0=1; +// } +//#endif +// +// if(!(b->led->isOn())){ +// b->led->on(); +// timer_set_alarm_value(b->group,b->idx,b->onTime); +// b->count--; +// } else { +// b->led->off(); +// if(b->count){ +// timer_set_alarm_value(b->group,b->idx,b->offTime); +// } else { +// timer_set_alarm_value(b->group,b->idx,b->delayTime); +// b->count=b->nBlinks; +// } +// } +// +// timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); +// +//} +// +//////////////////////////////////////// +// +//void Blinker::start(int period, float dutyCycle){ +// +// start(period, dutyCycle, 1, 0); +//} +// +//////////////////////////////////////// +// +//void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ +// +// pauseTime=millis(); +// isPaused=false; +// +// period*=10; +// onTime=dutyCycle*period; +// offTime=period-onTime; +// this->delayTime=delayTime*10+offTime; +// this->nBlinks=nBlinks; +// count=nBlinks; +// timer_set_counter_value(group,idx,0); +// timer_set_alarm_value(group,idx,0); +// timer_start(group,idx); +//} +// +//////////////////////////////////////// +// +//void Blinker::stop(){ +// +// timer_pause(group,idx); +//} +// +//////////////////////////////////////// +// +//void Blinker::on(){ +// +// pauseTime=millis(); +// isPaused=false; +// +// stop(); +// led->on(); +//} +// +//////////////////////////////////////// +// +//void Blinker::off(){ +// +// pauseTime=millis(); +// isPaused=false; +// +// stop(); +// led->off(); +//} +// +//////////////////////////////////////// +// +//void Blinker::check(){ +// +// if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); +//} +// +//////////////////////////////////////// +// +//int Blinker::getPin(){ +// return(led->getPin()); +//} diff --git a/src/Utils.h b/src/Utils.h index b32922c..7af49ed 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -29,6 +29,7 @@ #include #include +#include "extras/Blinker.h" namespace Utils { @@ -37,6 +38,21 @@ String mask(char *c, int n); // simply utility that creates a String fr } +//////////////////////////////// +// Blinkable Interface // +//////////////////////////////// + +//class Blinkable { +// protected: +// boolean onState=false; +// +// public: +// virtual void on()=0; +// virtual void off()=0; +// virtual int getPin()=0; +// boolean isOn() {return(onState);} +//}; + ///////////////////////////////////////////////// // Creates a temporary buffer that is freed after // going out of scope @@ -182,98 +198,104 @@ class PushButton{ }; + + +////////////////////////////////// +//// Generic_LED // +////////////////////////////////// +// +//class LED : public Blinkable { +// int pin; +// +// public: +// +// LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} +// void on() {digitalWrite(pin,HIGH);onState=true;} +// void off() {digitalWrite(pin,LOW);onState=false;} +// int getPin() {return(pin);} +//}; + //////////////////////////////// // Blinker // //////////////////////////////// -class Blinker { - - timer_group_t group; - timer_idx_t idx; - int pin; - - int nBlinks; - int onTime; - int offTime; - int delayTime; - int count; - - unsigned long pauseDuration; - unsigned long pauseTime; - boolean isPaused=false; - - static void isrTimer(void *arg); - - public: - - Blinker(); - Blinker(int pin, int timerNum=0, uint16_t autoOffDuration=0); - -// Creates a generic blinking LED on specified pin controlled -// in background via interrupts generated by an ESP32 Alarm Timer. +//class Blinker { +// +// timer_group_t group; +// timer_idx_t idx; // -// In the first form, a Blinker is instantiated without specifying -// the pin. In this case the pin must be specified in a subsequent call -// to init() before the Blinker can be used. +// Blinkable *led; // -// In the second form, a Blinker is instantiated and initialized with -// the specified pin, obviating the need for a separate call to init(). +// int nBlinks; +// int onTime; +// int offTime; +// int delayTime; +// int count; +// +// unsigned long pauseDuration; +// unsigned long pauseTime; +// boolean isPaused=false; // -// pin: Pin mumber to control. Blinker will set pinMode to OUTPUT automatically +// static void isrTimer(void *arg); // -// timerNum: ESP32 Alarm Timer to use. -// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// public: // -// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -// Blinker will resume normal operation upon next call to start(), on(), or off() -// Program must periodically call check() for auto-off functionality to work - - void init(int pin, int timerNum=0, uint16_t autoOffDuration=0); - -// Initializes Blinker, if not configured during instantiation. +// Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); // -// pin: Pin mumber to control. Blinker will set pinMode to OUTPUT automatically +//// Creates a generic blinking LED on specified pin controlled +//// in background via interrupts generated by an ESP32 Alarm Timer. +//// +//// In the first form, a Blinker is instantiated without specifying +//// the pin. In this case the pin must be specified in a subsequent call +//// to init() before the Blinker can be used. +//// +//// In the second form, a Blinker is instantiated and initialized with +//// the specified pin, obviating the need for a separate call to init(). +//// +//// led: An initialized LED device that implements the Blinkable Interface +//// +//// timerNum: ESP32 Alarm Timer to use. +//// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 +//// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +//// +//// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed +//// Blinker will resume normal operation upon next call to start(), on(), or off() +//// Program must periodically call check() for auto-off functionality to work +// +// void start(int period, float dutyCycle=0.5); +// +//// Starts simple ON/OFF blinking. +//// +//// period: ON/OFF blinking period, in milliseconds +//// dutyCycle: Fraction of period that LED is ON (default=50%) // -// timerNum: ESP32 Alarm Timer to use. -// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 -// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// void start(int period, float dutyCycle, int nBlinks, int delayTime); // -// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed -// Blinker will resume normal operation upon next call to start(), on(), or off() -// Program must periodically call check() for auto-off functionality to work - - void start(int period, float dutyCycle=0.5); - -// Starts simple ON/OFF blinking. +//// Starts ON/OFF blinking pattern. +//// +//// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern +//// dutyCycle: Fraction of period that LED is ON (default=50%) +//// nBlinks: Number of blinks in blinking portion of pattern +//// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern // -// period: ON/OFF blinking period, in milliseconds -// dutyCycle: Fraction of period that LED is ON (default=50%) - - void start(int period, float dutyCycle, int nBlinks, int delayTime); - -// Starts ON/OFF blinking pattern. +// void stop(); // -// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern -// dutyCycle: Fraction of period that LED is ON (default=50%) -// nBlinks: Number of blinks in blinking portion of pattern -// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern - - void stop(); - -// Stops current blinking pattern. - - void on(); - -// Stops current blinking pattern and turns on LED - - void off(); - -// Stops current blinking pattern and turns off LED - - void check(); - -// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) - - -}; +//// Stops current blinking pattern. +// +// void on(); +// +//// Stops current blinking pattern and turns on LED +// +// void off(); +// +//// Stops current blinking pattern and turns off LED +// +// void check(); +// +//// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) +// +// int getPin(); +// +//// Returns pin number of connected LED +// +//}; diff --git a/src/extras/Blinker.cpp b/src/extras/Blinker.cpp new file mode 100644 index 0000000..dad0ca2 --- /dev/null +++ b/src/extras/Blinker.cpp @@ -0,0 +1,176 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#include "Blinker.h" + + //////////////////////////////// +// Blinker // +//////////////////////////////// + +Blinker::Blinker(Blinkable *led, int timerNum, uint16_t autoOffDuration){ + + this->led=led; + + pauseDuration=autoOffDuration*1000; + +#if SOC_TIMER_GROUP_TIMERS_PER_GROUP>1 // ESP32 and ESP32-S2 contains two timers per timer group + group=((timerNum/2)%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; + idx=(timerNum%2==0)?TIMER_0:TIMER_1; // ESP32-C3 only contains one timer per timer group +#else + group=(timerNum%2==0)?TIMER_GROUP_0:TIMER_GROUP_1; + idx=TIMER_0; +#endif + + timer_config_t conf; + conf.alarm_en=TIMER_ALARM_EN; + conf.counter_en=TIMER_PAUSE; + conf.intr_type=TIMER_INTR_LEVEL; + conf.counter_dir=TIMER_COUNT_UP; + conf.auto_reload=TIMER_AUTORELOAD_EN; + conf.divider=getApbFrequency()/10000; // set divider to yield 10 kHz clock (0.1 ms pulses) + +#ifdef SOC_TIMER_GROUP_SUPPORT_XTAL // set clock to APB (default is XTAL!) if clk_src is defined in conf structure + conf.clk_src=TIMER_SRC_CLK_APB; +#endif + + timer_init(group,idx,&conf); + timer_isr_register(group,idx,Blinker::isrTimer,(void *)this,0,NULL); + timer_enable_intr(group,idx); + +} + +////////////////////////////////////// + +void Blinker::isrTimer(void *arg){ + + Blinker *b=(Blinker *)arg; + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) // use new method that is generic to ESP32, S2, and C3 + timer_group_clr_intr_status_in_isr(b->group,b->idx); +#else // use older method that is only for ESP32 + if(b->group){ + if(b->idx) + TIMERG1.int_clr_timers.t1=1; + else + TIMERG1.int_clr_timers.t0=1; + } else { + if(b->idx) + TIMERG0.int_clr_timers.t1=1; + else + TIMERG0.int_clr_timers.t0=1; + } +#endif + + if(!(b->led->isOn())){ + b->led->on(); + timer_set_alarm_value(b->group,b->idx,b->onTime); + b->count--; + } else { + b->led->off(); + if(b->count){ + timer_set_alarm_value(b->group,b->idx,b->offTime); + } else { + timer_set_alarm_value(b->group,b->idx,b->delayTime); + b->count=b->nBlinks; + } + } + + timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN); + +} + +////////////////////////////////////// + +void Blinker::start(int period, float dutyCycle){ + + start(period, dutyCycle, 1, 0); +} + +////////////////////////////////////// + +void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ + + pauseTime=millis(); + isPaused=false; + + period*=10; + onTime=dutyCycle*period; + offTime=period-onTime; + this->delayTime=delayTime*10+offTime; + this->nBlinks=nBlinks; + count=nBlinks; + timer_set_counter_value(group,idx,0); + timer_set_alarm_value(group,idx,0); + timer_start(group,idx); +} + +////////////////////////////////////// + +void Blinker::stop(){ + + timer_pause(group,idx); +} + +////////////////////////////////////// + +void Blinker::on(){ + + pauseTime=millis(); + isPaused=false; + + stop(); + led->on(); +} + +////////////////////////////////////// + +void Blinker::off(){ + + pauseTime=millis(); + isPaused=false; + + stop(); + led->off(); +} + +////////////////////////////////////// + +void Blinker::check(){ + + if(pauseDuration==0 || isPaused || (millis()-pauseTime)off(); +} + +////////////////////////////////////// + +int Blinker::getPin(){ + return(led->getPin()); +} diff --git a/src/extras/Blinker.h b/src/extras/Blinker.h new file mode 100644 index 0000000..9794d6b --- /dev/null +++ b/src/extras/Blinker.h @@ -0,0 +1,146 @@ +/********************************************************************************* + * MIT License + * + * Copyright (c) 2020-2022 Gregg E. Berman + * + * https://github.com/HomeSpan/HomeSpan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + ********************************************************************************/ + +#pragma once + +#include +#include + +//////////////////////////////// +// Blinkable Interface // +//////////////////////////////// + +class Blinkable { + protected: + boolean onState=false; + + public: + virtual void on()=0; + virtual void off()=0; + virtual int getPin()=0; + boolean isOn() {return(onState);} +}; + +//////////////////////////////// +// Generic_LED // +//////////////////////////////// + +class LED : public Blinkable { + int pin; + + public: + + LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} + void on() {digitalWrite(pin,HIGH);onState=true;} + void off() {digitalWrite(pin,LOW);onState=false;} + int getPin() {return(pin);} +}; + +//////////////////////////////// +// Blinker // +//////////////////////////////// + +class Blinker { + + timer_group_t group; + timer_idx_t idx; + + Blinkable *led; + + int nBlinks; + int onTime; + int offTime; + int delayTime; + int count; + + unsigned long pauseDuration; + unsigned long pauseTime; + boolean isPaused=false; + + static void isrTimer(void *arg); + + public: + + Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); + +// Creates a generic blinking LED on specified pin controlled +// in background via interrupts generated by an ESP32 Alarm Timer. +// +// In the first form, a Blinker is instantiated without specifying +// the pin. In this case the pin must be specified in a subsequent call +// to init() before the Blinker can be used. +// +// In the second form, a Blinker is instantiated and initialized with +// the specified pin, obviating the need for a separate call to init(). +// +// led: An initialized LED device that implements the Blinkable Interface +// +// timerNum: ESP32 Alarm Timer to use. +// For ESP32 and ESP32-S2: 0=Group0/Timer0, 1=Group0/Timer1, 2=Group1/Timer0, 3=Group1/Timer1 +// For ESP32-C3: 0=Group0/Timer0, 1=Group1/Timer0 +// +// autoOffDuration: If greater than zero, Blinker will automatically turn off after autoOffDuration (in seconds) has elapsed +// Blinker will resume normal operation upon next call to start(), on(), or off() +// Program must periodically call check() for auto-off functionality to work + + void start(int period, float dutyCycle=0.5); + +// Starts simple ON/OFF blinking. +// +// period: ON/OFF blinking period, in milliseconds +// dutyCycle: Fraction of period that LED is ON (default=50%) + + void start(int period, float dutyCycle, int nBlinks, int delayTime); + +// Starts ON/OFF blinking pattern. +// +// period: ON/OFF blinking period, in milliseconds, used for blinking portion of pattern +// dutyCycle: Fraction of period that LED is ON (default=50%) +// nBlinks: Number of blinks in blinking portion of pattern +// delayTime: delay, in milliseconds, during which LED is off before restarting blinking pattern + + void stop(); + +// Stops current blinking pattern. + + void on(); + +// Stops current blinking pattern and turns on LED + + void off(); + +// Stops current blinking pattern and turns off LED + + void check(); + +// Optional check to see if LED output should be paused (check is bypassed if pauseDuration=0) + + int getPin(); + +// Returns pin number of connected LED + +}; diff --git a/src/extras/Pixel.h b/src/extras/Pixel.h index 79a7b5a..27d431c 100644 --- a/src/extras/Pixel.h +++ b/src/extras/Pixel.h @@ -33,12 +33,13 @@ #include "RFControl.h" #include "PwmPin.h" +#include "Blinker.h" //////////////////////////////////////////// // Single-Wire RGB/RGBW NeoPixels // //////////////////////////////////////////// -class Pixel { +class Pixel : Blinkable { public: struct Color { diff --git a/src/src.ino b/src/src.ino index 19e4ae3..6e0bc53 100644 --- a/src/src.ino +++ b/src/src.ino @@ -3,6 +3,9 @@ // as well as compile and test from this point. This file is ignored when the library is included in other sketches. #include "HomeSpan.h" +#include "FeatherPins.h" +#include "extras/Pixel.h" +#include "extras/Blinker.h" #define STRING_t const char * // WORK-AROUND @@ -14,8 +17,11 @@ void setup() { Serial.begin(115200); // homeSpan.setLogLevel(2); -// homeSpan.setStatusPin(13); - homeSpan.setControlPin(33); + homeSpan.setControlPin(F25); + homeSpan.setStatusPin(F26); +// homeSpan.setStatusPin(new LED(F26)); + +// new Pixel(F27); homeSpan.setHostNameSuffix("-lamp1"); homeSpan.setPortNum(1201);