diff --git a/components/Helpers/Helpers/main_globals.cpp b/components/Helpers/Helpers/main_globals.cpp index cab36f8..8078234 100644 --- a/components/Helpers/Helpers/main_globals.cpp +++ b/components/Helpers/Helpers/main_globals.cpp @@ -4,15 +4,15 @@ // Forward declarations extern void start_video_streaming(void *arg); -bool startupCommandReceived = false; +static bool s_startupCommandReceived = false; bool getStartupCommandReceived() { - return startupCommandReceived; + return s_startupCommandReceived; } void setStartupCommandReceived(bool startupCommandReceived) { - startupCommandReceived = startupCommandReceived; + s_startupCommandReceived = startupCommandReceived; } static TaskHandle_t *g_serial_manager_handle = nullptr; @@ -27,15 +27,15 @@ void setSerialManagerHandle(TaskHandle_t *serialManagerHandle) } // Global pause state -bool startupPaused = false; +static bool s_startupPaused = false; bool getStartupPaused() { - return startupPaused; + return s_startupPaused; } void setStartupPaused(bool startupPaused) { - startupPaused = startupPaused; + s_startupPaused = startupPaused; } // Function to manually activate streaming @@ -47,4 +47,9 @@ void activateStreaming(bool disableSetup) void *serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr; start_video_streaming(serialTaskHandle); -} \ No newline at end of file +} + +// USB handover state +static bool s_usbHandoverDone = false; +bool getUsbHandoverDone() { return s_usbHandoverDone; } +void setUsbHandoverDone(bool done) { s_usbHandoverDone = done; } \ No newline at end of file diff --git a/components/Helpers/Helpers/main_globals.hpp b/components/Helpers/Helpers/main_globals.hpp index 5602c2c..7fe93c9 100644 --- a/components/Helpers/Helpers/main_globals.hpp +++ b/components/Helpers/Helpers/main_globals.hpp @@ -21,4 +21,8 @@ void setStartupCommandReceived(bool startupCommandReceived); bool getStartupPaused(); void setStartupPaused(bool startupPaused); +// Tracks whether USB handover from usb_serial_jtag to TinyUSB was performed +bool getUsbHandoverDone(); +void setUsbHandoverDone(bool done); + #endif \ No newline at end of file diff --git a/components/SerialManager/SerialManager/SerialManager.cpp b/components/SerialManager/SerialManager/SerialManager.cpp index c666acf..8cfa168 100644 --- a/components/SerialManager/SerialManager/SerialManager.cpp +++ b/components/SerialManager/SerialManager/SerialManager.cpp @@ -24,13 +24,36 @@ void SerialManager::try_receive() int current_position = 0; int len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20); + // If driver is uninstalled or an error occurs, abort read gracefully + if (len < 0) + { + return; + } + // since we've got something on the serial port // we gotta keep reading until we've got the whole message - while (len) + while (len > 0) { - memcpy(this->data + current_position, this->temp_data, len); + // Prevent buffer overflow + if (current_position + len >= BUF_SIZE) + { + int copy_len = BUF_SIZE - 1 - current_position; + if (copy_len > 0) + { + memcpy(this->data + current_position, this->temp_data, copy_len); + current_position += copy_len; + } + // Drop the rest of the input to avoid overflow + break; + } + memcpy(this->data + current_position, this->temp_data, (size_t)len); current_position += len; len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20); + if (len < 0) + { + // Driver likely uninstalled during handover; stop processing this cycle + break; + } } if (current_position) @@ -42,9 +65,10 @@ void SerialManager::try_receive() // Notify main that a command was received during startup notify_startup_command_received(); - const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast(this->data))); - const auto resultMessage = result.getResult(); - usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); + const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast(this->data))); + const auto resultMessage = result.getResult(); + int written = usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); + (void)written; // ignore errors if driver already uninstalled } } @@ -79,6 +103,7 @@ void SerialManager::send_heartbeat() sprintf(heartbeat, "{\"heartbeat\":\"openiris_setup_mode\",\"serial\":\"%s\"}\n", serial_number); usb_serial_jtag_write_bytes(heartbeat, strlen(heartbeat), 1000 / 20); + // Ignore return value; if the driver was uninstalled, this is a no-op } bool SerialManager::should_send_heartbeat() @@ -123,4 +148,19 @@ void HandleSerialManagerTask(void *pvParameters) lastHeartbeat = currentTime; } } +} + +void SerialManager::shutdown() +{ + // Stop heartbeats; timer will be deleted by main if needed. + // Uninstall the USB Serial JTAG driver to free the internal USB for TinyUSB. + esp_err_t err = usb_serial_jtag_driver_uninstall(); + if (err == ESP_OK) + { + ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled"); + } + else if (err != ESP_ERR_INVALID_STATE) + { + ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err)); + } } \ No newline at end of file diff --git a/components/SerialManager/SerialManager/SerialManager.hpp b/components/SerialManager/SerialManager/SerialManager.hpp index 5205a69..a612957 100644 --- a/components/SerialManager/SerialManager/SerialManager.hpp +++ b/components/SerialManager/SerialManager/SerialManager.hpp @@ -27,6 +27,7 @@ public: void send_heartbeat(); bool should_send_heartbeat(); void notify_startup_command_received(); + void shutdown(); private: std::shared_ptr commandManager; diff --git a/components/UVCStream/CMakeLists.txt b/components/UVCStream/CMakeLists.txt index d748e3f..8deee2a 100644 --- a/components/UVCStream/CMakeLists.txt +++ b/components/UVCStream/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "UVCStream/UVCStream.cpp" INCLUDE_DIRS "UVCStream" - REQUIRES esp_timer esp32-camera StateManager usb_device_uvc CameraManager + REQUIRES esp_timer esp32-camera StateManager usb_device_uvc CameraManager Helpers ) \ No newline at end of file diff --git a/components/UVCStream/UVCStream/UVCStream.cpp b/components/UVCStream/UVCStream/UVCStream.cpp index f0d8985..77b51e0 100644 --- a/components/UVCStream/UVCStream/UVCStream.cpp +++ b/components/UVCStream/UVCStream/UVCStream.cpp @@ -1,5 +1,9 @@ #include "UVCStream.hpp" #include // for snprintf +#include "driver/usb_serial_jtag.h" // for clean handover from COM to TinyUSB +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +// no deps on main globals here; handover is performed in main before calling setup when needed static const char *UVC_STREAM_TAG = "[UVC DEVICE]"; @@ -33,7 +37,6 @@ extern "C" { static esp_err_t UVCStreamHelpers::camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx) { - auto *mgr = static_cast(cb_ctx); ESP_LOGI(UVC_STREAM_TAG, "Camera Start"); ESP_LOGI(UVC_STREAM_TAG, "Format: %d, width: %d, height: %d, rate: %d", format, width, height, rate); framesize_t frame_size = FRAMESIZE_QVGA; @@ -79,6 +82,7 @@ static void UVCStreamHelpers::camera_stop_cb(void *cb_ctx) static uvc_fb_t *UVCStreamHelpers::camera_fb_get_cb(void *cb_ctx) { auto *mgr = static_cast(cb_ctx); + (void)mgr; s_fb.cam_fb_p = esp_camera_fb_get(); if (!s_fb.cam_fb_p) diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index 69a4139..4fbfa4a 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -98,6 +98,18 @@ void start_video_streaming(void *arg) #ifdef CONFIG_GENERAL_WIRED_MODE ESP_LOGI("[MAIN]", "Starting UVC streaming mode."); ESP_LOGI("[MAIN]", "Initializing UVC hardware..."); + // If we were given the Serial task handle, stop the task and uninstall the driver + if (arg != nullptr) + { + const auto serialTaskHandle = static_cast(arg); + vTaskDelete(serialTaskHandle); + ESP_LOGI("[MAIN]", "Serial task deleted before UVC init"); + serialManager->shutdown(); + ESP_LOGI("[MAIN]", "Serial driver uninstalled"); + // Leave a small gap for the host to see COM disappear + vTaskDelay(pdMS_TO_TICKS(200)); + setUsbHandoverDone(true); + } esp_err_t ret = uvcStream.setup(); if (ret != ESP_OK) { @@ -105,6 +117,8 @@ void start_video_streaming(void *arg) return; } uvcStream.start(); + ESP_LOGI("[MAIN]", "UVC streaming started"); + return; // UVC path complete, do not fall through to WiFi #else ESP_LOGE("[MAIN]", "UVC mode selected but the board likely does not support it."); ESP_LOGI("[MAIN]", "Falling back to WiFi mode if credentials available");