#include "sys_uart.h" #include #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" static const char *TAG = "SYS_UART"; static uint8_t s_rx_ring[SYS_UART_BUFFER_SIZE]; static uint32_t s_rx_head = 0; /* Total lifetime received bytes (monotonic) */ static uint32_t s_tx_total = 0; static uint32_t s_baud_rate = SYS_UART_DEFAULT_BAUD; static SemaphoreHandle_t s_uart_mutex = NULL; static bool s_initialized = false; static void uart_rx_task(void *pvParameters) { uint8_t tmp_buf[256]; while (1) { int len = uart_read_bytes(SYS_UART_NUM, tmp_buf, sizeof(tmp_buf), pdMS_TO_TICKS(20)); if (len > 0) { if (xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { for (int i = 0; i < len; i++) { s_rx_ring[s_rx_head % SYS_UART_BUFFER_SIZE] = tmp_buf[i]; s_rx_head++; } xSemaphoreGive(s_uart_mutex); } } } } esp_err_t sys_uart_init(uint32_t baud_rate) { if (s_initialized) { return ESP_OK; } if (baud_rate == 0) { baud_rate = SYS_UART_DEFAULT_BAUD; } s_baud_rate = baud_rate; s_uart_mutex = xSemaphoreCreateMutex(); if (!s_uart_mutex) { ESP_LOGE(TAG, "Failed to create UART mutex"); return ESP_ERR_NO_MEM; } uart_config_t uart_config = { .baud_rate = (int)s_baud_rate, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_DEFAULT, }; esp_err_t err = uart_driver_install(SYS_UART_NUM, 1024, 1024, 0, NULL, 0); if (err != ESP_OK) { ESP_LOGE(TAG, "uart_driver_install failed: %s", esp_err_to_name(err)); return err; } err = uart_param_config(SYS_UART_NUM, &uart_config); if (err != ESP_OK) { ESP_LOGE(TAG, "uart_param_config failed: %s", esp_err_to_name(err)); return err; } err = uart_set_pin(SYS_UART_NUM, SYS_UART_PIN_TX, SYS_UART_PIN_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); if (err != ESP_OK) { ESP_LOGE(TAG, "uart_set_pin failed: %s", esp_err_to_name(err)); return err; } /* Enable internal pull-up on RX to prevent floating line noise / framing errors */ gpio_set_pull_mode(SYS_UART_PIN_RX, GPIO_PULLUP_ONLY); gpio_pullup_en(SYS_UART_PIN_RX); memset(s_rx_ring, 0, sizeof(s_rx_ring)); s_rx_head = 0; s_tx_total = 0; s_initialized = true; xTaskCreate(uart_rx_task, "uart_rx_task", 3072, NULL, 5, NULL); ESP_LOGI(TAG, "Hardware UART initialized on TX:GPIO%d, RX:GPIO%d @ %lu baud", SYS_UART_PIN_TX, SYS_UART_PIN_RX, (unsigned long)s_baud_rate); return ESP_OK; } esp_err_t sys_uart_set_baud_rate(uint32_t baud_rate) { if (!s_initialized || baud_rate == 0) return ESP_ERR_INVALID_STATE; esp_err_t err = uart_set_baudrate(SYS_UART_NUM, baud_rate); if (err == ESP_OK) { s_baud_rate = baud_rate; uart_flush_input(SYS_UART_NUM); ESP_LOGI(TAG, "Baud rate changed to %lu", (unsigned long)baud_rate); } return err; } uint32_t sys_uart_get_baud_rate(void) { return s_baud_rate; } esp_err_t sys_uart_write(const uint8_t *data, size_t len) { if (!s_initialized || !data || len == 0) return ESP_ERR_INVALID_ARG; int sent = uart_write_bytes(SYS_UART_NUM, data, len); if (sent > 0) { if (xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { s_tx_total += (uint32_t)sent; xSemaphoreGive(s_uart_mutex); } return ESP_OK; } return ESP_FAIL; } esp_err_t sys_uart_write_str(const char *str) { if (!str) return ESP_ERR_INVALID_ARG; return sys_uart_write((const uint8_t *)str, strlen(str)); } size_t sys_uart_read_delta(uint32_t from_seq, uint8_t *out_buf, size_t max_len, uint32_t *new_seq) { if (!s_initialized || !out_buf || max_len == 0) { if (new_seq) *new_seq = from_seq; return 0; } size_t bytes_copied = 0; if (xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { uint32_t current_head = s_rx_head; if (from_seq > current_head) { from_seq = current_head; } uint32_t available = current_head - from_seq; if (available > SYS_UART_BUFFER_SIZE) { /* Client is too far behind, catch up to buffer start */ from_seq = current_head - SYS_UART_BUFFER_SIZE; available = SYS_UART_BUFFER_SIZE; } if (available > max_len) { available = max_len; } for (size_t i = 0; i < available; i++) { out_buf[i] = s_rx_ring[(from_seq + i) % SYS_UART_BUFFER_SIZE]; } bytes_copied = available; if (new_seq) { *new_seq = from_seq + available; } xSemaphoreGive(s_uart_mutex); } else { if (new_seq) *new_seq = from_seq; } return bytes_copied; } void sys_uart_get_stats(uint32_t *rx_count, uint32_t *tx_count) { if (xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { if (rx_count) *rx_count = s_rx_head; if (tx_count) *tx_count = s_tx_total; xSemaphoreGive(s_uart_mutex); } else { if (rx_count) *rx_count = s_rx_head; if (tx_count) *tx_count = s_tx_total; } } void sys_uart_clear_rx_buffer(void) { if (xSemaphoreTake(s_uart_mutex, pdMS_TO_TICKS(50)) == pdTRUE) { memset(s_rx_ring, 0, sizeof(s_rx_ring)); xSemaphoreGive(s_uart_mutex); } }