Add KiCad files and USB CDC logging support

This commit is contained in:
2025-12-16 08:57:32 +02:00
parent de959b9a8b
commit f3d5e4018b
18 changed files with 7557 additions and 14 deletions

View File

@@ -5,7 +5,7 @@
"idf.openOcdConfigs": [
"board/esp32s3-builtin.cfg"
],
"idf.port": "/dev/tty.BLTH",
"idf.port": "/dev/tty.usbmodem14301",
"idf.toolsPath": "/Users/tarassivas/.espressif",
"idf.customExtraVars": {
"IDF_TARGET": "esp32s3"
@@ -15,5 +15,6 @@
"--background-index",
"--query-driver=/Users/tarassivas/.espressif/tools/xtensa-esp-elf/esp-14.2.0_20241119/xtensa-esp-elf/bin/xtensa-esp32-elf-gcc",
"--compile-commands-dir=${workspaceFolder}/build"
]
],
"idf.flashType": "UART"
}

24
kicad/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Temporary and local KiCad artifacts
*-backups/
*.kicad_prl
*.kicad_pro-bak
*.kicad_sch-bak
*.kicad_pcb-bak
*.sch-bak
*.pcb-bak
*.bak
# Cache and automatically generated tables
sym-lib-table-bak
fp-lib-table-bak
*-cache.lib
*-cache.dcm
# Generated fabrication outputs (regenerate as needed)
*.net
*.xml
*.csv
*.tsv
*.pos
*.ipr
*.rpt

View File

@@ -0,0 +1,2 @@
(kicad_pcb (version 20241229) (generator "pcbnew") (generator_version "9.0")
)

View File

