Add Serial Terminal App & WebUi Theme

This commit is contained in:
dark98
2026-09-24 22:34:33 +01:00
parent dc3b72bdbd
commit 4bf31f908a
17 changed files with 1040 additions and 234 deletions
+4
View File
@@ -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
)
+79
View File
@@ -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
+191
View File
@@ -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);
}
}