#include "web_server_pages.h" #include "sys_uart.h" #include "esp_log.h" #include "esp_http_server.h" #include #include #include static const char *TAG = "SYS_WEB_SERIAL"; static const char SERIAL_HTML[] = "" "" "ABrobot Web Serial Terminal" "" "
" "
" "
" "
" "
ESP32-C3 Web Serial Terminal
" "
" " " "
" "
" "
" " " " " " " " " " " " " "
" "
" " RX: 0 B" " TX: 0 B" " " "
" "
" "
" "
" " " " " " " " " "
" "
" " Quick:" " " " " " " " " " " "
" "
" ""; static esp_err_t serial_page_get(httpd_req_t *request) { httpd_resp_set_type(request, "text/html"); return httpd_resp_send(request, SERIAL_HTML, HTTPD_RESP_USE_STRLEN); } static esp_err_t serial_data_get(httpd_req_t *request) { char query[64] = {0}; uint32_t from_seq = 0; if (httpd_req_get_url_query_str(request, query, sizeof(query)) == ESP_OK) { char val[16] = {0}; if (httpd_query_key_value(query, "seq", val, sizeof(val)) == ESP_OK) { from_seq = (uint32_t)strtoul(val, NULL, 10); } } uint8_t rx_delta[1024]; uint32_t new_seq = from_seq; size_t len = sys_uart_read_delta(from_seq, rx_delta, sizeof(rx_delta) - 1, &new_seq); rx_delta[len] = '\0'; uint32_t total_rx = 0, total_tx = 0; sys_uart_get_stats(&total_rx, &total_tx); uint32_t baud = sys_uart_get_baud_rate(); httpd_resp_set_type(request, "application/json"); /* Begin JSON response */ char prefix[128]; int p_len = snprintf(prefix, sizeof(prefix), "{\"seq\":%lu,\"rx\":%lu,\"tx\":%lu,\"baud\":%lu,\"text\":\"", (unsigned long)new_seq, (unsigned long)total_rx, (unsigned long)total_tx, (unsigned long)baud); httpd_resp_send_chunk(request, prefix, p_len); /* Stream escaped JSON text */ for (size_t i = 0; i < len; i++) { uint8_t c = rx_delta[i]; if (c == '"') { httpd_resp_send_chunk(request, "\\\"", 2); } else if (c == '\\') { httpd_resp_send_chunk(request, "\\\\", 2); } else if (c == '\n') { httpd_resp_send_chunk(request, "\\n", 2); } else if (c == '\r') { httpd_resp_send_chunk(request, "\\r", 2); } else if (c == '\t') { httpd_resp_send_chunk(request, "\\t", 2); } else if (c < 32 || c > 126) { /* Replace non-printable ASCII in text view with dot or hex escape */ char hex_esc[8]; int h_len = snprintf(hex_esc, sizeof(hex_esc), "\\u%04x", c); httpd_resp_send_chunk(request, hex_esc, h_len); } else { char ch[2] = {(char)c, '\0'}; httpd_resp_send_chunk(request, ch, 1); } } /* Stream raw byte array for HEX dump view */ httpd_resp_send_chunk(request, "\",\"hex\":[", 9); for (size_t i = 0; i < len; i++) { char num[8]; int n_len = snprintf(num, sizeof(num), "%s%u", (i > 0 ? "," : ""), (unsigned int)rx_delta[i]); httpd_resp_send_chunk(request, num, n_len); } httpd_resp_send_chunk(request, "]}", 2); return httpd_resp_send_chunk(request, NULL, 0); } static esp_err_t serial_tx_post(httpd_req_t *request) { char body[512] = {0}; int total_len = request->content_len; if (total_len >= sizeof(body)) { total_len = sizeof(body) - 1; } int received = httpd_req_recv(request, body, total_len); if (received > 0) { sys_uart_write((const uint8_t *)body, (size_t)received); httpd_resp_sendstr(request, "OK"); return ESP_OK; } return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "No TX data received"); } static esp_err_t serial_baud_post(httpd_req_t *request) { char body[32] = {0}; int received = httpd_req_recv(request, body, sizeof(body) - 1); if (received > 0) { body[received] = '\0'; uint32_t baud = (uint32_t)strtoul(body, NULL, 10); if (baud > 0) { esp_err_t err = sys_uart_set_baud_rate(baud); if (err == ESP_OK) { httpd_resp_sendstr(request, "OK"); return ESP_OK; } } } return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Invalid baud rate"); } static esp_err_t serial_clear_post(httpd_req_t *request) { sys_uart_clear_rx_buffer(); httpd_resp_sendstr(request, "OK"); return ESP_OK; } void web_server_register_serial_pages(httpd_handle_t server) { const httpd_uri_t page = {.uri = "/serial", .method = HTTP_GET, .handler = serial_page_get}; const httpd_uri_t data = {.uri = "/serial/data", .method = HTTP_GET, .handler = serial_data_get}; const httpd_uri_t tx = {.uri = "/serial/tx", .method = HTTP_POST, .handler = serial_tx_post}; const httpd_uri_t baud = {.uri = "/serial/baud", .method = HTTP_POST, .handler = serial_baud_post}; const httpd_uri_t clear = {.uri = "/serial/clear", .method = HTTP_POST, .handler = serial_clear_post}; httpd_register_uri_handler(server, &page); httpd_register_uri_handler(server, &data); httpd_register_uri_handler(server, &tx); httpd_register_uri_handler(server, &baud); httpd_register_uri_handler(server, &clear); ESP_LOGI(TAG, "Web Serial Terminal endpoints registered at /serial"); }