Upgrade Playlist Features

This commit is contained in:
2025-12-09 17:20:01 +08:00
parent 577990de69
commit 8bd2780688
683 changed files with 91812 additions and 81260 deletions

View File

@@ -1,233 +1,233 @@
#include "circular_strip.h"
#include "application.h"
#include <esp_log.h>
#define TAG "CircularStrip"
#define BLINK_INFINITE -1
CircularStrip::CircularStrip(gpio_num_t gpio, uint8_t max_leds) : max_leds_(max_leds) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
colors_.resize(max_leds_);
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = gpio;
strip_config.max_leds = max_leds_;
strip_config.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
strip_config.led_model = LED_MODEL_WS2812;
led_strip_rmt_config_t rmt_config = {};
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
led_strip_clear(led_strip_);
esp_timer_create_args_t strip_timer_args = {
.callback = [](void *arg) {
auto strip = static_cast<CircularStrip*>(arg);
std::lock_guard<std::mutex> lock(strip->mutex_);
if (strip->strip_callback_ != nullptr) {
strip->strip_callback_();
}
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "strip_timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&strip_timer_args, &strip_timer_));
}
CircularStrip::~CircularStrip() {
esp_timer_stop(strip_timer_);
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
}
void CircularStrip::SetAllColor(StripColor color) {
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
for (int i = 0; i < max_leds_; i++) {
colors_[i] = color;
led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue);
}
led_strip_refresh(led_strip_);
}
void CircularStrip::SetSingleColor(uint8_t index, StripColor color) {
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
colors_[index] = color;
led_strip_set_pixel(led_strip_, index, color.red, color.green, color.blue);
led_strip_refresh(led_strip_);
}
void CircularStrip::Blink(StripColor color, int interval_ms) {
for (int i = 0; i < max_leds_; i++) {
colors_[i] = color;
}
StartStripTask(interval_ms, [this]() {
static bool on = true;
if (on) {
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
led_strip_refresh(led_strip_);
} else {
led_strip_clear(led_strip_);
}
on = !on;
});
}
void CircularStrip::FadeOut(int interval_ms) {
StartStripTask(interval_ms, [this]() {
bool all_off = true;
for (int i = 0; i < max_leds_; i++) {
colors_[i].red /= 2;
colors_[i].green /= 2;
colors_[i].blue /= 2;
if (colors_[i].red != 0 || colors_[i].green != 0 || colors_[i].blue != 0) {
all_off = false;
}
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
if (all_off) {
led_strip_clear(led_strip_);
esp_timer_stop(strip_timer_);
} else {
led_strip_refresh(led_strip_);
}
});
}
void CircularStrip::Breathe(StripColor low, StripColor high, int interval_ms) {
StartStripTask(interval_ms, [this, low, high]() {
static bool increase = true;
static StripColor color = low;
if (increase) {
if (color.red < high.red) {
color.red++;
}
if (color.green < high.green) {
color.green++;
}
if (color.blue < high.blue) {
color.blue++;
}
if (color.red == high.red && color.green == high.green && color.blue == high.blue) {
increase = false;
}
} else {
if (color.red > low.red) {
color.red--;
}
if (color.green > low.green) {
color.green--;
}
if (color.blue > low.blue) {
color.blue--;
}
if (color.red == low.red && color.green == low.green && color.blue == low.blue) {
increase = true;
}
}
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue);
}
led_strip_refresh(led_strip_);
});
}
void CircularStrip::Scroll(StripColor low, StripColor high, int length, int interval_ms) {
for (int i = 0; i < max_leds_; i++) {
colors_[i] = low;
}
StartStripTask(interval_ms, [this, low, high, length]() {
static int offset = 0;
for (int i = 0; i < max_leds_; i++) {
colors_[i] = low;
}
for (int j = 0; j < length; j++) {
int i = (offset + j) % max_leds_;
colors_[i] = high;
}
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
led_strip_refresh(led_strip_);
offset = (offset + 1) % max_leds_;
});
}
void CircularStrip::StartStripTask(int interval_ms, std::function<void()> cb) {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
strip_callback_ = cb;
esp_timer_start_periodic(strip_timer_, interval_ms * 1000);
}
void CircularStrip::SetBrightness(uint8_t default_brightness, uint8_t low_brightness) {
default_brightness_ = default_brightness;
low_brightness_ = low_brightness;
OnStateChanged();
}
void CircularStrip::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting: {
StripColor low = { 0, 0, 0 };
StripColor high = { low_brightness_, low_brightness_, default_brightness_ };
Scroll(low, high, 3, 100);
break;
}
case kDeviceStateWifiConfiguring: {
StripColor color = { low_brightness_, low_brightness_, default_brightness_ };
Blink(color, 500);
break;
}
case kDeviceStateIdle:
FadeOut(50);
break;
case kDeviceStateConnecting: {
StripColor color = { low_brightness_, low_brightness_, default_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateListening:
case kDeviceStateAudioTesting: {
StripColor color = { default_brightness_, low_brightness_, low_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateSpeaking: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateUpgrading: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
Blink(color, 100);
break;
}
case kDeviceStateActivating: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
Blink(color, 500);
break;
}
default:
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
return;
}
}
#include "circular_strip.h"
#include "application.h"
#include <esp_log.h>
#define TAG "CircularStrip"
#define BLINK_INFINITE -1
CircularStrip::CircularStrip(gpio_num_t gpio, uint8_t max_leds) : max_leds_(max_leds) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
colors_.resize(max_leds_);
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = gpio;
strip_config.max_leds = max_leds_;
strip_config.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
strip_config.led_model = LED_MODEL_WS2812;
led_strip_rmt_config_t rmt_config = {};
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
led_strip_clear(led_strip_);
esp_timer_create_args_t strip_timer_args = {
.callback = [](void *arg) {
auto strip = static_cast<CircularStrip*>(arg);
std::lock_guard<std::mutex> lock(strip->mutex_);
if (strip->strip_callback_ != nullptr) {
strip->strip_callback_();
}
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "strip_timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&strip_timer_args, &strip_timer_));
}
CircularStrip::~CircularStrip() {
esp_timer_stop(strip_timer_);
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
}
void CircularStrip::SetAllColor(StripColor color) {
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
for (int i = 0; i < max_leds_; i++) {
colors_[i] = color;
led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue);
}
led_strip_refresh(led_strip_);
}
void CircularStrip::SetSingleColor(uint8_t index, StripColor color) {
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
colors_[index] = color;
led_strip_set_pixel(led_strip_, index, color.red, color.green, color.blue);
led_strip_refresh(led_strip_);
}
void CircularStrip::Blink(StripColor color, int interval_ms) {
for (int i = 0; i < max_leds_; i++) {
colors_[i] = color;
}
StartStripTask(interval_ms, [this]() {
static bool on = true;
if (on) {
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
led_strip_refresh(led_strip_);
} else {
led_strip_clear(led_strip_);
}
on = !on;
});
}
void CircularStrip::FadeOut(int interval_ms) {
StartStripTask(interval_ms, [this]() {
bool all_off = true;
for (int i = 0; i < max_leds_; i++) {
colors_[i].red /= 2;
colors_[i].green /= 2;
colors_[i].blue /= 2;
if (colors_[i].red != 0 || colors_[i].green != 0 || colors_[i].blue != 0) {
all_off = false;
}
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
if (all_off) {
led_strip_clear(led_strip_);
esp_timer_stop(strip_timer_);
} else {
led_strip_refresh(led_strip_);
}
});
}
void CircularStrip::Breathe(StripColor low, StripColor high, int interval_ms) {
StartStripTask(interval_ms, [this, low, high]() {
static bool increase = true;
static StripColor color = low;
if (increase) {
if (color.red < high.red) {
color.red++;
}
if (color.green < high.green) {
color.green++;
}
if (color.blue < high.blue) {
color.blue++;
}
if (color.red == high.red && color.green == high.green && color.blue == high.blue) {
increase = false;
}
} else {
if (color.red > low.red) {
color.red--;
}
if (color.green > low.green) {
color.green--;
}
if (color.blue > low.blue) {
color.blue--;
}
if (color.red == low.red && color.green == low.green && color.blue == low.blue) {
increase = true;
}
}
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue);
}
led_strip_refresh(led_strip_);
});
}
void CircularStrip::Scroll(StripColor low, StripColor high, int length, int interval_ms) {
for (int i = 0; i < max_leds_; i++) {
colors_[i] = low;
}
StartStripTask(interval_ms, [this, low, high, length]() {
static int offset = 0;
for (int i = 0; i < max_leds_; i++) {
colors_[i] = low;
}
for (int j = 0; j < length; j++) {
int i = (offset + j) % max_leds_;
colors_[i] = high;
}
for (int i = 0; i < max_leds_; i++) {
led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue);
}
led_strip_refresh(led_strip_);
offset = (offset + 1) % max_leds_;
});
}
void CircularStrip::StartStripTask(int interval_ms, std::function<void()> cb) {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(strip_timer_);
strip_callback_ = cb;
esp_timer_start_periodic(strip_timer_, interval_ms * 1000);
}
void CircularStrip::SetBrightness(uint8_t default_brightness, uint8_t low_brightness) {
default_brightness_ = default_brightness;
low_brightness_ = low_brightness;
OnStateChanged();
}
void CircularStrip::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting: {
StripColor low = { 0, 0, 0 };
StripColor high = { low_brightness_, low_brightness_, default_brightness_ };
Scroll(low, high, 3, 100);
break;
}
case kDeviceStateWifiConfiguring: {
StripColor color = { low_brightness_, low_brightness_, default_brightness_ };
Blink(color, 500);
break;
}
case kDeviceStateIdle:
FadeOut(50);
break;
case kDeviceStateConnecting: {
StripColor color = { low_brightness_, low_brightness_, default_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateListening:
case kDeviceStateAudioTesting: {
StripColor color = { default_brightness_, low_brightness_, low_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateSpeaking: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
SetAllColor(color);
break;
}
case kDeviceStateUpgrading: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
Blink(color, 100);
break;
}
case kDeviceStateActivating: {
StripColor color = { low_brightness_, default_brightness_, low_brightness_ };
Blink(color, 500);
break;
}
default:
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
return;
}
}

