/* * Developed by TComLab * Version: v0.1 * Date: 2025-12-15 */ #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "sdkconfig.h" #include "dcdc_controller.h" #include "ina226_monitor.h" #include "uart_mux.h" #include "usb_cdc_cli.h" #include "usb_cdc_log.h" #include "watch_config.h" #include "ws2812_status.h" static const char *TAG = "watch-watch"; static const char HB_MESSAGE[] = "{\"cmd\":\"status\"}\r\n"; static int noop_vprintf(const char *fmt, va_list args) { (void)fmt; (void)args; return 0; } void app_main(void) { esp_log_set_vprintf(noop_vprintf); if (watch_config_init() != ESP_OK) { ESP_LOGE(TAG, "Не вдалося ініціалізувати конфігурацію"); } else { ESP_LOGI(TAG, "Конфігурацію завантажено"); } if (usb_cdc_cli_init() == ESP_OK) { ESP_LOGI(TAG, "USB CDC CLI активовано"); } else { ESP_LOGW(TAG, "Не вдалося запустити USB CLI"); } bool ws_ready = false; if (ws2812_status_init() == ESP_OK) { ws_ready = true; } else { ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний"); } const watch_config_t *cfg = watch_config_get(); const uint32_t start_delay_ms = cfg->heartbeat_start_delay_sec * 1000U; TickType_t start_delay = pdMS_TO_TICKS(start_delay_ms); if (start_delay > 0) { ESP_LOGI(TAG, "Очікування %u с перед стартом опитування", cfg->heartbeat_start_delay_sec); if (ws_ready) { ws2812_status_set_startup_hold(start_delay_ms); } vTaskDelay(start_delay); if (ws_ready) { ws2812_status_set_startup_hold(0); } } ESP_LOGI(TAG, "Запуск watch-watch systems"); if (dcdc_init() != ESP_OK) { ESP_LOGE(TAG, "Помилка ініціалізації DCDC контролера"); if (ws_ready) { ws2812_status_set_error(true); } return; } if (ws_ready) { ws2812_status_refresh_from_dcdc(); } if (ina226_monitor_init() == ESP_OK) { ESP_LOGI(TAG, "INA226 моніторинг активовано"); ina226_monitor_sample(NULL); } else { ESP_LOGW(TAG, "Моніторинг навантаження недоступний"); } if (uart_mux_init() == ESP_OK) { ESP_LOGI(TAG, "UART мультиплексор активовано"); } else { ESP_LOGW(TAG, "UART мультиплексор недоступний"); } const TickType_t mux_timeout = pdMS_TO_TICKS(600); TickType_t channel_next_hb[DCDC_CHANNEL_COUNT] = {0}; bool channel_powered[DCDC_CHANNEL_COUNT] = {false}; const TickType_t power_on_stagger = pdMS_TO_TICKS(3000); ESP_LOGI(TAG, "Початок циклічного опитування всіх каналів"); while (true) { cfg = watch_config_get(); TickType_t hb_period = pdMS_TO_TICKS(cfg->heartbeat_period_sec * 1000U); if (hb_period == 0) { hb_period = 1; } TickType_t now = xTaskGetTickCount(); size_t channels = dcdc_channel_count(); for (size_t ch = 0; ch < channels; ++ch) { bool powered = dcdc_get_state(ch); if (!powered) { channel_powered[ch] = false; ESP_LOGI(TAG, "-> Ввімкнення каналу %d", (int)ch); if (dcdc_enable(ch) == ESP_OK) { powered = true; channel_powered[ch] = true; channel_next_hb[ch] = now + hb_period; if (ws_ready) { ws2812_status_set_channel_state(ch, true); } vTaskDelay(power_on_stagger); } else { ESP_LOGE(TAG, "Не вдалося ввімкнути канал %d", (int)ch); continue; } } if (!channel_powered[ch]) { channel_powered[ch] = true; channel_next_hb[ch] = now + hb_period; continue; } now = xTaskGetTickCount(); if (now < channel_next_hb[ch]) { continue; } ina226_reading_t reading = {0}; if (ina226_monitor_ready() && ina226_monitor_sample(&reading) == ESP_OK) { ESP_LOGI(TAG, "Живлення: %.2f В, %.1f мА, %.1f мВт", reading.voltage_v, reading.current_ma, reading.power_mw); } if (uart_mux_ready()) { esp_err_t tx_err = uart_mux_write(ch, (const uint8_t *)HB_MESSAGE, sizeof(HB_MESSAGE) - 1, mux_timeout); if (tx_err != ESP_OK) { ESP_LOGW(TAG, "Не вдалося надіслати heartbeat на канал %d: %s", (int)ch, esp_err_to_name(tx_err)); } else { uint8_t rx_buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN]; size_t received = 0; esp_err_t rx_err = uart_mux_read(ch, rx_buffer, sizeof(rx_buffer), &received, mux_timeout); if (rx_err == ESP_OK) { if (received > 0) { ESP_LOGI(TAG, "RX CH%u (%u байт після heartbeat)", (unsigned)ch, (unsigned)received); ESP_LOG_BUFFER_HEX_LEVEL(TAG, rx_buffer, received, ESP_LOG_INFO); uart_mux_process_rx(ch, rx_buffer, received); } else { ESP_LOGW(TAG, "Канал %d не відповів даними на heartbeat", (int)ch); uart_mux_report_miss(ch); } } else if (rx_err == ESP_ERR_TIMEOUT) { ESP_LOGW(TAG, "Час очікування відповіді з каналу %d перевищено", (int)ch); uart_mux_report_miss(ch); } else { ESP_LOGW(TAG, "Помилка читання відповіді CH%u: %s", (unsigned)ch, esp_err_to_name(rx_err)); uart_mux_report_miss(ch); } } } else { ESP_LOGW(TAG, "UART мультиплексор недоступний, очікування..."); } channel_next_hb[ch] = now + hb_period; } vTaskDelay(pdMS_TO_TICKS(50)); } }