Ver Fonte

Rolling 20220924

jomjol há 3 anos atrás
pai
commit
68e57d5ec4
100 ficheiros alterados com 3527 adições e 394 exclusões
  1. 9 0
      README.md
  2. BIN
      code/components/esp-nn_20220924.zip
  3. 29 11
      code/components/esp32-camera-master/.github/workflows/build.yml
  4. 1 3
      code/components/esp32-camera-master/.github/workflows/upload_component.yml
  5. 36 15
      code/components/esp32-camera-master/CMakeLists.txt
  6. 77 1
      code/components/esp32-camera-master/Kconfig
  7. 5 2
      code/components/esp32-camera-master/README.md
  8. 6 2
      code/components/esp32-camera-master/conversions/esp_jpg_decode.c
  9. 5 0
      code/components/esp32-camera-master/conversions/jpge.cpp
  10. 41 37
      code/components/esp32-camera-master/conversions/to_bmp.c
  11. 5 15
      code/components/esp32-camera-master/conversions/to_jpg.cpp
  12. 46 20
      code/components/esp32-camera-master/driver/cam_hal.c
  13. 61 10
      code/components/esp32-camera-master/driver/esp_camera.c
  14. 29 4
      code/components/esp32-camera-master/driver/include/esp_camera.h
  15. 11 1
      code/components/esp32-camera-master/driver/include/sensor.h
  16. 1 0
      code/components/esp32-camera-master/driver/private_include/sccb.h
  17. 46 24
      code/components/esp32-camera-master/driver/sccb.c
  18. 3 0
      code/components/esp32-camera-master/driver/sensor.c
  19. 7 2
      code/components/esp32-camera-master/examples/main/take_picture.c
  20. 1 4
      code/components/esp32-camera-master/idf_component.yml
  21. 404 0
      code/components/esp32-camera-master/sensors/bf20a6.c
  22. 6 5
      code/components/esp32-camera-master/sensors/gc0308.c
  23. 27 0
      code/components/esp32-camera-master/sensors/private_include/bf20a6.h
  24. 12 0
      code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h
  25. 158 0
      code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
  26. 17 4
      code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
  27. 2 1
      code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
  28. 31 0
      code/components/esp32-camera-master/sensors/private_include/sc030iot.h
  29. 491 0
      code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h
  30. 31 0
      code/components/esp32-camera-master/sensors/private_include/sc101iot.h
  31. 257 0
      code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h
  32. 335 0
      code/components/esp32-camera-master/sensors/sc030iot.c
  33. 342 0
      code/components/esp32-camera-master/sensors/sc101iot.c
  34. 13 6
      code/components/esp32-camera-master/target/esp32/ll_cam.c
  35. 14 8
      code/components/esp32-camera-master/target/esp32s2/ll_cam.c
  36. 99 22
      code/components/esp32-camera-master/target/esp32s3/ll_cam.c
  37. 8 2
      code/components/esp32-camera-master/target/private_include/ll_cam.h
  38. 44 9
      code/components/esp32-camera-master/test/test_camera.c
  39. BIN
      code/components/esp32-camera-master_20220924.zip
  40. 11 1
      code/components/tflite-lib/CMakeLists.txt
  41. 1 0
      code/components/tflite-lib/tensorflow/lite/builtin_ops.h
  42. 21 0
      code/components/tflite-lib/tensorflow/lite/c/common.cc
  43. 74 1
      code/components/tflite-lib/tensorflow/lite/c/common.h
  44. 8 6
      code/components/tflite-lib/tensorflow/lite/context_util.h
  45. 13 1
      code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc
  46. 4 0
      code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h
  47. 2 0
      code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc
  48. 3 3
      code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/hard_swish.h
  49. 46 0
      code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/mul.h
  50. 151 0
      code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/select.h
  51. 3 0
      code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc
  52. 14 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h
  53. 4 4
      code/components/tflite-lib/tensorflow/lite/micro/kernels/add_n.cc
  54. 8 9
      code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc
  55. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/batch_to_space_nd.cc
  56. 1 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc
  57. 12 12
      code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc
  58. 10 9
      code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc
  59. 3 4
      code/components/tflite-lib/tensorflow/lite/micro/kernels/cumsum.cc
  60. 2 3
      code/components/tflite-lib/tensorflow/lite/micro/kernels/depth_to_space.cc
  61. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.cc
  62. 5 6
      code/components/tflite-lib/tensorflow/lite/micro/kernels/div.cc
  63. 9 9
      code/components/tflite-lib/tensorflow/lite/micro/kernels/elementwise.cc
  64. 2 4
      code/components/tflite-lib/tensorflow/lite/micro/kernels/elu.cc
  65. 0 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/conv.cc
  66. 0 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/depthwise_conv.cc
  67. 0 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/softmax.cc
  68. 3 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/exp.cc
  69. 5 9
      code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc
  70. 4 6
      code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc
  71. 3 3
      code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_div.cc
  72. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_mod.cc
  73. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc
  74. 6 7
      code/components/tflite-lib/tensorflow/lite/micro/kernels/gather.cc
  75. 14 20
      code/components/tflite-lib/tensorflow/lite/micro/kernels/gather_nd.cc
  76. 13 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc
  77. 5 0
      code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.h
  78. 44 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc
  79. 16 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h
  80. 2 3
      code/components/tflite-lib/tensorflow/lite/micro/kernels/l2_pool_2d.cc
  81. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc
  82. 2 3
      code/components/tflite-lib/tensorflow/lite/micro/kernels/log_softmax.cc
  83. 33 7
      code/components/tflite-lib/tensorflow/lite/micro/kernels/lstm_eval.cc
  84. 4 6
      code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc
  85. 2 0
      code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h
  86. 1 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_tensor_utils.cc
  87. 10 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h
  88. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/neg.cc
  89. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc
  90. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc
  91. 4 4
      code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.cc
  92. 15 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.h
  93. 2 3
      code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc
  94. 1 1
      code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce_common.cc
  95. 2 4
      code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_bilinear.cc
  96. 1 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc
  97. 196 0
      code/components/tflite-lib/tensorflow/lite/micro/kernels/select.cc
  98. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/shape.cc
  99. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc
  100. 2 2
      code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.cc

+ 9 - 0
README.md

@@ -69,6 +69,15 @@ In other cases you can contact the developer via email: <img src="https://raw.gi
 
 ------
 
+##### Rolling (2022-09-24)
+
+- Updated menue 
+- Update tflite, esp32-cam-master, esp-nn (as of today 20220924) 
+
+##### Rolling (2022-09-21)
+
+- Spelling corrections (**[cristianmitran](https://github.com/cristianmitran)**) 
+
 ##### Rolling (2022-09-21)
 
 - New update mechanism: 

BIN
code/components/esp-nn_20220924.zip


+ 29 - 11
code/components/esp32-camera-master/.github/workflows/build.yml

@@ -8,41 +8,59 @@ on:
 jobs:
   build-master:
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        idf_target: ["esp32", "esp32s2", "esp32s3"]
     steps:
     - name: Checkout repo
       uses: actions/checkout@v2
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@latest
+      uses: espressif/esp-idf-ci-action@main
       with:
+        target: ${{ matrix.idf_target }}
         path: 'examples'
 
-  build-release-v4_0:
+  build-release-v5_0:
+    name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }}
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        idf_ver: ["release-v5.0"]
+        idf_target: ["esp32", "esp32s2", "esp32s3"]
     steps:
     - name: Checkout repo
       uses: actions/checkout@v2
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@release-v4.0
+      uses: espressif/esp-idf-ci-action@main
       with:
+        esp_idf_version: ${{ matrix.idf_ver }}
+        target: ${{ matrix.idf_target }}
         path: 'examples'
 
-  build-release-v4_1:
+  build-release-v4_4:
+    name: Build for ${{ matrix.idf_target }} on ${{ matrix.idf_ver }}
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        idf_ver: ["v4.4"]
+        idf_target: ["esp32", "esp32s2", "esp32s3"]
     steps:
     - name: Checkout repo
       uses: actions/checkout@v2
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@release-v4.1
+      uses: espressif/esp-idf-ci-action@main
       with:
+        esp_idf_version: ${{ matrix.idf_ver }}
+        target: ${{ matrix.idf_target }}
         path: 'examples'
 
-  build-release-v4_2:
+  build-release-v4_1:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout repo
@@ -50,11 +68,11 @@ jobs:
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@release-v4.2
+      uses: espressif/esp-idf-ci-action@release-v4.1
       with:
         path: 'examples'
 
-  build-release-v4_3:
+  build-release-v4_2:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout repo
@@ -62,11 +80,11 @@ jobs:
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@release-v4.3
+      uses: espressif/esp-idf-ci-action@release-v4.2
       with:
         path: 'examples'
 
-  build-release-v3_3:
+  build-release-v4_3:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout repo
@@ -74,6 +92,6 @@ jobs:
       with:
         submodules: 'recursive'
     - name: esp-idf build
-      uses: espressif/esp-idf-ci-action@release-v3.3
+      uses: espressif/esp-idf-ci-action@release-v4.3
       with:
         path: 'examples'

+ 1 - 3
code/components/esp32-camera-master/.github/workflows/upload_component.yml

@@ -10,12 +10,10 @@ jobs:
       - uses: actions/checkout@master
         with:
           submodules: "recursive"
-
       - name: Upload component to the component registry
         uses: espressif/github-actions/upload_components@master
         with:
           name: "esp32-camera"
-          version: "git"
           namespace: "espressif"
-          service_url: ${{ secrets.IDF_COMPONENT_API_URL }}
+          version: ${{ github.ref_name }}
           api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }}

+ 36 - 15
code/components/esp32-camera-master/CMakeLists.txt

