// // Created by shinys on 25. 8. 18.. // #include "driver/uart.h" #include "esp_err.h" #include "esp_http_server.h" #include "esp_log.h" #include "freertos/semphr.h" #include "nconfig.h" #include "pb.h" #include "pb_encode.h" #include "status.pb.h" #include "webserver.h" #define UART_NUM UART_NUM_1 #define BUF_SIZE (2048) #define UART_TX_PIN CONFIG_GPIO_UART_TX #define UART_RX_PIN CONFIG_GPIO_UART_RX #define CHUNK_SIZE (2048) #define PB_UART_BUFFER_SIZE (CHUNK_SIZE + 64) static const char* TAG = "ws-uart"; enum ws_message_type { WS_MSG_STATUS, WS_MSG_UART }; struct ws_message { enum ws_message_type type; uint8_t* data; size_t len; }; struct bytes_arg { const void* data; size_t len; }; #define MAX_CLIENT 7 static QueueHandle_t ws_queue; static QueueHandle_t uart_event_queue; static int client_fds[MAX_CLIENT]; static bool encode_bytes_callback(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) { struct bytes_arg* br = (struct bytes_arg*)(*arg); if (!pb_encode_tag_for_field(stream, field)) { return false; } return pb_encode_string(stream, (uint8_t*)br->data, br->len); } static void unified_ws_sender_task(void* arg) { httpd_handle_t server = (httpd_handle_t)arg; struct ws_message msg; while (1) { if (xQueueReceive(ws_queue, &msg, portMAX_DELAY)) { size_t clients = MAX_CLIENT; if (httpd_get_client_list(server, &clients, client_fds) != ESP_OK) { free(msg.data); continue; } if (clients == 0) { free(msg.data); continue; } httpd_ws_frame_t ws_pkt = {0}; ws_pkt.payload = msg.data; ws_pkt.len = msg.len; ws_pkt.type = HTTPD_WS_TYPE_BINARY; for (size_t i = 0; i < clients; ++i) { int fd = client_fds[i]; if (httpd_ws_get_fd_info(server, fd) == HTTPD_WS_CLIENT_WEBSOCKET) { esp_err_t err = httpd_ws_send_frame_async(server, fd, &ws_pkt); if (err != ESP_OK) { ESP_LOGW(TAG, "unified_ws_sender_task: async send failed for fd %d, error: %s", fd, esp_err_to_name(err)); } } } free(msg.data); } } free(client_fds); vTaskDelete(NULL); } static void uart_polling_task(void* arg) { static uint8_t data_buf[BUF_SIZE]; static uint8_t pb_buffer[PB_UART_BUFFER_SIZE]; while (1) { size_t available_len; uart_get_buffered_data_len(UART_NUM, &available_len); if (available_len == 0) { vTaskDelay(pdMS_TO_TICKS(10)); continue; } size_t read_len = (available_len > BUF_SIZE) ? BUF_SIZE : available_len; int bytes_read = uart_read_bytes(UART_NUM, data_buf, read_len, pdMS_TO_TICKS(5)); if (bytes_read > 0) { size_t offset = 0; while (offset < bytes_read) { size_t chunk_size = (bytes_read - offset > CHUNK_SIZE) ? CHUNK_SIZE : (bytes_read - offset); StatusMessage message = StatusMessage_init_zero; message.which_payload = StatusMessage_uart_data_tag; struct bytes_arg a = {.data = data_buf + offset, .len = chunk_size}; message.payload.uart_data.data.funcs.encode = &encode_bytes_callback; message.payload.uart_data.data.arg = &a; pb_ostream_t stream = pb_ostream_from_buffer(pb_buffer, sizeof(pb_buffer)); if (!pb_encode(&stream, StatusMessage_fields, &message)) { ESP_LOGE(TAG, "Failed to encode uart data: %s", PB_GET_ERROR(&stream)); offset += chunk_size; continue; } struct ws_message msg; msg.type = WS_MSG_UART; msg.len = stream.bytes_written; msg.data = malloc(msg.len); if (!msg.data) { ESP_LOGE(TAG, "Failed to allocate memory for uart ws msg"); offset += chunk_size; continue; } memcpy(msg.data, pb_buffer, msg.len); if (xQueueSend(ws_queue, &msg, pdMS_TO_TICKS(10)) != pdPASS) { ESP_LOGW(TAG, "ws sender queue full, dropping %zu bytes", chunk_size); free(msg.data); } offset += chunk_size; } } } vTaskDelete(NULL); } static void uart_event_task(void* arg) { uart_event_t event; while (1) { if (xQueueReceive(uart_event_queue, &event, portMAX_DELAY)) { switch (event.type) { case UART_FIFO_OVF: ESP_LOGW(TAG, "UART HW FIFO Overflow"); uart_flush_input(UART_NUM); xQueueReset(uart_event_queue); break; case UART_BUFFER_FULL: ESP_LOGW(TAG, "UART ring buffer full"); uart_flush_input(UART_NUM); xQueueReset(uart_event_queue); break; case UART_DATA: // Muting this event because it is too noisy break; default: ESP_LOGI(TAG, "unhandled uart event type: %d", event.type); break; } } } vTaskDelete(NULL); } static esp_err_t ws_handler(httpd_req_t* req) { if (req->method == HTTP_GET) { ESP_LOGI(TAG, "Handshake done, the new connection was opened"); return ESP_OK; } httpd_ws_frame_t ws_pkt = {0}; uint8_t buf[BUF_SIZE]; ws_pkt.payload = buf; ws_pkt.type = HTTPD_WS_TYPE_BINARY; esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, BUF_SIZE); if (ret != ESP_OK) { ESP_LOGW(TAG, "httpd_ws_recv_frame failed with error: %s", esp_err_to_name(ret)); return ret; } uart_write_bytes(UART_NUM, (const char*)ws_pkt.payload, ws_pkt.len); return ESP_OK; } void register_ws_endpoint(httpd_handle_t server) { size_t baud_rate_len; nconfig_get_str_len(UART_BAUD_RATE, &baud_rate_len); char buf[baud_rate_len]; nconfig_read(UART_BAUD_RATE, buf, baud_rate_len); uart_config_t uart_config = { .baud_rate = strtol(buf, NULL, 10), .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, }; ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config)); ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart_event_queue, 0)); httpd_uri_t ws = {.uri = "/ws", .method = HTTP_GET, .handler = ws_handler, .user_ctx = NULL, .is_websocket = true}; httpd_register_uri_handler(server, &ws); ws_queue = xQueueCreate(10, sizeof(struct ws_message)); xTaskCreate(uart_polling_task, "uart_polling_task", 1024 * 4, NULL, 8, NULL); xTaskCreate(unified_ws_sender_task, "ws_sender_task", 1024 * 6, server, 9, NULL); xTaskCreate(uart_event_task, "uart_event_task", 1024 * 2, NULL, 10, NULL); } void push_data_to_ws(const uint8_t* data, size_t len) { struct ws_message msg; msg.type = WS_MSG_STATUS; msg.data = malloc(len); if (!msg.data) { ESP_LOGE(TAG, "Failed to allocate memory for status ws msg"); return; } memcpy(msg.data, data, len); msg.len = len; if (xQueueSend(ws_queue, &msg, pdMS_TO_TICKS(10)) != pdPASS) { ESP_LOGW(TAG, "WS queue full, dropping status message"); free(msg.data); } } esp_err_t change_baud_rate(int baud_rate) { return uart_set_baudrate(UART_NUM, baud_rate); }