@@ -0,0 +1,417 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"rules": {},
"track_widths": [],
"via_dimensions": []
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "watch-watchkicad_pro.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 4
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "Default Editing",
"sort_asc": true,
"sort_field": "Налаштування"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"e717a214-aafd-4c38-81fe-477ca8e64d07",
"Root"
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -1,6 +1,6 @@
idf_component_register(SRCS "main.c"
"dcdc_controller.c"
"usb_cdc_cli.c"
"usb_cdc_log.c"
"ws2812_status.c"
"ina226_monitor.c"
"uart_mux.c"

View File

@@ -1,5 +1,34 @@
menu "Налаштування watch-watch"
menu "DC/DC контролер"
config WATCH_DCDC_EN_GPIO_0
int "GPIO EN каналу 0"
range 0 48
default 2
config WATCH_DCDC_EN_GPIO_1
int "GPIO EN каналу 1"
range 0 48
default 4
config WATCH_DCDC_EN_GPIO_2
int "GPIO EN каналу 2"
range 0 48
default 5
config WATCH_DCDC_EN_GPIO_3
int "GPIO EN каналу 3"
range 0 48
default 18
config WATCH_DCDC_EN_GPIO_4
int "GPIO EN каналу 4"
range 0 48
default 19
endmenu
config WATCH_WS2812_LED_COUNT
int "Кількість статусних світлодіодів WS2812"
range 1 30

View File

@@ -2,15 +2,32 @@
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "dcdc";
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_0
#define CONFIG_WATCH_DCDC_EN_GPIO_0 2
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_1
#define CONFIG_WATCH_DCDC_EN_GPIO_1 4
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_2
#define CONFIG_WATCH_DCDC_EN_GPIO_2 5
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_3
#define CONFIG_WATCH_DCDC_EN_GPIO_3 18
#endif
#ifndef CONFIG_WATCH_DCDC_EN_GPIO_4
#define CONFIG_WATCH_DCDC_EN_GPIO_4 19
#endif
static const gpio_num_t s_dcdc_gpio_map[DCDC_CHANNEL_COUNT] = {
GPIO_NUM_2,
GPIO_NUM_4,
GPIO_NUM_5,
GPIO_NUM_18,
GPIO_NUM_19,
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_0,
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_1,
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_2,
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_3,
(gpio_num_t)CONFIG_WATCH_DCDC_EN_GPIO_4,
};
static bool s_initialized;

View File

@@ -1,3 +1,9 @@
/*
* Developed by TComLab
* Version: v0.1
* Date: 2025-12-15
*/
#include "ina226_monitor.h"
#include <math.h>
@@ -50,6 +56,8 @@ static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
static ina226_reading_t s_last_reading;
#endif
// Ініціалізує контролер INA226: налаштовує I2C, записує конфігурацію та
// калібрувальні значення, а також зберігає початковий стан вимірювань.
esp_err_t ina226_monitor_init(void)
{
#if !CONFIG_WATCH_INA226_ENABLED
@@ -103,6 +111,7 @@ esp_err_t ina226_monitor_init(void)
#endif
}
// Дозволяє швидко перевірити, чи був модуль INA226 успішно ініціалізований.
bool ina226_monitor_ready(void)
{
#if CONFIG_WATCH_INA226_ENABLED
@@ -112,12 +121,14 @@ bool ina226_monitor_ready(void)
#endif
}
// INA226 вимірює єдиний канал живлення, тому повертаємо 1.
size_t ina226_monitor_channel_count(void)
{
return 1;
}
#if CONFIG_WATCH_INA226_ENABLED
// Зчитує 16-бітний регістр INA226, використовуючи транзакцію write-then-read.
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
{
uint8_t value[2];
@@ -132,6 +143,7 @@ static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
}
#endif
// Виконує одне вимірювання напруги/струму через INA226 та кешує результат.
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
{
#if !CONFIG_WATCH_INA226_ENABLED
@@ -162,6 +174,7 @@ esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
#endif
}
// Повертає останній виміряний набір даних без нового звернення до шини I2C.
const ina226_reading_t *ina226_monitor_get_last(void)
{
#if CONFIG_WATCH_INA226_ENABLED

View File

@@ -1,3 +1,9 @@
/*
* Developed by TComLab
* Version: v0.1
* Date: 2025-12-15
*/
#pragma once
#include <stdbool.h>
@@ -11,8 +17,13 @@ typedef struct {
float power_mw;
} ina226_reading_t;
// Ініціалізує INA226: конфігурує I2C та калібрує вимірювач.
esp_err_t ina226_monitor_init(void);
// true, якщо драйвер вже ініціалізований.
bool ina226_monitor_ready(void);
// INA226 підтримує один канал, але інтерфейс залишається узагальненим.
size_t ina226_monitor_channel_count(void);
// Зчитує нові дані; out_reading може бути NULL, якщо дані не потрібні.
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading);
// Повертає кеш останнього вимірювання або NULL, якщо модуль неактивний.
const ina226_reading_t *ina226_monitor_get_last(void);

View File

@@ -1,3 +1,9 @@
/*
* Developed by TComLab
* Version: v0.1
* Date: 2025-12-15
*/
#include <stddef.h>
#include "freertos/FreeRTOS.h"
@@ -7,13 +13,21 @@
#include "dcdc_controller.h"
#include "ina226_monitor.h"
#include "uart_mux.h"
#include "usb_cdc_cli.h"
#include "usb_cdc_log.h"
#include "ws2812_status.h"
static const char *TAG = "watch-watch";
void app_main(void)
{
if (usb_cdc_log_init() != ESP_OK) {
ESP_LOGW(TAG, "USB CDC лог недоступний");
} else {
ESP_LOGI(TAG, "USB CDC лог активовано");
}
vTaskDelay(pdMS_TO_TICKS(2000)); // Затримка для стабілізації живлення після перезавантаження
ESP_LOGI(TAG, "Запуск watch-watch systems");
if (dcdc_init() != ESP_OK) {
ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера");
ws2812_status_init();
@@ -23,6 +37,10 @@ void app_main(void)
if (ws2812_status_init() == ESP_OK) {
ws2812_status_refresh_from_dcdc();
esp_err_t anim_err = ws2812_status_play_bringup_animation(2, 120);
if (anim_err != ESP_OK) {
ESP_LOGW(TAG, "Анімація WS2812 недоступна: %s", esp_err_to_name(anim_err));
}
} else {
ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
}
@@ -40,11 +58,7 @@ void app_main(void)
ESP_LOGW(TAG, "UART мультиплексор недоступний");
}
if (usb_cdc_cli_init() != ESP_OK) {
ESP_LOGW(TAG, "USB CDC CLI недоступний");
} else {
ESP_LOGI(TAG, "USB CDC CLI запущено");
}
ESP_LOGI(TAG, "Початок послідовного ввімкнення каналів з інтервалом 4 с");
const TickType_t on_time = pdMS_TO_TICKS(4000);

View File

@@ -1,3 +1,9 @@
/*
* Developed by TComLab
* Version: v0.1
* Date: 2025-12-15
*/
#include "uart_mux.h"
#include <string.h>
@@ -38,6 +44,8 @@ static bool s_initialized;
static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS];
static TaskHandle_t s_watchdog_task;
// Перемикає апаратний мультиплексор на вказаний канал під захистом мьютекса,
// оновлюючи таймстемп останнього heartbeat для контролю watchdog.
static esp_err_t uart_mux_select_locked(size_t channel)
{
if (channel >= UART_MUX_MAX_CHANNELS) {
@@ -56,6 +64,8 @@ static esp_err_t uart_mux_select_locked(size_t channel)
return ESP_OK;
}
// Періодично опитує всі канали, щоб зчитати heartbeat та перезапускає DCDC,
// якщо канал «мовчить» довше за CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC.
static void uart_mux_watchdog_task(void *arg)
{
const TickType_t poll_interval = pdMS_TO_TICKS(1000);
@@ -94,6 +104,8 @@ static void uart_mux_watchdog_task(void *arg)
#endif // CONFIG_WATCH_UART_MUX_ENABLED
// Налаштовує GPIO-вибірники, драйвер UART та створює watchdog-задачу для
// мультиплексора; повторний виклик просто повертає ESP_OK.
esp_err_t uart_mux_init(void)
{
#if !CONFIG_WATCH_UART_MUX_ENABLED
@@ -156,6 +168,7 @@ esp_err_t uart_mux_init(void)
#endif
}
// Повертає ознаку ініціалізації модулю UART мультиплексора.
bool uart_mux_ready(void)
{
#if CONFIG_WATCH_UART_MUX_ENABLED
@@ -165,11 +178,14 @@ bool uart_mux_ready(void)
#endif
}
// Кількість доступних каналів, визначених у конфігурації.
size_t uart_mux_channel_count(void)
{
return CONFIG_WATCH_UART_MUX_CHANNELS;
}
// Перемикається на канал, передає буфер даних через загальний UART та
// захищає доступ до шини мьютексом, щоб уникнути гонок між задачами.
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout)
{
#if !CONFIG_WATCH_UART_MUX_ENABLED
@@ -199,6 +215,8 @@ esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, Tic
#endif
}
// Читає дані з вказаного каналу, оновлюючи heartbeat під час успішного
// зчитування, і повертає кількість байтів через out_length.
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout)
{
#if !CONFIG_WATCH_UART_MUX_ENABLED

View File

@@ -1,3 +1,9 @@
/*
* Developed by TComLab
* Version: v0.1
* Date: 2025-12-15
*/
#pragma once
#include <stddef.h>
@@ -5,8 +11,13 @@
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
// Ініціалізує апаратний мультиплексор UART: GPIO, UART драйвер та watchdog.
esp_err_t uart_mux_init(void);
// Повертає true, якщо драйвер готовий приймати виклики.
bool uart_mux_ready(void);
// Кількість доступних каналів мультиплексора.
size_t uart_mux_channel_count(void);
// Відправляє буфер даних на вказаний канал з тайм-аутом очікування мьютекса.
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout);
// Зчитує дані з каналу, повертаючи кількість байтів через out_length.
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout);