@@ -1,5 +1,29 @@
+# get IDF version for comparison
+set(idf_version "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
+
+# set conversion sources
+set(COMPONENT_SRCS
+  conversions/yuv.c
+  conversions/to_jpg.cpp
+  conversions/to_bmp.c
+  conversions/jpge.cpp
+  conversions/esp_jpg_decode.c
+  )
+
+set(COMPONENT_PRIV_INCLUDEDIRS
+  conversions/private_include
+  )
+
+set(COMPONENT_ADD_INCLUDEDIRS
+  driver/include
+  conversions/include
+  )
+
+set(COMPONENT_REQUIRES driver)
+
+# set driver sources only for supported platforms
 if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3")
-  set(COMPONENT_SRCS
+  list(APPEND COMPONENT_SRCS
     driver/esp_camera.c
     driver/cam_hal.c
     driver/sccb.c
@@ -14,22 +38,14 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
     sensors/gc2145.c
     sensors/gc032a.c
     sensors/bf3005.c
-    conversions/yuv.c
-    conversions/to_jpg.cpp
-    conversions/to_bmp.c
-    conversions/jpge.cpp
-    conversions/esp_jpg_decode.c
+    sensors/bf20a6.c
+    sensors/sc101iot.c
+    sensors/sc030iot.c
     )
 
-  set(COMPONENT_ADD_INCLUDEDIRS
-    driver/include
-    conversions/include
-    )
-
-  set(COMPONENT_PRIV_INCLUDEDIRS
+  list(APPEND COMPONENT_PRIV_INCLUDEDIRS
     driver/private_include
     sensors/private_include
-    conversions/private_include
     target/private_include
     )
 
@@ -58,8 +74,13 @@ if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET ST
       )
   endif()
 
-  set(COMPONENT_REQUIRES driver)
   set(COMPONENT_PRIV_REQUIRES freertos nvs_flash)
 
-  register_component()
+  set(min_version_for_esp_timer "4.2")
+  if (idf_version VERSION_GREATER_EQUAL min_version_for_esp_timer)
+    list(APPEND COMPONENT_PRIV_REQUIRES esp_timer)
+  endif()
+
 endif()
+
+register_component()

+ 77 - 1
code/components/esp32-camera-master/Kconfig

@@ -69,6 +69,45 @@ menu "Camera configuration"
         help
             Enable this option if you want to use the BF3005.
             Disable this option to save memory.
+            
+    config BF20A6_SUPPORT
+        bool "Support BF20A6(BYD20A6) VGA"
+        default y
+        help
+            Enable this option if you want to use the BF20A6.
+            Disable this option to save memory.
+
+    config SC101IOT_SUPPORT
+        bool "Support SC101IOT HD"
+        default n
+        help
+            Enable this option if you want to use the SC101IOT.
+            Disable this option to save memory.
+
+    choice SC101_REGS_SELECT
+        prompt "SC101iot default regs"
+        default SC101IOT_720P_15FPS_ENABLED
+        depends on SC101IOT_SUPPORT
+        help
+            Currently SC010iot has several register sets available.
+            Select the one that matches your needs.
+
+        config SC101IOT_720P_15FPS_ENABLED
+            bool "xclk20M_720p_15fps"
+        help
+            Select this option means that when xclk is 20M, the frame rate is 15fps at 720p resolution.
+        config SC101IOT_VGA_25FPS_ENABLED
+            bool "xclk20M_VGA_25fps"
+        help
+            Select this option means that when xclk is 20M, the frame rate is 25fps at VGA resolution.
+    endchoice
+
+    config SC030IOT_SUPPORT
+        bool "Support SC030IOT VGA"
+        default y
+        help
+            Enable this option if you want to use the SC030IOT.
+            Disable this option to save memory.
 
     choice SCCB_HARDWARE_I2C_PORT
         bool "I2C peripheral to use for SCCB"
@@ -103,6 +142,12 @@ menu "Camera configuration"
             bool "Subsample Mode"
     endchoice
 
+    config CAMERA_TASK_STACK_SIZE
+        int "CAM task stack size"
+        default 2048
+        help
+            Camera task stack size
+
     choice CAMERA_TASK_PINNED_TO_CORE
         bool "Camera task pinned to core"
         default CAMERA_CORE0
@@ -124,6 +169,37 @@ menu "Camera configuration"
         default 32768
         help
             Maximum value of DMA buffer
-            Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent
+            Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent.
+
+    config CAMERA_CONVERTER_ENABLED
+        bool "Enable camera RGB/YUV converter"
+        depends on IDF_TARGET_ESP32S3
+        default n
+        help
+            Enable this option if you want to use RGB565/YUV422/YUV420/YUV411 format conversion.
 
+    choice CAMERA_CONV_PROTOCOL
+        bool "Camera converter protocol"
+        depends on CAMERA_CONVERTER_ENABLED
+        default LCD_CAM_CONV_BT601_ENABLED
+        help
+            Supports format conversion under both BT601 and BT709 standards.
+
+        config LCD_CAM_CONV_BT601_ENABLED
+            bool "BT601"
+        config LCD_CAM_CONV_BT709_ENABLED
+            bool "BT709"
+    endchoice
+
+    config LCD_CAM_CONV_FULL_RANGE_ENABLED
+        bool "Camera converter full range mode"
+        depends on CAMERA_CONVERTER_ENABLED
+        default y
+        help
+            Supports format conversion under both full color range mode and limited color range mode.
+            If full color range mode is selected, the color range of RGB or YUV is 0~255.
+            If limited color range mode is selected, the color range of RGB is 16~240, and the color range of YUV is Y[16~240], UV[16~235].
+            Full color range mode has a wider color range, so details in the image show more clearly.
+            Please confirm the color range mode of the current camera sensor, incorrect color range mode may cause color difference in the final converted image.
+            Full range mode is used by default. If this option is not selected, the format conversion function will be done using the limited range mode.
 endmenu

+ 5 - 2
code/components/esp32-camera-master/README.md

@@ -25,6 +25,9 @@ This repository hosts ESP32 series Soc compatible driver for image sensors. Addi
 | GC0308  | 640 x 480      | color      | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565                        | 1/6.5"   |
 | GC2145  | 1600 x 1200    | color      | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565                        | 1/5"     |
 | BF3005  | 640 x 480      | color      | YUV/YCbCr422<br/>RAW Bayer<br/>RGB565                        | 1/4"     |
+| BF20A6  | 640 x 480      | color      | YUV/YCbCr422<br/>RAW Bayer                                   | 1/10"    |
+| SC101IOT| 1280 x 720     | color      | YUV/YCbCr422<br/>Raw RGB                                     | 1/4.2"   |
+| SC030IOT| 640 x 480      | color      | YUV/YCbCr422<br/>RAW Bayer                                   | 1/6.5"   |
 
 ## Important to Remember
 
@@ -128,8 +131,8 @@ static camera_config_t camera_config = {
     .pin_pwdn  = CAM_PIN_PWDN,
     .pin_reset = CAM_PIN_RESET,
     .pin_xclk = CAM_PIN_XCLK,
-    .pin_sscb_sda = CAM_PIN_SIOD,
-    .pin_sscb_scl = CAM_PIN_SIOC,
+    .pin_sccb_sda = CAM_PIN_SIOD,
+    .pin_sccb_scl = CAM_PIN_SIOC,
 
     .pin_d7 = CAM_PIN_D7,
     .pin_d6 = CAM_PIN_D6,

+ 6 - 2
code/components/esp32-camera-master/conversions/esp_jpg_decode.c

@@ -21,6 +21,10 @@
 #include "tjpgd.h"
 #elif CONFIG_IDF_TARGET_ESP32S3
 #include "esp32s3/rom/tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32C3
+#include "esp32c3/rom/tjpgd.h"
+#elif CONFIG_IDF_TARGET_ESP32H2
+#include "esp32h2/rom/tjpgd.h"
 #else
 #error Target CONFIG_IDF_TARGET is not supported
 #endif
@@ -57,7 +61,7 @@ static const char * jd_errors[] = {
     "Not supported JPEG standard"
 };
 
-static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
+static unsigned int _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
 {
     uint16_t x = rect->left;
     uint16_t y = rect->top;
@@ -73,7 +77,7 @@ static uint32_t _jpg_write(JDEC *decoder, void *bitmap, JRECT *rect)
     return 0;
 }
 
-static uint32_t _jpg_read(JDEC *decoder, uint8_t *buf, uint32_t len)
+static unsigned int _jpg_read(JDEC *decoder, uint8_t *buf, unsigned int len)
 {
     esp_jpg_decoder_t * jpeg = (esp_jpg_decoder_t *)decoder->device;
     if (jpeg->len && len > (jpeg->len - jpeg->index)) {

+ 5 - 0
code/components/esp32-camera-master/conversions/jpge.cpp

@@ -29,7 +29,12 @@ namespace jpge {
         if(b){
             return b;
         }
+    // check if SPIRAM is enabled and allocate on SPIRAM if allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
         return heap_caps_malloc(nSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#else
+        return NULL;
+#endif
     }
     static inline void jpge_free(void *p) { free(p); }
 

+ 41 - 37
code/components/esp32-camera-master/conversions/to_bmp.c

@@ -21,19 +21,6 @@
 #include "esp_jpg_decode.h"
 
 #include "esp_system.h"
-#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
-#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
-#include "esp32/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S2
-#include "esp32s2/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S3
-#include "esp32s3/spiram.h"
-#else 
-#error Target CONFIG_IDF_TARGET is not supported
-#endif
-#else // ESP32 Before IDF 4.0
-#include "esp_spiram.h"
-#endif
 
 #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
 #include "esp32-hal-log.h"
@@ -72,7 +59,12 @@ typedef struct {
 
 static void *_malloc(size_t size)
 {
+    // check if SPIRAM is enabled and allocate on SPIRAM if allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
     return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#endif
+    // try allocating in internal memory
+    return malloc(size);
 }
 
 //output buffer and image width
@@ -168,7 +160,7 @@ static bool _rgb565_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16
 }
 
 //input buffer
-static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
+static unsigned int _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len)
 {
     rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg;
     if(buf) {
@@ -310,7 +302,13 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
     *out_len = 0;
 
     int pix_count = width*height;
-    size_t out_size = (pix_count * 3) + BMP_HEADER_LEN;
+
+    // With BMP, 8-bit greyscale requires a palette.
+    // For a 640x480 image though, that's a savings
+    // over going RGB-24.
+    int bpp = (format == PIXFORMAT_GRAYSCALE) ? 1 : 3;
+    int palette_size = (format == PIXFORMAT_GRAYSCALE) ? 4 * 256 : 0;
+    size_t out_size = (pix_count * bpp) + BMP_HEADER_LEN + palette_size;
     uint8_t * out_buf = (uint8_t *)_malloc(out_size);
     if(!out_buf) {
         ESP_LOGE(TAG, "_malloc failed! %u", out_size);
@@ -322,45 +320,51 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
     bmp_header_t * bitmap  = (bmp_header_t*)&out_buf[2];
     bitmap->reserved = 0;
     bitmap->filesize = out_size;
-    bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN;
+    bitmap->fileoffset_to_pixelarray = BMP_HEADER_LEN + palette_size;
     bitmap->dibheadersize = 40;
     bitmap->width = width;
     bitmap->height = -height;//set negative for top to bottom
     bitmap->planes = 1;
-    bitmap->bitsperpixel = 24;
+    bitmap->bitsperpixel = bpp * 8;
     bitmap->compression = 0;
-    bitmap->imagesize = pix_count * 3;
+    bitmap->imagesize = pix_count * bpp;
     bitmap->ypixelpermeter = 0x0B13 ; //2835 , 72 DPI
     bitmap->xpixelpermeter = 0x0B13 ; //2835 , 72 DPI
     bitmap->numcolorspallette = 0;
     bitmap->mostimpcolor = 0;
 
-    uint8_t * rgb_buf = out_buf + BMP_HEADER_LEN;
+    uint8_t * palette_buf = out_buf + BMP_HEADER_LEN;
+    uint8_t * pix_buf = palette_buf + palette_size;
     uint8_t * src_buf = src;
 
+    if (palette_size > 0) {
+        // Grayscale palette
+        for (int i = 0; i < 256; ++i) {
+            for (int j = 0; j < 3; ++j) {
+                *palette_buf = i;
+                palette_buf++;
+            }
+            // Reserved / alpha channel.
+            *palette_buf = 0;
+            palette_buf++;
+        }
+    }
 
     //convert data to RGB888
     if(format == PIXFORMAT_RGB888) {
-        memcpy(rgb_buf, src_buf, pix_count*3);
+        memcpy(pix_buf, src_buf, pix_count*3);
     } else if(format == PIXFORMAT_RGB565) {
         int i;
         uint8_t hb, lb;
         for(i=0; i<pix_count; i++) {
             hb = *src_buf++;
             lb = *src_buf++;
-            *rgb_buf++ = (lb & 0x1F) << 3;
-            *rgb_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
-            *rgb_buf++ = hb & 0xF8;
+            *pix_buf++ = (lb & 0x1F) << 3;
+            *pix_buf++ = (hb & 0x07) << 5 | (lb & 0xE0) >> 3;
+            *pix_buf++ = hb & 0xF8;
         }
     } else if(format == PIXFORMAT_GRAYSCALE) {
-        int i;
-        uint8_t b;
-        for(i=0; i<pix_count; i++) {
-            b = *src_buf++;
-            *rgb_buf++ = b;
-            *rgb_buf++ = b;
-            *rgb_buf++ = b;
-        }
+        memcpy(pix_buf, src_buf, pix_count);
     } else if(format == PIXFORMAT_YUV422) {
         int i, maxi = pix_count / 2;
         uint8_t y0, y1, u, v;
@@ -372,14 +376,14 @@ bool fmt2bmp(uint8_t *src, size_t src_len, uint16_t width, uint16_t height, pixf
             v = *src_buf++;
 
             yuv2rgb(y0, u, v, &r, &g, &b);
-            *rgb_buf++ = b;
-            *rgb_buf++ = g;
-            *rgb_buf++ = r;
+            *pix_buf++ = b;
+            *pix_buf++ = g;
+            *pix_buf++ = r;
 
             yuv2rgb(y1, u, v, &r, &g, &b);
-            *rgb_buf++ = b;
-            *rgb_buf++ = g;
-            *rgb_buf++ = r;
+            *pix_buf++ = b;
+            *pix_buf++ = g;
+            *pix_buf++ = r;
         }
     }
     *out = out_buf;

+ 5 - 15
code/components/esp32-camera-master/conversions/to_jpg.cpp

@@ -21,21 +21,6 @@
 #include "jpge.h"
 #include "yuv.h"
 
-#include "esp_system.h"
-#if ESP_IDF_VERSION_MAJOR >= 4 // IDF 4+
-#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
-#include "esp32/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S2
-#include "esp32s2/spiram.h"
-#elif CONFIG_IDF_TARGET_ESP32S3
-#include "esp32s3/spiram.h"
-#else 
-#error Target CONFIG_IDF_TARGET is not supported
-#endif
-#else // ESP32 Before IDF 4.0
-#include "esp_spiram.h"
-#endif
-
 #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
 #include "esp32-hal-log.h"
 #define TAG ""
@@ -50,7 +35,12 @@ static void *_malloc(size_t size)
     if(res) {
         return res;
     }
+
+    // check if SPIRAM is enabled and is allocatable
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
     return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+#endif
+    return NULL;
 }
 
 static IRAM_ATTR void convert_line_format(uint8_t * src, pixformat_t format, uint8_t * dst, size_t width, size_t in_channels, size_t line)

+ 46 - 20
code/components/esp32-camera-master/driver/cam_hal.c

@@ -18,8 +18,27 @@
 #include "ll_cam.h"
 #include "cam_hal.h"
 
-static const char *TAG = "cam_hal";
+#if (ESP_IDF_VERSION_MAJOR == 3) && (ESP_IDF_VERSION_MINOR == 3)
+#include "rom/ets_sys.h"
+#else
+#include "esp_timer.h"
+#if CONFIG_IDF_TARGET_ESP32
+#include "esp32/rom/ets_sys.h"  // will be removed in idf v5.0
+#elif CONFIG_IDF_TARGET_ESP32S2
+#include "esp32s2/rom/ets_sys.h"
+#elif CONFIG_IDF_TARGET_ESP32S3
+#include "esp32s3/rom/ets_sys.h"
+#endif
+#endif // ESP_IDF_VERSION_MAJOR
+#define ESP_CAMERA_ETS_PRINTF ets_printf
+
+#if CONFIG_CAM_TASK_STACK_SIZE
+#define CAM_TASK_STACK             CONFIG_CAM_TASK_STACK_SIZE
+#else
+#define CAM_TASK_STACK             (2*1024)
+#endif
 
+static const char *TAG = "cam_hal";
 static cam_obj_t *cam_obj = NULL;
 
 static const uint32_t JPEG_SOI_MARKER = 0xFFD8FF;  // written in little-endian for esp32
@@ -32,7 +51,7 @@ static int cam_verify_jpeg_soi(const uint8_t *inbuf, uint32_t length)
         for (uint32_t i = 0; i < length; i++) {
             sig = *((uint32_t *)(&inbuf[i])) & 0xFFFFFF;
             if (sig == JPEG_SOI_MARKER) {
-                ESP_LOGW(TAG, "SOI: %d", i);
+                ESP_LOGW(TAG, "SOI: %d", (int) i);
                 return i;
             }
         }
@@ -93,7 +112,7 @@ void IRAM_ATTR ll_cam_send_event(cam_obj_t *cam, cam_event_t cam_event, BaseType
     if (xQueueSendFromISR(cam->event_queue, (void *)&cam_event, HPTaskAwoken) != pdTRUE) {
         ll_cam_stop(cam);
         cam->state = CAM_STATE_IDLE;
-        ESP_EARLY_LOGE(TAG, "EV-%s-OVF", cam_event==CAM_IN_SUC_EOF_EVENT ? "EOF" : "VSYNC");
+        ESP_CAMERA_ETS_PRINTF(DRAM_STR("cam_hal: EV-%s-OVF\r\n"), cam_event==CAM_IN_SUC_EOF_EVENT ? DRAM_STR("EOF") : DRAM_STR("VSYNC"));
     }
 }
 
@@ -104,7 +123,7 @@ static void cam_task(void *arg)
     int frame_pos = 0;
     cam_obj->state = CAM_STATE_IDLE;
     cam_event_t cam_event = 0;
-    
+
     xQueueReset(cam_obj->event_queue);
 
     while (1) {
@@ -127,7 +146,7 @@ static void cam_task(void *arg)
             case CAM_STATE_READ_BUF: {
                 camera_fb_t * frame_buffer_event = &cam_obj->frames[frame_pos].fb;
                 size_t pixels_per_dma = (cam_obj->dma_half_buffer_size * cam_obj->fb_bytes_per_pixel) / (cam_obj->dma_bytes_per_item * cam_obj->in_bytes_per_pixel);
-                
+
                 if (cam_event == CAM_IN_SUC_EOF_EVENT) {
                     if(!cam_obj->psram_mode){
                         if (cam_obj->fb_size < (frame_buffer_event->len + pixels_per_dma)) {
@@ -137,8 +156,8 @@ static void cam_task(void *arg)
                             continue;
                         }
                         frame_buffer_event->len += ll_cam_memcpy(cam_obj,
-                            &frame_buffer_event->buf[frame_buffer_event->len], 
-                            &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size], 
+                            &frame_buffer_event->buf[frame_buffer_event->len],
+                            &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
                             cam_obj->dma_half_buffer_size);
                     }
                     //Check for JPEG SOI in the first buffer. stop if not found
@@ -160,8 +179,8 @@ static void cam_task(void *arg)
                                     cnt--;
                                 } else {
                                     frame_buffer_event->len += ll_cam_memcpy(cam_obj,
-                                        &frame_buffer_event->buf[frame_buffer_event->len], 
-                                        &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size], 
+                                        &frame_buffer_event->buf[frame_buffer_event->len],
+                                        &cam_obj->dma_buffer[(cnt % cam_obj->dma_half_buffer_cnt) * cam_obj->dma_half_buffer_size],
                                         cam_obj->dma_half_buffer_size);
                                 }
                             }
@@ -179,7 +198,7 @@ static void cam_task(void *arg)
                         } else if (!cam_obj->jpeg_mode) {
                             if (frame_buffer_event->len != cam_obj->fb_size) {
                                 cam_obj->frames[frame_pos].en = 1;
-                                ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, cam_obj->fb_size);
+                                ESP_LOGE(TAG, "FB-SIZE: %u != %u", frame_buffer_event->len, (unsigned) cam_obj->fb_size);
                             }
                         }
                         //send frame
@@ -245,8 +264,9 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
     cam_obj->dma_node_cnt = (cam_obj->dma_buffer_size) / cam_obj->dma_node_buffer_size; // Number of DMA nodes
     cam_obj->frame_copy_cnt = cam_obj->recv_size / cam_obj->dma_half_buffer_size; // Number of interrupted copies, ping-pong copy
 
-    ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d", 
-             cam_obj->dma_buffer_size, cam_obj->dma_half_buffer_size, cam_obj->dma_node_buffer_size, cam_obj->dma_node_cnt, cam_obj->frame_copy_cnt);
+    ESP_LOGI(TAG, "buffer_size: %d, half_buffer_size: %d, node_buffer_size: %d, node_cnt: %d, total_cnt: %d",
+             (int) cam_obj->dma_buffer_size, (int) cam_obj->dma_half_buffer_size, (int) cam_obj->dma_node_buffer_size,
+             (int) cam_obj->dma_node_cnt, (int) cam_obj->frame_copy_cnt);
 
     cam_obj->dma_buffer = NULL;
     cam_obj->dma = NULL;
@@ -276,13 +296,19 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
         cam_obj->frames[x].fb_offset = 0;
         cam_obj->frames[x].en = 0;
         ESP_LOGI(TAG, "Allocating %d Byte frame buffer in %s", alloc_size, _caps & MALLOC_CAP_SPIRAM ? "PSRAM" : "OnBoard RAM");
+#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
+        // In IDF v4.2 and earlier, memory returned by heap_caps_aligned_alloc must be freed using heap_caps_aligned_free.
+        // And heap_caps_aligned_free is deprecated on v4.3.
+        cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_aligned_alloc(16, alloc_size, _caps);
+#else
         cam_obj->frames[x].fb.buf = (uint8_t *)heap_caps_malloc(alloc_size, _caps);
+#endif
         CAM_CHECK(cam_obj->frames[x].fb.buf != NULL, "frame buffer malloc failed", ESP_FAIL);
         if (cam_obj->psram_mode) {
             //align PSRAM buffer. TODO: save the offset so proper address can be freed later
             cam_obj->frames[x].fb_offset = dma_align - ((uint32_t)cam_obj->frames[x].fb.buf & (dma_align - 1));
             cam_obj->frames[x].fb.buf += cam_obj->frames[x].fb_offset;
-            ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (uint32_t)cam_obj->frames[x].fb.buf);
+            ESP_LOGI(TAG, "Frame[%d]: Offset: %u, Addr: 0x%08X", x, cam_obj->frames[x].fb_offset, (unsigned) cam_obj->frames[x].fb.buf);
             cam_obj->frames[x].dma = allocate_dma_descriptors(cam_obj->dma_node_cnt, cam_obj->dma_node_buffer_size, cam_obj->frames[x].fb.buf);
             CAM_CHECK(cam_obj->frames[x].dma != NULL, "frame dma malloc failed", ESP_FAIL);
         }
@@ -292,8 +318,8 @@ static esp_err_t cam_dma_config(const camera_config_t *config)
     if (!cam_obj->psram_mode) {
         cam_obj->dma_buffer = (uint8_t *)heap_caps_malloc(cam_obj->dma_buffer_size * sizeof(uint8_t), MALLOC_CAP_DMA);
         if(NULL == cam_obj->dma_buffer) {
-            ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__, 
-                     cam_obj->dma_buffer_size, heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
+            ESP_LOGE(TAG,"%s(%d): DMA buffer %d Byte malloc failed, the current largest free block:%d Byte", __FUNCTION__, __LINE__,
+                     (int) cam_obj->dma_buffer_size, (int) heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
             return ESP_FAIL;
         }
 
@@ -359,7 +385,7 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint
         cam_obj->recv_size = cam_obj->width * cam_obj->height * cam_obj->in_bytes_per_pixel;
         cam_obj->fb_size = cam_obj->width * cam_obj->height * cam_obj->fb_bytes_per_pixel;
     }
-    
+
     ret = cam_dma_config(config);
     CAM_CHECK_GOTO(ret == ESP_OK, "cam_dma_config failed", err);
 
@@ -376,13 +402,13 @@ esp_err_t cam_config(const camera_config_t *config, framesize_t frame_size, uint
     ret = ll_cam_init_isr(cam_obj);
     CAM_CHECK_GOTO(ret == ESP_OK, "cam intr alloc failed", err);
 
-    
+
 #if CONFIG_CAMERA_CORE0
-    xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
+    xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 0);
 #elif CONFIG_CAMERA_CORE1
-    xTaskCreatePinnedToCore(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
+    xTaskCreatePinnedToCore(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle, 1);
 #else
-    xTaskCreate(cam_task, "cam_task", 2048, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
+    xTaskCreate(cam_task, "cam_task", CAM_TASK_STACK, NULL, configMAX_PRIORITIES - 2, &cam_obj->task_handle);
 #endif
 
     ESP_LOGI(TAG, "cam config ok");

+ 61 - 10
code/components/esp32-camera-master/driver/esp_camera.c

@@ -57,6 +57,15 @@
 #if CONFIG_BF3005_SUPPORT
 #include "bf3005.h"
 #endif
+#if CONFIG_BF20A6_SUPPORT
+#include "bf20a6.h"
+#endif
+#if CONFIG_SC101IOT_SUPPORT
+#include "sc101iot.h"
+#endif
+#if CONFIG_SC030IOT_SUPPORT
+#include "sc030iot.h"
+#endif
 
 #if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
 #include "esp32-hal-log.h"
@@ -119,10 +128,20 @@ static const sensor_func_t g_sensors[] = {
 #if CONFIG_BF3005_SUPPORT
     {bf3005_detect, bf3005_init},
 #endif
+#if CONFIG_BF20A6_SUPPORT
+    {bf20a6_detect, bf20a6_init},
+#endif
+#if CONFIG_SC101IOT_SUPPORT
+    {sc101iot_detect, sc101iot_init},
+#endif
+#if CONFIG_SC030IOT_SUPPORT
+    {sc030iot_detect, sc030iot_init},
+#endif
 };
 
 static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out_camera_model)
 {
+    esp_err_t ret = ESP_OK;
     *out_camera_model = CAMERA_NONE;
     if (s_state != NULL) {
         return ESP_ERR_INVALID_STATE;
@@ -138,9 +157,17 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
         CAMERA_ENABLE_OUT_CLOCK(config);
     }
 
-    if (config->pin_sscb_sda != -1) {
-        ESP_LOGD(TAG, "Initializing SSCB");
-        SCCB_Init(config->pin_sscb_sda, config->pin_sscb_scl);
+    if (config->pin_sccb_sda != -1) {
+        ESP_LOGD(TAG, "Initializing SCCB");
+        ret = SCCB_Init(config->pin_sccb_sda, config->pin_sccb_scl);
+    } else {
+        ESP_LOGD(TAG, "Using existing I2C port");
+        ret = SCCB_Use_Port(config->sccb_i2c_port);
+    }
+
+    if(ret != ESP_OK) {
+        ESP_LOGE(TAG, "sccb init err");
+        goto err;
     }
 
     if (config->pin_pwdn >= 0) {
@@ -170,15 +197,14 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
         vTaskDelay(10 / portTICK_PERIOD_MS);
     }
 
-
     ESP_LOGD(TAG, "Searching for camera address");
     vTaskDelay(10 / portTICK_PERIOD_MS);
 
     uint8_t slv_addr = SCCB_Probe();
 
     if (slv_addr == 0) {
-        CAMERA_DISABLE_OUT_CLOCK();
-        return ESP_ERR_NOT_FOUND;
+        ret = ESP_ERR_NOT_FOUND;
+        goto err;
     }
 
     ESP_LOGI(TAG, "Detected camera at address=0x%02x", slv_addr);
@@ -203,9 +229,9 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
     }
 
     if (CAMERA_NONE == *out_camera_model) { //If no supported sensors are detected
-        CAMERA_DISABLE_OUT_CLOCK();
         ESP_LOGE(TAG, "Detected camera not supported.");
-        return ESP_ERR_NOT_SUPPORTED;
+        ret = ESP_ERR_NOT_SUPPORTED;
+        goto err;
     }
 
     ESP_LOGI(TAG, "Camera PID=0x%02x VER=0x%02x MIDL=0x%02x MIDH=0x%02x",
@@ -213,10 +239,29 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
 
     ESP_LOGD(TAG, "Doing SW reset of sensor");
     vTaskDelay(10 / portTICK_PERIOD_MS);
-    s_state->sensor.reset(&s_state->sensor);
+    
+    return s_state->sensor.reset(&s_state->sensor);
+err :
+    CAMERA_DISABLE_OUT_CLOCK();
+    return ret;
+}
 
-    return ESP_OK;
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
+{
+    pixformat_t format = PIXFORMAT_RGB565;
+    switch (conv_mode) {
+    case YUV422_TO_YUV420:
+        format = PIXFORMAT_YUV420;
+        break;
+    case YUV422_TO_RGB565: // default format is RGB565
+    default:
+        break;
+    }
+    ESP_LOGD(TAG, "Convert to %d format enabled", format);
+    return format;
 }
+#endif
 
 esp_err_t esp_camera_init(const camera_config_t *config)
 {
@@ -256,6 +301,7 @@ esp_err_t esp_camera_init(const camera_config_t *config)
 
     s_state->sensor.status.framesize = frame_size;
     s_state->sensor.pixformat = pix_format;
+
     ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
     if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
         ESP_LOGE(TAG, "Failed to set frame size");
@@ -263,6 +309,11 @@ esp_err_t esp_camera_init(const camera_config_t *config)
         goto fail;
     }
     s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+    if(config->conv_mode) {
+        s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
+    }
+#endif
 
     if (s_state->sensor.id.PID == OV2640_PID) {
         s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);

+ 29 - 4
code/components/esp32-camera-master/driver/include/esp_camera.h

@@ -18,8 +18,8 @@
         .pin_pwdn       = PIN_PWDN,
         .pin_reset      = PIN_RESET,
         .pin_xclk       = PIN_XCLK,
-        .pin_sscb_sda   = PIN_SIOD,
-        .pin_sscb_scl   = PIN_SIOC,
+        .pin_sccb_sda   = PIN_SIOD,
+        .pin_sccb_scl   = PIN_SIOC,
         .pin_d7         = PIN_D7,
         .pin_d6         = PIN_D6,
         .pin_d5         = PIN_D5,
@@ -70,6 +70,7 @@
 #include "driver/ledc.h"
 #include "sensor.h"
 #include "sys/time.h"
+#include "sdkconfig.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -91,6 +92,19 @@ typedef enum {
     CAMERA_FB_IN_DRAM           /*!< Frame buffer is placed in internal DRAM */
 } camera_fb_location_t;
 
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+/**
+ * @brief Camera RGB\YUV conversion mode
+ */
+typedef enum {
+    CONV_DISABLE,
+    RGB565_TO_YUV422,
+        
+    YUV422_TO_RGB565,
+    YUV422_TO_YUV420
+} camera_conv_mode_t;
+#endif
+
 /**
  * @brief Configuration structure for camera initialization
  */
@@ -98,8 +112,14 @@ typedef struct {
     int pin_pwdn;                   /*!< GPIO pin for camera power down line */
     int pin_reset;                  /*!< GPIO pin for camera reset line */
     int pin_xclk;                   /*!< GPIO pin for camera XCLK line */
-    int pin_sscb_sda;               /*!< GPIO pin for camera SDA line */
-    int pin_sscb_scl;               /*!< GPIO pin for camera SCL line */
+    union {
+        int pin_sccb_sda;           /*!< GPIO pin for camera SDA line */
+        int pin_sscb_sda __attribute__((deprecated("please use pin_sccb_sda instead")));           /*!< GPIO pin for camera SDA line (legacy name) */
+    };
+    union {
+        int pin_sccb_scl;           /*!< GPIO pin for camera SCL line */
+        int pin_sscb_scl __attribute__((deprecated("please use pin_sccb_scl instead")));           /*!< GPIO pin for camera SCL line (legacy name) */
+    };
     int pin_d7;                     /*!< GPIO pin for camera D7 line */
     int pin_d6;                     /*!< GPIO pin for camera D6 line */
     int pin_d5;                     /*!< GPIO pin for camera D5 line */
@@ -124,6 +144,11 @@ typedef struct {
     size_t fb_count;                /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed)  */
     camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
     camera_grab_mode_t grab_mode;   /*!< When buffers should be filled */
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+    camera_conv_mode_t conv_mode;   /*!< RGB<->YUV Conversion mode */
+#endif
+
+    int sccb_i2c_port;              /*!< If pin_sccb_sda is -1, use the already configured I2C bus by number */
 } camera_config_t;
 
 /**

+ 11 - 1
code/components/esp32-camera-master/driver/include/sensor.h

@@ -27,6 +27,9 @@ typedef enum {
     GC032A_PID = 0x232a,
     GC0308_PID = 0x9b,
     BF3005_PID = 0x30,
+    BF20A6_PID = 0x20a6,
+    SC101IOT_PID = 0xda4a,
+    SC030IOT_PID = 0x9a46,
 } camera_pid_t;
 
 typedef enum {
@@ -40,6 +43,9 @@ typedef enum {
     CAMERA_GC032A,
     CAMERA_GC0308,
     CAMERA_BF3005,
+    CAMERA_BF20A6,
+    CAMERA_SC101IOT,
+    CAMERA_SC030IOT,
     CAMERA_MODEL_MAX,
     CAMERA_NONE,
 } camera_model_t;
@@ -55,11 +61,15 @@ typedef enum {
     GC032A_SCCB_ADDR   = 0x21,// 0x42 >> 1
     GC0308_SCCB_ADDR   = 0x21,// 0x42 >> 1
     BF3005_SCCB_ADDR   = 0x6E,
+    BF20A6_SCCB_ADDR   = 0x6E,
+    SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
+    SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
 } camera_sccb_addr_t;
 
 typedef enum {
     PIXFORMAT_RGB565,    // 2BPP/RGB565
     PIXFORMAT_YUV422,    // 2BPP/YUV422
+    PIXFORMAT_YUV420,    // 1.5BPP/YUV420
     PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
     PIXFORMAT_JPEG,      // JPEG/COMPRESSED
     PIXFORMAT_RGB888,    // 3BPP/RGB888
@@ -199,7 +209,7 @@ typedef struct _sensor {
 
     // Sensor function pointers
     int  (*init_status)         (sensor_t *sensor);
-    int  (*reset)               (sensor_t *sensor);
+    int  (*reset)               (sensor_t *sensor); // Reset the configuration of the sensor, and return ESP_OK if reset is successful
     int  (*set_pixformat)       (sensor_t *sensor, pixformat_t pixformat);
     int  (*set_framesize)       (sensor_t *sensor, framesize_t framesize);
     int  (*set_contrast)        (sensor_t *sensor, int level);

+ 1 - 0
code/components/esp32-camera-master/driver/private_include/sccb.h

@@ -10,6 +10,7 @@
 #define __SCCB_H__
 #include <stdint.h>
 int SCCB_Init(int pin_sda, int pin_scl);
+int SCCB_Use_Port(int sccb_i2c_port);
 int SCCB_Deinit(void);
 uint8_t SCCB_Probe();
 uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg);

+ 46 - 24
code/components/esp32-camera-master/driver/sccb.c

@@ -25,6 +25,11 @@ static const char* TAG = "sccb";
 
 #include "driver/i2c.h"
 
+// support IDF 5.x
+#ifndef portTICK_RATE_MS
+#define portTICK_RATE_MS portTICK_PERIOD_MS
+#endif
+
 #define SCCB_FREQ               CONFIG_SCCB_CLK_FREQ  /*!< I2C master frequency*/
 #define WRITE_BIT               I2C_MASTER_WRITE      /*!< I2C master write */
 #define READ_BIT                I2C_MASTER_READ       /*!< I2C master read */
@@ -33,16 +38,26 @@ static const char* TAG = "sccb";
 #define ACK_VAL                 0x0                   /*!< I2C ack value */
 #define NACK_VAL                0x1                   /*!< I2C nack value */
 #if CONFIG_SCCB_HARDWARE_I2C_PORT1
-const int SCCB_I2C_PORT         = 1;
+const int SCCB_I2C_PORT_DEFAULT = 1;
 #else
-const int SCCB_I2C_PORT         = 0;
+const int SCCB_I2C_PORT_DEFAULT = 0;
 #endif
 
+static int sccb_i2c_port;
+static bool sccb_owns_i2c_port;
+
 int SCCB_Init(int pin_sda, int pin_scl)
 {
     ESP_LOGI(TAG, "pin_sda %d pin_scl %d", pin_sda, pin_scl);
     i2c_config_t conf;
+    esp_err_t ret;
+
     memset(&conf, 0, sizeof(i2c_config_t));
+
+    sccb_i2c_port = SCCB_I2C_PORT_DEFAULT;
+    sccb_owns_i2c_port = true;
+    ESP_LOGI(TAG, "sccb_i2c_port=%d\n", sccb_i2c_port);
+
     conf.mode = I2C_MODE_MASTER;
     conf.sda_io_num = pin_sda;
     conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
@@ -50,30 +65,37 @@ int SCCB_Init(int pin_sda, int pin_scl)
     conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
     conf.master.clk_speed = SCCB_FREQ;
 
-    i2c_param_config(SCCB_I2C_PORT, &conf);
-    i2c_driver_install(SCCB_I2C_PORT, conf.mode, 0, 0, 0);
-    return 0;
+    if ((ret =  i2c_param_config(sccb_i2c_port, &conf)) != ESP_OK) {
+        return ret;
+    }
+
+    return i2c_driver_install(sccb_i2c_port, conf.mode, 0, 0, 0);
+}
+
+int SCCB_Use_Port(int i2c_num) { // sccb use an already initialized I2C port
+    if (sccb_owns_i2c_port) {
+        SCCB_Deinit();
+    }
+    if (i2c_num < 0 || i2c_num > I2C_NUM_MAX) {
+        return ESP_ERR_INVALID_ARG;
+    }
+    sccb_i2c_port = i2c_num;
+    return ESP_OK;
 }
 
 int SCCB_Deinit(void)
 {
-    return i2c_driver_delete(SCCB_I2C_PORT);
+    if (!sccb_owns_i2c_port) {
+        return ESP_OK;
+    }
+    sccb_owns_i2c_port = false;
+    return i2c_driver_delete(sccb_i2c_port);
 }
 
 uint8_t SCCB_Probe(void)
 {
     uint8_t slave_addr = 0x0;
-    // for (size_t i = 1; i < 0x80; i++) {
-    //     i2c_cmd_handle_t cmd = i2c_cmd_link_create();
-    //     i2c_master_start(cmd);
-    //     i2c_master_write_byte(cmd, ( i << 1 ) | WRITE_BIT, ACK_CHECK_EN);
-    //     i2c_master_stop(cmd);
-    //     esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
-    //     i2c_cmd_link_delete(cmd);
-    //     if( ret == ESP_OK) {
-    //         ESP_LOGW(TAG, "Found I2C Device at 0x%02X", i);
-    //     }
-    // }
+
     for (size_t i = 0; i < CAMERA_MODEL_MAX; i++) {
         if (slave_addr == camera_sensor[i].sccb_addr) {
             continue;
@@ -83,7 +105,7 @@ uint8_t SCCB_Probe(void)
         i2c_master_start(cmd);
         i2c_master_write_byte(cmd, ( slave_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
         i2c_master_stop(cmd);
-        esp_err_t ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+        esp_err_t ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
         i2c_cmd_link_delete(cmd);
         if( ret == ESP_OK) {
             return slave_addr;
@@ -101,7 +123,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
     i2c_master_write_byte(cmd, ( slv_addr << 1 ) | WRITE_BIT, ACK_CHECK_EN);
     i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) return -1;
     cmd = i2c_cmd_link_create();
@@ -109,7 +131,7 @@ uint8_t SCCB_Read(uint8_t slv_addr, uint8_t reg)
     i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
     i2c_master_read_byte(cmd, &data, NACK_VAL);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) {
         ESP_LOGE(TAG, "SCCB_Read Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
@@ -126,7 +148,7 @@ uint8_t SCCB_Write(uint8_t slv_addr, uint8_t reg, uint8_t data)
     i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);
     i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) {
         ESP_LOGE(TAG, "SCCB_Write Failed addr:0x%02x, reg:0x%02x, data:0x%02x, ret:%d", slv_addr, reg, data, ret);
@@ -146,7 +168,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
     i2c_master_write_byte(cmd, reg_u8[0], ACK_CHECK_EN);
     i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) return -1;
     cmd = i2c_cmd_link_create();
@@ -154,7 +176,7 @@ uint8_t SCCB_Read16(uint8_t slv_addr, uint16_t reg)
     i2c_master_write_byte(cmd, ( slv_addr << 1 ) | READ_BIT, ACK_CHECK_EN);
     i2c_master_read_byte(cmd, &data, NACK_VAL);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) {
         ESP_LOGE(TAG, "W [%04x]=%02x fail\n", reg, data);
@@ -175,7 +197,7 @@ uint8_t SCCB_Write16(uint8_t slv_addr, uint16_t reg, uint8_t data)
     i2c_master_write_byte(cmd, reg_u8[1], ACK_CHECK_EN);
     i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
     i2c_master_stop(cmd);
-    ret = i2c_master_cmd_begin(SCCB_I2C_PORT, cmd, 1000 / portTICK_RATE_MS);
+    ret = i2c_master_cmd_begin(sccb_i2c_port, cmd, 1000 / portTICK_RATE_MS);
     i2c_cmd_link_delete(cmd);
     if(ret != ESP_OK) {
         ESP_LOGE(TAG, "W [%04x]=%02x %d fail\n", reg, data, i++);

+ 3 - 0
code/components/esp32-camera-master/driver/sensor.c

@@ -13,6 +13,9 @@ const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
     {CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, FRAMESIZE_VGA, false},
     {CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, FRAMESIZE_VGA, false},
     {CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, FRAMESIZE_VGA, false},
+    {CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, FRAMESIZE_VGA, false},
+    {CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, FRAMESIZE_HD, false},
+    {CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, FRAMESIZE_VGA, false},
 };
 
 const resolution_info_t resolution[FRAMESIZE_INVALID] = {

+ 7 - 2
code/components/esp32-camera-master/examples/main/take_picture.c

@@ -38,6 +38,11 @@
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 
+// support IDF 5.x
+#ifndef portTICK_RATE_MS
+#define portTICK_RATE_MS portTICK_PERIOD_MS
+#endif
+
 #include "esp_camera.h"
 
 #define BOARD_WROVER_KIT 1
@@ -94,8 +99,8 @@ static camera_config_t camera_config = {
     .pin_pwdn = CAM_PIN_PWDN,
     .pin_reset = CAM_PIN_RESET,
     .pin_xclk = CAM_PIN_XCLK,
-    .pin_sscb_sda = CAM_PIN_SIOD,
-    .pin_sscb_scl = CAM_PIN_SIOC,
+    .pin_sccb_sda = CAM_PIN_SIOD,
+    .pin_sccb_scl = CAM_PIN_SIOC,
 
     .pin_d7 = CAM_PIN_D7,
     .pin_d6 = CAM_PIN_D6,

+ 1 - 4
code/components/esp32-camera-master/idf_component.yml

@@ -1,5 +1,2 @@
 description: ESP32 compatible driver for OV2640, OV3660, OV5640, OV7670 and OV7725 image sensors.
-targets:
-  - esp32
-  - esp32s2
-  - esp32s3
+url: https://github.com/espressif/esp32-camera

+ 404 - 0
code/components/esp32-camera-master/sensors/bf20a6.c

@@ -0,0 +1,404 @@
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
+//
+// 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.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "sccb.h"
+#include "bf20a6.h"
+#include "bf20a6_regs.h"
+#include "bf20a6_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char *TAG = "bf20a6";
+#endif
+
+#define H8(v) ((v)>>8)
+#define L8(v) ((v)&0xff)
+
+//#define REG_DEBUG_ON
+
+static int read_reg(uint8_t slv_addr, const uint16_t reg)
+{
+    int ret = SCCB_Read(slv_addr, reg);
+    // ESP_LOGI(TAG, "READ Register 0x%02x VALUE: 0x%02x", reg, ret);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "READ REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
+{
+    int ret = SCCB_Write(slv_addr, reg, value);
+#ifdef REG_DEBUG_ON
+    if (ret < 0) {
+        ESP_LOGE(TAG, "WRITE REG 0x%04x FAILED: %d", reg, ret);
+    }
+#endif
+    return ret;
+}
+
+#ifdef DEBUG_PRINT_REG
+static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
+{
+    return (read_reg(slv_addr, reg) & mask) == mask;
+}
+
+static void print_regs(uint8_t slv_addr)
+{
+    vTaskDelay(pdMS_TO_TICKS(100));
+    ESP_LOGI(TAG, "REG list look ======================");
+    for (size_t i = 0xf0; i <= 0xfe; i++) {
+        ESP_LOGI(TAG, "reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 0 ===");
+    write_reg(slv_addr, 0xfe, 0x00); // page 0
+    for (size_t i = 0x03; i <= 0x24; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    for (size_t i = 0x40; i <= 0x95; i++) {
+        ESP_LOGI(TAG, "p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+    ESP_LOGI(TAG, "\npage 3 ===");
+    write_reg(slv_addr, 0xfe, 0x03); // page 3
+    for (size_t i = 0x01; i <= 0x43; i++) {
+        ESP_LOGI(TAG, "p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
+    }
+}
+
+static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = read_reg(slv_addr, regs[i][0]);
+        }
+        i++;
+    }
+    return ret;
+}
+#endif
+
+static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+
+    ret = SCCB_Read(sensor->slv_addr, reg);
+    if (ret < 0) {
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = SCCB_Write(sensor->slv_addr, reg & 0xFF, value);
+    return ret;
+}
+
+static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
+{
+    int i = 0, ret = 0;
+    while (!ret && regs[i][0] != REGLIST_TAIL) {
+        if (regs[i][0] == REG_DLY) {
+            vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
+        } else {
+            ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
+        }
+        i++;
+    }
+    return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret;
+    // Software Reset: clear all registers and reset them to their default values
+    ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
+    if (ret) {
+        ESP_LOGE(TAG, "Software Reset FAILED!");
+        return ret;
+    }
+    vTaskDelay(100 / portTICK_PERIOD_MS);
+
+    ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Camera defaults loaded");
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+    }
+
+    // int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
+
+    return ret;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret = 0;
+    switch (pixformat) {
+    case PIXFORMAT_YUV422:
+        set_reg_bits(sensor, 0x12, 0, 1, 0);
+        break;
+    case PIXFORMAT_RAW:
+        set_reg_bits(sensor, 0x12, 0, 1, 0x1);
+        break;
+    default:
+        ESP_LOGW(TAG, "set_pix unsupport format");
+        ret = -1;
+        break;
+    }
+    if (ret == 0) {
+        sensor->pixformat = pixformat;
+        ESP_LOGD(TAG, "Set pixformat to: %u", pixformat);
+    }
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    int ret = 0;
+    if (framesize > FRAMESIZE_VGA) {
+        return -1;
+    }
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+
+    sensor->status.framesize = framesize;
+
+    // Write MSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x17, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x18, w >> 2);
+
+    ret |= SCCB_Write(sensor->slv_addr, 0x19, 0);
+    ret |= SCCB_Write(sensor->slv_addr, 0x1a, h >> 2);
+
+    // Write LSBs
+    ret |= SCCB_Write(sensor->slv_addr, 0x1b, 0);
+
+    if ((w <= 320) && (h <= 240))     {
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 4));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 4));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 4));
+
+    } else if ((w <= 640) && (h <= 480))     {
+        ret |= SCCB_Write(sensor->slv_addr, 0x17, (80 - w / 8));
+        ret |= SCCB_Write(sensor->slv_addr, 0x18, (80 + w / 8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x19, (60 - h / 8));
+
+        ret |= SCCB_Write(sensor->slv_addr, 0x1a, (60 + h / 8));
+    }
+
+    // Delay
+    vTaskDelay(30 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.hmirror = enable;
+    //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    sensor->status.vflip = enable;
+    //ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
+    ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set v-flip to: %d", enable);
+    }
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    ret = write_reg(sensor->slv_addr, 0xb6, value);
+    if (ret == 0) {
+        sensor->status.colorbar = value;
+        ESP_LOGD(TAG, "Set colorbar to: %d", value);
+    }
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    ret = SCCB_Write(sensor->slv_addr, 0x70, level);
+    if (ret == 0) {
+        ESP_LOGD(TAG, "Set sharpness to: %d", level);
+        sensor->status.sharpness = level;
+    }
+    return ret;
+}
+
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret > 0) {
+        ret &= mask;
+    }
+    return ret;
+}
+
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    if (mask > 0xFF) {
+        ESP_LOGE(TAG, "mask should not more than 0xff");
+    } else {
+        ret = read_reg(sensor->slv_addr, reg);
+    }
+    if (ret < 0) {
+        return ret;
+    }
+    value = (ret & ~mask) | (value & mask);
+
+    if (mask > 0xFF) {
+
+    } else {
+        ret = write_reg(sensor->slv_addr, reg, value);
+    }
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    // write_reg(sensor->slv_addr, 0xfe, 0x00);
+    sensor->status.brightness = SCCB_Read(sensor->slv_addr, 0x6f);
+    sensor->status.contrast = SCCB_Read(sensor->slv_addr, 0xd6);
+    sensor->status.saturation = 0;
+    sensor->status.sharpness = SCCB_Read(sensor->slv_addr, 0x70);
+    sensor->status.denoise = 0;
+    sensor->status.ae_level = 0;
+    sensor->status.gainceiling = SCCB_Read(sensor->slv_addr, 0x13);
+    sensor->status.awb = 0;
+    sensor->status.dcw = 0;
+    sensor->status.agc = 0;
+    sensor->status.aec = 0;
+    sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
+    sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
+    sensor->status.colorbar = 0;
+    sensor->status.bpc = 0;
+    sensor->status.wpc = 0;
+    sensor->status.raw_gma = 0;
+    sensor->status.lenc = 0;
+    sensor->status.quality = 0;
+    sensor->status.special_effect = 0;
+    sensor->status.wb_mode = 0;
+    sensor->status.awb_gain = 0;
+    sensor->status.agc_gain = 0;
+    sensor->status.aec_value = 0;
+    sensor->status.aec2 = 0;
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val)
+{
+    ESP_LOGW(TAG, "dummy Unsupported");
+    return -1;
+}
+static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
+{
+    ESP_LOGW(TAG, "gainceiling Unsupported");
+    return -1;
+}
+
+int bf20a6_detect(int slv_addr, sensor_id_t *id)
+{
+    if (BF20A6_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, SENSOR_ID_LOW);
+        uint8_t MIDH = SCCB_Read(slv_addr, SENSOR_ID_HIGH);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (BF20A6_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int bf20a6_init(sensor_t *sensor)
+{
+    sensor->init_status = init_status;
+    sensor->reset = reset;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_contrast = set_dummy;
+    sensor->set_brightness = set_dummy;
+    sensor->set_saturation = set_dummy;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_denoise = set_dummy;
+    sensor->set_gainceiling = set_gainceiling_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_whitebal = set_dummy;
+    sensor->set_gain_ctrl = set_dummy;
+    sensor->set_exposure_ctrl = set_dummy;
+    sensor->set_hmirror = set_hmirror; // set_hmirror;
+    sensor->set_vflip = set_vflip; // set_vflip;
+
+    sensor->set_aec2 = set_dummy;
+    sensor->set_awb_gain = set_dummy;
+    sensor->set_agc_gain = set_dummy;
+    sensor->set_aec_value = set_dummy;
+
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+    sensor->set_dcw = set_dummy;
+    sensor->set_bpc = set_dummy;
+    sensor->set_wpc = set_dummy;
+
+    sensor->set_raw_gma = set_dummy;
+    sensor->set_lenc = set_dummy;
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_res_raw = NULL;
+    sensor->set_pll = NULL;
+    sensor->set_xclk = NULL;
+
+    ESP_LOGD(TAG, "BF20A6 Attached");
+    return 0;
+}

+ 6 - 5
code/components/esp32-camera-master/sensors/gc0308.c

@@ -88,10 +88,10 @@ static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t
     return ret;
 }
 
-static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
+static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
 {
     int i = 0, ret = 0;
-    while (!ret && regs[i][0] != REGLIST_TAIL) {
+    while (!ret && (i < regs_size)) {
         if (regs[i][0] == REG_DLY) {
             vTaskDelay(regs[i][1] / portTICK_PERIOD_MS);
         } else {
@@ -132,11 +132,12 @@ static int reset(sensor_t *sensor)
         ESP_LOGE(TAG, "Software Reset FAILED!");
         return ret;
     }
-    vTaskDelay(100 / portTICK_PERIOD_MS);
-    ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs);
+
+    vTaskDelay(80 / portTICK_PERIOD_MS);
+    ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
     if (ret == 0) {
         ESP_LOGD(TAG, "Camera defaults loaded");
-        vTaskDelay(100 / portTICK_PERIOD_MS);
+        vTaskDelay(80 / portTICK_PERIOD_MS);
         write_reg(sensor->slv_addr, 0xfe, 0x00);
 #ifdef CONFIG_IDF_TARGET_ESP32
         set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1);  //frequency division for esp32, ensure pclk <= 15MHz

+ 27 - 0
code/components/esp32-camera-master/sensors/private_include/bf20a6.h

@@ -0,0 +1,27 @@
+
+#ifndef __BF20A6_H__
+#define __BF20A6_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int bf20a6_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int bf20a6_init(sensor_t *sensor);
+
+#endif // __BF20A6_H__

+ 12 - 0
code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h

@@ -0,0 +1,12 @@
+/*
+ * BF20A6 register definitions.
+ */
+#ifndef __BF20A6_REG_REGS_H__
+#define __BF20A6_REG_REGS_H__
+
+#define SENSOR_ID_HIGH 0XFC
+#define SENSOR_ID_LOW 0XFD
+#define RESET_RELATED   0XF2
+
+
+#endif //__BF20A6_REG_REGS_H__

+ 158 - 0
code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h

@@ -0,0 +1,158 @@
+
+#include <stdint.h>
+
+#define REG_DLY 0xffff
+#define REGLIST_TAIL 0xffff /* Array end token */
+
+static const uint16_t bf20a6_default_init_regs[][2] = {
+    {0xf2,0x01},
+    {0x12,0x20},
+    {0x3a,0x00},
+    {0xe1,0x92},
+    {0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
+    {0xe0,0x00},
+    {0x2a,0x98},
+    {0xcd,0x17},
+    {0xc0,0x10},
+    {0xc6,0x1d},
+    {0x10,0x35},
+    {0xe2,0x09},
+    {0xe4,0x72},
+    {0xe5,0x22},
+    {0xe6,0x24},
+    {0xe7,0x64},
+    {0xe8,0xa2}, // DVP:a2},  SPI:f2        VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
+    {0x4a,0x00},
+    {0x00,0x03},
+    {0x1f,0x02},
+    {0x22,0x02},
+    {0x0c,0x31},
+
+    {0x00,0x00},
+    {0x60,0x81},
+    {0x61,0x81},
+
+    {0xa0,0x08},
+    {0x01,0x1a},
+    // {0x01,0x1a},
+    // {0x01,0x1a},
+    // {0x02,0x15},
+    // {0x02,0x15},
+    {0x02,0x15},
+    {0x13,0x08},
+    {0x8a,0x96},
+    {0x8b,0x06},
+    {0x87,0x18},
+
+
+    {0x34,0x48}, // lens
+    {0x35,0x40},
+    {0x36,0x40},
+
+    {0x71,0x44},
+    {0x72,0x48},
+    {0x74,0xa2},
+    {0x75,0xa9},
+    {0x78,0x12},
+    {0x79,0xa0},
+    {0x7a,0x94},
+    {0x7c,0x97},
+    {0x40,0x30},
+    {0x41,0x30},
+    {0x42,0x28},
+    {0x43,0x1f},
+    {0x44,0x1c},
+    {0x45,0x16},
+    {0x46,0x13},
+    {0x47,0x10},
+    {0x48,0x0D},
+    {0x49,0x0C},
+    {0x4B,0x0A},
+    {0x4C,0x0B},
+    {0x4E,0x09},
+    {0x4F,0x08},
+    {0x50,0x08},
+
+
+    {0x5f,0x29},
+    {0x23,0x33},
+    {0xa1,0x10}, // AWB
+    {0xa2,0x0d},
+    {0xa3,0x30},
+    {0xa4,0x06},
+    {0xa5,0x22},
+    {0xa6,0x56},
+    {0xa7,0x18},
+    {0xa8,0x1a},
+    {0xa9,0x12},
+    {0xaa,0x12},
+    {0xab,0x16},
+    {0xac,0xb1},
+    {0xba,0x12},
+    {0xbb,0x12},
+    {0xad,0x12},
+    {0xae,0x56},
+    {0xaf,0x0a},
+    {0x3b,0x30},
+    {0x3c,0x12},
+    {0x3d,0x22},
+    {0x3e,0x3f},
+    {0x3f,0x28},
+    {0xb8,0xc3},
+    {0xb9,0xa3},
+    {0x39,0x47}, // pure color threshold
+    {0x26,0x13},
+    {0x27,0x16},
+    {0x28,0x14},
+    {0x29,0x18},
+    {0xee,0x0d},
+
+        
+    {0x13,0x05},
+    {0x24,0x3C},
+    {0x81,0x20},
+    {0x82,0x40},
+    {0x83,0x30},
+    {0x84,0x58},
+    {0x85,0x30},
+    {0x92,0x08},
+    {0x86,0x80},
+    {0x8a,0x96},
+    {0x91,0xff},
+    {0x94,0x62},
+    {0x9a,0x18}, // outdoor threshold
+    {0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
+    {0x51,0x17}, // color normal
+    {0x52,0x03},
+    {0x53,0x5F},
+    {0x54,0x47},
+    {0x55,0x66},
+    {0x56,0x0F},
+    {0x7e,0x14},
+    {0x57,0x36}, // color
+    {0x58,0x2A},
+    {0x59,0xAA},
+    {0x5a,0xA8},
+    {0x5b,0x43},
+    {0x5c,0x10},
+    {0x5d,0x00},
+    {0x7d,0x36},
+    {0x5e,0x10},
+
+    {0xd6,0x88}, // contrast
+    {0xd5,0x20}, // bright
+    {0xb0,0x84}, // low light ctrl in gray section
+    {0xb5,0x08}, // the threshold of GLB_GAIN
+    {0xb1,0xc8}, // saturation
+    {0xb2,0xc0},
+    {0xb3,0xd0},
+    {0xb4,0xB0},
+
+    {0x32,0x10},
+    // {0x8a,0x00},
+    // {0x8b,0x10},
+    {0xa0,0x09},
+    {0x00,0x03},
+    {0x0b,0x02},
+    {REGLIST_TAIL, 0x00},
+};

+ 17 - 4
code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h

@@ -3,10 +3,9 @@
 
 #include <stdint.h>
 
-#define REG_DLY 0xffff
-#define REGLIST_TAIL 0x0000 /* Array end token */
+#define REG_DLY 0xff
 
-static const uint16_t gc0308_sensor_default_regs[][2] = {
+static const uint8_t gc0308_sensor_default_regs[][2] = {
     {0xfe, 0x00},
     {0xec, 0x20},
     {0x05, 0x00},
@@ -239,7 +238,21 @@ static const uint16_t gc0308_sensor_default_regs[][2] = {
     {0x65, 0xd3},
     {0x66, 0x60},
     {0xfe, 0x00},
-    {REGLIST_TAIL, 0x00},
+
+    {0x01, 0x32},   //frame setting                            
+	{0x02, 0x0c},                                  
+	{0x0f, 0x01},                                                                                                                     
+	{0xe2, 0x00},  
+	{0xe3, 0x78},                                                             
+	{0xe4, 0x00},      
+	{0xe5, 0xfe},  
+	{0xe6, 0x01},  
+	{0xe7, 0xe0},  
+	{0xe8, 0x01},  
+	{0xe9, 0xe0},  
+	{0xea, 0x01},  
+	{0xeb, 0xe0},
+	{0xfe, 0x00},
 };
 
 #endif

+ 2 - 1
code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h

@@ -42,7 +42,8 @@ static const DRAM_ATTR uint16_t sensor_default_regs[][2] = {
     {ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
 
     //sys reset
-    {0x3000, 0x00},
+    {0x3000, 0x20}, // reset MCU
+    {REG_DLY, 10}, // delay 10ms
     {0x3002, 0x1c},
 
     //clock enable

+ 31 - 0
code/components/esp32-camera-master/sensors/private_include/sc030iot.h

@@ -0,0 +1,31 @@
+/*
+ *
+ * SC030IOT DVP driver.
+ *
+ */
+#ifndef __SC030IOT_H__
+#define __SC030IOT_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int sc030iot_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int sc030iot_init(sensor_t *sensor);
+
+#endif // __SC030IOT_H__

+ 491 - 0
code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h

@@ -0,0 +1,491 @@
+//version: V01P00_20220303
+//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
+//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
+//port  0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
+//I2C Mode    :0:Normal 8Addr,8Data//  1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
+//I2C Mode    :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
+//Out Format  :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
+//MCLK Speed  :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
+//pin  :BIT0 pwdn// BIT1:reset
+//avdd  0:3.3V// 1:2.5V// 2:1.8V
+//dovdd  0:2.8V// 1:2.5V// 2:1.8V
+//dvdd  0:1.8V// 1:1.5V// 2:1.2V
+
+/*
+[DataBase]
+DBName=Dothinkey
+
+[Vendor]
+VendorName=SmartSens
+
+[Sensor]
+SensorName=SC031IOT
+width=640
+height=480
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xfa
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x46
+outformat=0
+mclk=20
+avdd=2.80000
+dovdd=2.800000
+dvdd=1.5
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.0000
+VPP=0.000000
+*/
+#include <stdint.h>
+
+static const uint8_t sc030iot_default_init_regs[][2] = {
+    {0xf0, 0x30},
+    {0x01, 0xff},
+    {0x02, 0xff},
+    {0x22, 0x07},
+    {0x19, 0xff},
+    {0x3f, 0x82},
+    {0x30, 0x02},
+    {0xf0, 0x01},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0x72, 0x20},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x75, 0x10},
+    {0x76, 0x81},
+    {0x77, 0x88},
+    {0x78, 0xe1},
+    {0x79, 0x01},
+    {0xf5, 0x01},
+    {0xf4, 0x0a},
+    {0xf0, 0x36},
+    {0x37, 0x79},
+    {0x31, 0x82},
+    {0x3e, 0x60},
+    {0x30, 0xf0},
+    {0x33, 0x33},
+    {0xf0, 0x32},
+    {0x48, 0x02},
+    {0xf0, 0x33},
+    {0x02, 0x12},
+    {0x7c, 0x02},
+    {0x7d, 0x0e},
+    {0xa2, 0x04},
+    {0x5e, 0x06},
+    {0x5f, 0x0a},
+    {0x0b, 0x58},
+    {0x06, 0x38},
+    {0xf0, 0x32},
+    {0x48, 0x02},
+    {0xf0, 0x39},
+    {0x02, 0x70},
+    {0xf0, 0x45},
+    {0x09, 0x1c},
+    {0xf0, 0x37},
+    {0x22, 0x0d},
+    {0xf0, 0x33},
+    {0x33, 0x10},
+    {0xb1, 0x80},
+    {0x34, 0x40},
+    {0x0b, 0x54},
+    {0xb2, 0x78},
+    {0xf0, 0x36},
+    {0x11, 0x80},
+    {0xf0, 0x30},
+    {0x38, 0x44},
+    {0xf0, 0x33},
+    {0xb3, 0x51},
+    {0x01, 0x10},
+    {0x0b, 0x6c},
+    {0x06, 0x24},
+    {0xf0, 0x36},
+    {0x31, 0x82},
+    {0x3e, 0x60},
+    {0x30, 0xf0},
+    {0x33, 0x33},
+    {0xf0, 0x34},
+    {0x9f, 0x02},
+    {0xa6, 0x40},
+    {0xa7, 0x47},
+    {0xe8, 0x5f},
+    {0xa8, 0x51},
+    {0xa9, 0x44},
+    {0xe9, 0x36},
+    {0xf0, 0x33},
+    {0xb3, 0x51},
+    {0x64, 0x17},
+    {0x90, 0x01},
+    {0x91, 0x03},
+    {0x92, 0x07},
+    {0x01, 0x10},
+    {0x93, 0x10},
+    {0x94, 0x10},
+    {0x95, 0x10},
+    {0x96, 0x01},
+    {0x97, 0x07},
+    {0x98, 0x1f},
+    {0x99, 0x10},
+    {0x9a, 0x20},
+    {0x9b, 0x28},
+    {0x9c, 0x28},
+    {0xf0, 0x36},
+    {0x70, 0x54},
+    {0xb6, 0x40},
+    {0xb7, 0x41},
+    {0xb8, 0x43},
+    {0xb9, 0x47},
+    {0xba, 0x4f},
+    {0xb0, 0x8b},
+    {0xb1, 0x8b},
+    {0xb2, 0x8b},
+    {0xb3, 0x9b},
+    {0xb4, 0xb8},
+    {0xb5, 0xf0},
+    {0x7e, 0x41},
+    {0x7f, 0x47},
+    {0x77, 0x80},
+    {0x78, 0x84},
+    {0x79, 0x8a},
+    {0xa0, 0x47},
+    {0xa1, 0x5f},
+    {0x96, 0x43},
+    {0x97, 0x44},
+    {0x98, 0x54},
+    {0xf0, 0x00},
+    {0xf0, 0x01},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0xf0, 0x36},
+    {0x37, 0x74},
+    {0xf0, 0x3f},
+    {0x03, 0xa1},
+    {0xf0, 0x36},//cvbs_off
+    {0x11, 0x80},
+    {0xf0, 0x01},
+    {0x79, 0xc1},
+    {0xf0, 0x37},
+    {0x24, 0x21},
+    {0xf0, 0x36},
+    {0x41, 0x00},
+    {0xea, 0x09},
+    {0xeb, 0x03},
+    {0xec, 0x19},
+    {0xed, 0x38},
+    {0xe9, 0x30},
+    {0xf0, 0x33},
+    {0x33, 0x00},
+    {0x34, 0x00},
+    {0xb1, 0x00},
+    {0xf0, 0x00},
+    {0xe0, 0x04},
+    {0xf0, 0x01},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0xf0, 0x36},
+    {0x32, 0x44},
+    {0xf0, 0x36},
+    {0x3e, 0xe0},
+    {0x70, 0x56},
+    {0x7c, 0x43},
+    {0x7d, 0x47},
+    {0x74, 0x00},
+    {0x75, 0x00},
+    {0x76, 0x00},
+    {0xa0, 0x47},
+    {0xa1, 0x5f},
+    {0x96, 0x22},
+    {0x97, 0x22},
+    {0x98, 0x22},
+    {0xf0, 0x00},
+    {0x72, 0x38},
+    {0x7a, 0x80},
+    {0x85, 0x18},
+    {0x9b, 0x35},
+    {0x9e, 0x20},
+    {0xd0, 0x66},
+    {0xd1, 0x34},
+    {0Xd3, 0x44},
+    {0xd6, 0x44},
+    {0xb0, 0x41},
+    {0xb2, 0x48},
+    {0xb3, 0xf4},
+    {0xb4, 0x0b},
+    {0xb5, 0x78},
+    {0xba, 0xff},
+    {0xbb, 0xc0},
+    {0xbc, 0x90},
+    {0xbd, 0x3a},
+    {0xc1, 0x67},
+    {0xf0, 0x01},
+    {0x20, 0x11},
+    {0x23, 0x90},
+    {0x24, 0x15},
+    {0x25, 0x87},
+    {0xbc, 0x9f},
+    {0xbd, 0x3a},
+    {0x48, 0xe6},
+    {0x49, 0xc0},
+    {0x4a, 0xd0},
+    {0x4b, 0x48},
+
+    // [cvbs_on]
+    {0xf0, 0x36},
+    {0x11, 0x00},
+    {0xf0, 0x01},
+    {0x79, 0xf1},
+
+    // [cvbs_off]
+    {0xf0, 0x36},
+    {0x11, 0x80},
+    {0xf0, 0x01},
+    {0x79, 0xc1},
+};
+
+/*
+[Sensor]
+SensorName=SC031IOT
+width=640
+height=480
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xfa
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x46
+outformat=0
+mclk=27
+avdd=2.80000
+dovdd=2.800000
+dvdd=1.5
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.0000
+VPP=0.000000
+*/
+/* 27M MCLK, 30fps
+static const uint8_t sc030iot_default_init_regs[][2] = {
+    {0xf0, 0x30},
+    {0x01, 0xff},
+    {0x02, 0xff},
+    {0x22, 0x07},
+    {0x19, 0xff},
+    {0x3f, 0x82},
+    {0x30, 0x02},
+    {0xf0, 0x01},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0x72, 0x20},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x75, 0x10},
+    {0x76, 0x81},
+    {0x77, 0x88},
+    {0x78, 0xe1},
+    {0x79, 0x01},
+    {0xf5, 0x01},
+    {0xf4, 0x0a},
+    {0xf0, 0x36},
+    {0x37, 0x79},
+    {0x31, 0x82},
+    {0x3e, 0x60},
+    {0x30, 0xf0},
+    {0x33, 0x33},
+    {0xf0, 0x32},
+    {0x48, 0x02},
+    {0xf0, 0x33},
+    {0x02, 0x12},
+    {0x7c, 0x02},
+    {0x7d, 0x0e},
+    {0xa2, 0x04},
+    {0x5e, 0x06},
+    {0x5f, 0x0a},
+    {0x0b, 0x58},
+    {0x06, 0x38},
+    {0xf0, 0x32},
+    {0x48, 0x02},
+    {0xf0, 0x39},
+    {0x02, 0x70},
+    {0xf0, 0x45},
+    {0x09, 0x1c},
+    {0xf0, 0x37},
+    {0x22, 0x0d},
+    {0xf0, 0x33},
+    {0x33, 0x10},
+    {0xb1, 0x80},
+    {0x34, 0x40},
+    {0x0b, 0x54},
+    {0xb2, 0x78},
+    {0xf0, 0x36},
+    {0x11, 0x80},
+    {0xf0, 0x30},
+    {0x38, 0x44},
+    {0xf0, 0x33},
+    {0xb3, 0x51},
+    {0x01, 0x10},
+    {0x0b, 0x6c},
+    {0x06, 0x24},
+    {0xf0, 0x36},
+    {0x31, 0x82},
+    {0x3e, 0x60},
+    {0x30, 0xf0},
+    {0x33, 0x33},
+    {0xf0, 0x34},
+    {0x9f, 0x02},
+    {0xa6, 0x40},
+    {0xa7, 0x47},
+    {0xe8, 0x5f},
+    {0xa8, 0x51},
+    {0xa9, 0x44},
+    {0xe9, 0x36},
+    {0xf0, 0x33},
+    {0xb3, 0x51},
+    {0x64, 0x17},
+    {0x90, 0x01},
+    {0x91, 0x03},
+    {0x92, 0x07},
+    {0x01, 0x10},
+    {0x93, 0x10},
+    {0x94, 0x10},
+    {0x95, 0x10},
+    {0x96, 0x01},
+    {0x97, 0x07},
+    {0x98, 0x1f},
+    {0x99, 0x10},
+    {0x9a, 0x20},
+    {0x9b, 0x28},
+    {0x9c, 0x28},
+    {0xf0, 0x36},
+    {0x70, 0x54},
+    {0xb6, 0x40},
+    {0xb7, 0x41},
+    {0xb8, 0x43},
+    {0xb9, 0x47},
+    {0xba, 0x4f},
+    {0xb0, 0x8b},
+    {0xb1, 0x8b},
+    {0xb2, 0x8b},
+    {0xb3, 0x9b},
+    {0xb4, 0xb8},
+    {0xb5, 0xf0},
+    {0x7e, 0x41},
+    {0x7f, 0x47},
+    {0x77, 0x80},
+    {0x78, 0x84},
+    {0x79, 0x8a},
+    {0xa0, 0x47},
+    {0xa1, 0x5f},
+    {0x96, 0x43},
+    {0x97, 0x44},
+    {0x98, 0x54},
+    {0xf0, 0x00},
+    {0xf0, 0x01},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0xf0, 0x36},
+    {0x37, 0x74},
+    {0xf0, 0x3f},
+    {0x03, 0x93},
+    {0xf0, 0x36},//cvbs_off
+    {0x11, 0x80},
+    {0xf0, 0x01},
+    {0x79, 0xc1},
+    {0xf0, 0x37},
+    {0x24, 0x21},
+    {0xf0, 0x36},
+    {0x41, 0x00},
+    {0xe9, 0x2c},
+    {0xf0, 0x33},
+    {0x33, 0x00},
+    {0x34, 0x00},
+    {0xb1, 0x00},
+    {0xf0, 0x00},
+    {0xe0, 0x04},
+    {0xf0, 0x01},
+    {0x73, 0x00},
+    {0x74, 0xe0},
+    {0x70, 0x00},
+    {0x71, 0x80},
+    {0xf0, 0x36},
+    {0x32, 0x44},
+    {0xf0, 0x36},
+    {0x3e, 0xe0},
+    {0x70, 0x56},
+    {0x7c, 0x43},
+    {0x7d, 0x47},
+    {0x74, 0x00},
+    {0x75, 0x00},
+    {0x76, 0x00},
+    {0xa0, 0x47},
+    {0xa1, 0x5f},
+    {0x96, 0x22},
+    {0x97, 0x22},
+    {0x98, 0x22},
+    {0xf0, 0x00},
+    {0x72, 0x38},
+    {0x7a, 0x80},
+    {0x85, 0x18},
+    {0x9b, 0x35},
+    {0x9e, 0x20},
+    {0xd0, 0x66},
+    {0xd1, 0x34},
+    {0Xd3, 0x44},
+    {0xd6, 0x44},
+    {0xb0, 0x41},
+    {0xb2, 0x48},
+    {0xb3, 0xf4},
+    {0xb4, 0x0b},
+    {0xb5, 0x78},
+    {0xba, 0xff},
+    {0xbb, 0xc0},
+    {0xbc, 0x90},
+    {0xbd, 0x3a},
+    {0xc1, 0x67},
+    {0xf0, 0x01},
+    {0x20, 0x11},
+    {0x23, 0x90},
+    {0x24, 0x15},
+    {0x25, 0x87},
+    {0xbc, 0x9f},
+    {0xbd, 0x3a},
+    {0x48, 0xe6},
+    {0x49, 0xc0},
+    {0x4a, 0xd0},
+    {0x4b, 0x48},
+
+    // [cvbs_on]
+    {0xf0, 0x36},
+    {0x11, 0x00},
+    {0xf0, 0x01},
+    {0x79, 0xf1},
+
+    // [cvbs_off]
+    {0xf0, 0x36},
+    {0x11, 0x80},
+    {0xf0, 0x01},
+    {0x79, 0xc1},
+};
+
+*/

+ 31 - 0
code/components/esp32-camera-master/sensors/private_include/sc101iot.h

@@ -0,0 +1,31 @@
+/*
+ *
+ * SC101IOT DVP driver.
+ *
+ */
+#ifndef __SC101IOT_H__
+#define __SC101IOT_H__
+
+#include "sensor.h"
+
+/**
+ * @brief Detect sensor pid
+ *
+ * @param slv_addr SCCB address
+ * @param id Detection result
+ * @return
+ *     0:       Can't detect this sensor
+ *     Nonzero: This sensor has been detected
+ */
+int sc101iot_detect(int slv_addr, sensor_id_t *id);
+
+/**
+ * @brief initialize sensor function pointers
+ *
+ * @param sensor pointer of sensor
+ * @return
+ *      Always 0
+ */
+int sc101iot_init(sensor_t *sensor);
+
+#endif // __SC101IOT_H__

+ 257 - 0
code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h

@@ -0,0 +1,257 @@
+//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
+//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
+//port  0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
+//I2C Mode    :0:Normal 8Addr,8Data//  1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
+//I2C Mode    :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
+//Out Format  :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
+//MCLK Speed  :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
+//pin  :BIT0 pwdn// BIT1:reset
+//avdd  0:2.8V// 1:2.5V// 2:1.8V
+//dovdd  0:2.8V// 1:2.5V// 2:1.8V
+//dvdd  0:1.8V// 1:1.5V// 2:1.2V
+/*
+[DataBase]
+DBName=DemoSens
+
+[Vendor]
+VendorName=SmartSens
+I2C_CRC=0
+
+[Sensor]
+SensorName=SC101AP_raw
+width=1280
+height=720
+port=1
+type=2
+pin=3
+SlaveID=0xd0
+mode=0
+FlagReg=0xf7
+FlagMask=0xff
+FlagData=0xda
+FlagReg1=0xf8
+FlagMask1=0xff
+FlagData1=0x4a
+outformat=0
+mclk=20
+avdd=2.800000
+dovdd=2.800000
+dvdd=1.200000
+
+Ext0=0
+Ext1=0
+Ext2=0
+AFVCC=0.00
+VPP=0.000000
+*/
+#include <stdint.h>
+
+static const uint8_t sc101iot_default_init_regs[][2] = {
+#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
+/* Here are some test results:
+#   size    xclk    fps    pic       pclk                       
+#  ------- ------- ------ --------- ------- --- --- --- --- --- 
+#   720p    4       3      err                                  
+#   720p    8       5      normal    15                         
+#   720p    10      7.8    normal    19                         
+#   720p    20      15     warning   37.5                       
+#   VGA     8       6      normal    
+#   VGA     20      16     normal    
+
+*/
+    {0xf0, 0x30},
+    {0x01, 0xff},
+    {0x02, 0xe0},
+    {0x30, 0x10},
+    {0x3f, 0x81},
+    {0xf0, 0x00},
+    {0x70, 0x6b},
+    {0x72, 0x30},
+    {0x84, 0xb4},
+    {0x8b, 0x00},
+    {0x8c, 0x20},
+    {0x8d, 0x02},
+    {0x8e, 0xec},
+    {0x9e, 0x10},
+    {0xb0, 0xc1},
+    {0xc8, 0x10},
+    {0xc9, 0x10},
+    {0xc6, 0x00},
+    {0xe0, 0x0f},
+    {0xb5, 0xf0},
+    {0xde, 0x80},
+    {0xb5, 0xf0},
+    {0xde, 0x80},
+    {0xb2, 0x50},
+    {0xb3, 0xfc},
+    {0xb4, 0x40},
+    {0xb5, 0xc0},
+    {0xb6, 0x50},
+    {0xb7, 0xfc},
+    {0xb8, 0x40},
+    {0xb9, 0xc0},
+    {0xba, 0xff},
+    {0xbb, 0xcc},
+    {0xbc, 0xa9},
+    {0xbd, 0x7d},
+    {0xc1, 0x77},
+    {0xf0, 0x01},
+    {0x70, 0x02},
+    {0x71, 0x02},
+    {0x72, 0x50},
+    {0x73, 0x02},
+    {0x74, 0xd2},
+    {0x75, 0x20},
+    {0x76, 0x81},
+    {0x77, 0x8c},
+    {0x78, 0x81},
+    {0xf4, 0x01},
+    {0xf5, 0x00},
+    {0xf6, 0x00},
+    {0xf0, 0x36},
+    {0x40, 0x03},
+    {0x41, 0x01},
+    {0xf0, 0x39},
+    {0x02, 0x70},
+    {0xf0, 0x32},
+    {0x41, 0x00},
+    {0x43, 0x01},
+    {0x48, 0x02},
+    {0xf0, 0x45},
+    {0x09, 0x20},
+    {0xf0, 0x33},
+    {0x33, 0x10},
+    {0xf0, 0x30},
+    {0x38, 0x44},
+    {0xf0, 0x39},
+    {0x07, 0x00},
+    {0x08, 0x19},
+    {0x47, 0x00},
+    {0x48, 0x00},
+    {0xf0, 0x37},
+    {0x24, 0x31},
+    {0xf0, 0x34},
+    {0x9f, 0x02},
+    {0xa6, 0x51},
+    {0xa7, 0x57},
+    {0xe8, 0x5f},
+    {0xa8, 0x50},
+    {0xa9, 0x50},
+    {0xe9, 0x50},
+    {0xf0, 0x33},
+    {0xb3, 0x58},
+    {0xb2, 0x78},
+    {0xf0, 0x34},
+    {0x9f, 0x03},
+    {0xa6, 0x51},
+    {0xa7, 0x57},
+    {0xaa, 0x01},
+    {0xab, 0x28},
+    {0xac, 0x01},
+    {0xad, 0x38},
+    {0xf0, 0x33},
+    {0x0a, 0x01},
+    {0x0b, 0x28},
+    {0xf0, 0x33},
+    {0x64, 0x0f},
+    {0xec, 0x51},
+    {0xed, 0x57},
+    {0x06, 0x58},
+    {0xe9, 0x58},
+    {0xeb, 0x68},
+    {0xf0, 0x33},
+    {0x64, 0x0f},
+    {0xf0, 0x36},
+    {0x70, 0xdf},
+    {0xb6, 0x40},
+    {0xb7, 0x51},
+    {0xb8, 0x53},
+    {0xb9, 0x57},
+    {0xba, 0x5f},
+    {0xb0, 0x84},
+    {0xb1, 0x82},
+    {0xb2, 0x84},
+    {0xb3, 0x88},
+    {0xb4, 0x90},
+    {0xb5, 0x90},
+    {0xf0, 0x36},
+    {0x7e, 0x50},
+    {0x7f, 0x51},
+    {0x77, 0x81},
+    {0x78, 0x86},
+    {0x79, 0x89},
+    {0xf0, 0x36},
+    {0x70, 0xdf},
+    {0x9c, 0x51},
+    {0x9d, 0x57},
+    {0x90, 0x54},
+    {0x91, 0x54},
+    {0x92, 0x56},
+    {0xf0, 0x36},
+    {0xa0, 0x51},
+    {0xa1, 0x57},
+    {0x96, 0x33},
+    {0x97, 0x43},
+    {0x98, 0x43},
+    {0xf0, 0x36},
+    {0x70, 0xdf},
+    {0x7c, 0x40},
+    {0x7d, 0x53},
+    {0x74, 0xd0},
+    {0x75, 0xf0},
+    {0x76, 0xf0},
+    {0xf0, 0x37},
+    {0x0f, 0xd5},
+    {0x7a, 0x40},
+    {0x7b, 0x57},
+    {0x71, 0x09},
+    {0x72, 0x09},
+    {0x73, 0x05},
+    {0xf0, 0x33},
+    {0x01, 0x44},
+    {0xf0, 0x36},
+    {0x37, 0xfb},
+    {0xf0, 0x36},
+    {0x3c, 0x0d},
+    {0xf0, 0x33},
+    {0x14, 0x95},
+    {0xf0, 0x33},
+    {0x8f, 0x80},
+    {0xf0, 0x37},
+    {0x27, 0x14},
+    {0x28, 0x03},
+    {0xf0, 0x36},
+    {0x37, 0xf4},
+    {0xf0, 0x33},
+    {0x01, 0x44},
+    {0xf0, 0x36},
+    {0x79, 0x89},
+    {0xf0, 0x34},
+    {0xac, 0x01},
+    {0xad, 0x40},
+    {0xf0, 0x33},
+    {0xeb, 0x70},
+    {0xf0, 0x34},
+    {0xa8, 0x50},
+    {0xa9, 0x50},
+    {0xf0, 0x33},
+    {0xb3, 0x58},
+    {0xf0, 0x36},
+    {0x11, 0x80},
+    {0xf0, 0x36},
+    {0x41, 0x51},
+    {0xf0, 0x3f},
+    {0x03, 0x09},	  
+    {0xf0, 0x32},
+    {0x0c, 0x06},
+    {0x0d, 0x82},
+    {0x0e, 0x02},
+    {0x0f, 0xee},
+    {0xf0, 0x36},
+    {0xea, 0x09},
+    {0xeb, 0xf5},
+    {0xec, 0x11},
+    {0xed, 0x27},
+    {0xe9, 0x20},
+#endif
+};

+ 335 - 0
code/components/esp32-camera-master/sensors/sc030iot.c

@@ -0,0 +1,335 @@
+/*
+ * SC030IOT driver.
+ * 
+ * Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
+ *
+ * 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.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "sc030iot.h"
+#include "sc030iot_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "sc030";
+#endif
+
+#define SC030_SENSOR_ID_HIGH_REG    0XF7
+#define SC030_SENSOR_ID_LOW_REG     0XF8
+#define SC030_MAX_FRAME_WIDTH       (640)
+#define SC030_MAX_FRAME_HIGH        (480)
+
+// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
+{
+    int ret = 0;
+    uint8_t reg_high = (reg>>8) & 0xFF;
+    uint8_t reg_low = reg & 0xFF;
+
+    if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+        return -1;
+    }
+
+    ret = SCCB_Read(sensor->slv_addr, reg_low);
+    if(ret > 0){
+        ret &= reg_value_mask;
+    }
+    return ret;
+}
+
+// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    uint8_t reg_high = (reg>>8) & 0xFF;
+    uint8_t reg_low = reg & 0xFF;
+
+    if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+        return -1;
+    }
+    
+    ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
+    return ret;
+}
+
+static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
+{
+    int i=0, res = 0;
+    while (i<regs_entry_len) {
+        res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
+        if (res) {
+            return res;
+        }
+        i++;
+    }
+    return res;
+}
+
+static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+    ret = get_reg(sensor, reg, 0xff);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
+    return ret;
+}
+
+#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
+#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
+#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    if(enable) {
+        SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
+    } else {
+        SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
+    }
+
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    if(enable) {
+        SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
+    } else {
+        SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
+    }
+
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
+
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
+    WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
+    WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
+
+    return ret;
+}
+
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
+    WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
+    WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
+    WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
+    WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
+    
+    return ret;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
+    WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
+
+    return ret;
+}
+
+static int set_awb_gain(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
+    WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
+    WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
+    WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
+    WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
+    WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
+    return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
+    
+    // Delay
+    vTaskDelay(50 / portTICK_PERIOD_MS);
+
+    // ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
+    // ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
+    if (ret) {
+        ESP_LOGE(TAG, "reset fail");
+    }
+    return ret;
+}
+
+static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
+{
+    int ret = 0;
+    //sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
+    WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
+    WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
+    WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
+
+    //sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
+    WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
+    WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
+    WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
+
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
+        goto err; 
+    }
+
+    uint16_t offset_x = (640-w) /2;   
+    uint16_t offset_y = (480-h) /2;
+    
+    if(set_window(sensor, offset_x, offset_y, w, h)) {
+        goto err; 
+    }
+    
+    sensor->status.framesize = framesize;
+    return 0;
+err:
+    ESP_LOGE(TAG, "frame size err");
+    return -1;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret=0;
+    sensor->pixformat = pixformat;
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+    case PIXFORMAT_RAW:
+    case PIXFORMAT_GRAYSCALE:
+        ESP_LOGE(TAG, "Not support");
+        break;
+    case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
+        break;
+    default:
+        return -1;
+    }
+
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+int sc030iot_detect(int slv_addr, sensor_id_t *id)
+{
+    if (SC030IOT_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, SC030_SENSOR_ID_LOW_REG);
+        uint8_t MIDH = SCCB_Read(slv_addr, SC030_SENSOR_ID_HIGH_REG);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (SC030IOT_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int sc030iot_init(sensor_t *sensor)
+{
+    // Set function pointers
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    
+    sensor->set_saturation= set_saturation;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_awb_gain = set_awb_gain;
+    sensor->set_contrast = set_contrast;
+    //not supported
+    sensor->set_denoise = set_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+    
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_xclk = set_xclk;
+    
+    ESP_LOGD(TAG, "sc030iot Attached");
+
+    return 0;
+}

+ 342 - 0
code/components/esp32-camera-master/sensors/sc101iot.c

@@ -0,0 +1,342 @@
+/*
+ * SC101IOT driver.
+ * 
+ * Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
+ *
+ * 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.
+ *
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "sccb.h"
+#include "xclk.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "sc101iot.h"
+#include "sc101iot_settings.h"
+
+#if defined(ARDUINO_ARCH_ESP32) && defined(CONFIG_ARDUHAL_ESP_LOG)
+#include "esp32-hal-log.h"
+#else
+#include "esp_log.h"
+static const char* TAG = "sc101";
+#endif
+
+#define SC101_SENSOR_ID_HIGH_REG    0XF7
+#define SC101_SENSOR_ID_LOW_REG     0XF8
+#define SC101_MAX_FRAME_WIDTH       (1280)
+#define SC101_MAX_FRAME_HIGH        (720)
+
+// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int get_reg(sensor_t *sensor, int reg, int mask)
+{
+    int ret = 0;
+    uint8_t reg_high = (reg>>8) & 0xFF;
+    uint8_t reg_low = reg & 0xFF;
+
+    if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+        return -1;
+    }
+
+    ret = SCCB_Read(sensor->slv_addr, reg_low);
+    if(ret > 0){
+        ret &= mask;
+    }
+    return ret;
+}
+
+// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
+// For more information please refer to the Technical Reference Manual.
+static int set_reg(sensor_t *sensor, int reg, int mask, int value)
+{
+    int ret = 0;
+    uint8_t reg_high = (reg>>8) & 0xFF;
+    uint8_t reg_low = reg & 0xFF;
+
+    if(SCCB_Write(sensor->slv_addr, 0xf0, reg_high)) {
+        return -1;
+    }
+    
+    ret = SCCB_Write(sensor->slv_addr, reg_low, value & 0xFF);
+    return ret;
+}
+
+static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
+{
+    int i=0, res = 0;
+    while (i<regs_entry_len) {
+        res = SCCB_Write(sensor->slv_addr, regs[i][0], regs[i][1]);
+        if (res) {
+            return res;
+        }
+        i++;
+    }
+    return res;
+}
+
+static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
+{
+    int ret = 0;
+    ret = get_reg(sensor, reg, 0xff);
+    if(ret < 0){
+        return ret;
+    }
+    uint8_t mask = ((1 << length) - 1) << offset;
+    value = (ret & ~mask) | ((value << offset) & mask);
+    ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
+    return ret;
+}
+
+#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
+#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
+#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
+
+static int set_hmirror(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    if(enable) {
+        SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
+    } else {
+        SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
+    }
+
+    return ret;
+}
+
+static int set_vflip(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    if(enable) {
+        SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
+    } else {
+        SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
+    }
+
+    return ret;
+}
+
+static int set_colorbar(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
+    return ret;
+}
+
+static int set_raw_gma(sensor_t *sensor, int enable)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
+
+    return ret;
+}
+
+static int set_sharpness(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
+    WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
+    WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
+
+    return ret;
+}
+
+static int set_agc_gain(sensor_t *sensor, int gain)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
+    WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
+    WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
+    WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
+    WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
+    
+    return ret;
+}
+
+static int set_aec_value(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
+    WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
+
+    return ret;
+}
+
+static int set_awb_gain(sensor_t *sensor, int value)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
+    WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
+    WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
+    return ret;
+}
+
+static int set_saturation(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
+    WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
+    WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
+    return ret;
+}
+
+static int set_contrast(sensor_t *sensor, int level)
+{
+    int ret = 0;
+    SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
+    WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
+    return ret;
+}
+
+static int reset(sensor_t *sensor)
+{
+    int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
+    
+    // Delay
+    vTaskDelay(50 / portTICK_PERIOD_MS);
+
+    // ESP_LOGI(TAG, "set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
+    // ESP_LOGI(TAG, "0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
+    if (ret) {
+        ESP_LOGE(TAG, "reset fail");
+    }
+    return ret;
+}
+
+static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
+{
+    int ret = 0;
+    //sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
+    WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
+    WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
+    WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
+
+    //sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
+    WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
+    WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
+    WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
+
+    vTaskDelay(10 / portTICK_PERIOD_MS);
+
+    return ret;
+}
+
+static int set_framesize(sensor_t *sensor, framesize_t framesize)
+{
+    uint16_t w = resolution[framesize].width;
+    uint16_t h = resolution[framesize].height;
+    if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
+        goto err; 
+    }
+
+    uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;   
+    uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
+    
+    if(set_window(sensor, offset_x, offset_y, w, h)) {
+        goto err; 
+    }
+    
+    sensor->status.framesize = framesize;
+    return 0;
+err:
+    ESP_LOGE(TAG, "frame size err");
+    return -1;
+}
+
+static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
+{
+    int ret=0;
+    sensor->pixformat = pixformat;
+
+    switch (pixformat) {
+    case PIXFORMAT_RGB565:
+    case PIXFORMAT_RAW:
+    case PIXFORMAT_GRAYSCALE:
+        ESP_LOGE(TAG, "Not support");
+        break;
+    case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
+        break;
+    default:
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static int init_status(sensor_t *sensor)
+{
+    return 0;
+}
+
+static int set_dummy(sensor_t *sensor, int val){ return -1; }
+
+static int set_xclk(sensor_t *sensor, int timer, int xclk)
+{
+    int ret = 0;
+    sensor->xclk_freq_hz = xclk * 1000000U;
+    ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
+    return ret;
+}
+
+int sc101iot_detect(int slv_addr, sensor_id_t *id)
+{
+    if (SC101IOT_SCCB_ADDR == slv_addr) {
+        uint8_t MIDL = SCCB_Read(slv_addr, SC101_SENSOR_ID_LOW_REG);
+        uint8_t MIDH = SCCB_Read(slv_addr, SC101_SENSOR_ID_HIGH_REG);
+        uint16_t PID = MIDH << 8 | MIDL;
+        if (SC101IOT_PID == PID) {
+            id->PID = PID;
+            return PID;
+        } else {
+            ESP_LOGI(TAG, "Mismatch PID=0x%x", PID);
+        }
+    }
+    return 0;
+}
+
+int sc101iot_init(sensor_t *sensor)
+{
+    // Set function pointers
+    sensor->reset = reset;
+    sensor->init_status = init_status;
+    sensor->set_pixformat = set_pixformat;
+    sensor->set_framesize = set_framesize;
+    sensor->set_hmirror = set_hmirror;
+    sensor->set_vflip = set_vflip;
+    sensor->set_colorbar = set_colorbar;
+    sensor->set_raw_gma = set_raw_gma;
+    sensor->set_sharpness = set_sharpness;
+    sensor->set_agc_gain = set_agc_gain;
+    sensor->set_aec_value = set_aec_value;
+    sensor->set_awb_gain = set_awb_gain;
+    sensor->set_saturation= set_saturation;
+    sensor->set_contrast = set_contrast;
+
+    sensor->set_denoise = set_dummy;
+    sensor->set_quality = set_dummy;
+    sensor->set_special_effect = set_dummy;
+    sensor->set_wb_mode = set_dummy;
+    sensor->set_ae_level = set_dummy;
+
+
+    sensor->get_reg = get_reg;
+    sensor->set_reg = set_reg;
+    sensor->set_xclk = set_xclk;
+    
+    ESP_LOGD(TAG, "sc101iot Attached");
+
+    return 0;
+}

+ 13 - 6
code/components/esp32-camera-master/target/esp32/ll_cam.c

@@ -34,10 +34,14 @@ static inline int gpio_ll_get_level(gpio_dev_t *hw, int gpio_num)
 #include "xclk.h"
 #include "cam_hal.h"
 
+#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
+#include "esp_rom_gpio.h"
+#endif
+
 #if (ESP_IDF_VERSION_MAJOR >= 5)
 #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
 #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
 #endif
 
 static const char *TAG = "esp32 ll_cam";
@@ -233,7 +237,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
     //DBG_PIN_SET(0);
 }
 
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
 {
     I2S0.conf.rx_start = 0;
     I2S_ISR_DISABLE(in_suc_eof);
@@ -308,7 +312,7 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
     I2S0.clkm_conf.clkm_div_a = 0;
     I2S0.clkm_conf.clkm_div_b = 0;
     I2S0.clkm_conf.clkm_div_num = 2;
-    
+
     I2S0.fifo_conf.dscr_en = 1;
     I2S0.fifo_conf.rx_fifo_mod = sampling_mode;
     I2S0.fifo_conf.rx_fifo_mod_force_en = 1;
@@ -442,9 +446,12 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
     }
     // Calculate DMA size
     dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
-    
-    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u", 
-            node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node, dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item, image_size);
+
+    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u, dma_half_buffer_min: %5u, dma_half_buffer: %5u,"
+            "lines_per_half_buffer: %2u, dma_buffer_size: %5u, image_size: %u",
+            (unsigned) (node_size * cam->dma_bytes_per_item), (unsigned) nodes_per_line, (unsigned) lines_per_node,
+            (unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
+            (unsigned) (lines_per_half_buffer), (unsigned) (dma_buffer_size * cam->dma_bytes_per_item), (unsigned) image_size);
 
     cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
     cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;

+ 14 - 8
code/components/esp32-camera-master/target/esp32s2/ll_cam.c

@@ -21,10 +21,15 @@
 #include "xclk.h"
 #include "cam_hal.h"
 
+#if (ESP_IDF_VERSION_MAJOR >= 4) && (ESP_IDF_VERSION_MINOR >= 3)
+#include "esp_rom_gpio.h"
+#endif
+
 #if (ESP_IDF_VERSION_MAJOR >= 5)
 #define GPIO_PIN_INTR_POSEDGE GPIO_INTR_POSEDGE
 #define GPIO_PIN_INTR_NEGEDGE GPIO_INTR_NEGEDGE
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
+#define ets_delay_us(a) esp_rom_delay_us(a)
 #endif
 
 static const char *TAG = "s2 ll_cam";
@@ -70,7 +75,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
     }
 }
 
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
 {
     I2S0.conf.rx_start = 0;
 
@@ -119,7 +124,7 @@ bool ll_cam_start(cam_obj_t *cam, int frame_pos)
     } else {
         I2S0.in_link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
     }
-    
+
     I2S0.in_link.start = 1;
     I2S0.conf.rx_start = 1;
     return true;
@@ -299,8 +304,8 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
         }
     }
 
-    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u", 
-            node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
+    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
+            (unsigned) (node_size * cam->dma_bytes_per_item), nodes_per_line, lines_per_node);
 
     cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
 
@@ -332,9 +337,10 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
         size_t dma_buffer_max = 2 * dma_half_buffer_max;
         size_t dma_buffer_size = dma_buffer_max;
         dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
-        
-        ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u", 
-                dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
+
+        ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
+                (unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
+                (unsigned) lines_per_half_buffer, (unsigned) (dma_buffer_size * cam->dma_bytes_per_item));
 
         cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
         cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;

+ 99 - 22
code/components/esp32-camera-master/target/esp32s3/ll_cam.c

@@ -22,10 +22,15 @@
 #include "soc/gdma_reg.h"
 #include "ll_cam.h"
 #include "cam_hal.h"
+#include "esp_rom_gpio.h"
 
 #if (ESP_IDF_VERSION_MAJOR >= 5)
-#define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
-#define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
+#include "soc/gpio_sig_map.h"
+#include "soc/gpio_periph.h"
+#include "soc/io_mux_reg.h"
+#define gpio_matrix_in(a,b,c) esp_rom_gpio_connect_in_signal(a,b,c)
+#define gpio_matrix_out(a,b,c,d) esp_rom_gpio_connect_out_signal(a,b,c,d)
+#define ets_delay_us(a) esp_rom_delay_us(a)
 #endif
 
 static const char *TAG = "s3 ll_cam";
@@ -74,7 +79,7 @@ static void IRAM_ATTR ll_cam_dma_isr(void *arg)
     }
 }
 
-bool ll_cam_stop(cam_obj_t *cam)
+bool IRAM_ATTR ll_cam_stop(cam_obj_t *cam)
 {
     if (cam->jpeg_mode || !cam->psram_mode) {
         GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
@@ -170,6 +175,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
     }
 
     GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
+    // GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2;
 
     GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
     //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
@@ -178,8 +184,57 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
     return ESP_OK;
 }
 
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
+{
+    esp_err_t ret = ESP_OK;
+
+    switch (config->conv_mode) {
+    case YUV422_TO_YUV420:
+        if (config->pixel_format != PIXFORMAT_YUV422) {
+            ret = ESP_FAIL;
+        } else {
+            ESP_LOGI(TAG, "YUV422 to YUV420 mode");
+            LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
+            LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
+            LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
+        }
+        break;
+    case YUV422_TO_RGB565:
+        if (config->pixel_format != PIXFORMAT_YUV422) {
+            ret = ESP_FAIL;
+        } else {
+            ESP_LOGI(TAG, "YUV422 to RGB565 mode");
+            LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
+            LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
+            LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
+        }
+        break;
+    default:
+        break;
+    }
+#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
+    LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
+#else
+    LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
+#endif
+#if CONFIG_LCD_CAM_CONV_FULL_RANGE_ENABLED
+    LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 1;
+    LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 1;
+#else
+    LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
+    LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
+#endif
+    LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
+    LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
+    cam->conv_mode = config->conv_mode;
+    return ret;
+}
+#endif
+
 esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
 {
+    esp_err_t ret = ESP_OK;
     if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
         REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
         REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
@@ -188,7 +243,7 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
     }
 
     LCD_CAM.cam_ctrl.val = 0;
-    
+
     LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
     LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
     LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;
@@ -215,15 +270,21 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
 
     LCD_CAM.cam_rgb_yuv.val = 0;
 
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+    if (config->conv_mode) {
+        ret = ll_cam_converter_config(cam, config);
+        if(ret != ESP_OK) {
+            return ret;
+        }
+    }
+#endif
+
     LCD_CAM.cam_ctrl.cam_update = 1;
     LCD_CAM.cam_ctrl1.cam_start = 1;
 
-    esp_err_t err = ll_cam_dma_init(cam);
-    if(err != ESP_OK) {
-        return err;
-    }
-    
-    return ESP_OK;
+    ret = ll_cam_dma_init(cam);
+
+    return ret;
 }
 
 void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
@@ -262,11 +323,12 @@ esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
         gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
         gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false);
     }
-
-    PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
-    gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
-    gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
-    gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
+    if (config->pin_xclk >= 0) { 
+        PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
+        gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
+        gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
+        gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
+    }
 
     return ESP_OK;
 }
@@ -338,8 +400,8 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
         }
     }
 
-    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u", 
-            node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
+    ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
+            (unsigned) (node_size * cam->dma_bytes_per_item), (unsigned) nodes_per_line, (unsigned) lines_per_node);
 
     cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
 
@@ -371,9 +433,10 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
     if (!cam->psram_mode) {
         dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
     }
-    
-    ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u", 
-            dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
+
+    ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
+            (unsigned) (dma_half_buffer_min * cam->dma_bytes_per_item), (unsigned) (dma_half_buffer * cam->dma_bytes_per_item),
+            (unsigned) lines_per_half_buffer, (unsigned) (dma_buffer_size * cam->dma_bytes_per_item));
 
     cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
     cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
@@ -382,7 +445,7 @@ static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
 }
 
 bool ll_cam_dma_sizes(cam_obj_t *cam)
-{    
+{
     cam->dma_bytes_per_item = 1;
     if (cam->jpeg_mode) {
         if (cam->psram_mode) {
@@ -433,8 +496,22 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
         }
         cam->fb_bytes_per_pixel = 1;       // frame buffer stores Y8
     } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
-            cam->in_bytes_per_pixel = 2;       // camera sends YU/YV
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+        switch (cam->conv_mode) {
+        case YUV422_TO_YUV420:
+            cam->in_bytes_per_pixel = 1.5;       // for DMA receive
+            cam->fb_bytes_per_pixel = 1.5;       // frame buffer stores YUV420
+            break;
+        case YUV422_TO_RGB565:
+        default:
+            cam->in_bytes_per_pixel = 2;       // for DMA receive
             cam->fb_bytes_per_pixel = 2;       // frame buffer stores YU/YV/RGB565
+            break;
+        }
+#else
+        cam->in_bytes_per_pixel = 2;       // for DMA receive
+        cam->fb_bytes_per_pixel = 2;       // frame buffer stores YU/YV/RGB565
+#endif
     } else if (pix_format == PIXFORMAT_JPEG) {
         cam->in_bytes_per_pixel = 1;
         cam->fb_bytes_per_pixel = 1;

+ 8 - 2
code/components/esp32-camera-master/target/private_include/ll_cam.h

@@ -101,7 +101,7 @@ typedef struct {
     QueueHandle_t frame_buffer_queue;
     TaskHandle_t task_handle;
     intr_handle_t cam_intr_handle;
-	
+
     uint8_t dma_num;//ESP32-S3
     intr_handle_t dma_intr_handle;//ESP32-S3
 
@@ -116,8 +116,14 @@ typedef struct {
     //for RGB/YUV modes
     uint16_t width;
     uint16_t height;
+#if CONFIG_CAMERA_CONVERTER_ENABLED
+    float in_bytes_per_pixel;
+    float fb_bytes_per_pixel;
+    camera_conv_mode_t conv_mode;
+#else
     uint8_t in_bytes_per_pixel;
     uint8_t fb_bytes_per_pixel;
+#endif
     uint32_t fb_size;
 
     cam_state_t state;
@@ -134,7 +140,7 @@ esp_err_t ll_cam_init_isr(cam_obj_t *cam);
 void ll_cam_do_vsync(cam_obj_t *cam);
 uint8_t ll_cam_get_dma_align(cam_obj_t *cam);
 bool ll_cam_dma_sizes(cam_obj_t *cam);
-size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len);
+size_t ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len);
 esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid);
 
 // implemented in cam_hal

+ 44 - 9
code/components/esp32-camera-master/test/test_camera.c

@@ -6,6 +6,7 @@
 #include "unity.h"
 #include <mbedtls/base64.h>
 #include "esp_log.h"
+#include "driver/i2c.h"
 
 #include "esp_camera.h"
 
@@ -105,11 +106,16 @@
 
 #endif
 
+#define I2C_MASTER_SCL_IO           4      /*!< GPIO number used for I2C master clock */
+#define I2C_MASTER_SDA_IO           5      /*!< GPIO number used for I2C master data  */
+#define I2C_MASTER_NUM              0      /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
+#define I2C_MASTER_FREQ_HZ          100000 /*!< I2C master clock frequency */
+
 static const char *TAG = "test camera";
 
 typedef void (*decode_func_t)(uint8_t *jpegbuffer, uint32_t size, uint8_t *outbuffer);
 
-static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count)
+static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, framesize_t frame_size, uint8_t fb_count, int sccb_sda_gpio_num, int sccb_port)
 {
     framesize_t size_bak = frame_size;
     if (PIXFORMAT_JPEG == pixel_format && FRAMESIZE_SVGA > frame_size) {
@@ -119,8 +125,9 @@ static esp_err_t init_camera(uint32_t xclk_freq_hz, pixformat_t pixel_format, fr
         .pin_pwdn = PWDN_GPIO_NUM,
         .pin_reset = RESET_GPIO_NUM,
         .pin_xclk = XCLK_GPIO_NUM,
-        .pin_sscb_sda = SIOD_GPIO_NUM,
-        .pin_sscb_scl = SIOC_GPIO_NUM,
+        .pin_sccb_sda = sccb_sda_gpio_num, // If pin_sccb_sda is -1, sccb will use the already initialized i2c port specified by `sccb_i2c_port`.
+        .pin_sccb_scl = SIOC_GPIO_NUM,
+        .sccb_i2c_port = sccb_port,
 
         .pin_d7 = Y9_GPIO_NUM,
         .pin_d6 = Y8_GPIO_NUM,
@@ -226,7 +233,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
 {
     esp_err_t ret = ESP_OK;
     //detect sensor information
-    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
     sensor_t *s = esp_camera_sensor_get();
     camera_sensor_info_t *info = esp_camera_sensor_get_info(&s->id);
     TEST_ASSERT_NOT_NULL(info);
@@ -249,7 +256,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
     for (; format_s <= format_e; format_s++) {
         for (size_t i = 0; i <= max_size; i++) {
             ESP_LOGI(TAG, "\n\n===> Testing format:%s resolution: %d x %d <===", get_cam_format_name(*format_s), resolution[i].width, resolution[i].height);
-            ret = init_camera(xclk_freq, *format_s, i, 2);
+            ret = init_camera(xclk_freq, *format_s, i, 2, SIOD_GPIO_NUM, -1);
             vTaskDelay(100 / portTICK_RATE_MS);
             if (ESP_OK != ret) {
                 ESP_LOGW(TAG, "Testing init failed :-(, skip this item");
@@ -276,7 +283,7 @@ static void camera_performance_test(uint32_t xclk_freq, uint32_t pic_num)
 TEST_CASE("Camera driver init, deinit test", "[camera]")
 {
     uint64_t t1 = esp_timer_get_time();
-    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
     uint64_t t2 = esp_timer_get_time();
     ESP_LOGI(TAG, "Camera init time %llu ms", (t2 - t1) / 1000);
 
@@ -285,7 +292,7 @@ TEST_CASE("Camera driver init, deinit test", "[camera]")
 
 TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
 {
-    TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2));
+    TEST_ESP_OK(init_camera(10000000, PIXFORMAT_RGB565, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
     vTaskDelay(500 / portTICK_RATE_MS);
     ESP_LOGI(TAG, "Taking picture...");
     camera_fb_t *pic = esp_camera_fb_get();
@@ -301,7 +308,7 @@ TEST_CASE("Camera driver take RGB565 picture test", "[camera]")
 
 TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
 {
-    TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2));
+    TEST_ESP_OK(init_camera(10000000, PIXFORMAT_YUV422, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
     vTaskDelay(500 / portTICK_RATE_MS);
     ESP_LOGI(TAG, "Taking picture...");
     camera_fb_t *pic = esp_camera_fb_get();
@@ -317,7 +324,7 @@ TEST_CASE("Camera driver take YUV422 picture test", "[camera]")
 
 TEST_CASE("Camera driver take JPEG picture test", "[camera]")
 {
-    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2));
+    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2, SIOD_GPIO_NUM, -1));
     vTaskDelay(500 / portTICK_RATE_MS);
     ESP_LOGI(TAG, "Taking picture...");
     camera_fb_t *pic = esp_camera_fb_get();
@@ -484,6 +491,25 @@ static void img_jpeg_decode_test(uint16_t pic_index, uint16_t lib_index)
     jpg_decode_test(lib_index, DECODE_RGB565, imgs[pic_index].buf, imgs[pic_index].length, imgs[pic_index].w, imgs[pic_index].h, 16);
 }
 
+/**
+ * @brief i2c master initialization
+ */
+static esp_err_t i2c_master_init(int i2c_port)
+{
+    i2c_config_t conf = {
+        .mode = I2C_MODE_MASTER,
+        .sda_io_num = I2C_MASTER_SDA_IO,
+        .scl_io_num = I2C_MASTER_SCL_IO,
+        .sda_pullup_en = GPIO_PULLUP_ENABLE,
+        .scl_pullup_en = GPIO_PULLUP_ENABLE,
+        .master.clk_speed = I2C_MASTER_FREQ_HZ,
+    };
+
+    i2c_param_config(i2c_port, &conf);
+
+    return i2c_driver_install(i2c_port, conf.mode, 0, 0, 0);
+}
+
 TEST_CASE("Conversions image 227x149 jpeg decode test", "[camera]")
 {
     img_jpeg_decode_test(0, 0);
@@ -498,3 +524,12 @@ TEST_CASE("Conversions image 480x320 jpeg decode test", "[camera]")
 {
     img_jpeg_decode_test(2, 0);
 }
+
+TEST_CASE("Camera driver uses an i2c port initialized by other devices test", "[camera]")
+{
+    TEST_ESP_OK(i2c_master_init(I2C_MASTER_NUM));
+    TEST_ESP_OK(init_camera(20000000, PIXFORMAT_JPEG, FRAMESIZE_QVGA, 2, -1, I2C_MASTER_NUM));
+    vTaskDelay(500 / portTICK_RATE_MS);
+    TEST_ESP_OK(esp_camera_deinit());
+    TEST_ESP_OK(i2c_driver_delete(I2C_MASTER_NUM));
+}

BIN
code/components/esp32-camera-master_20220924.zip


+ 11 - 1
code/components/tflite-lib/CMakeLists.txt

@@ -51,18 +51,28 @@ set(lib_srcs
           "${tflite_dir}/kernels/internal/quantization_util.cc"
           "${tflite_dir}/schema/schema_utils.cc")
 
+set(priv_req esp-nn)
+
+# include component requirements which were introduced after IDF version 4.1
+if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.1")
+    list(APPEND priv_req esp_timer driver)
+endif()
+
 idf_component_register(
             SRCS "${lib_srcs}"
             INCLUDE_DIRS "." "third_party/gemmlowp"
                          "third_party/flatbuffers/include"
                          "third_party/ruy"
                          "third_party/kissfft"
-            REQUIRES "esp-nn")
+            REQUIRES ${pub_req}
+            PRIV_REQUIRES ${priv_req})
 
 # Reduce the level of paranoia to be able to compile TF sources
 target_compile_options(${COMPONENT_LIB} PRIVATE
   -Wno-maybe-uninitialized
   -Wno-missing-field-initializers
+  -Wno-error=sign-compare
+  -Wno-error=double-promotion
   -DESP_NN # enables ESP-NN optimizations by Espressif
   -Wno-type-limits)
 

+ 1 - 0
code/components/tflite-lib/tensorflow/lite/builtin_ops.h

@@ -185,6 +185,7 @@ typedef enum {
   kTfLiteBuiltinUnsortedSegmentSum = 155,
   kTfLiteBuiltinAtan2 = 156,
   kTfLiteBuiltinUnsortedSegmentMin = 157,
+  kTfLiteBuiltinSign = 158,
 } TfLiteBuiltinOperator;
 
 #ifdef __cplusplus

+ 21 - 0
code/components/tflite-lib/tensorflow/lite/c/common.cc

@@ -283,4 +283,25 @@ const char* TfLiteTypeGetName(TfLiteType type) {
 
 TfLiteDelegate TfLiteDelegateCreate() { return TfLiteDelegate{}; }
 
+struct TfLiteOpaqueDelegateStruct* TfLiteOpaqueDelegateCreate(
+    const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder) {
+  if (!opaque_delegate_builder) return nullptr;
+
+  TfLiteDelegate* result = new TfLiteDelegate{};
+  result->opaque_delegate_builder = new TfLiteOpaqueDelegateBuilder{};
+  *(result->opaque_delegate_builder) = *opaque_delegate_builder;
+
+  return reinterpret_cast<struct TfLiteOpaqueDelegateStruct*>(result);
+}
+
+void TfLiteOpaqueDelegateDelete(
+    const struct TfLiteOpaqueDelegateStruct* opaque_delegate) {
+  if (!opaque_delegate) return;
+
+  const TfLiteDelegate* tflite_delegate =
+      reinterpret_cast<const TfLiteDelegate*>(opaque_delegate);
+  delete tflite_delegate->opaque_delegate_builder;
+  delete tflite_delegate;
+}
+
 }  // extern "C"

+ 74 - 1
code/components/tflite-lib/tensorflow/lite/c/common.h

@@ -63,6 +63,8 @@ typedef enum TfLiteExternalContextType {
 struct TfLiteContext;
 struct TfLiteDelegate;
 struct TfLiteRegistration;
+struct TfLiteOpaqueDelegateStruct;
+struct TfLiteOpaqueDelegateBuilder;
 
 // An external context is a collection of information unrelated to the TF Lite
 // framework, but useful to a subset of the ops. TF Lite knows very little
@@ -973,7 +975,7 @@ typedef enum TfLiteDelegateFlags {
 typedef struct TfLiteDelegate {
   // Data that delegate needs to identify itself. This data is owned by the
   // delegate. The delegate is owned in the user code, so the delegate is
-  // responsible for doing this when it is destroyed.
+  // responsible for deallocating this when it is destroyed.
   void* data_;
 
   // Invoked by ModifyGraphWithDelegate. This prepare is called, giving the
@@ -1010,12 +1012,83 @@ typedef struct TfLiteDelegate {
 
   // Bitmask flags. See the comments in `TfLiteDelegateFlags`.
   int64_t flags;
+
+  // The opaque delegate builder associated with this object.  If set then the
+  // TF Lite runtime will give precedence to this field.  E.g. instead of
+  // invoking 'Prepare' via the function pointer inside the 'TfLiteDelegate'
+  // object, the runtime will first check if the corresponding function
+  // pointer inside 'opaque_delegate_builder' is set and if so invoke that.
+  //
+  // If this field is non-null, then the 'Prepare' field (of the
+  // 'TfLiteDelegate') should be null.
+  struct TfLiteOpaqueDelegateBuilder* opaque_delegate_builder;
 } TfLiteDelegate;
 
 // Build a 'null' delegate, with all the fields properly set to their default
 // values.
 TfLiteDelegate TfLiteDelegateCreate(void);
 
+// `TfLiteOpaqueDelegateBuilder` is used for constructing
+// `TfLiteOpaqueDelegateStruct`, see `TfLiteOpaqueDelegateCreate` below.  Note:
+// This struct is not ABI stable.
+//
+// For forward source compatibility `TfLiteOpaqueDelegateBuilder` objects should
+// be brace-initialized, so that all fields (including any that might be added
+// in the future) get zero-initialized.  The purpose of each field is exactly
+// the same as with `TfLiteDelegate`.
+//
+// WARNING: This is an experimental interface that is subject to change.
+typedef struct TfLiteOpaqueDelegateBuilder {
+  // Data that delegate needs to identify itself. This data is owned by the
+  // delegate. The delegate is owned in the user code, so the delegate is
+  // responsible for deallocating this when it is destroyed.
+  void* data;
+  // Invoked by ModifyGraphWithDelegate. This prepare is called, giving the
+  // delegate a view of the current graph through TfLiteContext*. It typically
+  // will look at the nodes and call ReplaceNodeSubsetsWithDelegateKernels()
+  // to ask the TensorFlow lite runtime to create macro-nodes to represent
+  // delegated subgraphs of the original graph.
+  TfLiteStatus (*Prepare)(TfLiteOpaqueContext* context,  // NOLINT
+                          struct TfLiteOpaqueDelegateStruct* delegate,
+                          void* data);
+  // Copies the data from delegate buffer handle into raw memory of the given
+  // 'tensor'. Note that the delegate is allowed to allocate the raw bytes as
+  // long as it follows the rules for kTfLiteDynamic tensors, in which case this
+  // cannot be null.
+  TfLiteStatus (*CopyFromBufferHandle)(  // NOLINT
+      TfLiteOpaqueContext* context, struct TfLiteOpaqueDelegateStruct* delegate,
+      void* data, TfLiteBufferHandle buffer_handle, TfLiteOpaqueTensor* tensor);
+  // Copies the data from raw memory of the given 'tensor' to delegate buffer
+  // handle. This can be null if the delegate doesn't use its own buffer.
+  TfLiteStatus (*CopyToBufferHandle)(  // NOLINT
+      TfLiteOpaqueContext* context, struct TfLiteOpaqueDelegateStruct* delegate,
+      void* data, TfLiteBufferHandle buffer_handle, TfLiteOpaqueTensor* tensor);
+  // Frees the Delegate Buffer Handle. Note: This only frees the handle, but
+  // this doesn't release the underlying resource (e.g. textures). The
+  // resources are either owned by application layer or the delegate.
+  // This can be null if the delegate doesn't use its own buffer.
+  void (*FreeBufferHandle)(TfLiteOpaqueContext* context,  // NOLINT
+                           struct TfLiteOpaqueDelegateStruct* delegate,
+                           void* data, TfLiteBufferHandle* handle);
+  // Bitmask flags. See the comments in `TfLiteDelegateFlags`.
+  int64_t flags;
+} TfLiteOpaqueDelegateBuilder;
+
+// Creates an opaque delegate and returns its address.  The opaque delegate will
+// behave according to the provided 'opaque_delegate_builder'.  The lifetime of
+// the fields within the 'opaque_delegate_builder' must outlive any interaction
+// between the runtime and the returned 'TfLiteOpaqueDelegateStruct'.  The
+// returned address should be passed to 'TfLiteOpaqueDelegateDelete' for
+// deletion.  If 'opaque_delegate_builder' is a null pointer, then a null
+// pointer will be returned.
+struct TfLiteOpaqueDelegateStruct* TfLiteOpaqueDelegateCreate(
+    const TfLiteOpaqueDelegateBuilder* opaque_delegate_builder);
+
+// Deletes the provided opaque 'delegate'.  This function has no effect if the
+// 'delegate' is a null pointer.
+void TfLiteOpaqueDelegateDelete(
+    const struct TfLiteOpaqueDelegateStruct* delegate);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus

+ 8 - 6
code/components/tflite-lib/tensorflow/lite/context_util.h

@@ -12,8 +12,9 @@ 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.
 ==============================================================================*/
-// This provides a few C++ helpers that are useful for manipulating C structures
-// in C++.
+/// \file
+/// This provides a few C++ helpers that are useful for manipulating C
+/// structures in C++.
 #ifndef TENSORFLOW_LITE_CONTEXT_UTIL_H_
 #define TENSORFLOW_LITE_CONTEXT_UTIL_H_
 
@@ -23,13 +24,14 @@ limitations under the License.
 
 namespace tflite {
 
-// Provide a range iterable wrapper for TfLiteIntArray* (C lists that TfLite
-// C api uses. Can't use the google array_view, since we can't depend on even
+/// Provides a range iterable wrapper for TfLiteIntArray* (C lists) that TfLite
+/// C api uses.
+// Can't use the google array_view, since we can't depend on even
 // absl for embedded device reasons.
 class TfLiteIntArrayView {
  public:
-  // Construct a view of a TfLiteIntArray*. Note, `int_array` should be non-null
-  // and this view does not take ownership of it.
+  /// Construct a view of a TfLiteIntArray*. Note, `int_array` should be
+  /// non-null and this view does not take ownership of it.
   explicit TfLiteIntArrayView(const TfLiteIntArray* int_array)
       : int_array_(int_array) {}
 

+ 13 - 1
code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.cc

@@ -457,6 +457,10 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
       return ParseRsqrt(op, error_reporter, allocator, builtin_data);
     }
 
+    case BuiltinOperator_SELECT_V2: {
+      return ParseSelectV2(op, error_reporter, allocator, builtin_data);
+    }
+
     case BuiltinOperator_SHAPE: {
       return ParseShape(op, error_reporter, allocator, builtin_data);
     }
@@ -865,7 +869,6 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
     case BuiltinOperator_RELU_0_TO_1:
     case BuiltinOperator_SCATTER_ND:
     case BuiltinOperator_SELECT:
-    case BuiltinOperator_SELECT_V2:
     case BuiltinOperator_SLICE:
     case BuiltinOperator_TILE:
     case BuiltinOperator_TOPK_V2:
@@ -881,6 +884,7 @@ TfLiteStatus ParseOpDataTfLite(const Operator* op, BuiltinOperator op_type,
     case BuiltinOperator_UNSORTED_SEGMENT_PROD:
     case BuiltinOperator_UNSORTED_SEGMENT_SUM:
     case BuiltinOperator_ATAN2:
+    case BuiltinOperator_SIGN:
     case BuiltinOperator_WHERE:
       return kTfLiteOk;
     case BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES:
@@ -1982,6 +1986,14 @@ TfLiteStatus ParseRsqrt(const Operator*, ErrorReporter*, BuiltinDataAllocator*,
   return kTfLiteOk;
 }
 
+// We have this parse function instead of directly returning kTfLiteOk from the
+// switch-case in ParseOpData because this function is used as part of the
+// selective registration for the OpResolver implementation in micro.
+TfLiteStatus ParseSelectV2(const Operator*, ErrorReporter*,
+                           BuiltinDataAllocator*, void**) {
+  return kTfLiteOk;
+}
+
 TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
                         BuiltinDataAllocator* allocator, void** builtin_data) {
   SafeBuiltinDataAllocator safe_allocator(allocator);

+ 4 - 0
code/components/tflite-lib/tensorflow/lite/core/api/flatbuffer_conversions.h

@@ -319,6 +319,10 @@ TfLiteStatus ParseRound(const Operator* op, ErrorReporter* error_reporter,
 TfLiteStatus ParseRsqrt(const Operator* op, ErrorReporter* error_reporter,
                         BuiltinDataAllocator* allocator, void** builtin_data);
 
+TfLiteStatus ParseSelectV2(const Operator* op, ErrorReporter* error_reporter,
+                           BuiltinDataAllocator* allocator,
+                           void** builtin_data);
+
 TfLiteStatus ParseShape(const Operator* op, ErrorReporter* error_reporter,
                         BuiltinDataAllocator* allocator, void** builtin_data);
 

+ 2 - 0
code/components/tflite-lib/tensorflow/lite/experimental/microfrontend/lib/kiss_fft_int16.cc

@@ -1,3 +1,5 @@
+#include <cstdint>
+
 #include "tensorflow/lite/experimental/microfrontend/lib/kiss_fft_common.h"
 
 #define FIXED_POINT 16

+ 3 - 3
code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/hard_swish.h

@@ -12,8 +12,8 @@ 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.
 ==============================================================================*/
-#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ACTIVATIONS_H_
-#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_ACTIVATIONS_H_
+#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
+#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_
 
 #include <algorithm>
 
@@ -165,4 +165,4 @@ inline void HardSwish(const HardSwishParams& params,
 }  // namespace reference_ops
 }  // namespace tflite
 
-#endif  // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_CONV_H_
+#endif  // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_HARD_SWISH_H_

+ 46 - 0
code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/mul.h

@@ -16,6 +16,7 @@ limitations under the License.
 #define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_MUL_H_
 
 #include <algorithm>
+#include <complex>
 
 #include "tensorflow/lite/kernels/internal/common.h"
 
@@ -61,6 +62,20 @@ inline void Mul(const ArithmeticParams& params,
   }
 }
 
+inline void Mul(const ArithmeticParams& params,
+                const RuntimeShape& input1_shape,
+                const std::complex<float>* input1_data,
+                const RuntimeShape& input2_shape,
+                const std::complex<float>* input2_data,
+                const RuntimeShape& output_shape,
+                std::complex<float>* output_data) {
+  const int flat_size =
+      MatchingExtendedShapeFlatSize(input1_shape, input2_shape, output_shape);
+  for (int i = 0; i < flat_size; ++i) {
+    output_data[i] = input1_data[i] * input2_data[i];
+  }
+}
+
 inline void Mul(const ArithmeticParams& params,
                 const RuntimeShape& input1_shape, const uint8_t* input1_data,
                 const RuntimeShape& input2_shape, const uint8_t* input2_data,
@@ -162,6 +177,37 @@ void BroadcastMul4DSlow(const ArithmeticParams& params,
   }
 }
 
+inline void BroadcastMul4DSlow(const ArithmeticParams& params,
+                               const RuntimeShape& unextended_input1_shape,
+                               const std::complex<float>* input1_data,
+                               const RuntimeShape& unextended_input2_shape,
+                               const std::complex<float>* input2_data,
+                               const RuntimeShape& unextended_output_shape,
+                               std::complex<float>* output_data) {
+  TFLITE_DCHECK_LE(unextended_input1_shape.DimensionsCount(), 4);
+  TFLITE_DCHECK_LE(unextended_input2_shape.DimensionsCount(), 4);
+  TFLITE_DCHECK_LE(unextended_output_shape.DimensionsCount(), 4);
+  const RuntimeShape output_shape =
+      RuntimeShape::ExtendedShape(4, unextended_output_shape);
+
+  NdArrayDesc<4> desc1;
+  NdArrayDesc<4> desc2;
+  NdArrayDescsForElementwiseBroadcast(unextended_input1_shape,
+                                      unextended_input2_shape, &desc1, &desc2);
+
+  for (int b = 0; b < output_shape.Dims(0); ++b) {
+    for (int y = 0; y < output_shape.Dims(1); ++y) {
+      for (int x = 0; x < output_shape.Dims(2); ++x) {
+        for (int c = 0; c < output_shape.Dims(3); ++c) {
+          output_data[Offset(output_shape, b, y, x, c)] =
+              input1_data[SubscriptToIndex(desc1, b, y, x, c)] *
+              input2_data[SubscriptToIndex(desc2, b, y, x, c)];
+        }
+      }
+    }
+  }
+}
+
 }  // namespace reference_ops
 }  // namespace tflite
 

+ 151 - 0
code/components/tflite-lib/tensorflow/lite/kernels/internal/reference/select.h

@@ -0,0 +1,151 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+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.
+==============================================================================*/
+#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
+#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
+
+#include <cmath>
+
+#include "ruy/profiler/instrumentation.h"  // from @ruy
+#include "tensorflow/lite/kernels/internal/common.h"
+#include "tensorflow/lite/kernels/internal/types.h"
+
+namespace tflite {
+namespace reference_ops {
+
+template <typename D, typename T>
+void Select(const RuntimeShape& input_condition_shape,
+            const D* input_condition_data, const RuntimeShape& input_x_shape,
+            const T* input_x_data, const RuntimeShape& input_y_shape,
+            const T* input_y_data, const RuntimeShape& output_shape,
+            T* output_data) {
+  ruy::profiler::ScopeLabel label("Select");
+  int64_t flatsize;
+  // Allow select operator executions on mixed scalar tensors and one element
+  // tensors.
+  if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 &&
+      input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) {
+    flatsize = 1;
+  } else {
+    flatsize = MatchingFlatSize(input_condition_shape, input_x_shape,
+                                input_y_shape, output_shape);
+  }
+  for (int64_t i = 0; i < flatsize; ++i) {
+    output_data[i] =
+        input_condition_data[i] ? input_x_data[i] : input_y_data[i];
+  }
+}
+
+template <typename D, typename T>
+void RankOneSelect(const RuntimeShape& input_condition_shape,
+                   const D* input_condition_data,
+                   const RuntimeShape& input_x_shape, const T* input_x_data,
+                   const RuntimeShape& input_y_shape, const T* input_y_data,
+                   const RuntimeShape& output_shape, T* output_data) {
+  ruy::profiler::ScopeLabel label("Select/RankOneSelect");
+  const int64_t outer_size = input_condition_shape.FlatSize();
+  int64_t inner_size;
+  if (input_condition_shape.DimensionsCount() == 0) {
+    inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape);
+  } else {
+    TFLITE_DCHECK_EQ(
+        MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0),
+        outer_size);
+    inner_size =
+        MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape);
+  }
+
+  int64_t offset = 0;
+  for (int64_t i = 0; i < outer_size; i++) {
+    const T* input_data = input_condition_data[i] ? input_x_data : input_y_data;
+    memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T));
+    offset += inner_size;
+  }
+}
+
+template <typename D, typename T>
+void BroadcastSelect5DSlow(const RuntimeShape& input_condition_shape,
+                           const D* input_condition_data,
+                           const RuntimeShape& input_x_shape,
+                           const T* input_x_data,
+                           const RuntimeShape& input_y_shape,
+                           const T* input_y_data,
+                           const RuntimeShape& output_shape, T* output_data) {
+  ruy::profiler::ScopeLabel label("Select/BroadcastSelectSlow");
+  TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 5);
+  TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 5);
+  TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 5);
+  TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 5);
+
+  NdArrayDesc<5> desc_condition;
+  NdArrayDesc<5> desc_x;
+  NdArrayDesc<5> desc_y;
+  NdArrayDesc<5> desc_output;
+  const RuntimeShape extended_output_shape =
+      RuntimeShape::ExtendedShape(5, output_shape);
+  CopyDimsToDesc(extended_output_shape, &desc_output);
+  NdArrayDescsForElementwiseBroadcast(input_condition_shape, input_x_shape,
+                                      input_y_shape, &desc_condition, &desc_x,
+                                      &desc_y);
+
+  // In Tensorflow, the dimensions are canonically named (batch_number, row,
+  // col, channel), with extents (batches, height, width, depth), with the
+  // trailing dimension changing most rapidly (channels has the smallest
+  // stride, typically 1 element).
+  //
+  // In generated C code, we store arrays with the dimensions reversed. The
+  // first dimension has smallest stride.
+  //
+  // We name our variables by their Tensorflow convention, but generate C code
+  // nesting loops such that the innermost loop has the smallest stride for
+  // the best cache behavior.
+  for (int n = 0; n < desc_output.extents[0]; ++n) {
+    int out_idx_n = desc_output.extents[1] * n;
+    int cond_idx_n = desc_condition.strides[0] * n;
+    int in_idx1_n = desc_x.strides[0] * n;
+    int in_idx2_n = desc_y.strides[0] * n;
+    for (int b = 0; b < desc_output.extents[1]; ++b) {
+      int out_idx_b = (out_idx_n + b) * desc_output.extents[2];
+      int cond_idx_b = cond_idx_n + desc_condition.strides[1] * b;
+      int in_idx1_b = in_idx1_n + desc_x.strides[1] * b;
+      int in_idx2_b = in_idx2_n + desc_y.strides[1] * b;
+      for (int y = 0; y < desc_output.extents[2]; ++y) {
+        int out_idx_y = (out_idx_b + y) * desc_output.extents[3];
+        int cond_idx_y = cond_idx_b + desc_condition.strides[2] * y;
+        int in_idx1_y = in_idx1_b + desc_x.strides[2] * y;
+        int in_idx2_y = in_idx2_b + desc_y.strides[2] * y;
+        for (int x = 0; x < desc_output.extents[3]; ++x) {
+          int out_idx = (out_idx_y + x) * desc_output.extents[4];
+          int cond_idx = cond_idx_y + desc_condition.strides[3] * x;
+          int in_idx1 = in_idx1_y + desc_x.strides[3] * x;
+          int in_idx2 = in_idx2_y + desc_y.strides[3] * x;
+          for (int c = 0; c < desc_output.extents[4]; ++c) {
+            output_data[out_idx] = input_condition_data[cond_idx]
+                                       ? input_x_data[in_idx1]
+                                       : input_y_data[in_idx2];
+            out_idx++;
+            cond_idx += desc_condition.strides[4];
+            in_idx1 += desc_x.strides[4];
+            in_idx2 += desc_y.strides[4];
+          }
+        }
+      }
+    }
+  }
+}
+
+}  // namespace reference_ops
+}  // namespace tflite
+
+#endif  // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_

+ 3 - 0
code/components/tflite-lib/tensorflow/lite/micro/all_ops_resolver.cc

@@ -92,6 +92,7 @@ AllOpsResolver::AllOpsResolver() {
   AddResizeNearestNeighbor();
   AddRound();
   AddRsqrt();
+  AddSelectV2();
   AddShape();
   AddSin();
   AddSlice();
@@ -102,6 +103,7 @@ AllOpsResolver::AllOpsResolver() {
   AddSplitV();
   AddSqrt();
   AddSquare();
+  AddSquaredDifference();
   AddSqueeze();
   AddStridedSlice();
   AddSub();
@@ -110,6 +112,7 @@ AllOpsResolver::AllOpsResolver() {
   AddTanh();
   AddTranspose();
   AddTransposeConv();
+  AddUnidirectionalSequenceLSTM();
   AddUnpack();
   AddVarHandle();
   AddWhile();

+ 14 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/add.h

@@ -1,4 +1,4 @@
-/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -59,6 +59,19 @@ TfLiteStatus CalculateOpDataAdd(TfLiteContext* context, TfLiteAddParams* params,
 
 TfLiteStatus AddPrepare(TfLiteContext* context, TfLiteNode* node);
 
+// Generic must define registration function.
+TfLiteRegistration Register_ADD();
+
+#if defined(CMSIS_NN)
+TfLiteRegistration Register_ADD_INT8();
+
+TfLiteRegistration Register_ADD_INT16();
+#else
+// Fallback registration
+inline TfLiteRegistration Register_ADD_INT8() { return Register_ADD(); }
+
+inline TfLiteRegistration Register_ADD_INT16() { return Register_ADD(); }
+#endif
 }  // namespace tflite
 
 #endif  // TENSORFLOW_LITE_MICRO_KERNELS_ADD_H_

+ 4 - 4
code/components/tflite-lib/tensorflow/lite/micro/kernels/add_n.cc

@@ -121,8 +121,8 @@ TfLiteStatus CalculateOpData(TfLiteContext* context, TfLiteNode* node) {
         context, kTfLiteActNone, output, &data->output_activation_min,
         &data->output_activation_max));
   } else {
-    TF_LITE_KERNEL_LOG(context, "ADD_N only supports FLOAT32 and INT8, got %s.",
-                       TfLiteTypeGetName(output->type));
+    MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
+                TfLiteTypeGetName(output->type));
     return kTfLiteError;
   }
 
@@ -198,8 +198,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
   } else if (output->type == kTfLiteInt8) {
     EvalAddNQuantized<int8_t>(context, node, output);
   } else {
-    TF_LITE_KERNEL_LOG(context, "ADD_N only supports FLOAT32 and INT8, got %s.",
-                       TfLiteTypeGetName(output->type));
+    MicroPrintf("ADD_N only supports FLOAT32 and INT8, got %s.",
+                TfLiteTypeGetName(output->type));
     return kTfLiteError;
   }
   return kTfLiteOk;

+ 8 - 9
code/components/tflite-lib/tensorflow/lite/micro/kernels/arg_min_max.cc

@@ -70,21 +70,20 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node, bool is_arg_max) {
           TF_LITE_ARG_MIN_MAX(int8_t, int32_t, int32_t);
           break;
         default:
-          TF_LITE_KERNEL_LOG(context,
-                             "Only float32, uint8_t and int8_t are "
-                             "supported currently, got %s.",
-                             TfLiteTypeGetName(input->type));
+          MicroPrintf(
+              "Only float32, uint8_t and int8_t are "
+              "supported currently, got %s.",
+              TfLiteTypeGetName(input->type));
           return kTfLiteError;
       }
     } else {
-      TF_LITE_KERNEL_LOG(context,
-                         "Only int32_t are supported currently, got %s.",
-                         TfLiteTypeGetName(output->type));
+      MicroPrintf("Only int32_t are supported currently, got %s.",
+                  TfLiteTypeGetName(output->type));
       return kTfLiteError;
     }
   } else {
-    TF_LITE_KERNEL_LOG(context, "Only int32_t are supported currently, got %s.",
-                       TfLiteTypeGetName(axis->type));
+    MicroPrintf("Only int32_t are supported currently, got %s.",
+                TfLiteTypeGetName(axis->type));
     return kTfLiteError;
   }
 

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/batch_to_space_nd.cc

@@ -95,8 +95,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
           tflite::micro::GetTensorData<int8_t>(output));
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input->type), input->type);
+      MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
+                  input->type);
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 1 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/circular_buffer.cc

