diff --git a/FeatureRequest.md b/FeatureRequest.md
index d9157b90..a0c478e1 100644
--- a/FeatureRequest.md
+++ b/FeatureRequest.md
@@ -11,6 +11,22 @@
____
+#### #27 Use Homie Spec for Mqtt binding
+
+* Use the standardized Home Protocol for the Mqtt binding
+* https://homieiot.github.io/
+
+#### #26 Changes behaviour for "N" replacement
+
+* in case the higher digits has already increased by minium 1 - don't set the "N" to the last value, but to "0"
+* https://github.com/jomjol/AI-on-the-edge-device/issues/792
+
+
+#### #25 Trigger Measurement via MQTT
+
+* https://github.com/jomjol/AI-on-the-edge-device/issues/727
+
+
#### #24 Show Mqtt state directly in Webserver
* Show MQTT log in Web page. E.g. connection established or failed to connect...
@@ -48,18 +64,15 @@ ____
-#### #18 Document WLAN-strength in web page
+#### ~~#18 Document WLAN-strength in web page~~
-* https://github.com/jomjol/AI-on-the-edge-device/issues/563
+* ~~https://github.com/jomjol/AI-on-the-edge-device/issues/563~~
-#### #17 Direct InfluxDB connection
+#### ~~#17 Direct InfluxDB connection~~
-* https://github.com/jomjol/AI-on-the-edge-device/issues/534
-* Direct interface to a InfluxDB data base
-* Integrate InfluxDB interface in firmware
-* Adapt html web page for configuration
+* ~~Done in v10.6.0~~
#### #16 Serial Communication
@@ -101,9 +114,9 @@ ____
-#### #12 Less reboots due to memory leakage
+#### ~~#12 Less reboots due to memory leakage~~
-* Issue: #414 & #425 #430
+* ~~Issue: #414 & #425 #430~~
@@ -222,4 +235,4 @@ ____
* ~~Implementation of a software module for external light source (e.g. WS8132 LED controller, ...)~~
* ~~Update of the camera module to use the external light instead of the internal flash light~~
-* ~~Adopt the configuration algorithm with a configurable light source~~
\ No newline at end of file
+* ~~Adopt the configuration algorithm with a configurable light source~~
diff --git a/README.md b/README.md
index 66953384..61343250 100644
--- a/README.md
+++ b/README.md
@@ -52,9 +52,31 @@ In other cases you can contact the developer via email:
~/.ssh/id_rsa_base64
+ - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa
+ - chmod 600 ~/.ssh/id_rsa
+ - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
+
+before_script:
+ # Add gitlab ssh key
+ - *add_ssh_key
+ # Set git config
+ - *set_git_config
+
+.build_esp32s3: &build_esp32s3
+ - idf.py set-target esp32s3 build
+
+.build_esp32: &build_esp32
+ - idf.py set-target esp32 build
+
+build_demo:
+ stage: build
+ image: $CI_DOCKER_REGISTRY/esp32-ci-env:esp-nn
+ tags:
+ - build
+ script:
+ # Clone IDF
+ - git clone --recursive --single-branch -b release/v4.4 --reference-if-able /local_references/gitlab/ https://gitlab-ci-token:${BOT_TOKEN}@gitlab.espressif.cn:6688/espressif/esp-idf.git
+ - cd esp-idf
+ - ./install.sh
+ - . ./export.sh
+ - cd ..
+ # Build examples now
+ - cd test_app
+ # Build esp32s3
+ - *build_esp32s3
+ # Build esp32
+ - *build_esp32
+ - cd -
diff --git a/code/components/esp-nn/CMakeLists.txt b/code/components/esp-nn/CMakeLists.txt
new file mode 100644
index 00000000..ba45866a
--- /dev/null
+++ b/code/components/esp-nn/CMakeLists.txt
@@ -0,0 +1,50 @@
+idf_build_get_property(idf_target IDF_TARGET)
+
+set(c_srcs
+ "src/activation_functions/esp_nn_relu_ansi.c"
+ "src/basic_math/esp_nn_add_ansi.c"
+ "src/basic_math/esp_nn_mul_ansi.c"
+ "src/convolution/esp_nn_conv_ansi.c"
+ "src/convolution/esp_nn_conv_opt.c"
+ "src/convolution/esp_nn_depthwise_conv_ansi.c"
+ "src/convolution/esp_nn_depthwise_conv_opt.c"
+ "src/fully_connected/esp_nn_fully_connected_ansi.c"
+ "src/softmax/esp_nn_softmax_ansi.c"
+ "src/softmax/esp_nn_softmax_opt.c"
+ "src/pooling/esp_nn_avg_pool_ansi.c"
+ "src/pooling/esp_nn_max_pool_ansi.c")
+
+if(CONFIG_IDF_TARGET_ESP32S3)
+ set(s3_srcs
+ "src/common/esp_nn_common_functions_esp32s3.S"
+ "src/common/esp_nn_multiply_by_quantized_mult_esp32s3.S"
+ "src/common/esp_nn_multiply_by_quantized_mult_ver1_esp32s3.S"
+ "src/activation_functions/esp_nn_relu_s8_esp32s3.S"
+ "src/basic_math/esp_nn_add_s8_esp32s3.S"
+ "src/basic_math/esp_nn_mul_s8_esp32s3.S"
+ "src/convolution/esp_nn_conv_esp32s3.c"
+ "src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c"
+ "src/convolution/esp_nn_conv_s16_mult8_esp32s3.S"
+ "src/convolution/esp_nn_conv_s8_mult8_1x1_esp32s3.S"
+ "src/convolution/esp_nn_conv_s16_mult4_1x1_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult1_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult4_esp32s3.S"
+ "src/convolution/esp_nn_depthwise_conv_s16_mult8_esp32s3.S"
+ "src/fully_connected/esp_nn_fully_connected_s8_esp32s3.S"
+ "src/pooling/esp_nn_max_pool_s8_esp32s3.S"
+ "src/pooling/esp_nn_avg_pool_s8_esp32s3.S")
+endif()
+
+idf_component_register(SRCS "${c_srcs}"
+ "${s3_srcs}"
+ INCLUDE_DIRS "include" "src/common")
+
+if(CONFIG_IDF_TARGET_ESP32S3)
+ target_compile_options(${COMPONENT_LIB} PRIVATE -mlongcalls -fno-unroll-loops -O2 -Wno-unused-function)
+else()
+ target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
+endif()
\ No newline at end of file
diff --git a/code/components/esp-nn/Kconfig.projbuild b/code/components/esp-nn/Kconfig.projbuild
new file mode 100644
index 00000000..a146305b
--- /dev/null
+++ b/code/components/esp-nn/Kconfig.projbuild
@@ -0,0 +1,29 @@
+menu "ESP-NN"
+
+choice NN_OPTIMIZATIONS
+ bool "Optimization for nn functions"
+ default NN_OPTIMIZED
+ help
+ Use ANSI-C versions for verification and debug purpose.
+ Optimisations are automatically picked up for a chipset.
+ For ESP32-S3, assembly optimisations are selected.
+ For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
+
+config NN_ANSI_C
+ bool "ANSI C"
+ help
+ ANSI C versions for verification and debug purposes.
+config NN_OPTIMIZED
+ bool "Optimized versions"
+ help
+ Optimisations are automatically picked up for a chipset.
+ For ESP32-S3, assembly optimisations are selected.
+ For other platforms(viz., ESP32, ESP32-C3), generic optimisations are used.
+endchoice
+
+config NN_OPTIMIZATIONS
+ int
+ default 0 if NN_ANSI_C
+ default 1 if NN_OPTIMIZED
+
+endmenu
diff --git a/code/components/esp-nn/LICENSE b/code/components/esp-nn/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/code/components/esp-nn/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/code/components/esp-nn/README.md b/code/components/esp-nn/README.md
new file mode 100644
index 00000000..f70f4074
--- /dev/null
+++ b/code/components/esp-nn/README.md
@@ -0,0 +1,55 @@
+# ESP-NN
+
+The library contains optimised NN (Neural Network) functions for various Espressif chipsets.
+
+* Supported platforms:
+ * TensorFlow Lite Micro (TFLite Micro). Repo can be found [here](https://github.com/espressif/tflite-micro-esp-examples)
+
+* Supported ESP chipsets include:
+ * ESP32-S3 (Assembly versions optimised to benefit from vector instructions of ESP32-S3)
+ * ESP32 (Generic optimisations)
+ * ESP32-C3 (Generic optimisations)
+
+## Performance
+
+### Kernelwise performance for s8 versions:
+
+ * Kernelwise performance on ESP32-S3 chip
+ * Numbers are ticks taken for kernel to execute
+ * Chip config: 240MHz, SPI: QPI 80MHz, Data cache: 64KB
+
+ | Function | ANSI C | ESP32-S3 Opt | Opt Ratio | Data info | Memory |
+ | ----------------| --------|---------|---------|-------------|-----------|
+ | elementwise_add | 320397 | 87119 | 3.68 | size = 1615 | External |
+ | elementwise_mul | 125958 | 44239 | 2.85 | size = 1615 | External |
+ | convolution | 4663012 | 428675 | 10.88 | input(10,10), filter(64x1x1x64) | External |
+ | convolution | 301014 | 32433 | 9.28 | input(8,8), filter(16x1x1x16) | External |
+ | convolution | 2115418 | 1020923 | 2.07 | input(10,10), filter(64x3x3x3) | External |
+ | depthwise conv | 1190062 | 203278 | 5.85 | input (18, 18), pad(0,0), stride(1,1) filter: 1x3x3x16 | External |
+ | depthwise conv | 837072 | 182335 | 4.59 | input (12, 12), pad(1,1), stride(1,1) filter: 8x5x5x4 | External |
+ | max pool | 485714 | 76747 | 6.33 | input(16,16), filter (1x3x3x16) | Internal |
+ | avg pool | 541462 | 160580 | 3.37 | input(16,16), filter (1x3x3x16) | Internal |
+ | fully connected | 15853 | 9547 | 1.66 | len: 265, ch = 3 | Internal |
+ | prelu (relu6) | 19472 | 2734 | 7.12 | size, 1615 | Internal |
+
+
+## Configuration
+
+ * To configure, please use `idf.py menuconfig` and under `ESP-NN` select `NN_OPTIMIZATIONS`
+ * There are two options presented:
+ * Optimized versions
+ * ANSI C
+
+ * Default selection is for `Optimized versions`. For ESP32-S3, assembly versions are automatically selected, whereas for other chipsets (viz., ESP32, ESP32-C3), generic optimisations are selected.
+ * For debugging purposes, you may want to select `ANSI C` reference versions.
+
+
+## Contributing
+
+If you encounter an issue with ESP-NN, or wish to submit a feature request, please use the Issues section on the Github.
+
+For general questions related to this library, please use the esp32.com forum.
+
+## Copyrights and License
+
+All original source code in this repository is Copyright (C) 2020-2021 Espressif Systems. This source code is licensed under the Apache License 2.0 as described in the file LICENSE.
diff --git a/code/components/esp-nn/include/esp_nn.h b/code/components/esp-nn/include/esp_nn.h
new file mode 100644
index 00000000..bd533119
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn.h
@@ -0,0 +1,46 @@
+// Copyright 2020-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.
+
+#pragma once
+
+#if defined(CONFIG_NN_OPTIMIZED)
+// select apt optimisations
+#ifdef CONFIG_IDF_TARGET_ESP32S3
+#define ARCH_ESP32_S3 1
+#endif
+#ifdef CONFIG_IDF_TARGET_ESP32
+#define ARCH_ESP32 1
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* reference kernels included by default */
+#include "esp_nn_ansi_headers.h"
+
+#if defined(CONFIG_NN_OPTIMIZED)
+#if defined(ARCH_ESP32_S3)
+#include "esp_nn_esp32s3.h"
+#else // for other platforms use generic optimisations
+#include "esp_nn_generic_opt.h"
+#endif // #if defined(ARCH_ESP32_S3)
+#else
+#include "esp_nn_ansi_c.h"
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/code/components/esp-nn/include/esp_nn_ansi_c.h b/code/components/esp-nn/include/esp_nn_ansi_c.h
new file mode 100644
index 00000000..8279ebef
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn_ansi_c.h
@@ -0,0 +1,47 @@
+// Copyright 2020-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.
+
+/**
+ * @file Header definitions to include for ANSI C versions.
+ * These are just typedefs to pick up ANSI versions.
+ */
+
+#pragma once
+
+#include "esp_nn_defs.h"
+#include "esp_nn_ansi_headers.h"
+
+#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
+#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
+
+#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_ansi
+
+#define esp_nn_conv_s8 esp_nn_conv_s8_ansi
+
+#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_ansi
+#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_ansi
+
+#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_ansi
+#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_ansi
+
+#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
+
+#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
+#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
+
+#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
+
+#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_ansi
+#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_ansi
+#define esp_nn_softmax_s8 esp_nn_softmax_s8_ansi
diff --git a/code/components/esp-nn/include/esp_nn_ansi_headers.h b/code/components/esp-nn/include/esp_nn_ansi_headers.h
new file mode 100644
index 00000000..52ebb680
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn_ansi_headers.h
@@ -0,0 +1,309 @@
+// Copyright 2020-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.
+
+#pragma once
+
+/**
+ * @file Header definitions to include for esp_nn reference functions
+ */
+
+#include "esp_nn_defs.h"
+/************************** Basic math functions ****************************/
+
+/**
+ * @brief elementwise addition
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ *
+ * shift values are expected to be <= 0
+ */
+void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ const int32_t input1_mult,
+ const int32_t input2_mult,
+ const int32_t input1_shift,
+ const int32_t input2_shift,
+ const int32_t left_shift,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size);
+/**
+ * @brief elementwise multiplication
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ *
+ * output shift is expected to be <= 0
+ */
+void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size);
+
+
+/************************** Convolution functions *****************************/
+
+/**
+ * @brief depthwise convolution per channel
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * Version used in tflite is per channel.
+ * This version follows the same footsprints.
+ * Meaning, it has per out_channel shift and multiplier for
+ * requantization
+ *
+ * optimization notes: Though input_offset is int32 type,
+ * offset values are contained in 8 bits [-128, 127]
+ */
+void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+/**
+ * @brief 2d-convolution channelwise
+ *
+ * @note operation: result += (input + offset) * filter
+ *
+ * inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params);
+void esp_nn_set_conv_scratch_buf_ansi(const void *buf);
+
+int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params);
+void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf);
+
+/************************** Activation functions *****************************/
+
+/**
+ * @brief relu6
+ *
+ * @note inout: int8_t
+ */
+void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size);
+
+/************************** Pooling functions *****************************/
+
+
+/**
+ * @brief max_pool
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_max_pool_s8_ansi(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels);
+
+/**
+ * @brief avg_pool
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_avg_pool_s8_ansi(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels);
+
+
+/************************** Fully connected functions ***********************/
+
+/**
+ * @brief fully connected
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
+ const int32_t input_offset,
+ const uint16_t row_len,
+ const int8_t *filter_data,
+ const int32_t filter_offset,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t out_shift,
+ const int32_t out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+/**
+ * @brief Get scratch buffer size needed by softmax function
+ *
+ * @param width
+ * @param height
+ * @return size in bytes
+ *
+ * @note buffer must be 4 byte aligned
+ */
+int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height);
+
+/* ANSI C function to be hooked up when optimised version needed */
+int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height);
+
+/**
+ * @brief Set scratch buffer to be used by softmax function
+ *
+ * @param buffer this can be NULL if one needs to unset it
+ * must be aligned to 4 bytes
+ */
+void esp_nn_set_softmax_scratch_buf_ansi(void *buffer);
+
+/**
+ * @brief reference softmax function
+ *
+ * @note inputs type: int8_t, output: int8_t
+ */
+void esp_nn_softmax_s8_ansi(const int8_t *input_data,
+ const int32_t height,
+ const int32_t width,
+ const int32_t mult,
+ const int32_t shift,
+ const int32_t diff_min,
+ int8_t *output_data);
+
+
+//////////////////////////// Generic optimisations /////////////////////////////
+
+/************************** Convolution functions *****************************/
+
+/**
+ * @brief 2d-convolution channelwise optimized version
+ *
+ * @note operation: result += (input + offset) * filter
+ *
+ * inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+/**
+ * @brief depthwise convolution per channel optimized version
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * Version used in tflite is per channel.
+ * This version follows the same footsprints.
+ * Meaning, it has per out_channel shift and multiplier for
+ * requantization
+ *
+ * optimization notes: Though input_offset is int32 type,
+ * offset values are contained in 8 bits [-128, 127]
+ */
+void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params);
+void esp_nn_set_conv_scratch_buf_opt(const void *buf);
+
+int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params);
+void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf);
+
+/* ANSI C function to be hooked up when optimised version needed */
+void esp_nn_set_softmax_scratch_buf_opt(void *buffer);
+
+/**
+ * @brief optimised version of softmax function
+ *
+ * @note the function uses extra buffer (4 * width bytes)
+ * hence, scratch buffers must be set before calling this.
+ */
+void esp_nn_softmax_s8_opt(const int8_t *input_data,
+ const int32_t height,
+ const int32_t width,
+ const int32_t mult,
+ const int32_t shift,
+ const int32_t diff_min,
+ int8_t *output_data);
diff --git a/code/components/esp-nn/include/esp_nn_defs.h b/code/components/esp-nn/include/esp_nn_defs.h
new file mode 100644
index 00000000..756d8e6f
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn_defs.h
@@ -0,0 +1,83 @@
+// Copyright 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.
+
+#pragma once
+
+#include
+
+/**
+ * @brief structure to club data dims
+ * this structure can be used for input, output and filter
+ */
+typedef struct data_dims {
+ int32_t width;
+ int32_t height;
+ int32_t channels;
+
+ int32_t extra; // can be used as batch or any other param
+} data_dims_t;
+
+/**
+ * @brief 2d data structure (width, height)
+ *
+ */
+typedef struct data_2d {
+ int32_t width;
+ int32_t height;
+} data_2d_t;
+
+/**
+ * @brief min/max activation
+ */
+typedef struct act_params {
+ int32_t min;
+ int32_t max;
+} act_params_t;
+
+/**
+ * @brief per channel quant data
+ *
+ * @note number of shift and mult elements are equal to output channels
+ */
+typedef struct quant_data {
+ int32_t *shift;
+ int32_t *mult;
+} quant_data_t;
+
+/**
+ * @brief params specific to convolution 2d
+ *
+ */
+typedef struct conv_params {
+ int32_t in_offset;
+ int32_t out_offset;
+ data_2d_t stride;
+ data_2d_t padding;
+ data_2d_t dilation;
+ act_params_t activation;
+} conv_params_t;
+
+/**
+ * @brief params specific to depthwise convolution 2d
+ *
+ */
+typedef struct dw_conv_params {
+ int32_t in_offset;
+ int32_t out_offset;
+ int32_t ch_mult; // channel multiplier. (in_ch * ch_mult = out_ch)
+ data_2d_t stride;
+ data_2d_t padding;
+ data_2d_t dilation;
+ act_params_t activation;
+} dw_conv_params_t;
diff --git a/code/components/esp-nn/include/esp_nn_esp32s3.h b/code/components/esp-nn/include/esp_nn_esp32s3.h
new file mode 100644
index 00000000..0f52c943
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn_esp32s3.h
@@ -0,0 +1,231 @@
+// Copyright 2020-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.
+
+/**
+ * @file Header definitions to include for esp_nn optimized functions for
+ * the ESP32-S3 platform
+ */
+
+#pragma once
+
+#include "esp_nn_defs.h"
+#include "esp_nn_ansi_headers.h"
+
+/************************** Basic math functions *****************************/
+
+
+/**
+ * @brief elementwise addition
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ *
+ * shift values are expected to be <= 0
+ */
+void esp_nn_add_elementwise_s8_esp32s3(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ const int32_t input1_mult,
+ const int32_t input2_mult,
+ const int32_t input1_shift,
+ const int32_t input2_shift,
+ const int32_t left_shift,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size);
+
+/**
+ * @brief elementwise multiplication
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ *
+ * output shift is expected to be <= 0
+ */
+void esp_nn_mul_elementwise_s8_esp32s3(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size);
+
+
+/************************** Convolution functions *****************************/
+
+/**
+ * @brief depthwise convolution per channel
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * Version used in tflite is per channel.
+ * This version follows the same footsprints.
+ * Meaning, it has per out_channel shift and multiplier for
+ * requantization
+ *
+ * optimization notes: Though input_offset is int32 type,
+ * offset values are contained in 8 bits [-128, 127]
+ */
+void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *output_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+/**
+ * @brief 2d - convolution channelwise
+ *
+ * @note operation: result += (input + offset) * filter
+ *
+ * inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *output_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data);
+
+int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params);
+void esp_nn_set_conv_scratch_buf_esp32s3(const void *buf);
+
+int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params);
+void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(const void *buf);
+
+/************************** Pooling functions *****************************/
+
+/**
+ * @brief max_pool
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_max_pool_s8_esp32s3(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels);
+
+/**
+ * @brief avg_pool
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ */
+void esp_nn_avg_pool_s8_esp32s3(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels);
+
+
+/************************** Fully connected functions *****************************/
+
+/**
+ * @brief fully connected
+ *
+ * @note inputs type: int8_t, output: int8_t
+ * input offsets: although int32_t, they are contained in 8 bits [-128, 127]
+ *
+ * Current version works only on aligned input.
+ * row_len and channels should both be multiple of 8.
+ */
+void esp_nn_fully_connected_s8_esp32s3(const int8_t *input_data,
+ const int32_t input_offset,
+ const uint16_t row_len,
+ const int8_t *filter_data,
+ const int32_t filter_offset,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t out_shift,
+ const int32_t out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+/**
+ * @brief relu6
+ *
+ * @note inout: int8_t
+ */
+void esp_nn_relu6_s8_esp32s3(int8_t *data, uint16_t size);
+
+/********************** function defines ***************************/
+
+#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_esp32s3
+#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_esp32s3
+
+#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_esp32s3
+
+#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_esp32s3
+#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_esp32s3
+
+#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_esp32s3
+#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_esp32s3
+
+#define esp_nn_conv_s8 esp_nn_conv_s8_esp32s3
+
+#define esp_nn_relu6_s8 esp_nn_relu6_s8_esp32s3
+
+#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_esp32s3
+#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_esp32s3
+
+#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_esp32s3
+
+#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
+#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
+#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
diff --git a/code/components/esp-nn/include/esp_nn_generic_opt.h b/code/components/esp-nn/include/esp_nn_generic_opt.h
new file mode 100644
index 00000000..136cba5d
--- /dev/null
+++ b/code/components/esp-nn/include/esp_nn_generic_opt.h
@@ -0,0 +1,47 @@
+// Copyright 2020-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.
+
+/**
+ * @file Header definitions to include for esp_nn generic optimisations
+ * For functions which not having optimisations, _ansi versions are picked.
+ */
+
+#pragma once
+
+#include "esp_nn_defs.h"
+#include "esp_nn_ansi_headers.h"
+
+#define esp_nn_add_elementwise_s8 esp_nn_add_elementwise_s8_ansi
+#define esp_nn_mul_elementwise_s8 esp_nn_mul_elementwise_s8_ansi
+
+#define esp_nn_depthwise_conv_s8 esp_nn_depthwise_conv_s8_opt
+
+#define esp_nn_conv_s8 esp_nn_conv_s8_opt
+
+#define esp_nn_get_conv_scratch_size esp_nn_get_conv_scratch_size_opt
+#define esp_nn_set_conv_scratch_buf esp_nn_set_conv_scratch_buf_opt
+
+#define esp_nn_get_depthwise_conv_scratch_size esp_nn_get_depthwise_conv_scratch_size_opt
+#define esp_nn_set_depthwise_conv_scratch_buf esp_nn_set_depthwise_conv_scratch_buf_opt
+
+#define esp_nn_relu6_s8 esp_nn_relu6_s8_ansi
+
+#define esp_nn_avg_pool_s8 esp_nn_avg_pool_s8_ansi
+#define esp_nn_max_pool_s8 esp_nn_max_pool_s8_ansi
+
+#define esp_nn_fully_connected_s8 esp_nn_fully_connected_s8_ansi
+
+#define esp_nn_get_softmax_scratch_size esp_nn_get_softmax_scratch_size_opt
+#define esp_nn_set_softmax_scratch_buf esp_nn_set_softmax_scratch_buf_opt
+#define esp_nn_softmax_s8 esp_nn_softmax_s8_opt
diff --git a/code/components/esp-nn/src/activation_functions/esp_nn_relu_ansi.c b/code/components/esp-nn/src/activation_functions/esp_nn_relu_ansi.c
new file mode 100644
index 00000000..1d4c3d11
--- /dev/null
+++ b/code/components/esp-nn/src/activation_functions/esp_nn_relu_ansi.c
@@ -0,0 +1,30 @@
+// Copyright 2020-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
+#include
+
+#include
+
+void esp_nn_relu6_s8_ansi(int8_t *data, uint16_t size)
+{
+ int32_t i;
+
+ for (i = 0; i < size; i++) {
+ int32_t ip = data[i];
+
+ ip = max(ip, 0);
+ data[i] = min(ip, 6);
+ }
+}
diff --git a/code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c b/code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
new file mode 100644
index 00000000..617386cf
--- /dev/null
+++ b/code/components/esp-nn/src/basic_math/esp_nn_add_ansi.c
@@ -0,0 +1,97 @@
+// Copyright 2020-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
+
+#include
+
+void esp_nn_add_elementwise_u8_ansi(const uint8_t *input1_data,
+ const uint8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ const int32_t input1_mult,
+ const int32_t input2_mult,
+ const int32_t input1_shift,
+ const int32_t input2_shift,
+ const int32_t left_shift,
+ uint8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size)
+{
+ for (int i = 0; i < size; i++) {
+ int32_t tmp1 = input1_data[i] + input1_offset;
+ int32_t tmp2 = input2_data[i] + input2_offset;
+
+ tmp1 <<= left_shift;
+ tmp2 <<= left_shift;
+
+ tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
+ tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
+
+ tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
+ tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
+
+ int32_t out = tmp1 + tmp2;
+ out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
+ out = esp_nn_div_by_power_of_two(out, -out_shift);
+ out = out + out_offset;
+
+ out = max(activation_min, min(out, activation_max));
+ output[i] = (uint8_t) out;
+ }
+}
+
+void esp_nn_add_elementwise_s8_ansi(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ const int32_t input1_mult,
+ const int32_t input2_mult,
+ const int32_t input1_shift,
+ const int32_t input2_shift,
+ const int32_t left_shift,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size)
+{
+ for (int i = 0; i < size; i++) {
+ int32_t tmp1 = input1_data[i] + input1_offset;
+ int32_t tmp2 = input2_data[i] + input2_offset;
+
+ tmp1 <<= left_shift;
+ tmp2 <<= left_shift;
+
+ tmp1 = esp_nn_sat_round_doubling_high_mul(tmp1, input1_mult);
+ tmp2 = esp_nn_sat_round_doubling_high_mul(tmp2, input2_mult);
+
+ tmp1 = esp_nn_div_by_power_of_two(tmp1, -input1_shift);
+ tmp2 = esp_nn_div_by_power_of_two(tmp2, -input2_shift);
+
+ int32_t out = tmp1 + tmp2;
+ out = esp_nn_sat_round_doubling_high_mul(out, out_mult);
+ out = esp_nn_div_by_power_of_two(out, -out_shift);
+ out = out + out_offset;
+
+ out = max(activation_min, min(out, activation_max));
+ output[i] = (int8_t) out;
+ }
+}
diff --git a/code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c b/code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
new file mode 100644
index 00000000..db8e8cc0
--- /dev/null
+++ b/code/components/esp-nn/src/basic_math/esp_nn_mul_ansi.c
@@ -0,0 +1,42 @@
+// Copyright 2020-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
+
+#include
+
+void esp_nn_mul_elementwise_s8_ansi(const int8_t *input1_data,
+ const int8_t *input2_data,
+ const int32_t input1_offset,
+ const int32_t input2_offset,
+ int8_t *output,
+ const int32_t out_offset,
+ const int32_t out_mult,
+ const int32_t out_shift,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const int32_t size)
+{
+ for (int i = 0; i < size; i++) {
+ int32_t tmp1 = input1_data[i] + input1_offset;
+ int32_t tmp2 = input2_data[i] + input2_offset;
+
+ int32_t out = tmp1 * tmp2;
+ out = esp_nn_multiply_by_quantized_mult(out, out_mult, out_shift);
+ out = out + out_offset;
+
+ out = max(activation_min, min(out, activation_max));
+ output[i] = (int8_t) out;
+ }
+}
diff --git a/code/components/esp-nn/src/common/common_functions.h b/code/components/esp-nn/src/common/common_functions.h
new file mode 100644
index 00000000..0a74eca4
--- /dev/null
+++ b/code/components/esp-nn/src/common/common_functions.h
@@ -0,0 +1,255 @@
+// Copyright 2020-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.
+
+#pragma once
+
+#include
+#include
+#include
+
+/**
+ * c99 standard still doesn't strictly inline functions
+ * We need to use attribute as well to do this.
+ */
+#define __NN_FORCE_INLINE__ __attribute((always_inline)) static inline
+
+/* min/max macros */
+#ifndef max
+#define max(a, b) ({ \
+ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a > _b ? _a : _b; \
+})
+
+#define min(a, b) ({ \
+ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; \
+})
+#endif
+
+__NN_FORCE_INLINE__ int32_t esp_nn_clz32(uint32_t in)
+{
+#if CONFIG_IDF_TARGET_ARCH_XTENSA
+ __asm__ volatile("nsau %0, %0" : "+r" (in));
+ return in;
+#elif defined(__GNUC__)
+ return __builtin_clz(in);
+#else
+ int32_t count = 32;
+ uint32_t x = in, y = in >> 16;
+ if (y != 0) {
+ count -= 16;
+ x = y;
+ }
+ y = x >> 8;
+ if (y != 0) {
+ count -= 8;
+ x = y;
+ }
+ y = x >> 4;
+ if (y != 0) {
+ count -= 4;
+ x = y;
+ }
+ y = x >> 2;
+ if (y != 0) {
+ count -= 2;
+ x = y;
+ }
+ y = x >> 1;
+ if (y != 0) {
+ return count - 2;
+ }
+ return count - x;
+#endif
+}
+
+/**
+ * Signed saturate a 32 bit value to 8 bits keeping output in 32 bit variable.
+ */
+__NN_FORCE_INLINE__ int32_t esp_nn_saturate8(int32_t in)
+{
+#if CONFIG_IDF_TARGET_ARCH_XTENSA
+ __asm__ volatile("clamps %0, %0, 7" : "+a"(in));
+ return in;
+#else
+ return max(INT8_MIN, min(in, INT8_MAX));
+#endif
+}
+
+__NN_FORCE_INLINE__ int32_t esp_nn_pick_sat_high32_of64(int64_t val64)
+{
+ int32_t sign = (int32_t) (val64 >> 63);
+ int32_t to_add = sign & ((1ul << 31) - 1);
+ return (int32_t) ((int64_t) (val64 + to_add) >> 31);
+}
+
+__NN_FORCE_INLINE__ int32_t esp_nn_sat_round_doubling_high_mul(int32_t in0, int32_t in1)
+{
+ int32_t result;
+ int64_t in0_64 = (int64_t) in0;
+ bool overflow = (in0 == in1) && (in0 == (int32_t) INT32_MIN);
+
+ /* Nudge value */
+ int64_t nudge_val = 1 << 30;
+ if ((in0 < 0) ^ (in1 < 0)) {
+ nudge_val = 1 - nudge_val;
+ }
+
+ /* Multiply and add nudge */
+ int64_t mult = in0_64 * in1 + nudge_val;
+
+ /* Round and pickup 32 bits */
+ result = esp_nn_pick_sat_high32_of64(mult);
+
+ return overflow ? INT32_MAX : result;
+}
+
+/**
+ * fast version
+ * this will fail for values closer to INT32_MAX and INT32_MIN by `1 << (exponent - 1)`.
+ * We can afford to do this because we are at the very last stage of filter.
+ * Also it is pretty rare condition as our output is going to be 8 bit.
+ */
+__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two_fast(int32_t val, int32_t exponent)
+{
+ int32_t to_add = (1 << (exponent - 1)) - (val < 0);
+ return (int32_t) ((val + to_add) >> exponent);
+}
+
+__NN_FORCE_INLINE__ int32_t esp_nn_div_by_power_of_two(int32_t val, int32_t exponent)
+{
+ int32_t result;
+
+ const int32_t mask = (1 << exponent) - 1;
+ const int32_t remainder = val & mask;
+
+ result = val >> exponent;
+ int32_t threshold = (mask >> 1) + (result < 0);
+
+ if (remainder > threshold) {
+ result += 1;
+ }
+ return result;
+}
+
+__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult(int32_t x, int32_t mult, int32_t shift)
+{
+ int32_t left_shift = shift > 0 ? shift : 0;
+ int32_t right_shift = shift > 0 ? 0 : -shift;
+ int32_t result = esp_nn_sat_round_doubling_high_mul(x * (1 << left_shift), mult);
+ return esp_nn_div_by_power_of_two(result, right_shift);
+}
+
+__NN_FORCE_INLINE__ int32_t esp_nn_multiply_by_quantized_mult_fast(int32_t x, int32_t mult, int32_t shift)
+{
+ int32_t left_shift = max(shift, 0);
+ int32_t right_shift = left_shift - shift;
+
+ int64_t nudge_val = 1 << 30;
+ int64_t in0_64 = (int64_t) (x << left_shift);
+
+ /* Multiply and add nudge */
+ int64_t mult_64 = in0_64 * mult + nudge_val;
+ int32_t result = (int32_t) (mult_64 >> 31);
+ if (right_shift) {
+ result = esp_nn_div_by_power_of_two_fast(result, right_shift);
+ }
+ return result;
+}
+
+static void esp_nn_aligned_s8_pad_with_value(const int8_t *src, int8_t *dst,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const int32_t pad_val,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht)
+{
+ /* memset with pad_val */
+ memset(dst, pad_val, ((input_wd + 2 * pad_wd) * (input_ht + 2 * pad_ht)) * channels);
+ dst += (pad_wd + input_wd + pad_wd) * channels;
+
+ for (int i = 0; i < input_ht; i++) {
+ dst += pad_wd * channels;
+ for (int j = 0; j < input_wd * channels; j++) {
+ *dst++ = *src++;
+ }
+ dst += pad_wd * channels;
+ }
+}
+
+static void esp_nn_aligned_s8_pad_end_with_value(const int8_t *src, int8_t *dst,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const int32_t pad_val,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht)
+{
+ for (int i = 0; i < input_ht; i++) {
+ for (int j = 0; j < input_wd * channels; j++) {
+ *dst++ = *src++;
+ }
+ if (pad_wd) {
+ memset(dst, pad_val, pad_wd * channels);
+ dst += pad_wd * channels;
+ }
+ }
+ /* pad end `pad_ht` lines at end */
+ if (pad_ht) {
+ memset(dst, pad_val, (input_wd + pad_wd) * pad_ht * channels);
+ }
+}
+
+/**
+ * @brief convert 8 bit input data to 16 bit
+ *
+ * @param src int8_t source data
+ * @param dst int16_t dst data
+ * @param size length of data
+ * @param offset offset to be added to src data. Range: [-128, 127]
+ */
+__NN_FORCE_INLINE__ void esp_nn_s8_to_s16_with_offset(const int8_t *src, int16_t *dst,
+ const int size, const int32_t offset)
+{
+ int i = 0;
+ for (; i < size; i += 2) {
+ dst[i + 0] = src[i + 0] + offset;
+ dst[i + 1] = src[i + 1] + offset;
+ }
+ if(i < size) {
+ dst[i] = src[i] + offset;
+ }
+}
+
+/**
+ * @brief convert 8 bit input data to 16 bit
+ *
+ * @param src int8_t source data
+ * @param dst int16_t dst data
+ * @param size length of data
+ */
+__NN_FORCE_INLINE__ void esp_nn_s8_to_s16(const int8_t *src, int16_t *dst, const int size)
+{
+ int i = 0;
+ for (; i < size; i += 2) {
+ dst[i + 0] = src[i + 0];
+ dst[i + 1] = src[i + 1];
+ }
+ if(i < size) {
+ dst[i] = src[i];
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c b/code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
new file mode 100644
index 00000000..677c0ad8
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_conv_ansi.c
@@ -0,0 +1,179 @@
+// Copyright 2020-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
+
+#include
+
+int esp_nn_get_conv_scratch_size_ansi(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params)
+{
+ return 0;
+}
+
+void esp_nn_set_conv_scratch_buf_ansi(const void *buf)
+{
+
+}
+
+/**
+ * Assumption 1: i/p channels == o/p channels
+ * Assumption 2: Pointers are valid
+ * Assumption 3: dialation width = 1
+ */
+void esp_nn_conv_u8_ansi(const uint8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const int32_t input_offset,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint8_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t filter_offset,
+ const int32_t *bias,
+ uint8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t out_shift,
+ const int32_t out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+ for (int out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {//channel_loop
+ int32_t result = 0;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ for (int in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
+ int32_t input_index = (idx_y * input_wd + idx_x) * in_channels + in_ch_idx;
+ int32_t filter_index = ((out_ch_idx * filter_ht + filter_y_idx)
+ * filter_wd + filter_x_idx) * in_channels
+ + in_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index] + filter_offset;
+ result += input_val * filter_val;
+ }
+ }
+ }
+ if (bias) {
+ result += bias[out_ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ int out_index = (out_y * out_wd + out_x) * out_channels + out_ch_idx;
+ out_data[out_index] = (uint8_t) result;
+ }
+ }
+ }
+}
+
+/**
+ * Assumption 1: i/p channels == o/p channels
+ * Assumption 2: Pointers are valid
+ * Assumption 3: dialation width = 1
+ */
+void esp_nn_conv_s8_ansi(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t in_channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t out_channels = output_dims->channels;
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ const int32_t base_y = stride_ht * out_y - pad_ht;
+ const int32_t base_x = stride_wd * out_x - pad_wd;
+
+ const int32_t filter_y_start = max(0, -base_y);
+ const int32_t filter_x_start = max(0, -base_x);
+
+ const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
+ const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t in_row = base_y + filter_y_idx;
+ const int32_t in_col = base_x + filter_x_idx;
+ int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
+ int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
+ (filter_y_idx * filter_wd + filter_x_idx) * in_channels;
+ for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
+ conv_out +=
+ (input_data[input_base_offset + in_ch_idx] + input_offset) *
+ filter_data[filter_base_offset + in_ch_idx];
+ }
+ }
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c b/code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
new file mode 100644
index 00000000..e13129b2
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_conv_esp32s3.c
@@ -0,0 +1,463 @@
+// Copyright 2020-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
+#include
+
+#include
+
+static int16_t *scratch_buffer = NULL;
+
+extern void esp_nn_conv_s8_mult8_1x1_esp32s3(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const int32_t input_offset,
+ const int8_t *filter_aligned,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ void *buffer /* scratch buffer */);
+
+extern void esp_nn_conv_s16_mult4_1x1_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const int16_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ void *buffer /* scratch buffer */);
+
+extern void esp_nn_conv_s16_mult8_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int16_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
+ const int size, const int32_t offset);
+
+extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
+
+static void esp_nn_conv_s8_unrolled(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t in_ch = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t out_ch = output_dims->channels;
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ for (out_ch_idx = 0; out_ch_idx < out_ch; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ const int32_t base_y = stride_ht * out_y - pad_ht;
+ const int32_t base_x = stride_wd * out_x - pad_wd;
+
+ const int32_t filter_y_start = max(0, -base_y);
+ const int32_t filter_x_start = max(0, -base_x);
+
+ const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
+ const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t in_row = base_y + filter_y_idx;
+ const int32_t in_col = base_x + filter_x_idx;
+ int32_t input_base_offset = (in_row * input_wd + in_col) * in_ch;
+ int32_t filter_base_offset = out_ch_idx * in_ch * filter_ht * filter_wd +
+ (filter_y_idx * filter_wd + filter_x_idx) * in_ch;
+ for (in_ch_idx = 0; in_ch_idx < in_ch; in_ch_idx++) {
+ conv_out +=
+ (input_data[input_base_offset + in_ch_idx] + input_offset) *
+ filter_data[filter_base_offset + in_ch_idx];
+ }
+ }
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
+
+static void esp_nn_conv_s8_pad_valid(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const int32_t input_offset,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int8_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ const int32_t base_y = stride_ht * out_y;
+ const int32_t base_x = stride_wd * out_x;
+
+ for (filter_y_idx = 0; filter_y_idx < filter_ht; filter_y_idx++) {
+ for (filter_x_idx = 0; filter_x_idx < filter_wd; filter_x_idx++) {
+ const int32_t in_row = base_y + filter_y_idx;
+ const int32_t in_col = base_x + filter_x_idx;
+ int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
+ int32_t filter_base_offset = out_ch_idx * in_channels * filter_ht * filter_wd +
+ (filter_y_idx * filter_wd + filter_x_idx) * in_channels;
+ const int8_t *input_data_ptr = input_data + input_base_offset;
+ const int8_t *filter_data_ptr = filter_data + filter_base_offset;
+ for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
+ conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
+ }
+ }
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
+
+static void esp_nn_conv_s8_pad_valid_3x3(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t in_channels,
+ const int32_t input_offset,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ int32_t out_ch_idx, out_y, out_x, in_ch_idx, filter_y_idx, filter_x_idx;
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ const int32_t base_y = stride_ht * out_y;
+ const int32_t base_x = stride_wd * out_x;
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+ for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
+ for (filter_x_idx = 0; filter_x_idx < 3; filter_x_idx++) {
+ const int32_t in_row = base_y + filter_y_idx;
+ const int32_t in_col = base_x + filter_x_idx;
+ int32_t input_base_offset = (in_row * input_wd + in_col) * in_channels;
+ int32_t filter_base_offset = out_ch_idx * in_channels * 3 * 3 +
+ (filter_y_idx * 3 + filter_x_idx) * in_channels;
+ const int8_t *input_data_ptr = input_data + input_base_offset;
+ const int8_t *filter_data_ptr = filter_data + filter_base_offset;
+ for (in_ch_idx = 0; in_ch_idx < in_channels; in_ch_idx++) {
+ conv_out += (*input_data_ptr++ + input_offset) * *filter_data_ptr++;
+ }
+ }
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
+
+static void esp_nn_conv_s8_pad_valid_ch3_3x3(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const int32_t input_offset,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ int32_t out_ch_idx, out_y, out_x, filter_y_idx;
+
+ /* use scratch_buffer to pre-compute offset factor */
+ int16_t *filter_sum = (int16_t *) scratch_buffer;
+ const int8_t *filter_ptr = filter_data;
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int16_t sum_val = 0;
+ for (int i = 0; i < 9; i++) {
+ sum_val += *filter_ptr++;
+ sum_val += *filter_ptr++;
+ sum_val += *filter_ptr++;
+ }
+ *filter_sum++ = sum_val;
+ }
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ const int8_t *filter_data_ptr = filter_data;
+ const int32_t base_y = stride_ht * out_y;
+ const int32_t base_x = stride_wd * out_x;
+ const int8_t *input_base_ptr = input_data + (base_y * input_wd + base_x) * 3;
+ int16_t *filter_sum = (int16_t *) scratch_buffer;
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ for (filter_y_idx = 0; filter_y_idx < 3; filter_y_idx++) {
+ const int8_t *input_data_ptr = input_base_ptr + (filter_y_idx * input_wd) * 3;
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ conv_out += (*input_data_ptr++) * (*filter_data_ptr++);
+ }
+
+ conv_out += *filter_sum++ * input_offset;
+
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
+
+int esp_nn_get_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t in_ch = input_dims->channels;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_ch = output_dims->channels;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+
+ int filter_size = filter_wd * filter_ht * in_ch * out_ch;
+ int input_size = input_wd * input_ht * in_ch;
+
+ int transpose_buf_size = 2 * (8 * in_ch); /* to store intermediate data */
+ if (input_wd * input_ht < 8) {
+ transpose_buf_size = 0; // not using this for leftover
+ }
+ int align_buf_size = 32; /* extra buffer for alignment */
+ if (in_ch % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
+ pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
+ return filter_size + transpose_buf_size + align_buf_size;
+ }
+ return 2 * (filter_size + input_size) + transpose_buf_size + align_buf_size;
+}
+
+void esp_nn_set_conv_scratch_buf_esp32s3(void *buf)
+{
+ scratch_buffer = (int16_t *) buf;
+}
+
+void esp_nn_conv_s8_esp32s3(const data_dims_t *input_dims,
+ const int8_t *input,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t out_channels = output_dims->channels;
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int filter_size = filter_wd * filter_ht * channels * out_channels;
+ int input_size = input_wd * input_ht * channels;
+ int align_len = 16 - (filter_size & 15);
+ int16_t *filter_data16 = scratch_buffer;
+ int16_t *input_data16 = scratch_buffer + filter_size + align_len;
+
+ if (scratch_buffer == NULL) {
+ printf("esp_nn_conv error! scratch_buffer not set!\n");
+ return;
+ }
+
+ if (channels % 8 == 0 && filter_wd == 1 && filter_ht == 1 &&
+ pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
+ int8_t *filter_aligned = (int8_t *) scratch_buffer;
+ int scratch_offset = (int) (filter_aligned + filter_size);
+ void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
+ memcpy(filter_aligned, filter_data, filter_size); // copy to aligned address
+ esp_nn_conv_s8_mult8_1x1_esp32s3(
+ input, input_wd, input_ht, channels, input_offset, filter_aligned,
+ bias, out_data, out_wd, out_ht, out_channels, out_offset,
+ out_shift, out_mult, activation_min, activation_max, scratch_buf);
+ } else if (channels % 4 == 0 && filter_wd == 1 && filter_ht == 1 &&
+ (input_wd * input_ht) % 4 == 0 && /* TODO: remove this check */
+ pad_wd == 0 && pad_ht == 0 && stride_wd == 1 && stride_ht == 1) {
+ int scratch_offset = (int) (input_data16 + input_size);
+ void *scratch_buf = (void *) (scratch_offset + 16 - (scratch_offset & 15));
+ esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
+ esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
+ esp_nn_conv_s16_mult4_1x1_esp32s3(
+ input_data16, input_wd, input_ht, channels, filter_data16,
+ bias, out_data, out_wd, out_ht, out_channels, out_offset,
+ out_shift, out_mult, activation_min, activation_max, scratch_buf);
+ } else if (channels % 8 == 0) {
+ esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
+ esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input, input_data16, input_size, input_offset);
+ esp_nn_conv_s16_mult8_esp32s3(
+ input_data16, input_wd, input_ht, channels, pad_wd, pad_ht,
+ stride_wd, stride_ht, filter_data16, filter_wd, filter_ht, bias,
+ out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ } else if (pad_wd == 0 && pad_ht == 0) {
+ if (filter_wd == 3 && filter_ht == 3 && channels == 3) {
+ esp_nn_conv_s8_pad_valid_ch3_3x3(input, input_wd, input_ht, input_offset,
+ stride_wd, stride_ht, filter_data, bias,
+ out_data, out_wd, out_ht, out_channels, out_offset,
+ out_shift, out_mult, activation_min, activation_max);
+ } else {
+ esp_nn_conv_s8_pad_valid(input, input_wd, input_ht, channels, input_offset,
+ stride_wd, stride_ht, filter_data, filter_wd, filter_ht, bias,
+ out_data, out_wd, out_ht, out_channels, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ }
+ } else {
+ /* Basic unrolled version */
+ esp_nn_conv_s8_unrolled(input_dims, input, filter_dims, filter_data,
+ bias, output_dims, out_data, conv_params, quant_data);
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_conv_opt.c b/code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
new file mode 100644
index 00000000..be96430e
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_conv_opt.c
@@ -0,0 +1,179 @@
+// Copyright 2020-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
+
+#include
+
+int esp_nn_get_conv_scratch_size_opt(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const conv_params_t *conv_params)
+{
+ return 0;
+}
+
+void esp_nn_set_conv_scratch_buf_opt(const void *buf)
+{
+
+}
+
+__attribute__ ((noinline))
+static void esp_nn_conv_s8_1x1(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t in_channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t out_channels = output_dims->channels;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ for (int32_t in_row = 0; in_row < out_ht * stride_ht; in_row += stride_ht) {
+ for (int32_t in_col = 0; in_col < out_wd * stride_wd; in_col += stride_wd) {
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t *out_shift = quant_data->shift;
+ const int8_t *filter_ptr = filter_data;
+ const int8_t *input_base_ptr = input_data + (in_row * input_wd + in_col) * in_channels;
+ int32_t out_ch_idx = 0;
+ for (; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ const int8_t *input_ptr = input_base_ptr;
+
+ int32_t in_ch_idx = 0;
+ for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ }
+ for (; in_ch_idx < in_channels; in_ch_idx ++) {
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
+
+/**
+ * Assumption 1: i/p channels == o/p channels
+ * Assumption 2: Pointers are valid
+ * Assumption 3: dialation width = 1
+ */
+void esp_nn_conv_s8_opt(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+
+ if (filter_wd == 1 && filter_ht == 1) {
+ esp_nn_conv_s8_1x1(input_dims, input_data, filter_data, bias,
+ output_dims, out_data, conv_params, quant_data);
+ return;
+ }
+
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t in_channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t out_channels = output_dims->channels;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int32_t out_ch_idx, out_y, out_x, filter_y_idx, filter_x_idx;
+
+ for (out_y = 0; out_y < out_ht; out_y++) {
+ for (out_x = 0; out_x < out_wd; out_x++) {
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ for (out_ch_idx = 0; out_ch_idx < out_channels; out_ch_idx++) {
+ int32_t conv_out = 0;
+
+ const int32_t base_y = stride_ht * out_y - pad_ht;
+ const int32_t base_x = stride_wd * out_x - pad_wd;
+
+ const int32_t filter_y_start = max(0, -base_y);
+ const int32_t filter_x_start = max(0, -base_x);
+
+ const int32_t filter_y_end = min(filter_ht, input_ht - base_y);
+ const int32_t filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ for (filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t in_row = base_y + filter_y_idx;
+ const int32_t in_col = base_x + filter_x_idx;
+
+ const int8_t *input_ptr = input_data +
+ (in_row * input_wd + in_col) * in_channels;
+ const int8_t *filter_ptr = filter_data +
+ out_ch_idx * in_channels * filter_ht * filter_wd +
+ (filter_y_idx * filter_wd + filter_x_idx) * in_channels;
+ int32_t in_ch_idx = 0;
+ for (; in_ch_idx < in_channels - 3; in_ch_idx += 4) {
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ }
+ for (; in_ch_idx < in_channels; in_ch_idx ++) {
+ conv_out += (*input_ptr++ + input_offset) * *filter_ptr++;
+ }
+ }
+ }
+ if (bias) {
+ conv_out += bias[out_ch_idx];
+ }
+ conv_out = esp_nn_multiply_by_quantized_mult_fast(conv_out, *out_mult++, *out_shift++);
+ conv_out += out_offset;
+ conv_out = max(conv_out, activation_min);
+ conv_out = min(conv_out, activation_max);
+ *out_data++ = (int8_t) conv_out;
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_ansi.c b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_ansi.c
new file mode 100644
index 00000000..1cd02e0f
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_ansi.c
@@ -0,0 +1,100 @@
+// Copyright 2020-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
+#include
+
+int esp_nn_get_depthwise_conv_scratch_size_ansi(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params)
+{
+ return 0;
+}
+
+void esp_nn_set_depthwise_conv_scratch_buf_ansi(const void *buf)
+{
+
+}
+
+void esp_nn_depthwise_conv_s8_ansi(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+ const uint16_t ch_mult = conv_params->ch_mult;
+
+ int out_idx = 0;
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+ for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
+ for (int ch_mult_idx = 0; ch_mult_idx < ch_mult; ch_mult_idx++) {
+ int32_t result = 0;
+ const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index];
+ result += input_val * filter_val;
+ }
+ }
+ if (bias) {
+ result += bias[out_ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ out_data[out_idx++] = result;
+ }
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_opt.c b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_opt.c
new file mode 100644
index 00000000..4afea3f3
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_opt.c
@@ -0,0 +1,291 @@
+// Copyright 2020-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
+#include
+
+int esp_nn_get_depthwise_conv_scratch_size_opt(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params)
+{
+ return 0;
+}
+
+void esp_nn_set_depthwise_conv_scratch_buf_opt(const void *buf)
+{
+
+}
+
+/* common channel multiplier == 1 case */
+__attribute__ ((noinline))
+static void esp_nn_depthwise_conv_s8_ch_mult_1(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int out_idx = 0;
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ int ch_idx = 0;
+ for (; ch_idx < channels - 3; ch_idx += 4) {//channel_loop
+ int32_t result0 = 0;
+ int32_t result1 = 0;
+ int32_t result2 = 0;
+ int32_t result3 = 0;
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
+ int32_t input_val0 = input_data[input_index + 0] + input_offset;
+ int32_t input_val1 = input_data[input_index + 1] + input_offset;
+ int32_t input_val2 = input_data[input_index + 2] + input_offset;
+ int32_t input_val3 = input_data[input_index + 3] + input_offset;
+ int32_t filter_val0 = filter_data[filter_index + 0];
+ int32_t filter_val1 = filter_data[filter_index + 1];
+ int32_t filter_val2 = filter_data[filter_index + 2];
+ int32_t filter_val3 = filter_data[filter_index + 3];
+ result0 += input_val0 * filter_val0;
+ result1 += input_val1 * filter_val1;
+ result2 += input_val2 * filter_val2;
+ result3 += input_val3 * filter_val3;
+ }
+ }
+ if (bias) {
+ result0 += bias[ch_idx + 0];
+ result1 += bias[ch_idx + 1];
+ result2 += bias[ch_idx + 2];
+ result3 += bias[ch_idx + 3];
+ }
+ result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
+ result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
+ result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
+ result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
+
+ result0 += out_offset;
+ result1 += out_offset;
+ result2 += out_offset;
+ result3 += out_offset;
+
+ result0 = max(result0, activation_min);
+ result1 = max(result1, activation_min);
+ result2 = max(result2, activation_min);
+ result3 = max(result3, activation_min);
+
+ result0 = min(result0, activation_max);
+ result1 = min(result1, activation_max);
+ result2 = min(result2, activation_max);
+ result3 = min(result3, activation_max);
+
+ out_data[out_idx++] = result0;
+ out_data[out_idx++] = result1;
+ out_data[out_idx++] = result2;
+ out_data[out_idx++] = result3;
+ }
+ for (; ch_idx < channels; ch_idx++) {//channel_loop
+ int32_t result = 0;
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels) + ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index];
+ result += input_val * filter_val;
+ }
+ }
+ if (bias) {
+ result += bias[ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ out_data[out_idx++] = result;
+ }
+ }
+ }
+}
+
+void esp_nn_depthwise_conv_s8_opt(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t ch_mult = conv_params->ch_mult;
+ if (ch_mult == 1) {
+ esp_nn_depthwise_conv_s8_ch_mult_1(input_dims, input_data, filter_dims, filter_data,
+ bias, output_dims, out_data, conv_params, quant_data);
+ return;
+ }
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+
+ int out_idx = 0;
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
+ int ch_mult_idx = 0;
+ for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
+ int32_t result0 = 0;
+ int32_t result1 = 0;
+ int32_t result2 = 0;
+ int32_t result3 = 0;
+ const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val0 = filter_data[filter_index + 0];
+ int32_t filter_val1 = filter_data[filter_index + 1];
+ int32_t filter_val2 = filter_data[filter_index + 2];
+ int32_t filter_val3 = filter_data[filter_index + 3];
+ result0 += input_val * filter_val0;
+ result1 += input_val * filter_val1;
+ result2 += input_val * filter_val2;
+ result3 += input_val * filter_val3;
+ }
+ }
+ if (bias) {
+ result0 += bias[out_ch_idx + 0];
+ result1 += bias[out_ch_idx + 1];
+ result2 += bias[out_ch_idx + 2];
+ result3 += bias[out_ch_idx + 3];
+ }
+ result0 = esp_nn_multiply_by_quantized_mult_fast(result0, *out_mult++, *out_shift++);
+ result1 = esp_nn_multiply_by_quantized_mult_fast(result1, *out_mult++, *out_shift++);
+ result2 = esp_nn_multiply_by_quantized_mult_fast(result2, *out_mult++, *out_shift++);
+ result3 = esp_nn_multiply_by_quantized_mult_fast(result3, *out_mult++, *out_shift++);
+
+ result0 += out_offset;
+ result1 += out_offset;
+ result2 += out_offset;
+ result3 += out_offset;
+
+ result0 = max(result0, activation_min);
+ result1 = max(result1, activation_min);
+ result2 = max(result2, activation_min);
+ result3 = max(result3, activation_min);
+ result0 = min(result0, activation_max);
+ result1 = min(result1, activation_max);
+ result2 = min(result2, activation_max);
+ result3 = min(result3, activation_max);
+
+ out_data[out_idx++] = result0;
+ out_data[out_idx++] = result1;
+ out_data[out_idx++] = result2;
+ out_data[out_idx++] = result3;
+ }
+ for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
+ int32_t result = 0;
+ const int out_ch_idx = ch_idx * ch_mult + ch_mult_idx;
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index];
+ result += input_val * filter_val;
+ }
+ }
+ if (bias) {
+ result += bias[out_ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult_fast(result, *out_mult++, *out_shift++);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ out_data[out_idx++] = result;
+ }
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c
new file mode 100644
index 00000000..9167a43f
--- /dev/null
+++ b/code/components/esp-nn/src/convolution/esp_nn_depthwise_conv_s8_esp32s3.c
@@ -0,0 +1,543 @@
+// Copyright 2020-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
+#include
+
+#include
+
+static int16_t *scratch_buffer = NULL;
+
+extern void esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t ch_mult,
+ const int16_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const int32_t input_offset,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s16_mult1_3x3_no_pad_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int16_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s16_mult8_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t ch_mult,
+ const int16_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s16_mult4_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t ch_mult,
+ const int16_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int16_t *filter_data,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_depthwise_conv_s16_mult1_esp32s3(const int16_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int16_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max);
+
+extern void esp_nn_s8_to_s16_esp32s3(const int8_t *src, int16_t *dst, const int size);
+
+extern void esp_nn_aligned_s8_to_s16_with_offset_esp32s3(const int8_t *src, int16_t *dst,
+ const int size, const int32_t offset);
+
+static void esp_nn_depthwise_conv_s8_unrolled(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const int32_t input_offset,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t ch_mult,
+ const int8_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ int out_idx = 0;
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+ for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
+ int ch_mult_idx = 0;
+ for (; ch_mult_idx < ch_mult - 3; ch_mult_idx += 4) {
+ int32_t result0 = 0, result1 = 0, result2 = 0, result3 = 0;
+ const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val0 = filter_data[filter_index + 0];
+ int32_t filter_val1 = filter_data[filter_index + 1];
+ int32_t filter_val2 = filter_data[filter_index + 2];
+ int32_t filter_val3 = filter_data[filter_index + 3];
+ result0 += input_val * filter_val0;
+ result1 += input_val * filter_val1;
+ result2 += input_val * filter_val2;
+ result3 += input_val * filter_val3;
+ }
+ }
+ if (bias) {
+ result0 += bias[out_ch_idx + 0];
+ result1 += bias[out_ch_idx + 1];
+ result2 += bias[out_ch_idx + 2];
+ result3 += bias[out_ch_idx + 3];
+ }
+ result0 = esp_nn_multiply_by_quantized_mult(result0,
+ out_mult[out_ch_idx + 0], out_shift[out_ch_idx + 0]);
+ result1 = esp_nn_multiply_by_quantized_mult(result1,
+ out_mult[out_ch_idx + 1], out_shift[out_ch_idx + 1]);
+ result2 = esp_nn_multiply_by_quantized_mult(result2,
+ out_mult[out_ch_idx + 2], out_shift[out_ch_idx + 2]);
+ result3 = esp_nn_multiply_by_quantized_mult(result3,
+ out_mult[out_ch_idx + 3], out_shift[out_ch_idx + 3]);
+
+ result0 += out_offset;
+ result1 += out_offset;
+ result2 += out_offset;
+ result3 += out_offset;
+
+ result0 = max(result0, activation_min);
+ result1 = max(result1, activation_min);
+ result2 = max(result2, activation_min);
+ result3 = max(result3, activation_min);
+
+ result0 = min(result0, activation_max);
+ result1 = min(result1, activation_max);
+ result2 = min(result2, activation_max);
+ result3 = min(result3, activation_max);
+
+ out_data[out_idx++] = result0;
+ out_data[out_idx++] = result1;
+ out_data[out_idx++] = result2;
+ out_data[out_idx++] = result3;
+ }
+
+ /* left-over */
+ for (; ch_mult_idx < ch_mult; ch_mult_idx++) {
+ int32_t result = 0;
+ const int out_ch_idx = ch_mult_idx + ch_idx * ch_mult;
+
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * (channels * ch_mult) + out_ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index];
+ result += input_val * filter_val;
+ }
+ }
+ if (bias) {
+ result += bias[out_ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult(result, out_mult[out_ch_idx], out_shift[out_ch_idx]);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ out_data[out_idx++] = result;
+ }
+ }
+ }
+ }
+}
+
+void esp_nn_depthwise_conv_s8_ch_mult1(const int8_t *input_data,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ const uint16_t channels,
+ const int32_t input_offset,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const int8_t *filter_data,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_wd,
+ const uint16_t out_ht,
+ const int32_t out_offset,
+ const int32_t *out_shift,
+ const int32_t *out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ int out_idx = 0;
+ for (int out_y = 0; out_y < out_ht; out_y++) { //height loop
+ const int16_t base_y = (out_y * stride_ht) - pad_ht;
+ for (int out_x = 0; out_x < out_wd; out_x++) { //width_loop
+ const int16_t base_x = (out_x * stride_wd) - pad_wd;
+ for (int ch_idx = 0; ch_idx < channels; ch_idx++) {//channel_loop
+ int32_t result = 0;
+ /* Select filter so as the point doesn't lie outside block */
+ int filter_y_start = max(0, -base_y);
+ int filter_x_start = max(0, -base_x);
+ int filter_y_end = min(filter_ht, input_ht - base_y);
+ int filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int filter_y_idx = filter_y_start; filter_y_idx < filter_y_end; filter_y_idx++) {
+ const int32_t idx_y = base_y + filter_y_idx;
+ for (int filter_x_idx = filter_x_start; filter_x_idx < filter_x_end; filter_x_idx++) {
+ const int32_t idx_x = base_x + filter_x_idx;
+ int32_t input_index = (idx_y * input_wd + idx_x) * channels + ch_idx;
+ int32_t filter_index = (filter_y_idx * filter_wd + filter_x_idx) * channels + ch_idx;
+ int32_t input_val = input_data[input_index] + input_offset;
+ int32_t filter_val = filter_data[filter_index];
+ result += input_val * filter_val;
+ }
+ }
+ if (bias) {
+ result += bias[ch_idx];
+ }
+ result = esp_nn_multiply_by_quantized_mult(result, out_mult[ch_idx], out_shift[ch_idx]);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ out_data[out_idx++] = result;
+ }
+ }
+ }
+}
+
+int esp_nn_get_depthwise_conv_scratch_size_esp32s3(const data_dims_t *input_dims,
+ const data_dims_t *filter_dims,
+ const data_dims_t *output_dims,
+ const dw_conv_params_t *conv_params)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t ch_mult = conv_params->ch_mult;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+
+ int filter_size = filter_wd * filter_ht * channels * ch_mult;
+ int pad_width = 0, pad_height = 0;
+
+ if ((ch_mult == 1) && (channels % 8 == 0) && (filter_wd == 3) && (filter_ht == 3)) {
+ if (channels % 16 == 0) {
+ if (pad_wd || pad_ht) {
+ pad_width = pad_wd * 2;
+ pad_height = pad_ht * 2;
+ } else {
+ // check if we need to pad additionally
+ pad_width = (out_wd * stride_wd + filter_wd - 1) - input_wd;
+ pad_height = (out_ht * stride_ht + filter_ht - 1) - input_ht;
+ // printf("in(%d %d %d), out(%d %d), filter (%d %d) stride (%d %d), pad (%d %d)",
+ // input_wd, input_ht, channels, out_wd, out_ht, filter_wd, filter_ht,
+ // stride_wd, stride_ht, pad_wd, pad_ht);
+ }
+ if (pad_width || pad_height) {
+ int input_size = (input_wd + pad_width) * (input_ht + pad_height) * channels;
+ // printf("ask1 %d\n", filter_size + input_size + 16);
+ return filter_size + input_size + 16; // 16 for alignment
+ } else {
+ // printf("ask2 %d\n", filter_size + 16);
+ return filter_size + 16; // 16 for alignment
+ }
+ } else {
+ int input_size = input_wd * input_ht * channels;
+ // printf("ask3 %d\n", 2 * (filter_size + input_size) + 16);
+ return 2 * (filter_size + input_size) + 16; // 16 for alignment
+ }
+ } else if (ch_mult % 4 == 0) {
+ int input_size = input_wd * input_ht * channels;
+ // printf("ask4 %d\n", 2 * (filter_size + input_size) + 16);
+ return 2 * (filter_size + input_size) + 16; // 16 for alignment
+ }
+ return 32; // just few bytes
+}
+
+void esp_nn_set_depthwise_conv_scratch_buf_esp32s3(void *buf)
+{
+ scratch_buffer = (int16_t *) buf;
+}
+
+/**
+ * Assumption 1: i/p channels == o/p channels
+ * Assumption 2: Pointers are valid
+ * Assumption 3: dialation width = 1
+ */
+
+
+
+void esp_nn_depthwise_conv_s8_esp32s3(const data_dims_t *input_dims,
+ const int8_t *input_data,
+ const data_dims_t *filter_dims,
+ const int8_t *filter_data,
+ const int32_t *bias,
+ const data_dims_t *output_dims,
+ int8_t *out_data,
+ const dw_conv_params_t *conv_params,
+ const quant_data_t *quant_data)
+{
+ const uint16_t input_wd = input_dims->width;
+ const uint16_t input_ht = input_dims->height;
+ const uint16_t channels = input_dims->channels;
+ const int32_t input_offset = conv_params->in_offset;
+ const int32_t out_offset = conv_params->out_offset;
+ const uint16_t pad_wd = conv_params->padding.width;
+ const uint16_t pad_ht = conv_params->padding.height;
+ const uint16_t stride_wd = conv_params->stride.width;
+ const uint16_t stride_ht = conv_params->stride.height;
+ const uint16_t filter_wd = filter_dims->width;
+ const uint16_t filter_ht = filter_dims->height;
+ const uint16_t out_wd = output_dims->width;
+ const uint16_t out_ht = output_dims->height;
+ const int32_t *out_shift = quant_data->shift;
+ const int32_t *out_mult = quant_data->mult;
+ const int32_t activation_min = conv_params->activation.min;
+ const int32_t activation_max = conv_params->activation.max;
+ const uint16_t ch_mult = conv_params->ch_mult;
+
+ int filter_size = filter_wd * filter_ht * channels * ch_mult;
+ int align_len = 16 - (filter_size & 15);
+ int input_size = input_wd * input_ht * channels;
+ int16_t *filter_data16 = scratch_buffer;
+ int16_t *input_data16 = scratch_buffer + filter_size + align_len;
+ if (scratch_buffer == NULL) {
+ printf("esp_nn_depthwise_conv error! scratch_buffer not set!\n");
+ return;
+ }
+
+ if ((ch_mult == 1) && (channels % 8 == 0)) {
+ if ((filter_wd == 3) && (filter_ht == 3)) {
+ if ((channels % 16 == 0) && (pad_wd == 1) && (pad_ht == 1)) {
+ /* process in 8 bits */
+ int8_t *filter_aligned = (int8_t *) scratch_buffer;
+ int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
+ memcpy(filter_aligned, filter_data, filter_size);
+ esp_nn_aligned_s8_pad_with_value(input_data, input_padded, input_wd, input_ht, channels,
+ -input_offset, pad_wd, pad_ht);
+ esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + 2 * pad_wd,
+ input_ht + 2 * pad_ht, channels, input_offset,
+ stride_wd, stride_ht, filter_aligned, bias,
+ out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ } else if ((channels % 16 == 0) && (pad_wd == 0) && (pad_ht == 0)) {
+ /* process in 8 bits */
+ int8_t *filter_aligned = (int8_t *) scratch_buffer;
+ int8_t *input_padded = (int8_t *) scratch_buffer + filter_size + align_len;
+
+ // check if we need to pad additionally
+ int pad_right = (out_wd * stride_wd + filter_wd - 1) - input_wd;
+ int pad_bottom = (out_ht * stride_ht + filter_ht - 1) - input_ht;
+ if (pad_right || pad_bottom) { // pad right and bottom
+ esp_nn_aligned_s8_pad_end_with_value(input_data, input_padded, input_wd, input_ht,
+ channels, -input_offset, pad_right, pad_bottom);
+ } else {
+ input_padded = (int8_t *) input_data;
+ }
+ memcpy(filter_aligned, filter_data, filter_size);
+ esp_nn_depthwise_conv_s8_mult1_3x3_padded_esp32s3(input_padded, input_wd + pad_right,
+ input_ht + pad_bottom, channels, input_offset,
+ stride_wd, stride_ht, filter_aligned, bias,
+ out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ } else { /* (channels % 8) == 0 */
+ esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
+ esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
+ esp_nn_depthwise_conv_s16_mult1_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
+ pad_wd, pad_ht, stride_wd, stride_ht, filter_data16,
+ bias, out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ }
+ } else { // all other ch_mult == 1, `channels % 8 == 0`
+ esp_nn_depthwise_conv_s8_ch_mult1(input_data, input_wd, input_ht, channels, input_offset,
+ pad_wd, pad_ht, stride_wd, stride_ht,
+ filter_data, filter_wd, filter_ht,
+ bias, out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ }
+ } else if (ch_mult % 8 == 0) {
+ esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
+ esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
+ if (filter_wd == 3 && filter_ht == 3) {
+ esp_nn_depthwise_conv_s16_mult8_3x3_esp32s3(input_data16, input_wd, input_ht, channels,
+ pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
+ filter_data16, bias,
+ out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ } else {
+ esp_nn_depthwise_conv_s16_mult8_esp32s3(input_data16, input_wd, input_ht, channels,
+ pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
+ filter_data16, filter_wd, filter_ht, bias,
+ out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ }
+ } else if (ch_mult % 4 == 0) {
+ esp_nn_s8_to_s16_esp32s3(filter_data, filter_data16, filter_size);
+ esp_nn_aligned_s8_to_s16_with_offset_esp32s3(input_data, input_data16, input_size, input_offset);
+ esp_nn_depthwise_conv_s16_mult4_esp32s3(input_data16, input_wd, input_ht, channels,
+ pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
+ filter_data16, filter_wd, filter_ht, bias,
+ out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ } else {
+ esp_nn_depthwise_conv_s8_unrolled(input_data, input_wd, input_ht, channels, input_offset,
+ pad_wd, pad_ht, stride_wd, stride_ht, ch_mult,
+ filter_data, filter_wd, filter_ht,
+ bias, out_data, out_wd, out_ht, out_offset, out_shift,
+ out_mult, activation_min, activation_max);
+ }
+}
diff --git a/code/components/esp-nn/src/fully_connected/esp_nn_fully_connected_ansi.c b/code/components/esp-nn/src/fully_connected/esp_nn_fully_connected_ansi.c
new file mode 100644
index 00000000..6d800bc5
--- /dev/null
+++ b/code/components/esp-nn/src/fully_connected/esp_nn_fully_connected_ansi.c
@@ -0,0 +1,50 @@
+// Copyright 2020-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
+
+#include
+
+void esp_nn_fully_connected_s8_ansi(const int8_t *input_data,
+ const int32_t input_offset,
+ const uint16_t row_len,
+ const int8_t *filter_data,
+ const int32_t filter_offset,
+ const int32_t *bias,
+ int8_t *out_data,
+ const uint16_t out_channels,
+ const int32_t out_offset,
+ const int32_t out_shift,
+ const int32_t out_mult,
+ const int32_t activation_min,
+ const int32_t activation_max)
+{
+ for (int32_t out_c = 0; out_c < out_channels; ++out_c) {
+ int32_t result = 0;
+ for (int32_t data_idx = 0; data_idx < row_len; data_idx++) {
+ int32_t filter_index = row_len * out_c + data_idx;
+ int32_t input_val = input_data[data_idx];
+ int32_t filter_val = filter_data[filter_index];
+ result += (filter_val + filter_offset) * (input_val + input_offset);
+ }
+ if (bias) {
+ result += bias[out_c];
+ }
+ result = esp_nn_multiply_by_quantized_mult(result, out_mult, out_shift);
+ result += out_offset;
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+ out_data[out_c] = (int8_t) result;
+ }
+}
diff --git a/code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c b/code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
new file mode 100644
index 00000000..03846aa0
--- /dev/null
+++ b/code/components/esp-nn/src/pooling/esp_nn_avg_pool_ansi.c
@@ -0,0 +1,72 @@
+// Copyright 2020-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
+
+#include
+
+void esp_nn_avg_pool_s8_ansi(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels)
+{
+ int32_t base_y = -pad_ht;
+ for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
+ int32_t base_x = -pad_wd;
+ for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
+ for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
+ int32_t result = 0;
+ int32_t filter_cnt = 0;
+ /* Make sure filter does not cross the input box */
+ int32_t filter_y_start = max(0, -base_y);
+ int32_t filter_x_start = max(0, -base_x);
+
+ int32_t filter_y_end = min(filter_ht, input_ht - base_y);
+ int32_t filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
+ for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
+ int32_t in_x_idx = base_x + filter_x;
+ int32_t in_y_idx = base_y + filter_y;
+ int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
+ result += input[input_index];
+ filter_cnt++;
+ }
+ }
+
+ /* Rounded average */
+ result = result > 0 ? (result + filter_cnt / 2) / filter_cnt
+ : (result - filter_cnt / 2) / filter_cnt;
+
+ /* Activation function */
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
+ output[output_index] = (int8_t) result;
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c b/code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
new file mode 100644
index 00000000..4ca5c42d
--- /dev/null
+++ b/code/components/esp-nn/src/pooling/esp_nn_max_pool_ansi.c
@@ -0,0 +1,66 @@
+// Copyright 2020-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
+
+#include
+
+void esp_nn_max_pool_s8_ansi(const int8_t *input,
+ const uint16_t input_wd,
+ const uint16_t input_ht,
+ int8_t *output,
+ const uint16_t output_wd,
+ const uint16_t output_ht,
+ const uint16_t stride_wd,
+ const uint16_t stride_ht,
+ const uint16_t filter_wd,
+ const uint16_t filter_ht,
+ const uint16_t pad_wd,
+ const uint16_t pad_ht,
+ const int32_t activation_min,
+ const int32_t activation_max,
+ const uint16_t channels)
+{
+ int32_t base_y = -pad_ht;
+ for (int32_t out_y = 0; out_y < output_ht; out_y++, base_y += stride_ht) {
+ int32_t base_x = -pad_wd;
+ for (int32_t out_x = 0; out_x < output_wd; out_x++, base_x += stride_wd) {
+ /* Make sure filter does not cross the input box */
+ int32_t filter_y_start = max(0, -base_y);
+ int32_t filter_x_start = max(0, -base_x);
+ int32_t filter_y_end = min(filter_ht, input_ht - base_y);
+ int32_t filter_x_end = min(filter_wd, input_wd - base_x);
+
+ for (int32_t ch_idx = 0; ch_idx < channels; ch_idx++) {
+ int8_t result = INT8_MIN;
+
+ for (int32_t filter_y = filter_y_start; filter_y < filter_y_end; filter_y++) {
+ for (int32_t filter_x = filter_x_start; filter_x < filter_x_end; filter_x++) {
+ int32_t in_x_idx = base_x + filter_x;
+ int32_t in_y_idx = base_y + filter_y;
+ int32_t input_index = (in_y_idx * input_wd + in_x_idx) * channels + ch_idx;
+ result = max(input[input_index], result);
+ }
+ }
+
+ /* Activation function */
+ result = max(result, activation_min);
+ result = min(result, activation_max);
+
+ int32_t output_index = (out_y * output_wd + out_x) * channels + ch_idx;
+ output[output_index] = result;
+ }
+ }
+ }
+}
diff --git a/code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c b/code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
new file mode 100644
index 00000000..d71a8616
--- /dev/null
+++ b/code/components/esp-nn/src/softmax/esp_nn_softmax_ansi.c
@@ -0,0 +1,88 @@
+// Copyright 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 "softmax_common.h"
+
+int32_t esp_nn_get_softmax_scratch_size_ansi(const int32_t width, const int32_t height)
+{
+ (void) width;
+ (void) height;
+ return 0;
+}
+
+void esp_nn_set_softmax_scratch_buf_ansi(void *buffer)
+{
+ (void) buffer;
+ return;
+}
+
+void esp_nn_softmax_s8_ansi(const int8_t *input_data,
+ const int32_t height,
+ const int32_t width,
+ const int32_t mult,
+ const int32_t shift,
+ const int32_t diff_min,
+ int8_t *output_data)
+{
+ // The representation chosen for the input to the exp() function is Q5.26.
+ // We need to leave extra space since values that we skip might be as large as
+ // -32 before multiplying by input mult, and therefore as large as
+ // -16 afterwards. Note that exp(-8) is definitely not insignificant to
+ // accumulation, but exp(-16) definitely is.
+#define ACCUM_BITS 12
+#define DIFF_BITS 5
+
+ const int32_t mask = (1 << shift);
+ int32_t col = 0;
+ const int8_t *in_ptr = input_data;
+ int8_t *out_ptr = output_data;
+
+ for (int row_idx = 0; row_idx < height; row_idx++) {
+ int8_t max_in_row = in_ptr[0];
+ for (col = 1; col < width; col++) {
+ max_in_row = max(max_in_row, in_ptr[col]);
+ }
+
+ int32_t input_diff = 0;
+ int32_t sum_of_exps = 0;
+
+ for (col = 0; col < width; col++) {
+ input_diff = in_ptr[col] - max_in_row;
+ if (input_diff >= diff_min) {
+ const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
+ const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
+ sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
+ }
+ }
+
+ const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
+ const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
+ const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
+
+ for (col = 0; col < width; col++) {
+ input_diff = in_ptr[col] - max_in_row;
+ if (input_diff >= diff_min) {
+ const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
+ const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
+ const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
+ const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
+ out_ptr[col] = (int8_t) esp_nn_saturate8(result);
+ } else {
+ out_ptr[col] = -128;
+ }
+ }
+ in_ptr += width;
+ out_ptr += width;
+ }
+}
diff --git a/code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c b/code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
new file mode 100644
index 00000000..93337d32
--- /dev/null
+++ b/code/components/esp-nn/src/softmax/esp_nn_softmax_opt.c
@@ -0,0 +1,108 @@
+// Copyright 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 "softmax_common.h"
+#include
+
+static int32_t *scratch_buf = NULL;
+
+/**
+ * @brief Get scratch buffer size needed by softmax function
+ *
+ * @param width
+ * @param height
+ * @return size in bytes
+ *
+ * @note buffer must be 4 byte aligned
+ */
+int32_t esp_nn_get_softmax_scratch_size_opt(const int32_t width, const int32_t height)
+{
+ (void) height;
+ return width * 4;
+}
+
+/**
+ * @brief Set scratch buffer to be used by softmax function
+ *
+ * @param buffer this can be NULL if one needs to unset it
+ * must be aligned to 4 bytes
+ */
+void esp_nn_set_softmax_scratch_buf_opt(void *buffer)
+{
+ scratch_buf = (int32_t *) buffer;
+}
+
+void esp_nn_softmax_s8_opt(const int8_t *input_data,
+ const int32_t height,
+ const int32_t width,
+ const int32_t mult,
+ const int32_t shift,
+ const int32_t diff_min,
+ int8_t *output_data)
+{
+ if (scratch_buf == NULL) {
+ printf("%s error! scratch buffer not set\n", __FUNCTION__);
+ return;
+ }
+ // The representation chosen for the input to the exp() function is Q5.26.
+ // We need to leave extra space since values that we skip might be as large as
+ // -32 before multiplying by input mult, and therefore as large as
+ // -16 afterwards. Note that exp(-8) is definitely not insignificant to
+ // accumulation, but exp(-16) definitely is.
+#define ACCUM_BITS 12
+#define DIFF_BITS 5
+
+ const int32_t mask = (1 << shift);
+ int32_t col = 0;
+ const int8_t *in_ptr = input_data;
+ int8_t *out_ptr = output_data;
+
+ for (int row_idx = 0; row_idx < height; row_idx++) {
+ int8_t max_in_row = in_ptr[0];
+ for (col = 1; col < width; col++) {
+ max_in_row = max(max_in_row, in_ptr[col]);
+ }
+
+ int32_t input_diff = 0;
+ int32_t sum_of_exps = 0;
+
+ for (col = 0; col < width; col++) {
+ input_diff = in_ptr[col] - max_in_row;
+ if (input_diff >= diff_min) {
+ const int32_t input_diff_rescaled = SAT_HIGH_MUL(input_diff * mask, mult);
+ const int32_t exp_raw = esp_nn_exp_on_negative_values(input_diff_rescaled);
+ scratch_buf[col] = exp_raw; // store to avoid duplicate calculation later
+ sum_of_exps += DIV_POW2(exp_raw, ACCUM_BITS);
+ }
+ }
+
+ const int32_t headroom_plus1 = esp_nn_clz32((uint32_t) sum_of_exps);
+ const int32_t shifted_scale = ONE_OVER_ONE_X((sum_of_exps << headroom_plus1) - (1 << 31));
+ const int32_t bits_over_unit = ACCUM_BITS - headroom_plus1 + 31 - sizeof(int8_t) * 8;
+
+ for (col = 0; col < width; col++) {
+ input_diff = in_ptr[col] - max_in_row;
+ if (input_diff >= diff_min) {
+ int32_t exp_raw = scratch_buf[col];
+ const int32_t shifted_output = SAT_HIGH_MUL(shifted_scale, exp_raw);
+ const int32_t result = DIV_POW2(shifted_output, bits_over_unit) - 128;
+ out_ptr[col] = (int8_t) esp_nn_saturate8(result);
+ } else {
+ out_ptr[col] = -128;
+ }
+ }
+ in_ptr += width;
+ out_ptr += width;
+ }
+}
diff --git a/code/components/esp-nn/src/softmax/softmax_common.h b/code/components/esp-nn/src/softmax/softmax_common.h
new file mode 100644
index 00000000..254d6ace
--- /dev/null
+++ b/code/components/esp-nn/src/softmax/softmax_common.h
@@ -0,0 +1,104 @@
+// Copyright 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
+#include
+
+#define MASK_IF_ZERO(x) (x) == 0 ? ~0 : 0
+#define MASK_IF_NON_ZERO(x) (x) != 0 ? ~0 : 0
+#define SELECT_USING_MASK(mask, a, b) ((mask) & (a)) ^ (~(mask) & (b))
+#define SAT_HIGH_MUL(x, y) esp_nn_sat_round_doubling_high_mul((x), (y))
+#define DIV_POW2(x,y) esp_nn_div_by_power_of_two((x), (y))
+
+__NN_FORCE_INLINE__ int32_t mul_power_of_2(int val, int exp)
+{
+ const int32_t thresh = ((1 << (31 - exp)) - 1);
+ int32_t result = val << exp;
+ result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val > thresh), INT32_MAX, result);
+ result = SELECT_USING_MASK(MASK_IF_NON_ZERO(val < -thresh), INT32_MIN, result);
+ return result;
+}
+
+/**
+ * @brief Calculate `1 / (1 + x)` for x in [0, 1]
+ *
+ * @param val input value to calculate `1/(1+x)` for
+ * @return `int32_t` result
+ * @note Newton-Raphson division
+ *
+ * https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division
+ * Refer to that page for the logic behind the 48/17 and 32/17 constants.
+ * Pseudocode: https://en.wikipedia.org/wiki/Division_algorithm#Pseudocode
+ */
+__NN_FORCE_INLINE__ int32_t esp_nn_one_over_one_plus_x_for_x_in_0_1(int32_t val)
+{
+ const int64_t sum = (int64_t) val + INT32_MAX;
+ const int32_t half_denominator = (int32_t) ((sum + (sum >= 0 ? 1 : -1)) / 2L);
+ int32_t constant_48_over_17 = 1515870810;
+ int32_t constant_neg_32_over_17 = -1010580540;
+ int32_t x = constant_48_over_17 + SAT_HIGH_MUL(half_denominator, constant_neg_32_over_17);
+ const int32_t fixed_2_one = (1 << 29);
+
+ x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
+ x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
+ x += mul_power_of_2(SAT_HIGH_MUL(x, fixed_2_one - SAT_HIGH_MUL(half_denominator, x)), 2);
+
+ return mul_power_of_2(x, 1);
+}
+
+#define ONE_OVER_ONE_X(x) esp_nn_one_over_one_plus_x_for_x_in_0_1((x))
+
+/**
+ * @brief Return exp(x) for x < 0.
+ *
+ */
+__NN_FORCE_INLINE__ int32_t esp_nn_exp_on_negative_values(int32_t val)
+{
+ int32_t shift = 24;
+
+ const int32_t one_quarter = (1 << shift);
+ int32_t mask = one_quarter - 1;
+ const int32_t val_mod_minus_quarter = (val & mask) - one_quarter;
+ const int32_t remainder = val_mod_minus_quarter - val;
+
+ // calculate exponent for x in [-1/4, 0) in `result`
+ const int32_t x = (val_mod_minus_quarter << 5) + (1 << 28);
+ const int32_t x2 = SAT_HIGH_MUL(x, x);
+ const int32_t x3 = SAT_HIGH_MUL(x2, x);
+ const int32_t x4 = SAT_HIGH_MUL(x2, x2);
+ const int32_t one_over_3 = 715827883;
+ const int32_t one_over_8 = 1895147668;
+
+ const int32_t x4_over_4 = DIV_POW2(x4, 2);
+ const int32_t x4_over_4_plus_x3_over_6_plus_x2_over_2 = DIV_POW2(SAT_HIGH_MUL(x4_over_4 + x3, one_over_3) + x2, 1);
+ int32_t result = one_over_8 + SAT_HIGH_MUL(one_over_8, x + x4_over_4_plus_x3_over_6_plus_x2_over_2);
+
+#define SELECT_IF_NON_ZERO(x) { \
+ mask = MASK_IF_NON_ZERO(remainder & (1 << shift++)); \
+ result = SELECT_USING_MASK(mask, SAT_HIGH_MUL(result, x), result); \
+}
+
+ SELECT_IF_NON_ZERO(1672461947)
+ SELECT_IF_NON_ZERO(1302514674)
+ SELECT_IF_NON_ZERO(790015084)
+ SELECT_IF_NON_ZERO(290630308)
+ SELECT_IF_NON_ZERO(39332535)
+ SELECT_IF_NON_ZERO(720401)
+ SELECT_IF_NON_ZERO(242)
+
+#undef SELECT_IF_NON_ZERO
+
+ mask = MASK_IF_ZERO(val);
+ return SELECT_USING_MASK(mask, INT32_MAX, result);
+}
\ No newline at end of file
diff --git a/code/components/esp-nn/test_app/CMakeLists.txt b/code/components/esp-nn/test_app/CMakeLists.txt
new file mode 100644
index 00000000..8d332768
--- /dev/null
+++ b/code/components/esp-nn/test_app/CMakeLists.txt
@@ -0,0 +1,9 @@
+# The following lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+set(EXTRA_COMPONENT_DIRS "../" "../tests/")
+set(IDF_EXCLUDE_COMPONENTS test test_app)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(test_app)
diff --git a/code/components/esp-nn/test_app/main/CMakeLists.txt b/code/components/esp-nn/test_app/main/CMakeLists.txt
new file mode 100644
index 00000000..04161254
--- /dev/null
+++ b/code/components/esp-nn/test_app/main/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+set(COMPONENT_SRCS "main.c")
+set(COMPONENT_ADD_INCLUDEDIRS "")
+
+set(COMPONENT_PRIV_REQUIRES tests)
+
+register_component()
diff --git a/code/components/esp-nn/test_app/main/component.mk b/code/components/esp-nn/test_app/main/component.mk
new file mode 100644
index 00000000..5d85ad38
--- /dev/null
+++ b/code/components/esp-nn/test_app/main/component.mk
@@ -0,0 +1,8 @@
+#
+# Main component makefile.
+#
+# This Makefile can be left empty. By default, it will take the sources in the
+# src/ directory, compile them and link them into lib(subdirectory_name).a
+# in the build directory. This behaviour is entirely configurable,
+# please read the ESP-IDF documents if you need to do this.
+#
diff --git a/code/components/esp-nn/test_app/main/main.c b/code/components/esp-nn/test_app/main/main.c
new file mode 100644
index 00000000..267e35f2
--- /dev/null
+++ b/code/components/esp-nn/test_app/main/main.c
@@ -0,0 +1,87 @@
+// Copyright 2020-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
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+static const char *TAG = "test_app";
+static uint32_t start_c, start_opt, total_c, total_opt;
+
+void profile_c_start()
+{
+ /* initiate profiling */
+ start_c = esp_cpu_get_ccount();
+}
+
+void profile_c_end()
+{
+ /* record profile number */
+ total_c = esp_cpu_get_ccount() - start_c;
+}
+
+void profile_opt_start()
+{
+ /* initiate profiling */
+ start_opt = esp_cpu_get_ccount();
+}
+
+void profile_opt_end()
+{
+ /* record profile number */
+ total_opt = esp_cpu_get_ccount() - start_opt;
+}
+
+void app_main()
+{
+ /* s8 tests */
+ ESP_LOGI(TAG, "Running s8 tests...");
+ esp_nn_add_elementwise_s8_test();
+ printf("add, c %u opt %u\n", total_c, total_opt);
+ esp_nn_mul_elementwise_s8_test();
+ printf("mul, c %u opt %u\n", total_c, total_opt);
+ esp_nn_depthwise_conv_s8_test();
+ printf("depthwise, c %u opt %u\n", total_c, total_opt);
+ esp_nn_conv_s8_test();
+ printf("conv2d, c %u opt %u\n", total_c, total_opt);
+
+ esp_nn_relu6_s8_test();
+ printf("relu, c %u opt %u\n", total_c, total_opt);
+ esp_nn_avg_pool_s8_test();
+ printf("avg_pool, c %u opt %u\n", total_c, total_opt);
+ esp_nn_max_pool_s8_test();
+ printf("max_pool, c %u opt %u\n", total_c, total_opt);
+ esp_nn_fully_connected_s8_test();
+ printf("fully_connected, c %u opt %u\n", total_c, total_opt);
+ esp_nn_softmax_s8_test();
+ printf("softmax, c %u opt %u\n", total_c, total_opt);
+ ESP_LOGI(TAG, "s8 tests done!\n");
+
+ /* u8 tests */
+ //ESP_LOGI(TAG, "Running u8 tests...");
+ //esp_nn_add_elementwise_u8_test();
+ //esp_nn_depthwise_conv_u8_test();
+ //esp_nn_conv_u8_test();
+ //esp_nn_avg_pool_u8_test();
+ //esp_nn_max_pool_u8_test();
+ //esp_nn_fully_connected_u8_test();
+ //ESP_LOGI(TAG, "u8 tests done!\n");
+}
diff --git a/code/components/esp-nn/test_app/sdkconfig.defaults b/code/components/esp-nn/test_app/sdkconfig.defaults
new file mode 100644
index 00000000..bb37aac5
--- /dev/null
+++ b/code/components/esp-nn/test_app/sdkconfig.defaults
@@ -0,0 +1,5 @@
+
+#
+# esp-nn
+#
+CONFIG_NN_ESP32=y
diff --git a/code/components/esp-nn/test_app/sdkconfig.defaults.esp32s3 b/code/components/esp-nn/test_app/sdkconfig.defaults.esp32s3
new file mode 100644
index 00000000..1adc4b01
--- /dev/null
+++ b/code/components/esp-nn/test_app/sdkconfig.defaults.esp32s3
@@ -0,0 +1,8 @@
+# Default configurations for ESP32-S3
+
+CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
+CONFIG_ESP32S3_SPIRAM_SUPPORT=y
+
+CONFIG_ESP32S3_DATA_CACHE_64KB=y
+CONFIG_ESP32S3_DATA_CACHE_8WAYS=y
+CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
diff --git a/code/components/esp-nn/tests/CMakeLists.txt b/code/components/esp-nn/tests/CMakeLists.txt
new file mode 100644
index 00000000..97ec946f
--- /dev/null
+++ b/code/components/esp-nn/tests/CMakeLists.txt
@@ -0,0 +1,15 @@
+
+set(COMPONENT_ADD_INCLUDEDIRS ./include/)
+set(COMPONENT_SRCS "src/basic_math_test.c"
+ "src/convolution_test.c"
+ "src/fully_connected_test.c"
+ "src/pooling_test.c"
+ "src/relu_test.c"
+ "src/softmax_test.c")
+
+set(COMPONENT_REQUIRES )
+set(COMPONENT_PRIV_REQUIRES esp-nn)
+
+register_component()
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-function)
diff --git a/code/components/esp-nn/tests/README.md b/code/components/esp-nn/tests/README.md
new file mode 100644
index 00000000..41c94235
--- /dev/null
+++ b/code/components/esp-nn/tests/README.md
@@ -0,0 +1,4 @@
+# Tests for esp_nn library
+
+- Include these in your test framework and run the framework.
+- For IDF test please refer `test_app`
diff --git a/code/components/esp-nn/tests/component.mk b/code/components/esp-nn/tests/component.mk
new file mode 100644
index 00000000..2860f3ff
--- /dev/null
+++ b/code/components/esp-nn/tests/component.mk
@@ -0,0 +1,5 @@
+#FIXME
+
+COMPONENT_ADD_INCLUDEDIRS := include/
+
+COMPONENT_SRCDIRS := src/
diff --git a/code/components/esp-nn/tests/include/test_functions.h b/code/components/esp-nn/tests/include/test_functions.h
new file mode 100644
index 00000000..3e882efa
--- /dev/null
+++ b/code/components/esp-nn/tests/include/test_functions.h
@@ -0,0 +1,48 @@
+// Copyright 2020-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.
+
+
+/* int8_t ops tests */
+void esp_nn_add_elementwise_s8_test();
+void esp_nn_mul_elementwise_s8_test();
+
+void esp_nn_depthwise_conv_s8_test();
+void esp_nn_conv_s8_test();
+
+void esp_nn_avg_pool_s8_test();
+void esp_nn_max_pool_s8_test();
+
+void esp_nn_fully_connected_s8_test();
+
+void esp_nn_relu6_s8_test();
+
+void esp_nn_softmax_s8_test();
+
+/* uint8_t ops tests */
+void esp_nn_add_elementwise_u8_test();
+
+void esp_nn_depthwise_conv_u8_test();
+void esp_nn_conv_u8_test();
+
+void esp_nn_avg_pool_u8_test();
+void esp_nn_max_pool_u8_test();
+
+void esp_nn_fully_connected_u8_test();
+
+/* instructions test functions */
+void compare_instructions_test();
+void arith_instructions_test();
+void min_max_instructions_test();
+void bitwise_instructions_test();
+void load_store_instructions_test();
diff --git a/code/components/esp-nn/tests/include/test_utils.h b/code/components/esp-nn/tests/include/test_utils.h
new file mode 100644
index 00000000..a152549b
--- /dev/null
+++ b/code/components/esp-nn/tests/include/test_utils.h
@@ -0,0 +1,87 @@
+// Copyright 2020-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
+#include
+#include
+#include
+
+/* mult value range */
+#define MULT_MAX INT32_MAX
+#define MULT_MIN 0
+
+/* shift value range */
+#define SHIFT_MIN -31
+#define SHIFT_MAX 30
+
+/**
+ * @brief callback function to run before C function
+ */
+void profile_c_start();
+
+/**
+ * @brief callback function to run after C function
+ */
+void profile_c_end();
+
+/**
+ * @brief callback function to run before optimized function
+ */
+void profile_opt_start();
+
+/**
+ * @brief callback function to run after optimized function
+ */
+void profile_opt_end();
+
+#define ANSI_COLOR_RED "\x1b[31m"
+#define ANSI_COLOR_GREEN "\x1b[32m"
+#define ANSI_COLOR_YELLOW "\x1b[33m"
+#define ANSI_COLOR_BLUE "\x1b[34m"
+#define ANSI_COLOR_MAGENTA "\x1b[35m"
+#define ANSI_COLOR_CYAN "\x1b[36m"
+#define ANSI_COLOR_RESET "\x1b[0m"
+
+#define CHECK_EQUAL(ARRAY1, ARRAY2, size) ({ \
+ bool res = true; \
+ for (int _i = 0; _i < size; _i++) { \
+ if (ARRAY1[_i] != ARRAY2[_i]) { \
+ res = false; \
+ break; \
+ } \
+ } \
+ res; \
+})
+
+#define PRINT_ARRAY_INT(ARRAY, width, height) ({ \
+ int *_array = (int *) ARRAY; \
+ for (int _j = 0; _j < height; _j++) { \
+ for (int _i = 0; _i < width; _i++) { \
+ printf("%d\t", _array[width * _j + _i]); \
+ } \
+ printf("\n"); \
+ } \
+ printf("\n"); \
+})
+
+#define PRINT_ARRAY_HEX(ARRAY, width, height) ({ \
+ uint8_t *_array = (uint8_t *) ARRAY; \
+ for (int _j = 0; _j < height; _j++) { \
+ for (int _i = 0; _i < width; _i++) { \
+ printf("%02x\t", _array[width * _j + _i]); \
+ } \
+ printf("\n"); \
+ } \
+ printf("\n"); \
+})
diff --git a/code/components/esp-nn/tests/src/basic_math_test.c b/code/components/esp-nn/tests/src/basic_math_test.c
new file mode 100644
index 00000000..715d7c78
--- /dev/null
+++ b/code/components/esp-nn/tests/src/basic_math_test.c
@@ -0,0 +1,355 @@
+// Copyright 2020-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
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "test_utils.h"
+
+#if CONFIG_IDF_CMAKE
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
+#define IDF_HEAP_CAPS 1
+#endif
+
+#if IDF_HEAP_CAPS
+#include "esp_heap_caps.h"
+#endif
+#endif
+
+void esp_nn_add_elementwise_s8_test()
+{
+ /* prepare data */
+ const int size = 1600 + 8 + 7; /* odd len to test leftover */
+ int8_t *input1;
+ int8_t *input2;
+ int8_t *out_data_c;
+ int8_t *out_data_opt;
+ int8_t *input1_orig = NULL;
+ int8_t *input2_orig = NULL;
+ int8_t *out_c_orig = NULL;
+ int8_t *out_opt_orig = NULL;
+ int32_t input1_offset = 34;
+ int32_t input2_offset = 35;
+ int32_t output_offset = 36;
+ int32_t input1_shift = -8; // right_shift amt always <= 0
+ int32_t input2_shift = -8; // right_shift amt always <= 0
+ int32_t output_shift = -9; // right_shift amt always <= 0
+ int32_t left_shift = 15; // always +ve
+ int32_t input1_mult = INT32_MAX;
+ int32_t input2_mult = INT32_MAX;
+ int32_t output_mult = INT32_MAX;
+ int32_t activation_min = -128;
+ int32_t activation_max = 127;
+
+ for (int itr = 0; itr < 10; itr++) {
+ switch (itr) {
+ case 0: // all zeros
+ input1_offset = 0;
+ input2_offset = 0;
+ output_offset = 0;
+ input1_mult = 0;
+ input2_mult = 0;
+ output_mult = 0;
+ input1_shift = 0;
+ input2_shift = 0;
+ output_shift = 0;
+ left_shift = 0;
+ break;
+ case 1: // hit min
+ input1_offset = -127;
+ input2_offset = -127;
+ output_offset = -128;
+ input1_mult = MULT_MIN;
+ input2_mult = MULT_MIN;
+ output_mult = MULT_MIN;
+ input1_shift = 0;
+ input2_shift = 0;
+ output_shift = 0;
+ left_shift = 0;
+ break;
+ case 2: // hit max
+ input1_offset = 128;
+ input2_offset = 128;
+ output_offset = -127;
+ input1_mult = MULT_MAX;
+ input2_mult = MULT_MAX;
+ output_mult = MULT_MAX;
+ input1_shift = SHIFT_MIN;
+ input2_shift = SHIFT_MIN;
+ output_shift = SHIFT_MIN;
+ left_shift = 30 - 8; // since input is 8 bits
+ break;
+ case 3: // hit extreme max
+ input1_offset = 128;
+ input2_offset = 128;
+ output_offset = -127;
+ input1_mult = MULT_MAX;
+ input2_mult = MULT_MAX;
+ output_mult = MULT_MAX;
+ input1_shift = 0;
+ input2_shift = 0;
+ output_shift = 0;
+ left_shift = 30 - 8; // -8 since input is 8 bit
+ break;
+ default: // practical random input
+ input1_offset = rand() % 256 - 127; // range [-127, 128]
+ input2_offset = rand() % 256 - 127; // range [-127, 128]
+ output_offset = rand() % 256 - 128; // range [-128, 127]
+ input1_mult = MULT_MAX / 2 + rand() % INT16_MAX;
+ input2_mult = MULT_MAX / 2 + rand() % INT16_MAX;
+ output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
+ input1_shift = -8 + rand() % 4;
+ input2_shift = -8 + rand() % 4;
+ output_shift = -8 + rand() % 4;
+ left_shift = rand() % 15;
+ }
+#if IDF_HEAP_CAPS
+ input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+
+ input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
+ input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
+ out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
+ out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
+#else
+ input1 = memalign(16, size);
+ input2 = memalign(16, size);
+ out_data_c = memalign(16, size);
+ out_data_opt = memalign(16, size);
+
+ input1_orig = input1;
+ input2_orig = input2;
+ out_c_orig = out_data_c;
+ out_opt_orig = out_data_opt;
+#endif
+ if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
+ out_opt_orig == NULL) {
+ printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto elementwise_add_test_cleanup;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ input1[i] = rand() % 256 - 128;
+ input2[i] = rand() % 256 - 128;
+ }
+
+ if (itr == 0) {
+ /* enable profiler */
+ profile_c_start();
+ }
+ /* C function */
+ esp_nn_add_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
+ input1_mult, input2_mult, input1_shift, input2_shift,
+ left_shift, out_data_c, output_offset, output_mult,
+ output_shift, activation_min, activation_max, size);
+
+ if (itr == 0) {
+ profile_c_end();
+ profile_opt_start();
+ }
+
+ /* Optimized function */
+ esp_nn_add_elementwise_s8(input1, input2, input1_offset, input2_offset,
+ input1_mult, input2_mult, input1_shift, input2_shift,
+ left_shift, out_data_opt, output_offset, output_mult,
+ output_shift, activation_min, activation_max, size);
+ if (itr == 0) {
+ /* disable profiler */
+ profile_opt_end();
+ }
+
+ bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(out_data_opt, size, 1);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(out_data_c, size, 1);
+ printf("Input1:\n");
+ PRINT_ARRAY_HEX(input1, size, 1);
+ printf("Input2:\n");
+ PRINT_ARRAY_HEX(input2, size, 1);
+ printf("in1_shift %d, in2_shift %d, left_shift %d, out_shift %d\n",
+ input1_shift, input2_shift, left_shift, output_shift);
+ printf("in1_mult %d, in2_mult %d, out_mult %d\n", input1_mult, input2_mult, output_mult);
+ goto elementwise_add_test_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+
+elementwise_add_test_cleanup:
+ if (input1_orig) {
+ free(input1_orig);
+ }
+ if (input2_orig) {
+ free(input2_orig);
+ }
+ if (out_c_orig) {
+ free(out_c_orig);
+ }
+ if (out_opt_orig) {
+ free(out_opt_orig);
+ }
+ }
+}
+
+void esp_nn_mul_elementwise_s8_test()
+{
+ /* prepare data */
+ const int size = 1600 + 8 + 7; /* odd len to test leftover */
+ int8_t *input1;
+ int8_t *input2;
+ int8_t *out_data_c;
+ int8_t *out_data_opt;
+ int32_t input1_offset = 34;
+ int32_t input2_offset = 35;
+ int32_t output_offset = 36;
+ int32_t output_shift = -7;
+ int32_t output_mult = MULT_MAX; // max out_mult
+ int32_t activation_min = -128;
+ int32_t activation_max = 127;
+ int8_t *input1_orig = NULL;
+ int8_t *input2_orig = NULL;
+ int8_t *out_c_orig = NULL;
+ int8_t *out_opt_orig = NULL;
+
+ for (int itr = 0; itr < 10; itr++) {
+ switch (itr) {
+ case 0: // all zeros
+ input1_offset = 0;
+ input2_offset = 0;
+ output_offset = 0;
+ output_mult = 0;
+ output_shift = 0;
+ break;
+ case 1: // hit min
+ input1_offset = -127;
+ input2_offset = -127;
+ output_offset = -128;
+ output_mult = MULT_MIN;
+ output_shift = 0;
+ break;
+ case 2: // hit max
+ input1_offset = 128;
+ input2_offset = 128;
+ output_offset = -127;
+ output_mult = MULT_MAX;
+ output_shift = SHIFT_MIN;
+ break;
+ case 3: // hit extreme max
+ input1_offset = 128;
+ input2_offset = 128;
+ output_offset = -127;
+ output_mult = MULT_MAX;
+ output_shift = 0;
+ break;
+ default: // practical random input
+ input1_offset = rand() % 256 - 127; // range [-127, 128]
+ input2_offset = rand() % 256 - 127; // range [-127, 128]
+ output_offset = rand() % 256 - 128; // range [-128, 127]
+ output_mult = MULT_MAX / 2 + rand() % INT16_MAX;
+ output_shift = -8 + rand() % 4;
+ }
+
+#if IDF_HEAP_CAPS
+ input1_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ input2_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_c_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_opt_orig = (int8_t *) heap_caps_malloc(size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+
+ input1 = 16 + input1_orig - ((uint32_t) input1_orig & 0xf);
+ input2 = 16 + input2_orig - ((uint32_t) input2_orig & 0xf);
+ out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
+ out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
+#else
+ input1 = memalign(16, size);
+ input2 = memalign(16, size);
+ out_data_c = memalign(16, size);
+ out_data_opt = memalign(16, size);
+
+ input1_orig = input1;
+ input2_orig = input2;
+ out_c_orig = out_data_c;
+ out_opt_orig = out_data_opt;
+#endif
+ if (input1_orig == NULL || input2_orig == NULL || out_c_orig == NULL ||
+ out_opt_orig == NULL) {
+ printf(ANSI_COLOR_RED"%s error allocating buffers\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto elementwise_mult_test_cleanup;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ input1[i] = rand() % 256 - 128;
+ input2[i] = rand() % 256 - 128;
+ }
+
+ if (itr == 0) {
+ /* enable profiler */
+ profile_c_start();
+ }
+ /* C function */
+ esp_nn_mul_elementwise_s8_ansi(input1, input2, input1_offset, input2_offset,
+ out_data_c, output_offset, output_mult, output_shift,
+ activation_min, activation_max, size);
+
+ if (itr == 0) {
+ profile_c_end();
+ profile_opt_start();
+ }
+ /* Optimized function */
+ esp_nn_mul_elementwise_s8(input1, input2, input1_offset, input2_offset,
+ out_data_opt, output_offset, output_mult, output_shift,
+ activation_min, activation_max, size);
+
+ if (itr == 0) {
+ /* disable profiler */
+ profile_opt_end();
+ }
+
+ bool ret = CHECK_EQUAL(out_data_c, out_data_opt, size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(out_data_opt, size, 1);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(out_data_c, size, 1);
+ printf("Input1:\n");
+ PRINT_ARRAY_HEX(input1, size, 1);
+ printf("Input2:\n");
+ PRINT_ARRAY_HEX(input2, size, 1);
+ goto elementwise_mult_test_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+
+elementwise_mult_test_cleanup:
+ if (input1_orig) {
+ free(input1_orig);
+ }
+ if (input2_orig) {
+ free(input2_orig);
+ }
+ if (out_c_orig) {
+ free(out_c_orig);
+ }
+ if (out_opt_orig) {
+ free(out_opt_orig);
+ }
+ }
+}
diff --git a/code/components/esp-nn/tests/src/convolution_test.c b/code/components/esp-nn/tests/src/convolution_test.c
new file mode 100644
index 00000000..c86bdbab
--- /dev/null
+++ b/code/components/esp-nn/tests/src/convolution_test.c
@@ -0,0 +1,605 @@
+// Copyright 2020-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
+#include
+#include
+#include
+#include
+
+#include
+#include "test_utils.h"
+
+#if CONFIG_IDF_CMAKE
+#if (CONFIG_SPIRAM_SUPPORT && (CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC))
+#define IDF_HEAP_CAPS 1
+#endif
+#if IDF_HEAP_CAPS
+#include "esp_heap_caps.h"
+#endif
+#endif
+
+void esp_nn_depthwise_conv_s8_test()
+{
+ int8_t *input = NULL, *filter_data = NULL, *out_data_c = NULL, *out_data_opt = NULL;
+ int32_t *bias = NULL;
+ int32_t input_offset = 5; /* some number in [-128, 127] */
+ int32_t out_offset = 7;
+ int32_t activation_min = -125;
+ int32_t activation_max = 120;
+ void *scratch_buf = NULL;
+
+ /* independent variables */
+ int input_wd, input_ht, channels;
+ uint16_t filter_ht, filter_wd, ch_mult;
+ uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
+
+ // run for 15 iterations
+ for (int itr = 0; itr < 15; itr++) {
+ /* prepare data */
+ switch (itr) {
+ case 0: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0)
+ input_wd = 18;
+ input_ht = 18;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 16;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 1: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (1,1)
+ input_wd = 10;
+ input_ht = 10;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 16;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 2: // (ch_mult 1, (channels % 8) = 0), filter (3,3), pad (1,1)
+ input_wd = 10;
+ input_ht = 10;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 24;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 3: // other filter sizes (ch_mult 1, (channels % 8) = 0)
+ input_wd = 10;
+ input_ht = 10;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 24;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 4: // other filter sizes (ch_mult 8 = 0)
+ input_wd = 6;
+ input_ht = 6;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 8;
+ channels = 4;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 5: // other filter sizes (ch_mult 8 = 0)
+ input_wd = 12;
+ input_ht = 12;
+ filter_ht = 5;
+ filter_wd = 5;
+ ch_mult = 8;
+ channels = 4;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 6: // other filter sizes (ch_mult 4 = 0)
+ input_wd = 6;
+ input_ht = 6;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 4;
+ channels = 4;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 7: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
+ input_wd = 6;
+ input_ht = 6;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 16;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 2;
+ stride_ht = 2;
+ break;
+ case 8: // same as case 7, with large parameters
+ input_wd = 58;
+ input_ht = 58;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 128;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 2;
+ stride_ht = 2;
+ break;
+ case 9: // (ch_mult 1, (channels % 16) = 0), filter (3,3), pad (0,0) stride (2,2)
+ input_wd = 6;
+ input_ht = 6;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 16;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 2;
+ stride_ht = 2;
+ break;
+ default:
+ input_wd = 6;
+ input_ht = 6;
+ filter_ht = 3;
+ filter_wd = 3;
+ ch_mult = 1;
+ channels = 16;
+ stride_wd = rand() % 2 + 1;
+ stride_ht = stride_wd;
+ pad_wd = stride_wd == 1 ? 0 : rand() % 2;
+ pad_ht = pad_wd;
+ printf("stride(%d), pad (%d)\t", stride_wd, pad_wd);
+ break;
+ }
+
+ uint16_t out_wd = (input_wd - filter_wd + 1) / stride_wd;
+ uint16_t out_ht = (input_ht - filter_ht + 1) / stride_ht;
+ if (itr == 9) {
+ // expect the function to handle this gracefully
+ out_wd += 1;
+ out_ht += 1;
+ }
+ int in_size = input_wd * input_ht * channels;
+ int out_size = out_wd * out_ht * channels * ch_mult;
+ int filter_size = filter_wd * filter_ht * channels * ch_mult + 4;
+ int bias_size = channels * ch_mult + 1;
+ int32_t out_shift[channels * ch_mult];
+ int32_t out_mult[channels * ch_mult];
+
+#if IDF_HEAP_CAPS
+ int8_t *input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ int8_t *out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ int8_t *out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ filter_data = (int8_t *) heap_caps_malloc(filter_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ bias = (int32_t *) heap_caps_malloc(bias_size * 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+
+ input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
+ out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
+ out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
+#else
+ input = memalign(16, in_size + 16);
+ filter_data = memalign(16, filter_size);
+ out_data_c = memalign(16, out_size + 16);
+ out_data_opt = memalign(16, out_size + 16);
+ bias = memalign(16, bias_size * 4);
+ int8_t *input_orig = input;
+ int8_t *out_c_orig = out_data_c;
+ int8_t *out_opt_orig = out_data_opt;
+#endif
+ if (bias == NULL || input == NULL || filter_data == NULL ||
+ out_data_c == NULL || out_data_opt == NULL || bias == NULL) {
+ printf(ANSI_COLOR_RED"%s[%d] allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ goto dc_s8_cleanup;
+ }
+
+ /* Generate input data */
+ for (int i = 0; i < in_size; ++i) {
+ input[i] = rand() % 128;
+ }
+
+ /* Generate filter data */
+ for (int i = 0; i < filter_size; ++i) {
+ filter_data[i] = rand() % 256 - 128;
+ }
+
+ /* Generate bias data */
+ for (int i = 0; i < channels * ch_mult; ++i) {
+ bias[i + 1] = rand() % INT16_MAX; //0th index left for unalignment
+ out_shift[i] = -8 + rand() % 3;
+ out_mult[i] = 0x7eb0e200 + rand() % 50;
+ }
+
+ data_dims_t input_dims = {.width = input_wd, .height = input_ht, .channels = channels, 1};
+ data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = channels * ch_mult, 1};
+ data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
+ dw_conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset, .ch_mult = ch_mult,
+ .stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
+ .dilation = {0, 0}, .activation = {activation_min, activation_max}};
+ quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
+
+ int scratch_buf_size = esp_nn_get_depthwise_conv_scratch_size(&input_dims, &filter_dims,
+ &output_dims, &conv_params);
+ if (scratch_buf_size > 0) {
+#if IDF_HEAP_CAPS
+ scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
+#else
+ scratch_buf = memalign(16, scratch_buf_size);
+ int align_sz = 0;
+#endif
+ if (scratch_buf == NULL) {
+ printf(ANSI_COLOR_RED"%s[%d] scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET,
+ __FUNCTION__, itr, scratch_buf_size);
+ goto dc_s8_cleanup;
+ }
+ esp_nn_set_depthwise_conv_scratch_buf(scratch_buf + align_sz);
+ }
+ if (itr == 0) {
+ /* enable profiler */
+ profile_c_start();
+ }
+
+ /* C function */
+ esp_nn_depthwise_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 4,
+ bias + 1, &output_dims, out_data_c, &conv_params, &quant_data);
+
+ if (itr == 0) {
+ profile_c_end();
+ profile_opt_start();
+ }
+
+ /* Optimized function */
+ esp_nn_depthwise_conv_s8(&input_dims, input, &filter_dims, filter_data + 4,
+ bias + 1, &output_dims, out_data_opt, &conv_params, &quant_data);
+
+ if (itr == 0) {
+ /* disable profiler */
+ profile_opt_end();
+ }
+
+ bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, in_size / input_ht, input_ht);
+ printf("Filter data:\n");
+ PRINT_ARRAY_HEX(filter_data + 4, (filter_size - 4) / filter_ht, filter_ht);
+ printf("bias data:\n");
+ PRINT_ARRAY_INT(bias + 1, ch_mult * channels, 1);
+ goto dc_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+
+ dc_s8_cleanup:
+ if (input) {
+ free(input_orig);
+ }
+ if (filter_data) {
+ free(filter_data);
+ }
+ if (out_data_c) {
+ free(out_c_orig);
+ }
+ if (out_data_opt) {
+ free(out_opt_orig);
+ }
+ if (bias) {
+ free(bias);
+ }
+ if (scratch_buf) {
+ free(scratch_buf);
+ }
+ }
+}
+
+void esp_nn_conv_s8_test()
+{
+ const int32_t input_offset = 5; /* some number in [-128, 127] */
+ const int32_t activation_min = -125;
+ const int32_t activation_max = 122;
+ const int32_t out_offset = 3;
+
+ void *scratch_buf = NULL;
+ int8_t *input_orig;
+ int8_t *out_c_orig;
+ int8_t *out_opt_orig;
+ int8_t *filter_data;
+ int32_t *bias;
+
+ /* independent variable */
+ int in_wd, in_ht, in_channels, out_channels;
+ uint16_t filter_ht, filter_wd;
+ uint16_t pad_wd, pad_ht, stride_wd, stride_ht;
+
+ // run for 10 iterations
+ for (int itr = 0; itr < 10; itr++) {
+ switch (itr) {
+ case 0: // ch % 8 == 0 && filter (1,1), padding (0,0)
+ in_wd = 10;
+ in_ht = 10;
+ in_channels = 64;
+ out_channels = 64;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 1: // ch % 4 == 0 && (in_wd * in_ht) % 16 == 0
+ in_wd = 4;
+ in_ht = 4;
+ in_channels = 20;
+ out_channels = 8;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 2: // ch, filter (3x3x3)
+ in_wd = 10;
+ in_ht = 10;
+ in_channels = 3;
+ out_channels = 64;
+ filter_ht = 3;
+ filter_wd = 3;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 3: // remaining pad (0, 0)
+ in_wd = 10;
+ in_ht = 10;
+ in_channels = 3;
+ out_channels = 64;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 4: // unopt case
+ in_wd = 10;
+ in_ht = 10;
+ in_channels = 12;
+ out_channels = 64;
+ filter_ht = 3;
+ filter_wd = 3;
+ pad_wd = 1;
+ pad_ht = 1;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ case 5: // ch % 8 == 0 & stride (2,2)
+ in_wd = 16;
+ in_ht = 16;
+ in_channels = 16;
+ out_channels = 16;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 2;
+ stride_ht = 2;
+ break;
+ case 6: // ch % 8 == 0 && filter (1,1), padding (0,0)
+ in_wd = 2;
+ in_ht = 2;
+ in_channels = 8;
+ out_channels = 8;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ default: // ch % 8 == 0
+ in_wd = 8;
+ in_ht = 8;
+ in_channels = 16;
+ out_channels = 16;
+ filter_ht = 1;
+ filter_wd = 1;
+ pad_wd = 0;
+ pad_ht = 0;
+ stride_wd = 1;
+ stride_ht = 1;
+ break;
+ }
+
+ /* prepare data */
+ uint16_t out_wd = (in_wd - filter_wd + 1) / stride_wd;
+ uint16_t out_ht = (in_ht - filter_ht + 1) / stride_ht;
+
+ int in_size = in_wd * in_ht * in_channels;
+ int filter_size = filter_wd * filter_ht * in_channels * out_channels + 2;
+ int out_size = out_wd * out_ht * out_channels;
+
+#if IDF_HEAP_CAPS
+ input_orig = (int8_t *) heap_caps_malloc(in_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_c_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ out_opt_orig = (int8_t *) heap_caps_malloc(out_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ filter_data = (int8_t *) heap_caps_malloc(filter_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ bias = (int32_t *) heap_caps_malloc(128 + sizeof (int32_t) * out_channels, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+
+ int8_t *input = 16 + input_orig - ((uint32_t) input_orig & 0xf);
+ int8_t *out_data_c = 16 + out_c_orig - ((uint32_t) out_c_orig & 0xf);
+ int8_t *out_data_opt = 16 + out_opt_orig - ((uint32_t) out_opt_orig & 0xf);
+#else
+ int8_t *input = memalign(16, in_size);
+ int8_t *out_data_c = memalign(16, out_size);
+ int8_t *out_data_opt = memalign(16, out_size);
+ filter_data = memalign(16, filter_size);
+ bias = calloc(1, 128 + sizeof (int32_t) * out_channels);
+ input_orig = input;
+ out_c_orig = out_data_c;
+ out_opt_orig = out_data_opt;
+#endif
+ int32_t *out_shift = calloc(1, 128 + sizeof (int32_t) * out_channels);
+ int32_t *out_mult = calloc(1, 128 + sizeof (int32_t) * out_channels);
+
+ if (input == NULL || filter_data == NULL ||
+ out_data_c == NULL || out_data_opt == NULL) {
+ printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto conv_s8_cleanup;
+ }
+
+ if (bias == NULL || out_shift == NULL || out_mult == NULL) {
+ printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto conv_s8_cleanup;
+ }
+
+ /* Generate input data between -128 -> +127 */
+ for (int i = 0; i < in_size; ++i) {
+ input[i] = rand() % 255 - 128;
+ }
+
+ /* Generate filter data between -128 -> +127 */
+ for (int i = 0; i < filter_size; ++i) {
+ filter_data[i] = rand() % 256 - 128;
+ }
+
+ /* Generate bias data */
+ for (int i = 0; i < out_channels; ++i) {
+ bias[i] = (int32_t)rand() % UINT16_MAX + UINT8_MAX;
+ }
+
+ /* Shift and multiplier */
+ for (int i = 0; i < out_channels; ++i) {
+ out_shift[i] = -10 + rand() % 2;
+ out_mult[i] = 0x7f67f4f8 + rand() % 50;
+ }
+
+ data_dims_t input_dims = {.width = in_wd, .height = in_ht, .channels = in_channels, 1};
+ data_dims_t output_dims = {.width = out_wd, .height = out_ht, .channels = out_channels, 1};
+ data_dims_t filter_dims = {.width = filter_wd, .height = filter_ht, 0, 0};
+ conv_params_t conv_params = {.in_offset = input_offset, .out_offset = out_offset,
+ .stride = {stride_wd, stride_ht}, .padding = {pad_wd, pad_ht},
+ .dilation = {0, 0}, .activation = {activation_min, activation_max}};
+ quant_data_t quant_data = {.shift = out_shift, .mult = out_mult};
+
+ int scratch_buf_size = esp_nn_get_conv_scratch_size(&input_dims, &filter_dims,
+ &output_dims, &conv_params);
+ if (scratch_buf_size > 0) {
+#if IDF_HEAP_CAPS
+ void *scratch_buf = heap_caps_malloc(scratch_buf_size + 32, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ int align_sz = 16 - (((int32_t) scratch_buf) & 0xf);
+#else
+ void *scratch_buf = memalign(16, scratch_buf_size);
+ int align_sz = 0;
+#endif
+ if (scratch_buf == NULL) {
+ printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
+ goto conv_s8_cleanup;
+ }
+ esp_nn_set_conv_scratch_buf(scratch_buf + align_sz);
+ }
+
+ if (itr == 0) {
+ /* enable profiler */
+ profile_c_start();
+ }
+
+ /* C function */
+ esp_nn_conv_s8_ansi(&input_dims, input, &filter_dims, filter_data + 2,
+ bias, &output_dims, out_data_c, &conv_params, &quant_data);
+
+ if (itr == 0) {
+ profile_c_end();
+ profile_opt_start();
+ }
+
+ /* Optimized function */
+ esp_nn_conv_s8(&input_dims, input, &filter_dims, filter_data + 2,
+ bias, &output_dims, out_data_opt, &conv_params, &quant_data);
+
+ if (itr == 0) {
+ /* disable profiler */
+ profile_opt_end();
+ }
+
+ bool ret = CHECK_EQUAL(out_data_c, out_data_opt, out_size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(out_data_opt, out_size / out_ht, out_ht);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(out_data_c, out_size / out_ht, out_ht);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, in_size / in_ht, in_ht);
+ printf("Filter data:\n");
+ PRINT_ARRAY_HEX(filter_data + 2, (filter_size - 2) / filter_ht, filter_ht);
+ printf("bias data:\n");
+ PRINT_ARRAY_INT(bias, out_channels, 1);
+ goto conv_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+
+ conv_s8_cleanup:
+ if (input) {
+ free(input_orig);
+ }
+ if (filter_data) {
+ free(filter_data);
+ }
+ if (out_data_c) {
+ free(out_c_orig);
+ }
+ if (out_data_opt) {
+ free(out_opt_orig);
+ }
+ if (bias) {
+ free(bias);
+ }
+ if (out_shift) {
+ free(out_shift);
+ }
+ if (out_mult) {
+ free(out_mult);
+ }
+ if (scratch_buf) {
+ free(scratch_buf);
+ }
+ }
+}
diff --git a/code/components/esp-nn/tests/src/fully_connected_test.c b/code/components/esp-nn/tests/src/fully_connected_test.c
new file mode 100644
index 00000000..d0210b46
--- /dev/null
+++ b/code/components/esp-nn/tests/src/fully_connected_test.c
@@ -0,0 +1,111 @@
+// Copyright 2020-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
+#include
+#include
+#include
+
+#include
+#include "test_utils.h"
+
+
+void esp_nn_fully_connected_s8_test()
+{
+ /* prepare data */
+ static uint16_t row_len = 256 + 8 + 7; /* odd len to test unaligned+left-over */
+ static uint16_t out_channels = 3;
+ int8_t input[row_len];
+ int8_t filter_data[row_len * out_channels];
+ int8_t output_c[out_channels], output_opt[out_channels];
+ static int32_t activation_min = -128;
+ static int32_t activation_max = 127;
+ static int32_t input_offset = 0;
+ static int32_t filter_offset = 0;
+ int32_t out_shift = -10;
+ static int32_t out_offset = 127;
+ int32_t out_mult = 0x59e492c4;
+ for (int itr = 0; itr < 5; itr++) {
+ out_mult = INT32_MAX / row_len + rand() % INT16_MAX;
+ switch (itr) {
+ case 0:
+ out_shift = -10;
+ break;
+ case 1:
+ out_shift = SHIFT_MIN;
+ break;
+ case 2:
+ out_shift = SHIFT_MAX;
+ break;
+ case 3:
+ out_shift = 0;
+ break;
+ default:
+ out_shift = -10 + rand() % 5;
+ break;
+ }
+ if (itr == 0) {
+ out_shift = SHIFT_MAX;
+ }
+ /* Generate input and filter data */
+ for (int i = 0; i < row_len; ++i) {
+ input[i] = rand() % 256 - 128;
+ }
+ for (int i = 0; i < row_len * out_channels; ++i) {
+ filter_data[i] = rand() % 256 - 128;
+ }
+
+ if (itr == 0) {
+ /* enable profiler */
+ profile_c_start();
+ }
+
+ /* C function */
+ esp_nn_fully_connected_s8_ansi(input, input_offset, row_len, filter_data, filter_offset,
+ NULL, output_c, out_channels, out_offset, out_shift, out_mult,
+ activation_min, activation_max);
+
+ if (itr == 0) {
+ profile_c_end();
+ profile_opt_start();
+ }
+
+ /* Optimized function */
+ esp_nn_fully_connected_s8(input, input_offset, row_len, filter_data, filter_offset,
+ NULL, output_opt, out_channels, out_offset, out_shift, out_mult,
+ activation_min, activation_max);
+
+ if (itr == 0) {
+ /* disable profiler */
+ profile_opt_end();
+ }
+
+ bool ret = CHECK_EQUAL(output_c, output_opt, out_channels);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s[%d] failed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(output_opt, out_channels, 1);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(output_c, out_channels, 1);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, row_len, 1);
+ printf("Filter data:\n");
+ PRINT_ARRAY_HEX(filter_data, row_len, out_channels);
+ printf("Out shift: %d\n", out_shift);
+ printf("Out mult: %x\n", out_mult);
+ return;
+ }
+ printf(ANSI_COLOR_GREEN"%s[%d] passed\n"ANSI_COLOR_RESET, __FUNCTION__, itr);
+ }
+}
diff --git a/code/components/esp-nn/tests/src/pooling_test.c b/code/components/esp-nn/tests/src/pooling_test.c
new file mode 100644
index 00000000..c1c889e1
--- /dev/null
+++ b/code/components/esp-nn/tests/src/pooling_test.c
@@ -0,0 +1,184 @@
+// Copyright 2020-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
+#include
+#include
+#include
+#include
+
+#include
+#include "test_utils.h"
+
+
+void esp_nn_avg_pool_s8_test()
+{
+ /* prepare data */
+ const uint16_t input_wd = 16;
+ const uint16_t input_ht = 16;
+ const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
+ const int size = input_wd * input_ht * channels;
+ int8_t *input, *output_c, *output_opt;
+ const int32_t activation_min = -128;
+ const int32_t activation_max = 127;
+ const uint16_t pad_wd = 1;
+ const uint16_t pad_ht = 1;
+ const uint16_t stride_wd = 1;
+ const uint16_t stride_ht = 1;
+ const uint16_t filter_ht = 3;
+ const uint16_t filter_wd = 3;
+ const uint16_t out_wd = input_wd / stride_wd;
+ const uint16_t out_ht = input_ht / stride_ht;
+ const int out_size = out_wd * out_ht * channels;
+
+ input = memalign(16, size);
+ output_c = memalign(16, out_size);
+ output_opt = memalign(16, out_size);
+
+ if (input == NULL || output_c == NULL || output_opt == NULL) {
+ printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto avg_pool_s8_cleanup;
+ }
+ /**
+ * width/height, channels etc look suspicious but it it true.
+ * It actually depends upon where in model this is actually placed.
+ * If at the end wd/ht tends to be smaller and depth larger.
+ */
+
+ for (int i = 0; i < size; ++i) {
+ input[i] = rand() % 256 - 128;
+ }
+
+ /* enable profiler */
+ profile_c_start();
+
+ /* C function */
+ esp_nn_avg_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
+ stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
+ activation_min, activation_max, channels);
+
+ profile_c_end();
+ profile_opt_start();
+
+ /* Optimized function */
+ esp_nn_avg_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
+ stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
+ activation_min, activation_max, channels);
+
+ /* disable profiler */
+ profile_opt_end();
+
+
+ bool ret = CHECK_EQUAL(output_c, output_opt, out_size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(output_opt, out_wd * channels, out_ht);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(output_c, out_wd * channels, out_ht);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, input_wd * channels, input_ht);
+ goto avg_pool_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
+
+avg_pool_s8_cleanup:
+ if (input) {
+ free(input);
+ }
+ if (output_c) {
+ free(output_c);
+ }
+ if (output_opt) {
+ free(output_opt);
+ }
+}
+
+void esp_nn_max_pool_s8_test()
+{
+ /* prepare data */
+ const uint16_t input_wd = 16;
+ const uint16_t input_ht = 16;
+ const uint16_t channels = 16; /* With TFLite example, I have seen it 256 */
+ int8_t *input, *output_c, *output_opt;
+ const int size = input_wd * input_ht * channels;
+ const int32_t activation_min = -128;
+ const int32_t activation_max = 127;
+ const uint16_t pad_wd = 1;
+ const uint16_t pad_ht = 1;
+ const uint16_t stride_wd = 1;
+ const uint16_t stride_ht = 1;
+ const uint16_t filter_ht = 3;
+ const uint16_t filter_wd = 3;
+ const uint16_t out_wd = input_wd / stride_wd;
+ const uint16_t out_ht = input_ht / stride_ht;
+ const int out_size = out_wd * out_ht * channels;
+
+ input = memalign(16, size);
+ output_c = memalign(16, out_size);
+ output_opt = memalign(16, out_size);
+
+ if (input == NULL || output_c == NULL || output_opt == NULL) {
+ printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto max_pool_s8_cleanup;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ input[i] = rand() % 256 - 128;
+ }
+
+ /* enable profiler */
+ profile_c_start();
+
+ /* C function */
+ esp_nn_max_pool_s8_ansi(input, input_wd, input_ht, output_c, out_wd, out_ht,
+ stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
+ activation_min, activation_max, channels);
+
+ profile_c_end();
+ profile_opt_start();
+
+ /* Optimized function */
+ esp_nn_max_pool_s8(input, input_wd, input_ht, output_opt, out_wd, out_ht,
+ stride_wd, stride_ht, filter_wd, filter_ht, pad_wd, pad_ht,
+ activation_min, activation_max, channels);
+
+ /* disable profiler */
+ profile_opt_end();
+
+
+ bool ret = CHECK_EQUAL(output_c, output_opt, out_wd * out_ht * channels);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(output_opt, out_wd * out_ht * channels, 1);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(output_c, out_wd * out_ht * channels, 1);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, 8, size / 8);
+ goto max_pool_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
+
+max_pool_s8_cleanup:
+ if (input) {
+ free(input);
+ }
+ if (output_c) {
+ free(output_c);
+ }
+ if (output_opt) {
+ free(output_opt);
+ }
+}
diff --git a/code/components/esp-nn/tests/src/relu_test.c b/code/components/esp-nn/tests/src/relu_test.c
new file mode 100644
index 00000000..ce6f13f1
--- /dev/null
+++ b/code/components/esp-nn/tests/src/relu_test.c
@@ -0,0 +1,83 @@
+// Copyright 2020-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
+#include
+#include
+#include
+#include
+
+#include
+#include "test_utils.h"
+
+void esp_nn_relu6_s8_test()
+{
+ const int size = 1600 + 8 + 7;
+ int8_t *input, *inout_ansi, *inout_opt;
+
+ input = memalign(16, size);
+ inout_ansi = memalign(16, size);
+ inout_opt = memalign(16, size);
+
+ if (input == NULL || inout_ansi == NULL || inout_opt == NULL) {
+ printf(ANSI_COLOR_RED"%s allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto relu6_s8_cleanup;
+ }
+ /* Generate filter data between -128 -> +127 */
+ for (int i = 0; i < size; ++i) {
+ input[i] = rand() % 255 - 128;
+ inout_ansi[i] = input[i];
+ inout_opt[i] = input[i];
+ }
+
+ /* enable profiler */
+ profile_c_start();
+
+ /* C function */
+ esp_nn_relu6_s8_ansi(inout_ansi, size);
+
+ profile_c_end();
+ profile_opt_start();
+
+ /* Optimized function */
+ esp_nn_relu6_s8(inout_opt, size);
+
+ /* disable profiler */
+ profile_opt_end();
+
+ bool ret = CHECK_EQUAL(inout_ansi, inout_opt, size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(inout_opt, size, 1);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(inout_ansi, size, 1);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, size, 1);
+ goto relu6_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
+
+relu6_s8_cleanup:
+ if (input) {
+ free (input);
+ }
+ if (inout_ansi) {
+ free (inout_ansi);
+ }
+ if (inout_opt) {
+ free (inout_opt);
+ }
+
+}
diff --git a/code/components/esp-nn/tests/src/softmax_test.c b/code/components/esp-nn/tests/src/softmax_test.c
new file mode 100644
index 00000000..f7c734cd
--- /dev/null
+++ b/code/components/esp-nn/tests/src/softmax_test.c
@@ -0,0 +1,101 @@
+// Copyright 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
+#include
+#include
+#include
+#include
+
+#include
+#include "test_utils.h"
+
+void esp_nn_softmax_s8_test()
+{
+ const int32_t height = 8;
+ const int32_t width = 32;
+ const int32_t diff_min = -128;
+ const int32_t mult = INT32_MAX / 2;
+ const int32_t shift = 7;
+ void *scratch_buf = NULL;
+ const int size = width * height;
+ int8_t *input, *out_ansi, *out_opt;
+
+ input = memalign(16, size);
+ out_ansi = memalign(16, size);
+ out_opt = memalign(16, size);
+
+ if (input == NULL || out_ansi == NULL || out_opt == NULL) {
+ printf(ANSI_COLOR_RED"%s buffer allocations failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ goto softmax_s8_cleanup;
+ }
+
+ /* Generate input data between -128 -> +127 */
+ for (int i = 0; i < size; ++i) {
+ input[i] = rand() % 255 - 128;
+ }
+
+ /* enable profiler */
+ profile_c_start();
+
+ /* C function */
+ esp_nn_softmax_s8_ansi(input, height, width, mult, shift, diff_min, out_ansi);
+
+ profile_c_end();
+
+ int32_t scratch_buf_size = esp_nn_get_softmax_scratch_size(width, height);
+ if (scratch_buf_size) {
+ scratch_buf = memalign(4, scratch_buf_size);
+ if (scratch_buf == NULL) {
+ printf(ANSI_COLOR_RED"%s scratch_buf alloc failed size %d\n"ANSI_COLOR_RESET, __FUNCTION__, scratch_buf_size);
+ goto softmax_s8_cleanup;
+ }
+ esp_nn_set_softmax_scratch_buf(scratch_buf);
+ }
+
+ profile_opt_start();
+
+ /* Optimized function */
+ esp_nn_softmax_s8(input, height, width, mult, shift, diff_min, out_opt);
+
+ /* disable profiler */
+ profile_opt_end();
+
+ bool ret = CHECK_EQUAL(out_ansi, out_opt, size);
+ if (ret == false) {
+ printf(ANSI_COLOR_RED"%s failed\n"ANSI_COLOR_RESET, __FUNCTION__);
+ printf("Output: \n");
+ PRINT_ARRAY_HEX(out_opt, width, height);
+ printf("Expected: \n");
+ PRINT_ARRAY_HEX(out_ansi, width, height);
+ printf("Input:\n");
+ PRINT_ARRAY_HEX(input, width, height);
+ goto softmax_s8_cleanup;
+ }
+ printf(ANSI_COLOR_GREEN"%s passed\n"ANSI_COLOR_RESET, __FUNCTION__);
+
+softmax_s8_cleanup:
+ if (input) {
+ free (input);
+ }
+ if (out_ansi) {
+ free (out_ansi);
+ }
+ if (out_opt) {
+ free (out_opt);
+ }
+ if (scratch_buf) {
+ free (scratch_buf);
+ }
+}
diff --git a/code/components/esp-nn_20220716.zip b/code/components/esp-nn_20220716.zip
new file mode 100644
index 00000000..53c7bef2
Binary files /dev/null and b/code/components/esp-nn_20220716.zip differ
diff --git a/code/components/esp32-camera-master_neu_20220121.zip b/code/components/esp32-camera-master.zip
similarity index 77%
rename from code/components/esp32-camera-master_neu_20220121.zip
rename to code/components/esp32-camera-master.zip
index 3acbcf1a..8706b3d8 100644
Binary files a/code/components/esp32-camera-master_neu_20220121.zip and b/code/components/esp32-camera-master.zip differ
diff --git a/code/components/esp32-camera-master/.github/workflows/build.yml b/code/components/esp32-camera-master/.github/workflows/build.yml
index 85762b65..08f10dae 100644
--- a/code/components/esp32-camera-master/.github/workflows/build.yml
+++ b/code/components/esp32-camera-master/.github/workflows/build.yml
@@ -8,26 +8,37 @@ 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-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.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:
@@ -65,15 +76,3 @@ jobs:
uses: espressif/esp-idf-ci-action@release-v4.3
with:
path: 'examples'
-
- build-release-v3_3:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repo
- uses: actions/checkout@v2
- with:
- submodules: 'recursive'
- - name: esp-idf build
- uses: espressif/esp-idf-ci-action@release-v3.3
- with:
- path: 'examples'
diff --git a/code/components/esp32-camera-master/.github/workflows/upload_component.yml b/code/components/esp32-camera-master/.github/workflows/upload_component.yml
index 0fb12cc3..f550e696 100644
--- a/code/components/esp32-camera-master/.github/workflows/upload_component.yml
+++ b/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 }}
diff --git a/code/components/esp32-camera-master/CMakeLists.txt b/code/components/esp32-camera-master/CMakeLists.txt
index 8090f326..7c07b0bf 100644
--- a/code/components/esp32-camera-master/CMakeLists.txt
+++ b/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()
diff --git a/code/components/esp32-camera-master/Kconfig b/code/components/esp32-camera-master/Kconfig
index dbf67089..66253d0a 100644
--- a/code/components/esp32-camera-master/Kconfig
+++ b/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"
@@ -125,5 +164,24 @@ menu "Camera configuration"
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
+
+ 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
endmenu
diff --git a/code/components/esp32-camera-master/README.md b/code/components/esp32-camera-master/README.md
index de61a5bb..9b5282e4 100644
--- a/code/components/esp32-camera-master/README.md
+++ b/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
RAW Bayer
RGB565 | 1/6.5" |
| GC2145 | 1600 x 1200 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/5" |
| BF3005 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer
RGB565 | 1/4" |
+| BF20A6 | 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/10" |
+| SC101IOT| 1280 x 720 | color | YUV/YCbCr422
Raw RGB | 1/4.2" |
+| SC030IOT| 640 x 480 | color | YUV/YCbCr422
RAW Bayer | 1/6.5" |
## Important to Remember
diff --git a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c b/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
index a9615e36..52833a73 100644
--- a/code/components/esp32-camera-master/conversions/esp_jpg_decode.c
+++ b/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)) {
diff --git a/code/components/esp32-camera-master/conversions/jpge.cpp b/code/components/esp32-camera-master/conversions/jpge.cpp
index a8ab93e0..dd6790e6 100644
--- a/code/components/esp32-camera-master/conversions/jpge.cpp
+++ b/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); }
diff --git a/code/components/esp32-camera-master/conversions/to_bmp.c b/code/components/esp32-camera-master/conversions/to_bmp.c
index 5a54bdba..e267c78f 100644
--- a/code/components/esp32-camera-master/conversions/to_bmp.c
+++ b/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) {
diff --git a/code/components/esp32-camera-master/conversions/to_jpg.cpp b/code/components/esp32-camera-master/conversions/to_jpg.cpp
index 9b8905a7..24cc2989 100644
--- a/code/components/esp32-camera-master/conversions/to_jpg.cpp
+++ b/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)
diff --git a/code/components/esp32-camera-master/driver/cam_hal.c b/code/components/esp32-camera-master/driver/cam_hal.c
index 9b7e12b5..1604f8ac 100644
--- a/code/components/esp32-camera-master/driver/cam_hal.c
+++ b/code/components/esp32-camera-master/driver/cam_hal.c
@@ -18,8 +18,21 @@
#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
+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
@@ -93,7 +106,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"));
}
}
diff --git a/code/components/esp32-camera-master/driver/esp_camera.c b/code/components/esp32-camera-master/driver/esp_camera.c
index 5b671c0e..8327445c 100644
--- a/code/components/esp32-camera-master/driver/esp_camera.c
+++ b/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,6 +128,15 @@ 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)
@@ -218,6 +236,23 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
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)
{
esp_err_t err;
@@ -256,6 +291,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 +299,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);
diff --git a/code/components/esp32-camera-master/driver/include/esp_camera.h b/code/components/esp32-camera-master/driver/include/esp_camera.h
index b6047d31..2025bb40 100644
--- a/code/components/esp32-camera-master/driver/include/esp_camera.h
+++ b/code/components/esp32-camera-master/driver/include/esp_camera.h
@@ -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
*/
@@ -124,6 +138,9 @@ 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
} camera_config_t;
/**
diff --git a/code/components/esp32-camera-master/driver/include/sensor.h b/code/components/esp32-camera-master/driver/include/sensor.h
index b2bf55f1..d5ec7463 100644
--- a/code/components/esp32-camera-master/driver/include/sensor.h
+++ b/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
diff --git a/code/components/esp32-camera-master/driver/sccb.c b/code/components/esp32-camera-master/driver/sccb.c
index 314dd982..edc417f8 100644
--- a/code/components/esp32-camera-master/driver/sccb.c
+++ b/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 */
diff --git a/code/components/esp32-camera-master/driver/sensor.c b/code/components/esp32-camera-master/driver/sensor.c
index bf6d313f..2f4c9711 100644
--- a/code/components/esp32-camera-master/driver/sensor.c
+++ b/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] = {
diff --git a/code/components/esp32-camera-master/examples/main/take_picture.c b/code/components/esp32-camera-master/examples/main/take_picture.c
index 1cbad908..1fb1039d 100644
--- a/code/components/esp32-camera-master/examples/main/take_picture.c
+++ b/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
diff --git a/code/components/esp32-camera-master/idf_component.yml b/code/components/esp32-camera-master/idf_component.yml
index 848e1cd8..2b98f8d0 100644
--- a/code/components/esp32-camera-master/idf_component.yml
+++ b/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
diff --git a/code/components/esp32-camera-master/sensors/bf20a6.c b/code/components/esp32-camera-master/sensors/bf20a6.c
new file mode 100644
index 00000000..b1179c30
--- /dev/null
+++ b/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
+#include
+#include
+#include
+#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;
+}
diff --git a/code/components/esp32-camera-master/sensors/gc0308.c b/code/components/esp32-camera-master/sensors/gc0308.c
index 8b106a3a..f19025eb 100644
--- a/code/components/esp32-camera-master/sensors/gc0308.c
+++ b/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
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6.h
new file mode 100644
index 00000000..8c925eb5
--- /dev/null
+++ b/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6_regs.h
new file mode 100644
index 00000000..ab1ff69e
--- /dev/null
+++ b/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h b/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
new file mode 100644
index 00000000..0414bbac
--- /dev/null
+++ b/code/components/esp32-camera-master/sensors/private_include/bf20a6_settings.h
@@ -0,0 +1,158 @@
+
+#include
+
+#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},
+};
diff --git a/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
index 32ef3816..adf5f28d 100644
--- a/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
+++ b/code/components/esp32-camera-master/sensors/private_include/gc0308_settings.h
@@ -3,10 +3,9 @@
#include
-#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
diff --git a/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h b/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
index fec7d679..f52572fa 100644
--- a/code/components/esp32-camera-master/sensors/private_include/ov5640_settings.h
+++ b/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
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc030iot.h b/code/components/esp32-camera-master/sensors/private_include/sc030iot.h
new file mode 100644
index 00000000..19298b76
--- /dev/null
+++ b/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h b/code/components/esp32-camera-master/sensors/private_include/sc030iot_settings.h
new file mode 100644
index 00000000..56f5654c
--- /dev/null
+++ b/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
+
+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},
+};
+
+*/
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc101iot.h b/code/components/esp32-camera-master/sensors/private_include/sc101iot.h
new file mode 100644
index 00000000..85858498
--- /dev/null
+++ b/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__
diff --git a/code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h b/code/components/esp32-camera-master/sensors/private_include/sc101iot_settings.h
new file mode 100644
index 00000000..2eb14398
--- /dev/null
+++ b/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
+
+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
+};
diff --git a/code/components/esp32-camera-master/sensors/sc030iot.c b/code/components/esp32-camera-master/sensors/sc030iot.c
new file mode 100644
index 00000000..86f525f3
--- /dev/null
+++ b/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
+#include
+#include
+#include
+#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 (islv_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;
+}
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/sensors/sc101iot.c b/code/components/esp32-camera-master/sensors/sc101iot.c
new file mode 100644
index 00000000..310a0476
--- /dev/null
+++ b/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
+#include
+#include
+#include
+#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 (islv_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;
+}
\ No newline at end of file
diff --git a/code/components/esp32-camera-master/target/esp32/ll_cam.c b/code/components/esp32-camera-master/target/esp32/ll_cam.c
index d0f0c862..1e3def87 100644
--- a/code/components/esp32-camera-master/target/esp32/ll_cam.c
+++ b/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);
diff --git a/code/components/esp32-camera-master/target/esp32s2/ll_cam.c b/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
index e54b81f0..54764329 100644
--- a/code/components/esp32-camera-master/target/esp32s2/ll_cam.c
+++ b/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;
diff --git a/code/components/esp32-camera-master/target/esp32s3/ll_cam.c b/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
index ce405d16..2211a0ed 100644
--- a/code/components/esp32-camera-master/target/esp32s3/ll_cam.c
+++ b/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,52 @@ 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
+ LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
+ LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
+ 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);
@@ -215,15 +265,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;
- }
+ ret = ll_cam_dma_init(cam);
- return ESP_OK;
+ return ret;
}
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
@@ -417,6 +473,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in,
}
return len / 2;
}
+
// just memcpy
memcpy(out, in, len);
@@ -433,8 +490,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;
diff --git a/code/components/esp32-camera-master/target/private_include/ll_cam.h b/code/components/esp32-camera-master/target/private_include/ll_cam.h
index 7d30c370..c27db0c4 100644
--- a/code/components/esp32-camera-master/target/private_include/ll_cam.h
+++ b/code/components/esp32-camera-master/target/private_include/ll_cam.h
@@ -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;
diff --git a/code/components/esp32-camera-master_old_version.zip b/code/components/esp32-camera-master_old_version.zip
deleted file mode 100644
index c0c60f8f..00000000
Binary files a/code/components/esp32-camera-master_old_version.zip and /dev/null differ
diff --git a/code/components/jomjol_flowcontroll/CMakeLists.txt b/code/components/jomjol_flowcontroll/CMakeLists.txt
index 8a066910..6ee66829 100644
--- a/code/components/jomjol_flowcontroll/CMakeLists.txt
+++ b/code/components/jomjol_flowcontroll/CMakeLists.txt
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
idf_component_register(SRCS ${app_sources}
INCLUDE_DIRS "."
- REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
+ REQUIRES jomjol_tfliteclass jomjol_helper jomjol_controlcamera jomjol_mqtt jomjol_influxdb jomjol_fileserver_ota jomjol_image_proc jomjol_wlan)
diff --git a/code/components/jomjol_flowcontroll/ClassFlow.cpp b/code/components/jomjol_flowcontroll/ClassFlow.cpp
index ff14c1b2..f15844d5 100644
--- a/code/components/jomjol_flowcontroll/ClassFlow.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlow.cpp
@@ -19,7 +19,6 @@ void ClassFlow::SetInitialParameter(void)
std::vector ClassFlow::ZerlegeZeile(std::string input, std::string delimiter)
{
std::vector Output;
-// std::string delimiter = " =,";
input = trim(input, delimiter);
size_t pos = findDelimiterPos(input, delimiter);
diff --git a/code/components/jomjol_flowcontroll/ClassFlow.h b/code/components/jomjol_flowcontroll/ClassFlow.h
index 4df4777c..92184d32 100644
--- a/code/components/jomjol_flowcontroll/ClassFlow.h
+++ b/code/components/jomjol_flowcontroll/ClassFlow.h
@@ -26,7 +26,6 @@ struct HTMLInfo
class ClassFlow
{
protected:
-// std::vector ZerlegeZeile(string input);
std::vector ZerlegeZeile(string input, string delimiter = " =, \t");
bool isNewParagraph(string input);
bool GetNextParagraph(FILE* pfile, string& aktparamgraph);
diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp
index 164d83cf..8e76c720 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.cpp
@@ -19,6 +19,7 @@ void ClassFlowAlignment::SetInitialParameter(void)
initalrotate = 0;
anz_ref = 0;
initialmirror = false;
+ use_antialiasing = false;
initialflip = false;
SaveAllFiles = false;
namerawimage = "/sdcard/img_tmp/raw.jpg";
@@ -94,7 +95,12 @@ bool ClassFlowAlignment::ReadParameter(FILE* pfile, string& aktparamgraph)
if ((toUpper(zerlegt[0]) == "SEARCHFIELDY") && (zerlegt.size() > 1))
{
suchey = std::stod(zerlegt[1]);
- }
+ }
+ if ((toUpper(zerlegt[0]) == "ANTIALIASING") && (zerlegt.size() > 1))
+ {
+ if (toUpper(zerlegt[1]) == "TRUE")
+ use_antialiasing = true;
+ }
if ((zerlegt.size() == 3) && (anz_ref < 2))
{
References[anz_ref].image_file = FormatFileName("/sdcard" + zerlegt[0]);
@@ -175,7 +181,10 @@ bool ClassFlowAlignment::doFlow(string time)
if ((initalrotate != 0) || initialflip)
{
- rt.Rotate(initalrotate);
+ if (use_antialiasing)
+ rt.RotateAntiAliasing(initalrotate);
+ else
+ rt.Rotate(initalrotate);
if (SaveAllFiles) AlignAndCutImage->SaveToFile(FormatFileName("/sdcard/img_tmp/rot.jpg"));
}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h
index 180dc7f6..7e1efef7 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowAlignment.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowAlignment.h
@@ -16,6 +16,7 @@ protected:
float initalrotate;
bool initialmirror;
bool initialflip;
+ bool use_antialiasing;
RefInfo References[2];
int anz_ref;
string namerawimage;
diff --git a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
index 6f0f32da..a3d47753 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.cpp
@@ -17,6 +17,7 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
string cnnmodelfile = "";
modelxsize = 1;
modelysize = 1;
+ CNNGoodThreshold = 0.0;
ListFlowControll = NULL;
previousElement = NULL;
SaveAllFiles = false;
@@ -27,20 +28,21 @@ ClassFlowCNNGeneral::ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNTy
flowpostalignment = _flowalign;
}
-string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution = false)
+string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution, int prev)
{
string result = "";
+
if (GENERAL[_analog]->ROI.size() == 0)
return result;
+ if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout _analog=" + std::to_string(_analog) + ", _extendedResolution=" + std::to_string(_extendedResolution) + ", prev=" + std::to_string(prev));
- if (CNNType == Analogue)
+ if (CNNType == Analogue || CNNType == Analogue100)
{
float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
-
- int prev = -1;
-
+
prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
+ if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(analog) zahl=" + std::to_string(zahl) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
result = std::to_string(prev);
if (_extendedResolution && (CNNType != Digital))
@@ -66,7 +68,58 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
return result;
}
- if (CNNType == DigitalHyprid)
+ if ((CNNType == DoubleHyprid10) || (CNNType == Digital100))
+ {
+
+ float zahl = GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float;
+ if (zahl >= 0) // NaN?
+ {
+ if (_extendedResolution) // ist nur gesetzt, falls es die erste Ziffer ist (kein Analog vorher!)
+ {
+ int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
+ int ergebnis_vorkomma = ((int) floor(zahl)) % 10;
+
+ result = std::to_string(ergebnis_vorkomma) + std::to_string(ergebnis_nachkomma);
+ prev = ergebnis_vorkomma;
+ if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100-ext) ergebnis_vorkomma=" + std::to_string(ergebnis_vorkomma) + ", ergebnis_nachkomma=" + std::to_string(ergebnis_nachkomma) + ", prev=" + std::to_string(prev));
+
+
+ }
+ else
+ {
+// prev = ZeigerEval(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev);
+ prev = ZeigerEvalHybrid(GENERAL[_analog]->ROI[GENERAL[_analog]->ROI.size() - 1]->result_float, prev, prev);
+ result = std::to_string(prev);
+ if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::getReadout(dig100) prev=" + std::to_string(prev));
+
+ }
+ }
+ else
+ {
+ result = "N";
+ if (_extendedResolution && (CNNType != Digital))
+ result = "NN";
+ }
+
+ for (int i = GENERAL[_analog]->ROI.size() - 2; i >= 0; --i)
+ {
+ if (GENERAL[_analog]->ROI[i]->result_float >= 0)
+ {
+ prev = ZeigerEvalHybrid(GENERAL[_analog]->ROI[i]->result_float, GENERAL[_analog]->ROI[i+1]->result_float, prev);
+ result = std::to_string(prev) + result;
+
+ }
+ else
+ {
+ prev = -1;
+ result = "N" + result;
+ }
+ }
+ return result;
+ }
+
+/*
+ if (CNNType == Digital100)
{
int zif_akt = -1;
@@ -109,6 +162,7 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
}
return result;
}
+*/
return result;
}
@@ -116,8 +170,10 @@ string ClassFlowCNNGeneral::getReadout(int _analog = 0, bool _extendedResolution
int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int eval_vorgaenger)
{
int ergebnis_nachkomma = ((int) floor(zahl * 10)) % 10;
+ int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
- if (zahl_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
+
+ if (eval_vorgaenger < 0) // keine Vorzahl vorhanden !!! --> Runde die Zahl
{
if ((ergebnis_nachkomma <= 2) || (ergebnis_nachkomma >= 8)) // Band um die Ziffer --> Runden, da Ziffer im Rahmen Ungenauigkeit erreicht
return ((int) round(zahl) + 10) % 10;
@@ -125,6 +181,34 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
return ((int) trunc(zahl) + 10) % 10;
}
+ if ((zahl_vorgaenger >= 0.5 ) && (zahl_vorgaenger < 9.5))
+ {
+ // kein Ziffernwechsel, da Vorkomma weit genug weg ist (0+/-0.5) --> zahl wird gerundet
+ return ((int) round(zahl) + 10) % 10;
+ }
+ else
+ {
+ if (eval_vorgaenger <= 1) // Nulldurchgang hat stattgefunden (!Bewertung über Prev_value und nicht Zahl!) --> hier aufrunden (2.8 --> 3, aber auch 3.1 --> 3)
+ {
+ if (ergebnis_nachkomma > 5)
+ return (ergebnis_vorkomma + 1) % 10;
+ else
+ return ergebnis_vorkomma;
+ }
+ else // bleibt nur >= 9.5 --> noch kein Nulldurchgang --> 2.8 --> 2, und 3.1 --> 2
+ {
+ // hier auf 4 reduziert, da erst ab Vorgänder 9 anfängt umzustellen. Bei 9.5 Vorgänger kann die aktuelle
+ // Zahl noch x.4 - x.5 sein.
+ if (ergebnis_nachkomma >= 4)
+ return ergebnis_vorkomma;
+ else
+ return (ergebnis_vorkomma - 1 + 10) % 10;
+ }
+ }
+
+ return -1;
+
+/*
if (zahl_vorgaenger > 9.2) // Ziffernwechsel beginnt
{
if (eval_vorgaenger == 0) // Wechsel hat schon stattgefunden
@@ -151,20 +235,30 @@ int ClassFlowCNNGeneral::ZeigerEvalHybrid(float zahl, float zahl_vorgaenger, int
return ((int) round(zahl) + 10) % 10;
return ((int) trunc(zahl) + 10) % 10;
+*/
}
+
+
int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
-{
+{
int ergebnis_nachkomma = ((int) floor(zahl * 10) + 10) % 10;
int ergebnis_vorkomma = ((int) floor(zahl) + 10) % 10;
- int ergebnis, ergebnis_rating;
+ int ergebnis;
+ float ergebnis_rating;
+ if (debugdetailgeneral) LogFile.WriteToFile("ClassFlowCNNGeneral::ZeigerEval erg_v=" + std::to_string(ergebnis_vorkomma) + ", erg_n=" + std::to_string(ergebnis_nachkomma) + ", ziff_v=" + std::to_string(ziffer_vorgaenger));
if (ziffer_vorgaenger == -1)
return ergebnis_vorkomma % 10;
+ // Ist die aktuelle Stelle schon umgesprungen und die Vorstelle noch nicht?
+ // Akt.: 2.1, Vorstelle = 0.9 => 1.9
+ // Problem sind mehrere Rundungen
+ // Bsp. zahl=4.5, Vorgänger= 9.6 (ziffer_vorgaenger=0)
+ // Tritt nur auf bei Übergang von analog auf digit
ergebnis_rating = ergebnis_nachkomma - ziffer_vorgaenger;
if (ergebnis_nachkomma >= 5)
- ergebnis_rating-=5;
+ ergebnis_rating-=5.1;
else
ergebnis_rating+=5;
ergebnis = (int) round(zahl);
@@ -172,7 +266,7 @@ int ClassFlowCNNGeneral::ZeigerEval(float zahl, int ziffer_vorgaenger)
ergebnis-=1;
if (ergebnis == -1)
ergebnis+=10;
-
+
ergebnis = (ergebnis + 10) % 10;
return ergebnis;
}
@@ -206,12 +300,12 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
{
zerlegt = this->ZerlegeZeile(aktparamgraph);
- if ((zerlegt[0] == "LogImageLocation") && (zerlegt.size() > 1))
+ if ((toUpper(zerlegt[0]) == "LOGIMAGELOCATION") && (zerlegt.size() > 1))
{
this->LogImageLocation = "/sdcard" + zerlegt[1];
this->isLogImage = true;
}
- if ((zerlegt[0] == "LogImageSelect") && (zerlegt.size() > 1))
+ if ((toUpper(zerlegt[0]) == "LOGIMAGESELECT") && (zerlegt.size() > 1))
{
LogImageSelect = zerlegt[1];
isLogImageSelect = true;
@@ -221,20 +315,20 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
{
this->logfileRetentionInDays = std::stoi(zerlegt[1]);
}
- if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
- {
- if (toUpper(zerlegt[1]) == "DIGITHYPRID")
- CNNType = DigitalHyprid;
- }
+// if ((toUpper(zerlegt[0]) == "MODELTYPE") && (zerlegt.size() > 1))
+// {
+// if (toUpper(zerlegt[1]) == "DIGITHYPRID")
+// CNNType = DigitalHyprid;
+// }
- if ((zerlegt[0] == "Model") && (zerlegt.size() > 1))
+ if ((toUpper(zerlegt[0]) == "MODEL") && (zerlegt.size() > 1))
{
this->cnnmodelfile = zerlegt[1];
}
- if ((zerlegt[0] == "ModelInputSize") && (zerlegt.size() > 2))
+
+ if ((toUpper(zerlegt[0]) == "CNNGOODTHRESHOLD") && (zerlegt.size() > 1))
{
- this->modelxsize = std::stoi(zerlegt[1]);
- this->modelysize = std::stoi(zerlegt[2]);
+ CNNGoodThreshold = std::stof(zerlegt[1]);
}
if (zerlegt.size() >= 5)
{
@@ -256,11 +350,14 @@ bool ClassFlowCNNGeneral::ReadParameter(FILE* pfile, string& aktparamgraph)
}
}
+ if (!getNetworkParameter())
+ return false;
- for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
+
+ for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
- GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, 3);
+ GENERAL[_ana]->ROI[i]->image = new CImageBasis(modelxsize, modelysize, modelchannel);
GENERAL[_ana]->ROI[i]->image_org = new CImageBasis(GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, 3);
}
@@ -398,7 +495,7 @@ bool ClassFlowCNNGeneral::doAlignAndCut(string time)
void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
{
- if (CNNType == Analogue)
+ if (CNNType == Analogue || CNNType == Analogue100)
{
int r = 0;
int g = 255;
@@ -408,7 +505,6 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
{
_zw->drawRect(GENERAL[_ana]->ROI[i]->posx, GENERAL[_ana]->ROI[i]->posy, GENERAL[_ana]->ROI[i]->deltax, GENERAL[_ana]->ROI[i]->deltay, r, g, b, 1);
-// _zw->drawCircle((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), r, g, b, 2);
_zw->drawEllipse( (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) (GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
_zw->drawLine((int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) GENERAL[_ana]->ROI[i]->posy, (int) (GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax/2), (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay), r, g, b, 2);
_zw->drawLine((int) GENERAL[_ana]->ROI[i]->posx, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), (int) GENERAL[_ana]->ROI[i]->posx + GENERAL[_ana]->ROI[i]->deltax, (int) (GENERAL[_ana]->ROI[i]->posy + GENERAL[_ana]->ROI[i]->deltay/2), r, g, b, 2);
@@ -422,6 +518,71 @@ void ClassFlowCNNGeneral::DrawROI(CImageBasis *_zw)
}
}
+bool ClassFlowCNNGeneral::getNetworkParameter()
+{
+ if (disabled)
+ return true;
+
+ CTfLiteClass *tflite = new CTfLiteClass;
+ string zwcnn = "/sdcard" + cnnmodelfile;
+ zwcnn = FormatFileName(zwcnn);
+ printf(zwcnn.c_str());printf("\n");
+ if (!tflite->LoadModel(zwcnn)) {
+ printf("Can't read model file /sdcard%s\n", cnnmodelfile.c_str());
+ LogFile.WriteToFile("Cannot load model");
+ delete tflite;
+ return false;
+ }
+ tflite->MakeAllocate();
+
+ if (CNNType == AutoDetect)
+ {
+ tflite->GetInputDimension(false);
+ modelxsize = tflite->ReadInputDimenstion(0);
+ modelysize = tflite->ReadInputDimenstion(1);
+ modelchannel = tflite->ReadInputDimenstion(2);
+
+ int _anzoutputdimensions = tflite->GetAnzOutPut();
+ switch (_anzoutputdimensions)
+ {
+ case 2:
+ CNNType = Analogue;
+ printf("TFlite-Type set to Analogue\n");
+ break;
+ case 10:
+ CNNType = DoubleHyprid10;
+ printf("TFlite-Type set to DoubleHyprid10\n");
+ break;
+ case 11:
+ CNNType = Digital;
+ printf("TFlite-Type set to Digital\n");
+ break;
+ case 20:
+ CNNType = DigitalHyprid10;
+ printf("TFlite-Type set to DigitalHyprid10\n");
+ break;
+// case 22:
+// CNNType = DigitalHyprid;
+// printf("TFlite-Type set to DigitalHyprid\n");
+// break;
+ case 100:
+ if (modelxsize==32 && modelysize == 32) {
+ CNNType = Analogue100;
+ printf("TFlite-Type set to Analogue100\n");
+ } else {
+ CNNType = Digital100;
+ printf("TFlite-Type set to Digital\n");
+ }
+ break;
+ default:
+ printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
+ }
+ }
+
+ delete tflite;
+ return true;
+}
+
bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
{
if (disabled)
@@ -442,32 +603,6 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
}
tflite->MakeAllocate();
- if (CNNType == AutoDetect)
- {
- int _anzoutputdimensions = tflite->GetAnzOutPut();
- switch (_anzoutputdimensions)
- {
- case 2:
- CNNType = Analogue;
- printf("TFlite-Type set to Analogue\n");
- break;
- case 11:
- CNNType = Digital;
- printf("TFlite-Type set to Digital\n");
- break;
- case 20:
- CNNType = DigitalHyprid10;
- printf("TFlite-Type set to DigitalHyprid10\n");
- break;
- case 22:
- CNNType = DigitalHyprid;
- printf("TFlite-Type set to DigitalHyprid\n");
- break;
- default:
- printf("ERROR ERROR ERROR - tflite passt nicht zur Firmware - ERROR ERROR ERROR\n");
- }
- }
-
for (int _ana = 0; _ana < GENERAL.size(); ++_ana)
{
for (int i = 0; i < GENERAL[_ana]->ROI.size(); ++i)
@@ -492,6 +627,7 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
if (isLogImage)
LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
} break;
+
case Digital:
{
GENERAL[_ana]->ROI[i]->result_klasse = 0;
@@ -500,17 +636,19 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
if (isLogImage)
{
+ string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
if (isLogImageSelect)
{
if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
- LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
else
{
- LogImage(logPath, GENERAL[_ana]->ROI[i]->name, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
}
}
} break;
+/*
case DigitalHyprid:
{
int _num, _nachkomma;
@@ -536,8 +674,20 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
if (isLogImage)
- LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ {
+ string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
+ if (isLogImageSelect)
+ {
+ if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ else
+ {
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ }
} break;
+*/
case DigitalHyprid10:
{
int _num, _nachkomma;
@@ -560,8 +710,149 @@ bool ClassFlowCNNGeneral::doNeuralNetwork(string time)
if (debugdetailgeneral) LogFile.WriteToFile(_zwres);
if (isLogImage)
- LogImage(logPath, GENERAL[_ana]->ROI[i]->name, &GENERAL[_ana]->ROI[i]->result_float, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ {
+ string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
+ if (isLogImageSelect)
+ {
+ if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ else
+ {
+ LogImage(logPath, _imagename, NULL, &GENERAL[_ana]->ROI[i]->result_klasse, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ }
} break;
+
+ case DoubleHyprid10:
+ {
+ int _num, _numplus, _numminus;
+ float _val, _valplus, _valminus;
+ float _fit;
+ float _result_save_file;
+
+ tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
+ tflite->Invoke();
+ if (debugdetailgeneral) LogFile.WriteToFile("Nach Invoke");
+
+ _num = tflite->GetOutClassification(0, 9);
+ _numplus = (_num + 1) % 10;
+ _numminus = (_num - 1 + 10) % 10;
+
+ _val = tflite->GetOutputValue(_num);
+ _valplus = tflite->GetOutputValue(_numplus);
+ _valminus = tflite->GetOutputValue(_numminus);
+
+ float result = _num;
+
+ if (_valplus > _valminus)
+ {
+ result = result + _valplus / (_valplus + _val);
+ _fit = _val + _valplus;
+ }
+ else
+ {
+ result = result - _valminus / (_val + _valminus);
+ _fit = _val + _valminus;
+
+ }
+ if (result >= 10)
+ result = result - 10;
+ if (result < 0)
+ result = result + 10;
+
+ string zw = "_num (p, m): " + to_string(_num) + " " + to_string(_numplus) + " " + to_string(_numminus);
+ zw = zw + " _val (p, m): " + to_string(_val) + " " + to_string(_valplus) + " " + to_string(_valminus);
+ zw = zw + " result: " + to_string(result) + " _fit: " + to_string(_fit);
+ printf("details cnn: %s\n", zw.c_str());
+ LogFile.WriteToFile(zw);
+
+
+ _result_save_file = result;
+
+ if (_fit < CNNGoodThreshold)
+ {
+ GENERAL[_ana]->ROI[i]->isReject = true;
+ result = -1;
+ _result_save_file+= 100; // Für den Fall, dass fit nicht ausreichend, soll trotzdem das Ergebnis mit "-10x.y" abgespeichert werden.
+ string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + "Threshold: " + to_string(CNNGoodThreshold);
+ printf("Value Rejected due to Threshold (Fit: %f, Threshold: %f\n", _fit, CNNGoodThreshold);
+ LogFile.WriteToFile(zw);
+ }
+ else
+ {
+ GENERAL[_ana]->ROI[i]->isReject = false;
+ }
+
+
+ GENERAL[_ana]->ROI[i]->result_float = result;
+ printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
+
+ if (isLogImage)
+ {
+ string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
+ if (isLogImageSelect)
+ {
+ if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
+ LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ else
+ {
+ LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ }
+ }
+ break;
+ case Digital100:
+ case Analogue100:
+ {
+ int _num;
+ float _fit;
+ float _result_save_file;
+
+ tflite->LoadInputImageBasis(GENERAL[_ana]->ROI[i]->image);
+ tflite->Invoke();
+
+ _num = tflite->GetOutClassification();
+ _fit = tflite->GetOutputValue(_num);
+
+ GENERAL[_ana]->ROI[i]->result_float = (float)_num / 10.0;
+
+
+ _result_save_file = GENERAL[_ana]->ROI[i]->result_float;
+
+ if (_fit < CNNGoodThreshold)
+ {
+ GENERAL[_ana]->ROI[i]->isReject = true;
+ GENERAL[_ana]->ROI[i]->result_float = -1;
+ _result_save_file+= 100; // Für den Fall, dass fit nicht ausreichend, soll trotzdem das Ergebnis mit "-10x.y" abgespeichert werden.
+ string zw = "Value Rejected due to Threshold (Fit: " + to_string(_fit) + "Threshold: " + to_string(CNNGoodThreshold);
+ printf("Value Rejected due to Threshold (Fit: %f, Threshold: %f\n", _fit, CNNGoodThreshold);
+ LogFile.WriteToFile(zw);
+ }
+ else
+ {
+ GENERAL[_ana]->ROI[i]->isReject = false;
+ }
+
+ printf("Result General(Analog)%i: %f\n", i, GENERAL[_ana]->ROI[i]->result_float);
+
+ if (isLogImage)
+ {
+ string _imagename = GENERAL[_ana]->name + "_" + GENERAL[_ana]->ROI[i]->name;
+ if (isLogImageSelect)
+ {
+ if (LogImageSelect.find(GENERAL[_ana]->ROI[i]->name) != std::string::npos)
+ LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ else
+ {
+ LogImage(logPath, _imagename, &_result_save_file, NULL, time, GENERAL[_ana]->ROI[i]->image_org);
+ }
+ }
+
+ } break;
+
default:
break;
}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
index ef0a6cd1..66aa56d5 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowCNNGeneral.h
@@ -8,9 +8,12 @@
enum t_CNNType {
AutoDetect,
Analogue,
+ Analogue100,
Digital,
- DigitalHyprid,
+// DigitalHyprid,
DigitalHyprid10,
+ DoubleHyprid10,
+ Digital100,
None
};
@@ -20,9 +23,10 @@ class ClassFlowCNNGeneral :
protected:
t_CNNType CNNType;
std::vector GENERAL;
+ float CNNGoodThreshold;
string cnnmodelfile;
- int modelxsize, modelysize;
+ int modelxsize, modelysize, modelchannel;
bool isLogImageSelect;
string LogImageSelect;
ClassFlowAlignment* flowpostalignment;
@@ -37,6 +41,8 @@ protected:
bool doNeuralNetwork(string time);
bool doAlignAndCut(string time);
+ bool getNetworkParameter();
+
public:
ClassFlowCNNGeneral(ClassFlowAlignment *_flowalign, t_CNNType _cnntype = AutoDetect);
@@ -44,7 +50,7 @@ public:
bool doFlow(string time);
string getHTMLSingleStep(string host);
- string getReadout(int _analog, bool _extendedResolution);
+ string getReadout(int _analog, bool _extendedResolution = false, int prev = -1);
void DrawROI(CImageBasis *_zw);
diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
index 9a0758ca..751d25af 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowControll.cpp
@@ -49,6 +49,9 @@ std::string ClassFlowControll::doSingleStep(std::string _stepname, std::string _
if ((_stepname.compare("[MQTT]") == 0) || (_stepname.compare(";[MQTT]") == 0)){
_classname = "ClassFlowMQTT";
}
+ if ((_stepname.compare("[InfluxDB]") == 0) || (_stepname.compare(";[InfluxDB]") == 0)){
+ _classname = "ClassFlowInfluxDB";
+ }
for (int i = 0; i < FlowControll.size(); ++i)
if (FlowControll[i]->name().compare(_classname) == 0){
@@ -67,14 +70,16 @@ std::string ClassFlowControll::TranslateAktstatus(std::string _input)
return ("Take Image");
if (_input.compare("ClassFlowAlignment") == 0)
return ("Aligning");
- //if (_input.compare("ClassFlowAnalog") == 0)
- // return ("Analog ROIs");
if (_input.compare("ClassFlowCNNGeneral") == 0)
return ("Digitalization of ROIs");
if (_input.compare("ClassFlowMQTT") == 0)
return ("Sending MQTT");
+ if (_input.compare("ClassFlowInfluxDB") == 0)
+ return ("Sending InfluxDB");
if (_input.compare("ClassFlowPostProcessing") == 0)
return ("Processing");
+ if (_input.compare("ClassFlowWriteList") == 0)
+ return ("Processing");
return "Unkown Status";
}
@@ -180,7 +185,13 @@ ClassFlow* ClassFlowControll::CreateClassFlow(std::string _type)
}
if (toUpper(_type).compare("[MQTT]") == 0)
cfc = new ClassFlowMQTT(&FlowControll);
+
+ if (toUpper(_type).compare("[INFLUXDB]") == 0)
+ cfc = new ClassFlowInfluxDB(&FlowControll);
+ if (toUpper(_type).compare("[WRITELIST]") == 0)
+ cfc = new ClassFlowWriteList(&FlowControll);
+
if (toUpper(_type).compare("[POSTPROCESSING]") == 0)
{
cfc = new ClassFlowPostProcessing(&FlowControll, flowanalog, flowdigit);
@@ -632,35 +643,7 @@ esp_err_t ClassFlowControll::GetJPGStream(std::string _fn, httpd_req_t *req)
return result;
}
-
-string ClassFlowControll::getJSON()
+string ClassFlowControll::getJSON(std::string _id, std::string _mac)
{
- std::vector* NUMBERS = flowpostprocessing->GetNumbers();
-
- std::string json="{\n";
-
- for (int i = 0; i < (*NUMBERS).size(); ++i)
- {
- json += "\"" + (*NUMBERS)[i]->name + "\":\n";
- json += " {\n";
- if ((*NUMBERS)[i]->ReturnValue.length() > 0)
- json += " \"value\": " + (*NUMBERS)[i]->ReturnValue + ",\n";
- else
- json += " \"value\": \"\",\n";
- json += " \"raw\": \"" + (*NUMBERS)[i]->ReturnRawValue + "\",\n";
- json += " \"error\": \"" + (*NUMBERS)[i]->ErrorMessageText + "\",\n";
- if ((*NUMBERS)[i]->ReturnRateValue.length() > 0)
- json += " \"rate\": " + (*NUMBERS)[i]->ReturnRateValue + ",\n";
- else
- json += " \"rate\": \"\",\n";
-
- json += " \"timestamp\": \"" + (*NUMBERS)[i]->timeStamp + "\"\n";
- if ((i+1) < (*NUMBERS).size())
- json += " },\n";
- else
- json += " }\n";
- }
- json += "}";
-
- return json;
+ return flowpostprocessing->GetJSON(_id, _mac);
}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowControll.h b/code/components/jomjol_flowcontroll/ClassFlowControll.h
index 3f568b26..a8f92dc3 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowControll.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowControll.h
@@ -9,7 +9,9 @@
#include "ClassFlowCNNGeneral.h"
#include "ClassFlowPostProcessing.h"
#include "ClassFlowMQTT.h"
+#include "ClassFlowInfluxDB.h"
#include "ClassFlowCNNGeneral.h"
+#include "ClassFlowWriteList.h"
#define READOUT_TYPE_VALUE 0
@@ -48,7 +50,7 @@ public:
string UpdatePrevalue(std::string _newvalue, std::string _numbers, bool _extern);
string GetPrevalue(std::string _number = "");
bool ReadParameter(FILE* pfile, string& aktparamgraph);
- string getJSON();
+ string getJSON(std::string _id = "", std::string _mac = "");
string TranslateAktstatus(std::string _input);
diff --git a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
index 7d9b2bec..98432886 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowDefineTypes.h
@@ -7,6 +7,7 @@ struct roi {
int posx, posy, deltax, deltay;
float result_float;
int result_klasse;
+ bool isReject;
string name;
CImageBasis *image, *image_org;
};
@@ -36,9 +37,10 @@ struct NumberPost {
float PreValue; // letzter Wert, der gut ausgelesen wurde
float Value; // letzer ausgelesener Wert, inkl. Korrekturen
string ReturnRateValue; // RückgabewertRate
+ string ReturnChangeAbsolute; // RückgabewertRate
string ReturnRawValue; // Rohwert (mit N & führenden 0)
string ReturnValue; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
- string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
+ string ReturnPreValue; // korrigierter Rückgabewert ohne Fehlermeldung
string ErrorMessageText; // Fehlermeldung bei Consistency Check
int AnzahlAnalog;
int AnzahlDigital;
diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
new file mode 100644
index 00000000..55b1f9ff
--- /dev/null
+++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.cpp
@@ -0,0 +1,161 @@
+#include
+#include "ClassFlowInfluxDB.h"
+#include "Helper.h"
+#include "connect_wlan.h"
+
+#include "time_sntp.h"
+#include "interface_influxdb.h"
+#include "ClassFlowPostProcessing.h"
+
+#include
+
+void ClassFlowInfluxDB::SetInitialParameter(void)
+{
+ uri = "";
+ database = "";
+ measurement = "";
+
+ OldValue = "";
+ flowpostprocessing = NULL;
+ user = "";
+ password = "";
+ previousElement = NULL;
+ ListFlowControll = NULL;
+ disabled = false;
+ InfluxDBenable = false;
+}
+
+ClassFlowInfluxDB::ClassFlowInfluxDB()
+{
+ SetInitialParameter();
+}
+
+ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector* lfc)
+{
+ SetInitialParameter();
+
+ ListFlowControll = lfc;
+ for (int i = 0; i < ListFlowControll->size(); ++i)
+ {
+ if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
+ {
+ flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
+ }
+ }
+}
+
+ClassFlowInfluxDB::ClassFlowInfluxDB(std::vector* lfc, ClassFlow *_prev)
+{
+ SetInitialParameter();
+
+ previousElement = _prev;
+ ListFlowControll = lfc;
+
+ for (int i = 0; i < ListFlowControll->size(); ++i)
+ {
+ if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
+ {
+ flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
+ }
+ }
+}
+
+
+bool ClassFlowInfluxDB::ReadParameter(FILE* pfile, string& aktparamgraph)
+{
+ std::vector zerlegt;
+
+ aktparamgraph = trim(aktparamgraph);
+
+ if (aktparamgraph.size() == 0)
+ if (!this->GetNextParagraph(pfile, aktparamgraph))
+ return false;
+
+ if (toUpper(aktparamgraph).compare("[INFLUXDB]") != 0)
+ return false;
+
+ while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
+ {
+ printf("while loop reading line: %s\n", aktparamgraph.c_str());
+ zerlegt = this->ZerlegeZeile(aktparamgraph);
+ if ((toUpper(zerlegt[0]) == "USER") && (zerlegt.size() > 1))
+ {
+ this->user = zerlegt[1];
+ }
+ if ((toUpper(zerlegt[0]) == "PASSWORD") && (zerlegt.size() > 1))
+ {
+ this->password = zerlegt[1];
+ }
+ if ((toUpper(zerlegt[0]) == "URI") && (zerlegt.size() > 1))
+ {
+ this->uri = zerlegt[1];
+ }
+ if (((toUpper(zerlegt[0]) == "MEASUREMENT")) && (zerlegt.size() > 1))
+ {
+ this->measurement = zerlegt[1];
+ }
+ if (((toUpper(zerlegt[0]) == "DATABASE")) && (zerlegt.size() > 1))
+ {
+ this->database = zerlegt[1];
+ }
+ }
+
+ if ((uri.length() > 0) && (database.length() > 0) && (measurement.length() > 0))
+ {
+ printf("Init InfluxDB with uri: %s, measurement: %s, user: %s, password: %s\n", uri.c_str(), measurement.c_str(), user.c_str(), password.c_str());
+ InfluxDBInit(uri, database, measurement, user, password);
+ InfluxDBenable = true;
+ } else {
+ printf("InfluxDB init skipped as we are missing some parameters");
+ }
+
+ return true;
+}
+
+
+string ClassFlowInfluxDB::GetInfluxDBMeasurement()
+{
+ return measurement;
+}
+
+
+bool ClassFlowInfluxDB::doFlow(string zwtime)
+{
+ if (!InfluxDBenable)
+ return true;
+
+ std::string result;
+ std::string resulterror = "";
+ std::string resultraw = "";
+ std::string resultrate = "";
+ std::string resulttimestamp = "";
+ string zw = "";
+ string namenumber = "";
+
+ if (flowpostprocessing)
+ {
+ std::vector* NUMBERS = flowpostprocessing->GetNumbers();
+
+ for (int i = 0; i < (*NUMBERS).size(); ++i)
+ {
+ result = (*NUMBERS)[i]->ReturnValue;
+ resultraw = (*NUMBERS)[i]->ReturnRawValue;
+ resulterror = (*NUMBERS)[i]->ErrorMessageText;
+ resultrate = (*NUMBERS)[i]->ReturnRateValue;
+ resulttimestamp = (*NUMBERS)[i]->timeStamp;
+
+ namenumber = (*NUMBERS)[i]->name;
+ if (namenumber == "default")
+ namenumber = "value";
+ else
+ namenumber = namenumber + "/value";
+
+ if (result.length() > 0 && resulttimestamp.length() > 0)
+ InfluxDBPublish(namenumber, result, resulttimestamp);
+ }
+ }
+
+ OldValue = result;
+
+ return true;
+}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
new file mode 100644
index 00000000..b7e25362
--- /dev/null
+++ b/code/components/jomjol_flowcontroll/ClassFlowInfluxDB.h
@@ -0,0 +1,31 @@
+#pragma once
+#include "ClassFlow.h"
+
+#include "ClassFlowPostProcessing.h"
+
+#include
+
+class ClassFlowInfluxDB :
+ public ClassFlow
+{
+protected:
+ std::string uri, database, measurement;
+ std::string OldValue;
+ ClassFlowPostProcessing* flowpostprocessing;
+ std::string user, password;
+ bool InfluxDBenable;
+
+ void SetInitialParameter(void);
+
+public:
+ ClassFlowInfluxDB();
+ ClassFlowInfluxDB(std::vector* lfc);
+ ClassFlowInfluxDB(std::vector* lfc, ClassFlow *_prev);
+
+ string GetInfluxDBMeasurement();
+
+ bool ReadParameter(FILE* pfile, string& aktparamgraph);
+ bool doFlow(string time);
+ string name(){return "ClassFlowInfluxDB";};
+};
+
diff --git a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp
index d78399cb..f4e014e9 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowMQTT.cpp
@@ -25,7 +25,8 @@ void ClassFlowMQTT::SetInitialParameter(void)
OldValue = "";
flowpostprocessing = NULL;
user = "";
- password = "";
+ password = "";
+ SetRetainFlag = 0;
previousElement = NULL;
ListFlowControll = NULL;
disabled = false;
@@ -99,6 +100,12 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
{
this->uri = zerlegt[1];
}
+ if ((toUpper(zerlegt[0]) == "SETRETAINFLAG") && (zerlegt.size() > 1))
+ {
+ if (toUpper(zerlegt[1]) == "TRUE")
+ SetRetainFlag = 1;
+ }
+
if ((toUpper(zerlegt[0]) == "CLIENTID") && (zerlegt.size() > 1))
{
@@ -118,7 +125,7 @@ bool ClassFlowMQTT::ReadParameter(FILE* pfile, string& aktparamgraph)
mainerrortopic = maintopic + "/connection";
printf("Init MQTT with uri: %s, clientname: %s, user: %s, password: %s, maintopic: %s\n", uri.c_str(), clientname.c_str(), user.c_str(), password.c_str(), mainerrortopic.c_str());
MQTTInit(uri, clientname, user, password, mainerrortopic, 60);
- MQTTPublish(mainerrortopic, "connected");
+ MQTTPublish(mainerrortopic, "connected", SetRetainFlag);
MQTTenable = true;
}
@@ -142,6 +149,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
std::string resultraw = "";
std::string resultrate = "";
std::string resulttimestamp = "";
+ std::string resultchangabs = "";
string zw = "";
string namenumber = "";
@@ -150,17 +158,17 @@ bool ClassFlowMQTT::doFlow(string zwtime)
zw = maintopic + "/" + "uptime";
char uptimeStr[11];
sprintf(uptimeStr, "%ld", (long)getUpTime());
- MQTTPublish(zw, uptimeStr);
+ MQTTPublish(zw, uptimeStr, SetRetainFlag);
zw = maintopic + "/" + "freeMem";
char freeheapmem[11];
sprintf(freeheapmem, "%zu", esp_get_free_heap_size());
- MQTTPublish(zw, freeheapmem);
+ MQTTPublish(zw, freeheapmem, SetRetainFlag);
zw = maintopic + "/" + "wifiRSSI";
char rssi[11];
sprintf(rssi, "%d", get_WIFI_RSSI());
- MQTTPublish(zw, rssi);
+ MQTTPublish(zw, rssi, SetRetainFlag);
if (flowpostprocessing)
@@ -173,6 +181,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
resultraw = (*NUMBERS)[i]->ReturnRawValue;
resulterror = (*NUMBERS)[i]->ErrorMessageText;
resultrate = (*NUMBERS)[i]->ReturnRateValue;
+ resultchangabs = (*NUMBERS)[i]->ReturnChangeAbsolute;
resulttimestamp = (*NUMBERS)[i]->timeStamp;
namenumber = (*NUMBERS)[i]->name;
@@ -183,23 +192,27 @@ bool ClassFlowMQTT::doFlow(string zwtime)
zw = namenumber + "value";
if (result.length() > 0)
- MQTTPublish(zw, result);
+ MQTTPublish(zw, result, SetRetainFlag);
zw = namenumber + "error";
if (resulterror.length() > 0)
- MQTTPublish(zw, resulterror, 1);
+ MQTTPublish(zw, resulterror, SetRetainFlag);
zw = namenumber + "rate";
if (resultrate.length() > 0)
- MQTTPublish(zw, resultrate);
+ MQTTPublish(zw, resultrate, SetRetainFlag);
+
+ zw = namenumber + "changeabsolut";
+ if (resultchangabs.length() > 0)
+ MQTTPublish(zw, resultchangabs, SetRetainFlag);
zw = namenumber + "raw";
if (resultraw.length() > 0)
- MQTTPublish(zw, resultraw);
+ MQTTPublish(zw, resultraw, SetRetainFlag);
zw = namenumber + "timestamp";
if (resulttimestamp.length() > 0)
- MQTTPublish(zw, resulttimestamp);
+ MQTTPublish(zw, resulttimestamp, SetRetainFlag);
std::string json = "";
@@ -218,7 +231,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
json += ",\"timestamp\":\""+resulttimestamp+"\"}";
zw = namenumber + "json";
- MQTTPublish(zw, json);
+ MQTTPublish(zw, json, SetRetainFlag);
}
}
else
@@ -234,7 +247,7 @@ bool ClassFlowMQTT::doFlow(string zwtime)
result = result + "\t" + zw;
}
}
- MQTTPublish(topic, result);
+ MQTTPublish(topic, result, SetRetainFlag);
}
OldValue = result;
diff --git a/code/components/jomjol_flowcontroll/ClassFlowMQTT.h b/code/components/jomjol_flowcontroll/ClassFlowMQTT.h
index 19cc9fdf..816389b1 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowMQTT.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowMQTT.h
@@ -13,6 +13,7 @@ protected:
std::string OldValue;
ClassFlowPostProcessing* flowpostprocessing;
std::string user, password;
+ int SetRetainFlag;
bool MQTTenable;
std::string maintopic, mainerrortopic;
diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
index f37d44a5..306b6b5f 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
+++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.cpp
@@ -15,6 +15,42 @@
#define PREVALUE_TIME_FORMAT_INPUT "%d-%d-%dT%d:%d:%d"
+std::string ClassFlowPostProcessing::GetJSON(std::string _id, std::string _mac, std::string _lineend)
+{
+ std::string json="{" + _lineend;
+
+ for (int i = 0; i < NUMBERS.size(); ++i)
+ {
+ json += "\"" + NUMBERS[i]->name + "\":" + _lineend;
+ json += " {" + _lineend;
+
+ if (_id.length() > 0)
+ json += " \"ID\": \"" + _id + "\"," + _lineend;
+ if (_mac.length() > 0)
+ json += " \"MAC\": \"" + _mac + "\"," + _lineend;
+
+ if (NUMBERS[i]->ReturnValue.length() > 0)
+ json += " \"value\": \"" + NUMBERS[i]->ReturnValue + "\"," + _lineend;
+ else
+ json += " \"value\": \"\"," + _lineend;
+ json += " \"raw\": \"" + NUMBERS[i]->ReturnRawValue + "\"," + _lineend;
+ json += " \"error\": \"" + NUMBERS[i]->ErrorMessageText + "\"," + _lineend;
+ if (NUMBERS[i]->ReturnRateValue.length() > 0)
+ json += " \"rate\": " + NUMBERS[i]->ReturnRateValue + "," + _lineend;
+ else
+ json += " \"rate\": \"\"," + _lineend;
+
+ json += " \"timestamp\": \"" + NUMBERS[i]->timeStamp + "\"" + _lineend;
+ if ((i+1) < NUMBERS.size())
+ json += " }," + _lineend;
+ else
+ json += " }" + _lineend;
+ }
+ json += "}";
+
+ return json;
+}
+
string ClassFlowPostProcessing::GetPreValue(std::string _number)
{
std::string result;
@@ -41,6 +77,8 @@ void ClassFlowPostProcessing::SetPreValue(float zw, string _numbers, bool _exter
if (NUMBERS[j]->name == _numbers)
{
NUMBERS[j]->PreValue = zw;
+ NUMBERS[j]->ReturnPreValue = std::to_string(zw);
+ NUMBERS[j]->PreValueOkay = true;
if (_extern)
{
time(&(NUMBERS[j]->lastvalue));
@@ -505,7 +543,6 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
-// _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
_number->ReturnPreValue = "";
_number->PreValueOkay = false;
@@ -524,7 +561,6 @@ void ClassFlowPostProcessing::InitNUMBERS()
_number->Value = 0; // letzer ausgelesener Wert, inkl. Korrekturen
_number->ReturnRawValue = ""; // Rohwert (mit N & führenden 0)
_number->ReturnValue = ""; // korrigierter Rückgabewert, ggf. mit Fehlermeldung
-// _number->ReturnValueNoError = ""; // korrigierter Rückgabewert ohne Fehlermeldung
_number->ErrorMessageText = ""; // Fehlermeldung bei Consistency Check
_number->Nachkomma = _number->AnzahlAnalog;
@@ -612,18 +648,29 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
UpdateNachkommaDecimalShift();
+ int previous_value = -1;
+
+ if (NUMBERS[j]->analog_roi)
+ {
+ NUMBERS[j]->ReturnRawValue = flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
+ if (NUMBERS[j]->ReturnRawValue.length() > 0)
+ {
+ char zw = NUMBERS[j]->ReturnRawValue[0];
+ if (zw >= 48 && zw <=57)
+ previous_value = zw - 48;
+ }
+ }
+
+ if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
+ NUMBERS[j]->ReturnRawValue = "." + NUMBERS[j]->ReturnRawValue;
+
if (NUMBERS[j]->digit_roi)
{
if (NUMBERS[j]->analog_roi)
- NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false);
+ NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, false, previous_value) + NUMBERS[j]->ReturnRawValue;
else
- NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution); // Extended Resolution nur falls es keine analogen Ziffern gibt
+ NUMBERS[j]->ReturnRawValue = flowDigit->getReadout(j, NUMBERS[j]->isExtendedResolution, previous_value); // Extended Resolution nur falls es keine analogen Ziffern gibt
}
- if (NUMBERS[j]->digit_roi && NUMBERS[j]->analog_roi)
- NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + ".";
-
- if (NUMBERS[j]->analog_roi)
- NUMBERS[j]->ReturnRawValue = NUMBERS[j]->ReturnRawValue + flowAnalog->getReadout(j, NUMBERS[j]->isExtendedResolution);
NUMBERS[j]->ReturnRawValue = ShiftDecimal(NUMBERS[j]->ReturnRawValue, NUMBERS[j]->DecimalShift);
@@ -675,7 +722,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
if (NUMBERS[j]->useMaxRateValue && PreValueUse && NUMBERS[j]->PreValueOkay)
{
- float _ratedifference;
+ float _ratedifference;
if (NUMBERS[j]->RateType == RateChange)
_ratedifference = NUMBERS[j]->FlowRateAct;
else
@@ -691,6 +738,7 @@ bool ClassFlowPostProcessing::doFlow(string zwtime)
}
}
+ NUMBERS[j]->ReturnChangeAbsolute = RundeOutput(NUMBERS[j]->Value - NUMBERS[j]->PreValue, NUMBERS[j]->Nachkomma);
NUMBERS[j]->lastvalue = imagetime;
NUMBERS[j]->PreValue = NUMBERS[j]->Value;
NUMBERS[j]->PreValueOkay = true;
diff --git a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
index aa50a85c..34b2309c 100644
--- a/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
+++ b/code/components/jomjol_flowcontroll/ClassFlowPostProcessing.h
@@ -60,6 +60,8 @@ public:
string GetPreValue(std::string _number = "");
void SetPreValue(float zw, string _numbers, bool _extern = false);
+ std::string GetJSON(std::string _id = "", std::string _mac = "", std::string _lineend = "\n");
+
void UpdateNachkommaDecimalShift();
std::vector* GetNumbers(){return &NUMBERS;};
diff --git a/code/components/jomjol_flowcontroll/ClassFlowWriteList.cpp b/code/components/jomjol_flowcontroll/ClassFlowWriteList.cpp
new file mode 100644
index 00000000..4d406728
--- /dev/null
+++ b/code/components/jomjol_flowcontroll/ClassFlowWriteList.cpp
@@ -0,0 +1,97 @@
+#include
+#include "ClassFlowWriteList.h"
+#include "Helper.h"
+
+#include "time_sntp.h"
+
+
+#include
+
+void ClassFlowWriteList::SetInitialParameter(void)
+{
+ flowpostprocessing = NULL;
+ previousElement = NULL;
+ ListFlowControll = NULL;
+ disabled = false;
+}
+
+ClassFlowWriteList::ClassFlowWriteList()
+{
+ SetInitialParameter();
+}
+
+ClassFlowWriteList::ClassFlowWriteList(std::vector* lfc)
+{
+ SetInitialParameter();
+
+ ListFlowControll = lfc;
+ for (int i = 0; i < ListFlowControll->size(); ++i)
+ {
+ if (((*ListFlowControll)[i])->name().compare("ClassFlowPostProcessing") == 0)
+ {
+ flowpostprocessing = (ClassFlowPostProcessing*) (*ListFlowControll)[i];
+ }
+ }
+}
+
+
+bool ClassFlowWriteList::ReadParameter(FILE* pfile, string& aktparamgraph)
+{
+ std::vector zerlegt;
+
+ aktparamgraph = trim(aktparamgraph);
+
+ if (aktparamgraph.size() == 0)
+ if (!this->GetNextParagraph(pfile, aktparamgraph))
+ return false;
+
+ if (toUpper(aktparamgraph).compare("[MQTT]") != 0) // Paragraph passt nich zu MakeImage
+ return false;
+
+ while (this->getNextLine(pfile, &aktparamgraph) && !this->isNewParagraph(aktparamgraph))
+ {
+ zerlegt = this->ZerlegeZeile(aktparamgraph);
+/*
+ if ((toUpper(zerlegt[0]) == "USER") && (zerlegt.size() > 1))
+ {
+ this->user = zerlegt[1];
+ }
+*/
+ }
+
+ return true;
+}
+
+
+
+bool ClassFlowWriteList::doFlow(string zwtime)
+{
+ std::string line = "";
+
+ std::string result;
+ std::string resulterror = "";
+ std::string resultraw = "";
+ std::string resultrate = "";
+ std::string resulttimestamp = "";
+ string zw = "";
+ string namenumber = "";
+
+ if (flowpostprocessing)
+ {
+ std::vector* NUMBERS = flowpostprocessing->GetNumbers();
+
+ for (int i = 0; i < (*NUMBERS).size(); ++i)
+ {
+ result = (*NUMBERS)[i]->ReturnValue;
+ resultraw = (*NUMBERS)[i]->ReturnRawValue;
+ resulterror = (*NUMBERS)[i]->ErrorMessageText;
+ resultrate = (*NUMBERS)[i]->ReturnRateValue;
+ resulttimestamp = (*NUMBERS)[i]->timeStamp;
+
+ line = line + resulttimestamp + "\t" + resultraw + "\t" + result + "\t" + resultraw + "\t" + resultrate + "\t" + resulttimestamp + "\t";
+
+ }
+ }
+
+ return true;
+}
diff --git a/code/components/jomjol_flowcontroll/ClassFlowWriteList.h b/code/components/jomjol_flowcontroll/ClassFlowWriteList.h
new file mode 100644
index 00000000..49078f19
--- /dev/null
+++ b/code/components/jomjol_flowcontroll/ClassFlowWriteList.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "ClassFlow.h"
+#include "ClassFlowPostProcessing.h"
+
+#include
+
+class ClassFlowWriteList :
+ public ClassFlow
+{
+protected:
+ ClassFlowPostProcessing* flowpostprocessing;
+ void SetInitialParameter(void);
+
+public:
+ ClassFlowWriteList();
+ ClassFlowWriteList(std::vector* lfc);
+
+ bool ReadParameter(FILE* pfile, string& aktparamgraph);
+ bool doFlow(string time);
+ string name(){return "ClassFlowWriteList";};
+};
+
diff --git a/code/components/jomjol_image_proc/CRotateImage.cpp b/code/components/jomjol_image_proc/CRotateImage.cpp
index b6b2321b..96eec7a0 100644
--- a/code/components/jomjol_image_proc/CRotateImage.cpp
+++ b/code/components/jomjol_image_proc/CRotateImage.cpp
@@ -156,12 +156,140 @@ void CRotateImage::Rotate(float _angle, int _centerx, int _centery)
RGBImageRelease();
}
+
+
+void CRotateImage::RotateAntiAliasing(float _angle, int _centerx, int _centery)
+{
+ int org_width, org_height;
+ float m[2][3];
+
+ float x_center = _centerx;
+ float y_center = _centery;
+ _angle = _angle / 180 * M_PI;
+
+ if (doflip)
+ {
+ org_width = width;
+ org_height = height;
+ height = org_width;
+ width = org_height;
+ x_center = x_center - (org_width/2) + (org_height/2);
+ y_center = y_center + (org_width/2) - (org_height/2);
+ if (ImageOrg)
+ {
+ ImageOrg->height = height;
+ ImageOrg->width = width;
+ }
+ }
+ else
+ {
+ org_width = width;
+ org_height = height;
+ }
+
+ m[0][0] = cos(_angle);
+ m[0][1] = sin(_angle);
+ m[0][2] = (1 - m[0][0]) * x_center - m[0][1] * y_center;
+
+ m[1][0] = -m[0][1];
+ m[1][1] = m[0][0];
+ m[1][2] = m[0][1] * x_center + (1 - m[0][0]) * y_center;
+
+ if (doflip)
+ {
+ m[0][2] = m[0][2] + (org_width/2) - (org_height/2);
+ m[1][2] = m[1][2] - (org_width/2) + (org_height/2);
+ }
+
+ int memsize = width * height * channels;
+ uint8_t* odata;
+ if (ImageTMP)
+ {
+ odata = ImageTMP->RGBImageLock();
+ }
+ else
+ {
+ odata = (unsigned char*)GET_MEMORY(memsize);
+ }
+
+
+ int x_source_1, y_source_1, x_source_2, y_source_2;
+ float x_source, y_source;
+ float quad_ul, quad_ur, quad_ol, quad_or;
+ stbi_uc* p_target;
+ stbi_uc *p_source_ul, *p_source_ur, *p_source_ol, *p_source_or;
+
+ RGBImageLock();
+
+ for (int x = 0; x < width; ++x)
+ for (int y = 0; y < height; ++y)
+ {
+ p_target = odata + (channels * (y * width + x));
+
+ x_source = (m[0][0] * x + m[0][1] * y);
+ y_source = (m[1][0] * x + m[1][1] * y);
+
+ x_source += (m[0][2]);
+ y_source += (m[1][2]);
+
+ x_source_1 = (int)x_source;
+ x_source_2 = x_source_1 + 1;
+ y_source_1 = (int)y_source;
+ y_source_2 = y_source_1 + 1;
+
+ quad_ul = (x_source_2 - x_source) * (y_source_2 - y_source);
+ quad_ur = (1- (x_source_2 - x_source)) * (y_source_2 - y_source);
+ quad_or = (x_source_2 - x_source) * (1-(y_source_2 - y_source));
+ quad_ol = (1- (x_source_2 - x_source)) * (1-(y_source_2 - y_source));
+
+
+ if ((x_source_1 >= 0) && (x_source_2 < org_width) && (y_source_1 >= 0) && (y_source_2 < org_height))
+ {
+ p_source_ul = rgb_image + (channels * (y_source_1 * org_width + x_source_1));
+ p_source_ur = rgb_image + (channels * (y_source_1 * org_width + x_source_2));
+ p_source_or = rgb_image + (channels * (y_source_2 * org_width + x_source_1));
+ p_source_ol = rgb_image + (channels * (y_source_2 * org_width + x_source_2));
+ for (int _channels = 0; _channels < channels; ++_channels)
+ {
+ p_target[_channels] = (int)((float)p_source_ul[_channels] * quad_ul
+ + (float)p_source_ur[_channels] * quad_ur
+ + (float)p_source_or[_channels] * quad_or
+ + (float)p_source_ol[_channels] * quad_ol);
+ }
+ }
+ else
+ {
+ for (int _channels = 0; _channels < channels; ++_channels)
+ p_target[_channels] = 255;
+ }
+ }
+
+ // memcpy(rgb_image, odata, memsize);
+ memCopy(odata, rgb_image, memsize);
+
+ if (!ImageTMP)
+ {
+ stbi_image_free(odata);
+ }
+ if (ImageTMP)
+ ImageTMP->RGBImageRelease();
+
+ RGBImageRelease();
+}
+
+
void CRotateImage::Rotate(float _angle)
{
// printf("width %d, height %d\n", width, height);
Rotate(_angle, width / 2, height / 2);
}
+void CRotateImage::RotateAntiAliasing(float _angle)
+{
+// printf("width %d, height %d\n", width, height);
+ RotateAntiAliasing(_angle, width / 2, height / 2);
+}
+
void CRotateImage::Translate(int _dx, int _dy)
{
int memsize = width * height * channels;
diff --git a/code/components/jomjol_image_proc/CRotateImage.h b/code/components/jomjol_image_proc/CRotateImage.h
index 90ad7d71..4dec78ef 100644
--- a/code/components/jomjol_image_proc/CRotateImage.h
+++ b/code/components/jomjol_image_proc/CRotateImage.h
@@ -11,7 +11,11 @@ class CRotateImage: public CImageBasis
CRotateImage(CImageBasis *_org, CImageBasis *_temp, bool _flip = false);
void Rotate(float _angle);
+ void RotateAntiAliasing(float _angle);
+
void Rotate(float _angle, int _centerx, int _centery);
+ void RotateAntiAliasing(float _angle, int _centerx, int _centery);
+
void Translate(int _dx, int _dy);
void Mirror();
};
diff --git a/code/components/jomjol_influxdb/CMakeLists.txt b/code/components/jomjol_influxdb/CMakeLists.txt
new file mode 100644
index 00000000..47330bd5
--- /dev/null
+++ b/code/components/jomjol_influxdb/CMakeLists.txt
@@ -0,0 +1,7 @@
+FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
+
+idf_component_register(SRCS ${app_sources}
+ INCLUDE_DIRS "."
+ REQUIRES tflite-lib esp_http_client jomjol_logfile)
+
+
diff --git a/code/components/jomjol_influxdb/interface_influxdb.cpp b/code/components/jomjol_influxdb/interface_influxdb.cpp
new file mode 100644
index 00000000..2089d98e
--- /dev/null
+++ b/code/components/jomjol_influxdb/interface_influxdb.cpp
@@ -0,0 +1,114 @@
+#include "interface_influxdb.h"
+
+//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
+#include "esp_log.h"
+#include
+#include "ClassLogFile.h"
+#include "esp_http_client.h"
+
+#define MAX_HTTP_OUTPUT_BUFFER 2048
+
+static const char *TAG_INTERFACEINFLUXDB = "interface_influxdb";
+
+std::string _influxDBURI;
+std::string _influxDBDatabase;
+std::string _influxDBMeasurement;
+std::string _influxDBUser;
+std::string _influxDBPassword;
+
+static esp_err_t http_event_handler(esp_http_client_event_t *evt)
+{
+ switch(evt->event_id)
+ {
+ case HTTP_EVENT_ERROR:
+ ESP_LOGE(TAG_INTERFACEINFLUXDB, "HTTP Client Error encountered");
+ break;
+ case HTTP_EVENT_ON_CONNECTED:
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "HTTP Client Connected");
+ break;
+ case HTTP_EVENT_HEADERS_SENT:
+ ESP_LOGV(TAG_INTERFACEINFLUXDB, "HTTP Client sent all request headers");
+ break;
+ case HTTP_EVENT_ON_HEADER:
+ ESP_LOGV(TAG_INTERFACEINFLUXDB, "Header: key=%s, value=%s", evt->header_key, evt->header_value);
+ break;
+ case HTTP_EVENT_ON_DATA:
+ ESP_LOGV(TAG_INTERFACEINFLUXDB, "HTTP Client data recevied: len=%d", evt->data_len);
+ break;
+ case HTTP_EVENT_ON_FINISH:
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "HTTP Client finished");
+ break;
+ case HTTP_EVENT_DISCONNECTED:
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "HTTP Client Disconnected");
+ break;
+ }
+ return ESP_OK;
+}
+
+void InfluxDBPublish(std::string _key, std::string _content, std::string _timestamp) {
+ char response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
+ esp_http_client_config_t http_config = {
+ .user_agent = "ESP32 Meter reader",
+ .method = HTTP_METHOD_POST,
+ .event_handler = http_event_handler,
+ .buffer_size = MAX_HTTP_OUTPUT_BUFFER,
+ .user_data = response_buffer
+ };
+
+ if (_influxDBUser.length() && _influxDBPassword.length()){
+ http_config.username = _influxDBUser.c_str();
+ http_config.password = _influxDBPassword.c_str();
+ http_config.auth_type = HTTP_AUTH_TYPE_BASIC;
+ }
+
+ // generate timestamp (TODO: parse result timestamp passed as string and convert it to POSIX timestamp?)
+ time_t now = time(NULL);
+ char nowTimestamp[21];
+ // pad with zeroes to get nanoseconds
+ sprintf(nowTimestamp,"%jd000000000", (intmax_t)now);
+
+ std::string payload = _influxDBMeasurement + " " + _key + "=" + _content + " " + nowTimestamp;
+ payload.shrink_to_fit();
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "sending line to influxdb: %s\n", payload.c_str());
+
+ // use the default retention policy of the database
+ std::string apiURI = _influxDBURI + "/api/v2/write?bucket=" + _influxDBDatabase + "/";
+ apiURI.shrink_to_fit();
+ http_config.url = apiURI.c_str();
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "API URI: %s", apiURI.c_str());
+
+ esp_http_client_handle_t http_client = esp_http_client_init(&http_config);
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "client is initialized%s\n", "");
+
+ esp_http_client_set_header(http_client, "Content-Type", "text/plain");
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "header is set%s\n", "");
+
+ ESP_ERROR_CHECK(esp_http_client_set_post_field(http_client, payload.c_str(), payload.length()));
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "post payload is set%s\n", "");
+
+ esp_err_t err = ESP_ERROR_CHECK_WITHOUT_ABORT(esp_http_client_perform(http_client));
+
+ if( err == ESP_OK ) {
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "HTTP request was performed%s\n", "");
+ int status_code = esp_http_client_get_status_code(http_client);
+ ESP_LOGI(TAG_INTERFACEINFLUXDB, "HTTP status code %d\n", status_code);
+ } else {
+ ESP_LOGW(TAG_INTERFACEINFLUXDB, "HTTP request failed%s\n", "");
+ }
+ esp_http_client_cleanup(http_client);
+}
+
+
+void InfluxDBInit(std::string _uri, std::string _database, std::string _measurement, std::string _user, std::string _password){
+ _influxDBURI = _uri;
+ _influxDBDatabase = _database;
+ _influxDBMeasurement = _measurement;
+ _influxDBUser = _user;
+ _influxDBPassword = _password;
+
+}
+
+void InfluxDBdestroy() {
+}
+
+
diff --git a/code/components/jomjol_influxdb/interface_influxdb.h b/code/components/jomjol_influxdb/interface_influxdb.h
new file mode 100644
index 00000000..33ae0564
--- /dev/null
+++ b/code/components/jomjol_influxdb/interface_influxdb.h
@@ -0,0 +1,13 @@
+#ifndef INTERFACE_INFLUXDB_H
+#define INTERFACE_INFLUXDB_H
+
+#include
+#include