98
main/usb_cdc_log.c Normal file
View File

@@ -0,0 +1,98 @@
#include "usb_cdc_log.h"
#include <stdarg.h>
#include <stdio.h>
#include "esp_check.h"
#include "esp_log.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
static const char *TAG = "usb_log";
static bool s_initialized;
static bool s_host_ready;
static vprintf_like_t s_prev_vprintf;
static int usb_cdc_vprintf(const char *fmt, va_list args)
{
if (s_host_ready) {
char buffer[256];
va_list args_copy;
va_copy(args_copy, args);
int len = vsnprintf(buffer, sizeof(buffer), fmt, args_copy);
va_end(args_copy);
if (len > 0) {
if (len >= (int)sizeof(buffer)) {
len = sizeof(buffer) - 1;
}
if (tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)buffer, len) == ESP_OK) {
tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0);
}
}
return len;
}
if (s_prev_vprintf) {
va_list args_copy;
va_copy(args_copy, args);
int ret = s_prev_vprintf(fmt, args_copy);
va_end(args_copy);
return ret;
}
return 0;
}
static void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
{
(void)itf;
s_host_ready = event->line_state_changed_data.dtr;
if (s_host_ready) {
const char banner[] = "\r\nwatch-watch log over USB CDC\r\n";
tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, (const uint8_t *)banner, sizeof(banner) - 1);
tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, 0);
}
}
bool usb_cdc_log_ready(void)
{
return s_host_ready;
}
esp_err_t usb_cdc_log_init(void)
{
if (s_initialized) {
return ESP_OK;
}
const tinyusb_config_t tusb_cfg = {
.device_descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false,
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = NULL,
.hs_configuration_descriptor = NULL,
.qualifier_descriptor = NULL,
#else
.configuration_descriptor = NULL,
#endif
};
ESP_RETURN_ON_ERROR(tinyusb_driver_install(&tusb_cfg), TAG, "TinyUSB init failed");
const tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = NULL,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = usb_cdc_line_state_changed_callback,
.callback_line_coding_changed = NULL,
};
ESP_RETURN_ON_ERROR(tusb_cdc_acm_init(&acm_cfg), TAG, "CDC init failed");
s_prev_vprintf = esp_log_set_vprintf(usb_cdc_vprintf);
s_initialized = true;
ESP_LOGI(TAG, "ESP_LOG перенаправлено в USB CDC");
return ESP_OK;
}

15
main/usb_cdc_log.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Ініціалізує TinyUSB CDC та перенаправляє ESP_LOG у USB.
*/
esp_err_t usb_cdc_log_init(void);
/**
* @brief Чи встановив хост DTR і готовий приймати лог.
*/
bool usb_cdc_log_ready(void);

View File

@@ -4,6 +4,8 @@
#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
@@ -132,3 +134,62 @@ esp_err_t ws2812_status_refresh_from_dcdc(void)
}
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;
}

View File

@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
@@ -18,3 +19,4 @@ esp_err_t ws2812_status_mark_active(size_t channel);
esp_err_t ws2812_status_clear_active(void);
esp_err_t ws2812_status_set_error(bool has_error);
esp_err_t ws2812_status_refresh_from_dcdc(void);
esp_err_t ws2812_status_play_bringup_animation(size_t cycles, uint32_t step_delay_ms);