@@ -90,7 +90,7 @@ TfLiteStatus CircularBufferEval(TfLiteContext* context, TfLiteNode* node) {
     EvalInt8(tflite::micro::GetTensorData<int8_t>(input), num_slots, depth,
              tflite::micro::GetTensorData<int8_t>(output));
   } else {
-    TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
+    MicroPrintf("Type %s (%d) not supported.",
                        TfLiteTypeGetName(input->type), input->type);
     return kTfLiteError;
   }

+ 12 - 12
code/components/tflite-lib/tensorflow/lite/micro/kernels/comparisons.cc

@@ -118,8 +118,8 @@ TfLiteStatus EqualEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -210,8 +210,8 @@ TfLiteStatus NotEqualEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -288,8 +288,8 @@ TfLiteStatus GreaterEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -366,8 +366,8 @@ TfLiteStatus GreaterEqualEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -444,8 +444,8 @@ TfLiteStatus LessEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -522,8 +522,8 @@ TfLiteStatus LessEqualEval(TfLiteContext* context, TfLiteNode* node) {
                 output_data);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input1->type), input1->type);
+      MicroPrintf("Type %s (%d) not supported.",
+                  TfLiteTypeGetName(input1->type), input1->type);
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 10 - 9
code/components/tflite-lib/tensorflow/lite/micro/kernels/concatenation.cc

