started development of Blinkable Interface to allow generic LED to be used with Blinker

This commit is contained in:
Gregg 2022-08-27 17:33:08 -05:00
parent c286875640
commit fc76db092d
11 changed files with 669 additions and 314 deletions

View File

@ -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);
}

View File

@ -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<maxConnections) // if specific request for max connections is less than computed max connections
maxConnections=requestedMaxCon; // over-ride max connections with requested value
@ -96,8 +94,8 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
Serial.print("Message Logs: Level ");
Serial.print(logLevel);
Serial.print("\nStatus LED: Pin ");
if(statusPin>=0){
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 <RETURN>'.\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
}

View File

@ -40,6 +40,7 @@
#include <nvs.h>
#include <ArduinoOTA.h>
#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

View File

@ -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+="<meta http-equiv = \"refresh\" content = \"" + String(waitTime) + "; url = /wifi-status\" />"
"<p>Initiating WiFi connection to:</p><p><b>" + String(wifiData.ssid) + "</p>";
@ -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+="<p>SUCCESS! Connected to:</p><p><b>" + String(wifiData.ssid) + "</b></p>";
responseBody+="<p>You may enter new 8-digit Setup Code below, or leave blank to retain existing code.</p>";
@ -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+="<p>Welcome to HomeSpan! This page allows you to configure the above HomeSpan device to connect to your WiFi network.</p>"

View File

@ -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

View File

@ -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)<pauseDuration)
return;
Serial.print("Pausing Status LED\n");
isPaused=true;
gpio_set_direction((gpio_num_t)pin, GPIO_MODE_DISABLE);
}
//////////////////////////////////
//// 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)<pauseDuration)
// return;
//
// Serial.print("Pausing Status LED\n");
// isPaused=true;
// led->off();
//}
//
////////////////////////////////////////
//
//int Blinker::getPin(){
// return(led->getPin());
//}

View File

@ -29,6 +29,7 @@
#include <Arduino.h>
#include <driver/timer.h>
#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
//
//};

176
src/extras/Blinker.cpp Normal file
View File

@ -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)<pauseDuration)
return;
Serial.print("Pausing Status LED\n");
isPaused=true;
led->off();
}
//////////////////////////////////////
int Blinker::getPin(){
return(led->getPin());
}

146
src/extras/Blinker.h Normal file
View File

@ -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 <Arduino.h>
#include <driver/timer.h>
////////////////////////////////
// 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
};

View File

@ -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 {

View File

@ -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);