Initial commit
ESP32-S3 firmware acting as BMC for another board, controlling an ATX power supply and providing access to UART. Co-authored with GLM-5
This commit is contained in:
410
main/uart_handler.c
Normal file
410
main/uart_handler.c
Normal file
@@ -0,0 +1,410 @@
|
||||
#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 <string.h>
|
||||
|
||||
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 longer timeout
|
||||
int len = uart_read_bytes(BMC_UART_NUM, temp_buf, 256, pdMS_TO_TICKS(100));
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user