@@ -133,7 +133,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   TF_LITE_ENSURE(context,
                  input_type == kTfLiteFloat32 || input_type == kTfLiteInt8 ||
                      input_type == kTfLiteInt16 || input_type == kTfLiteInt32 ||
-                     input_type == kTfLiteInt64);
+                     input_type == kTfLiteInt64 || input_type == kTfLiteBool);
 
   // Output type must match input type
   TF_LITE_ENSURE_EQ(context, output_type, input_type);
@@ -149,8 +149,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     int num_dimensions = NumDimensions(input);
 
     if (num_dimensions > RuntimeShape::kMaxSmallSize) {
-      TF_LITE_KERNEL_LOG(
-          context,
+      MicroPrintf(
           "Op Concatenation does not currently support num dimensions > %d "
           "Tensor has %d dimensions.",
           RuntimeShape::kMaxSmallSize, num_dimensions);
@@ -168,6 +167,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   TF_LITE_ENSURE(context, output != nullptr);
 
   switch (output_type) {  // Already know in/outtypes are same.
+    case kTfLiteBool:
     case kTfLiteFloat32:
     case kTfLiteInt16:
     case kTfLiteInt32:
@@ -205,9 +205,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
       break;
     }
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "Op Concatenation does not currently support Type '%s'.",
-          TfLiteTypeGetName(output_type));
+      MicroPrintf("Op Concatenation does not currently support Type '%s'.",
+                  TfLiteTypeGetName(output_type));
       return kTfLiteError;
   }
 
