From 4a72f3d78c3cad4ba07b23f0801a6163acce6c9d Mon Sep 17 00:00:00 2001 From: Li Junru Date: Wed, 14 Aug 2024 11:57:29 +0800 Subject: [PATCH 1/9] feat(button_keyboard): publish release version --- components/keyboard_button/CHANGELOG.md | 4 ++++ components/keyboard_button/idf_component.yml | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/components/keyboard_button/CHANGELOG.md b/components/keyboard_button/CHANGELOG.md index 80079326b..f9baa7532 100644 --- a/components/keyboard_button/CHANGELOG.md +++ b/components/keyboard_button/CHANGELOG.md @@ -1,5 +1,9 @@ # ChangeLog +## v1.0.0 - 2024-8-14 + +* Release the official version. + ## v0.1.1 - 2024-6-3 ### Bug Fixes: diff --git a/components/keyboard_button/idf_component.yml b/components/keyboard_button/idf_component.yml index 5be9bd275..71101c47d 100644 --- a/components/keyboard_button/idf_component.yml +++ b/components/keyboard_button/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.1.1" +version: "1.0.0" description: Keyboard button driver url: https://github.com/espressif/esp-iot-solution/tree/master/components/keyboard_button repository: https://github.com/espressif/esp-iot-solution.git @@ -7,3 +7,5 @@ issues: https://github.com/espressif/esp-iot-solution/issues dependencies: idf: ">=5.0" cmake_utilities: "0.*" +examples: + - path: ../../examples/keyboard From a6daa19f2ab9eb91885f07c9cc0418d6901094f3 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Fri, 25 Oct 2024 15:16:22 +0800 Subject: [PATCH 2/9] fix(led_indicator): make set brightness can set index Close https://github.com/espressif/esp-iot-solution/issues/414 --- .../audio/pwm_audio/test/pwm_audio_test.c | 62 ++++++++- .../test_apps/main/avi_player_test.c | 120 ++++++++++++++++++ components/led/led_indicator/CHANGELOG.md | 5 +- .../led/led_indicator/src/led_indicator.c | 6 +- 4 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 components/avi_player/test_apps/main/avi_player_test.c diff --git a/components/audio/pwm_audio/test/pwm_audio_test.c b/components/audio/pwm_audio/test/pwm_audio_test.c index f36f3b157..50e345bc4 100644 --- a/components/audio/pwm_audio/test/pwm_audio_test.c +++ b/components/audio/pwm_audio/test/pwm_audio_test.c @@ -1,17 +1,24 @@ -/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include +#include "inttypes.h" #include "unity.h" -#include "test_utils.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" #include "esp_log.h" +#include "esp_idf_version.h" #include "math.h" #include "pwm_audio.h" static const char *TAG = "pwm_audio test"; +// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + #define LEFT_CHANNEL_GPIO 26 #define RIGHT_CHANNEL_GPIO 25 @@ -39,8 +46,10 @@ TEST_CASE("pwm audio sine wave test", "[audio][iot]") pac.gpio_num_right = RIGHT_CHANNEL_GPIO; pac.ledc_channel_right = LEDC_CHANNEL_1; pac.ledc_timer_sel = LEDC_TIMER_0; +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) pac.tg_num = TIMER_GROUP_0; pac.timer_num = TIMER_0; +#endif pac.ringbuf_len = 1024 * 8; TEST_ASSERT(ESP_OK == pwm_audio_init(&pac)); pwm_audio_set_param(48000, 8, 2); @@ -76,7 +85,7 @@ static void play_audio(const uint8_t *data, size_t wave_size, uint32_t rate, uin uint32_t index = 0; size_t cnt; uint32_t block_w = 2048; - ESP_LOGI(TAG, "parameter: samplerate:%d, bits:%d, channel:%d", rate, bits, ch); + ESP_LOGI(TAG, "parameter: samplerate:%"PRIu32", bits:%"PRIu32", channel:%"PRIu32"", rate, bits, ch); pwm_audio_set_param(rate, bits, ch); pwm_audio_start(); @@ -86,7 +95,7 @@ static void play_audio(const uint8_t *data, size_t wave_size, uint32_t rate, uin block_w = wave_size - index; } pwm_audio_write((uint8_t*)data + index, block_w, &cnt, 1000 / portTICK_PERIOD_MS); - ESP_LOGD(TAG, "write [%d] [%d]", block_w, cnt); + ESP_LOGD(TAG, "write [%"PRIu32"] [%d]", block_w, cnt); index += cnt; } else { ESP_LOGI(TAG, "play completed"); @@ -111,8 +120,10 @@ static void play_with_param(ledc_timer_bit_t duty_resolution, uint8_t hw_ch) } pac.ledc_channel_right = LEDC_CHANNEL_1; pac.ledc_timer_sel = LEDC_TIMER_0; +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) pac.tg_num = TIMER_GROUP_0; pac.timer_num = TIMER_0; +#endif pac.ringbuf_len = 1024 * 8; TEST_ASSERT(ESP_OK == pwm_audio_init(&pac)); @@ -141,3 +152,46 @@ TEST_CASE("pwm audio play music test", "[audio][iot]") play_with_param(LEDC_TIMER_9_BIT, 2); play_with_param(LEDC_TIMER_10_BIT, 2); } + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + /* + * _______ ____ __ _ _ _____ _____ ____ _______ ______ _____ _______ + * | __ \ \ / / \/ | /\ | | | | __ \_ _/ __ \ |__ __| ____|/ ____|__ __| + * | |__) \ \ /\ / /| \ / | / \ | | | | | | || || | | | | | | |__ | (___ | | + * | ___/ \ \/ \/ / | |\/| | / /\ \| | | | | | || || | | | | | | __| \___ \ | | + * | | \ /\ / | | | | / ____ \ |__| | |__| || || |__| | | | | |____ ____) | | | + * |_| \/ \/ |_| |_| /_/ \_\____/|_____/_____\____/ |_| |______|_____/ |_| + */ + printf(" _______ ____ __ _ _ _____ _____ ____ _______ ______ _____ _______ \n"); + printf(" | __ \\ \\ / / \\/ | /\\ | | | | __ \\_ _/ __ \\ |__ __| ____|/ ____|__ __|\n"); + printf(" | |__) \\ \\ /\\ / /| \\ / | / \\ | | | | | | || || | | | | | | |__ | (___ | | \n"); + printf(" | ___/ \\ \\/ \\/ / | |\\/| | / /\\ \\| | | | | | || || | | | | | | __| \\___ \\ | | \n"); + printf(" | | \\ /\\ / | | | | / ____ \\ |__| | |__| || || |__| | | | | |____ ____) | | | \n"); + printf(" |_| \\/ \\/ |_| |_| /_/ \\_\\____/|_____/_____/____/ |_| |______|_____/ |_| \n"); + unity_run_menu(); +} diff --git a/components/avi_player/test_apps/main/avi_player_test.c b/components/avi_player/test_apps/main/avi_player_test.c new file mode 100644 index 000000000..15d7abfa1 --- /dev/null +++ b/components/avi_player/test_apps/main/avi_player_test.c @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_spiffs.h" +#include "avi_player.h" + +static const char *TAG = "avi_player_test"; + +// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +static bool end_play = false; + +void video_write(frame_data_t *data, void *arg) +{ + TEST_ASSERT_TRUE(data->type == FRAME_TYPE_VIDEO); + ESP_LOGI(TAG, "Video write: %d", data->data_bytes); +} + +void audio_write(frame_data_t *data, void *arg) +{ + TEST_ASSERT_TRUE(data->type == FRAME_TYPE_AUDIO); + ESP_LOGI(TAG, "Audio write: %d", data->data_bytes); +} + +void audio_set_clock(uint32_t rate, uint32_t bits_cfg, uint32_t ch, void *arg) +{ + ESP_LOGI(TAG, "Audio set clock, rate %"PRIu32", bits %"PRIu32", ch %"PRIu32"", rate, bits_cfg, ch); +} + +void avi_play_end(void *arg) +{ + ESP_LOGI(TAG, "Play end"); + end_play = true; +} + +TEST_CASE("avi_player_test", "[avi_player]") +{ + end_play = false; + avi_player_config_t config = { + .buffer_size = 60 * 1024, + .audio_cb = audio_write, + .video_cb = video_write, + .audio_set_clock_cb = audio_set_clock, + .avi_play_end_cb = avi_play_end, + }; + + avi_player_init(config); + + avi_player_play_from_file("/spiffs/p4_introduce.avi"); + + while (!end_play) { + vTaskDelay(500 / portTICK_PERIOD_MS); + } + avi_player_deinit(); + vTaskDelay(500 / portTICK_PERIOD_MS); +} + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static void check_leak(size_t before_free, size_t after_free, const char *type) +{ + ssize_t delta = after_free - before_free; + printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); + TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); +} + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT"); + check_leak(before_free_32bit, after_free_32bit, "32BIT"); +} + +void app_main(void) +{ + /* ___ _ _ _____ ______ _ _____ _____________ + * / _ \| | | |_ _| | ___ \ | / _ \ \ / / ___| ___ \ + * / /_\ \ | | | | | | |_/ / | / /_\ \ V /| |__ | |_/ / + * | _ | | | | | | | __/| | | _ |\ / | __|| / + * | | | \ \_/ /_| |_ | | | |____| | | || | | |___| |\ \ + * \_| |_/\___/ \___/ \_| \_____/\_| |_/\_/ \____/\_| \_| + */ + printf(" ___ _ _ _____ ______ _ _____ _____________ \n"); + printf(" / _ \\| | | |_ _| | ___ \\ | / _ \\ \\ / / ___| ___ \\\n"); + printf("/ /_\\ \\ | | | | | | |_/ / | / /_\\ \\ V /| |__ | |_/ /\n"); + printf("| _ | | | | | | | __/| | | _ |\\ / | __|| / \n"); + printf("| | | \\ \\_/ /_| |_ | | | |____| | | || | | |___| |\\ \\ \n"); + printf("\\_| |_/\\___/ \\___/ \\_| \\_____/\\_| |_/\\_/ \\____/\\_| \\_|\n"); + + esp_vfs_spiffs_conf_t conf = { + .base_path = "/spiffs", + .partition_label = "avi", + .max_files = 5, // This decides the maximum number of files that can be created on the storage + .format_if_mount_failed = false + }; + + ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf)); + + unity_run_menu(); +} diff --git a/components/led/led_indicator/CHANGELOG.md b/components/led/led_indicator/CHANGELOG.md index e81e621dc..4d9857156 100644 --- a/components/led/led_indicator/CHANGELOG.md +++ b/components/led/led_indicator/CHANGELOG.md @@ -1,6 +1,9 @@ - # ChangeLog +## v0.9.4 - 2024-10-28 + +* make set brightness can set index. [!414](https://github.com/espressif/esp-iot-solution/issues/414) + ## v0.9.3 - 2024-6-20 * Setting light brightness perform gamma correction. diff --git a/components/led/led_indicator/src/led_indicator.c b/components/led/led_indicator/src/led_indicator.c index 7f80d3aee..99368bda1 100644 --- a/components/led/led_indicator/src/led_indicator.c +++ b/components/led/led_indicator/src/led_indicator.c @@ -421,7 +421,7 @@ static void _blink_list_runner(TimerHandle_t xTimer) p_led_indicator->current_fade_value.v = p_blink_step_value.v; p_led_indicator->current_fade_value.i = p_blink_step_value.i; - p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, led_indicator_get_gamma_value(p_blink_step_value.v)); + p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, _ihsv_convert_to_gamma(p_led_indicator->current_fade_value.value)); p_led_indicator->last_fade_value = p_led_indicator->current_fade_value; if (p_blink_step->hold_time_ms == 0) { break; @@ -448,7 +448,7 @@ static void _blink_list_runner(TimerHandle_t xTimer) // diff_brightness < 0: Fade decrease if (p_blink_step->hold_time_ms == 0) { p_led_indicator->current_fade_value.v = brightness_value; - p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, led_indicator_get_gamma_value(brightness_value)); + p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, _ihsv_convert_to_gamma(p_led_indicator->current_fade_value.value)); p_led_indicator->last_fade_value = p_led_indicator->current_fade_value; p_led_indicator->p_blink_steps[active_blink] += 1; break; @@ -460,7 +460,7 @@ static void _blink_list_runner(TimerHandle_t xTimer) brightness_value = p_led_indicator->last_fade_value.v - p_led_indicator->fade_value_count; } p_led_indicator->current_fade_value.v = brightness_value; - p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, led_indicator_get_gamma_value(brightness_value)); + p_led_indicator->hal_indicator_set_brightness(p_led_indicator->hardware_data, _ihsv_convert_to_gamma(p_led_indicator->current_fade_value.value)); if (diff_value == 0) { ticks = p_blink_step->hold_time_ms; From a0b0e4f2587d2572f857e800c0f0044ae6a1fa07 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Wed, 14 Aug 2024 14:07:57 +0800 Subject: [PATCH 3/9] feat(led_indicator): publish official version --- components/led/led_indicator/CHANGELOG.md | 4 ++++ components/led/led_indicator/idf_component.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/led/led_indicator/CHANGELOG.md b/components/led/led_indicator/CHANGELOG.md index 4d9857156..855e15c39 100644 --- a/components/led/led_indicator/CHANGELOG.md +++ b/components/led/led_indicator/CHANGELOG.md @@ -1,5 +1,9 @@ # ChangeLog +## v1.0.0 - 2024-8-14 + +* Publish the official version + ## v0.9.4 - 2024-10-28 * make set brightness can set index. [!414](https://github.com/espressif/esp-iot-solution/issues/414) diff --git a/components/led/led_indicator/idf_component.yml b/components/led/led_indicator/idf_component.yml index d9c5bad77..b1a08c8f4 100644 --- a/components/led/led_indicator/idf_component.yml +++ b/components/led/led_indicator/idf_component.yml @@ -1,4 +1,4 @@ -version: "0.9.3" +version: "1.0.0" description: LED indicator driver url: https://github.com/espressif/esp-iot-solution/tree/master/components/led/led_indicator dependencies: From 05c5fe0bac370757a0aa8aa9f17ddd21499a2911 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Thu, 15 Aug 2024 14:33:47 +0800 Subject: [PATCH 4/9] feat(device_uvc): add components test apps --- .gitlab/ci/build.yml | 9 +- .gitlab/ci/rules.yml | 11 ++ components/.build-rules.yml | 4 + components/usb/usb_device_uvc/CHANGELOG.md | 3 + .../usb/usb_device_uvc/idf_component.yml | 2 +- .../usb_device_uvc/test_apps/CMakeLists.txt | 9 ++ .../test_apps/main/CMakeLists.txt | 3 + .../test_apps/main/esp_1280_720.jpg | Bin 0 -> 28338 bytes .../test_apps/main/usb_device_uvc_test.c | 125 ++++++++++++++++++ .../test_apps/sdkconfig.defaults | 7 + 10 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 components/usb/usb_device_uvc/test_apps/CMakeLists.txt create mode 100644 components/usb/usb_device_uvc/test_apps/main/CMakeLists.txt create mode 100644 components/usb/usb_device_uvc/test_apps/main/esp_1280_720.jpg create mode 100644 components/usb/usb_device_uvc/test_apps/main/usb_device_uvc_test.c create mode 100644 components/usb/usb_device_uvc/test_apps/sdkconfig.defaults diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index 3c627868f..6c0f2d780 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -1242,11 +1242,18 @@ build_components_usb_usb_device_uac: - .rules:build:components_usb_usb_device_uac parallel: matrix: - - IMAGE: espressif/idf:release-v4.4 - IMAGE: espressif/idf:release-v5.0 variables: EXAMPLE_DIR: components/usb/usb_device_uac +build_components_usb_usb_device_uvc: + extends: + - .build_examples_template + - .rules:build:components_usb_usb_device_uvc + - .build_idf_active_release_version + variables: + EXAMPLE_DIR: components/usb/usb_device_uvc + build_components_led_lightbulb_driver_test_apps: extends: - .build_examples_template diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 34fc45461..c72868cd5 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -2101,6 +2101,17 @@ - <<: *if-dev-push changes: *patterns-components_usb_usb_device_uac +.rules:build:components_usb_usb_device_uvc: + rules: + - <<: *if-protected + - <<: *if-label-build + - <<: *if-label-target_test + - <<: *if-trigger-job + - <<: *if-dev-push + changes: *patterns-build_system + - <<: *if-dev-push + changes: *patterns-components_usb_usb_device_uvc + .rules:build:components_sensors_humiture_aht20_test_apps: rules: - <<: *if-protected diff --git a/components/.build-rules.yml b/components/.build-rules.yml index 8180f4b32..347d9d203 100644 --- a/components/.build-rules.yml +++ b/components/.build-rules.yml @@ -146,6 +146,10 @@ components/usb/usb_device_uac/test_apps: enable: - if: SOC_USB_OTG_SUPPORTED == 1 and IDF_VERSION_MAJOR >= 5 +components/usb/usb_device_uvc/test_apps: + enable: + - if: SOC_USB_OTG_SUPPORTED == 1 and IDF_VERSION_MAJOR >= 5 + components/sensors/power_monitor/ina236/test_apps: enable: - if: INCLUDE_DEFAULT == 1 diff --git a/components/usb/usb_device_uvc/CHANGELOG.md b/components/usb/usb_device_uvc/CHANGELOG.md index 09ad68178..a64ba2e41 100644 --- a/components/usb/usb_device_uvc/CHANGELOG.md +++ b/components/usb/usb_device_uvc/CHANGELOG.md @@ -1,4 +1,7 @@ # ChangeLog +## v1.1.2 2024-10-25 + +* Add test-apps ## v1.1.1 2024-10-17 diff --git a/components/usb/usb_device_uvc/idf_component.yml b/components/usb/usb_device_uvc/idf_component.yml index 48e10bd8e..b995aa500 100644 --- a/components/usb/usb_device_uvc/idf_component.yml +++ b/components/usb/usb_device_uvc/idf_component.yml @@ -1,4 +1,4 @@ -version: "1.1.1" +version: "1.1.2" targets: - esp32s2 - esp32s3 diff --git a/components/usb/usb_device_uvc/test_apps/CMakeLists.txt b/components/usb/usb_device_uvc/test_apps/CMakeLists.txt new file mode 100644 index 000000000..43f446eb8 --- /dev/null +++ b/components/usb/usb_device_uvc/test_apps/CMakeLists.txt @@ -0,0 +1,9 @@ + +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components" + "../../usb_device_uvc/") +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(usb_device_uvc_test) diff --git a/components/usb/usb_device_uvc/test_apps/main/CMakeLists.txt b/components/usb/usb_device_uvc/test_apps/main/CMakeLists.txt new file mode 100644 index 000000000..5e95fc853 --- /dev/null +++ b/components/usb/usb_device_uvc/test_apps/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + EMBED_FILES "esp_1280_720.jpg") diff --git a/components/usb/usb_device_uvc/test_apps/main/esp_1280_720.jpg b/components/usb/usb_device_uvc/test_apps/main/esp_1280_720.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef05e4476d24439de51892aeaeba852928c62d09 GIT binary patch literal 28338 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�{!2BkQMDN;@K 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 M{BM~7m!0wd07P9@&Hw-a literal 0 HcmV?d00001 diff --git a/components/usb/usb_device_uvc/test_apps/main/usb_device_uvc_test.c b/components/usb/usb_device_uvc/test_apps/main/usb_device_uvc_test.c new file mode 100644 index 000000000..aaa77df46 --- /dev/null +++ b/components/usb/usb_device_uvc/test_apps/main/usb_device_uvc_test.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "usb_device_uvc.h" +#include "esp_timer.h" + +static const char *TAG = "usb_device_uvc_test"; + +// Some resources are lazy allocated in GPTimer driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) +#define UVC_MAX_FRAMESIZE_SIZE (60*1024) +#define WIDTH CONFIG_UVC_CAM1_FRAMESIZE_WIDTH +#define HEIGHT CONFIG_UVC_CAM1_FRAMESIZE_HEIGT + +extern const unsigned char jpg_start[] asm("_binary_esp_1280_720_jpg_start"); +extern const unsigned char jpg_end[] asm("_binary_esp_1280_720_jpg_end"); + +static uvc_fb_t s_fb; + +static void camera_stop_cb(void *cb_ctx) +{ + ESP_LOGI(TAG, "camera stop"); +} + +static esp_err_t camera_start_cb(uvc_format_t format, int width, int height, int rate, void *cb_ctx) +{ + (void)cb_ctx; + ESP_LOGI(TAG, "Camera: Start"); + ESP_LOGI(TAG, "Format: %d, width: %d, height: %d, rate: %d", format, width, height, rate); + + return ESP_OK; +} + +static void camera_fb_return_cb(uvc_fb_t *fb, void *cb_ctx) +{ + (void)cb_ctx; + assert(fb == &s_fb); +} + +static uvc_fb_t* camera_fb_get_cb(void *cb_ctx) +{ + (void)cb_ctx; + uint64_t us = (uint64_t)esp_timer_get_time(); + s_fb.buf = (uint8_t *)jpg_start; + s_fb.len = jpg_end - jpg_start; + s_fb.width = WIDTH; + s_fb.height = HEIGHT; + s_fb.format = UVC_FORMAT_JPEG; + s_fb.timestamp.tv_sec = us / 1000000UL; + s_fb.timestamp.tv_usec = us % 1000000UL; + + if (s_fb.len > UVC_MAX_FRAMESIZE_SIZE) { + ESP_LOGE(TAG, "Frame size %d is larger than max frame size %d", s_fb.len, UVC_MAX_FRAMESIZE_SIZE); + return NULL; + } + + return &s_fb; +} + +TEST_CASE("usb_device_uvc_test", "[usb_device_uvc]") +{ + + uint8_t *uvc_buffer = (uint8_t *)malloc(UVC_MAX_FRAMESIZE_SIZE); + TEST_ASSERT_NOT_NULL(uvc_buffer); + + uvc_device_config_t config = { + .uvc_buffer = uvc_buffer, + .uvc_buffer_size = UVC_MAX_FRAMESIZE_SIZE, + .start_cb = camera_start_cb, + .fb_get_cb = camera_fb_get_cb, + .fb_return_cb = camera_fb_return_cb, + .stop_cb = camera_stop_cb, + .cb_ctx = NULL, + }; + uvc_device_config(0, &config); + uvc_device_init(); + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +static size_t before_free_8bit; +static size_t before_free_32bit; + +void setUp(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void tearDown(void) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD); + unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + + // _ _ _ _ _____ _____ _____ _____ _____ + // | | | | | | / __ \ |_ _| ___/ ___|_ _| + // | | | | | | | / \/_____| | | |__ \ `--. | | + // | | | | | | | | |______| | | __| `--. \ | | + // | |_| \ \_/ / \__/\ | | | |___/\__/ / | | + // \___/ \___/ \____/ \_/ \____/\____/ \_/ + printf(" _ _ _ _ _____ _____ _____ _____ _____ \n"); + printf("| | | | | | / __ \\ |_ _| ___/ ___|_ _|\n"); + printf("| | | | | | | / \\/_____| | | |__ \\ `--. | | \n"); + printf("| | | | | | | | |______| | | __| `--. \\ | | \n"); + printf("| |_| \\ \\_/ / \\__/\\ | | | |___/\\__/ / | | \n"); + printf(" \\___/ \\___/ \\____/ \\_/ \\____/\\____/ \\_/ \n"); + unity_run_menu(); +} diff --git a/components/usb/usb_device_uvc/test_apps/sdkconfig.defaults b/components/usb/usb_device_uvc/test_apps/sdkconfig.defaults new file mode 100644 index 000000000..cb920f910 --- /dev/null +++ b/components/usb/usb_device_uvc/test_apps/sdkconfig.defaults @@ -0,0 +1,7 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.2.2 Project Minimal Configuration +# +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_UVC_CAM1_MULTI_FRAMESIZE=n +CONFIG_ESP_TASK_WDT_EN=n From 57309e37f52c5c525ddca76e59592bc6b41717e5 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Thu, 15 Aug 2024 14:37:15 +0800 Subject: [PATCH 5/9] feat(msc_ota): publish official version --- components/usb/esp_msc_ota/CHANGELOG.md | 4 ++++ components/usb/esp_msc_ota/idf_component.yml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/components/usb/esp_msc_ota/CHANGELOG.md b/components/usb/esp_msc_ota/CHANGELOG.md index 2095c4b30..6010a28fc 100644 --- a/components/usb/esp_msc_ota/CHANGELOG.md +++ b/components/usb/esp_msc_ota/CHANGELOG.md @@ -1,5 +1,9 @@ # changelog +## v1.0.0 - 2024-8-15 + +* Publish the official version + ## v0.1.2 - 2024-3-8 ### Bug Fix diff --git a/components/usb/esp_msc_ota/idf_component.yml b/components/usb/esp_msc_ota/idf_component.yml index f35a6c189..3295e1471 100644 --- a/components/usb/esp_msc_ota/idf_component.yml +++ b/components/usb/esp_msc_ota/idf_component.yml @@ -1,7 +1,8 @@ -version: "0.1.2" +version: "1.0.0" targets: - esp32s2 - esp32s3 + - esp32p4 description: Support OTA update through USB host MSC url: https://github.com/espressif/esp-iot-solution/tree/master/components/usb/esp_msc_ota From c64bd353f079d84f71f0bce29b24849d2a3783ce Mon Sep 17 00:00:00 2001 From: Li Junru Date: Thu, 15 Aug 2024 14:44:33 +0800 Subject: [PATCH 6/9] feat(knob): publish official version --- components/knob/CHANGELOG.md | 4 ++++ components/knob/README.md | 2 +- components/knob/idf_component.yml | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/knob/CHANGELOG.md b/components/knob/CHANGELOG.md index 63454a23e..ae3dbee94 100644 --- a/components/knob/CHANGELOG.md +++ b/components/knob/CHANGELOG.md @@ -1,5 +1,9 @@ # ChangeLog +## v1.0.0 - 2024-8-15 + +* Publish the official version + ## v0.1.5 - 2024-7-3 ### Enhancements: diff --git a/components/knob/README.md b/components/knob/README.md index e2ff45172..8a0767d4b 100644 --- a/components/knob/README.md +++ b/components/knob/README.md @@ -2,7 +2,7 @@ ## Component Knob -`Knob` is the component that provides the software PCNT, it can be used on chips(esp32c2, esp32c3) that do not have PCNT hardware capabilities. By using this component, you can quickly use a physical encoder, such as the EC11 encoder. +`Knob` is the component that provides the software quadrature decoding, it can be used on chips(esp32c2, esp32c3) that do not have PCNT hardware capabilities. By using this component, you can quickly use a physical encoder, such as the EC11 encoder. Features: diff --git a/components/knob/idf_component.yml b/components/knob/idf_component.yml index 785668abe..34f92e922 100644 --- a/components/knob/idf_component.yml +++ b/components/knob/idf_component.yml @@ -1,5 +1,5 @@ -version: "0.1.5" -description: Knob driver implemented through software pcnt +version: "1.0.0" +description: Knob driver implemented through software quadrature decoding. url: https://github.com/espressif/esp-iot-solution/tree/master/components/knob documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/knob.html issues: https://github.com/espressif/esp-iot-solution/issues From 73a10027872e744824cc564f3917f4ca385466c7 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Fri, 25 Oct 2024 15:31:23 +0800 Subject: [PATCH 7/9] fix(avi_player): fix potential errors that may occur during deinitialization --- components/avi_player/avi_player.c | 13 +++++++++++-- docs/en/index.rst | 1 + .../usb_dual_uvc_device/main/idf_component.yml | 2 ++ .../device/usb_extend_screen/main/idf_component.yml | 2 ++ .../device/usb_surface_dial/main/idf_component.yml | 2 ++ .../host/usb_cdc_4g_module/main/idf_component.yml | 2 ++ .../usb/host/usb_msc_ota/main/idf_component.yml | 2 ++ 7 files changed, 22 insertions(+), 2 deletions(-) diff --git a/components/avi_player/avi_player.c b/components/avi_player/avi_player.c index 55bf65a05..d05c3a715 100644 --- a/components/avi_player/avi_player.c +++ b/components/avi_player/avi_player.c @@ -23,8 +23,9 @@ static const char *TAG = "avi player"; #define EVENT_START_PLAY ((1 << 1)) #define EVENT_STOP_PLAY ((1 << 2)) #define EVENT_DEINIT ((1 << 3)) -#define EVENT_VIDEO_BUF_READY ((1 << 4)) -#define EVENT_AUDIO_BUF_READY ((1 << 5)) +#define EVENT_DEINIT_DONE ((1 << 4)) +#define EVENT_VIDEO_BUF_READY ((1 << 5)) +#define EVENT_AUDIO_BUF_READY ((1 << 6)) #define EVENT_ALL (EVENT_FPS_TIME_UP | EVENT_START_PLAY | EVENT_STOP_PLAY | EVENT_DEINIT) @@ -266,6 +267,7 @@ static void avi_player_task(void *args) } } + xEventGroupSetBits(s_avi->event_group, EVENT_DEINIT_DONE); vTaskDelete(NULL); } @@ -395,6 +397,13 @@ esp_err_t avi_player_deinit(void) return ESP_FAIL; } + xEventGroupSetBits(s_avi->event_group, EVENT_DEINIT); + EventBits_t uxBits = xEventGroupWaitBits(s_avi->event_group, EVENT_DEINIT_DONE, pdTRUE, pdTRUE, pdMS_TO_TICKS(1000)); + if (!(uxBits & EVENT_DEINIT_DONE)) { + ESP_LOGE(TAG, "AVI player deinit timeout"); + return ESP_ERR_TIMEOUT; + } + if (s_avi->timer_handle != NULL) { esp_timer_stop(s_avi->timer_handle); esp_timer_delete(s_avi->timer_handle); diff --git a/docs/en/index.rst b/docs/en/index.rst index e1641d878..c34720a89 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -68,6 +68,7 @@ ESP-IoT-Solution contains device drivers and code frameworks for the development Display USB Host&Device Audio