From 23bb930255b3afdef4efd8c30df94b66cb50a1a2 Mon Sep 17 00:00:00 2001 From: Lorow Date: Sat, 19 Oct 2024 14:24:50 +0200 Subject: [PATCH] PoC wired mode over UVC, add logs over websockets, needs cleanup --- bootloader_components/CMakeLists.txt | 11 + bootloader_components/boot_hooks.c | 24 ++ .../CameraManager/CameraManager.cpp | 65 ----- .../Preferences/Preferences/Preferences.hpp | 6 +- components/StreamServer/CMakeLists.txt | 2 +- .../StreamServer/StreamServer.cpp | 32 ++- .../StreamServer/StreamServer.hpp | 11 +- components/UVCStream/CMakeLists.txt | 4 + components/UVCStream/UVCStream/UVCStream.cpp | 239 ++++++++++++++++++ components/UVCStream/UVCStream/UVCStream.hpp | 37 +++ components/WebSocketLogger/CMakeLists.txt | 4 + .../WebSocketLogger/WebSocketLogger.cpp | 71 ++++++ .../WebSocketLogger/WebSocketLogger.hpp | 39 +++ main/openiris_main.cpp | 66 +++-- sdkconfig | 34 +-- sdkconfig.old | 34 ++- 16 files changed, 548 insertions(+), 131 deletions(-) create mode 100644 bootloader_components/CMakeLists.txt create mode 100644 bootloader_components/boot_hooks.c create mode 100644 components/UVCStream/CMakeLists.txt create mode 100644 components/UVCStream/UVCStream/UVCStream.cpp create mode 100644 components/UVCStream/UVCStream/UVCStream.hpp create mode 100644 components/WebSocketLogger/CMakeLists.txt create mode 100644 components/WebSocketLogger/WebSocketLogger/WebSocketLogger.cpp create mode 100644 components/WebSocketLogger/WebSocketLogger/WebSocketLogger.hpp diff --git a/bootloader_components/CMakeLists.txt b/bootloader_components/CMakeLists.txt new file mode 100644 index 0000000..6ff8f57 --- /dev/null +++ b/bootloader_components/CMakeLists.txt @@ -0,0 +1,11 @@ +# source: https://github.com/espressif/esp-iot-solution/blob/4730d91db70df7e6e0a3191d725ab1c5f98ff9ce/examples/usb/device/usb_webcam/bootloader_components/boot_hooks/CMakeLists.txt +if("$ENV{IDF_TARGET}" STREQUAL "esp32s3") +idf_component_register(SRCS "boot_hooks.c") +endif() + +# We need to force GCC to integrate this static library into the +# bootloader link. Indeed, by default, as the hooks in the bootloader are weak, +# the linker would just ignore the symbols in the extra. (i.e. not strictly +# required) +# To do so, we need to define the symbol (function) `bootloader_hooks_include` +# within hooks.c source file. \ No newline at end of file diff --git a/bootloader_components/boot_hooks.c b/bootloader_components/boot_hooks.c new file mode 100644 index 0000000..6ed84f6 --- /dev/null +++ b/bootloader_components/boot_hooks.c @@ -0,0 +1,24 @@ +// source: https://github.com/espressif/esp-iot-solution/blob/4730d91db70df7e6e0a3191d725ab1c5f98ff9ce/examples/usb/device/usb_webcam/bootloader_components/boot_hooks/boot_hooks.c + +#ifdef CONFIG_WIRED_MODE +#include "esp_log.h" +#include "soc/rtc_cntl_struct.h" +#include "soc/usb_serial_jtag_reg.h" + +/* Function used to tell the linker to include this file + * with all its symbols. + */ + +void bootloader_hooks_include(void) +{ +} + +void bootloader_before_init(void) +{ + + // Disable D+ pullup, to prevent the USB host from retrieving USB-Serial-JTAG's descriptor. + SET_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_PAD_PULL_OVERRIDE); + CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_DP_PULLUP); + CLEAR_PERI_REG_MASK(USB_SERIAL_JTAG_CONF0_REG, USB_SERIAL_JTAG_USB_PAD_ENABLE); +} +#endif \ No newline at end of file diff --git a/components/CameraManager/CameraManager/CameraManager.cpp b/components/CameraManager/CameraManager/CameraManager.cpp index 7106056..53a7765 100644 --- a/components/CameraManager/CameraManager/CameraManager.cpp +++ b/components/CameraManager/CameraManager/CameraManager.cpp @@ -51,40 +51,6 @@ void CameraManager::setupCameraPinout() #if ETVR_EYE_TRACKER_USB_API xclk_freq_hz = USB_DEFAULT_XCLK_FREQ_HZ; #endif - - // config = { - // .pin_pwdn = CAM_PIN_PWDN, - // .pin_reset = CAM_PIN_RESET, - // .pin_xclk = CAM_PIN_XCLK, - // .pin_sccb_sda = CAM_PIN_SIOD, - // .pin_sccb_scl = CAM_PIN_SIOC, - - // .pin_d7 = CAM_PIN_D7, - // .pin_d6 = CAM_PIN_D6, - // .pin_d5 = CAM_PIN_D5, - // .pin_d4 = CAM_PIN_D4, - // .pin_d3 = CAM_PIN_D3, - // .pin_d2 = CAM_PIN_D2, - // .pin_d1 = CAM_PIN_D1, - // .pin_d0 = CAM_PIN_D0, - // .pin_vsync = CAM_PIN_VSYNC, - // .pin_href = CAM_PIN_HREF, - // .pin_pclk = CAM_PIN_PCLK, - - // // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental) - // .xclk_freq_hz = 20000000, - // .ledc_timer = LEDC_TIMER_0, - // .ledc_channel = LEDC_CHANNEL_0, - - // .pixel_format = PIXFORMAT_RGB565, // YUV422,GRAYSCALE,RGB565,JPEG - // .frame_size = FRAMESIZE_QVGA, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates. - - // .jpeg_quality = 12, // 0-63, for OV series camera sensors, lower number means higher quality - // .fb_count = 1, // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. - // .fb_location = CAMERA_FB_IN_PSRAM, - // .grab_mode = CAMERA_GRAB_WHEN_EMPTY, - // }; - // todo fix pinout in sdkconfig lmao config = { .pin_pwdn = -1, // CAM_PIN_PWDN, @@ -118,37 +84,6 @@ void CameraManager::setupCameraPinout() .fb_count = 2, // 3 // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode. .fb_location = CAMERA_FB_IN_PSRAM, // maybe it cannot put them fully in psram? .grab_mode = CAMERA_GRAB_WHEN_EMPTY, // CAMERA_GRAB_LATEST - - // .pin_pwdn = CONFIG_PWDN_GPIO_NUM, - // .pin_reset = CONFIG_RESET_GPIO_NUM, - // .pin_xclk = CONFIG_XCLK_GPIO_NUM, - // .pin_sccb_sda = CONFIG_SIOD_GPIO_NUM, - // .pin_sccb_scl = CONFIG_SIOC_GPIO_NUM, - - // .pin_d7 = CONFIG_Y2_GPIO_NUM, - // .pin_d6 = CONFIG_Y3_GPIO_NUM, - // .pin_d5 = CONFIG_Y4_GPIO_NUM, - // .pin_d4 = CONFIG_Y5_GPIO_NUM, - // .pin_d3 = CONFIG_Y6_GPIO_NUM, - // .pin_d2 = CONFIG_Y7_GPIO_NUM, - // .pin_d1 = CONFIG_Y8_GPIO_NUM, - // .pin_d0 = CONFIG_Y9_GPIO_NUM, - - // .pin_vsync = CONFIG_VSYNC_GPIO_NUM, - // .pin_href = CONFIG_HREF_GPIO_NUM, - // .pin_pclk = CONFIG_PCLK_GPIO_NUM, - - // .xclk_freq_hz = xclk_freq_hz, - - // .ledc_timer = LEDC_TIMER_0, - // .ledc_channel = LEDC_CHANNEL_0, - - // .pixel_format = PIXFORMAT_JPEG, - // .frame_size = FRAMESIZE_240X240, - // .jpeg_quality = 7, - // .fb_count = 3, - // .fb_location = CAMERA_FB_IN_PSRAM, - // .grab_mode = CAMERA_GRAB_LATEST, }; } diff --git a/components/Preferences/Preferences/Preferences.hpp b/components/Preferences/Preferences/Preferences.hpp index 2f4f5c3..5eef308 100644 --- a/components/Preferences/Preferences/Preferences.hpp +++ b/components/Preferences/Preferences/Preferences.hpp @@ -2,8 +2,8 @@ // ported by: Lorow, do not trust this please #pragma once -#ifndef _PREFERENCES_HPP_ -#define _PREFERENCES_HPP_ +#ifndef PREFERENCES_HPP +#define PREFERENCES_HPP #include #include @@ -84,4 +84,4 @@ public: size_t freeEntries(); }; -#endif _PREFERENCES_HPP_ +#endif PREFERENCES_HPP diff --git a/components/StreamServer/CMakeLists.txt b/components/StreamServer/CMakeLists.txt index 02e35bb..3c0a30a 100644 --- a/components/StreamServer/CMakeLists.txt +++ b/components/StreamServer/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "StreamServer/StreamServer.cpp" INCLUDE_DIRS "StreamServer" - REQUIRES esp32-camera StateManager ProjectConfig esp_http_server Helpers + REQUIRES esp32-camera StateManager ProjectConfig esp_http_server Helpers WebSocketLogger ) \ No newline at end of file diff --git a/components/StreamServer/StreamServer/StreamServer.cpp b/components/StreamServer/StreamServer/StreamServer.cpp index 84c5cec..1b5d3ca 100644 --- a/components/StreamServer/StreamServer/StreamServer.cpp +++ b/components/StreamServer/StreamServer/StreamServer.cpp @@ -6,7 +6,10 @@ constexpr static const char *STREAM_PART = "Content-Type: image/jpeg\r\nContent- static const char *STREAM_SERVER_TAG = "[STREAM_SERVER]"; -// the stream is super low FPS, find out why +StreamServer::StreamServer(const int STREAM_PORT) : STREAM_SERVER_PORT(STREAM_PORT) +{ +} + esp_err_t StreamHelpers::stream(httpd_req_t *req) { long last_request_time = 0; @@ -76,15 +79,14 @@ esp_err_t StreamHelpers::stream(httpd_req_t *req) return response; } -StreamServer::StreamServer(const int STREAM_PORT) : STREAM_SERVER_PORT(STREAM_PORT) {} +esp_err_t StreamHelpers::ws_logs_handle(httpd_req_t *req) +{ + auto ret = webSocketLogger.register_socket_client(req); + return ret; +} esp_err_t StreamServer::startStreamServer() { - if (cameraStateManager.getCurrentState() != CameraState_e::Camera_Success) - { - ESP_LOGE(STREAM_SERVER_TAG, "Camera not initialized. Cannot start stream server."); - return ESP_FAIL; - } httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.stack_size = 20480; @@ -99,6 +101,14 @@ esp_err_t StreamServer::startStreamServer() .user_ctx = nullptr, }; + httpd_uri_t logs_ws = { + .uri = "/ws", + .method = HTTP_GET, + .handler = &StreamHelpers::ws_logs_handle, + .user_ctx = nullptr, + .is_websocket = true, + }; + int status = httpd_start(&camera_stream, &config); if (status != ESP_OK) @@ -107,7 +117,15 @@ esp_err_t StreamServer::startStreamServer() return status; } + httpd_register_uri_handler(camera_stream, &logs_ws); + if (cameraStateManager.getCurrentState() != CameraState_e::Camera_Success) + { + ESP_LOGE(STREAM_SERVER_TAG, "Camera not initialized. Cannot start stream server. Logs server will be running."); + return ESP_FAIL; + } + httpd_register_uri_handler(camera_stream, &stream_page); + ESP_LOGI(STREAM_SERVER_TAG, "Stream server started on port %d", STREAM_SERVER_PORT); // todo add printing IP addr here diff --git a/components/StreamServer/StreamServer/StreamServer.hpp b/components/StreamServer/StreamServer/StreamServer.hpp index ae584be..244b7b7 100644 --- a/components/StreamServer/StreamServer/StreamServer.hpp +++ b/components/StreamServer/StreamServer/StreamServer.hpp @@ -9,22 +9,29 @@ #include "esp_http_server.h" #include "esp_timer.h" #include +#include #include +extern WebSocketLogger webSocketLogger; + namespace StreamHelpers { esp_err_t stream(httpd_req_t *req); + esp_err_t ws_logs_handle(httpd_req_t *req); } class StreamServer { private: - httpd_handle_t camera_stream = nullptr; int STREAM_SERVER_PORT; + httpd_handle_t camera_stream = nullptr; public: - StreamServer(const int STREAM_PORT = 80); + StreamServer(const int STREAM_PORT); esp_err_t startStreamServer(); + + esp_err_t stream(httpd_req_t *req); + esp_err_t ws_logs_handle(httpd_req_t *req); }; #endif \ No newline at end of file diff --git a/components/UVCStream/CMakeLists.txt b/components/UVCStream/CMakeLists.txt new file mode 100644 index 0000000..6f7719c --- /dev/null +++ b/components/UVCStream/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "UVCStream/UVCStream.cpp" + INCLUDE_DIRS "UVCStream" + REQUIRES esp_timer esp32-camera StateManager usb_device_uvc +) \ No newline at end of file diff --git a/components/UVCStream/UVCStream/UVCStream.cpp b/components/UVCStream/UVCStream/UVCStream.cpp new file mode 100644 index 0000000..4fd0b54 --- /dev/null +++ b/components/UVCStream/UVCStream/UVCStream.cpp @@ -0,0 +1,239 @@ +#include "UVCStream.hpp" +constexpr int UVC_MAX_FRAMESIZE_SIZE(75 * 1024); + +static const char *UVC_STREAM_TAG = "[UVC DEVICE]"; + +// debug only +static esp_err_t camera_init(int xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, int jpeg_quality, uint8_t fb_count) +{ + static bool inited = false; + static int cur_xclk_freq_hz = 20000000; + static pixformat_t cur_pixel_format = PIXFORMAT_JPEG; + static framesize_t cur_frame_size = FRAMESIZE_96X96; + static int cur_jpeg_quality = 0; + static uint8_t cur_fb_count = 0; + + if ((inited && cur_xclk_freq_hz == xclk_freq_hz && cur_pixel_format == pixel_format && cur_frame_size == frame_size && cur_fb_count == fb_count && cur_jpeg_quality == jpeg_quality)) + { + ESP_LOGD(UVC_STREAM_TAG, "camera already inited"); + return ESP_OK; + } + else if (inited) + { + esp_camera_return_all(); + esp_camera_deinit(); + inited = false; + ESP_LOGI(UVC_STREAM_TAG, "camera RESTART"); + } + + camera_config_t camera_config = { + .pin_pwdn = -1, // CAM_PIN_PWDN, + .pin_reset = -1, // CAM_PIN_RESET, + .pin_xclk = 10, // CAM_PIN_XCLK, + .pin_sccb_sda = 40, // CAM_PIN_SIOD, + .pin_sccb_scl = 39, // CAM_PIN_SIOC, + + .pin_d7 = 48, /// CAM_PIN_D7, + .pin_d6 = 11, /// CAM_PIN_D6, + .pin_d5 = 12, // CAM_PIN_D5, + .pin_d4 = 14, // CAM_PIN_D4, + .pin_d3 = 16, // CAM_PIN_D3, + .pin_d2 = 18, // CAM_PIN_D2, + .pin_d1 = 17, // CAM_PIN_D1, + .pin_d0 = 15, // CAM_PIN_D0, + .pin_vsync = 38, // CAM_PIN_VSYNC, + .pin_href = 47, // CAM_PIN_HREF, + .pin_pclk = 13, // CAM_PIN_PCLK, + + .xclk_freq_hz = xclk_freq_hz, + .ledc_timer = LEDC_TIMER_0, + .ledc_channel = LEDC_CHANNEL_0, + + .pixel_format = pixel_format, + .frame_size = frame_size, + + .jpeg_quality = jpeg_quality, + .fb_count = fb_count, + .fb_location = CAMERA_FB_IN_PSRAM, + .grab_mode = CAMERA_GRAB_WHEN_EMPTY, + }; + + // initialize the camera sensor + esp_err_t ret = esp_camera_init(&camera_config); + if (ret != ESP_OK) + { + return ret; + } + + // Get the sensor object, and then use some of its functions to adjust the parameters when taking a photo. + // Note: Do not call functions that set resolution, set picture format and PLL clock, + // If you need to reset the appeal parameters, please reinitialize the sensor. + sensor_t *s = esp_camera_sensor_get(); + s->set_vflip(s, 1); // flip it back + // initial sensors are flipped vertically and colors are a bit saturated + if (s->id.PID == OV3660_PID) + { + s->set_brightness(s, 1); // up the blightness just a bit + s->set_saturation(s, -2); // lower the saturation + } + + if (s->id.PID == OV3660_PID || s->id.PID == OV2640_PID) + { + s->set_vflip(s, 1); // flip it back + } + else if (s->id.PID == GC0308_PID) + { + s->set_hmirror(s, 0); + } + else if (s->id.PID == GC032A_PID) + { + s->set_vflip(s, 1); + } + + // Get the basic information of the sensor. + camera_sensor_info_t *s_info = esp_camera_sensor_get_info(&(s->id)); + + if (ESP_OK == ret && PIXFORMAT_JPEG == pixel_format && s_info->support_jpeg == true) + { + cur_xclk_freq_hz = xclk_freq_hz; + cur_pixel_format = pixel_format; + cur_frame_size = frame_size; + cur_jpeg_quality = jpeg_quality; + cur_fb_count = fb_count; + inited = true; + } + else + { + ESP_LOGE(UVC_STREAM_TAG, "JPEG format is not supported"); + return ESP_ERR_NOT_SUPPORTED; + } + + return ret; +} + +static esp_err_t UVCStreamHelpers::camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx) +{ + (void)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; + int jpeg_quality = 10; + + if (format != UVC_FORMAT_JPEG) + { + ESP_LOGE(UVC_STREAM_TAG, "Only support MJPEG format"); + return ESP_ERR_NOT_SUPPORTED; + } + + if (width == 240 && height == 240) + { + frame_size = FRAMESIZE_240X240; + jpeg_quality = 10; + } + else + { + ESP_LOGE(UVC_STREAM_TAG, "Unsupported frame size %dx%d", width, height); + return ESP_ERR_NOT_SUPPORTED; + } + + esp_err_t ret = camera_init(20000000, PIXFORMAT_JPEG, frame_size, jpeg_quality, 2); + if (ret != ESP_OK) + { + ESP_LOGE(UVC_STREAM_TAG, "camera init failed"); + return ret; + } + + return ESP_OK; +} + +static void UVCStreamHelpers::camera_stop_cb(void *cb_ctx) +{ + (void)cb_ctx; + if (s_fb.cam_fb_p) + { + esp_camera_fb_return(s_fb.cam_fb_p); + s_fb.cam_fb_p = nullptr; + } + + ESP_LOGI(UVC_STREAM_TAG, "Camera Stop"); +} + +static uvc_fb_t *UVCStreamHelpers::camera_fb_get_cb(void *cb_ctx) +{ + (void)cb_ctx; + s_fb.cam_fb_p = esp_camera_fb_get(); + + if (!s_fb.cam_fb_p) + { + return nullptr; + } + + s_fb.uvc_fb.buf = s_fb.cam_fb_p->buf; + s_fb.uvc_fb.len = s_fb.cam_fb_p->len; + s_fb.uvc_fb.width = s_fb.cam_fb_p->width; + s_fb.uvc_fb.height = s_fb.cam_fb_p->height; + s_fb.uvc_fb.format = UVC_FORMAT_JPEG; // we gotta make sure we're ALWAYS using JPEG + s_fb.uvc_fb.timestamp = s_fb.cam_fb_p->timestamp; + + if (s_fb.uvc_fb.len > UVC_MAX_FRAMESIZE_SIZE) + { + ESP_LOGE(UVC_STREAM_TAG, "Frame size %d is larger than max frame size %d", s_fb.uvc_fb.len, UVC_MAX_FRAMESIZE_SIZE); + esp_camera_fb_return(s_fb.cam_fb_p); + return nullptr; + } + + return &s_fb.uvc_fb; +} + +static void UVCStreamHelpers::camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx) +{ + (void)cb_ctx; + assert(fb == &s_fb.uvc_fb); + esp_camera_fb_return(s_fb.cam_fb_p); +} + +esp_err_t UVCStreamManager::setup() +{ + ESP_LOGI(UVC_STREAM_TAG, "Setting up UVC Stream"); + + uvc_buffer = (uint8_t *)malloc(UVC_MAX_FRAMESIZE_SIZE); + if (uvc_buffer == nullptr) + { + ESP_LOGE(UVC_STREAM_TAG, "Allocating buffer for UVC Device failed"); + return ESP_FAIL; + } + + uvc_device_config_t config = { + .uvc_buffer = uvc_buffer, + .uvc_buffer_size = UVC_MAX_FRAMESIZE_SIZE, + .start_cb = UVCStreamHelpers::camera_start_cb, + .fb_get_cb = UVCStreamHelpers::camera_fb_get_cb, + .fb_return_cb = UVCStreamHelpers::camera_fb_return_cb, + .stop_cb = UVCStreamHelpers::camera_stop_cb, + }; + + esp_err_t ret = uvc_device_config(0, &config); + if (ret != ESP_OK) + { + ESP_LOGE(UVC_STREAM_TAG, "Configuring UVC Device failed: %s", esp_err_to_name(ret)); + return ret; + } + else + { + ESP_LOGI(UVC_STREAM_TAG, "Configured UVC Device"); + } + + ESP_LOGI(UVC_STREAM_TAG, "Initializing UVC Device"); + ret = uvc_device_init(); + if (ret != ESP_OK) + { + ESP_LOGE(UVC_STREAM_TAG, "Initializing UVC Device failed: %s", esp_err_to_name(ret)); + return ret; + } + else + { + ESP_LOGI(UVC_STREAM_TAG, "Initialized UVC Device"); + } + + return ESP_OK; +} \ No newline at end of file diff --git a/components/UVCStream/UVCStream/UVCStream.hpp b/components/UVCStream/UVCStream/UVCStream.hpp new file mode 100644 index 0000000..2a8dda2 --- /dev/null +++ b/components/UVCStream/UVCStream/UVCStream.hpp @@ -0,0 +1,37 @@ +#pragma once +#ifndef UVCSTREAM_HPP +#define UVCSTREAM_HPP +#include "esp_timer.h" +#include "esp_camera.h" +#include "esp_log.h" +#include "usb_device_uvc.h" + +namespace UVCStreamHelpers +{ + // TODO move the camera handling code to the camera manager and have the uvc maanger initialize it in wired mode + + typedef struct + { + camera_fb_t *cam_fb_p; + uvc_fb_t uvc_fb; + } fb_t; + + static fb_t s_fb; + + static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx); + static void camera_stop_cb(void *cb_ctx); + static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx); + static uvc_fb_t *camera_fb_get_cb(void *cb_ctx); + static void camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx); +} + +class UVCStreamManager +{ +private: + uint8_t *uvc_buffer; + +public: + esp_err_t setup(); +}; + +#endif // UVCSTREAM_HPP diff --git a/components/WebSocketLogger/CMakeLists.txt b/components/WebSocketLogger/CMakeLists.txt new file mode 100644 index 0000000..d887b90 --- /dev/null +++ b/components/WebSocketLogger/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "WebSocketLogger/WebSocketLogger.cpp" + INCLUDE_DIRS "WebSocketLogger" + REQUIRES esp_http_server +) \ No newline at end of file diff --git a/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.cpp b/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.cpp new file mode 100644 index 0000000..75f4f20 --- /dev/null +++ b/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.cpp @@ -0,0 +1,71 @@ +#include "WebSocketLogger.hpp" + +WebSocketLogger::WebSocketLogger() +{ + this->connected_socket_client = async_resp_arg{ + .hd = nullptr, + .fd = -1, + }; + + this->ws_log_buffer[0] = '\0'; +} + +void LoggerHelpers::ws_async_send(void *arg) +{ + char *log_buffer = webSocketLogger.get_websocket_log_buffer(); + + struct async_resp_arg *resp_arg = (struct async_resp_arg *)arg; + httpd_handle_t hd = resp_arg->hd; + int fd = resp_arg->fd; + + httpd_ws_frame_t websocket_packet = httpd_ws_frame_t{}; + + websocket_packet.payload = (uint8_t *)log_buffer; + websocket_packet.len = strlen(log_buffer); + websocket_packet.type = HTTPD_WS_TYPE_TEXT; + + httpd_ws_send_frame_async(hd, fd, &websocket_packet); +} + +esp_err_t WebSocketLogger::log_message(const char *format, va_list args) +{ + vsnprintf(this->ws_log_buffer, 100, format, args); + + if (connected_socket_client.fd == -1 || connected_socket_client.hd == nullptr) + { + return ESP_FAIL; + } + + esp_err_t ret = httpd_queue_work(connected_socket_client.hd, LoggerHelpers::ws_async_send, &connected_socket_client); + if (ret != ESP_OK) + { + connected_socket_client.fd = -1; + connected_socket_client.hd = nullptr; + } + + return ret; +} + +esp_err_t WebSocketLogger::register_socket_client(httpd_req_t *req) +{ + if (connected_socket_client.fd != -1 && connected_socket_client.hd != nullptr) + { + // we're already connected + return ESP_OK; + } + + connected_socket_client.hd = req->handle; + connected_socket_client.fd = httpd_req_to_sockfd(req); + return ESP_OK; +} + +void WebSocketLogger::unregister_socket_client() +{ + connected_socket_client.fd = -1; + connected_socket_client.hd = nullptr; +} + +char *WebSocketLogger::get_websocket_log_buffer() +{ + return this->ws_log_buffer; +} \ No newline at end of file diff --git a/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.hpp b/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.hpp new file mode 100644 index 0000000..7c783cd --- /dev/null +++ b/components/WebSocketLogger/WebSocketLogger/WebSocketLogger.hpp @@ -0,0 +1,39 @@ +#pragma once +#ifndef WEBSOCKETLOGGER_HPP +#define WEBSOCKETLOGGER_HPP + +#include "esp_http_server.h" +#include "esp_log.h" + +#define WS_LOG_BUFFER_LEN 1024 + +struct async_resp_arg +{ + httpd_handle_t hd; + int fd; +}; + +namespace LoggerHelpers +{ + void ws_async_send(void *arg); +} + +class WebSocketLogger +{ +private: + async_resp_arg connected_socket_client; + char ws_log_buffer[WS_LOG_BUFFER_LEN]; + +public: + WebSocketLogger(); + + esp_err_t log_message(const char *format, va_list args); + esp_err_t register_socket_client(httpd_req_t *req); + void unregister_socket_client(); + bool is_client_connected(); + char *get_websocket_log_buffer(); +}; + +extern WebSocketLogger webSocketLogger; + +#endif \ No newline at end of file diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index 9025239..ca91ff6 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -6,7 +6,6 @@ #include "esp_log.h" #include "sdkconfig.h" #include "esp_camera.h" -#include "esp_log.h" #include "nvs_flash.h" #include "esp_psram.h" @@ -16,12 +15,40 @@ #include #include #include +#include #include +#include + +#ifdef CONFIG_WIRED_MODE +#include +#endif + #define BLINK_GPIO (gpio_num_t) CONFIG_BLINK_GPIO static const char *TAG = "[MAIN]"; +WebSocketLogger webSocketLogger; + +// TODO add this option +// ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME); +ProjectConfig deviceConfig("openiris", "openiristracker"); +WiFiManager wifiManager; +MDNSManager mdnsManager(deviceConfig); +CameraManager cameraHandler(deviceConfig); +StreamServer streamServer(80); + +#ifdef CONFIG_WIRED_MODE +UVCStreamManager uvcStream; +#endif + +#ifdef CONFIG_USE_ILLUMNATIOR_PIN +// LEDManager ledManager(BLINK_GPIO, ILLUMINATOR_PIN); +LEDManager ledManager(BLINK_GPIO, 1); +#else +LEDManager ledManager(BLINK_GPIO); +#endif + static void initNVSStorage() { esp_err_t ret = nvs_flash_init(); @@ -33,8 +60,22 @@ static void initNVSStorage() ESP_ERROR_CHECK(ret); } +int test_log(const char *format, va_list args) +{ + webSocketLogger.log_message(format, args); + return vprintf(format, args); +} + +void mylog(const char *format, ...); + extern "C" void app_main(void) { + // uvc plan + + // cleanup the logs - done + // prepare the camera to be initialized with UVC + // debug uvc performance - done + // porting plan: // port the wifi manager first. - worky!!! // get it connect to the network and setup an AP with hardcoded creds first -- connects. AP will be next @@ -52,35 +93,24 @@ extern "C" void app_main(void) // then port the Elegant OTA stuff // then port the serial manager - // TODO add this option - // ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME); - ProjectConfig deviceConfig("openiris", "openiristracker"); - WiFiManager wifiManager; - MDNSManager mdnsManager(deviceConfig); - CameraManager cameraHandler(deviceConfig); - StreamServer streamServer; - -#ifdef CONFIG_USE_ILLUMNATIOR_PIN - // LEDManager ledManager(BLINK_GPIO, ILLUMINATOR_PIN); - LEDManager ledManager(BLINK_GPIO, 1); -#else - LEDManager ledManager(BLINK_GPIO); -#endif - Logo::printASCII(); initNVSStorage(); + esp_log_set_vprintf(&test_log); ledManager.setup(); deviceConfig.load(); wifiManager.Begin(); mdnsManager.start(); - cameraHandler.setupCamera(); + // cameraHandler.setupCamera(); streamServer.startStreamServer(); +#ifdef CONFIG_WIRED_MODE + uvcStream.setup(); +#endif + while (1) { ledManager.handleLED(); - vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS); } } diff --git a/sdkconfig b/sdkconfig index dfa2005..85f8619 100644 --- a/sdkconfig +++ b/sdkconfig @@ -539,7 +539,7 @@ CONFIG_BLINK_LED_GPIO=y # CONFIG_BLINK_LED_STRIP is not set CONFIG_BLINK_GPIO=21 CONFIG_BLINK_PERIOD=1000 -# CONFIG_WIRED_MODE is not set +CONFIG_WIRED_MODE=y CONFIG_WIFI_SSID="UPC7878684" CONFIG_WIFI_PASSOWRD="j3ttQPpfvhep" # end of OpenIris basic configuration @@ -882,7 +882,7 @@ CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 # CONFIG_HTTPD_LOG_PURGE_DATA is not set -# CONFIG_HTTPD_WS_SUPPORT is not set +CONFIG_HTTPD_WS_SUPPORT=y # CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set # end of HTTP Server @@ -2142,17 +2142,17 @@ CONFIG_TUSB_SERIAL_NUM="12345678" CONFIG_FORMAT_MJPEG_CAM1=y # CONFIG_FORMAT_H264_CAM1 is not set # CONFIG_FORMAT_UNCOMPR_CAM1 is not set -CONFIG_UVC_MODE_ISOC_CAM1=y -# CONFIG_UVC_MODE_BULK_CAM1 is not set +# CONFIG_UVC_MODE_ISOC_CAM1 is not set +CONFIG_UVC_MODE_BULK_CAM1=y CONFIG_FRAMESIZE_QVGA=y # CONFIG_FRAMESIZE_HVGA is not set # CONFIG_FRAMESIZE_VGA is not set # CONFIG_FRAMESIZE_SVGA is not set # CONFIG_FRAMESIZE_HD is not set # CONFIG_FRAMESIZE_FHD is not set -CONFIG_UVC_CAM1_FRAMERATE=70 -CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=1280 -CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=720 +CONFIG_UVC_CAM1_FRAMERATE=60 +CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=240 +CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=240 CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y # end of USB Cam1 Config @@ -2163,34 +2163,34 @@ CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y # # FRAME_SIZE_1 # -CONFIG_UVC_MULTI_FRAME_WIDTH_1=640 -CONFIG_UVC_MULTI_FRAME_HEIGHT_1=480 -CONFIG_UVC_MULTI_FRAME_FPS_1=15 +CONFIG_UVC_MULTI_FRAME_WIDTH_1=240 +CONFIG_UVC_MULTI_FRAME_HEIGHT_1=240 +CONFIG_UVC_MULTI_FRAME_FPS_1=60 # end of FRAME_SIZE_1 # # FRAME_SIZE_2 # -CONFIG_UVC_MULTI_FRAME_WIDTH_2=480 -CONFIG_UVC_MULTI_FRAME_HEIGHT_2=320 -CONFIG_UVC_MULTI_FRAME_FPS_2=30 +CONFIG_UVC_MULTI_FRAME_WIDTH_2=240 +CONFIG_UVC_MULTI_FRAME_HEIGHT_2=240 +CONFIG_UVC_MULTI_FRAME_FPS_2=60 # end of FRAME_SIZE_2 # # FRAME_SIZE_3 # -CONFIG_UVC_MULTI_FRAME_WIDTH_3=320 +CONFIG_UVC_MULTI_FRAME_WIDTH_3=240 CONFIG_UVC_MULTI_FRAME_HEIGHT_3=240 -CONFIG_UVC_MULTI_FRAME_FPS_3=30 +CONFIG_UVC_MULTI_FRAME_FPS_3=60 # end of FRAME_SIZE_3 # end of UVC_MULTI_FRAME_CONFIG # # UVC Task Config # -CONFIG_UVC_TINYUSB_TASK_PRIORITY=5 +CONFIG_UVC_TINYUSB_TASK_PRIORITY=4 CONFIG_UVC_TINYUSB_TASK_CORE=-1 -CONFIG_UVC_CAM1_TASK_PRIORITY=4 +CONFIG_UVC_CAM1_TASK_PRIORITY=3 CONFIG_UVC_CAM1_TASK_CORE=-1 # end of UVC Task Config # end of USB Device UVC diff --git a/sdkconfig.old b/sdkconfig.old index d52c63c..7f75294 100644 --- a/sdkconfig.old +++ b/sdkconfig.old @@ -539,7 +539,7 @@ CONFIG_BLINK_LED_GPIO=y # CONFIG_BLINK_LED_STRIP is not set CONFIG_BLINK_GPIO=21 CONFIG_BLINK_PERIOD=1000 -# CONFIG_WIRED_MODE is not set +CONFIG_WIRED_MODE=y CONFIG_WIFI_SSID="UPC7878684" CONFIG_WIFI_PASSOWRD="j3ttQPpfvhep" # end of OpenIris basic configuration @@ -882,7 +882,7 @@ CONFIG_HTTPD_MAX_URI_LEN=512 CONFIG_HTTPD_ERR_RESP_NO_DELAY=y CONFIG_HTTPD_PURGE_BUF_LEN=32 # CONFIG_HTTPD_LOG_PURGE_DATA is not set -# CONFIG_HTTPD_WS_SUPPORT is not set +CONFIG_HTTPD_WS_SUPPORT=y # CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set # end of HTTP Server @@ -1433,15 +1433,13 @@ CONFIG_HEAP_TRACING_OFF=y # Log output # # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set -# CONFIG_LOG_DEFAULT_LEVEL_INFO is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set -CONFIG_LOG_DEFAULT_LEVEL=1 +CONFIG_LOG_DEFAULT_LEVEL=3 # CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT is not set -# CONFIG_LOG_MAXIMUM_LEVEL_WARN is not set -# CONFIG_LOG_MAXIMUM_LEVEL_INFO is not set # CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE=y CONFIG_LOG_MAXIMUM_LEVEL=5 @@ -2152,9 +2150,9 @@ CONFIG_FRAMESIZE_QVGA=y # CONFIG_FRAMESIZE_SVGA is not set # CONFIG_FRAMESIZE_HD is not set # CONFIG_FRAMESIZE_FHD is not set -CONFIG_UVC_CAM1_FRAMERATE=70 -CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=1280 -CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=720 +CONFIG_UVC_CAM1_FRAMERATE=60 +CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=240 +CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=240 CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y # end of USB Cam1 Config @@ -2165,25 +2163,25 @@ CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y # # FRAME_SIZE_1 # -CONFIG_UVC_MULTI_FRAME_WIDTH_1=640 -CONFIG_UVC_MULTI_FRAME_HEIGHT_1=480 -CONFIG_UVC_MULTI_FRAME_FPS_1=15 +CONFIG_UVC_MULTI_FRAME_WIDTH_1=240 +CONFIG_UVC_MULTI_FRAME_HEIGHT_1=240 +CONFIG_UVC_MULTI_FRAME_FPS_1=60 # end of FRAME_SIZE_1 # # FRAME_SIZE_2 # -CONFIG_UVC_MULTI_FRAME_WIDTH_2=480 -CONFIG_UVC_MULTI_FRAME_HEIGHT_2=320 -CONFIG_UVC_MULTI_FRAME_FPS_2=30 +CONFIG_UVC_MULTI_FRAME_WIDTH_2=240 +CONFIG_UVC_MULTI_FRAME_HEIGHT_2=240 +CONFIG_UVC_MULTI_FRAME_FPS_2=60 # end of FRAME_SIZE_2 # # FRAME_SIZE_3 # -CONFIG_UVC_MULTI_FRAME_WIDTH_3=320 +CONFIG_UVC_MULTI_FRAME_WIDTH_3=240 CONFIG_UVC_MULTI_FRAME_HEIGHT_3=240 -CONFIG_UVC_MULTI_FRAME_FPS_3=30 +CONFIG_UVC_MULTI_FRAME_FPS_3=60 # end of FRAME_SIZE_3 # end of UVC_MULTI_FRAME_CONFIG