diff --git a/components/SerialManager/CMakeLists.txt b/components/SerialManager/CMakeLists.txt index aaaa174..41c18ab 100644 --- a/components/SerialManager/CMakeLists.txt +++ b/components/SerialManager/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "SerialManager/SerialManager.cpp" INCLUDE_DIRS "SerialManager" - REQUIRES esp_driver_uart CommandManager ProjectConfig + REQUIRES esp_driver_uart CommandManager ProjectConfig tinyusb ) \ No newline at end of file diff --git a/components/SerialManager/SerialManager/SerialManager.cpp b/components/SerialManager/SerialManager/SerialManager.cpp index 8cfa168..9c8ce38 100644 --- a/components/SerialManager/SerialManager/SerialManager.cpp +++ b/components/SerialManager/SerialManager/SerialManager.cpp @@ -1,6 +1,7 @@ #include "SerialManager.hpp" #include "esp_log.h" #include "main_globals.hpp" +#include "tusb.h" #define BUF_SIZE (1024) @@ -65,10 +66,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(); - int written = usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); - (void)written; // ignore errors if driver already uninstalled + 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 } } @@ -127,6 +128,22 @@ bool SerialManager::should_send_heartbeat() return wifiConfigs.empty(); } +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)); + // } +} + +// we can cancel this task once we're in cdc void HandleSerialManagerTask(void *pvParameters) { auto const serialManager = static_cast(pvParameters); @@ -150,17 +167,71 @@ void HandleSerialManagerTask(void *pvParameters) } } -void SerialManager::shutdown() +void HandleCDCSerialManagerTask(void *pvParameters) { - // 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) + auto const commandManager = static_cast(pvParameters); + static char buffer[BUF_SIZE]; + auto idx = 0; + + cdc_command_packet_t packet; + while (true) { - ESP_LOGI("[SERIAL]", "usb_serial_jtag driver uninstalled"); + if (xQueueReceive(cdcMessageQueue, &packet, portMAX_DELAY) == pdTRUE) + { + for (auto i = 0; i < packet.len; i++) + { + buffer[idx++] = packet.data[i]; + // if we're at the end of the buffer, try to process the command anyway + // if we've got a new line, we've finished sending the commands, process them + if (idx >= BUF_SIZE || buffer[idx - 1] == '\n' || buffer[idx - 1] == '\r') + { + buffer[idx - 1] = '\0'; + const auto result = commandManager->executeFromJson(std::string_view(reinterpret_cast(buffer))); + const auto resultMessage = result.getResult(); + tud_cdc_write(resultMessage.c_str(), resultMessage.length()); + tud_cdc_write_flush(); + idx = 0; + } + } + } } - else if (err != ESP_ERR_INVALID_STATE) +} + +// tud_cdc_rx_cb is defined as TU_ATTR_WEAK so we can override it, we will be called back if we get some data +// but we don't want to do any processing here since we don't want to risk blocking +// grab the data and send it to a queue, a special task will process it and handle with the command manager +extern "C" void tud_cdc_rx_cb(uint8_t itf) +{ + // we can void the interface number + (void)itf; + cdc_command_packet_t packet; + auto len = tud_cdc_available(); + + if (len > 0) { - ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err)); + auto read = tud_cdc_read(packet.data, sizeof(packet.data)); + if (read > 0) + { + // we should be safe here, given that the max buffer size is 64 + packet.len = static_cast(read); + xQueueSend(cdcMessageQueue, &packet, 1); + } } +} + +extern "C" void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) +{ + (void)itf; + (void)dtr; + (void)rts; + + ESP_LOGI("[SERIAL]", "CDC line state changed: DTR=%d, RTS=%d", dtr, rts); +} + +void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding) +{ + (void)itf; + ESP_LOGI("[SERIAL]", "CDC line coding: %" PRIu32 " bps, %d stop bits, %d parity, %d data bits", + p_line_coding->bit_rate, p_line_coding->stop_bits, + p_line_coding->parity, p_line_coding->data_bits); } \ No newline at end of file diff --git a/components/SerialManager/SerialManager/SerialManager.hpp b/components/SerialManager/SerialManager/SerialManager.hpp index a612957..39ee44a 100644 --- a/components/SerialManager/SerialManager/SerialManager.hpp +++ b/components/SerialManager/SerialManager/SerialManager.hpp @@ -18,6 +18,17 @@ #include "esp_vfs_dev.h" #include "esp_mac.h" +void tud_cdc_rx_cb(uint8_t itf); +void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts); + +extern QueueHandle_t cdcMessageQueue; + +struct cdc_command_packet_t +{ + uint8_t len; + uint8_t data[64]; +}; + class SerialManager { public: @@ -38,4 +49,5 @@ private: }; void HandleSerialManagerTask(void *pvParameters); +void HandleCDCSerialManagerTask(void *pvParameters) #endif \ No newline at end of file diff --git a/components/UVCStream/UVCStream/UVCStream.hpp b/components/UVCStream/UVCStream/UVCStream.hpp index 46ff69e..b0dfb43 100644 --- a/components/UVCStream/UVCStream/UVCStream.hpp +++ b/components/UVCStream/UVCStream/UVCStream.hpp @@ -17,7 +17,8 @@ extern std::shared_ptr cameraHandler; extern std::shared_ptr deviceConfig; #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif const char *get_uvc_device_name(); diff --git a/components/usb_device_uvc/tusb/tusb_config.h b/components/usb_device_uvc/tusb/tusb_config.h index 41aa694..bbcb2da 100644 --- a/components/usb_device_uvc/tusb/tusb_config.h +++ b/components/usb_device_uvc/tusb/tusb_config.h @@ -28,17 +28,18 @@ #include "uvc_frame_config.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -//--------------------------------------------------------------------+ -// Board Specific Configuration -//--------------------------------------------------------------------+ + //--------------------------------------------------------------------+ + // Board Specific Configuration + //--------------------------------------------------------------------+ #ifdef CONFIG_TINYUSB_RHPORT_HS -# define CFG_TUSB_RHPORT1_MODE OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED +#define CFG_TUSB_RHPORT1_MODE OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED #else -# define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED #endif //-------------------------------------------------------------------- @@ -55,20 +56,20 @@ extern "C" { #endif #ifndef CFG_TUSB_OS -#define CFG_TUSB_OS OPT_OS_FREERTOS +#define CFG_TUSB_OS OPT_OS_FREERTOS #endif // Espressif IDF requires "freertos/" prefix in include path #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32P4) -#define CFG_TUSB_OS_INC_PATH freertos/ +#define CFG_TUSB_OS_INC_PATH freertos/ #endif #ifndef CFG_TUSB_DEBUG -#define CFG_TUSB_DEBUG 0 +#define CFG_TUSB_DEBUG 0 #endif // Enable Device stack -#define CFG_TUD_ENABLED 1 +#define CFG_TUD_ENABLED 1 /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. * Tinyusb use follows macros to declare transferring memory so that they can be put @@ -82,71 +83,54 @@ extern "C" { #endif #ifndef CFG_TUSB_MEM_ALIGN -#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4))) #endif -//-------------------------------------------------------------------- -// DEVICE CONFIGURATION -//-------------------------------------------------------------------- + //-------------------------------------------------------------------- + // DEVICE CONFIGURATION + //-------------------------------------------------------------------- #ifndef CFG_TUD_ENDPOINT0_SIZE -#define CFG_TUD_ENDPOINT0_SIZE 64 +#define CFG_TUD_ENDPOINT0_SIZE 64 #endif -//------------- CLASS -------------// + //------------- CLASS -------------// -// The number of video control interfaces -// The number of video streaming interfaces -#if CONFIG_UVC_SUPPORT_TWO_CAM -#define CFG_TUD_VIDEO 2 -#define CFG_TUD_VIDEO_STREAMING 2 -#else -#define CFG_TUD_VIDEO 1 -#define CFG_TUD_VIDEO_STREAMING 1 -#endif +#define CFG_TUD_CDC 1 + +// CDC FIFO size of TX and RX +#define CFG_TUD_CDC_RX_BUFSIZE 256 +#define CFG_TUD_CDC_TX_BUFSIZE 256 + +// CDC Endpoint transfer buffer size, more is faster +#define CFG_TUD_CDC_EP_BUFSIZE 64 + + // The number of video control interfaces + // The number of video streaming interfaces + +#define CFG_TUD_VIDEO 1 +#define CFG_TUD_VIDEO_STREAMING 1 // video streaming endpoint size #ifdef UVC_CAM1_BULK_MODE #if CONFIG_TINYUSB_RHPORT_HS -#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 512 +#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 512 #else -#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 64 +#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 64 #endif #define CFG_TUD_CAM1_VIDEO_STREAMING_BULK 1 #else #define CFG_TUD_CAM1_VIDEO_STREAMING_BULK 0 #if CONFIG_TINYUSB_RHPORT_HS -#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 1023 +#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 1023 #else -#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 512 +#define CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE 512 #endif #endif #define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG (!FORMAT_MJPEG) -#if CONFIG_UVC_SUPPORT_TWO_CAM -#ifdef UVC_CAM2_BULK_MODE -#if CONFIG_TINYUSB_RHPORT_HS -#define CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE 512 -#else -#define CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE 64 -#endif -#define CFG_TUD_CAM2_VIDEO_STREAMING_BULK 1 -#else -#define CFG_TUD_CAM2_VIDEO_STREAMING_BULK 0 -#if CONFIG_TINYUSB_RHPORT_HS -#define CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE 1023 -#else -#define CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE 512 -#endif -#endif -#endif - -#if CONFIG_UVC_SUPPORT_TWO_CAM -#define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE (CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE > CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE?CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE:CFG_TUD_CAM2_VIDEO_STREAMING_EP_BUFSIZE) -#else #define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE -#endif #ifdef __cplusplus } diff --git a/components/usb_device_uvc/tusb/usb_descriptors.c b/components/usb_device_uvc/tusb/usb_descriptors.c index 24dc7c1..315d141 100644 --- a/components/usb_device_uvc/tusb/usb_descriptors.c +++ b/components/usb_device_uvc/tusb/usb_descriptors.c @@ -65,14 +65,18 @@ uint8_t const *tud_descriptor_device_cb(void) // Configuration Descriptor //--------------------------------------------------------------------+ // String descriptor indices used in interface descriptors -#define STRID_LANGID 0 -#define STRID_MANUFACTURER 1 -#define STRID_PRODUCT 2 -#define STRID_SERIAL 3 -#define STRID_UVC_CAM1 4 +#define STRID_LANGID 0 +#define STRID_MANUFACTURER 1 +#define STRID_PRODUCT 2 +#define STRID_SERIAL 3 +#define STRID_UVC_CAM1 4 +// Endpoint numbers for CDC +#define EPNUM_CDC_NOTIF 0x81 +#define EPNUM_CDC_OUT 0x02 +#define EPNUM_CDC_IN 0x82 // Endpoint numbers for UVC video IN endpoints (device -> host) -#define EPNUM_CAM1_VIDEO_IN 0x81 +#define EPNUM_CAM1_VIDEO_IN 0x83 #if CFG_TUD_CAM1_VIDEO_STREAMING_BULK @@ -113,7 +117,7 @@ uint8_t const *tud_descriptor_device_cb(void) #endif // CFG_TUD_CAM1_VIDEO_STREAMING_BULK // Total length of this configuration -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CAM1_VIDEO_CAPTURE_DESC_LEN) +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_CAM1_VIDEO_CAPTURE_DESC_LEN) // Full-speed configuration descriptor static uint8_t const desc_fs_configuration[] = { @@ -121,6 +125,7 @@ static uint8_t const desc_fs_configuration[] = { // total_length, attributes, power_mA) // attributes: 0 = bus-powered (default). Add TUSB_DESC_CONFIG_ATT_SELF_POWERED or _REMOTE_WAKEUP if needed. TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500), + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 6, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), // IAD for Video Control #if CFG_TUD_CAM1_VIDEO_STREAMING_BULK #if CONFIG_UVC_CAM1_MULTI_FRAMESIZE @@ -235,16 +240,16 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors - if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) + if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { return NULL; } const char *str = string_desc_arr[index]; - // Allow dynamic overrides for specific indices - if (index == STRID_SERIAL) + // Allow dynamic overrides for specific indices + if (index == STRID_SERIAL) str = get_serial_number(); - if (index == STRID_UVC_CAM1) + if (index == STRID_UVC_CAM1) str = get_uvc_device_name(); if (str == NULL) str = string_desc_arr[index]; diff --git a/components/usb_device_uvc/tusb/usb_descriptors.h b/components/usb_device_uvc/tusb/usb_descriptors.h index 42574cb..286a495 100644 --- a/components/usb_device_uvc/tusb/usb_descriptors.h +++ b/components/usb_device_uvc/tusb/usb_descriptors.h @@ -36,13 +36,13 @@ #define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02 enum { +#if (CFG_TUD_CDC) + ITF_NUM_CDC, + ITF_NUM_CDC_DATA, +#endif #if (CFG_TUD_VIDEO) ITF_NUM_VIDEO_CONTROL, ITF_NUM_VIDEO_STREAMING, -#if CONFIG_UVC_SUPPORT_TWO_CAM - ITF_NUM_VIDEO_CONTROL_2, - ITF_NUM_VIDEO_STREAMING_2, -#endif #endif ITF_NUM_TOTAL }; diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index fc9900b..7b0230d 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -32,6 +32,7 @@ esp_timer_handle_t timerHandle; QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent)); QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t)); +QueueHandle_t cdcMessageQueue = xQueueCreate(3, sizeof(cdc_command_packet_t)); auto *stateManager = new StateManager(eventQueue, ledStateQueue); auto dependencyRegistry = std::make_shared(); @@ -84,6 +85,11 @@ void disable_serial_manager_task(TaskHandle_t serialManagerHandle) // 3. Device attempts WiFi connection while maintaining setup interfaces // 4. Device reports connection status via serial // 5. User explicitly starts streaming after verifying connectivity + +// todo this is getting yeeted lmao +// new flow once more - auto - start the setup mode, we're gonna wait for a decision of what to do, maybe try and detect if we're connected to usb +// uvc mode - start the uvc stream right away, kill webserver and wifi +// wifi mode - start the wifi stream right away void start_video_streaming(void *arg) { // Get the stored device mode @@ -120,7 +126,7 @@ void start_video_streaming(void *arg) 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_LOGE("[MAIN]", "UVC mode selected but the board likely does not support it."); ESP_LOGI("[MAIN]", "Falling back to WiFi mode if credentials available"); deviceMode = StreamingMode::WIFI; #endif @@ -324,6 +330,14 @@ extern "C" void app_main(void) 1, // we only rely on the serial manager during provisioning, we can run it slower &serialManagerHandle); + xTaskCreate( + HandleCDCSerialManagerTask, + "HandleCDCSerialManagerTask", + 1024 * 6, + commandManager.get(), + 1, + nullptr); + wifiManager->Begin(); mdnsManager.start(); restAPI->begin(); @@ -355,5 +369,6 @@ extern "C" void app_main(void) ESP_ERROR_CHECK(esp_timer_start_once(timerHandle, CONFIG_GENERAL_UVC_DELAY * 1000000)); ESP_LOGI("[MAIN]", "Started 20-second startup timer"); ESP_LOGI("[MAIN]", "Send any command within 20 seconds to enter heartbeat mode"); + // todo make this a tasks context that stores all the handles setSerialManagerHandle(&serialManagerHandle); } \ No newline at end of file