diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml new file mode 100644 index 00000000..15a61d6b --- /dev/null +++ b/.github/workflows/github-actions-demo.yml @@ -0,0 +1,18 @@ +name: GitHub Actions Demo +run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 +on: [push] +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + - name: Check out repository code + uses: actions/checkout@v4 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + - run: echo "🖥️ The workflow is now ready to test your code on the runner." + - name: List files in the repository + run: | + ls ${{ github.workspace }} + - run: echo "🍏 This job's status is ${{ job.status }}." diff --git a/components/who_app/who_recognition_app/who_recognition_app.cpp b/components/who_app/who_recognition_app/who_recognition_app.cpp index 428395a9..79d45313 100644 --- a/components/who_app/who_recognition_app/who_recognition_app.cpp +++ b/components/who_app/who_recognition_app/who_recognition_app.cpp @@ -32,5 +32,27 @@ bool WhoRecognitionApp::run() ret &= m_recognition->run(3584, 2, 0); return ret; } + +void WhoRecognitionApp::new_result_subscription(const std::function &cb) +{ + m_recognition->new_result_subscription(cb); +} + + + void WhoRecognitionApp::recognize() + { + // simulate a button was pressed + m_recognition->virtual_btn_event_handler(RECOGNIZE); + } + void WhoRecognitionApp::enroll() + { + // simulate a button was pressed + m_recognition->virtual_btn_event_handler(ENROLL); + } + void WhoRecognitionApp::delete_face() + { + // simulate a button was pressed + m_recognition->virtual_btn_event_handler(DELETE); + } } // namespace app } // namespace who diff --git a/components/who_app/who_recognition_app/who_recognition_app.hpp b/components/who_app/who_recognition_app/who_recognition_app.hpp index 0bb873fa..2f42f896 100644 --- a/components/who_app/who_recognition_app/who_recognition_app.hpp +++ b/components/who_app/who_recognition_app/who_recognition_app.hpp @@ -8,8 +8,13 @@ class WhoRecognitionApp : public WhoTasks { public: WhoRecognitionApp(); void set_cam(cam::WhoCam *cam) { m_frame_cap->set_cam(cam); } - void set_lcd(lcd::WhoLCD *lcd) { m_frame_cap->set_lcd(lcd); } + void set_lcd(lcd::WhoLCDiface *lcd) { m_frame_cap->set_lcd(lcd); } bool run() override; + void new_result_subscription(const std::function &cb); + + void recognize() ; + void enroll() ; + void delete_face() ; protected: frame_cap::WhoFrameCapLCD *m_frame_cap; diff --git a/components/who_frame_cap/who_frame_cap.hpp b/components/who_frame_cap/who_frame_cap.hpp index 02c73a43..97994a3d 100644 --- a/components/who_frame_cap/who_frame_cap.hpp +++ b/components/who_frame_cap/who_frame_cap.hpp @@ -39,20 +39,20 @@ class WhoFrameCapLCD : public WhoFrameCap { { } WhoFrameCapLCD(who::cam::WhoCam *cam, - who::lcd::WhoLCD *lcd, + who::lcd::WhoLCDiface *lcd, const std::string &name, bool display_back_frame = false) : WhoFrameCap(cam, name), m_lcd(lcd), m_display_back_frame(display_back_frame) { } - void set_lcd(who::lcd::WhoLCD *lcd) { m_lcd = lcd; } - who::lcd::WhoLCD *get_lcd() { return m_lcd; } + void set_lcd(who::lcd::WhoLCDiface *lcd) { m_lcd = lcd; } + who::lcd::WhoLCDiface *get_lcd() { return m_lcd; } bool run(const configSTACK_DEPTH_TYPE uxStackDepth, UBaseType_t uxPriority, const BaseType_t xCoreID) override; private: void run_lcd_display_cbs(who::cam::cam_fb_t *fb); void on_new_frame() override; - who::lcd::WhoLCD *m_lcd; + who::lcd::WhoLCDiface *m_lcd; bool m_display_back_frame; }; } // namespace frame_cap diff --git a/components/who_peripherals/who_lcd/who_lcd.hpp b/components/who_peripherals/who_lcd/who_lcd.hpp index e6d470bc..d35b7a40 100644 --- a/components/who_peripherals/who_lcd/who_lcd.hpp +++ b/components/who_peripherals/who_lcd/who_lcd.hpp @@ -1,18 +1,32 @@ #pragma once #include "esp_lcd_types.h" #include "bsp/esp-bsp.h" + + +namespace who { +namespace lcd { + +class WhoLCDiface { +public: + virtual ~WhoLCDiface() = default; + virtual void init() = 0; + virtual esp_lcd_panel_handle_t get_lcd_panel_handle() = 0; + virtual void draw_full_lcd(const void *data) = 0; +}; +} +} // namespace who + #if !BSP_CONFIG_NO_GRAPHIC_LIB #include "who_lvgl_lcd.hpp" #else - namespace who { namespace lcd { -class WhoLCD { +class WhoLCD : public WhoLCDiface { public: WhoLCD() { init(); } - void init(); - esp_lcd_panel_handle_t get_lcd_panel_handle(); - void draw_full_lcd(const void *data); + void init() override; + esp_lcd_panel_handle_t get_lcd_panel_handle() override; + void draw_full_lcd(const void *data) override; private: #if CONFIG_IDF_TARGET_ESP32S3 diff --git a/components/who_recognition/who_recognition.cpp b/components/who_recognition/who_recognition.cpp index a7108682..f7821aad 100644 --- a/components/who_recognition/who_recognition.cpp +++ b/components/who_recognition/who_recognition.cpp @@ -1,14 +1,21 @@ #include "who_recognition.hpp" +#include +#if !BSP_CONFIG_NO_GRAPHIC_LIB #include "who_lvgl_utils.hpp" +#endif #if CONFIG_IDF_TARGET_ESP32P4 #define WHO_REC_RES_SHOW_N_FRAMES (60) #elif CONFIG_IDF_TARGET_ESP32S3 #define WHO_REC_RES_SHOW_N_FRAMES (30) #endif +#if !BSP_CONFIG_NO_GRAPHIC_LIB LV_FONT_DECLARE(montserrat_bold_26); - +#endif namespace who { namespace recognition { + +void copy_img(const dl::image::img_t &src, std::list &detect_res, dl::image::img_t &dst, bool cropped); + detect::WhoDetectBase::result_t WhoDetectLCD::get_result() { xSemaphoreTake(m_res_mutex, portMAX_DELAY); @@ -22,6 +29,10 @@ void WhoDetectLCD::on_new_detect_result(const result_t &result) detect::WhoDetectLCD::on_new_detect_result(result); xSemaphoreTake(m_res_mutex, portMAX_DELAY); m_result = result; + + if (!result.det_res.empty() && subscriptor_m_event_group_ != nullptr) { + xEventGroupSetBits(subscriptor_m_event_group_, subscriptor_m_event_type_); + } xSemaphoreGive(m_res_mutex); } @@ -51,48 +62,121 @@ void WhoRecognition::task() if (event_bits & (RECOGNIZE | ENROLL)) { auto result = m_detect->get_result(); auto img = who::cam::fb2img(result.fb); - if (event_bits & RECOGNIZE) { - auto rec_res = m_recognizer->recognize(img, result.det_res); + + // If a face is detected + if (!result.det_res.empty()) { + // Cppy the latest detected result + img_last_det_res = result.det_res; + char *text = new char[64]; - if (rec_res.empty()) { - strcpy(text, "who?"); - } else { - snprintf(text, 64, "id: %d, sim: %.2f", rec_res[0].id, rec_res[0].similarity); - } + char *json = new char[128]; + + // Crop the first detected face xSemaphoreTake(m_res_mutex, portMAX_DELAY); - m_results.emplace_back(text); + copy_img(img, result.det_res, latest_image_with_a_face, 0); + copy_img(img, result.det_res, latest_face_cropped, 1); xSemaphoreGive(m_res_mutex); - } - if (event_bits & ENROLL) { - esp_err_t ret = m_recognizer->enroll(img, result.det_res); + + // Prepare the text for the result + if (event_bits & RECOGNIZE) { + dl::recognition::result_t recognition_result; + auto rec_res = m_recognizer->recognize(img, result.det_res); + + if (rec_res.empty()) { + recognition_result.id = 0; + recognition_result.similarity = 0.0f; + strcpy(text, "who?"); + } else { + recognition_result.id = rec_res[0].id; + recognition_result.similarity = rec_res[0].similarity; + snprintf(text, 64, "id: %d, sim: %.2f", rec_res[0].id, rec_res[0].similarity); + } + snprintf(json, 128, "{\"action\":\"recognize\",\"id\":%d,\"sim\":%.2f,\"message\":\"%s\"}", recognition_result.id, recognition_result.similarity, text); + + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + m_results.emplace_back(text); + xSemaphoreGive(m_res_mutex); + } + if (event_bits & ENROLL) { + int enrollId = 0; + esp_err_t ret = m_recognizer->enroll(img, result.det_res); + if (ret == ESP_FAIL) { + strcpy(text, "Failed to enroll."); + } else { + snprintf(text, 64, "id: %d enrolled.", m_recognizer->get_num_feats()); + enrollId = m_recognizer->get_num_feats(); + } + snprintf(json, 128, "{\"action\":\"enroll\",\"id\":%d,\"message\":\"%s\"}", enrollId, text); + + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + m_results.emplace_back(text); + xSemaphoreGive(m_res_mutex); + } + + // Display the result + if (m_result_cb) { + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + m_result_cb(json, latest_image_with_a_face, latest_face_cropped); + xSemaphoreGive(m_res_mutex); + } + } else { char *text = new char[64]; - if (ret == ESP_FAIL) { - strcpy(text, "Failed to enroll."); - } else { - snprintf(text, 64, "id: %d enrolled.", m_recognizer->get_num_feats()); + char *json = new char[128]; + int enrollId = 0; + + // No face detected -> enroll last detected face + if (event_bits & RECOGNIZE) { + strcpy(text, "No face detected"); + snprintf(json, 128, "{\"action\":\"recognize\",\"id\":%d,\"sim\":%d,\"message\":\"%s\"}", 0, 0, text); + + } else if (event_bits & ENROLL) { + esp_err_t ret = m_recognizer->enroll(latest_image_with_a_face, img_last_det_res); + if (ret == ESP_FAIL) { + strcpy(text, "Failed to enroll latest detected face."); + } else { + snprintf(text, 64, "id: %d enrolled.", m_recognizer->get_num_feats()); + enrollId = m_recognizer->get_num_feats(); + } + snprintf(json, 128, "{\"action\":\"enroll\",\"id\":%d,\"message\":\"%s\"}", enrollId, text); + + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + m_results.emplace_back(text); + xSemaphoreGive(m_res_mutex); + } + if (m_result_cb) { + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + m_result_cb(json, latest_image_with_a_face, latest_face_cropped); + xSemaphoreGive(m_res_mutex); } - xSemaphoreTake(m_res_mutex, portMAX_DELAY); - m_results.emplace_back(text); - xSemaphoreGive(m_res_mutex); } } if (event_bits & DELETE) { esp_err_t ret = m_recognizer->delete_last_feat(); char *text = new char[64]; + char *json = new char[128]; + int deletedId = 0; if (ret == ESP_FAIL) { strcpy(text, "Failed to delete."); } else { - snprintf(text, 64, "id: %d deleted.", m_recognizer->get_num_feats() + 1); + deletedId = m_recognizer->get_num_feats() + 1; // The ID of the deleted face is the current number of faces + 1 + snprintf(text, 64, "id: %d deleted.", deletedId); } + snprintf(json, 128, "{\"action\":\"delete\",\"id\":%d,\"message\":\"%s\"}", deletedId, text); + + xSemaphoreTake(m_res_mutex, portMAX_DELAY); m_results.emplace_back(text); xSemaphoreGive(m_res_mutex); + if (m_result_cb) { + m_result_cb(json, latest_image_with_a_face, latest_face_cropped); + } } m_detect->resume(); } vTaskDelete(NULL); } +#if !BSP_CONFIG_NO_GRAPHIC_LIB void WhoRecognition::lvgl_btn_event_handler(lv_event_t *e) { user_data_t *user_data = reinterpret_cast(lv_event_get_user_data(e)); @@ -102,17 +186,52 @@ void WhoRecognition::lvgl_btn_event_handler(lv_event_t *e) xEventGroupSetBits(event_group, user_data->event); } } +#endif // !BSP_CONFIG_NO_GRAPHIC_LIB + +void WhoRecognition::virtual_btn_event_handler(event_type_t event) +{ + EventGroupHandle_t event_group = get_event_group(); + EventBits_t event_bits = xEventGroupGetBits(event_group); + if (event == RECOGNIZE) { + // Activate autorecognize whenever there is a face detected + printf("Autorecognize activated\n"); + m_detect->set_subscriptor_event_group(event_group, RECOGNIZE); + } else { + // Deactivate autorecognize when not recognizing solicited + printf("Autorecognize deactivated\n"); + m_detect->set_subscriptor_event_group(nullptr, RECOGNIZE); + } + if (event_bits & BLOCKING && !(event_bits & PAUSE)) { + xEventGroupSetBits(event_group, event); + } +} void WhoRecognition::iot_btn_event_handler(void *button_handle, void *usr_data) { user_data_t *user_data = reinterpret_cast(usr_data); EventGroupHandle_t event_group = user_data->who_rec_ptr->get_event_group(); EventBits_t event_bits = xEventGroupGetBits(event_group); + + if (user_data->event == RECOGNIZE) { + // Activate autorecognize whenever there is a face detected + printf("Autorecognize activated\n"); + user_data->who_rec_ptr->m_detect->set_subscriptor_event_group(event_group, RECOGNIZE); + } else { + // Deactivate autorecognize when not recognizing solicited + printf("Autorecognize deactivated\n"); + user_data->who_rec_ptr->m_detect->set_subscriptor_event_group(nullptr, RECOGNIZE); + } + if (event_bits & BLOCKING && !(event_bits & PAUSE)) { xEventGroupSetBits(event_group, user_data->event); } } +void WhoRecognition::new_result_subscription(const std::function &cb) +{ + m_result_cb = cb; +} + void WhoRecognition::create_btns() { #if CONFIG_IDF_TARGET_ESP32P4 @@ -143,27 +262,33 @@ void WhoRecognition::create_btns() ESP_ERROR_CHECK(bsp_iot_button_create(btns, NULL, BSP_BUTTON_NUM)); // play recognize ESP_ERROR_CHECK( - iot_button_register_cb(btns[1], BUTTON_SINGLE_CLICK, nullptr, iot_btn_event_handler, (void *)m_btn_user_data)); + iot_button_register_cb(btns[2], BUTTON_SINGLE_CLICK, nullptr, iot_btn_event_handler, (void *)m_btn_user_data)); + // up enroll ESP_ERROR_CHECK(iot_button_register_cb( - btns[3], BUTTON_SINGLE_CLICK, nullptr, iot_btn_event_handler, (void *)(m_btn_user_data + 1))); + btns[2], BUTTON_DOUBLE_CLICK, nullptr, iot_btn_event_handler, (void *)(m_btn_user_data + 1))); + // down delete ESP_ERROR_CHECK(iot_button_register_cb( - btns[2], BUTTON_SINGLE_CLICK, nullptr, iot_btn_event_handler, (void *)(m_btn_user_data + 2))); + btns[2], BUTTON_LONG_PRESS_START, nullptr, iot_btn_event_handler, (void *)(m_btn_user_data + 2))); + #endif } void WhoRecognition::create_label() { +#if !BSP_CONFIG_NO_GRAPHIC_LIB bsp_display_lock(0); m_label = create_lvgl_label("", &montserrat_bold_26); const lv_font_t *font = lv_obj_get_style_text_font(m_label, LV_PART_MAIN); lv_obj_align(m_label, LV_ALIGN_TOP_MID, 0, font->line_height); bsp_display_unlock(); +#endif // !BSP_CONFIG_NO_GRAPHIC_LIB } void WhoRecognition::lcd_display_cb(who::cam::cam_fb_t *fb) { +#if !BSP_CONFIG_NO_GRAPHIC_LIB xSemaphoreTake(m_res_mutex, portMAX_DELAY); static int cnt = WHO_REC_RES_SHOW_N_FRAMES; if (!m_results.empty()) { @@ -178,6 +303,73 @@ void WhoRecognition::lcd_display_cb(who::cam::cam_fb_t *fb) lv_label_set_text(m_label, ""); } xSemaphoreGive(m_res_mutex); +#endif +} + +// Crops an RGB888 image buffer +void copy_img(const dl::image::img_t &src, std::list &detect_res, dl::image::img_t &dst, bool cropped) +{ + if (detect_res.empty()) { + ESP_LOGW("CROP", "Failed to crop. No face detected."); + return; + } + + int x1 = INT_MAX;//face.box[0]; + int y1 = INT_MAX;//face.box[1]; + int x2 = 0;//face.box[2]; + int y2 = 0;//face.box[3]; + + // Expand bounding box to include all faces + for (const auto &face : detect_res) { + x1 = std::min(x1, face.box[0]); + y1 = std::min(y1, face.box[1]); + x2 = std::max(x2, face.box[2]); + y2 = std::max(y2, face.box[3]); + } + + int crop_w = x2 - x1; + int crop_h = y2 - y1; + if (crop_h <= 0 || crop_w <= 0) { + ESP_LOGE("WhoRecognition", "Invalid crop dimensions: width=%d, height=%d", crop_w, crop_h); + if (crop_h <= 0) { + crop_h = y1 - y2; + } + if (crop_w <= 0) { + crop_w = x1 - x2; + } + } + + if (dst.data) { + free(dst.data); + } + memset(&dst, 0, sizeof(dl::image::img_t)); + + if (cropped) { + // Ensure the crop does not exceed the source image dimensions + x1 = std::max(0, x1); + y1 = std::max(0, y1); + crop_w = std::min(crop_w, src.width - x1); + crop_h = std::min(crop_h, src.height - y1); + } else { + // If not cropping, use the full image dimensions + x1 = 0; + y1 = 0; + crop_w = src.width; + crop_h = src.height; + } + dst.width = crop_w; + dst.height = crop_h; + dst.pix_type = src.pix_type; + size_t pixel_size = + get_img_byte_size(dst) / (dst.width * dst.height); // For RGB888 is 3 bytes per pixel + + dst.data = (uint8_t *)malloc(get_img_byte_size(dst)); + + for (int y = 0; y < crop_h; ++y) { + memcpy(dst.data + y * crop_w * pixel_size, + src.data + ((y + y1) * src.width + x1) * pixel_size, + crop_w * pixel_size); + } } } // namespace recognition diff --git a/components/who_recognition/who_recognition.hpp b/components/who_recognition/who_recognition.hpp index 0c3d2652..22f13729 100644 --- a/components/who_recognition/who_recognition.hpp +++ b/components/who_recognition/who_recognition.hpp @@ -22,11 +22,20 @@ class WhoDetectLCD : public detect::WhoDetectLCD { } ~WhoDetectLCD() { vSemaphoreDelete(m_res_mutex); } detect::WhoDetectBase::result_t get_result(); + void set_subscriptor_event_group (EventGroupHandle_t event_group, event_type_t event_type = RECOGNIZE) + { + xSemaphoreTake(m_res_mutex, portMAX_DELAY); + subscriptor_m_event_group_ = event_group; + subscriptor_m_event_type_ = event_type; + xSemaphoreGive(m_res_mutex); + } private: void on_new_detect_result(const result_t &result) override; SemaphoreHandle_t m_res_mutex; result_t m_result; + EventGroupHandle_t subscriptor_m_event_group_ = nullptr; // Event group for subscriber notifications + event_type_t subscriptor_m_event_type_ = static_cast(0); // Default event type for recognition }; class WhoRecognition : public WhoSubscriber { @@ -41,6 +50,7 @@ class WhoRecognition : public WhoSubscriber { WhoSubscriber(name), m_detect(detect), m_recognizer(recognizer), m_res_mutex(xSemaphoreCreateMutex()) { m_detect->m_frame_cap->add_element(this); + m_detect->set_subscriptor_event_group(get_event_group(), RECOGNIZE); m_btn_user_data = new user_data_t[3]; m_btn_user_data[0] = {this, RECOGNIZE}; m_btn_user_data[1] = {this, ENROLL}; @@ -51,13 +61,23 @@ class WhoRecognition : public WhoSubscriber { { vSemaphoreDelete(m_res_mutex); delete[] m_btn_user_data; + if (latest_image_with_a_face.data) free(latest_image_with_a_face.data); + if (latest_face_cropped.data) free(latest_face_cropped.data); } void task() override; void lcd_display_cb(who::cam::cam_fb_t *fb) override; + void new_result_subscription(const std::function &cb); + void virtual_btn_event_handler( event_type_t event); + + + + private: +#if !BSP_CONFIG_NO_GRAPHIC_LIB static void lvgl_btn_event_handler(lv_event_t *e); +#endif // !BSP_CONFIG_NO_GRAPHIC_LIB static void iot_btn_event_handler(void *button_handle, void *usr_data); void create_btns(); void create_label(); @@ -67,8 +87,17 @@ class WhoRecognition : public WhoSubscriber { SemaphoreHandle_t m_res_mutex; std::list m_results; +#if !BSP_CONFIG_NO_GRAPHIC_LIB lv_obj_t *m_label; +#endif // !BSP_CONFIG_NO_GRAPHIC_LIB user_data_t *m_btn_user_data; + std::function m_result_cb; + + // Store the last image with a face detected in case we want to enroll it later + dl::image::img_t latest_image_with_a_face = {nullptr, 0, 0, dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8}; + std::list img_last_det_res; + + dl::image::img_t latest_face_cropped = {nullptr, 0, 0, dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8}; }; } // namespace recognition } // namespace who diff --git a/examples/human_face_recognition/dependencies.lock.esp32s3 b/examples/human_face_recognition/dependencies.lock.esp32s3 index 4c17c00c..cd876e07 100644 --- a/examples/human_face_recognition/dependencies.lock.esp32s3 +++ b/examples/human_face_recognition/dependencies.lock.esp32s3 @@ -148,7 +148,7 @@ dependencies: idf: source: type: idf - version: 5.4.0 + version: 5.4.1 lvgl/lvgl: component_hash: 096c69af22eaf8a2b721e3913da91918c5e6bf1a762a113ec01f401aa61337a0 dependencies: [] @@ -162,6 +162,6 @@ direct_dependencies: - espressif/esp_lvgl_port - espressif/human_face_recognition - idf -manifest_hash: 40f12dea6eb8d013bdf75319cc6425d959451fd3a01e33b855533ccc886afc4c +manifest_hash: f34b3f41832a2311ecd482bd1cb77108e32f3f81060e856fe13f040d461fd674 target: esp32s3 version: 2.0.0 diff --git a/examples/human_face_recognition/main/idf_component.yml b/examples/human_face_recognition/main/idf_component.yml index ebeae3e4..cb09392e 100644 --- a/examples/human_face_recognition/main/idf_component.yml +++ b/examples/human_face_recognition/main/idf_component.yml @@ -1,3 +1,4 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_lvgl_port: "*" \ No newline at end of file + espressif/esp_lvgl_port: '*' + espressif/esp-dl: ^3.1.2 diff --git a/examples/human_face_recognition_mqtt/CMakeLists.txt b/examples/human_face_recognition_mqtt/CMakeLists.txt new file mode 100644 index 00000000..6ee262d1 --- /dev/null +++ b/examples/human_face_recognition_mqtt/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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 ../../components/who_peripherals/who_cam + ../../components/who_peripherals/who_lcd + ../../components/who_peripherals/who_spiflash_fatfs + ../../components/who_task + ../../components/who_frame_cap + ../../components/who_detect + ../../components/who_recognition + ../../components/who_app/who_recognition_app) + +add_compile_options(-fdiagnostics-color=always) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(DEPENDENCIES_LOCK dependencies.lock.${IDF_TARGET}) +project(human_face_recognition) diff --git a/examples/human_face_recognition_mqtt/README.md b/examples/human_face_recognition_mqtt/README.md new file mode 100644 index 00000000..91bceb99 --- /dev/null +++ b/examples/human_face_recognition_mqtt/README.md @@ -0,0 +1,28 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + + +# Human Face Recognition Example + +### Interaction + +#### ESP-S3-EYE + +Physical buttons + +| btn | opertion | +|------|------------------| +| play | recognize | +| up | enroll | +| down | delete last feat | + +#### ESP32-P4-Function-EV-Board + +Touch screen buttons + +| btn | opertion | +|-----------|------------------| +| recognize | recognize | +| enroll | enroll | +| delete | delete last feat | + diff --git a/examples/human_face_recognition_mqtt/dependencies.lock.esp32p4 b/examples/human_face_recognition_mqtt/dependencies.lock.esp32p4 new file mode 100644 index 00000000..d6f137aa --- /dev/null +++ b/examples/human_face_recognition_mqtt/dependencies.lock.esp32p4 @@ -0,0 +1,284 @@ +dependencies: + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 0.5.3 + espressif/esp-dl: + component_hash: f451adb8650cf7cbfd94001d07099190cc15273156fc53f748a1288b727c48cb + dependencies: + - name: espressif/esp_jpeg + registry_url: https://components.espressif.com + require: private + version: ^1.0.5~3 + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + - esp32p4 + version: 3.1.2 + espressif/esp32_p4_function_ev_board_noglib: + component_hash: 00c80e12ee9d3f056f63437c1a868ac887a8a4e9cdca95b7a08eefca0322f734 + dependencies: + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: public + version: 1.2.* + - name: espressif/esp_lcd_ek79007 + registry_url: https://components.espressif.com + require: private + version: 1.* + - name: espressif/esp_lcd_ili9881c + registry_url: https://components.espressif.com + require: private + version: 1.* + - name: espressif/esp_lcd_touch_gt911 + registry_url: https://components.espressif.com + require: private + version: ^1 + - name: espressif/esp_lcd_lt8912b + registry_url: https://components.espressif.com + require: private + version: '>=0.1.1,<1.0.0' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32p4 + version: 5.0.0 + espressif/esp_cam_sensor: + component_hash: 7b110b08542aeae833895dacd74425e926dd36eed68ae1a26f2b6109e04ea984 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_sccb_intf + registry_url: https://components.espressif.com + require: private + version: '>=0.0.2' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 0.9.0 + espressif/esp_codec_dev: + component_hash: 014948481bda426cd46714f297fe1891711246c62bea288863a8cc8cf13ef1f0 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.0 + espressif/esp_h264: + component_hash: fa5daaebc8a304f0b79bc57e51aab43e0a03eabc2ba3865bff4063c56b5d4564 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.4 + espressif/esp_ipa: + component_hash: 7547b050a99351a85aaba8ba38211c36fd4769c2ae3e570fa330dc90dfd2704c + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.4' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 0.2.0 + espressif/esp_jpeg: + component_hash: fdddaa1ceeee223e0abd7e865b05dbceea7ff175440a5ea26eeff8304564e7aa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.0 + espressif/esp_lcd_ek79007: + component_hash: 07c1afab7e9fd4dd2fd06ff9245e65327c5bbd5485efec199496e19a9304d47b + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 1.0.2 + espressif/esp_lcd_ili9881c: + component_hash: f4f374226b62baf13f735864e8fae58e17c537df34d598e059f6caad4761ef65 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 1.0.1 + espressif/esp_lcd_lt8912b: + component_hash: 232578085e67a0fe1a22d58f290ba5c10959d9a247022bec561500b5661fb360 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.1.1 + espressif/esp_lcd_touch: + component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.2 + espressif/esp_lcd_touch_gt911: + component_hash: acc1c184358aa29ef72506f618c9c76a8cc2bf12af38a2bff3d44d84f3a08857 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.1.0 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.3 + espressif/esp_lvgl_port: + component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556 + dependencies: + - name: idf + require: private + version: '>=4.4' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.0 + espressif/esp_sccb_intf: + component_hash: 71e3def402f6193a23c599bdde4fa0b544ca2b1f63608b6d0ec2bee3cd9a1c33 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.0.5 + espressif/esp_video: + component_hash: 37f7a3b16aa842e612180d5a4698db1c3bc36142a0b78664eb4ffe67c8cd0510 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_cam_sensor + registry_url: https://components.espressif.com + require: private + version: 0.9.* + - name: espressif/esp_h264 + registry_url: https://components.espressif.com + require: private + version: '>=1.0.3' + - name: espressif/esp_ipa + registry_url: https://components.espressif.com + require: private + version: 0.2.* + - name: idf + require: private + version: '>=5.4' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32p4 + version: 0.8.0~3 + espressif/human_face_detect: + component_hash: cba8a6be8221aa08618e2ffb8087f732210731f1f3e6f17e025a81ae413b139f + dependencies: + - name: espressif/esp-dl + registry_url: https://components.espressif.com + require: private + version: ^3.1.1 + source: + registry_url: https://components.espressif.com + type: service + version: 0.2.2 + espressif/human_face_recognition: + component_hash: 6e158c3501d53d6fa65c2ea35194fd43dc2994d9f3b032798c544657664223b6 + dependencies: + - name: espressif/human_face_detect + registry_url: https://components.espressif.com + require: private + version: ^0.2.1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.2 + idf: + source: + type: idf + version: 5.4.0 + lvgl/lvgl: + component_hash: 096c69af22eaf8a2b721e3913da91918c5e6bf1a762a113ec01f401aa61337a0 + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 9.2.2 +direct_dependencies: +- espressif/esp-dl +- espressif/esp32_p4_function_ev_board_noglib +- espressif/esp_h264 +- espressif/esp_lvgl_port +- espressif/esp_video +- espressif/human_face_recognition +- idf +manifest_hash: 40f12dea6eb8d013bdf75319cc6425d959451fd3a01e33b855533ccc886afc4c +target: esp32p4 +version: 2.0.0 diff --git a/examples/human_face_recognition_mqtt/dependencies.lock.esp32s3 b/examples/human_face_recognition_mqtt/dependencies.lock.esp32s3 new file mode 100644 index 00000000..cd876e07 --- /dev/null +++ b/examples/human_face_recognition_mqtt/dependencies.lock.esp32s3 @@ -0,0 +1,167 @@ +dependencies: + espressif/button: + component_hash: f53face2ab21fa0ffaf4cf0f6e513d393f56df6586bb2ad1146120f03f19ee05 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com + type: service + version: 4.1.3 + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 0.5.3 + espressif/esp-dl: + component_hash: f451adb8650cf7cbfd94001d07099190cc15273156fc53f748a1288b727c48cb + dependencies: + - name: espressif/esp_jpeg + registry_url: https://components.espressif.com + require: private + version: ^1.0.5~3 + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + - esp32p4 + version: 3.1.2 + espressif/esp32-camera: + component_hash: c3eb05fbeeae884a23bed9b17d48d5f0da8872beadae0c0e747d2fbea6094f06 + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 2.0.15 + espressif/esp32_s3_eye_noglib: + component_hash: 270a15b503a9977bf394dd5cc659f12adc05663bb9ac24bbc104c8a95cb58fe9 + dependencies: + - name: espressif/button + registry_url: https://components.espressif.com + require: public + version: ^4 + - name: espressif/esp32-camera + registry_url: https://components.espressif.com + require: public + version: ^2.0.13 + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: public + version: ~1.3.1 + - name: idf + require: private + version: '>=5.4' + - name: espressif/qma6100p + registry_url: https://components.espressif.com + require: public + version: ^2 + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + version: 5.0.0 + espressif/esp_codec_dev: + component_hash: 18c22e1411224ba6103c4aca1b01bc740af33926756e9e106370a477d52bcba1 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.4 + espressif/esp_jpeg: + component_hash: fdddaa1ceeee223e0abd7e865b05dbceea7ff175440a5ea26eeff8304564e7aa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.0 + espressif/esp_lvgl_port: + component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556 + dependencies: + - name: idf + require: private + version: '>=4.4' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.0 + espressif/human_face_detect: + component_hash: cba8a6be8221aa08618e2ffb8087f732210731f1f3e6f17e025a81ae413b139f + dependencies: + - name: espressif/esp-dl + registry_url: https://components.espressif.com + require: private + version: ^3.1.1 + source: + registry_url: https://components.espressif.com + type: service + version: 0.2.2 + espressif/human_face_recognition: + component_hash: 6e158c3501d53d6fa65c2ea35194fd43dc2994d9f3b032798c544657664223b6 + dependencies: + - name: espressif/human_face_detect + registry_url: https://components.espressif.com + require: private + version: ^0.2.1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.2 + espressif/qma6100p: + component_hash: dee71383df1d07ad56557eccf8b6f6de3689bee5afc14e353f9149299daf5ca5 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com + type: service + version: 2.0.0 + idf: + source: + type: idf + version: 5.4.1 + lvgl/lvgl: + component_hash: 096c69af22eaf8a2b721e3913da91918c5e6bf1a762a113ec01f401aa61337a0 + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 9.2.2 +direct_dependencies: +- espressif/esp-dl +- espressif/esp32_s3_eye_noglib +- espressif/esp_lvgl_port +- espressif/human_face_recognition +- idf +manifest_hash: f34b3f41832a2311ecd482bd1cb77108e32f3f81060e856fe13f040d461fd674 +target: esp32s3 +version: 2.0.0 diff --git a/examples/human_face_recognition_mqtt/main/CMakeLists.txt b/examples/human_face_recognition_mqtt/main/CMakeLists.txt new file mode 100644 index 00000000..022cfb9c --- /dev/null +++ b/examples/human_face_recognition_mqtt/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(src_dirs ./) + +set(include_dirs ./) + +set(requires who_spiflash_fatfs + who_recognition_app + + + esp-tls esp_http_server nvs_flash esp_netif esp_wifi + ) + +idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires}) + + + + diff --git a/examples/human_face_recognition_mqtt/main/Kconfig b/examples/human_face_recognition_mqtt/main/Kconfig new file mode 100644 index 00000000..75f5d40c --- /dev/null +++ b/examples/human_face_recognition_mqtt/main/Kconfig @@ -0,0 +1,96 @@ +menu "Example Configuration" + + config EXAMPLE_BASIC_AUTH + bool "Basic Authentication" + default n + help + Basic Authentication is a method for an HTTP user agent (e.g. a web browser) + to provide a user name and password when making a request. It is the simplest + technique for enforcing access controls to web resources. because it doesn't + require cookies, session identifiers, or login pages; rather, it uses standard + fields in the HTTP header. + Note that, Basic Authentication is not encrypted channel and also easy to retrieve + credentials as they are sent in plain text format. + + config EXAMPLE_BASIC_AUTH_USERNAME + string "Basic Authenticate User Name" + depends on EXAMPLE_BASIC_AUTH + default "ESP32" + help + The client's user name which used for basic authenticate. + + config EXAMPLE_BASIC_AUTH_PASSWORD + string "Basic Authenticate Password" + depends on EXAMPLE_BASIC_AUTH + default "ESP32Webserver" + help + The client's password which used for basic authenticate. + + config ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + choice ESP_WIFI_SAE_MODE + prompt "WPA3 SAE mode selection" + default ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + help + Select mode for SAE as Hunt and Peck, H2E or both. + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK + bool "HUNT AND PECK" + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT + bool "H2E" + depends on ESP_WIFI_ENABLE_SAE_H2E + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + bool "BOTH" + depends on ESP_WIFI_ENABLE_SAE_H2E + endchoice + + config ESP_WIFI_PW_ID + string "PASSWORD IDENTIFIER" + depends on ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT|| ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + default "" + help + password identifier for SAE H2E + + config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. + + choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD + prompt "WiFi Scan auth mode threshold" + default ESP_WIFI_AUTH_WPA2_PSK + help + The weakest authmode to accept in the scan mode. + This value defaults to ESP_WIFI_AUTH_WPA2_PSK in case password is present and ESP_WIFI_AUTH_OPEN is used. + Please select ESP_WIFI_AUTH_WEP/ESP_WIFI_AUTH_WPA_PSK in case AP is operating in WEP/WPA mode. + + config ESP_WIFI_AUTH_OPEN + bool "OPEN" + config ESP_WIFI_AUTH_WEP + bool "WEP" + config ESP_WIFI_AUTH_WPA_PSK + bool "WPA PSK" + config ESP_WIFI_AUTH_WPA2_PSK + bool "WPA2 PSK" + config ESP_WIFI_AUTH_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config ESP_WIFI_AUTH_WPA3_PSK + bool "WPA3 PSK" + config ESP_WIFI_AUTH_WPA2_WPA3_PSK + bool "WPA2/WPA3 PSK" + config ESP_WIFI_AUTH_WAPI_PSK + bool "WAPI PSK" + endchoice + + +endmenu \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt/main/app_main.cpp b/examples/human_face_recognition_mqtt/main/app_main.cpp new file mode 100644 index 00000000..8636cdaa --- /dev/null +++ b/examples/human_face_recognition_mqtt/main/app_main.cpp @@ -0,0 +1,716 @@ +#include "who_recognition_app.hpp" +#include "who_spiflash_fatfs.hpp" + +#include "esp_netif.h" +#include +#include +#include +#include +#include +#include +// #include "protocol_examples_common.h" +// #include "protocol_examples_utils.h" +#include "esp_event.h" +#include "esp_tls_crypto.h" +#include +// #include "esp_netif.h" +#include "esp_check.h" +#include "esp_tls.h" + +#include "nvs_flash.h" +#include +#include +// #include "esp_eth.h" +#include "esp_camera.h" +#include "esp_http_server.h" +#include "esp_timer.h" + +#define PART_BOUNDARY "123456789000000000000987654321" +static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; +static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY + +#if CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK +#define EXAMPLE_H2E_IDENTIFIER "" +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#endif +#if CONFIG_ESP_WIFI_AUTH_OPEN +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN +#elif CONFIG_ESP_WIFI_AUTH_WEP +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP +#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK +#endif + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +// static const char *TAG = "wifi station"; + +static int s_retry_num = 0; + +using namespace who::cam; +using namespace who::lcd; +using namespace who::app; + +static auto cam = new WhoS3Cam(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, true); +static httpd_handle_t server = NULL; + +#define EXAMPLE_HTTP_QUERY_KEY_MAX_LEN (64) + +/* A simple example that demonstrates how to create GET and POST + * handlers for the web server. + */ + +static const char *TAG = "example"; + +#if CONFIG_EXAMPLE_BASIC_AUTH + +typedef struct { + char *username; + char *password; +} basic_auth_info_t; + +#define HTTPD_401 "401 UNAUTHORIZED" /*!< HTTP Response 401 */ + +static char *http_auth_basic(const char *username, const char *password) +{ + size_t out; + char *user_info = NULL; + char *digest = NULL; + size_t n = 0; + int rc = asprintf(&user_info, "%s:%s", username, password); + if (rc < 0) { + ESP_LOGE(TAG, "asprintf() returned: %d", rc); + return NULL; + } + + if (!user_info) { + ESP_LOGE(TAG, "No enough memory for user information"); + return NULL; + } + esp_crypto_base64_encode(NULL, 0, &n, (const unsigned char *)user_info, strlen(user_info)); + + /* 6: The length of the "Basic " string + * n: Number of bytes for a base64 encode format + * 1: Number of bytes for a reserved which be used to fill zero + */ + digest = calloc(1, 6 + n + 1); + if (digest) { + strcpy(digest, "Basic "); + esp_crypto_base64_encode( + (unsigned char *)digest + 6, n, &out, (const unsigned char *)user_info, strlen(user_info)); + } + free(user_info); + return digest; +} + +/* An HTTP GET handler */ +static esp_err_t basic_auth_get_handler(httpd_req_t *req) +{ + char *buf = NULL; + size_t buf_len = 0; + basic_auth_info_t *basic_auth_info = req->user_ctx; + + buf_len = httpd_req_get_hdr_value_len(req, "Authorization") + 1; + if (buf_len > 1) { + buf = calloc(1, buf_len); + if (!buf) { + ESP_LOGE(TAG, "No enough memory for basic authorization"); + return ESP_ERR_NO_MEM; + } + + if (httpd_req_get_hdr_value_str(req, "Authorization", buf, buf_len) == ESP_OK) { + ESP_LOGI(TAG, "Found header => Authorization: %s", buf); + } else { + ESP_LOGE(TAG, "No auth value received"); + } + + char *auth_credentials = http_auth_basic(basic_auth_info->username, basic_auth_info->password); + if (!auth_credentials) { + ESP_LOGE(TAG, "No enough memory for basic authorization credentials"); + free(buf); + return ESP_ERR_NO_MEM; + } + + if (strncmp(auth_credentials, buf, buf_len)) { + ESP_LOGE(TAG, "Not authenticated"); + httpd_resp_set_status(req, HTTPD_401); + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Connection", "keep-alive"); + httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"Hello\""); + httpd_resp_send(req, NULL, 0); + } else { + ESP_LOGI(TAG, "Authenticated!"); + char *basic_auth_resp = NULL; + httpd_resp_set_status(req, HTTPD_200); + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Connection", "keep-alive"); + int rc = + asprintf(&basic_auth_resp, "{\"authenticated\": true,\"user\": \"%s\"}", basic_auth_info->username); + if (rc < 0) { + ESP_LOGE(TAG, "asprintf() returned: %d", rc); + free(auth_credentials); + return ESP_FAIL; + } + if (!basic_auth_resp) { + ESP_LOGE(TAG, "No enough memory for basic authorization response"); + free(auth_credentials); + free(buf); + return ESP_ERR_NO_MEM; + } + httpd_resp_send(req, basic_auth_resp, strlen(basic_auth_resp)); + free(basic_auth_resp); + } + free(auth_credentials); + free(buf); + } else { + ESP_LOGE(TAG, "No auth header received"); + httpd_resp_set_status(req, HTTPD_401); + httpd_resp_set_type(req, "application/json"); + httpd_resp_set_hdr(req, "Connection", "keep-alive"); + httpd_resp_set_hdr(req, "WWW-Authenticate", "Basic realm=\"Hello\""); + httpd_resp_send(req, NULL, 0); + } + + return ESP_OK; +} + +static httpd_uri_t basic_auth = { + .uri = "/basic_auth", + .method = HTTP_GET, + .handler = basic_auth_get_handler, +}; + +static void httpd_register_basic_auth(httpd_handle_t server) +{ + basic_auth_info_t *basic_auth_info = calloc(1, sizeof(basic_auth_info_t)); + if (basic_auth_info) { + basic_auth_info->username = CONFIG_EXAMPLE_BASIC_AUTH_USERNAME; + basic_auth_info->password = CONFIG_EXAMPLE_BASIC_AUTH_PASSWORD; + + basic_auth.user_ctx = basic_auth_info; + httpd_register_uri_handler(server, &basic_auth); + } +} +#endif + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +/* An HTTP GET handler */ +// static esp_err_t hello_get_handler(httpd_req_t *req) +// { +// char *buf; +// size_t buf_len; + +// /* Get header value string length and allocate memory for length + 1, +// * extra byte for null termination */ +// buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1; +// if (buf_len > 1) { +// buf = static_cast(malloc(buf_len)); +// ESP_RETURN_ON_FALSE(buf, ESP_ERR_NO_MEM, TAG, "buffer alloc failed"); +// /* Copy null terminated value string into buffer */ +// if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) { +// ESP_LOGI(TAG, "Found header => Host: %s", buf); +// } +// free(buf); +// } + +// buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-2") + 1; +// if (buf_len > 1) { +// buf = static_cast(malloc(buf_len)); +// ESP_RETURN_ON_FALSE(buf, ESP_ERR_NO_MEM, TAG, "buffer alloc failed"); +// if (httpd_req_get_hdr_value_str(req, "Test-Header-2", buf, buf_len) == ESP_OK) { +// ESP_LOGI(TAG, "Found header => Test-Header-2: %s", buf); +// } +// free(buf); +// } + +// buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-1") + 1; +// if (buf_len > 1) { +// buf = static_cast(malloc(buf_len)); +// ESP_RETURN_ON_FALSE(buf, ESP_ERR_NO_MEM, TAG, "buffer alloc failed"); +// if (httpd_req_get_hdr_value_str(req, "Test-Header-1", buf, buf_len) == ESP_OK) { +// ESP_LOGI(TAG, "Found header => Test-Header-1: %s", buf); +// } +// free(buf); +// } + +// /* Read URL query string length and allocate memory for length + 1, +// * extra byte for null termination */ +// buf_len = httpd_req_get_url_query_len(req) + 1; +// if (buf_len > 1) { +// buf = static_cast(malloc(buf_len)); +// ESP_RETURN_ON_FALSE(buf, ESP_ERR_NO_MEM, TAG, "buffer alloc failed"); +// if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { +// ESP_LOGI(TAG, "Found URL query => %s", buf); +// char param[EXAMPLE_HTTP_QUERY_KEY_MAX_LEN], dec_param[EXAMPLE_HTTP_QUERY_KEY_MAX_LEN] = {0}; +// /* Get value of expected key from query string */ +// if (httpd_query_key_value(buf, "query1", param, sizeof(param)) == ESP_OK) { +// ESP_LOGI(TAG, "Found URL query parameter => query1=%s", param); +// example_uri_decode(dec_param, param, strnlen(param, EXAMPLE_HTTP_QUERY_KEY_MAX_LEN)); +// ESP_LOGI(TAG, "Decoded query parameter => %s", dec_param); +// } +// if (httpd_query_key_value(buf, "query3", param, sizeof(param)) == ESP_OK) { +// ESP_LOGI(TAG, "Found URL query parameter => query3=%s", param); +// example_uri_decode(dec_param, param, strnlen(param, EXAMPLE_HTTP_QUERY_KEY_MAX_LEN)); +// ESP_LOGI(TAG, "Decoded query parameter => %s", dec_param); +// } +// if (httpd_query_key_value(buf, "query2", param, sizeof(param)) == ESP_OK) { +// ESP_LOGI(TAG, "Found URL query parameter => query2=%s", param); +// example_uri_decode(dec_param, param, strnlen(param, EXAMPLE_HTTP_QUERY_KEY_MAX_LEN)); +// ESP_LOGI(TAG, "Decoded query parameter => %s", dec_param); +// } +// } +// free(buf); +// } + +// /* Set some custom headers */ +// httpd_resp_set_hdr(req, "Custom-Header-1", "Custom-Value-1"); +// httpd_resp_set_hdr(req, "Custom-Header-2", "Custom-Value-2"); + +// /* Send response with custom headers and body set as the +// * string passed in user context*/ +// const char *resp_str = (const char *)req->user_ctx; +// httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + +// /* After sending the HTTP response the old HTTP request +// * headers are lost. Check if HTTP request headers can be read now. */ +// if (httpd_req_get_hdr_value_len(req, "Host") == 0) { +// ESP_LOGI(TAG, "Request headers lost"); +// } +// return ESP_OK; +// } + +// static const httpd_uri_t hello = {.uri = "/hello", +// .method = HTTP_GET, +// .handler = hello_get_handler, +// /* Let's pass response string in user +// * context to demonstrate it's usage */ +// .user_ctx = "Hello World!"}; + +/* An HTTP POST handler */ +static esp_err_t echo_post_handler(httpd_req_t *req) +{ + char buf[100]; + int ret, remaining = req->content_len; + + while (remaining > 0) { + /* Read the data for the request */ + if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + /* Retry receiving if timeout occurred */ + continue; + } + return ESP_FAIL; + } + + /* Send back the same data */ + httpd_resp_send_chunk(req, buf, ret); + remaining -= ret; + + /* Log data received */ + ESP_LOGI(TAG, "=========== RECEIVED DATA =========="); + ESP_LOGI(TAG, "%.*s", ret, buf); + ESP_LOGI(TAG, "===================================="); + } + + // End response + httpd_resp_send_chunk(req, NULL, 0); + return ESP_OK; +} + +static const httpd_uri_t echo = {.uri = "/echo", .method = HTTP_POST, .handler = echo_post_handler, .user_ctx = NULL}; + +esp_err_t jpg_stream_httpd_handler(httpd_req_t *req) +{ + camera_fb_t *fb = NULL; + esp_err_t res = ESP_OK; + size_t _jpg_buf_len; + uint8_t *_jpg_buf; + char *part_buf[64]; + static int64_t last_frame = 0; + if (!last_frame) { + last_frame = esp_timer_get_time(); + } + + res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); + if (res != ESP_OK) { + return res; + } + + while (true) { + fb = cam->cam_fb_get(); + if (!fb) { + ESP_LOGE(TAG, "Camera capture failed"); + res = ESP_FAIL; + break; + } + if (fb->format != PIXFORMAT_JPEG) { + bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + ESP_LOGE(TAG, "JPEG compression failed"); + esp_camera_fb_return(fb); + res = ESP_FAIL; + } + } else { + _jpg_buf_len = fb->len; + _jpg_buf = fb->buf; + } + + if (res == ESP_OK) { + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); + } + if (res == ESP_OK) { + size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len); + + res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen); + } + if (res == ESP_OK) { + res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len); + } + if (fb->format != PIXFORMAT_JPEG) { + free(_jpg_buf); + } + esp_camera_fb_return(fb); + if (res != ESP_OK) { + break; + } + int64_t fr_end = esp_timer_get_time(); + int64_t frame_time = fr_end - last_frame; + last_frame = fr_end; + frame_time /= 1000; + ESP_LOGI(TAG, + "MJPG: %luKB %lums (%.1ffps)", + (uint32_t)(_jpg_buf_len / 1024), + (uint32_t)frame_time, + 1000.0 / (uint32_t)frame_time); + } + + last_frame = 0; + return res; +} + +static const httpd_uri_t jpeg_stream_uri = { + .uri = "/stream", .method = HTTP_GET, .handler = jpg_stream_httpd_handler, .user_ctx = NULL}; + +// /* An HTTP_ANY handler */ +// static esp_err_t any_handler(httpd_req_t *req) +// { +// /* Send response with body set as the +// * string passed in user context*/ +// const char *resp_str = (const char *)req->user_ctx; +// httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN); + +// // End response +// httpd_resp_send_chunk(req, NULL, 0); +// return ESP_OK; +// } + +// static const httpd_uri_t any = {.uri = "/any", +// .method = HTTP_ANY, +// .handler = any_handler, +// /* Let's pass response string in user +// * context to demonstrate it's usage */ +// .user_ctx = "Hello World!"}; + +/* This handler allows the custom error handling functionality to be + * tested from client side. For that, when a PUT request 0 is sent to + * URI /ctrl, the /hello and /echo URIs are unregistered and following + * custom error handler http_404_error_handler() is registered. + * Afterwards, when /hello or /echo is requested, this custom error + * handler is invoked which, after sending an error message to client, + * either closes the underlying socket (when requested URI is /echo) + * or keeps it open (when requested URI is /hello). This allows the + * client to infer if the custom error handler is functioning as expected + * by observing the socket state. + */ +esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err) +{ + if (strcmp("/hello", req->uri) == 0) { + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/hello URI is not available"); + /* Return ESP_OK to keep underlying socket open */ + return ESP_OK; + } else if (strcmp("/echo", req->uri) == 0) { + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/echo URI is not available"); + /* Return ESP_FAIL to close underlying socket */ + return ESP_FAIL; + } + /* For any other URI send 404 and close socket */ + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message"); + return ESP_FAIL; +} + +/* An HTTP PUT handler. This demonstrates realtime + * registration and deregistration of URI handlers + */ +static esp_err_t ctrl_put_handler(httpd_req_t *req) +{ + char buf; + int ret; + + if ((ret = httpd_req_recv(req, &buf, 1)) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } + return ESP_FAIL; + } + + if (buf == '0') { + /* URI handlers can be unregistered using the uri string */ + ESP_LOGI(TAG, "Unregistering /hello and /echo URIs"); + // httpd_unregister_uri(req->handle, "/hello"); + httpd_unregister_uri(req->handle, "/echo"); + /* Register the custom error handler */ + httpd_register_err_handler(req->handle, HTTPD_404_NOT_FOUND, http_404_error_handler); + } else { + ESP_LOGI(TAG, "Registering /hello and /echo URIs"); + // httpd_register_uri_handler(req->handle, &hello); + httpd_register_uri_handler(req->handle, &echo); + /* Unregister custom error handler */ + httpd_register_err_handler(req->handle, HTTPD_404_NOT_FOUND, NULL); + } + + /* Respond with empty body */ + httpd_resp_send(req, NULL, 0); + return ESP_OK; +} + +static const httpd_uri_t ctrl = {.uri = "/ctrl", .method = HTTP_PUT, .handler = ctrl_put_handler, .user_ctx = NULL}; + +static httpd_handle_t start_webserver(void) +{ + httpd_handle_t server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + // Setting port as 8001 when building for Linux. Port 80 can be used only by a privileged user in linux. + // So when a unprivileged user tries to run the application, it throws bind error and the server is not started. + // Port 8001 can be used by an unprivileged user as well. So the application will not throw bind error and the + // server will be started. + config.server_port = 8001; + config.lru_purge_enable = true; + + // Start the httpd server + ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); + if (httpd_start(&server, &config) == ESP_OK) { + // Set URI handlers + ESP_LOGI(TAG, "Registering URI handlers"); + // httpd_register_uri_handler(server, &hello); + httpd_register_uri_handler(server, &echo); + httpd_register_uri_handler(server, &ctrl); + // httpd_register_uri_handler(server, &any); + httpd_register_uri_handler(server, &jpeg_stream_uri); +#if CONFIG_EXAMPLE_BASIC_AUTH + httpd_register_basic_auth(server); +#endif + return server; + } + + ESP_LOGI(TAG, "Error starting server!"); + return NULL; +} + +static esp_err_t stop_webserver(httpd_handle_t server) +{ + // Stop the httpd server + return httpd_stop(server); +} + + + +void wifi_init_sta(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + + ESP_ERROR_CHECK( + esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); + ESP_ERROR_CHECK( + esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); + + // wifi_config_t wifi_config = { + // .sta = + // { + // .ssid = EXAMPLE_ESP_WIFI_SSID, + // .password = EXAMPLE_ESP_WIFI_PASS, + // /* Authmode threshold resets to WPA2 as default if password matches WPA2 standards (password len => + // 8). + // * If you want to connect the device to deprecated WEP/WPA networks, Please set the threshold value + // * to WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK and set the password with length and format matching to + // * WIFI_AUTH_WEP/WIFI_AUTH_WPA_PSK standards. + // */ + // .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, + // .sae_pwe_h2e = ESP_WIFI_SAE_MODE, + // .sae_h2e_identifier = EXAMPLE_H2E_IDENTIFIER, + // }, + // }; + wifi_config_t wifi_config = {}; + strncpy((char *)wifi_config.sta.ssid, EXAMPLE_ESP_WIFI_SSID, sizeof(wifi_config.sta.ssid)); + strncpy((char *)wifi_config.sta.password, EXAMPLE_ESP_WIFI_PASS, sizeof(wifi_config.sta.password)); + wifi_config.sta.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD; + wifi_config.sta.sae_pwe_h2e = ESP_WIFI_SAE_MODE; + strncpy( + (char *)wifi_config.sta.sae_h2e_identifier, EXAMPLE_H2E_IDENTIFIER, sizeof(wifi_config.sta.sae_h2e_identifier)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = + xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } +} + +extern "C" void app_main(void) +{ +#if CONFIG_DB_FATFS_FLASH + ESP_ERROR_CHECK(fatfs_flash_mount()); +#elif CONFIG_DB_SPIFFS + ESP_ERROR_CHECK(bsp_spiffs_mount()); +#endif +#if CONFIG_DB_FATFS_SDCARD || CONFIG_HUMAN_FACE_DETECT_MODEL_IN_SDCARD || CONFIG_HUMAN_FACE_FEAT_MODEL_IN_SDCARD + ESP_ERROR_CHECK(bsp_sdcard_mount()); +#endif + +#if CONFIG_IDF_TARGET_ESP32S3 + ESP_ERROR_CHECK(bsp_leds_init()); + ESP_ERROR_CHECK(bsp_led_set(BSP_LED_GREEN, false)); +#endif + +#if CONFIG_IDF_TARGET_ESP32P4 + auto cam = new WhoP4Cam(VIDEO_PIX_FMT_RGB565, 3, V4L2_MEMORY_USERPTR, true); + // auto cam = new WhoP4PPACam(VIDEO_PIX_FMT_RGB565, 4, V4L2_MEMORY_USERPTR, 160, 120, true); +#elif CONFIG_IDF_TARGET_ESP32S3 + cam = new WhoS3Cam(PIXFORMAT_RGB565, FRAMESIZE_240X240, 2, true); +#endif + // auto lcd = new WhoLCD(); + + auto recognition = new WhoRecognitionApp(); + // cam->cam_fb_get() + recognition->set_cam(cam); + // recognition->set_lcd(lcd); + recognition->run(); + + // =============================================== + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + // ESP_ERROR_CHECK(example_connect()); + + wifi_init_sta(); + + /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected, + * and re-start it upon connection. + */ + // ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server)); + // ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, + // &server)); + + /* Start the server for the first time */ + server = start_webserver(); + if (server == NULL) { + ESP_LOGE(TAG, "Failed to start server!"); + return; + } +} + +// ===================================================================================== + +// static constexpr const char* TAG = "httpd_jpg_stream"; + +// ===================================================================================== +/* The examples use WiFi configuration that you can set via project configuration menu + + If you'd rather not, just change the below entries to strings with + the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid" +*/ + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(TAG, "connect to the AP fail"); + + if (server) { + ESP_LOGI(TAG, "Stopping webserver"); + if (stop_webserver(server) == ESP_OK) { + server = NULL; + } else { + ESP_LOGE(TAG, "Failed to stop http server"); + } + } + + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + + if (server == NULL) { + ESP_LOGI(TAG, "Starting webserver"); + server = start_webserver(); + } + } +} diff --git a/examples/human_face_recognition_mqtt/main/idf_component.yml b/examples/human_face_recognition_mqtt/main/idf_component.yml new file mode 100644 index 00000000..cb09392e --- /dev/null +++ b/examples/human_face_recognition_mqtt/main/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_lvgl_port: '*' + espressif/esp-dl: ^3.1.2 diff --git a/examples/human_face_recognition_mqtt/partitions.csv b/examples/human_face_recognition_mqtt/partitions.csv new file mode 100644 index 00000000..25efea83 --- /dev/null +++ b/examples/human_face_recognition_mqtt/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + +nvs, data, nvs, 0x9000, 24K, +phy_init, data, phy, 0xf000, 4K, +factory, app, factory, 0x010000, 7000K, +storage, data, fat, , 1M, diff --git a/examples/human_face_recognition_mqtt/partitions2.csv b/examples/human_face_recognition_mqtt/partitions2.csv new file mode 100644 index 00000000..ec4a2c67 --- /dev/null +++ b/examples/human_face_recognition_mqtt/partitions2.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + +nvs, data, nvs, 0x9000, 24K, +phy_init, data, phy, 0xf000, 4K, +factory, app, factory, 0x010000, 1900K, +human_face_det, data, spiffs, , 200K, +human_face_feat, data, spiffs, , 5000K, +storage, data, fat, , 1M, diff --git a/examples/human_face_recognition_mqtt/sdkconfig.defaults b/examples/human_face_recognition_mqtt/sdkconfig.defaults new file mode 100644 index 00000000..e69de29b diff --git a/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32p4 b/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32p4 new file mode 100644 index 00000000..d6d193c3 --- /dev/null +++ b/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32p4 @@ -0,0 +1,27 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32p4" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_CACHE_L2_CACHE_256KB=y +CONFIG_CACHE_L2_CACHE_LINE_128B=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=n +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64=y +CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_LCD_DPI_BUFFER_NUMS=2 +CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR=y +CONFIG_CAMERA_SC2336=y +CONFIG_CAMERA_SC2336_MIPI_RAW8_1024x600_30FPS=y +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32s3 b/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32s3 new file mode 100644 index 00000000..7718f4c2 --- /dev/null +++ b/examples/human_face_recognition_mqtt/sdkconfig.defaults.esp32s3 @@ -0,0 +1,24 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=n +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64=y +CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL=y diff --git a/examples/human_face_recognition_mqtt_simple/CMakeLists.txt b/examples/human_face_recognition_mqtt_simple/CMakeLists.txt new file mode 100644 index 00000000..6ee262d1 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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 ../../components/who_peripherals/who_cam + ../../components/who_peripherals/who_lcd + ../../components/who_peripherals/who_spiflash_fatfs + ../../components/who_task + ../../components/who_frame_cap + ../../components/who_detect + ../../components/who_recognition + ../../components/who_app/who_recognition_app) + +add_compile_options(-fdiagnostics-color=always) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(DEPENDENCIES_LOCK dependencies.lock.${IDF_TARGET}) +project(human_face_recognition) diff --git a/examples/human_face_recognition_mqtt_simple/README.md b/examples/human_face_recognition_mqtt_simple/README.md new file mode 100644 index 00000000..91bceb99 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/README.md @@ -0,0 +1,28 @@ +| Supported Targets | ESP32-P4 | +| ----------------- | -------- | + + +# Human Face Recognition Example + +### Interaction + +#### ESP-S3-EYE + +Physical buttons + +| btn | opertion | +|------|------------------| +| play | recognize | +| up | enroll | +| down | delete last feat | + +#### ESP32-P4-Function-EV-Board + +Touch screen buttons + +| btn | opertion | +|-----------|------------------| +| recognize | recognize | +| enroll | enroll | +| delete | delete last feat | + diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/.component_hash b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/.component_hash new file mode 100644 index 00000000..d66b9ffc --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/.component_hash @@ -0,0 +1 @@ +270a15b503a9977bf394dd5cc659f12adc05663bb9ac24bbc104c8a95cb58fe9 \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/CMakeLists.txt b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/CMakeLists.txt new file mode 100644 index 00000000..ec8a5ce1 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register( + SRCS "esp32_s3_camera.c" "esp32_s3_camera_idf5.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES esp_driver_gpio esp_driver_sdmmc spiffs esp_driver_i2c fatfs + PRIV_REQUIRES esp_lcd esp_driver_ledc esp_driver_spi +) diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/Kconfig b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/Kconfig new file mode 100644 index 00000000..1e0be15d --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/Kconfig @@ -0,0 +1,111 @@ +menu "Board Support Package" + + config BSP_ERROR_CHECK + bool "Enable error check in BSP" + default y + help + Error check assert the application before returning the error code. + + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 1 + range 0 1 + help + ESP32-S3 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Maximum files that could be open at the same time" + default 2 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + + menu "uSD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format uSD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the uSD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "uSD card mount point" + default "/sdcard" + help + Mount point of the uSD card in the Virtual File System + + endmenu + menu "Display" + config BSP_DISPLAY_LVGL_TASK_PRIORITY + int "LVGL task priority" + default 2 + help + The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler(). + + config BSP_DISPLAY_LVGL_TICK + int "LVGL tick period" + default 5 + range 1 100 + help + Period of LVGL tick timer. + + config BSP_DISPLAY_LVGL_MAX_SLEEP + int "LVGL maximum sleep time in ms" + default 1 + range 1 500 + help + Maximum time for task sleep in ms. + + config BSP_DISPLAY_LVGL_BUFFER_IN_PSRAM + bool "Allocate LVGL buffer in PSRAM " + default n + help + LVGL buffer will be allocated in PSRAM instead of internal RAM with DMA. + + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH + int "LEDC channel index" + default 1 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls display brightness. + Set LEDC index that should be used. + endmenu + + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 1 + help + ESP32-S3 has two I2S peripherals, pick the one you want to use. +endmenu diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/LICENSE b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/README.md b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/README.md new file mode 100644 index 00000000..35f012d9 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/README.md @@ -0,0 +1,36 @@ +> :warning: This is **No Graphical Library version** of esp32_s3_eye BSP. If you want to use this BSP with LVGL use [esp32_s3_eye](https://components.espressif.com/components/espressif/esp32_s3_eye) component. +# BSP: ESP32-S3-EYE + +[![Component Registry](https://components.espressif.com/components/espressif/esp32_s3_eye/badge.svg)](https://components.espressif.com/components/espressif/esp32_s3_eye) + +### Overview + +The ESP32-S3-EYE board consists of two parts: the main board (ESP32-S3-EYE-MB) that integrates the ESP32-S3-WROOM-1 module, camera, uSD card slot, digital microphone, USB port, and function buttons; and the sub board (ESP32-S3-EYE-SUB) that contains an LCD display. The main board and sub board are connected through pin headers. + +* [Hardware Reference](https://www.espressif.com/en/products/devkits/esp-eye/overview.) + +![](https://raw.githubusercontent.com/espressif/esp-who/master/docs/_static/get-started/ESP32-S3-EYE_MB-annotated-photo.png) + +**The development board has the following features:** +* ESP32-S3 module with built-in 8 MB flash and 8 MB octal RAM +* 1.3-inch 240x240 LCD color screen +* Onboard uSD card slot +* Digital microphone +* Accelerometer +* OV2640 camera + + +### Capabilities and dependencies +| Capability | Available | Component |Version| +|-------------|------------------|----------------------------------------------------------------------------------------------|-------| +| DISPLAY |:heavy_check_mark:| idf | >=5.4 | +| LVGL_PORT |:heavy_check_mark:|[espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port)| ^2 | +| TOUCH | :x: | | | +| BUTTONS |:heavy_check_mark:| [espressif/button](https://components.espressif.com/components/espressif/button) | ^4 | +| AUDIO |:heavy_check_mark:|[espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev)| ~1.3.1| +|AUDIO_SPEAKER| :x: | | | +| AUDIO_MIC |:heavy_check_mark:| | | +| SDCARD |:heavy_check_mark:| idf | >=5.4 | +| IMU |:heavy_check_mark:| [qma6100p](https://components.espressif.com/components/qma6100p) | ^2 | +| CAMERA |:heavy_check_mark:| [espressif/esp32-camera](https://components.espressif.com/components/espressif/esp32-camera) |^2.0.13| + diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera.c b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera.c new file mode 100644 index 00000000..c65edb69 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera.c @@ -0,0 +1,573 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_vfs_fat.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_vfs_fat.h" +#include "esp_spiffs.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "driver/spi_master.h" +#include "driver/ledc.h" +#include "driver/i2c_master.h" + +#include "bsp/esp32_s3_camera.h" +#include "bsp_err_check.h" +#include "bsp/display.h" +#include "button_gpio.h" +#include "button_adc.h" + +static const char *TAG = "S3-EYE"; + +/** + * @brief I2C handle for BSP usage + * + * In IDF v5.4 you can call i2c_master_get_bus_handle(BSP_I2C_NUM, i2c_master_bus_handle_t *ret_handle) + * from #include "esp_private/i2c_platform.h" to get this handle + * + * For IDF 5.2 and 5.3 you must call bsp_i2c_get_handle() + */ +static i2c_master_bus_handle_t i2c_handle = NULL; +static bool i2c_initialized = false; +static adc_oneshot_unit_handle_t bsp_adc_handle = NULL; +static sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler + +typedef enum { + BSP_BUTTON_TYPE_GPIO, + BSP_BUTTON_TYPE_ADC +} bsp_button_type_t; + +typedef struct { + bsp_button_type_t type; + union { + button_gpio_config_t gpio; + button_adc_config_t adc; + } cfg; +} bsp_button_config_t; + +static const bsp_button_config_t bsp_button_config[BSP_BUTTON_NUM] = { + // { + // .type = BSP_BUTTON_TYPE_ADC, + // .cfg.adc = { + // .adc_handle = &bsp_adc_handle, + // .adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1 + // .button_index = BSP_BUTTON_MENU, + // .min = 2310, // middle is 2410mV + // .max = 2510 + // } + // }, + // { + // .type = BSP_BUTTON_TYPE_ADC, + // .cfg.adc = { + // .adc_handle = &bsp_adc_handle, + // .adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1 + // .button_index = BSP_BUTTON_PLAY, + // .min = 1880, // middle is 1980mV + // .max = 2080 + // } + // }, + // { + // .type = BSP_BUTTON_TYPE_ADC, + // .cfg.adc = { + // .adc_handle = &bsp_adc_handle, + // .adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1 + // .button_index = BSP_BUTTON_DOWN, + // .min = 720, // middle is 820mV + // .max = 920 + // } + // }, + // { + // .type = BSP_BUTTON_TYPE_ADC, + // .cfg.adc = { + // .adc_handle = &bsp_adc_handle, + // .adc_channel = ADC_CHANNEL_0, // ADC1 channel 0 is GPIO1 + // .button_index = BSP_BUTTON_UP, + // .min = 280, // middle is 380mV + // .max = 480 + // } + // }, + { + .type = BSP_BUTTON_TYPE_GPIO, + .cfg.gpio = { + .active_level = 0, + .gpio_num = GPIO_NUM_1 + } + }, + { + .type = BSP_BUTTON_TYPE_GPIO, + .cfg.gpio = { + .active_level = 0, + .gpio_num = GPIO_NUM_2 + } + }, + { + .type = BSP_BUTTON_TYPE_GPIO, + .cfg.gpio = { + .active_level = 0, + .gpio_num = BSP_BUTTON_BOOT_IO + } + } +}; + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_master_bus_config_t i2c_config = { + .i2c_port = BSP_I2C_NUM, + .sda_io_num = BSP_I2C_SDA, + .scl_io_num = BSP_I2C_SCL, + .clk_source = I2C_CLK_SRC_DEFAULT, + .flags.enable_internal_pullup = true, + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_config, &i2c_handle)); + + i2c_initialized = true; + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle)); + i2c_initialized = false; + return ESP_OK; +} + +i2c_master_bus_handle_t bsp_i2c_get_handle(void) +{ + bsp_i2c_init(); + return i2c_handle; +} + +esp_err_t bsp_spiffs_mount(void) +{ + esp_vfs_spiffs_conf_t conf = { + .base_path = CONFIG_BSP_SPIFFS_MOUNT_POINT, + .partition_label = CONFIG_BSP_SPIFFS_PARTITION_LABEL, + .max_files = CONFIG_BSP_SPIFFS_MAX_FILES, +#ifdef CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + }; + + esp_err_t ret_val = esp_vfs_spiffs_register(&conf); + + BSP_ERROR_CHECK_RETURN_ERR(ret_val); + + size_t total = 0, used = 0; + ret_val = esp_spiffs_info(conf.partition_label, &total, &used); + if (ret_val != ESP_OK) { + ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret_val)); + } else { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + return ret_val; +} + +esp_err_t bsp_spiffs_unmount(void) +{ + return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); +} + +sdmmc_card_t *bsp_sdcard_get_handle(void) +{ + return bsp_sdcard; +} + +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config) +{ + assert(config); + + sdmmc_host_t host_config = SDMMC_HOST_DEFAULT(); + + memcpy(config, &host_config, sizeof(sdmmc_host_t)); +} + +void bsp_sdcard_get_sdspi_host(const int slot, sdmmc_host_t *config) +{ + assert(config); + memset(config, 0, sizeof(sdmmc_host_t)); + ESP_LOGE(TAG, "SD card SPI mode is not supported by HW!"); +} + +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config) +{ + assert(config); + memset(config, 0, sizeof(sdmmc_slot_config_t)); + + config->clk = BSP_SD_CLK; + config->cmd = BSP_SD_CMD; + config->d0 = BSP_SD_D0; + config->d1 = GPIO_NUM_NC; + config->d2 = GPIO_NUM_NC; + config->d3 = GPIO_NUM_NC; + config->d4 = GPIO_NUM_NC; + config->d5 = GPIO_NUM_NC; + config->d6 = GPIO_NUM_NC; + config->d7 = GPIO_NUM_NC; + config->cd = SDMMC_SLOT_NO_CD; + config->wp = SDMMC_SLOT_NO_WP; + config->width = 1; + config->flags = SDMMC_SLOT_FLAG_INTERNAL_PULLUP; +} + +void bsp_sdcard_sdspi_get_slot(const spi_host_device_t spi_host, sdspi_device_config_t *config) +{ + assert(config); + memset(config, 0, sizeof(sdspi_device_config_t)); + ESP_LOGE(TAG, "SD card SPI mode is not supported by HW!"); +} + +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg) +{ + sdmmc_host_t sdhost = {0}; + sdmmc_slot_config_t sdslot = {0}; + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 16 * 1024 + }; + assert(cfg); + + if (!cfg->mount) { + cfg->mount = &mount_config; + } + + if (!cfg->host) { + bsp_sdcard_get_sdmmc_host(SDMMC_HOST_SLOT_0, &sdhost); + cfg->host = &sdhost; + } + + if (!cfg->slot.sdmmc) { + bsp_sdcard_sdmmc_get_slot(SDMMC_HOST_SLOT_0, &sdslot); + cfg->slot.sdmmc = &sdslot; + } + +#if !CONFIG_FATFS_LONG_FILENAMES + ESP_LOGW(TAG, "Warning: Long filenames on SD card are disabled in menuconfig!"); +#endif + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, cfg->host, cfg->slot.sdmmc, cfg->mount, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_sdspi_mount(bsp_sdcard_cfg_t *cfg) +{ + ESP_LOGE(TAG, "SD card SPI mode is not supported by HW!"); + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t bsp_sdcard_mount(void) +{ + bsp_sdcard_cfg_t cfg = {0}; + return bsp_sdcard_sdmmc_mount(&cfg); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + esp_err_t ret = ESP_OK; + + ret |= esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); + bsp_sdcard = NULL; + + return ret; +} + +#define LCD_CMD_BITS (8) +#define LCD_PARAM_BITS (8) +#define LCD_LEDC_CH (CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH) +#define LVGL_TICK_PERIOD_MS (CONFIG_BSP_DISPLAY_LVGL_TICK) +#define LVGL_MAX_SLEEP_MS (CONFIG_BSP_DISPLAY_LVGL_MAX_SLEEP) + +esp_err_t bsp_display_brightness_init(void) +{ + // Setup LEDC peripheral for PWM backlight control + const ledc_channel_config_t LCD_backlight_channel = { + .gpio_num = BSP_LCD_BACKLIGHT, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LCD_LEDC_CH, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = 1, + .duty = 0, + .hpoint = 0, + .flags.output_invert = true + }; + const ledc_timer_config_t LCD_backlight_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_num = 1, + .freq_hz = 5000, + .clk_cfg = LEDC_AUTO_CLK + }; + + BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_config(&LCD_backlight_timer)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_channel_config(&LCD_backlight_channel)); + + return ESP_OK; +} + +esp_err_t bsp_display_brightness_set(int brightness_percent) +{ + if (brightness_percent > 100) { + brightness_percent = 100; + } else if (brightness_percent < 0) { + brightness_percent = 0; + } + + ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent); + // LEDC resolution set to 10bits, thus: 100% = 1023 + uint32_t duty_cycle = (1023 * brightness_percent) / 100; + BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); + + return ESP_OK; +} + +esp_err_t bsp_display_backlight_off(void) +{ + return bsp_display_brightness_set(0); +} + +esp_err_t bsp_display_backlight_on(void) +{ + return bsp_display_brightness_set(100); +} + +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_err_t ret = ESP_OK; + assert(config != NULL && config->max_transfer_sz > 0); + + ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed"); + + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = BSP_LCD_SPI_CLK, + .mosi_io_num = BSP_LCD_SPI_MOSI, + .miso_io_num = GPIO_NUM_NC, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = config->max_transfer_sz, + }; + ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed"); + + ESP_LOGD(TAG, "Install panel IO"); + const esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = BSP_LCD_DC, + .cs_gpio_num = BSP_LCD_SPI_CS, + .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = LCD_CMD_BITS, + .lcd_param_bits = LCD_PARAM_BITS, + .spi_mode = 2, + .trans_queue_depth = 10, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, ret_io), err, TAG, "New panel IO failed"); + + ESP_LOGD(TAG, "Install LCD driver"); + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = BSP_LCD_RST, + .color_space = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = BSP_LCD_BITS_PER_PIXEL, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(*ret_io, &panel_config, ret_panel), err, TAG, "New panel failed"); + + + esp_lcd_panel_reset(*ret_panel); + esp_lcd_panel_init(*ret_panel); + esp_lcd_panel_invert_color(*ret_panel, true); + return ret; + +err: + if (*ret_panel) { + esp_lcd_panel_del(*ret_panel); + } + if (*ret_io) { + esp_lcd_panel_io_del(*ret_io); + } + spi_bus_free(BSP_LCD_SPI_NUM); + return ret; +} + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = BSP_LCD_DRAW_BUFF_SIZE * sizeof(uint16_t), + }; + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle)); + + esp_lcd_panel_disp_on_off(panel_handle, true); + + /* Add LCD screen */ + ESP_LOGD(TAG, "Add LCD screen"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = cfg->buffer_size, + .double_buffer = cfg->double_buffer, + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + .monochrome = false, + /* Rotation values must be same as used in esp_lcd for initial settings of the screen */ + .rotation = { + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + }, + .flags = { + .buff_dma = cfg->flags.buff_dma, + .buff_spiram = cfg->flags.buff_spiram, +#if LVGL_VERSION_MAJOR >= 9 + .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false), +#endif + } + }; + + return lvgl_port_add_disp(&disp_cfg); +} + +lv_display_t *bsp_display_start(void) +{ + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = { + .task_priority = CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY, + .task_stack = 6144, + .task_affinity = 1, + .timer_period_ms = LVGL_TICK_PERIOD_MS, + .task_max_sleep_ms = LVGL_MAX_SLEEP_MS, + }, + .buffer_size = BSP_LCD_DRAW_BUFF_SIZE, + .double_buffer = BSP_LCD_DRAW_BUFF_DOUBLE, + .flags = { +#if (CONFIG_BSP_DISPLAY_LVGL_BUFFER_IN_PSRAM && CONFIG_SPIRAM) + .buff_dma = false, + .buff_spiram = true, +#else + .buff_dma = true, + .buff_spiram = false, +#endif + } + }; + return bsp_display_start_with_config(&cfg); +} + +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) +{ + lv_display_t *disp; + assert(cfg != NULL); + BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg)); + BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL); + + return disp; +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return NULL; +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + lvgl_port_unlock(); +} + +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation) +{ + lv_disp_set_rotation(disp, rotation); +} +#endif // (BSP_CONFIG_NO_GRAPHIC_LIB == 0) + +esp_err_t bsp_leds_init(void) +{ + const gpio_config_t led_io_config = { + .pin_bit_mask = BIT64(BSP_LED_GREEN), + .mode = GPIO_MODE_OUTPUT_OD, // GPIO3 is connected directly to the LED (on board revision 2.1), so we use Open-drain here + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + BSP_ERROR_CHECK_RETURN_ERR(gpio_config(&led_io_config)); + return ESP_OK; +} + +esp_err_t bsp_led_set(const bsp_led_t led_io, const bool on) +{ + BSP_ERROR_CHECK_RETURN_ERR(gpio_set_level((gpio_num_t) led_io, (uint32_t) on)); + return ESP_OK; +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + const audio_codec_data_if_t *i2s_data_if = bsp_audio_get_codec_itf(); + if (i2s_data_if == NULL) { + /* Configure I2S peripheral and Power Amplifier */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_audio_init(NULL)); + i2s_data_if = bsp_audio_get_codec_itf(); + } + assert(i2s_data_if); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = NULL, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size) +{ + esp_err_t ret = ESP_OK; + const button_config_t btn_config = {0}; + if ((btn_array_size < BSP_BUTTON_NUM) || + (btn_array == NULL)) { + return ESP_ERR_INVALID_ARG; + } + /* Initialize ADC and get ADC handle */ + BSP_ERROR_CHECK_RETURN_NULL(bsp_adc_initialize()); + bsp_adc_handle = bsp_adc_get_handle(); + + if (btn_cnt) { + *btn_cnt = 0; + } + for (int i = 0; i < BSP_BUTTON_NUM; i++) { + if (bsp_button_config[i].type == BSP_BUTTON_TYPE_GPIO) { + ret |= iot_button_new_gpio_device(&btn_config, &bsp_button_config[i].cfg.gpio, &btn_array[i]); + } else if (bsp_button_config[i].type == BSP_BUTTON_TYPE_ADC) { + ret |= iot_button_new_adc_device(&btn_config, &bsp_button_config[i].cfg.adc, &btn_array[i]); + } else { + ESP_LOGW(TAG, "Unsupported button type!"); + } + if (btn_cnt) { + (*btn_cnt)++; + } + } + return ret; +} diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera_idf5.c b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera_idf5.c new file mode 100644 index 00000000..723bab07 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/esp32_s3_camera_idf5.c @@ -0,0 +1,137 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "bsp/esp32_s3_camera.h" +#include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" + +static const char *TAG = "S3-EYE"; + +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ +static adc_oneshot_unit_handle_t bsp_adc_handle = NULL; + +/* Sample rate of MSM261S4030H0 */ +#define BSP_MIC_SAMPLE_RATE (48000u) + +/* + * ESP32-S3-EYE I2S pinout + * Can be used for i2s_std_gpio_config_t and/or i2s_std_config_t initialization + */ +#define BSP_I2S_GPIO_CFG() \ + { \ + .mclk = GPIO_NUM_NC, \ + .bclk = BSP_I2S_SCLK, \ + .ws = BSP_I2S_LCLK, \ + .dout = GPIO_NUM_NC, \ + .din = BSP_I2S_DIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +/* This configuration is used by default in bsp_audio_init() */ +#define BSP_I2S_SIMPLEX_MONO_CFG() \ + { \ + .clk_cfg = { \ + .sample_rate_hz = BSP_MIC_SAMPLE_RATE, \ + .clk_src = I2S_CLK_SRC_DEFAULT, \ + .mclk_multiple = I2S_MCLK_MULTIPLE_384, \ + }, \ + .slot_cfg = { \ + .data_bit_width = I2S_DATA_BIT_WIDTH_24BIT, \ + .slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT, \ + .slot_mode = I2S_SLOT_MODE_MONO, \ + .slot_mask = I2S_STD_SLOT_LEFT, \ + .ws_width = 32, \ + .ws_pol = false, \ + .bit_shift = true, \ + .left_align = true, \ + .big_endian = false, \ + .bit_order_lsb = false, \ + }, \ + .gpio_cfg = BSP_I2S_GPIO_CFG(), \ + } + +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + BSP_ERROR_CHECK_RETURN_ERR(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S_SIMPLEX_MONO_CFG(); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + if (i2s_tx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_tx_chan), err, TAG, "I2S enabling failed"); + } + if (i2s_rx_chan != NULL) { + ESP_GOTO_ON_ERROR(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg), err, TAG, "I2S channel initialization failed"); + ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG, "I2S enabling failed"); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .rx_handle = i2s_rx_chan, + .tx_handle = i2s_tx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + BSP_NULL_CHECK_GOTO(i2s_data_if, err); + + return ESP_OK; + +err: + if (i2s_tx_chan) { + i2s_del_channel(i2s_tx_chan); + } + if (i2s_rx_chan) { + i2s_del_channel(i2s_rx_chan); + } + + return ret; +} + +const audio_codec_data_if_t *bsp_audio_get_codec_itf(void) +{ + return i2s_data_if; +} + +esp_err_t bsp_adc_initialize(void) +{ + /* ADC was initialized before */ + if (bsp_adc_handle != NULL) { + return ESP_OK; + } + + /* Initialize ADC */ + const adc_oneshot_unit_init_cfg_t init_config1 = { + .unit_id = BSP_ADC_UNIT, + }; + BSP_ERROR_CHECK_RETURN_ERR(adc_oneshot_new_unit(&init_config1, &bsp_adc_handle)); + + return ESP_OK; +} + +adc_oneshot_unit_handle_t bsp_adc_get_handle(void) +{ + return bsp_adc_handle; +} diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/idf_component.yml b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/idf_component.yml new file mode 100644 index 00000000..07dc2527 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/idf_component.yml @@ -0,0 +1,22 @@ +dependencies: + button: + public: true + version: ^4 + esp32-camera: + public: true + version: ^2.0.13 + esp_codec_dev: + public: true + version: ~1.3.1 + idf: '>=5.4' + qma6100p: + public: true + version: ^2 +description: Board Support Package (BSP) for ESP32-S3-EYE with no graphical library +repository: git://github.com/espressif/esp-bsp.git +tags: +- bsp +targets: +- esp32s3 +url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_s3_eye +version: 5.0.0 diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/config.h b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/config.h new file mode 100644 index 00000000..d90eee8e --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/config.h @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/************************************************************************************************** + * BSP configuration + **************************************************************************************************/ +// By default, this BSP is shipped with LVGL graphical library. Enabling this option will exclude it. +// If you want to use BSP without LVGL, select BSP version with 'noglib' suffix. +#if !defined(BSP_CONFIG_NO_GRAPHIC_LIB) // Check if the symbol is not coming from compiler definitions (-D...) +#define BSP_CONFIG_NO_GRAPHIC_LIB (1) +#endif diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/display.h b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/display.h new file mode 100644 index 00000000..942180e0 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/display.h @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP LCD + * + * This file offers API for basic LCD control. + * It is useful for users who want to use the LCD without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once +#include "esp_lcd_types.h" + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +/* LCD display color bytes endianess */ +#define BSP_LCD_BIGENDIAN (1) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB) +/* LCD display definition: VGA */ +#define BSP_LCD_H_RES (640) +#define BSP_LCD_V_RES (480) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ +} bsp_display_config_t; + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * spi_bus_free(spi_num_from_configuration); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_panel esp_lcd panel handle + * @param[out] ret_io esp_lcd IO handle + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Initialize display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_init(void); + +/** + * @brief Set display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @param[in] brightness_percent Brightness in [%] + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_set(int brightness_percent); + +/** + * @brief Turn on display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Brightness must be already initialized by calling bsp_display_brightness_init() or bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_off(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp-bsp.h b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp-bsp.h new file mode 100644 index 00000000..ab6d3405 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp-bsp.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "bsp/esp32_s3_eye.h" diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp32_s3_camera.h b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp32_s3_camera.h new file mode 100644 index 00000000..08d16669 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/include/bsp/esp32_s3_camera.h @@ -0,0 +1,574 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief ESP BSP: S3-EYE + */ + +#pragma once + +#include "sdkconfig.h" +#include "driver/gpio.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "esp_vfs_fat.h" +#include "esp_codec_dev.h" +#include "iot_button.h" +#include "bsp/config.h" +#include "bsp/display.h" +#include "driver/i2s_std.h" +#include "driver/i2c_master.h" +#include "esp_adc/adc_oneshot.h" + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +#include "lvgl.h" +#include "esp_lvgl_port.h" +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +#define BSP_CAPS_DISPLAY 1 +#define BSP_CAPS_TOUCH 0 +#define BSP_CAPS_BUTTONS 1 +#define BSP_CAPS_AUDIO 1 +#define BSP_CAPS_AUDIO_SPEAKER 0 +#define BSP_CAPS_AUDIO_MIC 1 +#define BSP_CAPS_SDCARD 1 +#define BSP_CAPS_IMU 1 +#define BSP_CAPS_CAMERA 1 + +/************************************************************************************************** + * ESP32-S3-EYE pinout + **************************************************************************************************/ + +/* I2C */ +#define BSP_I2C_SCL (GPIO_NUM_5) +#define BSP_I2C_SDA (GPIO_NUM_4) + +/* Audio */ +#define BSP_I2S_SCLK (GPIO_NUM_41) +#define BSP_I2S_LCLK (GPIO_NUM_42) +#define BSP_I2S_DIN (GPIO_NUM_2) + +/* Display */ +#define BSP_LCD_SPI_MOSI (GPIO_NUM_47) +#define BSP_LCD_SPI_CLK (GPIO_NUM_21) +#define BSP_LCD_SPI_CS (GPIO_NUM_44) +#define BSP_LCD_DC (GPIO_NUM_43) +#define BSP_LCD_RST (GPIO_NUM_NC) +#define BSP_LCD_BACKLIGHT (GPIO_NUM_48) + +/* Camera */ +#define BSP_CAMERA_XCLK (GPIO_NUM_15) +#define BSP_CAMERA_PCLK (GPIO_NUM_13) +#define BSP_CAMERA_VSYNC (GPIO_NUM_6) +#define BSP_CAMERA_HSYNC (GPIO_NUM_7) +#define BSP_CAMERA_D0 (GPIO_NUM_11) +#define BSP_CAMERA_D1 (GPIO_NUM_9) +#define BSP_CAMERA_D2 (GPIO_NUM_8) +#define BSP_CAMERA_D3 (GPIO_NUM_10) +#define BSP_CAMERA_D4 (GPIO_NUM_12) +#define BSP_CAMERA_D5 (GPIO_NUM_18) +#define BSP_CAMERA_D6 (GPIO_NUM_17) +#define BSP_CAMERA_D7 (GPIO_NUM_16) + +/* uSD card MMC */ +#define BSP_SD_D0 (GPIO_NUM_40) +#define BSP_SD_CMD (GPIO_NUM_38) +#define BSP_SD_CLK (GPIO_NUM_39) + +/* Buttons */ +#define BSP_BUTTON_BOOT_IO (GPIO_NUM_0) +#define BSP_BUTTONS_IO (GPIO_NUM_1) // All 4 buttons mapped to this GPIO +typedef enum bsp_led_t { + BSP_LED_GREEN = GPIO_NUM_3, +} bsp_led_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************************************** + * + * Buttons interface + * + * Example configuration: + * \code{.c} + * button_handle_t btns[BSP_BUTTON_NUM]; + * bsp_iot_button_create(btns, NULL, BSP_BUTTON_NUM); + * iot_button_register_cb(btns[0], ... + * \endcode + **************************************************************************************************/ +typedef enum { + BSP_BUTTON_MENU = 0, + BSP_BUTTON_PLAY, + // BSP_BUTTON_DOWN, + // BSP_BUTTON_UP, + BSP_BUTTON_BOOT, + BSP_BUTTON_NUM +} bsp_button_t; + +/** + * @brief Initialize all buttons + * + * Returned button handlers must be used with espressif/button component API + * + * @note For LCD panel button which is defined as BSP_BUTTON_MAIN, bsp_display_start should + * be called before call this function. + * + * @param[out] btn_array Output button array + * @param[out] btn_cnt Number of button handlers saved to btn_array, can be NULL + * @param[in] btn_array_size Size of output button array. Must be at least BSP_BUTTON_NUM + * @return + * - ESP_OK All buttons initialized + * - ESP_ERR_INVALID_ARG btn_array is too small or NULL + * - ESP_FAIL Underlaying iot_button_create failed + */ +esp_err_t bsp_iot_button_create(button_handle_t btn_array[], int *btn_cnt, int btn_array_size); + +/************************************************************************************************** + * + * I2C interface + * + * There are two devices connected to I2C peripheral: + * - QMA7981 or QMA6100P Inertial measurement unit + * - OV2640 Camera module + **************************************************************************************************/ +#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM + +/** + * @brief Init I2C driver + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_i2c_init(void); + +/** + * @brief Deinit I2C driver and free its resources + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * + */ +esp_err_t bsp_i2c_deinit(void); + +/** + * @brief Get I2C driver handle + * + * @return + * - I2C handle + */ +i2c_master_bus_handle_t bsp_i2c_get_handle(void); + +/************************************************************************************************** + * + * Camera interface + * + * ESP32-S3-EYE is shipped with OV2640 camera module. + * As a camera driver, esp32-camera component is used. + * + * Example configuration: + * \code{.c} + * const camera_config_t camera_config = BSP_CAMERA_DEFAULT_CONFIG; + * esp_err_t err = esp_camera_init(&camera_config); + * \endcode + **************************************************************************************************/ +/** + * @brief ESP32-S3-EYE camera default configuration + * + * In this configuration we select RGB565 color format and 240x240 image size - matching the display. + * We use double-buffering for the best performance. + * Since we don't want to waste internal SRAM, we allocate the framebuffers in external PSRAM. + * By setting XCLK to 16MHz, we configure the esp32-camera driver to use EDMA when accessing the PSRAM. + * + * @attention I2C must be enabled by bsp_i2c_init(), before camera is initialized + */ +#define BSP_CAMERA_DEFAULT_CONFIG \ + { \ + .pin_pwdn = GPIO_NUM_NC, \ + .pin_reset = GPIO_NUM_NC, \ + .pin_xclk = BSP_CAMERA_XCLK, \ + .pin_sccb_sda = GPIO_NUM_NC, \ + .pin_sccb_scl = GPIO_NUM_NC, \ + .pin_d7 = BSP_CAMERA_D7, \ + .pin_d6 = BSP_CAMERA_D6, \ + .pin_d5 = BSP_CAMERA_D5, \ + .pin_d4 = BSP_CAMERA_D4, \ + .pin_d3 = BSP_CAMERA_D3, \ + .pin_d2 = BSP_CAMERA_D2, \ + .pin_d1 = BSP_CAMERA_D1, \ + .pin_d0 = BSP_CAMERA_D0, \ + .pin_vsync = BSP_CAMERA_VSYNC, \ + .pin_href = BSP_CAMERA_HSYNC, \ + .pin_pclk = BSP_CAMERA_PCLK, \ + .xclk_freq_hz = 16000000, \ + .ledc_timer = LEDC_TIMER_0, \ + .ledc_channel = LEDC_CHANNEL_0, \ + .pixel_format = PIXFORMAT_RGB565, \ + .frame_size = FRAMESIZE_240X240, \ + .jpeg_quality = 12, \ + .fb_count = 2, \ + .fb_location = CAMERA_FB_IN_PSRAM,\ + .sccb_i2c_port = BSP_I2C_NUM, \ + } + +#define BSP_CAMERA_VFLIP 1 +#define BSP_CAMERA_HMIRROR 0 + +/************************************************************************************************** + * + * SPIFFS + * + * After mounting the SPIFFS, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_SPIFFS_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello World!\n"); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SPIFFS_MOUNT_POINT CONFIG_BSP_SPIFFS_MOUNT_POINT + +/** + * @brief Mount SPIFFS to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_spiffs_register was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes + */ +esp_err_t bsp_spiffs_mount(void); + +/** + * @brief Unmount SPIFFS from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if already unmounted + */ +esp_err_t bsp_spiffs_unmount(void); + +/************************************************************************************************** + * + * uSD card + * + * After mounting the uSD card, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name); + * fclose(f); + * \endcode + **************************************************************************************************/ +#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT +#define BSP_SDSPI_HOST (SPI3_HOST) + +typedef struct { + const esp_vfs_fat_sdmmc_mount_config_t *mount; + sdmmc_host_t *host; + union { + const sdmmc_slot_config_t *sdmmc; + const sdspi_device_config_t *sdspi; + } slot; +} bsp_sdcard_cfg_t; + +/** + * @brief Mount microSD card to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_mount(void); + +/** + * @brief Unmount microSD card from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers + */ +esp_err_t bsp_sdcard_unmount(void); + +/** + * @brief Get SD card handle + * + * @return SD card handle + */ +sdmmc_card_t *bsp_sdcard_get_handle(void); + +/** + * @brief Get SD card MMC host config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config); + +/** + * @brief Get SD card SPI host config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_get_sdspi_host(const int slot, sdmmc_host_t *config); + +/** + * @brief Get SD card MMC slot config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config); + +/** + * @brief Get SD card SPI slot config + * + * @param spi_host SPI host ID + * @param config Structure which will be filled + */ +void bsp_sdcard_sdspi_get_slot(const spi_host_device_t spi_host, sdspi_device_config_t *config); + +/** + * @brief Mount microSD card to virtual file system (MMC mode) + * + * @param cfg SD card configuration + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg); + +/** + * @brief Mount microSD card to virtual file system (SPI mode) + * + * @param cfg SD card configuration + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_sdspi_mount(bsp_sdcard_cfg_t *cfg); + +/************************************************************************************************** + * + * LCD interface + * + * ESP32-S3-EYE is shipped with 1.3inch ST7789 display controller. + * It features 16-bit colors and 240x240 resolution. + * + * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex + * by calling bsp_display_lock() before calling any LVGL API (lv_...) and then give the mutex with + * bsp_display_unlock(). + * + * If you want to use the display without LVGL, see bsp/display.h API and use BSP version with 'noglib' suffix. + **************************************************************************************************/ +#define BSP_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000) +#define BSP_LCD_SPI_NUM (SPI3_HOST) + +#if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) +#define BSP_LCD_DRAW_BUFF_SIZE (BSP_LCD_H_RES * BSP_LCD_V_RES) +#define BSP_LCD_DRAW_BUFF_DOUBLE (0) + +/** + * @brief BSP display configuration structure + */ +typedef struct { + lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */ + uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */ + bool double_buffer; /*!< True, if should be allocated two buffers */ + struct { + unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */ + unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */ + } flags; +} bsp_display_cfg_t; + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * + * @return Pointer to LVGL display or NULL when error occurred + */ +lv_display_t *bsp_display_start(void); + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @param cfg display configuration + * + * @return Pointer to LVGL display or NULL when error occurred + */ +lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg); + +/** + * @brief Get pointer to input device (touch, buttons, ...) + * + * @note The LVGL input device is initialized in bsp_display_start() function. + * + * @return Pointer to LVGL input device or NULL when not initialized + */ +lv_indev_t *bsp_display_get_input_dev(void); + +/** + * @brief Take LVGL mutex + * + * @param timeout_ms Timeout in [ms]. 0 will block indefinitely. + * @return true Mutex was taken + * @return false Mutex was NOT taken + */ +bool bsp_display_lock(uint32_t timeout_ms); + +/** + * @brief Give LVGL mutex + * + */ +void bsp_display_unlock(void); + +/** + * @brief Rotate screen + * + * Display must be already initialized by calling bsp_display_start() + * + * @param[in] disp Pointer to LVGL display + * @param[in] rotation Angle of the display rotation + */ +void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation); +#endif // BSP_CONFIG_NO_GRAPHIC_LIB == 0 + +/************************************************************************************************** + * + * LEDs + * + **************************************************************************************************/ + +/** + * @brief Set LED's GPIOs as output push-pull + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_leds_init(void); + +/** + * @brief Turn LED on/off + * + * @param led_io LED io + * @param on Switch LED on/off + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_led_set(const bsp_led_t led_io, const bool on); + +/************************************************************************************************** + * + * I2S audio interface + * + * There is only one device connected to the I2S peripheral + * - MEMSensing Microsystems MSM261S4030H0: 48kHz, 24bit mono digital microphone + * + * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init(). + * After microphone initialization, use functions from esp_codec_dev for record audio. + * Example audio play: + * \code{.c} + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + * esp_codec_dev_close(spk_codec_dev); + * \endcode + **************************************************************************************************/ + +/** + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel() + * @warning The type of i2s_config param is depending on IDF version. + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @param[out] tx_channel I2S TX channel + * @param[out] rx_channel I2S RX channel + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); + +/** + * @brief Get codec I2S interface (initialized in bsp_audio_init) + * + * @return + * - Pointer to codec I2S interface handle or NULL when error occurred + */ +const audio_codec_data_if_t *bsp_audio_get_codec_itf(void); + +/** + * @brief Initialize microphone codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); + +/************************************************************************************************** + * + * ADC interface + * + * There are multiple devices connected to ADC peripheral: + * - Buttons + * + * After initialization of ADC, use adc_handle when using ADC driver. + **************************************************************************************************/ + +#define BSP_ADC_UNIT ADC_UNIT_1 + +/** + * @brief Initialize ADC + * + * The ADC can be initialized inside BSP, when needed. + * + * @param[out] adc_handle Returned ADC handle + */ +esp_err_t bsp_adc_initialize(void); + +/** + * @brief Get ADC handle + * + * @return ADC handle + */ +adc_oneshot_unit_handle_t bsp_adc_get_handle(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/priv_include/bsp_err_check.h b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/priv_include/bsp_err_check.h new file mode 100644 index 00000000..e0019994 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/components/espressif__esp32_s3_camera/priv_include/bsp_err_check.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_check.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Assert on error, if selected in menuconfig. Otherwise return error code. */ +#if CONFIG_BSP_ERROR_CHECK +#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x) +#define BSP_NULL_CHECK(x, ret) assert(x) +#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x) +#else +#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \ + if (unlikely((x) != ESP_OK)) { \ + return NULL; \ + } \ + } while(0) + +#define BSP_NULL_CHECK(x, ret) do { \ + if ((x) == NULL) { \ + return ret; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK(x, ret) do { \ + if (unlikely((x) != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \ + if ((x) == NULL) { \ + goto goto_tag; \ + } \ + } while(0) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32p4 b/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32p4 new file mode 100644 index 00000000..d6f137aa --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32p4 @@ -0,0 +1,284 @@ +dependencies: + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 0.5.3 + espressif/esp-dl: + component_hash: f451adb8650cf7cbfd94001d07099190cc15273156fc53f748a1288b727c48cb + dependencies: + - name: espressif/esp_jpeg + registry_url: https://components.espressif.com + require: private + version: ^1.0.5~3 + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + - esp32p4 + version: 3.1.2 + espressif/esp32_p4_function_ev_board_noglib: + component_hash: 00c80e12ee9d3f056f63437c1a868ac887a8a4e9cdca95b7a08eefca0322f734 + dependencies: + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: public + version: 1.2.* + - name: espressif/esp_lcd_ek79007 + registry_url: https://components.espressif.com + require: private + version: 1.* + - name: espressif/esp_lcd_ili9881c + registry_url: https://components.espressif.com + require: private + version: 1.* + - name: espressif/esp_lcd_touch_gt911 + registry_url: https://components.espressif.com + require: private + version: ^1 + - name: espressif/esp_lcd_lt8912b + registry_url: https://components.espressif.com + require: private + version: '>=0.1.1,<1.0.0' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32p4 + version: 5.0.0 + espressif/esp_cam_sensor: + component_hash: 7b110b08542aeae833895dacd74425e926dd36eed68ae1a26f2b6109e04ea984 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_sccb_intf + registry_url: https://components.espressif.com + require: private + version: '>=0.0.2' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 0.9.0 + espressif/esp_codec_dev: + component_hash: 014948481bda426cd46714f297fe1891711246c62bea288863a8cc8cf13ef1f0 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.0 + espressif/esp_h264: + component_hash: fa5daaebc8a304f0b79bc57e51aab43e0a03eabc2ba3865bff4063c56b5d4564 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.4 + espressif/esp_ipa: + component_hash: 7547b050a99351a85aaba8ba38211c36fd4769c2ae3e570fa330dc90dfd2704c + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.4' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 0.2.0 + espressif/esp_jpeg: + component_hash: fdddaa1ceeee223e0abd7e865b05dbceea7ff175440a5ea26eeff8304564e7aa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.0 + espressif/esp_lcd_ek79007: + component_hash: 07c1afab7e9fd4dd2fd06ff9245e65327c5bbd5485efec199496e19a9304d47b + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 1.0.2 + espressif/esp_lcd_ili9881c: + component_hash: f4f374226b62baf13f735864e8fae58e17c537df34d598e059f6caad4761ef65 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + version: 1.0.1 + espressif/esp_lcd_lt8912b: + component_hash: 232578085e67a0fe1a22d58f290ba5c10959d9a247022bec561500b5661fb360 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.1.1 + espressif/esp_lcd_touch: + component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.2 + espressif/esp_lcd_touch_gt911: + component_hash: acc1c184358aa29ef72506f618c9c76a8cc2bf12af38a2bff3d44d84f3a08857 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.1.0 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.3 + espressif/esp_lvgl_port: + component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556 + dependencies: + - name: idf + require: private + version: '>=4.4' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.0 + espressif/esp_sccb_intf: + component_hash: 71e3def402f6193a23c599bdde4fa0b544ca2b1f63608b6d0ec2bee3cd9a1c33 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.0.5 + espressif/esp_video: + component_hash: 37f7a3b16aa842e612180d5a4698db1c3bc36142a0b78664eb4ffe67c8cd0510 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_cam_sensor + registry_url: https://components.espressif.com + require: private + version: 0.9.* + - name: espressif/esp_h264 + registry_url: https://components.espressif.com + require: private + version: '>=1.0.3' + - name: espressif/esp_ipa + registry_url: https://components.espressif.com + require: private + version: 0.2.* + - name: idf + require: private + version: '>=5.4' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32p4 + version: 0.8.0~3 + espressif/human_face_detect: + component_hash: cba8a6be8221aa08618e2ffb8087f732210731f1f3e6f17e025a81ae413b139f + dependencies: + - name: espressif/esp-dl + registry_url: https://components.espressif.com + require: private + version: ^3.1.1 + source: + registry_url: https://components.espressif.com + type: service + version: 0.2.2 + espressif/human_face_recognition: + component_hash: 6e158c3501d53d6fa65c2ea35194fd43dc2994d9f3b032798c544657664223b6 + dependencies: + - name: espressif/human_face_detect + registry_url: https://components.espressif.com + require: private + version: ^0.2.1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.2 + idf: + source: + type: idf + version: 5.4.0 + lvgl/lvgl: + component_hash: 096c69af22eaf8a2b721e3913da91918c5e6bf1a762a113ec01f401aa61337a0 + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 9.2.2 +direct_dependencies: +- espressif/esp-dl +- espressif/esp32_p4_function_ev_board_noglib +- espressif/esp_h264 +- espressif/esp_lvgl_port +- espressif/esp_video +- espressif/human_face_recognition +- idf +manifest_hash: 40f12dea6eb8d013bdf75319cc6425d959451fd3a01e33b855533ccc886afc4c +target: esp32p4 +version: 2.0.0 diff --git a/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32s3 b/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32s3 new file mode 100644 index 00000000..80943b08 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/dependencies.lock.esp32s3 @@ -0,0 +1,172 @@ +dependencies: + esp32_s3_camera: + dependencies: + - name: espressif/button + public: true + version: ^4 + - name: espressif/esp32-camera + public: true + version: ^2.0.13 + - name: espressif/esp_codec_dev + public: true + version: ~1.3.1 + - name: idf + version: '>=5.4' + - name: espressif/qma6100p + public: true + version: ^2 + source: + path: C:\Projects\ESPcam\esp-who\esp-who\examples\human_face_recognition_mqtt_simple\components\espressif__esp32_s3_camera + type: local + targets: + - esp32s3 + version: 5.0.0 + espressif/button: + component_hash: f53face2ab21fa0ffaf4cf0f6e513d393f56df6586bb2ad1146120f03f19ee05 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.1.3 + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 0.5.3 + espressif/esp-dl: + component_hash: f451adb8650cf7cbfd94001d07099190cc15273156fc53f748a1288b727c48cb + dependencies: + - name: espressif/esp_jpeg + registry_url: https://components.espressif.com + require: private + version: ^1.0.5~3 + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + - esp32p4 + version: 3.1.2 + espressif/esp32-camera: + component_hash: c3eb05fbeeae884a23bed9b17d48d5f0da8872beadae0c0e747d2fbea6094f06 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.15 + espressif/esp32_s3_eye_noglib: + component_hash: 270a15b503a9977bf394dd5cc659f12adc05663bb9ac24bbc104c8a95cb58fe9 + dependencies: + - name: espressif/button + registry_url: https://components.espressif.com + require: public + version: ^4 + - name: espressif/esp32-camera + registry_url: https://components.espressif.com + require: public + version: ^2.0.13 + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: public + version: ~1.3.1 + - name: idf + require: private + version: '>=5.4' + - name: espressif/qma6100p + registry_url: https://components.espressif.com + require: public + version: ^2 + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + version: 5.0.0 + espressif/esp_codec_dev: + component_hash: 18c22e1411224ba6103c4aca1b01bc740af33926756e9e106370a477d52bcba1 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.3.4 + espressif/esp_jpeg: + component_hash: fdddaa1ceeee223e0abd7e865b05dbceea7ff175440a5ea26eeff8304564e7aa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.0 + espressif/human_face_detect: + component_hash: cba8a6be8221aa08618e2ffb8087f732210731f1f3e6f17e025a81ae413b139f + dependencies: + - name: espressif/esp-dl + registry_url: https://components.espressif.com + require: private + version: ^3.1.1 + source: + registry_url: https://components.espressif.com + type: service + version: 0.2.2 + espressif/human_face_recognition: + component_hash: 6e158c3501d53d6fa65c2ea35194fd43dc2994d9f3b032798c544657664223b6 + dependencies: + - name: espressif/human_face_detect + registry_url: https://components.espressif.com + require: private + version: ^0.2.1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.2 + espressif/qma6100p: + component_hash: dee71383df1d07ad56557eccf8b6f6de3689bee5afc14e353f9149299daf5ca5 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.0 + idf: + source: + type: idf + version: 5.4.1 +direct_dependencies: +- esp32_s3_camera +- espressif/button +- espressif/esp-dl +- espressif/esp32-camera +- espressif/esp32_s3_eye_noglib +- espressif/esp_codec_dev +- espressif/human_face_recognition +- espressif/qma6100p +- idf +manifest_hash: 9531314e6fb7312c44a743088b3972e767bb494149f75c7cccc99bb5522e2a9d +target: esp32s3 +version: 2.0.0 diff --git a/examples/human_face_recognition_mqtt_simple/main/CMakeLists.txt b/examples/human_face_recognition_mqtt_simple/main/CMakeLists.txt new file mode 100644 index 00000000..a0d172d0 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/CMakeLists.txt @@ -0,0 +1,16 @@ +set(src_dirs ./) + +set(include_dirs ./) + +set(requires who_spiflash_fatfs + who_recognition_app + + + esp-tls esp_http_server nvs_flash esp_netif esp_wifi mqtt json + ) + +idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires}) + + + + diff --git a/examples/human_face_recognition_mqtt_simple/main/Kconfig b/examples/human_face_recognition_mqtt_simple/main/Kconfig new file mode 100644 index 00000000..a7b7e17d --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/Kconfig @@ -0,0 +1,266 @@ +menu "Application Configuration" + + # config EXAMPLE_BASIC_AUTH + # bool "Basic Authentication" + # default n + # help + # Basic Authentication is a method for an HTTP user agent (e.g. a web browser) + # to provide a user name and password when making a request. It is the simplest + # technique for enforcing access controls to web resources. because it doesn't + # require cookies, session identifiers, or login pages; rather, it uses standard + # fields in the HTTP header. + # Note that, Basic Authentication is not encrypted channel and also easy to retrieve + # credentials as they are sent in plain text format. + + # config EXAMPLE_BASIC_AUTH_USERNAME + # string "Basic Authenticate User Name" + # depends on EXAMPLE_BASIC_AUTH + # default "ESP32" + # help + # The client's user name which used for basic authenticate. + + # config EXAMPLE_BASIC_AUTH_PASSWORD + # string "Basic Authenticate Password" + # depends on EXAMPLE_BASIC_AUTH + # default "ESP32Webserver" + # help + # The client's password which used for basic authenticate. + + config ESP_WIFI_SSID + string "WiFi SSID" + default "CasetaWifi" + help + SSID (network name) for the example to connect to. + + config ESP_WIFI_PASSWORD + string "WiFi Password" + default "apd14505jc" + help + WiFi password (WPA or WPA2) for the example to use. + + choice ESP_WIFI_SAE_MODE + prompt "WPA3 SAE mode selection" + default ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + help + Select mode for SAE as Hunt and Peck, H2E or both. + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK + bool "HUNT AND PECK" + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT + bool "H2E" + depends on ESP_WIFI_ENABLE_SAE_H2E + config ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + bool "BOTH" + depends on ESP_WIFI_ENABLE_SAE_H2E + endchoice + + config ESP_WIFI_PW_ID + string "PASSWORD IDENTIFIER" + depends on ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT|| ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH + default "" + help + password identifier for SAE H2E + + config ESP_MAXIMUM_RETRY + int "Maximum retry" + default 5 + help + Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. + + choice ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD + prompt "WiFi Scan auth mode threshold" + default ESP_WIFI_AUTH_WPA2_PSK + help + The weakest authmode to accept in the scan mode. + This value defaults to ESP_WIFI_AUTH_WPA2_PSK in case password is present and ESP_WIFI_AUTH_OPEN is used. + Please select ESP_WIFI_AUTH_WEP/ESP_WIFI_AUTH_WPA_PSK in case AP is operating in WEP/WPA mode. + + config ESP_WIFI_AUTH_OPEN + bool "OPEN" + config ESP_WIFI_AUTH_WEP + bool "WEP" + config ESP_WIFI_AUTH_WPA_PSK + bool "WPA PSK" + config ESP_WIFI_AUTH_WPA2_PSK + bool "WPA2 PSK" + config ESP_WIFI_AUTH_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config ESP_WIFI_AUTH_WPA3_PSK + bool "WPA3 PSK" + config ESP_WIFI_AUTH_WPA2_WPA3_PSK + bool "WPA2/WPA3 PSK" + config ESP_WIFI_AUTH_WAPI_PSK + bool "WAPI PSK" + endchoice + + config MQTT_HOST + string "MQTT_HOST" + default "mqtt://myhost" + help + MQtt host url + config MQTT_TOPIC_RECOGNITION + string "MQTT TOPIC_RECOGNITION" + default "/topic/face/recognized/01" + help + Face recognition topic + config MQTT_TOPIC_COMMAND + string "MQTT_TOPIC_COMMAND" + default "/topic/face/command/01" + help + Camera command topic + + menu "Camera Pins" + choice CAMERA_MODEL + bool "Select Camera Pinout" + default CAMERA_MODEL_WROVER_KIT + help + Select Camera Pinout. + + config CAMERA_MODEL_ESP32_S3_CAM + bool "ESP32-S3-CAM" + config CAMERA_MODEL_WROVER_KIT + bool "WROVER-KIT With OV2640 Module" + config CAMERA_MODEL_ESP32_CAM_BOARD + bool "ESP32 Camera Development Board" + config CAMERA_MODEL_ESP_EYE + bool "ESP_EYE DevKit" + config CAMERA_MODEL_M5STACK_PSRAM + bool "M5Stack Camera With PSRAM" + config CAMERA_MODEL_M5STACK_WIDE + bool "M5Stack Camera F (Wide)" + config CAMERA_MODEL_AI_THINKER + bool "ESP32-CAM by AI-Thinker" + config CAMERA_MODEL_CUSTOM + bool "Custom Camera Pinout" + endchoice + + config CAMERA_PIN_PWDN + depends on CAMERA_MODEL_CUSTOM + int "Power Down pin" + range -1 33 + default -1 + help + Select Power Down pin or -1 for unmanaged. + + config CAMERA_PIN_RESET + depends on CAMERA_MODEL_CUSTOM + int "Reset pin" + range -1 33 + default -1 + help + Select Camera Reset pin or -1 for software reset. + + config CAMERA_PIN_XCLK + depends on CAMERA_MODEL_CUSTOM + int "XCLK pin" + range 0 33 + default 21 + help + Select Camera XCLK pin. + + config CAMERA_PIN_SIOD + depends on CAMERA_MODEL_CUSTOM + int "SIOD pin" + range 0 33 + default 26 + help + Select Camera SIOD pin. + + config CAMERA_PIN_SIOC + depends on CAMERA_MODEL_CUSTOM + int "SIOC pin" + range 0 33 + default 27 + help + Select Camera SIOC pin. + + config CAMERA_PIN_VSYNC + depends on CAMERA_MODEL_CUSTOM + int "VSYNC pin" + range 0 39 + default 25 + help + Select Camera VSYNC pin. + + config CAMERA_PIN_HREF + depends on CAMERA_MODEL_CUSTOM + int "HREF pin" + range 0 39 + default 23 + help + Select Camera HREF pin. + + config CAMERA_PIN_PCLK + depends on CAMERA_MODEL_CUSTOM + int "PCLK pin" + range 0 39 + default 25 + help + Select Camera PCLK pin. + + config CAMERA_PIN_Y2 + depends on CAMERA_MODEL_CUSTOM + int "Y2 pin" + range 0 39 + default 4 + help + Select Camera Y2 pin. + + config CAMERA_PIN_Y3 + depends on CAMERA_MODEL_CUSTOM + int "Y3 pin" + range 0 39 + default 5 + help + Select Camera Y3 pin. + + config CAMERA_PIN_Y4 + depends on CAMERA_MODEL_CUSTOM + int "Y4 pin" + range 0 39 + default 18 + help + Select Camera Y4 pin. + + config CAMERA_PIN_Y5 + depends on CAMERA_MODEL_CUSTOM + int "Y5 pin" + range 0 39 + default 19 + help + Select Camera Y5 pin. + + config CAMERA_PIN_Y6 + depends on CAMERA_MODEL_CUSTOM + int "Y6 pin" + range 0 39 + default 36 + help + Select Camera Y6 pin. + + config CAMERA_PIN_Y7 + depends on CAMERA_MODEL_CUSTOM + int "Y7 pin" + range 0 39 + default 39 + help + Select Camera Y7 pin. + + config CAMERA_PIN_Y8 + depends on CAMERA_MODEL_CUSTOM + int "Y8 pin" + range 0 39 + default 34 + help + Select Camera Y8 pin. + + config CAMERA_PIN_Y9 + depends on CAMERA_MODEL_CUSTOM + int "Y9 pin" + range 0 39 + default 35 + help + Select Camera Y9 pin. + + endmenu + +endmenu \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt_simple/main/app_main.cpp b/examples/human_face_recognition_mqtt_simple/main/app_main.cpp new file mode 100644 index 00000000..d0260c46 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/app_main.cpp @@ -0,0 +1,689 @@ +#include "who_recognition_app.hpp" +#include "who_spiflash_fatfs.hpp" + +#include "esp_netif.h" +#include +#include +#include +#include +#include +#include +// #include "protocol_examples_common.h" +// #include "protocol_examples_utils.h" +#include "esp_event.h" +#include "esp_tls_crypto.h" +#include +// #include "esp_netif.h" +#include "esp_check.h" +#include "esp_tls.h" + +#include "nvs_flash.h" +#include +#include +// #include "esp_eth.h" +#include "esp_camera.h" +#include "esp_timer.h" +#include "http_lcd.hpp" +#include "mqtt_handler.hpp" + +#include "cJSON.h" + + +#define PART_BOUNDARY "123456789000000000000987654321" +static const char *_STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY; +static const char *_STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n"; +static const char *_STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n"; + +// static const char* html = +// "" +// "

Recognition

" +// "

" +// "
" +// "
" +// "
" +// ""; + +/* FreeRTOS event group to signal when we are connected*/ +static EventGroupHandle_t s_wifi_event_group; + +#define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD +#define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY + +#if CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HUNT_AND_PECK +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HUNT_AND_PECK +#define EXAMPLE_H2E_IDENTIFIER "" +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_HASH_TO_ELEMENT +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_HASH_TO_ELEMENT +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#elif CONFIG_ESP_STATION_EXAMPLE_WPA3_SAE_PWE_BOTH +#define ESP_WIFI_SAE_MODE WPA3_SAE_PWE_BOTH +#define EXAMPLE_H2E_IDENTIFIER CONFIG_ESP_WIFI_PW_ID +#endif +#if CONFIG_ESP_WIFI_AUTH_OPEN +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_OPEN +#elif CONFIG_ESP_WIFI_AUTH_WEP +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WEP +#elif CONFIG_ESP_WIFI_AUTH_WPA_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA_WPA2_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA_WPA2_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WPA2_WPA3_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WPA2_WPA3_PSK +#elif CONFIG_ESP_WIFI_AUTH_WAPI_PSK +#define ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD WIFI_AUTH_WAPI_PSK +#endif + +/* The event group allows multiple bits for each event, but we only care about two events: + * - we are connected to the AP with an IP + * - we failed to connect after the maximum amount of retries */ +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +// static const char *TAG = "wifi station"; + +static int s_retry_num = 0; + +using namespace who::cam; +using namespace who::lcd; +using namespace who::app; + +WhoLCDiface* lcd = nullptr; // = new HttpLCD(); +WhoRecognitionApp* recognition = nullptr; // = new WhoRecognitionApp(); +static char latest_result[128] = "No result yet"; +static dl::image::img_t img_latest_result = {nullptr, 0, 0, dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8}; +static dl::image::img_t img_latest_result_cropped = {nullptr, 0, 0, dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8}; + +static httpd_handle_t ws_server_handle = NULL; +static int ws_fd = -1; + +static httpd_handle_t server = NULL; + +#define EXAMPLE_HTTP_QUERY_KEY_MAX_LEN (64) + +/* A simple example that demonstrates how to create GET and POST + * handlers for the web server. + */ + +static const char *TAG = "example"; + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +esp_err_t stream_httpd_handler(httpd_req_t *req) +{ + esp_err_t res = ESP_OK; + char part_buf[64]; + static int64_t last_frame = 0; + if (!last_frame) { + last_frame = esp_timer_get_time(); + } + + res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE); + if (res != ESP_OK) { + return res; + } + + while (true) { + // 1. Get the latest LCD buffer + size_t img_size = 0; + const uint8_t* img = static_cast(lcd)->get_buffer(img_size); + + if (!img || img_size == 0) { + ESP_LOGE(TAG, "No LCD buffer available"); + vTaskDelay(10 / portTICK_PERIOD_MS); + continue; + } + + // ESP_LOGI(TAG, "buffer available"); + + // 2. Wrap the buffer as a camera_fb_t + camera_fb_t fb; + fb.buf = (uint8_t*)img; + fb.len = img_size; + fb.width = BSP_LCD_H_RES; + fb.height = BSP_LCD_V_RES; + fb.format = PIXFORMAT_RGB565; + + // 3. Convert to JPEG + uint8_t* _jpg_buf = nullptr; + size_t _jpg_buf_len = 0; + bool jpeg_converted = frame2jpg(&fb, 80, &_jpg_buf, &_jpg_buf_len); + if (!jpeg_converted) { + ESP_LOGE(TAG, "JPEG compression failed"); + res = ESP_FAIL; + break; + } + + // 4. Send as multipart JPEG stream + if (res == ESP_OK) { + res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY)); + } + if (res == ESP_OK) { + size_t hlen = snprintf(part_buf, sizeof(part_buf), _STREAM_PART, _jpg_buf_len); + res = httpd_resp_send_chunk(req, part_buf, hlen); + } + if (res == ESP_OK) { + res = httpd_resp_send_chunk(req, (const char*)_jpg_buf, _jpg_buf_len); + } + + free(_jpg_buf); + + if (res != ESP_OK) { + break; + } + + // Optional: add a small delay to control frame rate + vTaskDelay(5 / portTICK_PERIOD_MS); + } + + last_frame = 0; + return res; +} + +static const httpd_uri_t stream_uri = { + .uri = "/stream", .method = HTTP_GET, .handler = stream_httpd_handler, .user_ctx = NULL, .is_websocket = false, + .handle_ws_control_frames = NULL}; + + +static esp_err_t latest_image_handler(httpd_req_t *req) +{ + // Convert img_latest_result to JPEG + camera_fb_t fb; + fb.buf = (uint8_t*)img_latest_result.data; // Assuming img_latest_result.data is a valid pointer + fb.len = get_img_byte_size(img_latest_result); // Assuming RGB565 format + fb.width = img_latest_result.width; + fb.height = img_latest_result.height; + fb.format = img_latest_result.pix_type == dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8 ? PIXFORMAT_RGB888 : PIXFORMAT_RGB565; + + uint8_t* jpg_buf = nullptr; + size_t jpg_buf_len = 0; + bool jpeg_converted = frame2jpg(&fb, 80, &jpg_buf, &jpg_buf_len); + if (!jpeg_converted) { + httpd_resp_send_500(req); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "image/jpeg"); + esp_err_t res = httpd_resp_send(req, (const char*)jpg_buf, jpg_buf_len); + free(jpg_buf); + return res; +} + +static const httpd_uri_t latest_image_uri = { + .uri = "/latest_image", + .method = HTTP_GET, + .handler = latest_image_handler, + .user_ctx = NULL, + .is_websocket = false, + .handle_ws_control_frames = NULL +}; + +static esp_err_t latest_face_handler(httpd_req_t *req) +{ + // Convert img_latest_result to JPEG + camera_fb_t fb; + fb.buf = (uint8_t*)img_latest_result_cropped.data; // Assuming img_latest_result.data is a valid pointer + fb.len = get_img_byte_size(img_latest_result_cropped); // Assuming RGB565 format + fb.width = img_latest_result_cropped.width; + fb.height = img_latest_result_cropped.height; + fb.format = img_latest_result_cropped.pix_type == dl::image::DL_IMAGE_PIX_TYPE_RGB888_QINT8 ? PIXFORMAT_RGB888 : PIXFORMAT_RGB565; + + uint8_t* jpg_buf = nullptr; + size_t jpg_buf_len = 0; + bool jpeg_converted = frame2jpg(&fb, 80, &jpg_buf, &jpg_buf_len); + if (!jpeg_converted) { + httpd_resp_send_500(req); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "image/jpeg"); + esp_err_t res = httpd_resp_send(req, (const char*)jpg_buf, jpg_buf_len); + free(jpg_buf); + return res; +} + +static const httpd_uri_t latest_face_uri = { + .uri = "/latest_face", + .method = HTTP_GET, + .handler = latest_face_handler, + .user_ctx = NULL, + .is_websocket = false, + .handle_ws_control_frames = NULL +}; + +static esp_err_t recognize_page_handler(httpd_req_t *req) +{ + // char html[1024]; + // snprintf(html, sizeof(html), + // "" + // "

Recognition

" + // "
Result: %s

" + // "

" + // "
" + // "
" + // "
" + // "
" + // "
" + // "
" + // "", + // latest_result + // ); + + // httpd_resp_set_type(req, "text/html"); + // httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN); + +char html[1024]; +snprintf(html, sizeof(html), + "" + "

Recognition

" + "
Result: %s

" + "

" + "" + "" + "" + "
" + "" + "", + latest_result +); + httpd_resp_set_type(req, "text/html"); + httpd_resp_send(req, html, HTTPD_RESP_USE_STRLEN); + return ESP_OK; +} + +static const httpd_uri_t recognize_page_uri = { + .uri = "/recognize", + .method = HTTP_GET, + .handler = recognize_page_handler, + .user_ctx = NULL, + .is_websocket = false, + .handle_ws_control_frames = NULL +}; + + +// static esp_err_t recognizebt_post_handler(httpd_req_t *req) +// { +// ESP_LOGI(TAG, "Recognize button pressed"); +// recognition->recognize(); +// httpd_resp_sendstr(req, "Recognize button pressed!"); +// return ESP_OK; +// } + +// static esp_err_t enrollbt_post_handler(httpd_req_t *req) +// { +// ESP_LOGI(TAG, "Enroll button pressed"); +// recognition->enroll(); +// httpd_resp_sendstr(req, "Enroll button pressed!"); +// return ESP_OK; +// } + +// static esp_err_t deletebt_post_handler(httpd_req_t *req) +// { +// ESP_LOGI(TAG, "Delete button pressed"); +// recognition->delete_face(); +// httpd_resp_sendstr(req, "Delete button pressed!"); +// return ESP_OK; +// } + +// static const httpd_uri_t recognizebt_uri = { +// .uri = "/recognize_action", +// .method = HTTP_POST, +// .handler = recognizebt_post_handler, +// .user_ctx = NULL +// }; + +// static const httpd_uri_t enrollbt_uri = { +// .uri = "/enroll_action", +// .method = HTTP_POST, +// .handler = enrollbt_post_handler, +// .user_ctx = NULL +// }; + +// static const httpd_uri_t deletebt_uri = { +// .uri = "/delete_action", +// .method = HTTP_POST, +// .handler = deletebt_post_handler, +// .user_ctx = NULL +// }; + + +// WebSocket handler +static esp_err_t ws_handler(httpd_req_t *req) +{ + printf("WebSocket request received: %s\n", req->uri); + + if (req->method == HTTP_GET) { + // Handshake done, nothing to do here + ws_server_handle = req->handle; + ws_fd = httpd_req_to_sockfd(req); + return ESP_OK; + } + + printf("2WebSocket request received: %s\n", req->uri); + + + httpd_ws_frame_t ws_pkt; + memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); + ws_pkt.type = HTTPD_WS_TYPE_TEXT; + + // Get frame length + esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, 0); + if (ret != ESP_OK) { + return ret; + } + ws_pkt.payload = (uint8_t*)malloc(ws_pkt.len + 1); + if (ws_pkt.payload == NULL) { + return ESP_ERR_NO_MEM; + } + ret = httpd_ws_recv_frame(req, &ws_pkt, ws_pkt.len); + if (ret != ESP_OK) { + free(ws_pkt.payload); + return ret; + } + ws_pkt.payload[ws_pkt.len] = 0; // Null-terminate + + // Handle commands from client + if (strcmp((char*)ws_pkt.payload, "recognize") == 0) { + recognition->recognize(); + } else if (strcmp((char*)ws_pkt.payload, "enroll") == 0) { + recognition->enroll(); + } else if (strcmp((char*)ws_pkt.payload, "delete") == 0) { + recognition->delete_face(); + } + + ws_server_handle = req->handle; + ws_fd = httpd_req_to_sockfd(req); + + free(ws_pkt.payload); + return ESP_OK; +} + +static const httpd_uri_t ws_uri = { + .uri = "/ws", + .method = HTTP_GET, + .handler = ws_handler, + .user_ctx = NULL, + .is_websocket = true, + .handle_ws_control_frames = NULL +}; + +/* This handler allows the custom error handling functionality to be + * tested from client side. For that, when a PUT request 0 is sent to + * URI /ctrl, the /hello and /echo URIs are unregistered and following + * custom error handler http_404_error_handler() is registered. + * Afterwards, when /hello or /echo is requested, this custom error + * handler is invoked which, after sending an error message to client, + * either closes the underlying socket (when requested URI is /echo) + * or keeps it open (when requested URI is /hello). This allows the + * client to infer if the custom error handler is functioning as expected + * by observing the socket state. + */ +esp_err_t http_404_error_handler(httpd_req_t *req, httpd_err_code_t err) +{ + if (strcmp("/hello", req->uri) == 0) { + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/hello URI is not available"); + /* Return ESP_OK to keep underlying socket open */ + return ESP_OK; + } else if (strcmp("/echo", req->uri) == 0) { + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "/echo URI is not available"); + /* Return ESP_FAIL to close underlying socket */ + return ESP_FAIL; + } + /* For any other URI send 404 and close socket */ + httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Some 404 error message"); + return ESP_FAIL; +} + +static httpd_handle_t start_webserver(void) +{ + server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + // Setting port as 8001 when building for Linux. Port 80 can be used only by a privileged user in linux. + // So when a unprivileged user tries to run the application, it throws bind error and the server is not started. + // Port 8001 can be used by an unprivileged user as well. So the application will not throw bind error and the + // server will be started. + // config.server_port = 8001; + config.lru_purge_enable = true; + + // Start the httpd server + ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); + if (httpd_start(&server, &config) == ESP_OK) { + // Set URI handlers + ESP_LOGI(TAG, "Registering URI handlers"); + // httpd_register_uri_handler(server, &hello); + // httpd_register_uri_handler(server, &echo); + // httpd_register_uri_handler(server, &ctrl); + // httpd_register_uri_handler(server, &any); + httpd_register_uri_handler(server, &stream_uri); + httpd_register_uri_handler(server, &recognize_page_uri); + httpd_register_uri_handler(server, &latest_image_uri); + httpd_register_uri_handler(server, &latest_face_uri); + + + // httpd_register_uri_handler(server, &recognizebt_uri); + // httpd_register_uri_handler(server, &enrollbt_uri); + // httpd_register_uri_handler(server, &deletebt_uri); + httpd_register_uri_handler(server, &ws_uri); + +#if CONFIG_EXAMPLE_BASIC_AUTH + httpd_register_basic_auth(server); +#endif + return server; + } + + ESP_LOGI(TAG, "Error starting server!"); + return NULL; +} + +static esp_err_t stop_webserver(httpd_handle_t server) +{ + // Stop the httpd server + return httpd_stop(server); +} + + + +void wifi_init_sta(void) +{ + s_wifi_event_group = xEventGroupCreate(); + + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + + ESP_ERROR_CHECK( + esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, &instance_any_id)); + ESP_ERROR_CHECK( + esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, &instance_got_ip)); + + wifi_config_t wifi_config = {}; + strncpy((char *)wifi_config.sta.ssid, EXAMPLE_ESP_WIFI_SSID, sizeof(wifi_config.sta.ssid)); + strncpy((char *)wifi_config.sta.password, EXAMPLE_ESP_WIFI_PASS, sizeof(wifi_config.sta.password)); + wifi_config.sta.threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD; + wifi_config.sta.sae_pwe_h2e = ESP_WIFI_SAE_MODE; + strncpy( + (char *)wifi_config.sta.sae_h2e_identifier, EXAMPLE_H2E_IDENTIFIER, sizeof(wifi_config.sta.sae_h2e_identifier)); + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "wifi_init_sta finished."); + + /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum + * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ + EventBits_t bits = + xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); + + /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually + * happened. */ + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else if (bits & WIFI_FAIL_BIT) { + ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); + } else { + ESP_LOGE(TAG, "UNEXPECTED EVENT"); + } +} + +void recognition_result_cb(char *result, dl::image::img_t img, dl::image::img_t img_cropped) +{ + ESP_LOGI(TAG, "Recognition result: %s", result); + strncpy(latest_result, result, sizeof(latest_result) - 1); + latest_result[sizeof(latest_result) - 1] = '\0'; // Ensure null-termination + + // Only save the image if "id" is present in the result string + cJSON *root = cJSON_Parse(result); + if (root) { + cJSON *action = cJSON_GetObjectItem(root, "action"); + if (action && cJSON_IsString(action) && strcmp(action->valuestring, "recognize") == 0) { + ESP_LOGI(TAG, "Recognition result contains valid JSON with action:recognize"); + // Your code here + ESP_LOGI(TAG, "Saving the image"); + memcpy(&img_latest_result, &img, sizeof(dl::image::img_t)); + memcpy(&img_latest_result_cropped, &img_cropped, sizeof(dl::image::img_t)); + } + cJSON_Delete(root); + } + // Send result to browser via WebSocket + if (ws_server_handle && ws_fd >= 0) { + httpd_ws_frame_t ws_pkt = { + .final = true, + .fragmented = false, + .type = HTTPD_WS_TYPE_TEXT, + .payload = (uint8_t*)result, + .len = strlen(result) + }; + httpd_ws_send_frame_async(ws_server_handle, ws_fd, &ws_pkt); + } + + mqtt_publish(result); +} + +void mqtt_event_cb(const char *data, const size_t data_len) +{ + ESP_LOGI(TAG, "MQTT event received: %.*s", (int)data_len, data); + + if (!recognition) return; + + if (strnstr(data, "\"action\": \"enroll\"", data_len)) { + recognition->enroll(); + ESP_LOGI(TAG, "Enroll action triggered from MQTT"); + } else if (strnstr(data, "\"action\": \"delete\"", data_len)) { + recognition->delete_face(); + ESP_LOGI(TAG, "Delete action triggered from MQTT"); + } else if (strnstr(data, "\"action\": \"recognize\"", data_len)) { + recognition->recognize(); + ESP_LOGI(TAG, "Recognize action triggered from MQTT"); + } +} + +extern "C" void app_main(void) +{ +#if CONFIG_DB_FATFS_FLASH + ESP_ERROR_CHECK(fatfs_flash_mount()); +#elif CONFIG_DB_SPIFFS + ESP_ERROR_CHECK(bsp_spiffs_mount()); +#endif +#if CONFIG_DB_FATFS_SDCARD || CONFIG_HUMAN_FACE_DETECT_MODEL_IN_SDCARD || CONFIG_HUMAN_FACE_FEAT_MODEL_IN_SDCARD + ESP_ERROR_CHECK(bsp_sdcard_mount()); +#endif + +#if CONFIG_IDF_TARGET_ESP32S3 + + ESP_LOGI(TAG, "Staring APP"); + + ESP_ERROR_CHECK(bsp_leds_init()); + ESP_ERROR_CHECK(bsp_led_set(BSP_LED_GREEN, false)); +#endif + +// =============================================== + ESP_LOGI(TAG, "Staring WhoS3Cam"); + WhoCam* cam = new WhoS3Cam(PIXFORMAT_RGB565, FRAMESIZE_VGA, 2, true); + + ESP_LOGI(TAG, "Creating HttpLCD"); + lcd = new HttpLCD(); + + ESP_LOGI(TAG, "WhoRecognitionApp"); + recognition = new WhoRecognitionApp(); + recognition->set_cam(cam); + recognition->set_lcd(lcd); + recognition->new_result_subscription(recognition_result_cb); + + ESP_LOGI(TAG, "Recognition run"); + + recognition->run(); + + + ESP_LOGI(TAG, "Staring nvs"); + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_LOGI(TAG, "netif"); + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_LOGI(TAG, "Staring event loop"); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + ESP_LOGI(TAG, "wifi_init_sta"); + + wifi_init_sta(); + + + +} + + +static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { + esp_wifi_connect(); + s_retry_num++; + ESP_LOGI(TAG, "retry to connect to the AP"); + } else { + xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); + } + ESP_LOGI(TAG, "connect to the AP fail"); + + if (server) { + ESP_LOGI(TAG, "Stopping webserver"); + if (stop_webserver(server) == ESP_OK) { + server = NULL; + } else { + ESP_LOGE(TAG, "Failed to stop http server"); + } + } + + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); + s_retry_num = 0; + xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); + + if (server == NULL) { + ESP_LOGI(TAG, "Starting webserver"); + server = start_webserver(); + } + mqtt_app_start(mqtt_event_cb); + } +} diff --git a/examples/human_face_recognition_mqtt_simple/main/http_lcd.hpp b/examples/human_face_recognition_mqtt_simple/main/http_lcd.hpp new file mode 100644 index 00000000..8ea9b20b --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/http_lcd.hpp @@ -0,0 +1,70 @@ +#pragma once +#include "who_recognition_app.hpp" +#include "who_spiflash_fatfs.hpp" + +#include "esp_netif.h" +#include +#include +#include +#include +#include +#include +// #include "protocol_examples_common.h" +// #include "protocol_examples_utils.h" +#include "esp_event.h" +#include "esp_tls_crypto.h" +#include +// #include "esp_netif.h" +#include "esp_check.h" +#include "esp_tls.h" + +#include "nvs_flash.h" +#include +#include +// #include "esp_eth.h" +#include "esp_camera.h" +#include "esp_http_server.h" +#include "esp_timer.h" +#include + +// class HttpLCD : public who::lcd::WhoLCD { +class HttpLCD : public who::lcd::WhoLCDiface { +public: + HttpLCD() : buffer(nullptr), buffer_size(0) {} + + ~HttpLCD() { + if (buffer) free(buffer); + } + + void init() override {} // Prevent base class LCD hardware init + + esp_lcd_panel_handle_t get_lcd_panel_handle() override { + // Return a dummy handle since we don't use the actual LCD hardware + return nullptr; + } + + void draw_full_lcd(const void* data) override { + if (!data) { + ESP_LOGE("HttpLCD", "No data provided to draw_full_lcd"); + return; + } + std::lock_guard lock(mutex); + if (!buffer) { + buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES * (BSP_LCD_BITS_PER_PIXEL / 8); + buffer = (uint8_t*)malloc(buffer_size); + } + memcpy(buffer, data, buffer_size); + } + + // Call this from your HTTP handler to get the latest frame + const uint8_t* get_buffer(size_t& out_size) { + std::lock_guard lock(mutex); + out_size = buffer_size; + return buffer; + } + +private: + uint8_t* buffer; + size_t buffer_size; + std::mutex mutex; +}; \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt_simple/main/idf_component.yml b/examples/human_face_recognition_mqtt_simple/main/idf_component.yml new file mode 100644 index 00000000..1a72ded8 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + esp32_s3_camera: + path: ../components/espressif__esp32_s3_camera + espressif/esp-dl: ^3.1.2 diff --git a/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.cpp b/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.cpp new file mode 100644 index 00000000..0d796780 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.cpp @@ -0,0 +1,131 @@ + + +#include +#include +#include +#include +#include +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_event.h" +#include "esp_netif.h" +// #include "protocol_examples_common.h" +#include "mqtt_client.h" +#include "esp_log.h" + + +#define BROKER_URI CONFIG_MQTT_HOST +#define TOPIC_FACE_RECOGNITION CONFIG_MQTT_TOPIC_RECOGNITION +#define TOPIC_CAM_COMMAND CONFIG_MQTT_TOPIC_COMMAND +static const char *TAG = "mqtt_example"; + + +static struct +{ + esp_mqtt_client_handle_t client; + uint8_t connected; +} mqtt; + + +void (*mqtt_event_cb)(const char *data, const size_t data_len) = NULL; + +/*mqtt event handler*/ +static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) +{ + printf("Event received from MQTT client, event_id=%d\n", event->event_id); + esp_mqtt_client_handle_t client = event->client; + switch (event->event_id) + { + case MQTT_EVENT_CONNECTED: + mqtt.connected = 1; + //ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); + esp_mqtt_client_subscribe(client, TOPIC_CAM_COMMAND, 0); + //ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); + break; + case MQTT_EVENT_DISCONNECTED: + mqtt.connected = 0; + //ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); + break; + case MQTT_EVENT_SUBSCRIBED: + //ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_UNSUBSCRIBED: + //ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); + break; + case MQTT_EVENT_PUBLISHED: +#ifdef APP_DEBUG + //ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); +#endif + break; + case MQTT_EVENT_DATA: + printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); + printf("DATA=%.*s\r\n", event->data_len, event->data); + + if (mqtt_event_cb) + { + mqtt_event_cb(event->data, event->data_len); + } + break; + case MQTT_EVENT_ERROR: + // ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); + break; + default: + // ESP_LOGI(TAG, "Other event id:%d", event->event_id); + break; + } + return ESP_OK; +} + +/* Standard esp event handler that calls the mqtt event handler */ +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + printf("Event received from event loop base"); + // ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id); + mqtt_event_handler_cb((esp_mqtt_event_handle_t)event_data); +} + +void mqtt_task(void *arg) +{ + + // ESP_ERROR_CHECK(esp_event_loop_create_default()); + printf("Starting MQTT client...\n"); + esp_mqtt_client_config_t mqtt_cfg = {}; // Zero-initialize the struct + mqtt_cfg.broker.address.uri = BROKER_URI; + mqtt_cfg.credentials.username = "caseta"; + mqtt_cfg.credentials.authentication.password = "apd14505jc"; + printf("BROKER_URI: %s\n", BROKER_URI); + printf("mqtt_cfg.broker.address.uri: %s\n", mqtt_cfg.broker.address.uri); + printf("Starting MQTT client...\n"); + mqtt.client = esp_mqtt_client_init(&mqtt_cfg); + printf("Starting MQTT client...\n"); + esp_mqtt_client_register_event(mqtt.client, (esp_mqtt_event_id_t)ESP_EVENT_ANY_ID, mqtt_event_handler, mqtt.client); + printf("Starting MQTT client...\n"); + esp_mqtt_client_start(mqtt.client); + + // Properly delete the task before exiting + vTaskDelete(NULL); +} + +void mqtt_app_start(void (*mqtt_event_cb_)(const char *data, const size_t data_len)) +{ + mqtt_event_cb = mqtt_event_cb_; // Store the callback function for later use + xTaskCreatePinnedToCore(mqtt_task, "mqtt_task", 3 * 1024, NULL, 5, NULL, 1); +} + +esp_err_t mqtt_publish(const char *data) +{ + if (mqtt.connected) + { + printf("mqttpublish\n"); + + int msg_id = esp_mqtt_client_publish(mqtt.client, TOPIC_FACE_RECOGNITION, data, 0, 1, 1); + if (msg_id < 0) + { + // ESP_LOGE(TAG, "Unable to publish topic to %s", BROKER_URI); + return ESP_FAIL; + } + } + + return ESP_OK; +} + diff --git a/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.hpp b/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.hpp new file mode 100644 index 00000000..d50968dd --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/main/mqtt_handler.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +// #include "freertos/FreeRTOS.h" +// #include "freertos/queue.h" +// #include "freertos/task.h" +// #include "mqtt_client.h" +// #include "esp_event.h" +// #include "esp_log.h" +// #include "esp_err.h" +// #include "face_recognition_tool.hpp" +// #include "human_face_recognition.hpp" +// #include "led.h" +// #include "mqtt_client.h" + +//void mqtt_app_start(); +void mqtt_app_start(void (*mqtt_event_cb_)(const char *data, const size_t data_len)); +esp_err_t mqtt_publish(const char *data); diff --git a/examples/human_face_recognition_mqtt_simple/partitions.csv b/examples/human_face_recognition_mqtt_simple/partitions.csv new file mode 100644 index 00000000..25efea83 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + +nvs, data, nvs, 0x9000, 24K, +phy_init, data, phy, 0xf000, 4K, +factory, app, factory, 0x010000, 7000K, +storage, data, fat, , 1M, diff --git a/examples/human_face_recognition_mqtt_simple/partitions2.csv b/examples/human_face_recognition_mqtt_simple/partitions2.csv new file mode 100644 index 00000000..ec4a2c67 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/partitions2.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild + +nvs, data, nvs, 0x9000, 24K, +phy_init, data, phy, 0xf000, 4K, +factory, app, factory, 0x010000, 1900K, +human_face_det, data, spiffs, , 200K, +human_face_feat, data, spiffs, , 5000K, +storage, data, fat, , 1M, diff --git a/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults b/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults new file mode 100644 index 00000000..e69de29b diff --git a/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32p4 b/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32p4 new file mode 100644 index 00000000..d6d193c3 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32p4 @@ -0,0 +1,27 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32p4" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_CACHE_L2_CACHE_256KB=y +CONFIG_CACHE_L2_CACHE_LINE_128B=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=n +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64=y +CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_LCD_DPI_BUFFER_NUMS=2 +CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR=y +CONFIG_CAMERA_SC2336=y +CONFIG_CAMERA_SC2336_MIPI_RAW8_1024x600_30FPS=y +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y \ No newline at end of file diff --git a/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32s3 b/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32s3 new file mode 100644 index 00000000..7718f4c2 --- /dev/null +++ b/examples/human_face_recognition_mqtt_simple/sdkconfig.defaults.esp32s3 @@ -0,0 +1,24 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32s3" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=n +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64=y +CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL=y +CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL=y