Complete re-write of Blinker Class

Rather than use ESP32 timers, simply spawn a new task that turn on/off LED using simple delays.  By avoiding the use of the ESP32 timer, we are no longer limited by the number of Blinkers possible.  Also, Blinker relied on the timer interrupt, which crashed when it tried to call Pixel since Pixel in turn uses interrupts from the RMT.  Switching to spawned tasks is much cleaner and does not consume more CPU time since the timer interrupts were driven by CPU anyway.

Blinker class and generic LED class are now in extras.h.  Blinkable Interface is also in extras.h.

To Do:  Incorporate new Blinker class into HomeSpan code
This commit is contained in:
Gregg 2022-08-28 17:49:48 -05:00
parent fc76db092d
commit 294b8d8d71
4 changed files with 59 additions and 132 deletions

View File

@ -31,76 +31,27 @@
// Blinker // // Blinker //
//////////////////////////////// ////////////////////////////////
Blinker::Blinker(Blinkable *led, int timerNum, uint16_t autoOffDuration){ Blinker::Blinker(Blinkable *led, uint16_t autoOffDuration){
this->led=led; this->led=led;
pauseDuration=autoOffDuration*1000; 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){ void Blinker::blinkTask(void *arg){
Blinker *b=(Blinker *)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 for(;;){
timer_group_clr_intr_status_in_isr(b->group,b->idx); for(int i=0;i<b->nBlinks;i++){
#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(); b->led->on();
timer_set_alarm_value(b->group,b->idx,b->onTime); delay(b->onTime);
b->count--;
} else {
b->led->off(); b->led->off();
if(b->count){ delay(b->offTime);
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;
} }
delay(b->delayTime);
} }
timer_set_alarm(b->group,b->idx,TIMER_ALARM_EN);
} }
////////////////////////////////////// //////////////////////////////////////
@ -114,45 +65,47 @@ void Blinker::start(int period, float dutyCycle){
void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){ void Blinker::start(int period, float dutyCycle, int nBlinks, int delayTime){
pauseTime=millis();
isPaused=false;
period*=10;
onTime=dutyCycle*period; onTime=dutyCycle*period;
offTime=period-onTime; offTime=period-onTime;
this->delayTime=delayTime*10+offTime; this->delayTime=delayTime+offTime;
this->nBlinks=nBlinks; this->nBlinks=nBlinks;
count=nBlinks;
timer_set_counter_value(group,idx,0); stop();
timer_set_alarm_value(group,idx,0); Serial.printf("Starting Blink Task\n");
timer_start(group,idx); xTaskCreate( blinkTask, "BlinkTask", 1024, (void *)this, 0, &blinkHandle );
pauseTime=millis();
isPaused=false;
} }
////////////////////////////////////// //////////////////////////////////////
void Blinker::stop(){ void Blinker::stop(){
timer_pause(group,idx); if(blinkHandle!=NULL){
Serial.printf("Deleting Blink Task\n");
vTaskDelete(blinkHandle);
blinkHandle=NULL;
}
isPaused=true;
} }
////////////////////////////////////// //////////////////////////////////////
void Blinker::on(){ void Blinker::on(){
pauseTime=millis();
isPaused=false;
stop(); stop();
led->on(); led->on();
pauseTime=millis();
isPaused=false;
} }
////////////////////////////////////// //////////////////////////////////////
void Blinker::off(){ void Blinker::off(){
pauseTime=millis();
isPaused=false;
stop(); stop();
led->off(); led->off();
} }
@ -164,9 +117,8 @@ void Blinker::check(){
if(pauseDuration==0 || isPaused || (millis()-pauseTime)<pauseDuration) if(pauseDuration==0 || isPaused || (millis()-pauseTime)<pauseDuration)
return; return;
Serial.print("Pausing Status LED\n"); Serial.print("Pausing LED\n");
isPaused=true; off();
led->off();
} }
////////////////////////////////////// //////////////////////////////////////

View File

