Add Serial Terminal App & WebUi Theme
This commit is contained in:
@@ -2,6 +2,7 @@ idf_component_register(
|
||||
SRCS
|
||||
"sys_event.c"
|
||||
"sys_status.c"
|
||||
"sys_uart.c"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
REQUIRES
|
||||
@@ -9,4 +10,7 @@ idf_component_register(
|
||||
esp_timer
|
||||
esp_system
|
||||
log
|
||||
driver
|
||||
esp_driver_gpio
|
||||
esp_driver_uart
|
||||
)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef SYS_UART_H
|
||||
#define SYS_UART_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SYS_UART_NUM UART_NUM_1
|
||||
#define SYS_UART_PIN_TX GPIO_NUM_21
|
||||
#define SYS_UART_PIN_RX GPIO_NUM_20
|
||||
#define SYS_UART_DEFAULT_BAUD 115200
|
||||
#define SYS_UART_BUFFER_SIZE 4096
|
||||
|
||||
/**
|
||||
* @brief Initialize the hardware UART subsystem and RX background task.
|
||||
* @param baud_rate Initial baud rate (e.g., 115200).
|
||||
* @return ESP_OK on success.
|
||||
*/
|
||||
esp_err_t sys_uart_init(uint32_t baud_rate);
|
||||
|
||||
/**
|
||||
* @brief Change the UART baud rate dynamically.
|
||||
* @param baud_rate New baud rate.
|
||||
* @return ESP_OK on success.
|
||||
*/
|
||||
esp_err_t sys_uart_set_baud_rate(uint32_t baud_rate);
|
||||
|
||||
/**
|
||||
* @brief Get the currently active baud rate.
|
||||
*/
|
||||
uint32_t sys_uart_get_baud_rate(void);
|
||||
|
||||
/**
|
||||
* @brief Transmit data over UART TX pin.
|
||||
* @param data Pointer to buffer.
|
||||
* @param len Number of bytes to send.
|
||||
* @return ESP_OK on success.
|
||||
*/
|
||||
esp_err_t sys_uart_write(const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Transmit a null-terminated string over UART TX.
|
||||
* @param str Null-terminated string.
|
||||
* @return ESP_OK on success.
|
||||
*/
|
||||
esp_err_t sys_uart_write_str(const char *str);
|
||||
|
||||
/**
|
||||
* @brief Read new incoming data since a given monotonic sequence index.
|
||||
* @param from_seq Sequence number previously returned to the caller.
|
||||
* @param out_buf Buffer to store newly received bytes.
|
||||
* @param max_len Size of out_buf.
|
||||
* @param[out] new_seq Pointer to store the updated sequence counter.
|
||||
* @return Number of bytes copied to out_buf.
|
||||
*/
|
||||
size_t sys_uart_read_delta(uint32_t from_seq, uint8_t *out_buf, size_t max_len, uint32_t *new_seq);
|
||||
|
||||
/**
|
||||
* @brief Get total transmitted and received byte counts.
|
||||
*/
|
||||
void sys_uart_get_stats(uint32_t *rx_count, uint32_t *tx_count);
|
||||
|
||||
/**
|
||||
* @brief Clear the RX circular buffer and reset sequence.
|
||||
*/
|
||||
void sys_uart_clear_rx_buffer(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SYS_UART_H
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "sys_uart.h"
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user