View File

@@ -1,51 +1,51 @@
#ifndef _CIRCULAR_STRIP_H_
#define _CIRCULAR_STRIP_H_
#include "led.h"
#include <driver/gpio.h>
#include <led_strip.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
#include <vector>
#define DEFAULT_BRIGHTNESS 32
#define LOW_BRIGHTNESS 4
struct StripColor {
uint8_t red = 0, green = 0, blue = 0;
};
class CircularStrip : public Led {
public:
CircularStrip(gpio_num_t gpio, uint8_t max_leds);
virtual ~CircularStrip();
void OnStateChanged() override;
void SetBrightness(uint8_t default_brightness, uint8_t low_brightness);
void SetAllColor(StripColor color);
void SetSingleColor(uint8_t index, StripColor color);
void Blink(StripColor color, int interval_ms);
void Breathe(StripColor low, StripColor high, int interval_ms);
void Scroll(StripColor low, StripColor high, int length, int interval_ms);
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
led_strip_handle_t led_strip_ = nullptr;
int max_leds_ = 0;
std::vector<StripColor> colors_;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t strip_timer_ = nullptr;
std::function<void()> strip_callback_ = nullptr;
uint8_t default_brightness_ = DEFAULT_BRIGHTNESS;
uint8_t low_brightness_ = LOW_BRIGHTNESS;
void StartStripTask(int interval_ms, std::function<void()> cb);
void Rainbow(StripColor low, StripColor high, int interval_ms);
void FadeOut(int interval_ms);
};
#endif // _CIRCULAR_STRIP_H_
#ifndef _CIRCULAR_STRIP_H_
#define _CIRCULAR_STRIP_H_
#include "led.h"
#include <driver/gpio.h>
#include <led_strip.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
#include <vector>
#define DEFAULT_BRIGHTNESS 32
#define LOW_BRIGHTNESS 4
struct StripColor {
uint8_t red = 0, green = 0, blue = 0;
};
class CircularStrip : public Led {
public:
CircularStrip(gpio_num_t gpio, uint8_t max_leds);
virtual ~CircularStrip();
void OnStateChanged() override;
void SetBrightness(uint8_t default_brightness, uint8_t low_brightness);
void SetAllColor(StripColor color);
void SetSingleColor(uint8_t index, StripColor color);
void Blink(StripColor color, int interval_ms);
void Breathe(StripColor low, StripColor high, int interval_ms);
void Scroll(StripColor low, StripColor high, int length, int interval_ms);
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
led_strip_handle_t led_strip_ = nullptr;
int max_leds_ = 0;
std::vector<StripColor> colors_;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t strip_timer_ = nullptr;
std::function<void()> strip_callback_ = nullptr;
uint8_t default_brightness_ = DEFAULT_BRIGHTNESS;
uint8_t low_brightness_ = LOW_BRIGHTNESS;
void StartStripTask(int interval_ms, std::function<void()> cb);
void Rainbow(StripColor low, StripColor high, int interval_ms);
void FadeOut(int interval_ms);
};
#endif // _CIRCULAR_STRIP_H_

