Files
watch-watch/main/ws2812_status.c

196 lines
5.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ws2812_status.h"
#include "dcdc_controller.h"
#include "led_strip.h"
#include "esp_check.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#ifndef CONFIG_WATCH_WS2812_LED_COUNT
#define CONFIG_WATCH_WS2812_LED_COUNT 5
#endif
#ifndef CONFIG_WATCH_WS2812_GPIO
#define CONFIG_WATCH_WS2812_GPIO 8
#endif
#ifndef CONFIG_WATCH_WS2812_RMT_RESOLUTION
#define CONFIG_WATCH_WS2812_RMT_RESOLUTION (10 * 1000 * 1000)
#endif
#define WS2812_STATUS_GPIO ((gpio_num_t)CONFIG_WATCH_WS2812_GPIO)
#define WS2812_STATUS_RESOLUTION_HZ CONFIG_WATCH_WS2812_RMT_RESOLUTION
static const char *TAG = "ws2812";
static led_strip_handle_t s_strip;
static bool s_led_state[WS2812_STATUS_LED_COUNT];
static bool s_error_state;
static size_t s_active_channel = SIZE_MAX;
static esp_err_t ws2812_status_apply(void)
{
if (!s_strip) {
return ESP_ERR_INVALID_STATE;
}
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
uint8_t r = 0, g = 0, b = 0;
if (s_error_state) {
r = 40;
} else if (s_active_channel == i) {
g = 60;
} else if (s_led_state[i]) {
g = 18;
} else {
b = 10;
}
ESP_RETURN_ON_ERROR(led_strip_set_pixel(s_strip, i, r, g, b), TAG,
"led pixel set failed");
}
return led_strip_refresh(s_strip);
}
esp_err_t ws2812_status_init(void)
{
if (s_strip) {
return ESP_OK;
}
led_strip_config_t strip_config = {
.strip_gpio_num = WS2812_STATUS_GPIO,
.max_leds = WS2812_STATUS_LED_COUNT,
.led_model = LED_MODEL_WS2812,
.flags.invert_out = false,
};
led_strip_rmt_config_t rmt_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = WS2812_STATUS_RESOLUTION_HZ,
.flags.with_dma = false,
};
ESP_RETURN_ON_ERROR(
led_strip_new_rmt_device(&strip_config, &rmt_cfg, &s_strip),
TAG,
"Не вдалося створити RMT пристрій для WS2812");
ESP_RETURN_ON_ERROR(led_strip_clear(s_strip), TAG, "clear fail");
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
s_led_state[i] = false;
}
s_active_channel = SIZE_MAX;
s_error_state = false;
return ws2812_status_apply();
}
esp_err_t ws2812_status_set_channel_state(size_t channel, bool enabled)
{
if (channel >= WS2812_STATUS_LED_COUNT) {
return ESP_ERR_INVALID_ARG;
}
s_led_state[channel] = enabled;
return ws2812_status_apply();
}
esp_err_t ws2812_status_mark_active(size_t channel)
{
if (channel >= WS2812_STATUS_LED_COUNT) {
return ESP_ERR_INVALID_ARG;
}
s_active_channel = channel;
return ws2812_status_apply();
}
esp_err_t ws2812_status_clear_active(void)
{
s_active_channel = SIZE_MAX;
return ws2812_status_apply();
}
esp_err_t ws2812_status_set_error(bool has_error)
{
s_error_state = has_error;
if (has_error) {
s_active_channel = SIZE_MAX;
}
return ws2812_status_apply();
}
esp_err_t ws2812_status_refresh_from_dcdc(void)
{
const size_t available_channels = dcdc_channel_count();
const size_t count = available_channels < WS2812_STATUS_LED_COUNT
? available_channels
: WS2812_STATUS_LED_COUNT;
for (size_t i = 0; i < count; ++i) {
s_led_state[i] = dcdc_get_state(i);
}
for (size_t i = count; i < WS2812_STATUS_LED_COUNT; ++i) {
s_led_state[i] = false;
}
return ws2812_status_apply();
}
esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms)
{
if (!s_strip) {
return ESP_ERR_INVALID_STATE;
}
if (cycles == 0) {
cycles = 1;
}
if (step_delay_ms == 0) {
step_delay_ms = 150;
}
bool saved_led_state[WS2812_STATUS_LED_COUNT];
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
saved_led_state[i] = s_led_state[i];
}
const bool saved_error = s_error_state;
const size_t saved_active = s_active_channel;
s_error_state = false;
s_active_channel = SIZE_MAX;
const TickType_t delay_ticks = pdMS_TO_TICKS(step_delay_ms);
esp_err_t err = ESP_OK;
for (size_t cycle = 0; cycle < cycles && err == ESP_OK; ++cycle) {
for (size_t led = 0; led < WS2812_STATUS_LED_COUNT; ++led) {
for (size_t idx = 0; idx < WS2812_STATUS_LED_COUNT; ++idx) {
const uint8_t g = (idx == led) ? 35 : 0;
err = led_strip_set_pixel(s_strip, idx, 0, g, 0);
if (err != ESP_OK) {
break;
}
}
if (err != ESP_OK) {
break;
}
err = led_strip_refresh(s_strip);
if (err != ESP_OK) {
break;
}
vTaskDelay(delay_ticks);
}
}
for (size_t i = 0; i < WS2812_STATUS_LED_COUNT; ++i) {
s_led_state[i] = saved_led_state[i];
}
s_error_state = saved_error;
s_active_channel = saved_active;
esp_err_t restore_err = ws2812_status_apply();
if (err != ESP_OK) {
return err;
}
return restore_err;
}