Initial CDC implementation

This commit is contained in:
Lorow
2025-08-30 22:29:18 +02:00
parent f818023d0a
commit 0c9e254aba
8 changed files with 169 additions and 81 deletions

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "SerialManager/SerialManager.cpp" idf_component_register(SRCS "SerialManager/SerialManager.cpp"
INCLUDE_DIRS "SerialManager" INCLUDE_DIRS "SerialManager"
REQUIRES esp_driver_uart CommandManager ProjectConfig REQUIRES esp_driver_uart CommandManager ProjectConfig tinyusb
) )

View File

@@ -1,6 +1,7 @@
#include "SerialManager.hpp" #include "SerialManager.hpp"
#include "esp_log.h" #include "esp_log.h"
#include "main_globals.hpp" #include "main_globals.hpp"
#include "tusb.h"
#define BUF_SIZE (1024) #define BUF_SIZE (1024)
@@ -127,6 +128,22 @@ bool SerialManager::should_send_heartbeat()
return wifiConfigs.empty(); 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) void HandleSerialManagerTask(void *pvParameters)
{ {
auto const serialManager = static_cast<SerialManager *>(pvParameters); auto const serialManager = static_cast<SerialManager *>(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. auto const commandManager = static_cast<CommandManager *>(pvParameters);
// Uninstall the USB Serial JTAG driver to free the internal USB for TinyUSB. static char buffer[BUF_SIZE];
esp_err_t err = usb_serial_jtag_driver_uninstall(); auto idx = 0;
if (err == ESP_OK)
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<const char *>(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)
{
ESP_LOGW("[SERIAL]", "usb_serial_jtag_driver_uninstall returned %s", esp_err_to_name(err));
} }
} }
// 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)
{
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<uint8_t>(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);
}

View File

@@ -18,6 +18,17 @@
#include "esp_vfs_dev.h" #include "esp_vfs_dev.h"
#include "esp_mac.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 class SerialManager
{ {
public: public:
@@ -38,4 +49,5 @@ private:
}; };
void HandleSerialManagerTask(void *pvParameters); void HandleSerialManagerTask(void *pvParameters);
void HandleCDCSerialManagerTask(void *pvParameters)
#endif #endif

View File

@@ -17,7 +17,8 @@ extern std::shared_ptr<CameraManager> cameraHandler;
extern std::shared_ptr<ProjectConfig> deviceConfig; extern std::shared_ptr<ProjectConfig> deviceConfig;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C"
{
#endif #endif
const char *get_uvc_device_name(); const char *get_uvc_device_name();

View File

@@ -28,17 +28,18 @@
#include "uvc_frame_config.h" #include "uvc_frame_config.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C"
{
#endif #endif
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
// Board Specific Configuration // Board Specific Configuration
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
#ifdef CONFIG_TINYUSB_RHPORT_HS #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 #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 #endif
//-------------------------------------------------------------------- //--------------------------------------------------------------------
@@ -82,28 +83,33 @@ extern "C" {
#endif #endif
#ifndef CFG_TUSB_MEM_ALIGN #ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
#endif #endif
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// DEVICE CONFIGURATION // DEVICE CONFIGURATION
//-------------------------------------------------------------------- //--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE #ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64 #define CFG_TUD_ENDPOINT0_SIZE 64
#endif #endif
//------------- CLASS -------------// //------------- CLASS -------------//
#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
// 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 1
#define CFG_TUD_VIDEO_STREAMING 1 #define CFG_TUD_VIDEO_STREAMING 1
#endif
// video streaming endpoint size // video streaming endpoint size
#ifdef UVC_CAM1_BULK_MODE #ifdef UVC_CAM1_BULK_MODE
@@ -124,29 +130,7 @@ extern "C" {
#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG (!FORMAT_MJPEG) #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 #define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE CFG_TUD_CAM1_VIDEO_STREAMING_EP_BUFSIZE
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -71,8 +71,12 @@ uint8_t const *tud_descriptor_device_cb(void)
#define STRID_SERIAL 3 #define STRID_SERIAL 3
#define STRID_UVC_CAM1 4 #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) // 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 #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 #endif // CFG_TUD_CAM1_VIDEO_STREAMING_BULK
// Total length of this configuration // 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 // Full-speed configuration descriptor
static uint8_t const desc_fs_configuration[] = { static uint8_t const desc_fs_configuration[] = {
@@ -121,6 +125,7 @@ static uint8_t const desc_fs_configuration[] = {
// total_length, attributes, power_mA) // total_length, attributes, power_mA)
// attributes: 0 = bus-powered (default). Add TUSB_DESC_CONFIG_ATT_SELF_POWERED or _REMOTE_WAKEUP if needed. // 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_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 // IAD for Video Control
#if CFG_TUD_CAM1_VIDEO_STREAMING_BULK #if CFG_TUD_CAM1_VIDEO_STREAMING_BULK
#if CONFIG_UVC_CAM1_MULTI_FRAMESIZE #if CONFIG_UVC_CAM1_MULTI_FRAMESIZE

View File

@@ -36,13 +36,13 @@
#define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02 #define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02
enum { enum {
#if (CFG_TUD_CDC)
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
#endif
#if (CFG_TUD_VIDEO) #if (CFG_TUD_VIDEO)
ITF_NUM_VIDEO_CONTROL, ITF_NUM_VIDEO_CONTROL,
ITF_NUM_VIDEO_STREAMING, ITF_NUM_VIDEO_STREAMING,
#if CONFIG_UVC_SUPPORT_TWO_CAM
ITF_NUM_VIDEO_CONTROL_2,
ITF_NUM_VIDEO_STREAMING_2,
#endif
#endif #endif
ITF_NUM_TOTAL ITF_NUM_TOTAL
}; };

View File

@@ -32,6 +32,7 @@
esp_timer_handle_t timerHandle; esp_timer_handle_t timerHandle;
QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent)); QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent));
QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t)); 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 *stateManager = new StateManager(eventQueue, ledStateQueue);
auto dependencyRegistry = std::make_shared<DependencyRegistry>(); auto dependencyRegistry = std::make_shared<DependencyRegistry>();
@@ -84,6 +85,11 @@ void disable_serial_manager_task(TaskHandle_t serialManagerHandle)
// 3. Device attempts WiFi connection while maintaining setup interfaces // 3. Device attempts WiFi connection while maintaining setup interfaces
// 4. Device reports connection status via serial // 4. Device reports connection status via serial
// 5. User explicitly starts streaming after verifying connectivity // 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) void start_video_streaming(void *arg)
{ {
// Get the stored device mode // Get the stored device mode
@@ -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 1, // we only rely on the serial manager during provisioning, we can run it slower
&serialManagerHandle); &serialManagerHandle);
xTaskCreate(
HandleCDCSerialManagerTask,
"HandleCDCSerialManagerTask",
1024 * 6,
commandManager.get(),
1,
nullptr);
wifiManager->Begin(); wifiManager->Begin();
mdnsManager.start(); mdnsManager.start();
restAPI->begin(); 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_ERROR_CHECK(esp_timer_start_once(timerHandle, CONFIG_GENERAL_UVC_DELAY * 1000000));
ESP_LOGI("[MAIN]", "Started 20-second startup timer"); ESP_LOGI("[MAIN]", "Started 20-second startup timer");
ESP_LOGI("[MAIN]", "Send any command within 20 seconds to enter heartbeat mode"); 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); setSerialManagerHandle(&serialManagerHandle);
} }