View File

@@ -1,249 +1,249 @@
#include "gpio_led.h"
#include "application.h"
#include "device_state.h"
#include <esp_log.h>
#define TAG "GpioLed"
#define DEFAULT_BRIGHTNESS 50
#define HIGH_BRIGHTNESS 100
#define LOW_BRIGHTNESS 10
#define IDLE_BRIGHTNESS 5
#define SPEAKING_BRIGHTNESS 75
#define UPGRADING_BRIGHTNESS 25
#define ACTIVATING_BRIGHTNESS 35
#define BLINK_INFINITE -1
// GPIO_LED
#define LEDC_LS_TIMER LEDC_TIMER_1
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY (8191)
#define LEDC_FADE_TIME (1000)
// GPIO_LED
GpioLed::GpioLed(gpio_num_t gpio)
: GpioLed(gpio, 0, LEDC_LS_TIMER, LEDC_LS_CH0_CHANNEL) {
}
GpioLed::GpioLed(gpio_num_t gpio, int output_invert)
: GpioLed(gpio, output_invert, LEDC_LS_TIMER, LEDC_LS_CH0_CHANNEL) {
}
GpioLed::GpioLed(gpio_num_t gpio, int output_invert, ledc_timer_t timer_num, ledc_channel_t channel) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
/*
* Prepare and set configuration of timers
* that will be used by LED Controller
*/
ledc_timer_config_t ledc_timer = {};
ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; // resolution of PWM duty
ledc_timer.freq_hz = 4000; // frequency of PWM signal
ledc_timer.speed_mode = LEDC_LS_MODE; // timer mode
ledc_timer.timer_num = timer_num; // timer index
ledc_timer.clk_cfg = LEDC_AUTO_CLK; // Auto select the source clock
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_.channel = channel,
ledc_channel_.duty = 0,
ledc_channel_.gpio_num = gpio,
ledc_channel_.speed_mode = LEDC_LS_MODE,
ledc_channel_.hpoint = 0,
ledc_channel_.timer_sel = timer_num,
ledc_channel_.flags.output_invert = output_invert & 0x01,
// Set LED Controller with previously prepared configuration
ledc_channel_config(&ledc_channel_);
// Initialize fade service.
ledc_fade_func_install(0);
// When the callback registered by ledc_cb_degister is called, run led ->OnFadeEnd()
ledc_cbs_t ledc_callbacks = {
.fade_cb = FadeCallback
};
ledc_cb_register(ledc_channel_.speed_mode, ledc_channel_.channel, &ledc_callbacks, this);
esp_timer_create_args_t blink_timer_args = {
.callback = [](void *arg) {
auto led = static_cast<GpioLed*>(arg);
led->OnBlinkTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "Blink Timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&blink_timer_args, &blink_timer_));
ledc_initialized_ = true;
}
GpioLed::~GpioLed() {
esp_timer_stop(blink_timer_);
if (ledc_initialized_) {
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_fade_func_uninstall();
}
}
void GpioLed::SetBrightness(uint8_t brightness) {
if (brightness == 100) {
duty_ = LEDC_DUTY;
} else {
duty_ = brightness * LEDC_DUTY / 100;
}
}
void GpioLed::TurnOn() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_);
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::TurnOff() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0);
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::BlinkOnce() {
Blink(1, 100);
}
void GpioLed::Blink(int times, int interval_ms) {
StartBlinkTask(times, interval_ms);
}
void GpioLed::StartContinuousBlink(int interval_ms) {
StartBlinkTask(BLINK_INFINITE, interval_ms);
}
void GpioLed::StartBlinkTask(int times, int interval_ms) {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
blink_counter_ = times * 2;
blink_interval_ms_ = interval_ms;
esp_timer_start_periodic(blink_timer_, interval_ms * 1000);
}
void GpioLed::OnBlinkTimer() {
std::lock_guard<std::mutex> lock(mutex_);
blink_counter_--;
if (blink_counter_ & 1) {
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_);
} else {
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0);
if (blink_counter_ == 0) {
esp_timer_stop(blink_timer_);
}
}
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::StartFadeTask() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
fade_up_ = true;
ledc_set_fade_with_time(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_DUTY, LEDC_FADE_TIME);
ledc_fade_start(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_FADE_NO_WAIT);
}
void GpioLed::OnFadeEnd() {
std::lock_guard<std::mutex> lock(mutex_);
fade_up_ = !fade_up_;
ledc_set_fade_with_time(ledc_channel_.speed_mode,
ledc_channel_.channel, fade_up_ ? LEDC_DUTY : 0, LEDC_FADE_TIME);
ledc_fade_start(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_FADE_NO_WAIT);
}
bool IRAM_ATTR GpioLed::FadeCallback(const ledc_cb_param_t *param, void *user_arg) {
if (param->event == LEDC_FADE_END_EVT) {
auto led = static_cast<GpioLed*>(user_arg);
led->OnFadeEnd();
}
return true;
}
void GpioLed::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting:
SetBrightness(DEFAULT_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateWifiConfiguring:
SetBrightness(DEFAULT_BRIGHTNESS);
StartContinuousBlink(500);
break;
case kDeviceStateIdle:
SetBrightness(IDLE_BRIGHTNESS);
TurnOn();
// TurnOff();
break;
case kDeviceStateConnecting:
SetBrightness(DEFAULT_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) {
SetBrightness(HIGH_BRIGHTNESS);
} else {
SetBrightness(LOW_BRIGHTNESS);
}
// TurnOn();
StartFadeTask();
break;
case kDeviceStateSpeaking:
SetBrightness(SPEAKING_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateUpgrading:
SetBrightness(UPGRADING_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateActivating:
SetBrightness(ACTIVATING_BRIGHTNESS);
StartContinuousBlink(500);
break;
default:
ESP_LOGE(TAG, "Unknown gpio led event: %d", device_state);
return;
}
}
#include "gpio_led.h"
#include "application.h"
#include "device_state.h"
#include <esp_log.h>
#define TAG "GpioLed"
#define DEFAULT_BRIGHTNESS 50
#define HIGH_BRIGHTNESS 100
#define LOW_BRIGHTNESS 10
#define IDLE_BRIGHTNESS 5
#define SPEAKING_BRIGHTNESS 75
#define UPGRADING_BRIGHTNESS 25
#define ACTIVATING_BRIGHTNESS 35
#define BLINK_INFINITE -1
// GPIO_LED
#define LEDC_LS_TIMER LEDC_TIMER_1
#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE
#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY (8191)
#define LEDC_FADE_TIME (1000)
// GPIO_LED
GpioLed::GpioLed(gpio_num_t gpio)
: GpioLed(gpio, 0, LEDC_LS_TIMER, LEDC_LS_CH0_CHANNEL) {
}
GpioLed::GpioLed(gpio_num_t gpio, int output_invert)
: GpioLed(gpio, output_invert, LEDC_LS_TIMER, LEDC_LS_CH0_CHANNEL) {
}
GpioLed::GpioLed(gpio_num_t gpio, int output_invert, ledc_timer_t timer_num, ledc_channel_t channel) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
/*
* Prepare and set configuration of timers
* that will be used by LED Controller
*/
ledc_timer_config_t ledc_timer = {};
ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; // resolution of PWM duty
ledc_timer.freq_hz = 4000; // frequency of PWM signal
ledc_timer.speed_mode = LEDC_LS_MODE; // timer mode
ledc_timer.timer_num = timer_num; // timer index
ledc_timer.clk_cfg = LEDC_AUTO_CLK; // Auto select the source clock
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_.channel = channel,
ledc_channel_.duty = 0,
ledc_channel_.gpio_num = gpio,
ledc_channel_.speed_mode = LEDC_LS_MODE,
ledc_channel_.hpoint = 0,
ledc_channel_.timer_sel = timer_num,
ledc_channel_.flags.output_invert = output_invert & 0x01,
// Set LED Controller with previously prepared configuration
ledc_channel_config(&ledc_channel_);
// Initialize fade service.
ledc_fade_func_install(0);
// When the callback registered by ledc_cb_degister is called, run led ->OnFadeEnd()
ledc_cbs_t ledc_callbacks = {
.fade_cb = FadeCallback
};
ledc_cb_register(ledc_channel_.speed_mode, ledc_channel_.channel, &ledc_callbacks, this);
esp_timer_create_args_t blink_timer_args = {
.callback = [](void *arg) {
auto led = static_cast<GpioLed*>(arg);
led->OnBlinkTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "Blink Timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&blink_timer_args, &blink_timer_));
ledc_initialized_ = true;
}
GpioLed::~GpioLed() {
esp_timer_stop(blink_timer_);
if (ledc_initialized_) {
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_fade_func_uninstall();
}
}
void GpioLed::SetBrightness(uint8_t brightness) {
if (brightness == 100) {
duty_ = LEDC_DUTY;
} else {
duty_ = brightness * LEDC_DUTY / 100;
}
}
void GpioLed::TurnOn() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_);
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::TurnOff() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0);
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::BlinkOnce() {
Blink(1, 100);
}
void GpioLed::Blink(int times, int interval_ms) {
StartBlinkTask(times, interval_ms);
}
void GpioLed::StartContinuousBlink(int interval_ms) {
StartBlinkTask(BLINK_INFINITE, interval_ms);
}
void GpioLed::StartBlinkTask(int times, int interval_ms) {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
blink_counter_ = times * 2;
blink_interval_ms_ = interval_ms;
esp_timer_start_periodic(blink_timer_, interval_ms * 1000);
}
void GpioLed::OnBlinkTimer() {
std::lock_guard<std::mutex> lock(mutex_);
blink_counter_--;
if (blink_counter_ & 1) {
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_);
} else {
ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0);
if (blink_counter_ == 0) {
esp_timer_stop(blink_timer_);
}
}
ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel);
}
void GpioLed::StartFadeTask() {
if (!ledc_initialized_) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel);
fade_up_ = true;
ledc_set_fade_with_time(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_DUTY, LEDC_FADE_TIME);
ledc_fade_start(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_FADE_NO_WAIT);
}
void GpioLed::OnFadeEnd() {
std::lock_guard<std::mutex> lock(mutex_);
fade_up_ = !fade_up_;
ledc_set_fade_with_time(ledc_channel_.speed_mode,
ledc_channel_.channel, fade_up_ ? LEDC_DUTY : 0, LEDC_FADE_TIME);
ledc_fade_start(ledc_channel_.speed_mode,
ledc_channel_.channel, LEDC_FADE_NO_WAIT);
}
bool IRAM_ATTR GpioLed::FadeCallback(const ledc_cb_param_t *param, void *user_arg) {
if (param->event == LEDC_FADE_END_EVT) {
auto led = static_cast<GpioLed*>(user_arg);
led->OnFadeEnd();
}
return true;
}
void GpioLed::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting:
SetBrightness(DEFAULT_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateWifiConfiguring:
SetBrightness(DEFAULT_BRIGHTNESS);
StartContinuousBlink(500);
break;
case kDeviceStateIdle:
SetBrightness(IDLE_BRIGHTNESS);
TurnOn();
// TurnOff();
break;
case kDeviceStateConnecting:
SetBrightness(DEFAULT_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) {
SetBrightness(HIGH_BRIGHTNESS);
} else {
SetBrightness(LOW_BRIGHTNESS);
}
// TurnOn();
StartFadeTask();
break;
case kDeviceStateSpeaking:
SetBrightness(SPEAKING_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateUpgrading:
SetBrightness(UPGRADING_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateActivating:
SetBrightness(ACTIVATING_BRIGHTNESS);
StartContinuousBlink(500);
break;
default:
ESP_LOGE(TAG, "Unknown gpio led event: %d", device_state);
return;
}
}

View File

@@ -1,47 +1,47 @@
#ifndef _GPIO_LED_H_
#define _GPIO_LED_H_
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "led.h"
#include <driver/gpio.h>
#include <driver/ledc.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
class GpioLed : public Led {
public:
GpioLed(gpio_num_t gpio);
GpioLed(gpio_num_t gpio, int output_invert);
GpioLed(gpio_num_t gpio, int output_invert, ledc_timer_t timer_num, ledc_channel_t channel);
virtual ~GpioLed();
void OnStateChanged() override;
void TurnOn();
void TurnOff();
void SetBrightness(uint8_t brightness);
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
ledc_channel_config_t ledc_channel_ = {0};
bool ledc_initialized_ = false;
uint32_t duty_ = 0;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t blink_timer_ = nullptr;
bool fade_up_ = true;
void StartBlinkTask(int times, int interval_ms);
void OnBlinkTimer();
void BlinkOnce();
void Blink(int times, int interval_ms);
void StartContinuousBlink(int interval_ms);
void StartFadeTask();
void OnFadeEnd();
static bool IRAM_ATTR FadeCallback(const ledc_cb_param_t *param, void *user_arg);
};
#endif // _GPIO_LED_H_
#ifndef _GPIO_LED_H_
#define _GPIO_LED_H_
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "led.h"
#include <driver/gpio.h>
#include <driver/ledc.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
class GpioLed : public Led {
public:
GpioLed(gpio_num_t gpio);
GpioLed(gpio_num_t gpio, int output_invert);
GpioLed(gpio_num_t gpio, int output_invert, ledc_timer_t timer_num, ledc_channel_t channel);
virtual ~GpioLed();
void OnStateChanged() override;
void TurnOn();
void TurnOff();
void SetBrightness(uint8_t brightness);
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
ledc_channel_config_t ledc_channel_ = {0};
bool ledc_initialized_ = false;
uint32_t duty_ = 0;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t blink_timer_ = nullptr;
bool fade_up_ = true;
void StartBlinkTask(int times, int interval_ms);
void OnBlinkTimer();
void BlinkOnce();
void Blink(int times, int interval_ms);
void StartContinuousBlink(int interval_ms);
void StartFadeTask();
void OnFadeEnd();
static bool IRAM_ATTR FadeCallback(const ledc_cb_param_t *param, void *user_arg);
};
#endif // _GPIO_LED_H_

View File

@@ -1,17 +1,17 @@
#ifndef _LED_H_
#define _LED_H_
class Led {
public:
virtual ~Led() = default;
// Set the led state based on the device state
virtual void OnStateChanged() = 0;
};
class NoLed : public Led {
public:
virtual void OnStateChanged() override {}
};
#endif // _LED_H_
#ifndef _LED_H_
#define _LED_H_
class Led {
public:
virtual ~Led() = default;
// Set the led state based on the device state
virtual void OnStateChanged() = 0;
};
class NoLed : public Led {
public:
virtual void OnStateChanged() override {}
};
#endif // _LED_H_

View File

@@ -1,163 +1,163 @@
#include "single_led.h"
#include "application.h"
#include <esp_log.h>
#define TAG "SingleLed"
#define DEFAULT_BRIGHTNESS 4
#define HIGH_BRIGHTNESS 16
#define LOW_BRIGHTNESS 2
#define BLINK_INFINITE -1
SingleLed::SingleLed(gpio_num_t gpio) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = gpio;
strip_config.max_leds = 1;
strip_config.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
strip_config.led_model = LED_MODEL_WS2812;
led_strip_rmt_config_t rmt_config = {};
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
led_strip_clear(led_strip_);
esp_timer_create_args_t blink_timer_args = {
.callback = [](void *arg) {
auto led = static_cast<SingleLed*>(arg);
led->OnBlinkTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "blink_timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&blink_timer_args, &blink_timer_));
}
SingleLed::~SingleLed() {
esp_timer_stop(blink_timer_);
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
}
void SingleLed::SetColor(uint8_t r, uint8_t g, uint8_t b) {
r_ = r;
g_ = g;
b_ = b;
}
void SingleLed::TurnOn() {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
led_strip_refresh(led_strip_);
}
void SingleLed::TurnOff() {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
led_strip_clear(led_strip_);
}
void SingleLed::BlinkOnce() {
Blink(1, 100);
}
void SingleLed::Blink(int times, int interval_ms) {
StartBlinkTask(times, interval_ms);
}
void SingleLed::StartContinuousBlink(int interval_ms) {
StartBlinkTask(BLINK_INFINITE, interval_ms);
}
void SingleLed::StartBlinkTask(int times, int interval_ms) {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
blink_counter_ = times * 2;
blink_interval_ms_ = interval_ms;
esp_timer_start_periodic(blink_timer_, interval_ms * 1000);
}
void SingleLed::OnBlinkTimer() {
std::lock_guard<std::mutex> lock(mutex_);
blink_counter_--;
if (blink_counter_ & 1) {
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
led_strip_refresh(led_strip_);
} else {
led_strip_clear(led_strip_);
if (blink_counter_ == 0) {
esp_timer_stop(blink_timer_);
}
}
}
void SingleLed::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateWifiConfiguring:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
StartContinuousBlink(500);
break;
case kDeviceStateIdle:
TurnOff();
break;
case kDeviceStateConnecting:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) {
SetColor(HIGH_BRIGHTNESS, 0, 0);
} else {
SetColor(LOW_BRIGHTNESS, 0, 0);
}
TurnOn();
break;
case kDeviceStateSpeaking:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
TurnOn();
break;
case kDeviceStateUpgrading:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
StartContinuousBlink(100);
break;
case kDeviceStateActivating:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
StartContinuousBlink(500);
break;
default:
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
return;
}
}
#include "single_led.h"
#include "application.h"
#include <esp_log.h>
#define TAG "SingleLed"
#define DEFAULT_BRIGHTNESS 4
#define HIGH_BRIGHTNESS 16
#define LOW_BRIGHTNESS 2
#define BLINK_INFINITE -1
SingleLed::SingleLed(gpio_num_t gpio) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = gpio;
strip_config.max_leds = 1;
strip_config.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
strip_config.led_model = LED_MODEL_WS2812;
led_strip_rmt_config_t rmt_config = {};
rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_));
led_strip_clear(led_strip_);
esp_timer_create_args_t blink_timer_args = {
.callback = [](void *arg) {
auto led = static_cast<SingleLed*>(arg);
led->OnBlinkTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "blink_timer",
.skip_unhandled_events = false,
};
ESP_ERROR_CHECK(esp_timer_create(&blink_timer_args, &blink_timer_));
}
SingleLed::~SingleLed() {
esp_timer_stop(blink_timer_);
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
}
void SingleLed::SetColor(uint8_t r, uint8_t g, uint8_t b) {
r_ = r;
g_ = g;
b_ = b;
}
void SingleLed::TurnOn() {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
led_strip_refresh(led_strip_);
}
void SingleLed::TurnOff() {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
led_strip_clear(led_strip_);
}
void SingleLed::BlinkOnce() {
Blink(1, 100);
}
void SingleLed::Blink(int times, int interval_ms) {
StartBlinkTask(times, interval_ms);
}
void SingleLed::StartContinuousBlink(int interval_ms) {
StartBlinkTask(BLINK_INFINITE, interval_ms);
}
void SingleLed::StartBlinkTask(int times, int interval_ms) {
if (led_strip_ == nullptr) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
esp_timer_stop(blink_timer_);
blink_counter_ = times * 2;
blink_interval_ms_ = interval_ms;
esp_timer_start_periodic(blink_timer_, interval_ms * 1000);
}
void SingleLed::OnBlinkTimer() {
std::lock_guard<std::mutex> lock(mutex_);
blink_counter_--;
if (blink_counter_ & 1) {
led_strip_set_pixel(led_strip_, 0, r_, g_, b_);
led_strip_refresh(led_strip_);
} else {
led_strip_clear(led_strip_);
if (blink_counter_ == 0) {
esp_timer_stop(blink_timer_);
}
}
}
void SingleLed::OnStateChanged() {
auto& app = Application::GetInstance();
auto device_state = app.GetDeviceState();
switch (device_state) {
case kDeviceStateStarting:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
StartContinuousBlink(100);
break;
case kDeviceStateWifiConfiguring:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
StartContinuousBlink(500);
break;
case kDeviceStateIdle:
TurnOff();
break;
case kDeviceStateConnecting:
SetColor(0, 0, DEFAULT_BRIGHTNESS);
TurnOn();
break;
case kDeviceStateListening:
case kDeviceStateAudioTesting:
if (app.IsVoiceDetected()) {
SetColor(HIGH_BRIGHTNESS, 0, 0);
} else {
SetColor(LOW_BRIGHTNESS, 0, 0);
}
TurnOn();
break;
case kDeviceStateSpeaking:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
TurnOn();
break;
case kDeviceStateUpgrading:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
StartContinuousBlink(100);
break;
case kDeviceStateActivating:
SetColor(0, DEFAULT_BRIGHTNESS, 0);
StartContinuousBlink(500);
break;
default:
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
return;
}
}

View File

@@ -1,38 +1,38 @@
#ifndef _SINGLE_LED_H_
#define _SINGLE_LED_H_
#include "led.h"
#include <driver/gpio.h>
#include <led_strip.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
class SingleLed : public Led {
public:
SingleLed(gpio_num_t gpio);
virtual ~SingleLed();
void OnStateChanged() override;
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
led_strip_handle_t led_strip_ = nullptr;
uint8_t r_ = 0, g_ = 0, b_ = 0;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t blink_timer_ = nullptr;
void StartBlinkTask(int times, int interval_ms);
void OnBlinkTimer();
void BlinkOnce();
void Blink(int times, int interval_ms);
void StartContinuousBlink(int interval_ms);
void TurnOn();
void TurnOff();
void SetColor(uint8_t r, uint8_t g, uint8_t b);
};
#endif // _SINGLE_LED_H_
#ifndef _SINGLE_LED_H_
#define _SINGLE_LED_H_
#include "led.h"
#include <driver/gpio.h>
#include <led_strip.h>
#include <esp_timer.h>
#include <atomic>
#include <mutex>
class SingleLed : public Led {
public:
SingleLed(gpio_num_t gpio);
virtual ~SingleLed();
void OnStateChanged() override;
private:
std::mutex mutex_;
TaskHandle_t blink_task_ = nullptr;
led_strip_handle_t led_strip_ = nullptr;
uint8_t r_ = 0, g_ = 0, b_ = 0;
int blink_counter_ = 0;
int blink_interval_ms_ = 0;
esp_timer_handle_t blink_timer_ = nullptr;
void StartBlinkTask(int times, int interval_ms);
void OnBlinkTimer();
void BlinkOnce();
void Blink(int times, int interval_ms);
void StartContinuousBlink(int interval_ms);
void TurnOn();
void TurnOff();
void SetColor(uint8_t r, uint8_t g, uint8_t b);
};
#endif // _SINGLE_LED_H_