@@ -238,11 +237,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     case kTfLiteInt16:
       EvalUnquantized<int16_t>(context, node);
       break;
+    case kTfLiteBool:
+      EvalUnquantized<bool>(context, node);
+      break;
 
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "Op Concatenation does not currently support Type '%s'.",
-          TfLiteTypeGetName(output_type));
+      MicroPrintf("Op Concatenation does not currently support Type '%s'.",
+                  TfLiteTypeGetName(output_type));
       return kTfLiteError;
   }
 

+ 3 - 4
code/components/tflite-lib/tensorflow/lite/micro/kernels/cumsum.cc

@@ -123,7 +123,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
   if (axis < 0) axis += input_shape.DimensionsCount();
 
   if (axis < 0 || axis >= input_shape.DimensionsCount()) {
-    TF_LITE_KERNEL_LOG(context, "CUMSUM Invalid axis: %d", axis);
+    MicroPrintf("CUMSUM Invalid axis: %d", axis);
     return kTfLiteError;
   }
 
@@ -156,9 +156,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     } break;
 
     default: {
-      TF_LITE_KERNEL_LOG(context,
-                         "CUMSUM only supports FLOAT32 and INT8, got %s.",
-                         TfLiteTypeGetName(output->type));
+      MicroPrintf("CUMSUM only supports FLOAT32 and INT8, got %s.",
+                  TfLiteTypeGetName(output->type));
       return kTfLiteError;
     }
   }