@ -35,14 +35,11 @@
//////////////////////////////// ////////////////////////////////
class Blinkable { class Blinkable {
protected:
boolean onState=false;
public: public:
virtual void on()=0; virtual void on()=0;
virtual void off()=0; virtual void off()=0;
virtual int getPin()=0; virtual int getPin()=0;
boolean isOn() {return(onState);}
}; };
//////////////////////////////// ////////////////////////////////
@ -55,8 +52,8 @@ class LED : public Blinkable {
public: public:
LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);} LED(int pin) : pin{pin} {pinMode(pin,OUTPUT);digitalWrite(pin,0);}
void on() {digitalWrite(pin,HIGH);onState=true;} void on() {digitalWrite(pin,HIGH);}
void off() {digitalWrite(pin,LOW);onState=false;} void off() {digitalWrite(pin,LOW);}
int getPin() {return(pin);} int getPin() {return(pin);}
}; };
@ -66,43 +63,28 @@ class LED : public Blinkable {
class Blinker { class Blinker {
timer_group_t group; TaskHandle_t blinkHandle = NULL;
timer_idx_t idx;
Blinkable *led; Blinkable *led;
int nBlinks; int nBlinks;
int onTime; int onTime;
int offTime; int offTime;
int delayTime; int delayTime;
int count;
unsigned long pauseDuration; unsigned long pauseDuration;
unsigned long pauseTime; unsigned long pauseTime;
boolean isPaused=false; boolean isPaused=false;
static void isrTimer(void *arg); static void blinkTask(void *arg);
public: public:
Blinker(Blinkable *led, int timerNum=0, uint16_t autoOffDuration=0); Blinker(Blinkable *led, uint16_t autoOffDuration=0);
// Creates a generic blinking LED on specified pin controlled // Creates a generic blinking LED in a separate task thread
// 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 // 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 // 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() // Blinker will resume normal operation upon next call to start(), on(), or off()
// Program must periodically call check() for auto-off functionality to work // Program must periodically call check() for auto-off functionality to work

View File

@ -39,7 +39,7 @@
// Single-Wire RGB/RGBW NeoPixels // // Single-Wire RGB/RGBW NeoPixels //
//////////////////////////////////////////// ////////////////////////////////////////////
class Pixel : Blinkable { class Pixel : public Blinkable {
public: public:
struct Color { struct Color {
@ -152,6 +152,9 @@ class Pixel : Blinkable {
operator bool(){ // override boolean operator to return true/false if creation succeeded/failed operator bool(){ // override boolean operator to return true/false if creation succeeded/failed
return(*rf); return(*rf);
} }
void on() {set(RGB(255,0,0));}
void off() {set(RGB(0,0,0));}
}; };
//////////////////////////////////////////// ////////////////////////////////////////////

View File

@ -1,13 +1,11 @@
// This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE, // This is a placeholder .ino file that allows you to easily edit the contents of this files using the Arduino IDE,
// as well as compile and test from this point. This file is ignored when the library is included in other sketches. // as well as compile and test from this point. This file is ignored when the library is included in other sketches.
#include "Blinker.h"
#include "Pixel.h" #include "Pixel.h"
//#define PixelType Pixel //Blinker p(new LED(26));
#define PixelType Dot Blinker p(new Pixel(27),20);
//Pixel p(8);
Dot p(0,1);
void setup() { void setup() {
@ -15,29 +13,21 @@ void setup() {
Serial.flush(); Serial.flush();
delay(1000); // wait for interface to flush delay(1000); // wait for interface to flush
Serial.println("\n\nHomeSpan Pixel Example\n"); Serial.println("\n\nHomeSpan Blinker Example\n");
// Serial.printf("Pins = %d %d\n",b.getPin(),p.getPin());
PixelType::Color off=PixelType::RGB(0,0,0); p.on();
delay(2000);
p.set(PixelType::RGB(0,0,255),3); p.off();
delay(1000); delay(2000);
p.start(300,0.25,4,1000);
p.set(off,3); delay(5000);
delay(1000); Serial.printf("New Pattern\n");
p.start(200,0.2,2,200);
PixelType::Color c[]={p.HSV(120,100,30),p.HSV(0,0,0),p.HSV(0,0,0)}; delay(3000);
p.set(c,3); p.off();
delay(1000);
c[0].HSV(0,0,0);
c[1].HSV(60,100,30);
p.set(c,3);
delay(1000);
c[1].HSV(0,0,0);
c[2].HSV(0,100,30);
p.set(c,3);
} }
void loop(){ void loop(){
p.check();
} }