mirror of
https://github.com/MrUnknownDE/OpenIris-ESPIDF.git
synced 2026-04-10 18:33:44 +02:00
Initial CDC implementation
This commit is contained in:
@@ -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
|
||||
)
|
||||
@@ -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<const char *>(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<const char *>(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<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.
|
||||
// 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<CommandManager *>(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<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)
|
||||
}
|
||||
|
||||
// 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<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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -17,7 +17,8 @@ extern std::shared_ptr<CameraManager> cameraHandler;
|
||||
extern std::shared_ptr<ProjectConfig> deviceConfig;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
const char *get_uvc_device_name();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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<DependencyRegistry>();
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user