+ 2 - 3
code/components/tflite-lib/tensorflow/lite/micro/kernels/depth_to_space.cc

@@ -124,9 +124,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                                   tflite::micro::GetTensorData<int8_t>(output));
       break;
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "DEPTH_TO_SPACE only supports FLOAT32 and INT8, got %s.",
-          TfLiteTypeGetName(output->type));
+      MicroPrintf("DEPTH_TO_SPACE only supports FLOAT32 and INT8, got %s.",
+                  TfLiteTypeGetName(output->type));
       return kTfLiteError;
   }
 

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/depthwise_conv.cc

@@ -82,8 +82,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
       break;
     }
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input->type), input->type);
+      MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
+                  input->type);
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 5 - 6
code/components/tflite-lib/tensorflow/lite/micro/kernels/div.cc

@@ -162,8 +162,7 @@ TfLiteStatus EvalQuantized(TfLiteContext* context, TfLiteNode* node,
     }
 #undef TF_LITE_DIV
   } else {
-    TF_LITE_KERNEL_LOG(
-        context, "Unsupported combination of input and output types in DIV.");
+    MicroPrintf("Unsupported combination of input and output types in DIV.");
     return kTfLiteError;
   }
 
@@ -189,10 +188,10 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     TF_LITE_ENSURE_OK(context, EvalQuantized(context, node, params, data,
                                              input1, input2, output));
   } else {
-    TF_LITE_KERNEL_LOG(context,
-                       "DIV only supports FLOAT32, quantized INT8 "
-                       "now, got type %s (%d).",
-                       TfLiteTypeGetName(output->type), output->type);
+    MicroPrintf(
+        "DIV only supports FLOAT32, quantized INT8 "
+        "now, got type %s (%d).",
+        TfLiteTypeGetName(output->type), output->type);
     return kTfLiteError;
   }
 

