#include "uart_handler.h" #include "config.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" #include "freertos/semphr.h" #include static const char *TAG = TAG_UART; // Runtime UART configuration static bmc_uart_config_t current_config; static bool uart_initialized = false; static TaskHandle_t uart_rx_task_handle = NULL; static SemaphoreHandle_t uart_mutex = NULL; // Callback for WebSocket bridge static uart_rx_callback_t rx_callback = NULL; static SemaphoreHandle_t callback_mutex = NULL; // Ring buffer for received data #define UART_RING_BUF_SIZE 4096 static uint8_t *ring_buffer = NULL; static volatile size_t ring_head = 0; static volatile size_t ring_tail = 0; // ============================================================================ // Ring Buffer Functions // ============================================================================ static size_t ring_buffer_available(void) { if (ring_head >= ring_tail) { return ring_head - ring_tail; } else { return UART_RING_BUF_SIZE - ring_tail + ring_head; } } static size_t ring_buffer_read(uint8_t *buf, size_t len) { size_t available = ring_buffer_available(); size_t to_read = (len < available) ? len : available; for (size_t i = 0; i < to_read; i++) { buf[i] = ring_buffer[ring_tail]; ring_tail = (ring_tail + 1) % UART_RING_BUF_SIZE; } return to_read; } static size_t ring_buffer_write(const uint8_t *buf, size_t len) { size_t written = 0; for (size_t i = 0; i < len; i++) { size_t next_head = (ring_head + 1) % UART_RING_BUF_SIZE; if (next_head == ring_tail) { // Buffer full, overwrite oldest data ring_tail = (ring_tail + 1) % UART_RING_BUF_SIZE; } ring_buffer[ring_head] = buf[i]; ring_head = next_head; written++; } return written; } // ============================================================================ // UART Receive Task // ============================================================================ static void uart_rx_task(void *arg) { uint8_t *temp_buf = malloc(256); if (!temp_buf) { ESP_LOGE(TAG, "Failed to allocate UART RX buffer"); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "UART RX task started, waiting for data on UART%d", BMC_UART_NUM); while (uart_initialized) { // Check for data from UART with short timeout for responsive console int len = uart_read_bytes(BMC_UART_NUM, temp_buf, 256, pdMS_TO_TICKS(10)); if (len > 0) { // Store in ring buffer ring_buffer_write(temp_buf, len); // Call callback if registered (outside mutex to avoid deadlock) uart_rx_callback_t cb = NULL; xSemaphoreTake(callback_mutex, pdMS_TO_TICKS(100)); cb = rx_callback; xSemaphoreGive(callback_mutex); if (cb) { cb(temp_buf, len); } } else if (len < 0) { ESP_LOGW(TAG, "UART read error: %d", len); } } free(temp_buf); ESP_LOGI(TAG, "UART RX task stopped"); vTaskDelete(NULL); } // ============================================================================ // Public Functions // ============================================================================ esp_err_t uart_handler_init(void) { if (uart_initialized) { ESP_LOGW(TAG, "UART handler already initialized"); return ESP_OK; } ESP_LOGI(TAG, "Initializing UART handler"); // Allocate ring buffer ring_buffer = calloc(UART_RING_BUF_SIZE, 1); if (!ring_buffer) { ESP_LOGE(TAG, "Failed to allocate ring buffer"); return ESP_ERR_NO_MEM; } ring_head = 0; ring_tail = 0; // Create mutex for UART access uart_mutex = xSemaphoreCreateMutex(); if (!uart_mutex) { ESP_LOGE(TAG, "Failed to create UART mutex"); free(ring_buffer); return ESP_ERR_NO_MEM; } // Create mutex for callback callback_mutex = xSemaphoreCreateMutex(); if (!callback_mutex) { ESP_LOGE(TAG, "Failed to create callback mutex"); vSemaphoreDelete(uart_mutex); free(ring_buffer); return ESP_ERR_NO_MEM; } // Set default configuration current_config.port = BMC_UART_NUM; current_config.baud_rate = BMC_UART_BAUD_RATE; current_config.data_bits = UART_DATA_8_BITS; current_config.parity = UART_PARITY_DISABLE; current_config.stop_bits = UART_STOP_BITS_1; current_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; // Configure UART uart_config_t uart_cfg = { .baud_rate = current_config.baud_rate, .data_bits = current_config.data_bits, .parity = current_config.parity, .stop_bits = current_config.stop_bits, .flow_ctrl = current_config.flow_ctrl, }; esp_err_t ret = uart_param_config(BMC_UART_NUM, &uart_cfg); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to configure UART: %s", esp_err_to_name(ret)); goto cleanup; } // Set UART pins ret = uart_set_pin(BMC_UART_NUM, BMC_UART_TX_GPIO, BMC_UART_RX_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to set UART pins: %s", esp_err_to_name(ret)); goto cleanup; } // Install UART driver ret = uart_driver_install(BMC_UART_NUM, BMC_UART_BUF_SIZE * 2, 0, 0, NULL, 0); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to install UART driver: %s", esp_err_to_name(ret)); goto cleanup; } uart_initialized = true; // Start receive task BaseType_t task_ret = xTaskCreate(uart_rx_task, "uart_rx", BMC_UART_TASK_STACK, NULL, BMC_UART_TASK_PRIO, &uart_rx_task_handle); if (task_ret != pdPASS) { ESP_LOGE(TAG, "Failed to create UART RX task"); uart_driver_delete(BMC_UART_NUM); uart_initialized = false; ret = ESP_FAIL; goto cleanup; } ESP_LOGI(TAG, "UART initialized: port=%d, baud=%d, tx=%d, rx=%d", BMC_UART_NUM, current_config.baud_rate, BMC_UART_TX_GPIO, BMC_UART_RX_GPIO); return ESP_OK; cleanup: vSemaphoreDelete(callback_mutex); vSemaphoreDelete(uart_mutex); free(ring_buffer); return ret; } esp_err_t uart_handler_deinit(void) { if (!uart_initialized) { return ESP_OK; } ESP_LOGI(TAG, "Deinitializing UART handler"); uart_initialized = false; // Wait for task to finish if (uart_rx_task_handle) { vTaskDelay(pdMS_TO_TICKS(100)); uart_rx_task_handle = NULL; } // Delete UART driver uart_driver_delete(BMC_UART_NUM); // Clean up resources if (callback_mutex) { vSemaphoreDelete(callback_mutex); callback_mutex = NULL; } if (uart_mutex) { vSemaphoreDelete(uart_mutex); uart_mutex = NULL; } if (ring_buffer) { free(ring_buffer); ring_buffer = NULL; } rx_callback = NULL; ESP_LOGI(TAG, "UART handler deinitialized"); return ESP_OK; } esp_err_t uart_get_config(bmc_uart_config_t *config) { if (!uart_initialized) { return ESP_ERR_INVALID_STATE; } if (config) { memcpy(config, ¤t_config, sizeof(bmc_uart_config_t)); } return ESP_OK; } esp_err_t uart_set_config(const bmc_uart_config_t *config) { if (!uart_initialized || !config) { return ESP_ERR_INVALID_STATE; } xSemaphoreTake(uart_mutex, portMAX_DELAY); // Apply new configuration uart_config_t uart_cfg = { .baud_rate = config->baud_rate, .data_bits = config->data_bits, .parity = config->parity, .stop_bits = config->stop_bits, .flow_ctrl = config->flow_ctrl, }; esp_err_t ret = uart_param_config(BMC_UART_NUM, &uart_cfg); if (ret == ESP_OK) { memcpy(¤t_config, config, sizeof(bmc_uart_config_t)); ESP_LOGI(TAG, "UART configuration updated: baud=%d", config->baud_rate); } xSemaphoreGive(uart_mutex); return ret; } int uart_read_data(uint8_t *buf, size_t len, uint32_t timeout_ms) { if (!uart_initialized || !buf || len == 0) { return -1; } xSemaphoreTake(uart_mutex, pdMS_TO_TICKS(timeout_ms)); size_t available = ring_buffer_available(); if (available == 0) { xSemaphoreGive(uart_mutex); return 0; } size_t to_read = (len < available) ? len : available; size_t read = ring_buffer_read(buf, to_read); xSemaphoreGive(uart_mutex); return read; } int uart_write_data(const uint8_t *buf, size_t len) { if (!uart_initialized || !buf || len == 0) { return -1; } xSemaphoreTake(uart_mutex, portMAX_DELAY); int written = uart_write_bytes(BMC_UART_NUM, buf, len); xSemaphoreGive(uart_mutex); if (written < 0) { ESP_LOGE(TAG, "Failed to write to UART"); return -1; } ESP_LOGD(TAG, "Wrote %d bytes to UART", written); return written; } int uart_data_available(void) { if (!uart_initialized) { return -1; } return ring_buffer_available(); } esp_err_t uart_flush_buffers(void) { if (!uart_initialized) { return ESP_ERR_INVALID_STATE; } xSemaphoreTake(uart_mutex, portMAX_DELAY); // Flush hardware buffers uart_flush_input(BMC_UART_NUM); // Clear ring buffer ring_head = 0; ring_tail = 0; xSemaphoreGive(uart_mutex); ESP_LOGI(TAG, "UART buffers flushed"); return ESP_OK; } esp_err_t uart_set_baud_rate(int baud_rate) { if (!uart_initialized) { return ESP_ERR_INVALID_STATE; } xSemaphoreTake(uart_mutex, portMAX_DELAY); esp_err_t ret = uart_set_baudrate(BMC_UART_NUM, baud_rate); if (ret == ESP_OK) { current_config.baud_rate = baud_rate; ESP_LOGI(TAG, "UART baud rate changed to %d", baud_rate); } else { ESP_LOGE(TAG, "Failed to set baud rate: %s", esp_err_to_name(ret)); } xSemaphoreGive(uart_mutex); return ret; } int uart_get_baud_rate(void) { return current_config.baud_rate; } // ============================================================================ // WebSocket Bridge Functions // ============================================================================ esp_err_t uart_register_rx_callback(uart_rx_callback_t callback) { if (!uart_initialized) { ESP_LOGW(TAG, "UART not initialized, cannot register callback"); return ESP_ERR_INVALID_STATE; } if (!callback_mutex) { ESP_LOGE(TAG, "callback_mutex is NULL!"); return ESP_ERR_INVALID_STATE; } xSemaphoreTake(callback_mutex, portMAX_DELAY); rx_callback = callback; xSemaphoreGive(callback_mutex); ESP_LOGI(TAG, "UART RX callback registered successfully, rx_callback=%p", rx_callback); return ESP_OK; } esp_err_t uart_unregister_rx_callback(void) { if (!uart_initialized) { return ESP_ERR_INVALID_STATE; } xSemaphoreTake(callback_mutex, portMAX_DELAY); rx_callback = NULL; xSemaphoreGive(callback_mutex); ESP_LOGI(TAG, "UART RX callback unregistered"); return ESP_OK; }