+ 9 - 9
code/components/tflite-lib/tensorflow/lite/micro/kernels/elementwise.cc

@@ -90,8 +90,8 @@ TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
   TF_LITE_ENSURE(context, output != nullptr);
   TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
   if (!IsSupportedType(input->type)) {
-    TF_LITE_KERNEL_LOG(context, "Input data type %s (%d) is not supported.",
-                       TfLiteTypeGetName(input->type), input->type);
+    MicroPrintf("Input data type %s (%d) is not supported.",
+                TfLiteTypeGetName(input->type), input->type);
     return kTfLiteError;
   }
 
@@ -112,8 +112,8 @@ TfLiteStatus PrepareAbsRsqrt(TfLiteContext* context, TfLiteNode* node) {
   TF_LITE_ENSURE(context, output != nullptr);
   TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
   if (!IsSupportedType(input->type)) {
-    TF_LITE_KERNEL_LOG(context, "Input data type %s (%d) is not supported.",
-                       TfLiteTypeGetName(input->type), input->type);
+    MicroPrintf("Input data type %s (%d) is not supported.",
+                TfLiteTypeGetName(input->type), input->type);
     return kTfLiteError;
   }
 
@@ -317,8 +317,8 @@ TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) {
                                            type);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
-                         TfLiteTypeGetName(type));
+      MicroPrintf("Current data type %s is not supported.",
+                  TfLiteTypeGetName(type));
       return kTfLiteError;
       break;
   }
@@ -355,8 +355,8 @@ TfLiteStatus RsqrtEval(TfLiteContext* context, TfLiteNode* node) {
                                        elementwise::validate_input_func, type);
 
     default:
-      TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
-                         TfLiteTypeGetName(type));
+      MicroPrintf("Current data type %s is not supported.",
+                  TfLiteTypeGetName(type));
       return kTfLiteError;
   }
 }
@@ -426,4 +426,4 @@ TfLiteRegistration Register_LOGICAL_NOT() {
 
 }  // namespace micro
 }  // namespace ops
-}  // namespace tflite
+}  // namespace tflite

+ 2 - 4
code/components/tflite-lib/tensorflow/lite/micro/kernels/elu.cc

@@ -25,7 +25,6 @@ limitations under the License.
 #include "tensorflow/lite/kernels/internal/types.h"
 #include "tensorflow/lite/kernels/kernel_util.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
-#include "tensorflow/lite/micro/micro_error_reporter.h"
 
 namespace tflite {
 namespace {
@@ -136,9 +135,8 @@ TfLiteStatus EluEval(TfLiteContext* context, TfLiteNode* node) {
       return kTfLiteOk;
     }
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "ELU only supports float32 and int8 currently, got %s.",
-          TfLiteTypeGetName(input->type));
+      MicroPrintf("ELU only supports float32 and int8 currently, got %s.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
 }

+ 0 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/conv.cc

@@ -26,7 +26,6 @@ limitations under the License.
 #include "tensorflow/lite/kernels/padding.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
 
-#include "freertos/FreeRTOS.h"
 #include <esp_timer.h>
 
 #if ESP_NN

+ 0 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/depthwise_conv.cc

@@ -27,7 +27,6 @@ limitations under the License.
 #include "tensorflow/lite/kernels/padding.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
 
-#include "freertos/FreeRTOS.h"
 #include <esp_timer.h>
 
 #if ESP_NN

+ 0 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/esp_nn/softmax.cc

@@ -25,7 +25,6 @@ limitations under the License.
 #include "tensorflow/lite/kernels/op_macros.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
 
-#include "freertos/FreeRTOS.h"
 #include <esp_timer.h>
 
 #if ESP_NN

+ 3 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/exp.cc

@@ -19,6 +19,7 @@ limitations under the License.
 #include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
 #include "tensorflow/lite/kernels/kernel_util.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/micro_error_reporter.h"
 
 namespace tflite {
 namespace {
@@ -63,8 +64,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                        static_cast<size_t>(flat_size),
                        tflite::micro::GetTensorData<float>(output));
   } else {
-    TF_LITE_KERNEL_LOG(context, "Type %s (%d) currently not supported by Exp.",
-                       TfLiteTypeGetName(input->type), input->type);
+    MicroPrintf("Type %s (%d) currently not supported by Exp.",
+                TfLiteTypeGetName(input->type), input->type);
     return kTfLiteError;
   }
   return kTfLiteOk;

+ 5 - 9
code/components/tflite-lib/tensorflow/lite/micro/kernels/expand_dims.cc

@@ -31,8 +31,7 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context,
                                     int32_t* axis_value) {
   const int axis_dims = (tflite::GetTensorShape(axis)).DimensionsCount();
   if (axis_dims > 1) {
-    TF_LITE_KERNEL_LOG(context, "Axis has only one element for Expand_Dims.",
-                       axis_dims);
+    MicroPrintf("Axis has only one element for Expand_Dims.", axis_dims);
     return kTfLiteError;
   }
 
@@ -41,9 +40,8 @@ TfLiteStatus GetAxisValueFromTensor(TfLiteContext* context,
     *axis_value = axis_ptr[0];
     return kTfLiteOk;
   } else {
-    TF_LITE_KERNEL_LOG(context,
-                       "Axis type %s (%d) not supported by Expand_Dims.",
-                       TfLiteTypeGetName(axis->type), axis->type);
+    MicroPrintf("Axis type %s (%d) not supported by Expand_Dims.",
+                TfLiteTypeGetName(axis->type), axis->type);
     return kTfLiteError;
   }
 }
@@ -99,8 +97,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   TF_LITE_ENSURE(context, output != nullptr);
   output->type = input->type;
   if (IsDynamicTensor(axis)) {
-    TF_LITE_KERNEL_LOG(context,
-                       "DynamicTensor is not yet supported by Expand_Dims.");
+    MicroPrintf("DynamicTensor is not yet supported by Expand_Dims.");
     return kTfLiteError;
   }
   TF_LITE_ENSURE_OK(context, VerifyTensorDim(context, input, axis, output));
@@ -135,8 +132,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                tflite::micro::GetTensorData<int8_t>(input), flat_size);
     } break;
     default:
-      TF_LITE_KERNEL_LOG(
-          context,
+      MicroPrintf(
           "Expand_Dims only currently supports int8 and float32, got %d.",
           input->type);
       return kTfLiteError;

+ 4 - 6
code/components/tflite-lib/tensorflow/lite/micro/kernels/fill.cc

@@ -53,9 +53,8 @@ TfLiteStatus EnsureEq(TfLiteContext* context, const TfLiteIntArray* array,
     case kTfLiteInt64:
       return EnsureEqImpl<int64_t>(context, array, tensor);
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "cannot compare int array to tensor of type %d.",
-                         tensor->type);
+      MicroPrintf("cannot compare int array to tensor of type %d.",
+                  tensor->type);
       return kTfLiteError;
   }
 }
@@ -123,9 +122,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
       FillImpl<int8_t>(value, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "Fill only currently supports float32 for input 1, got %d.",
-          TfLiteTypeGetName(value->type));
+      MicroPrintf("Fill only currently supports float32 for input 1, got %d.",
+                  TfLiteTypeGetName(value->type));
       return kTfLiteError;
   }
 

+ 3 - 3
code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_div.cc

@@ -74,7 +74,7 @@ TfLiteStatus EvalFloorDiv(TfLiteContext* context,
   // Validate the denominator.
   for (int i = 0; i < tflite::ElementCount(*input2->dims); ++i) {
     if (std::equal_to<T>()(denominator_data[i], 0)) {
-      TF_LITE_KERNEL_LOG(context, "Division by 0");
+      MicroPrintf("Division by 0");
       return kTfLiteError;
     }
   }
@@ -113,8 +113,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
       return EvalFloorDiv<float>(context, input1, input2, output);
     }
     default: {
-      TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by FLOOR_DIV.",
-                         TfLiteTypeGetName(input1->type));
+      MicroPrintf("Type '%s' is not supported by FLOOR_DIV.",
+                  TfLiteTypeGetName(input1->type));
       return kTfLiteError;
     }
   }

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/floor_mod.cc

@@ -111,8 +111,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                                  output);
     }
     default: {
-      TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by FLOOR_MOD.",
-                         TfLiteTypeGetName(input1->type));
+      MicroPrintf("Type '%s' is not supported by FLOOR_MOD.",
+                  TfLiteTypeGetName(input1->type));
       return kTfLiteError;
     }
   }

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/fully_connected.cc

@@ -141,8 +141,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     }
 
     default: {
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input->type), input->type);
+      MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
+                  input->type);
       return kTfLiteError;
     }
   }

+ 6 - 7
code/components/tflite-lib/tensorflow/lite/micro/kernels/gather.cc

@@ -118,9 +118,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     case kTfLiteInt32:
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "Positions of type '%s' are not supported by gather.",
-                         TfLiteTypeGetName(coords->type));
+      MicroPrintf("Positions of type '%s' are not supported by gather.",
+                  TfLiteTypeGetName(coords->type));
       return kTfLiteError;
       break;
   }
@@ -134,8 +133,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     case kTfLiteInt8:
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by gather.",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("Type '%s' is not supported by gather.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
       break;
   }
@@ -207,8 +206,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
         return Gather<int8_t, int32_t>(params, input, coords, output);
         break;
       default:
-        TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by gather.",
-                           TfLiteTypeGetName(input->type));
+        MicroPrintf("Type '%s' is not supported by gather.",
+                    TfLiteTypeGetName(input->type));
         return kTfLiteError;
         break;
     }

+ 14 - 20
code/components/tflite-lib/tensorflow/lite/micro/kernels/gather_nd.cc

@@ -47,9 +47,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     case kTfLiteInt8:
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "Params of type '%s' are not supported by gather_nd.",
-                         TfLiteTypeGetName(params->type));
+      MicroPrintf("Params of type '%s' are not supported by gather_nd.",
+                  TfLiteTypeGetName(params->type));
       return kTfLiteError;
       break;
   }
@@ -57,9 +56,8 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
     case kTfLiteInt32:
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "Indices of type '%s' are not supported by gather_nd.",
-                         TfLiteTypeGetName(indices->type));
+      MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
+                  TfLiteTypeGetName(indices->type));
       return kTfLiteError;
   }
 
@@ -67,22 +65,20 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   const int indices_rank = NumDimensions(indices);
   const int indices_nd = SizeOfDimension(indices, indices_rank - 1);
   if (params_rank < 1) {
-    TF_LITE_KERNEL_LOG(context, "Params must be at least a vector.");
+    MicroPrintf("Params must be at least a vector.");
     return kTfLiteError;
   }
   if (indices_rank < 1) {
-    TF_LITE_KERNEL_LOG(context, "Indices must be at least a vector.");
+    MicroPrintf("Indices must be at least a vector.");
     return kTfLiteError;
   }
   if (indices_nd > params_rank) {
-    TF_LITE_KERNEL_LOG(
-        context, "Index innermost dimension length must be <= params rank.");
+    MicroPrintf("Index innermost dimension length must be <= params rank.");
     return kTfLiteError;
   }
   if (indices_nd > MAX_INDICES_ND) {
-    TF_LITE_KERNEL_LOG(context,
-                       "Index innermost dimension length must not exceed %d.",
-                       MAX_INDICES_ND);
+    MicroPrintf("Index innermost dimension length must not exceed %d.",
+                MAX_INDICES_ND);
     return kTfLiteError;
   }
 
@@ -171,13 +167,12 @@ TfLiteStatus EvalGatherNd(TfLiteContext* context,
       status = GatherNd<int8_t, IndicesT>(params, indices, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "Params type '%s' are not supported by gather_nd.",
-                         TfLiteTypeGetName(params->type));
+      MicroPrintf("Params type '%s' are not supported by gather_nd.",
+                  TfLiteTypeGetName(params->type));
       return kTfLiteError;
   }
   if (status != kTfLiteOk) {
-    TF_LITE_KERNEL_LOG(context, "gather_nd index out of bounds");
+    MicroPrintf("gather_nd index out of bounds");
   }
   return status;
 }
@@ -195,9 +190,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
       return EvalGatherNd<int32_t>(context, params, indices, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "Indices of type '%s' are not supported by gather_nd.",
-                         TfLiteTypeGetName(indices->type));
+      MicroPrintf("Indices of type '%s' are not supported by gather_nd.",
+                  TfLiteTypeGetName(indices->type));
       return kTfLiteError;
   }
 }

+ 13 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.cc

@@ -106,5 +106,17 @@ TfLiteStatus KernelRunner::Invoke() {
   return kTfLiteOk;
 }
 
+TfLiteStatus KernelRunner::Free() {
+  tflite::micro::ClearBufferApi(&context_);
+  context_.GetScratchBuffer = MicroContextGetScratchBuffer;
+
+  if (registration_.free == nullptr) {
+    MicroPrintf("TfLiteRegistration missing free function pointer!");
+    return kTfLiteError;
+  }
+
+  registration_.free(&context_, node_.user_data);
+  return kTfLiteOk;
+}
 }  // namespace micro
-}  // namespace tflite
+}  // namespace tflite

+ 5 - 0
code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_runner.h

@@ -48,6 +48,11 @@ class KernelRunner {
   // passed into the constructor of this class.
   TfLiteStatus Invoke();
 
+  // Calls Free on a given TfLiteRegistration pointer(if it's implemented).
+  // After successful Free, kTfLiteOk status will be returned. If Free is not
+  // implemented for a given kernel kTfLiteError will be returned.
+  TfLiteStatus Free();
+
   // Returns a pointer to the internal MockMicroGraph which KernelRunner uses
   // to stub out MicroGraph methods and track invocations on each subgraph.
   MockMicroGraph* GetMockGraph() { return &mock_micro_graph_; }

+ 44 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.cc

@@ -17,6 +17,7 @@ limitations under the License.
 
 #include "tensorflow/lite/c/common.h"
 #include "tensorflow/lite/micro/memory_helpers.h"
+#include "tensorflow/lite/micro/micro_error_reporter.h"
 
 namespace tflite {
 namespace micro {
@@ -39,9 +40,10 @@ int ValidateTensorIndexing(const TfLiteContext* context, int index,
 TfLiteRegistration RegisterOp(
     void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
     TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
-    TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node)) {
+    TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
+    void (*free)(TfLiteContext* context, void* buffer)) {
   return {/*init=*/init,
-          /*free=*/nullptr,
+          /*free=*/free,
           /*prepare=*/prepare,
           /*invoke=*/invoke,
           /*profiling_string=*/nullptr,
@@ -160,6 +162,46 @@ TfLiteStatus CopyOpInputsToOpOutputs(TfLiteContext* context, TfLiteNode* node) {
   return kTfLiteOk;
 }
 
+//  Args:
+//    1. int8_t tensor_data - int8_t buffer of unknown size who's data you'd
+//    like
+//  to print
+//    2. int n_btyes -  a small int representing number of bytes you want to
+//    print
+//  to debug output. It should always be <= tensor_data's size.
+//    3. prefix - optional message you'd like to print before printing bytes
+//
+//  Purpose:
+//    Function takes in paramaters above and prints n_bytes bytes from the
+//  tensor_data buffer. This can be use to debug  the output of a model and it's
+//  op.
+
+void PrintNBytes(const int8_t* tensor_data, int n_bytes, const char* prefix) {
+  if (prefix != nullptr) {
+    MicroPrintf("%s", prefix);
+  }
+
+  for (int i = 0; i < n_bytes; ++i) {
+    MicroPrintf(" %x", tensor_data[i]);
+  }
+  MicroPrintf("\n");
+}
+
+// same as the PrintNBytes above but the buffer needs to be extracted out of the
+// TfLiteEvalTensor*
+void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
+                 const char* prefix) {
+  const int8_t* tensor_data = tflite::micro::GetTensorData<int8_t>(tensor);
+  PrintNBytes(tensor_data, n_bytes, prefix);
+}
+
+// same as the PrintNBytes above but the buffer needs to be extracted out of the
+// TfLiteEvalTensor*
+void PrintNBytes(const TfLiteTensor* tensor, int n_bytes, const char* prefix) {
+  const int8_t* tensor_data = tflite::GetTensorData<int8_t>(tensor);
+  PrintNBytes(tensor_data, n_bytes, prefix);
+}
+
 TfLiteStatus CopyOpInputsToSubgraphInputs(TfLiteContext* context,
                                           TfLiteNode* node,
                                           MicroGraph* graph_info,

+ 16 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/kernel_util.h

@@ -21,8 +21,10 @@ limitations under the License.
 #include "tensorflow/lite/c/builtin_op_data.h"
 #include "tensorflow/lite/c/common.h"
 #include "tensorflow/lite/kernels/internal/compatibility.h"
+#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
 #include "tensorflow/lite/kernels/internal/types.h"
 #include "tensorflow/lite/micro/micro_context.h"
+#include "tensorflow/lite/micro/micro_error_reporter.h"
 
 namespace tflite {
 namespace micro {
@@ -30,7 +32,20 @@ namespace micro {
 TfLiteRegistration RegisterOp(
     void* (*init)(TfLiteContext* context, const char* buffer, size_t length),
     TfLiteStatus (*prepare)(TfLiteContext* context, TfLiteNode* node),
-    TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node));
+    TfLiteStatus (*invoke)(TfLiteContext* context, TfLiteNode* node),
+    void (*free)(TfLiteContext* context, void* buffer) = nullptr);
+
+// Prints out n bytes in a int8_t buffer as hex
+void PrintNBytes(const int8_t* tensor_data, int n_bytes,
+                 const char* prefix = nullptr);
+
+// Prints out the the n bytes in a TfLiteEvalTensor as hex
+void PrintNBytes(const TfLiteEvalTensor* tensor, int n_bytes,
+                 const char* prefix = nullptr);
+
+// Prints out the the n bytes in a TfLiteTensor as hex
+void PrintNBytes(const TfLiteTensor* tensor, int n_bytes,
+                 const char* prefix = nullptr);
 
 // Returns a mutable tensor for a given input index. is_variable must be checked
 // during prepare when the full TfLiteTensor is available.

+ 2 - 3
code/components/tflite-lib/tensorflow/lite/micro/kernels/l2_pool_2d.cc

@@ -125,9 +125,8 @@ TfLiteStatus L2Eval(TfLiteContext* context, TfLiteNode* node) {
       L2EvalFloat(*params, *input, &op_params, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "L2_POOL_2D only supports float32 currently, got %s.",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("L2_POOL_2D only supports float32 currently, got %s.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/l2norm.cc

@@ -126,8 +126,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
         tflite::micro::GetTensorData<int8_t>(input),
         tflite::micro::GetTensorData<int8_t>(output));
   } else {
-    TF_LITE_KERNEL_LOG(context, "Output type is %s, requires float.",
-                       TfLiteTypeGetName(output->type));
+    MicroPrintf("Output type is %s, requires float.",
+                TfLiteTypeGetName(output->type));
     return kTfLiteError;
   }
 

+ 2 - 3
code/components/tflite-lib/tensorflow/lite/micro/kernels/log_softmax.cc

@@ -132,9 +132,8 @@ TfLiteStatus LogSoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
       return kTfLiteOk;
     }
     default:
-      TF_LITE_KERNEL_LOG(context,
-                         "LOG_SOFTMAX only supports float32, int8, got %s.",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("LOG_SOFTMAX only supports float32, int8, got %s.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
 }

+ 33 - 7
code/components/tflite-lib/tensorflow/lite/micro/kernels/lstm_eval.cc

@@ -22,6 +22,8 @@ limitations under the License.
 #include "tensorflow/lite/c/builtin_op_data.h"
 #include "tensorflow/lite/c/common.h"
 #include "tensorflow/lite/kernels/internal/compatibility.h"
+#include "tensorflow/lite/kernels/internal/reference/integer_ops/logistic.h"
+#include "tensorflow/lite/kernels/internal/reference/integer_ops/tanh.h"
 #include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
 #include "tensorflow/lite/kernels/op_macros.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
@@ -530,11 +532,20 @@ void CalculateLstmGateInteger8x8_16(
   // Apply activation
   switch (activation) {
     case kTfLiteActSigmoid:
-      micro_tensor_utils::ApplySigmoid(gate, n_batch, n_cell, gate);
-      break;
-    case kTfLiteActTanh:
-      micro_tensor_utils::ApplyTanh(3, gate, n_batch, n_cell, gate);
+
+      reference_integer_ops::Logistic(
+          0 /*data->input_multiplier*/, 0 /*data->input_left_shift */,
+          n_batch * n_cell /*NumElements(input->dims)*/,
+          gate /* tflite::micro::GetTensorData<int16_t>(input) */,
+          gate /*tflite::micro::GetTensorData<int16_t>(output) */);
+
       break;
+    case kTfLiteActTanh: {
+      int32_t dims_data = n_batch * n_cell;
+      RuntimeShape tanh_inp_shape = RuntimeShape(1, &dims_data);
+      reference_integer_ops::Tanh(0, 0, tanh_inp_shape, gate, tanh_inp_shape,
+                                  gate);
+    } break;
     default:
       // Only Sigmoid or Tanh is used.
       TFLITE_ASSERT_FALSE;
@@ -599,7 +610,7 @@ void UpdateLstmCellInteger(int n_batch, int n_cell, int16_t* cell_state,
 //  - scratch1: scratch area of size n_batch*n_cell
 //  - scratch2: scratch area used by MatrixBatchVectorMultiplyAccumulate
 void CalculateLstmOutputInteger8x8_16(
-    int n_batch, int n_cell, int n_output, const int16_t* cell_state,
+    int n_batch, int n_cell, int n_output, int16_t* cell_state,
     int32_t cell_state_scale, const int16_t* output_gate,
     int32_t hidden_scale_a, int32_t hidden_scale_b, int32_t hidden_zp,
     const int8_t* projection_weights, int32_t proj_scale_a,
@@ -607,8 +618,23 @@ void CalculateLstmOutputInteger8x8_16(
     int32_t output_state_zp, int8_t quantized_proj_clip, int8_t* output_state,
     int16_t* scratch0, int8_t* scratch1, int32_t* scratch2) {
   // Note: unlike float/hybrid, the activation is always Tanh.
-  micro_tensor_utils::ApplyTanh(15 + cell_state_scale, cell_state, n_batch,
-                                n_cell, scratch0);
+
+  {
+    int32_t tanh_input_left_shift = (15 + cell_state_scale) - 3;
+    int32_t dims_data = n_batch * n_cell;
+    if (tanh_input_left_shift < 0) /* handling negative shift value */
+    {
+      int32_t i;
+      tanh_input_left_shift = -tanh_input_left_shift;
+      for (i = 0; i < dims_data; i++) {
+        cell_state[i] = cell_state[i] >> tanh_input_left_shift;
+      }
+      tanh_input_left_shift = 0;
+    }
+    RuntimeShape tanh_inp_shape = RuntimeShape(1, &dims_data);
+    reference_integer_ops::Tanh(0, tanh_input_left_shift, tanh_inp_shape,
+                                cell_state, tanh_inp_shape, scratch0);
+  }
   micro_tensor_utils::CwiseMul(output_gate, scratch0, hidden_scale_a,
                                hidden_scale_b, n_batch, n_cell, hidden_zp,
                                scratch1);

+ 4 - 6
code/components/tflite-lib/tensorflow/lite/micro/kernels/maximum_minimum.cc

@@ -98,15 +98,13 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
         TFLiteOperation<int64_t, OpType>(context, node, op_context);
         break;
       default:
-        TF_LITE_KERNEL_LOG(context,
-                           "Type %s (%d) is not supported by Maximum/Minimum.",
-                           TfLiteTypeGetName(op_context.output->type),
-                           op_context.output->type);
+        MicroPrintf("Type %s (%d) is not supported by Maximum/Minimum.",
+                    TfLiteTypeGetName(op_context.output->type),
+                    op_context.output->type);
         return kTfLiteError;
     }
   } else {
-    TF_LITE_KERNEL_LOG(context,
-                       "Kernel type not supported by Maximum/Minimum.");
+    MicroPrintf("Kernel type not supported by Maximum/Minimum.");
     return kTfLiteError;
   }
   return kTfLiteOk;

+ 2 - 0
code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_ops.h

@@ -72,6 +72,7 @@ TfLiteRegistration Register_READ_VARIABLE();
 TfLiteRegistration Register_RELU();
 TfLiteRegistration Register_RELU6();
 TfLiteRegistration Register_RESIZE_BILINEAR();
+TfLiteRegistration Register_SELECT_V2();
 TfLiteRegistration Register_SHAPE();
 TfLiteRegistration Register_SLICE();
 TfLiteRegistration Register_SPACE_TO_BATCH_ND();
@@ -79,6 +80,7 @@ TfLiteRegistration Register_SPACE_TO_DEPTH();
 TfLiteRegistration Register_SQUARED_DIFFERENCE();
 TfLiteRegistration Register_SQUEEZE();
 TfLiteRegistration Register_SUB();
+TfLiteRegistration Register_SUM();
 TfLiteRegistration Register_SVDF();
 TfLiteRegistration Register_TRANSPOSE();
 TfLiteRegistration Register_TRANSPOSE_CONV();

+ 1 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/micro_tensor_utils.cc

@@ -663,7 +663,7 @@ void PortableCwiseMul(const int16_t* input_1, const int16_t* input_2,
       const int16_t b = input_2[index];
       int32_t value = static_cast<int32_t>(a) * static_cast<int32_t>(b);
       value = MultiplyByQuantizedMultiplier(value, multiplier, shift);
-      value -= output_zp;
+      value += output_zp;
       value = std::min(std::max(static_cast<int32_t>(-128), value),
                        static_cast<int32_t>(127));
 

+ 10 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/mul.h

@@ -1,4 +1,4 @@
-/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -60,6 +60,15 @@ void EvalMulFloatReference(TfLiteContext* context, TfLiteNode* node,
                            const TfLiteEvalTensor* input2,
                            TfLiteEvalTensor* output);
 
+// Generic must define registration function.
+TfLiteRegistration Register_MUL();
+
+#if defined(CMSIS_NN)
+TfLiteRegistration Register_MUL_INT8();
+#else
+// Fallback registration
+inline TfLiteRegistration Register_MUL_INT8() { return Register_MUL(); }
+#endif
 }  // namespace tflite
 
 #endif  // TENSORFLOW_LITE_MICRO_KERNELS_MUL_H_

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/neg.cc

@@ -41,8 +41,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                             tflite::micro::GetTensorData<float>(output));
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input->type), input->type);
+      MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
+                  input->type);
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/pack.cc

@@ -95,8 +95,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
                                data->axis);
     }
     default: {
-      TF_LITE_KERNEL_LOG(context, "Type '%s' is not supported by pack.",
-                         TfLiteTypeGetName(output->type));
+      MicroPrintf("Type '%s' is not supported by pack.",
+                  TfLiteTypeGetName(output->type));
       return kTfLiteError;
     }
   }

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/pad.cc

@@ -213,8 +213,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     } break;
     default:
 
-      TF_LITE_KERNEL_LOG(context, "Type %s not currently supported by Pad.",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("Type %s not currently supported by Pad.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 4 - 4
code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.cc

@@ -45,8 +45,8 @@ TfLiteStatus AverageEval(TfLiteContext* context, TfLiteNode* node) {
       AveragePoolingEvalQuantized(context, node, params, data, input, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Input type %s is not currently supported",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("Input type %s is not currently supported",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
   return kTfLiteOk;
@@ -73,8 +73,8 @@ TfLiteStatus MaxEval(TfLiteContext* context, TfLiteNode* node) {
       MaxPoolingEvalQuantized(context, node, params, data, input, output);
       break;
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s not currently supported.",
-                         TfLiteTypeGetName(input->type));
+      MicroPrintf("Type %s not currently supported.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
   return kTfLiteOk;

+ 15 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/pooling.h

@@ -1,4 +1,4 @@
-/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ limitations under the License.
 
 #include "tensorflow/lite/c/builtin_op_data.h"
 #include "tensorflow/lite/c/common.h"
+#include "tensorflow/lite/micro/kernels/micro_ops.h"
 
 namespace tflite {
 
@@ -66,6 +67,19 @@ void MaxPoolingEvalQuantized(TfLiteContext* context, TfLiteNode* node,
                              const TfLiteEvalTensor* input,
                              TfLiteEvalTensor* output);
 
+#if defined(CMSIS_NN)
+TfLiteRegistration Register_AVERAGE_POOL_2D_INT8();
+
+TfLiteRegistration Register_MAX_POOL_2D_INT8();
+#else
+inline TfLiteRegistration Register_AVERAGE_POOL_2D_INT8() {
+  return tflite::Register_AVERAGE_POOL_2D();
+}
+
+inline TfLiteRegistration Register_MAX_POOL_2D_INT8() {
+  return tflite::Register_MAX_POOL_2D();
+}
+#endif
 }  // namespace tflite
 
 #endif  // TENSORFLOW_LITE_MICRO_KERNELS_POOLING_H_

+ 2 - 3
code/components/tflite-lib/tensorflow/lite/micro/kernels/prelu.cc

@@ -61,9 +61,8 @@ TfLiteStatus PreluEval(TfLiteContext* context, TfLiteNode* node) {
       return kTfLiteOk;
     } break;
     default:
-      TF_LITE_KERNEL_LOG(
-          context, "Only float32 and uint8_t are supported currently, got %d.",
-          TfLiteTypeGetName(input->type));
+      MicroPrintf("Only float32 and uint8_t are supported currently, got %d.",
+                  TfLiteTypeGetName(input->type));
       return kTfLiteError;
   }
 }

+ 1 - 1
code/components/tflite-lib/tensorflow/lite/micro/kernels/reduce_common.cc

@@ -28,7 +28,7 @@ limitations under the License.
 
 namespace tflite {
 
-const int kMaxNumberOfAxis = 4;
+const int kMaxNumberOfAxis = 5;
 const int kMaxNumberOfReducedAxis = 2;
 
 TfLiteStatus PrepareSimple(TfLiteContext* context, TfLiteNode* node,

+ 2 - 4
code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_bilinear.cc

@@ -55,8 +55,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   auto* params =
       reinterpret_cast<TfLiteResizeBilinearParams*>(node->builtin_data);
   if (params->half_pixel_centers && params->align_corners) {
-    TF_LITE_KERNEL_LOG(
-        context, "If half_pixel_centers is True, align_corners must be False.");
+    MicroPrintf("If half_pixel_centers is True, align_corners must be False.");
     return kTfLiteError;
   }
 
@@ -100,8 +99,7 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
         tflite::micro::GetTensorShape(output),
         tflite::micro::GetTensorData<int8_t>(output));
   } else {
-    TF_LITE_KERNEL_LOG(context, "Output type is %d, requires float or int8.",
-                       output->type);
+    MicroPrintf("Output type is %d, requires float or int8.", output->type);
     return kTfLiteError;
   }
 

+ 1 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/resize_nearest_neighbor.cc

@@ -21,7 +21,6 @@ limitations under the License.
 #include "tensorflow/lite/kernels/kernel_util.h"
 #include "tensorflow/lite/kernels/op_macros.h"
 #include "tensorflow/lite/micro/kernels/kernel_util.h"
-#include "tensorflow/lite/micro/micro_error_reporter.h"
 
 namespace tflite {
 namespace ops {
@@ -55,7 +54,7 @@ TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
   output->type = input->type;
 
   if (!IsConstantTensor(size)) {
-    TF_LITE_KERNEL_LOG(context, "Dynamic tensors are unsupported in tfmicro.");
+    MicroPrintf("Dynamic tensors are unsupported in tfmicro.");
     return kTfLiteError;
   }
 

+ 196 - 0
code/components/tflite-lib/tensorflow/lite/micro/kernels/select.cc

@@ -0,0 +1,196 @@
+/* Copyright 2022 The TensorFlow Authors. All Rights Reserved.
+
+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.
+==============================================================================*/
+#include "tensorflow/lite/kernels/internal/reference/select.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "tensorflow/lite/c/common.h"
+#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
+#include "tensorflow/lite/kernels/kernel_util.h"
+#include "tensorflow/lite/micro/kernels/kernel_util.h"
+
+namespace tflite {
+
+constexpr int kInputTensorCondition = 0;
+constexpr int kInputTensorX = 1;
+constexpr int kInputTensorY = 2;
+constexpr int kOutputTensor = 0;
+
+struct OpData {
+  bool requires_broadcast;
+  // True if input condition is scalar or input condition has rank one and
+  // matches the first dimension of other inputs.
+  bool has_low_rank_input_condition;
+};
+
+void* SelectInit(TfLiteContext* context, const char* buffer, size_t length) {
+  TFLITE_DCHECK(context->AllocatePersistentBuffer != nullptr);
+  auto* data = static_cast<OpData*>(
+      context->AllocatePersistentBuffer(context, sizeof(OpData)));
+  data->requires_broadcast = false;
+  data->has_low_rank_input_condition = false;
+  return data;
+}
+
+TfLiteStatus CheckBroadcastShape(TfLiteContext* context,
+                                 const TfLiteTensor* input1,
+                                 const TfLiteTensor* input2,
+                                 const TfLiteTensor* input3,
+                                 const TfLiteIntArray* output_shape) {
+  const int dims1 = NumDimensions(input1);
+  const int dims2 = NumDimensions(input2);
+  const int dims3 = NumDimensions(input3);
+  const int out_dims = std::max(std::max(dims1, dims2), dims3);
+  TF_LITE_ENSURE_EQ(context, out_dims, output_shape->size);
+
+  for (int i = 0; i < out_dims; ++i) {
+    const int d1 = i >= dims1 ? 1 : SizeOfDimension(input1, dims1 - i - 1);
+    const int d2 = i >= dims2 ? 1 : SizeOfDimension(input2, dims2 - i - 1);
+    const int d3 = i >= dims3 ? 1 : SizeOfDimension(input3, dims3 - i - 1);
+    const int min_value = std::min(std::min(d1, d2), d3);
+    int max_value = std::max(std::max(d1, d2), d3);
+    // If one dimention is 0, others must be 0 or 1.
+    if (min_value == 0) max_value = 0;
+    if (!(d1 == 1 || d1 == max_value) || !(d2 == 1 || d2 == max_value) ||
+        !(d3 == 1 || d3 == max_value)) {
+      MicroPrintf("Given shapes are not broadcastable.");
+      return kTfLiteError;
+    }
+    TF_LITE_ENSURE_EQ(context, output_shape->data[out_dims - i - 1], max_value);
+  }
+  return kTfLiteOk;
+}
+
+TfLiteStatus SelectPrepare(TfLiteContext* context, TfLiteNode* node) {
+  OpData* data = reinterpret_cast<OpData*>(node->user_data);
+
+  TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
+  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
+
+  MicroContext* micro_context = GetMicroContext(context);
+  TfLiteTensor* input_condition =
+      micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
+
+  TfLiteTensor* input_x =
+      micro_context->AllocateTempInputTensor(node, kInputTensorX);
+
+  TfLiteTensor* input_y =
+      micro_context->AllocateTempInputTensor(node, kInputTensorY);
+
+  TfLiteTensor* output =
+      micro_context->AllocateTempOutputTensor(node, kOutputTensor);
+
+  // Input must be bool.
+  TF_LITE_ENSURE_TYPES_EQ(context, input_condition->type, kTfLiteBool);
+  TF_LITE_ENSURE_TYPES_EQ(context, input_x->type, input_y->type);
+  output->type = input_x->type;
+
+  // Respect the original output shape when there are mixed shapes to represent
+  // a scalar data.
+  if (GetTensorShape(input_condition).FlatSize() == 1 &&
+      GetTensorShape(input_x).FlatSize() == 1 &&
+      GetTensorShape(input_y).FlatSize() == 1 &&
+      GetTensorShape(output).FlatSize() == 1) {
+    return kTfLiteOk;
+  }
+
+  bool same_shape = HaveSameShapes(input_condition, input_x) &&
+                    HaveSameShapes(input_x, input_y);
+  if (!same_shape) {
+    TF_LITE_ENSURE_OK(
+        context, CheckBroadcastShape(context, input_condition, input_x, input_y,
+                                     output->dims));
+    data->requires_broadcast = true;
+  }
+
+  micro_context->DeallocateTempTfLiteTensor(input_condition);
+  micro_context->DeallocateTempTfLiteTensor(input_x);
+  micro_context->DeallocateTempTfLiteTensor(input_y);
+  micro_context->DeallocateTempTfLiteTensor(output);
+
+  return kTfLiteOk;
+}
+
+TfLiteStatus SelectEval(TfLiteContext* context, TfLiteNode* node) {
+  OpData* data = static_cast<OpData*>(node->user_data);
+  MicroContext* micro_context = GetMicroContext(context);
+
+  TfLiteTensor* input_condition =
+      micro_context->AllocateTempInputTensor(node, kInputTensorCondition);
+
+  TfLiteTensor* input_x =
+      micro_context->AllocateTempInputTensor(node, kInputTensorX);
+
+  TfLiteTensor* input_y =
+      micro_context->AllocateTempInputTensor(node, kInputTensorY);
+
+  TfLiteTensor* output =
+      micro_context->AllocateTempOutputTensor(node, kOutputTensor);
+
+#define TF_LITE_SELECT(type, op)                                           \
+  reference_ops::op(GetTensorShape(input_condition),                       \
+                    GetTensorData<bool>(input_condition),                  \
+                    GetTensorShape(input_x), GetTensorData<type>(input_x), \
+                    GetTensorShape(input_y), GetTensorData<type>(input_y), \
+                    GetTensorShape(output), GetTensorData<type>(output));
+
+#define TF_LITE_SWITCH(type, op)                                     \
+  switch (type) {                                                    \
+    case kTfLiteFloat32:                                             \
+      TF_LITE_SELECT(float, op);                                     \
+      break;                                                         \
+    case kTfLiteInt8:                                                \
+      TF_LITE_SELECT(int8_t, op);                                    \
+      break;                                                         \
+    case kTfLiteInt16:                                               \
+      TF_LITE_SELECT(int16_t, op);                                   \
+      break;                                                         \
+    default:                                                         \
+      MicroPrintf("Does not support type other than %s, but got %s", \
+                  "int8|int16|float32", TfLiteTypeGetName(type));    \
+      return kTfLiteError;                                           \
+  }
+
+  if (data->has_low_rank_input_condition) {
+    MicroPrintf("Not yet implemented.");
+    return kTfLiteError;
+  } else if (data->requires_broadcast) {
+    TF_LITE_SWITCH(input_x->type, BroadcastSelect5DSlow);
+  } else {
+    TF_LITE_SWITCH(input_x->type, Select);
+  }
+
+#undef TF_LITE_SELECT
+#undef TF_LITE_SWITCH
+  micro_context->DeallocateTempTfLiteTensor(input_condition);
+  micro_context->DeallocateTempTfLiteTensor(input_x);
+  micro_context->DeallocateTempTfLiteTensor(input_y);
+  micro_context->DeallocateTempTfLiteTensor(output);
+
+  return kTfLiteOk;
+}
+
+// SelectV2 op selects values of 'x' if the corresponding value of 'condition'
+// is true or the value of 'y' if false. There are valid condition input sizes:
+//
+// 1. Either the same shape (in which case the select is elementwise), or
+// 2. Broadcastable shapes between 'condition', 'x' and 'y'.
+TfLiteRegistration Register_SELECT_V2() {
+  return tflite::micro::RegisterOp(tflite::SelectInit, tflite::SelectPrepare,
+                                   tflite::SelectEval);
+}
+
+}  // namespace tflite

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/shape.cc

@@ -47,8 +47,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
   TfLiteEvalTensor* output =
       tflite::micro::GetEvalOutput(context, node, kOutputTensor);
   if (output->type != kTfLiteInt32) {
-    TF_LITE_KERNEL_LOG(context, "Output type %s (%d) not supported.",
-                       TfLiteTypeGetName(output->type), output->type);
+    MicroPrintf("Output type %s (%d) not supported.",
+                TfLiteTypeGetName(output->type), output->type);
     return kTfLiteError;
   } else {
     ExtractShape(input, tflite::micro::GetTensorData<int32_t>(output));

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/slice.cc

@@ -106,8 +106,8 @@ TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
     GetBeginAndSizeVectors<int64_t>(input->dims->size, begin, size,
                                     op_params.begin, op_params.size);
   } else {
-    TF_LITE_KERNEL_LOG(context, "Begin tensor type %s (%d) not supported.",
-                       TfLiteTypeGetName(input->type), input->type);
+    MicroPrintf("Begin tensor type %s (%d) not supported.",
+                TfLiteTypeGetName(input->type), input->type);
     return kTfLiteError;
   }
 

+ 2 - 2
code/components/tflite-lib/tensorflow/lite/micro/kernels/softmax.cc

@@ -75,8 +75,8 @@ TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
       return kTfLiteOk;
     }
     default:
-      TF_LITE_KERNEL_LOG(context, "Type %s (%d) not supported.",
-                         TfLiteTypeGetName(input->type), input->type);
+      MicroPrintf("Type %s (%d) not supported.", TfLiteTypeGetName(input->type),
+                  input->type);
       return kTfLiteError;
   }
 }

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff